diff options
757 files changed, 43643 insertions, 8778 deletions
diff --git a/.gitignore b/.gitignore index 8faa6c02b39..5d56a3fd0de 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ modules.builtin *.gz *.bz2 *.lzma +*.xz *.lzo *.patch *.gcno diff --git a/Documentation/DocBook/filesystems.tmpl b/Documentation/DocBook/filesystems.tmpl index 5e87ad58c0b..f51f28531b8 100644 --- a/Documentation/DocBook/filesystems.tmpl +++ b/Documentation/DocBook/filesystems.tmpl @@ -82,6 +82,11 @@ </sect1> </chapter> + <chapter id="fs_events"> + <title>Events based on file descriptors</title> +!Efs/eventfd.c + </chapter> + <chapter id="sysfs"> <title>The Filesystem for Exporting Kernel Objects</title> !Efs/sysfs/file.c diff --git a/Documentation/hwmon/jc42 b/Documentation/hwmon/jc42 index 0e76ef12e4c..a22ecf48f25 100644 --- a/Documentation/hwmon/jc42 +++ b/Documentation/hwmon/jc42 @@ -51,7 +51,8 @@ Supported chips: * JEDEC JC 42.4 compliant temperature sensor chips Prefix: 'jc42' Addresses scanned: I2C 0x18 - 0x1f - Datasheet: - + Datasheet: + http://www.jedec.org/sites/default/files/docs/4_01_04R19.pdf Author: Guenter Roeck <guenter.roeck@ericsson.com> @@ -60,7 +61,11 @@ Author: Description ----------- -This driver implements support for JEDEC JC 42.4 compliant temperature sensors. +This driver implements support for JEDEC JC 42.4 compliant temperature sensors, +which are used on many DDR3 memory modules for mobile devices and servers. Some +systems use the sensor to prevent memory overheating by automatically throttling +the memory controller. + The driver auto-detects the chips listed above, but can be manually instantiated to support other JC 42.4 compliant chips. @@ -81,15 +86,19 @@ limits. The chip supports only a single register to configure the hysteresis, which applies to all limits. This register can be written by writing into temp1_crit_hyst. Other hysteresis attributes are read-only. +If the BIOS has configured the sensor for automatic temperature management, it +is likely that it has locked the registers, i.e., that the temperature limits +cannot be changed. + Sysfs entries ------------- temp1_input Temperature (RO) -temp1_min Minimum temperature (RW) -temp1_max Maximum temperature (RW) -temp1_crit Critical high temperature (RW) +temp1_min Minimum temperature (RO or RW) +temp1_max Maximum temperature (RO or RW) +temp1_crit Critical high temperature (RO or RW) -temp1_crit_hyst Critical hysteresis temperature (RW) +temp1_crit_hyst Critical hysteresis temperature (RO or RW) temp1_max_hyst Maximum hysteresis temperature (RO) temp1_min_alarm Temperature low alarm diff --git a/Documentation/hwmon/k10temp b/Documentation/hwmon/k10temp index 6526eee525a..d2b56a4fd1f 100644 --- a/Documentation/hwmon/k10temp +++ b/Documentation/hwmon/k10temp @@ -9,6 +9,8 @@ Supported chips: Socket S1G3: Athlon II, Sempron, Turion II * AMD Family 11h processors: Socket S1G2: Athlon (X2), Sempron (X2), Turion X2 (Ultra) +* AMD Family 12h processors: "Llano" +* AMD Family 14h processors: "Brazos" (C/E/G-Series) Prefix: 'k10temp' Addresses scanned: PCI space @@ -17,10 +19,14 @@ Supported chips: http://support.amd.com/us/Processor_TechDocs/31116.pdf BIOS and Kernel Developer's Guide (BKDG) for AMD Family 11h Processors: http://support.amd.com/us/Processor_TechDocs/41256.pdf + BIOS and Kernel Developer's Guide (BKDG) for AMD Family 14h Models 00h-0Fh Processors: + http://support.amd.com/us/Processor_TechDocs/43170.pdf Revision Guide for AMD Family 10h Processors: http://support.amd.com/us/Processor_TechDocs/41322.pdf Revision Guide for AMD Family 11h Processors: http://support.amd.com/us/Processor_TechDocs/41788.pdf + Revision Guide for AMD Family 14h Models 00h-0Fh Processors: + http://support.amd.com/us/Processor_TechDocs/47534.pdf AMD Family 11h Processor Power and Thermal Data Sheet for Notebooks: http://support.amd.com/us/Processor_TechDocs/43373.pdf AMD Family 10h Server and Workstation Processor Power and Thermal Data Sheet: @@ -34,7 +40,7 @@ Description ----------- This driver permits reading of the internal temperature sensor of AMD -Family 10h and 11h processors. +Family 10h/11h/12h/14h processors. All these processors have a sensor, but on those for Socket F or AM2+, the sensor may return inconsistent values (erratum 319). The driver diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 89835a4766a..f4a04c0c7ed 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -144,6 +144,11 @@ a fixed number of characters. This limit depends on the architecture and is between 256 and 4096 characters. It is defined in the file ./include/asm/setup.h as COMMAND_LINE_SIZE. +Finally, the [KMG] suffix is commonly described after a number of kernel +parameter values. These 'K', 'M', and 'G' letters represent the _binary_ +multipliers 'Kilo', 'Mega', and 'Giga', equalling 2^10, 2^20, and 2^30 +bytes respectively. Such letter suffixes can also be entirely omitted. + acpi= [HW,ACPI,X86] Advanced Configuration and Power Interface @@ -545,16 +550,20 @@ and is between 256 and 4096 characters. It is defined in the file Format: <first_slot>,<last_slot>,<port>,<enum_bit>[,<debug>] - crashkernel=nn[KMG]@ss[KMG] - [KNL] Reserve a chunk of physical memory to - hold a kernel to switch to with kexec on panic. + crashkernel=size[KMG][@offset[KMG]] + [KNL] Using kexec, Linux can switch to a 'crash kernel' + upon panic. This parameter reserves the physical + memory region [offset, offset + size] for that kernel + image. If '@offset' is omitted, then a suitable offset + is selected automatically. Check + Documentation/kdump/kdump.txt for further details. crashkernel=range1:size1[,range2:size2,...][@offset] [KNL] Same as above, but depends on the memory in the running system. The syntax of range is start-[end] where start and end are both a memory unit (amount[KMG]). See also - Documentation/kdump/kdump.txt for a example. + Documentation/kdump/kdump.txt for an example. cs89x0_dma= [HW,NET] Format: <dma> @@ -1262,10 +1271,9 @@ and is between 256 and 4096 characters. It is defined in the file 6 (KERN_INFO) informational 7 (KERN_DEBUG) debug-level messages - log_buf_len=n Sets the size of the printk ring buffer, in bytes. - Format: { n | nk | nM } - n must be a power of two. The default size - is set in the kernel config file. + log_buf_len=n[KMG] Sets the size of the printk ring buffer, + in bytes. n must be a power of two. The default + size is set in the kernel config file. logo.nologo [FB] Disables display of the built-in Linux logo. This may be used to provide more screen space for diff --git a/Documentation/networking/00-INDEX b/Documentation/networking/00-INDEX index fe5c099b8fc..4edd78dfb36 100644 --- a/Documentation/networking/00-INDEX +++ b/Documentation/networking/00-INDEX @@ -40,8 +40,6 @@ decnet.txt - info on using the DECnet networking layer in Linux. depca.txt - the Digital DEPCA/EtherWORKS DE1?? and DE2?? LANCE Ethernet driver -dgrs.txt - - the Digi International RightSwitch SE-X Ethernet driver dmfe.txt - info on the Davicom DM9102(A)/DM9132/DM9801 fast ethernet driver. e100.txt @@ -50,8 +48,6 @@ e1000.txt - info on Intel's E1000 line of gigabit ethernet boards eql.txt - serial IP load balancing -ethertap.txt - - the Ethertap user space packet reception and transmission driver ewrk3.txt - the Digital EtherWORKS 3 DE203/4/5 Ethernet driver filter.txt @@ -104,8 +100,6 @@ tuntap.txt - TUN/TAP device driver, allowing user space Rx/Tx of packets. vortex.txt - info on using 3Com Vortex (3c590, 3c592, 3c595, 3c597) Ethernet cards. -wavelan.txt - - AT&T GIS (nee NCR) WaveLAN card: An Ethernet-like radio transceiver x25.txt - general info on X.25 development. x25-iface.txt diff --git a/Documentation/networking/dns_resolver.txt b/Documentation/networking/dns_resolver.txt index aefd1e68180..04ca06325b0 100644 --- a/Documentation/networking/dns_resolver.txt +++ b/Documentation/networking/dns_resolver.txt @@ -61,7 +61,6 @@ before the more general line given above as the first match is the one taken. create dns_resolver foo:* * /usr/sbin/dns.foo %k - ===== USAGE ===== @@ -104,6 +103,14 @@ implemented in the module can be called after doing: returned also. +=============================== +READING DNS KEYS FROM USERSPACE +=============================== + +Keys of dns_resolver type can be read from userspace using keyctl_read() or +"keyctl read/print/pipe". + + ========= MECHANISM ========= diff --git a/MAINTAINERS b/MAINTAINERS index 0d83e589823..75760e796ae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -885,7 +885,7 @@ S: Supported ARM/QUALCOMM MSM MACHINE SUPPORT M: David Brown <davidb@codeaurora.org> -M: Daniel Walker <dwalker@codeaurora.org> +M: Daniel Walker <dwalker@fifo99.com> M: Bryan Huntsman <bryanh@codeaurora.org> L: linux-arm-msm@vger.kernel.org F: arch/arm/mach-msm/ @@ -1467,6 +1467,7 @@ F: include/net/bluetooth/ BONDING DRIVER M: Jay Vosburgh <fubar@us.ibm.com> +M: Andy Gospodarek <andy@greyhouse.net> L: netdev@vger.kernel.org W: http://sourceforge.net/projects/bonding/ S: Supported @@ -1692,6 +1693,13 @@ M: Andy Whitcroft <apw@canonical.com> S: Supported F: scripts/checkpatch.pl +CHINESE DOCUMENTATION +M: Harry Wei <harryxiyou@gmail.com> +L: xiyoulinuxkernelgroup@googlegroups.com +L: linux-kernel@zh-kernel.org (moderated for non-subscribers) +S: Maintained +F: Documentation/zh_CN/ + CISCO VIC ETHERNET NIC DRIVER M: Christian Benvenuti <benve@cisco.com> M: Vasanthy Kolluri <vkolluri@cisco.com> @@ -2027,7 +2035,7 @@ F: Documentation/scsi/dc395x.txt F: drivers/scsi/dc395x.* DCCP PROTOCOL -M: Arnaldo Carvalho de Melo <acme@ghostprotocols.net> +M: Gerrit Renker <gerrit@erg.abdn.ac.uk> L: dccp@vger.kernel.org W: http://www.linuxfoundation.org/collaborate/workgroups/networking/dccp S: Maintained @@ -2874,7 +2882,6 @@ M: Guenter Roeck <guenter.roeck@ericsson.com> L: lm-sensors@lm-sensors.org W: http://www.lm-sensors.org/ T: quilt kernel.org/pub/linux/kernel/people/jdelvare/linux-2.6/jdelvare-hwmon/ -T: quilt kernel.org/pub/linux/kernel/people/groeck/linux-staging/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git S: Maintained F: Documentation/hwmon/ @@ -5269,7 +5276,7 @@ S: Maintained F: drivers/net/wireless/rtl818x/rtl8180/ RTL8187 WIRELESS DRIVER -M: Herton Ronaldo Krzesinski <herton@mandriva.com.br> +M: Herton Ronaldo Krzesinski <herton@canonical.com> M: Hin-Tak Leung <htl10@users.sourceforge.net> M: Larry Finger <Larry.Finger@lwfinger.net> L: linux-wireless@vger.kernel.org @@ -6107,7 +6114,7 @@ S: Maintained F: security/tomoyo/ TOPSTAR LAPTOP EXTRAS DRIVER -M: Herton Ronaldo Krzesinski <herton@mandriva.com.br> +M: Herton Ronaldo Krzesinski <herton@canonical.com> L: platform-driver-x86@vger.kernel.org S: Maintained F: drivers/platform/x86/topstar-laptop.c @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 38 -EXTRAVERSION = -rc5 +EXTRAVERSION = -rc7 NAME = Flesh-Eating Bats with Fangs # *DOCUMENTATION* diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig index 47f63d48014..cc31bec2e31 100644 --- a/arch/alpha/Kconfig +++ b/arch/alpha/Kconfig @@ -11,6 +11,7 @@ config ALPHA select HAVE_GENERIC_HARDIRQS select GENERIC_IRQ_PROBE select AUTO_IRQ_AFFINITY if SMP + select GENERIC_HARDIRQS_NO_DEPRECATED help The Alpha is a 64-bit general-purpose processor designed and marketed by the Digital Equipment Corporation of blessed memory, diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 9ab234f48dd..a19d6008229 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -44,11 +44,16 @@ static char irq_user_affinity[NR_IRQS]; int irq_select_affinity(unsigned int irq) { - struct irq_desc *desc = irq_to_desc[irq]; + struct irq_data *data = irq_get_irq_data(irq); + struct irq_chip *chip; static int last_cpu; int cpu = last_cpu + 1; - if (!desc || !get_irq_desc_chip(desc)->set_affinity || irq_user_affinity[irq]) + if (!data) + return 1; + chip = irq_data_get_irq_chip(data); + + if (!chip->irq_set_affinity || irq_user_affinity[irq]) return 1; while (!cpu_possible(cpu) || @@ -56,8 +61,8 @@ int irq_select_affinity(unsigned int irq) cpu = (cpu < (NR_CPUS-1) ? cpu + 1 : 0); last_cpu = cpu; - cpumask_copy(desc->affinity, cpumask_of(cpu)); - get_irq_desc_chip(desc)->set_affinity(irq, cpumask_of(cpu)); + cpumask_copy(data->affinity, cpumask_of(cpu)); + chip->irq_set_affinity(data, cpumask_of(cpu), false); return 0; } #endif /* CONFIG_SMP */ diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 2d0679b6093..411ca11d0a1 100644 --- a/arch/alpha/kernel/irq_alpha.c +++ b/arch/alpha/kernel/irq_alpha.c @@ -228,14 +228,9 @@ struct irqaction timer_irqaction = { void __init init_rtc_irq(void) { - struct irq_desc *desc = irq_to_desc(RTC_IRQ); - - if (desc) { - desc->status |= IRQ_DISABLED; - set_irq_chip_and_handler_name(RTC_IRQ, &no_irq_chip, - handle_simple_irq, "RTC"); - setup_irq(RTC_IRQ, &timer_irqaction); - } + set_irq_chip_and_handler_name(RTC_IRQ, &no_irq_chip, + handle_simple_irq, "RTC"); + setup_irq(RTC_IRQ, &timer_irqaction); } /* Dummy irqactions. */ diff --git a/arch/alpha/kernel/irq_i8259.c b/arch/alpha/kernel/irq_i8259.c index 956ea0ed169..c7cc9813e45 100644 --- a/arch/alpha/kernel/irq_i8259.c +++ b/arch/alpha/kernel/irq_i8259.c @@ -33,10 +33,10 @@ i8259_update_irq_hw(unsigned int irq, unsigned long mask) } inline void -i8259a_enable_irq(unsigned int irq) +i8259a_enable_irq(struct irq_data *d) { spin_lock(&i8259_irq_lock); - i8259_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); + i8259_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq)); spin_unlock(&i8259_irq_lock); } @@ -47,16 +47,18 @@ __i8259a_disable_irq(unsigned int irq) } void -i8259a_disable_irq(unsigned int irq) +i8259a_disable_irq(struct irq_data *d) { spin_lock(&i8259_irq_lock); - __i8259a_disable_irq(irq); + __i8259a_disable_irq(d->irq); spin_unlock(&i8259_irq_lock); } void -i8259a_mask_and_ack_irq(unsigned int irq) +i8259a_mask_and_ack_irq(struct irq_data *d) { + unsigned int irq = d->irq; + spin_lock(&i8259_irq_lock); __i8259a_disable_irq(irq); @@ -71,9 +73,9 @@ i8259a_mask_and_ack_irq(unsigned int irq) struct irq_chip i8259a_irq_type = { .name = "XT-PIC", - .unmask = i8259a_enable_irq, - .mask = i8259a_disable_irq, - .mask_ack = i8259a_mask_and_ack_irq, + .irq_unmask = i8259a_enable_irq, + .irq_mask = i8259a_disable_irq, + .irq_mask_ack = i8259a_mask_and_ack_irq, }; void __init diff --git a/arch/alpha/kernel/irq_impl.h b/arch/alpha/kernel/irq_impl.h index b63ccd7386f..d507a234b05 100644 --- a/arch/alpha/kernel/irq_impl.h +++ b/arch/alpha/kernel/irq_impl.h @@ -31,11 +31,9 @@ extern void init_rtc_irq(void); extern void common_init_isa_dma(void); -extern void i8259a_enable_irq(unsigned int); -extern void i8259a_disable_irq(unsigned int); -extern void i8259a_mask_and_ack_irq(unsigned int); -extern unsigned int i8259a_startup_irq(unsigned int); -extern void i8259a_end_irq(unsigned int); +extern void i8259a_enable_irq(struct irq_data *d); +extern void i8259a_disable_irq(struct irq_data *d); +extern void i8259a_mask_and_ack_irq(struct irq_data *d); extern struct irq_chip i8259a_irq_type; extern void init_i8259a_irqs(void); diff --git a/arch/alpha/kernel/irq_pyxis.c b/arch/alpha/kernel/irq_pyxis.c index 2863458c853..b30227fa7f5 100644 --- a/arch/alpha/kernel/irq_pyxis.c +++ b/arch/alpha/kernel/irq_pyxis.c @@ -29,21 +29,21 @@ pyxis_update_irq_hw(unsigned long mask) } static inline void -pyxis_enable_irq(unsigned int irq) +pyxis_enable_irq(struct irq_data *d) { - pyxis_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); + pyxis_update_irq_hw(cached_irq_mask |= 1UL << (d->irq - 16)); } static void -pyxis_disable_irq(unsigned int irq) +pyxis_disable_irq(struct irq_data *d) { - pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); + pyxis_update_irq_hw(cached_irq_mask &= ~(1UL << (d->irq - 16))); } static void -pyxis_mask_and_ack_irq(unsigned int irq) +pyxis_mask_and_ack_irq(struct irq_data *d) { - unsigned long bit = 1UL << (irq - 16); + unsigned long bit = 1UL << (d->irq - 16); unsigned long mask = cached_irq_mask &= ~bit; /* Disable the interrupt. */ @@ -58,9 +58,9 @@ pyxis_mask_and_ack_irq(unsigned int irq) static struct irq_chip pyxis_irq_type = { .name = "PYXIS", - .mask_ack = pyxis_mask_and_ack_irq, - .mask = pyxis_disable_irq, - .unmask = pyxis_enable_irq, + .irq_mask_ack = pyxis_mask_and_ack_irq, + .irq_mask = pyxis_disable_irq, + .irq_unmask = pyxis_enable_irq, }; void @@ -103,7 +103,7 @@ init_pyxis_irqs(unsigned long ignore_mask) if ((ignore_mask >> i) & 1) continue; set_irq_chip_and_handler(i, &pyxis_irq_type, handle_level_irq); - irq_to_desc(i)->status |= IRQ_LEVEL; + irq_set_status_flags(i, IRQ_LEVEL); } setup_irq(16+7, &isa_cascade_irqaction); diff --git a/arch/alpha/kernel/irq_srm.c b/arch/alpha/kernel/irq_srm.c index 0e57e828b41..82a47bba41c 100644 --- a/arch/alpha/kernel/irq_srm.c +++ b/arch/alpha/kernel/irq_srm.c @@ -18,27 +18,27 @@ DEFINE_SPINLOCK(srm_irq_lock); static inline void -srm_enable_irq(unsigned int irq) +srm_enable_irq(struct irq_data *d) { spin_lock(&srm_irq_lock); - cserve_ena(irq - 16); + cserve_ena(d->irq - 16); spin_unlock(&srm_irq_lock); } static void -srm_disable_irq(unsigned int irq) +srm_disable_irq(struct irq_data *d) { spin_lock(&srm_irq_lock); - cserve_dis(irq - 16); + cserve_dis(d->irq - 16); spin_unlock(&srm_irq_lock); } /* Handle interrupts from the SRM, assuming no additional weirdness. */ static struct irq_chip srm_irq_type = { .name = "SRM", - .unmask = srm_enable_irq, - .mask = srm_disable_irq, - .mask_ack = srm_disable_irq, + .irq_unmask = srm_enable_irq, + .irq_mask = srm_disable_irq, + .irq_mask_ack = srm_disable_irq, }; void __init @@ -52,7 +52,7 @@ init_srm_irqs(long max, unsigned long ignore_mask) if (i < 64 && ((ignore_mask >> i) & 1)) continue; set_irq_chip_and_handler(i, &srm_irq_type, handle_level_irq); - irq_to_desc(i)->status |= IRQ_LEVEL; + irq_set_status_flags(i, IRQ_LEVEL); } } diff --git a/arch/alpha/kernel/sys_alcor.c b/arch/alpha/kernel/sys_alcor.c index 7bef6176823..88d95e872f5 100644 --- a/arch/alpha/kernel/sys_alcor.c +++ b/arch/alpha/kernel/sys_alcor.c @@ -44,31 +44,31 @@ alcor_update_irq_hw(unsigned long mask) } static inline void -alcor_enable_irq(unsigned int irq) +alcor_enable_irq(struct irq_data *d) { - alcor_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); + alcor_update_irq_hw(cached_irq_mask |= 1UL << (d->irq - 16)); } static void -alcor_disable_irq(unsigned int irq) +alcor_disable_irq(struct irq_data *d) { - alcor_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); + alcor_update_irq_hw(cached_irq_mask &= ~(1UL << (d->irq - 16))); } static void -alcor_mask_and_ack_irq(unsigned int irq) +alcor_mask_and_ack_irq(struct irq_data *d) { - alcor_disable_irq(irq); + alcor_disable_irq(d); /* On ALCOR/XLT, need to dismiss interrupt via GRU. */ - *(vuip)GRU_INT_CLEAR = 1 << (irq - 16); mb(); + *(vuip)GRU_INT_CLEAR = 1 << (d->irq - 16); mb(); *(vuip)GRU_INT_CLEAR = 0; mb(); } static void -alcor_isa_mask_and_ack_irq(unsigned int irq) +alcor_isa_mask_and_ack_irq(struct irq_data *d) { - i8259a_mask_and_ack_irq(irq); + i8259a_mask_and_ack_irq(d); /* On ALCOR/XLT, need to dismiss interrupt via GRU. */ *(vuip)GRU_INT_CLEAR = 0x80000000; mb(); @@ -77,9 +77,9 @@ alcor_isa_mask_and_ack_irq(unsigned int irq) static struct irq_chip alcor_irq_type = { .name = "ALCOR", - .unmask = alcor_enable_irq, - .mask = alcor_disable_irq, - .mask_ack = alcor_mask_and_ack_irq, + .irq_unmask = alcor_enable_irq, + .irq_mask = alcor_disable_irq, + .irq_mask_ack = alcor_mask_and_ack_irq, }; static void @@ -126,9 +126,9 @@ alcor_init_irq(void) if (i >= 16+20 && i <= 16+30) continue; set_irq_chip_and_handler(i, &alcor_irq_type, handle_level_irq); - irq_to_desc(i)->status |= IRQ_LEVEL; + irq_set_status_flags(i, IRQ_LEVEL); } - i8259a_irq_type.ack = alcor_isa_mask_and_ack_irq; + i8259a_irq_type.irq_ack = alcor_isa_mask_and_ack_irq; init_i8259a_irqs(); common_init_isa_dma(); diff --git a/arch/alpha/kernel/sys_cabriolet.c b/arch/alpha/kernel/sys_cabriolet.c index b0c916493ae..57eb6307bc2 100644 --- a/arch/alpha/kernel/sys_cabriolet.c +++ b/arch/alpha/kernel/sys_cabriolet.c @@ -46,22 +46,22 @@ cabriolet_update_irq_hw(unsigned int irq, unsigned long mask) } static inline void -cabriolet_enable_irq(unsigned int irq) +cabriolet_enable_irq(struct irq_data *d) { - cabriolet_update_irq_hw(irq, cached_irq_mask &= ~(1UL << irq)); + cabriolet_update_irq_hw(d->irq, cached_irq_mask &= ~(1UL << d->irq)); } static void -cabriolet_disable_irq(unsigned int irq) +cabriolet_disable_irq(struct irq_data *d) { - cabriolet_update_irq_hw(irq, cached_irq_mask |= 1UL << irq); + cabriolet_update_irq_hw(d->irq, cached_irq_mask |= 1UL << d->irq); } static struct irq_chip cabriolet_irq_type = { .name = "CABRIOLET", - .unmask = cabriolet_enable_irq, - .mask = cabriolet_disable_irq, - .mask_ack = cabriolet_disable_irq, + .irq_unmask = cabriolet_enable_irq, + .irq_mask = cabriolet_disable_irq, + .irq_mask_ack = cabriolet_disable_irq, }; static void @@ -107,7 +107,7 @@ common_init_irq(void (*srm_dev_int)(unsigned long v)) for (i = 16; i < 35; ++i) { set_irq_chip_and_handler(i, &cabriolet_irq_type, handle_level_irq); - irq_to_desc(i)->status |= IRQ_LEVEL; + irq_set_status_flags(i, IRQ_LEVEL); } } diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index edad5f759cc..481df4ecb65 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -98,37 +98,37 @@ tsunami_update_irq_hw(unsigned long mask) } static void -dp264_enable_irq(unsigned int irq) +dp264_enable_irq(struct irq_data *d) { spin_lock(&dp264_irq_lock); - cached_irq_mask |= 1UL << irq; + cached_irq_mask |= 1UL << d->irq; tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } static void -dp264_disable_irq(unsigned int irq) +dp264_disable_irq(struct irq_data *d) { spin_lock(&dp264_irq_lock); - cached_irq_mask &= ~(1UL << irq); + cached_irq_mask &= ~(1UL << d->irq); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } static void -clipper_enable_irq(unsigned int irq) +clipper_enable_irq(struct irq_data *d) { spin_lock(&dp264_irq_lock); - cached_irq_mask |= 1UL << (irq - 16); + cached_irq_mask |= 1UL << (d->irq - 16); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } static void -clipper_disable_irq(unsigned int irq) +clipper_disable_irq(struct irq_data *d) { spin_lock(&dp264_irq_lock); - cached_irq_mask &= ~(1UL << (irq - 16)); + cached_irq_mask &= ~(1UL << (d->irq - 16)); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); } @@ -149,10 +149,11 @@ cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) } static int -dp264_set_affinity(unsigned int irq, const struct cpumask *affinity) -{ +dp264_set_affinity(struct irq_data *d, const struct cpumask *affinity, + bool force) +{ spin_lock(&dp264_irq_lock); - cpu_set_irq_affinity(irq, *affinity); + cpu_set_irq_affinity(d->irq, *affinity); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); @@ -160,10 +161,11 @@ dp264_set_affinity(unsigned int irq, const struct cpumask *affinity) } static int -clipper_set_affinity(unsigned int irq, const struct cpumask *affinity) -{ +clipper_set_affinity(struct irq_data *d, const struct cpumask *affinity, + bool force) +{ spin_lock(&dp264_irq_lock); - cpu_set_irq_affinity(irq - 16, *affinity); + cpu_set_irq_affinity(d->irq - 16, *affinity); tsunami_update_irq_hw(cached_irq_mask); spin_unlock(&dp264_irq_lock); @@ -171,19 +173,19 @@ clipper_set_affinity(unsigned int irq, const struct cpumask *affinity) } static struct irq_chip dp264_irq_type = { - .name = "DP264", - .unmask = dp264_enable_irq, - .mask = dp264_disable_irq, - .mask_ack = dp264_disable_irq, - .set_affinity = dp264_set_affinity, + .name = "DP264", + .irq_unmask = dp264_enable_irq, + .irq_mask = dp264_disable_irq, + .irq_mask_ack = dp264_disable_irq, + .irq_set_affinity = dp264_set_affinity, }; static struct irq_chip clipper_irq_type = { - .name = "CLIPPER", - .unmask = clipper_enable_irq, - .mask = clipper_disable_irq, - .mask_ack = clipper_disable_irq, - .set_affinity = clipper_set_affinity, + .name = "CLIPPER", + .irq_unmask = clipper_enable_irq, + .irq_mask = clipper_disable_irq, + .irq_mask_ack = clipper_disable_irq, + .irq_set_affinity = clipper_set_affinity, }; static void @@ -268,8 +270,8 @@ init_tsunami_irqs(struct irq_chip * ops, int imin, int imax) { long i; for (i = imin; i <= imax; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, ops, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } } diff --git a/arch/alpha/kernel/sys_eb64p.c b/arch/alpha/kernel/sys_eb64p.c index ae5f29d127b..402e908ffb3 100644 --- a/arch/alpha/kernel/sys_eb64p.c +++ b/arch/alpha/kernel/sys_eb64p.c @@ -44,22 +44,22 @@ eb64p_update_irq_hw(unsigned int irq, unsigned long mask) } static inline void -eb64p_enable_irq(unsigned int irq) +eb64p_enable_irq(struct irq_data *d) { - eb64p_update_irq_hw(irq, cached_irq_mask &= ~(1 << irq)); + eb64p_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << d->irq)); } static void -eb64p_disable_irq(unsigned int irq) +eb64p_disable_irq(struct irq_data *d) { - eb64p_update_irq_hw(irq, cached_irq_mask |= 1 << irq); + eb64p_update_irq_hw(d->irq, cached_irq_mask |= 1 << d->irq); } static struct irq_chip eb64p_irq_type = { .name = "EB64P", - .unmask = eb64p_enable_irq, - .mask = eb64p_disable_irq, - .mask_ack = eb64p_disable_irq, + .irq_unmask = eb64p_enable_irq, + .irq_mask = eb64p_disable_irq, + .irq_mask_ack = eb64p_disable_irq, }; static void @@ -118,9 +118,9 @@ eb64p_init_irq(void) init_i8259a_irqs(); for (i = 16; i < 32; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &eb64p_irq_type, handle_level_irq); - } + irq_set_status_flags(i, IRQ_LEVEL); + } common_init_isa_dma(); setup_irq(16+5, &isa_cascade_irqaction); diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c index 1121bc5c6c6..0b44a54c152 100644 --- a/arch/alpha/kernel/sys_eiger.c +++ b/arch/alpha/kernel/sys_eiger.c @@ -51,16 +51,18 @@ eiger_update_irq_hw(unsigned long irq, unsigned long mask) } static inline void -eiger_enable_irq(unsigned int irq) +eiger_enable_irq(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long mask; mask = (cached_irq_mask[irq >= 64] &= ~(1UL << (irq & 63))); eiger_update_irq_hw(irq, mask); } static void -eiger_disable_irq(unsigned int irq) +eiger_disable_irq(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long mask; mask = (cached_irq_mask[irq >= 64] |= 1UL << (irq & 63)); eiger_update_irq_hw(irq, mask); @@ -68,9 +70,9 @@ eiger_disable_irq(unsigned int irq) static struct irq_chip eiger_irq_type = { .name = "EIGER", - .unmask = eiger_enable_irq, - .mask = eiger_disable_irq, - .mask_ack = eiger_disable_irq, + .irq_unmask = eiger_enable_irq, + .irq_mask = eiger_disable_irq, + .irq_mask_ack = eiger_disable_irq, }; static void @@ -136,8 +138,8 @@ eiger_init_irq(void) init_i8259a_irqs(); for (i = 16; i < 128; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &eiger_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } } diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c index 34f55e03d33..00341b75c8b 100644 --- a/arch/alpha/kernel/sys_jensen.c +++ b/arch/alpha/kernel/sys_jensen.c @@ -63,34 +63,34 @@ */ static void -jensen_local_enable(unsigned int irq) +jensen_local_enable(struct irq_data *d) { /* the parport is really hw IRQ 1, silly Jensen. */ - if (irq == 7) - i8259a_enable_irq(1); + if (d->irq == 7) + i8259a_enable_irq(d); } static void -jensen_local_disable(unsigned int irq) +jensen_local_disable(struct irq_data *d) { /* the parport is really hw IRQ 1, silly Jensen. */ - if (irq == 7) - i8259a_disable_irq(1); + if (d->irq == 7) + i8259a_disable_irq(d); } static void -jensen_local_mask_ack(unsigned int irq) +jensen_local_mask_ack(struct irq_data *d) { /* the parport is really hw IRQ 1, silly Jensen. */ - if (irq == 7) - i8259a_mask_and_ack_irq(1); + if (d->irq == 7) + i8259a_mask_and_ack_irq(d); } static struct irq_chip jensen_local_irq_type = { .name = "LOCAL", - .unmask = jensen_local_enable, - .mask = jensen_local_disable, - .mask_ack = jensen_local_mask_ack, + .irq_unmask = jensen_local_enable, + .irq_mask = jensen_local_disable, + .irq_mask_ack = jensen_local_mask_ack, }; static void diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c index 2bfc9f1b1dd..e61910734e4 100644 --- a/arch/alpha/kernel/sys_marvel.c +++ b/arch/alpha/kernel/sys_marvel.c @@ -104,9 +104,10 @@ io7_get_irq_ctl(unsigned int irq, struct io7 **pio7) } static void -io7_enable_irq(unsigned int irq) +io7_enable_irq(struct irq_data *d) { volatile unsigned long *ctl; + unsigned int irq = d->irq; struct io7 *io7; ctl = io7_get_irq_ctl(irq, &io7); @@ -115,7 +116,7 @@ io7_enable_irq(unsigned int irq) __func__, irq); return; } - + spin_lock(&io7->irq_lock); *ctl |= 1UL << 24; mb(); @@ -124,9 +125,10 @@ io7_enable_irq(unsigned int irq) } static void -io7_disable_irq(unsigned int irq) +io7_disable_irq(struct irq_data *d) { volatile unsigned long *ctl; + unsigned int irq = d->irq; struct io7 *io7; ctl = io7_get_irq_ctl(irq, &io7); @@ -135,7 +137,7 @@ io7_disable_irq(unsigned int irq) __func__, irq); return; } - + spin_lock(&io7->irq_lock); *ctl &= ~(1UL << 24); mb(); @@ -144,35 +146,29 @@ io7_disable_irq(unsigned int irq) } static void -marvel_irq_noop(unsigned int irq) -{ - return; -} - -static unsigned int -marvel_irq_noop_return(unsigned int irq) -{ - return 0; +marvel_irq_noop(struct irq_data *d) +{ + return; } static struct irq_chip marvel_legacy_irq_type = { .name = "LEGACY", - .mask = marvel_irq_noop, - .unmask = marvel_irq_noop, + .irq_mask = marvel_irq_noop, + .irq_unmask = marvel_irq_noop, }; static struct irq_chip io7_lsi_irq_type = { .name = "LSI", - .unmask = io7_enable_irq, - .mask = io7_disable_irq, - .mask_ack = io7_disable_irq, + .irq_unmask = io7_enable_irq, + .irq_mask = io7_disable_irq, + .irq_mask_ack = io7_disable_irq, }; static struct irq_chip io7_msi_irq_type = { .name = "MSI", - .unmask = io7_enable_irq, - .mask = io7_disable_irq, - .ack = marvel_irq_noop, + .irq_unmask = io7_enable_irq, + .irq_mask = io7_disable_irq, + .irq_ack = marvel_irq_noop, }; static void @@ -280,8 +276,8 @@ init_io7_irqs(struct io7 *io7, /* Set up the lsi irqs. */ for (i = 0; i < 128; ++i) { - irq_to_desc(base + i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(base + i, lsi_ops, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } /* Disable the implemented irqs in hardware. */ @@ -294,8 +290,8 @@ init_io7_irqs(struct io7 *io7, /* Set up the msi irqs. */ for (i = 128; i < (128 + 512); ++i) { - irq_to_desc(base + i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(base + i, msi_ops, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } for (i = 0; i < 16; ++i) diff --git a/arch/alpha/kernel/sys_mikasa.c b/arch/alpha/kernel/sys_mikasa.c index bcc1639e8ef..cf7f43dd314 100644 --- a/arch/alpha/kernel/sys_mikasa.c +++ b/arch/alpha/kernel/sys_mikasa.c @@ -43,22 +43,22 @@ mikasa_update_irq_hw(int mask) } static inline void -mikasa_enable_irq(unsigned int irq) +mikasa_enable_irq(struct irq_data *d) { - mikasa_update_irq_hw(cached_irq_mask |= 1 << (irq - 16)); + mikasa_update_irq_hw(cached_irq_mask |= 1 << (d->irq - 16)); } static void -mikasa_disable_irq(unsigned int irq) +mikasa_disable_irq(struct irq_data *d) { - mikasa_update_irq_hw(cached_irq_mask &= ~(1 << (irq - 16))); + mikasa_update_irq_hw(cached_irq_mask &= ~(1 << (d->irq - 16))); } static struct irq_chip mikasa_irq_type = { .name = "MIKASA", - .unmask = mikasa_enable_irq, - .mask = mikasa_disable_irq, - .mask_ack = mikasa_disable_irq, + .irq_unmask = mikasa_enable_irq, + .irq_mask = mikasa_disable_irq, + .irq_mask_ack = mikasa_disable_irq, }; static void @@ -98,8 +98,8 @@ mikasa_init_irq(void) mikasa_update_irq_hw(0); for (i = 16; i < 32; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &mikasa_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } init_i8259a_irqs(); diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c index e88f4ae1260..92bc188e94a 100644 --- a/arch/alpha/kernel/sys_noritake.c +++ b/arch/alpha/kernel/sys_noritake.c @@ -48,22 +48,22 @@ noritake_update_irq_hw(int irq, int mask) } static void -noritake_enable_irq(unsigned int irq) +noritake_enable_irq(struct irq_data *d) { - noritake_update_irq_hw(irq, cached_irq_mask |= 1 << (irq - 16)); + noritake_update_irq_hw(d->irq, cached_irq_mask |= 1 << (d->irq - 16)); } static void -noritake_disable_irq(unsigned int irq) +noritake_disable_irq(struct irq_data *d) { - noritake_update_irq_hw(irq, cached_irq_mask &= ~(1 << (irq - 16))); + noritake_update_irq_hw(d->irq, cached_irq_mask &= ~(1 << (d->irq - 16))); } static struct irq_chip noritake_irq_type = { .name = "NORITAKE", - .unmask = noritake_enable_irq, - .mask = noritake_disable_irq, - .mask_ack = noritake_disable_irq, + .irq_unmask = noritake_enable_irq, + .irq_mask = noritake_disable_irq, + .irq_mask_ack = noritake_disable_irq, }; static void @@ -127,8 +127,8 @@ noritake_init_irq(void) outw(0, 0x54c); for (i = 16; i < 48; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &noritake_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } init_i8259a_irqs(); diff --git a/arch/alpha/kernel/sys_rawhide.c b/arch/alpha/kernel/sys_rawhide.c index 6a51364dd1c..936d4140ed5 100644 --- a/arch/alpha/kernel/sys_rawhide.c +++ b/arch/alpha/kernel/sys_rawhide.c @@ -56,9 +56,10 @@ rawhide_update_irq_hw(int hose, int mask) (((h) < MCPCIA_MAX_HOSES) && (cached_irq_masks[(h)] != 0)) static inline void -rawhide_enable_irq(unsigned int irq) +rawhide_enable_irq(struct irq_data *d) { unsigned int mask, hose; + unsigned int irq = d->irq; irq -= 16; hose = irq / 24; @@ -76,9 +77,10 @@ rawhide_enable_irq(unsigned int irq) } static void -rawhide_disable_irq(unsigned int irq) +rawhide_disable_irq(struct irq_data *d) { unsigned int mask, hose; + unsigned int irq = d->irq; irq -= 16; hose = irq / 24; @@ -96,9 +98,10 @@ rawhide_disable_irq(unsigned int irq) } static void -rawhide_mask_and_ack_irq(unsigned int irq) +rawhide_mask_and_ack_irq(struct irq_data *d) { unsigned int mask, mask1, hose; + unsigned int irq = d->irq; irq -= 16; hose = irq / 24; @@ -123,9 +126,9 @@ rawhide_mask_and_ack_irq(unsigned int irq) static struct irq_chip rawhide_irq_type = { .name = "RAWHIDE", - .unmask = rawhide_enable_irq, - .mask = rawhide_disable_irq, - .mask_ack = rawhide_mask_and_ack_irq, + .irq_unmask = rawhide_enable_irq, + .irq_mask = rawhide_disable_irq, + .irq_mask_ack = rawhide_mask_and_ack_irq, }; static void @@ -177,8 +180,8 @@ rawhide_init_irq(void) } for (i = 16; i < 128; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &rawhide_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } init_i8259a_irqs(); diff --git a/arch/alpha/kernel/sys_rx164.c b/arch/alpha/kernel/sys_rx164.c index 89e7e37ec84..cea22a62913 100644 --- a/arch/alpha/kernel/sys_rx164.c +++ b/arch/alpha/kernel/sys_rx164.c @@ -47,22 +47,22 @@ rx164_update_irq_hw(unsigned long mask) } static inline void -rx164_enable_irq(unsigned int irq) +rx164_enable_irq(struct irq_data *d) { - rx164_update_irq_hw(cached_irq_mask |= 1UL << (irq - 16)); + rx164_update_irq_hw(cached_irq_mask |= 1UL << (d->irq - 16)); } static void -rx164_disable_irq(unsigned int irq) +rx164_disable_irq(struct irq_data *d) { - rx164_update_irq_hw(cached_irq_mask &= ~(1UL << (irq - 16))); + rx164_update_irq_hw(cached_irq_mask &= ~(1UL << (d->irq - 16))); } static struct irq_chip rx164_irq_type = { .name = "RX164", - .unmask = rx164_enable_irq, - .mask = rx164_disable_irq, - .mask_ack = rx164_disable_irq, + .irq_unmask = rx164_enable_irq, + .irq_mask = rx164_disable_irq, + .irq_mask_ack = rx164_disable_irq, }; static void @@ -99,8 +99,8 @@ rx164_init_irq(void) rx164_update_irq_hw(0); for (i = 16; i < 40; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &rx164_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } init_i8259a_irqs(); diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c index 5c4423d1b06..a349538aabc 100644 --- a/arch/alpha/kernel/sys_sable.c +++ b/arch/alpha/kernel/sys_sable.c @@ -443,11 +443,11 @@ lynx_swizzle(struct pci_dev *dev, u8 *pinp) /* GENERIC irq routines */ static inline void -sable_lynx_enable_irq(unsigned int irq) +sable_lynx_enable_irq(struct irq_data *d) { unsigned long bit, mask; - bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; + bit = sable_lynx_irq_swizzle->irq_to_mask[d->irq]; spin_lock(&sable_lynx_irq_lock); mask = sable_lynx_irq_swizzle->shadow_mask &= ~(1UL << bit); sable_lynx_irq_swizzle->update_irq_hw(bit, mask); @@ -459,11 +459,11 @@ sable_lynx_enable_irq(unsigned int irq) } static void -sable_lynx_disable_irq(unsigned int irq) +sable_lynx_disable_irq(struct irq_data *d) { unsigned long bit, mask; - bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; + bit = sable_lynx_irq_swizzle->irq_to_mask[d->irq]; spin_lock(&sable_lynx_irq_lock); mask = sable_lynx_irq_swizzle->shadow_mask |= 1UL << bit; sable_lynx_irq_swizzle->update_irq_hw(bit, mask); @@ -475,11 +475,11 @@ sable_lynx_disable_irq(unsigned int irq) } static void -sable_lynx_mask_and_ack_irq(unsigned int irq) +sable_lynx_mask_and_ack_irq(struct irq_data *d) { unsigned long bit, mask; - bit = sable_lynx_irq_swizzle->irq_to_mask[irq]; + bit = sable_lynx_irq_swizzle->irq_to_mask[d->irq]; spin_lock(&sable_lynx_irq_lock); mask = sable_lynx_irq_swizzle->shadow_mask |= 1UL << bit; sable_lynx_irq_swizzle->update_irq_hw(bit, mask); @@ -489,9 +489,9 @@ sable_lynx_mask_and_ack_irq(unsigned int irq) static struct irq_chip sable_lynx_irq_type = { .name = "SABLE/LYNX", - .unmask = sable_lynx_enable_irq, - .mask = sable_lynx_disable_irq, - .mask_ack = sable_lynx_mask_and_ack_irq, + .irq_unmask = sable_lynx_enable_irq, + .irq_mask = sable_lynx_disable_irq, + .irq_mask_ack = sable_lynx_mask_and_ack_irq, }; static void @@ -518,9 +518,9 @@ sable_lynx_init_irq(int nr_of_irqs) long i; for (i = 0; i < nr_of_irqs; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &sable_lynx_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } common_init_isa_dma(); diff --git a/arch/alpha/kernel/sys_takara.c b/arch/alpha/kernel/sys_takara.c index f8a1e8a862f..42a5331f13c 100644 --- a/arch/alpha/kernel/sys_takara.c +++ b/arch/alpha/kernel/sys_takara.c @@ -45,16 +45,18 @@ takara_update_irq_hw(unsigned long irq, unsigned long mask) } static inline void -takara_enable_irq(unsigned int irq) +takara_enable_irq(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long mask; mask = (cached_irq_mask[irq >= 64] &= ~(1UL << (irq & 63))); takara_update_irq_hw(irq, mask); } static void -takara_disable_irq(unsigned int irq) +takara_disable_irq(struct irq_data *d) { + unsigned int irq = d->irq; unsigned long mask; mask = (cached_irq_mask[irq >= 64] |= 1UL << (irq & 63)); takara_update_irq_hw(irq, mask); @@ -62,9 +64,9 @@ takara_disable_irq(unsigned int irq) static struct irq_chip takara_irq_type = { .name = "TAKARA", - .unmask = takara_enable_irq, - .mask = takara_disable_irq, - .mask_ack = takara_disable_irq, + .irq_unmask = takara_enable_irq, + .irq_mask = takara_disable_irq, + .irq_mask_ack = takara_disable_irq, }; static void @@ -136,8 +138,8 @@ takara_init_irq(void) takara_update_irq_hw(i, -1); for (i = 16; i < 128; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, &takara_irq_type, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } common_init_isa_dma(); diff --git a/arch/alpha/kernel/sys_titan.c b/arch/alpha/kernel/sys_titan.c index e02494bf5ef..f6c108a3d67 100644 --- a/arch/alpha/kernel/sys_titan.c +++ b/arch/alpha/kernel/sys_titan.c @@ -112,8 +112,9 @@ titan_update_irq_hw(unsigned long mask) } static inline void -titan_enable_irq(unsigned int irq) +titan_enable_irq(struct irq_data *d) { + unsigned int irq = d->irq; spin_lock(&titan_irq_lock); titan_cached_irq_mask |= 1UL << (irq - 16); titan_update_irq_hw(titan_cached_irq_mask); @@ -121,8 +122,9 @@ titan_enable_irq(unsigned int irq) } static inline void -titan_disable_irq(unsigned int irq) +titan_disable_irq(struct irq_data *d) { + unsigned int irq = d->irq; spin_lock(&titan_irq_lock); titan_cached_irq_mask &= ~(1UL << (irq - 16)); titan_update_irq_hw(titan_cached_irq_mask); @@ -144,7 +146,8 @@ titan_cpu_set_irq_affinity(unsigned int irq, cpumask_t affinity) } static int -titan_set_irq_affinity(unsigned int irq, const struct cpumask *affinity) +titan_set_irq_affinity(struct irq_data *d, const struct cpumask *affinity, + bool force) { spin_lock(&titan_irq_lock); titan_cpu_set_irq_affinity(irq - 16, *affinity); @@ -175,17 +178,17 @@ init_titan_irqs(struct irq_chip * ops, int imin, int imax) { long i; for (i = imin; i <= imax; ++i) { - irq_to_desc(i)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i, ops, handle_level_irq); + irq_set_status_flags(i, IRQ_LEVEL); } } static struct irq_chip titan_irq_type = { - .name = "TITAN", - .unmask = titan_enable_irq, - .mask = titan_disable_irq, - .mask_ack = titan_disable_irq, - .set_affinity = titan_set_irq_affinity, + .name = "TITAN", + .irq_unmask = titan_enable_irq, + .irq_mask = titan_disable_irq, + .irq_mask_ack = titan_disable_irq, + .irq_set_affinity = titan_set_irq_affinity, }; static irqreturn_t diff --git a/arch/alpha/kernel/sys_wildfire.c b/arch/alpha/kernel/sys_wildfire.c index eec52594d41..ca60a387ef0 100644 --- a/arch/alpha/kernel/sys_wildfire.c +++ b/arch/alpha/kernel/sys_wildfire.c @@ -104,10 +104,12 @@ wildfire_init_irq_hw(void) } static void -wildfire_enable_irq(unsigned int irq) +wildfire_enable_irq(struct irq_data *d) { + unsigned int irq = d->irq; + if (irq < 16) - i8259a_enable_irq(irq); + i8259a_enable_irq(d); spin_lock(&wildfire_irq_lock); set_bit(irq, &cached_irq_mask); @@ -116,10 +118,12 @@ wildfire_enable_irq(unsigned int irq) } static void -wildfire_disable_irq(unsigned int irq) +wildfire_disable_irq(struct irq_data *d) { + unsigned int irq = d->irq; + if (irq < 16) - i8259a_disable_irq(irq); + i8259a_disable_irq(d); spin_lock(&wildfire_irq_lock); clear_bit(irq, &cached_irq_mask); @@ -128,10 +132,12 @@ wildfire_disable_irq(unsigned int irq) } static void -wildfire_mask_and_ack_irq(unsigned int irq) +wildfire_mask_and_ack_irq(struct irq_data *d) { + unsigned int irq = d->irq; + if (irq < 16) - i8259a_mask_and_ack_irq(irq); + i8259a_mask_and_ack_irq(d); spin_lock(&wildfire_irq_lock); clear_bit(irq, &cached_irq_mask); @@ -141,9 +147,9 @@ wildfire_mask_and_ack_irq(unsigned int irq) static struct irq_chip wildfire_irq_type = { .name = "WILDFIRE", - .unmask = wildfire_enable_irq, - .mask = wildfire_disable_irq, - .mask_ack = wildfire_mask_and_ack_irq, + .irq_unmask = wildfire_enable_irq, + .irq_mask = wildfire_disable_irq, + .irq_mask_ack = wildfire_mask_and_ack_irq, }; static void __init @@ -177,21 +183,21 @@ wildfire_init_irq_per_pca(int qbbno, int pcano) for (i = 0; i < 16; ++i) { if (i == 2) continue; - irq_to_desc(i+irq_bias)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i+irq_bias, &wildfire_irq_type, handle_level_irq); + irq_set_status_flags(i + irq_bias, IRQ_LEVEL); } - irq_to_desc(36+irq_bias)->status |= IRQ_LEVEL; set_irq_chip_and_handler(36+irq_bias, &wildfire_irq_type, handle_level_irq); + irq_set_status_flags(36 + irq_bias, IRQ_LEVEL); for (i = 40; i < 64; ++i) { - irq_to_desc(i+irq_bias)->status |= IRQ_LEVEL; set_irq_chip_and_handler(i+irq_bias, &wildfire_irq_type, handle_level_irq); + irq_set_status_flags(i + irq_bias, IRQ_LEVEL); } - setup_irq(32+irq_bias, &isa_enable); + setup_irq(32+irq_bias, &isa_enable); } static void __init diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 26d45e5b636..166efa2a19c 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1177,6 +1177,31 @@ config ARM_ERRATA_743622 visible impact on the overall performance or power consumption of the processor. +config ARM_ERRATA_751472 + bool "ARM errata: Interrupted ICIALLUIS may prevent completion of broadcasted operation" + depends on CPU_V7 && SMP + help + This option enables the workaround for the 751472 Cortex-A9 (prior + to r3p0) erratum. An interrupted ICIALLUIS operation may prevent the + completion of a following broadcasted operation if the second + operation is received by a CPU before the ICIALLUIS has completed, + potentially leading to corrupted entries in the cache or TLB. + +config ARM_ERRATA_753970 + bool "ARM errata: cache sync operation may be faulty" + depends on CACHE_PL310 + help + This option enables the workaround for the 753970 PL310 (r3p0) erratum. + + Under some condition the effect of cache sync operation on + the store buffer still remains when the operation completes. + This means that the store buffer is always asked to drain and + this prevents it from merging any further writes. The workaround + is to replace the normal offset of cache sync operation (0x730) + by another offset targeting an unmapped PL310 register 0x740. + This has the same effect as the cache sync operation: store buffer + drain and waiting for all buffers empty. + endmenu source "arch/arm/common/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c22c1adfedd..6f7b29294c8 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -15,7 +15,7 @@ ifeq ($(CONFIG_CPU_ENDIAN_BE8),y) LDFLAGS_vmlinux += --be8 endif -OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S +OBJCOPYFLAGS :=-O binary -R .comment -S GZFLAGS :=-9 #KBUILD_CFLAGS +=-pipe # Explicitly specifiy 32-bit ARM ISA since toolchain default can be -mthumb: diff --git a/arch/arm/boot/compressed/.gitignore b/arch/arm/boot/compressed/.gitignore index ab204db594d..c6028967d33 100644 --- a/arch/arm/boot/compressed/.gitignore +++ b/arch/arm/boot/compressed/.gitignore @@ -1,3 +1,7 @@ font.c -piggy.gz +lib1funcs.S +piggy.gzip +piggy.lzo +piggy.lzma +vmlinux vmlinux.lds diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 5aeec1e1735..16bd4803158 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -36,6 +36,7 @@ #define L2X0_RAW_INTR_STAT 0x21C #define L2X0_INTR_CLEAR 0x220 #define L2X0_CACHE_SYNC 0x730 +#define L2X0_DUMMY_REG 0x740 #define L2X0_INV_LINE_PA 0x770 #define L2X0_INV_WAY 0x77C #define L2X0_CLEAN_LINE_PA 0x7B0 diff --git a/arch/arm/include/asm/hardware/sp810.h b/arch/arm/include/asm/hardware/sp810.h index 721847dc68a..e0d1c0cfa54 100644 --- a/arch/arm/include/asm/hardware/sp810.h +++ b/arch/arm/include/asm/hardware/sp810.h @@ -58,6 +58,9 @@ static inline void sysctl_soft_reset(void __iomem *base) { + /* switch to slow mode */ + writel(0x2, base + SCCTRL); + /* writing any value to SCSYSSTAT reg will reset system */ writel(0, base + SCSYSSTAT); } diff --git a/arch/arm/include/asm/tlb.h b/arch/arm/include/asm/tlb.h index f41a6f57cd1..82dfe5d0c41 100644 --- a/arch/arm/include/asm/tlb.h +++ b/arch/arm/include/asm/tlb.h @@ -18,16 +18,34 @@ #define __ASMARM_TLB_H #include <asm/cacheflush.h> -#include <asm/tlbflush.h> #ifndef CONFIG_MMU #include <linux/pagemap.h> + +#define tlb_flush(tlb) ((void) tlb) + #include <asm-generic/tlb.h> #else /* !CONFIG_MMU */ +#include <linux/swap.h> #include <asm/pgalloc.h> +#include <asm/tlbflush.h> + +/* + * We need to delay page freeing for SMP as other CPUs can access pages + * which have been removed but not yet had their TLB entries invalidated. + * Also, as ARMv7 speculative prefetch can drag new entries into the TLB, + * we need to apply this same delaying tactic to ensure correct operation. + */ +#if defined(CONFIG_SMP) || defined(CONFIG_CPU_32v7) +#define tlb_fast_mode(tlb) 0 +#define FREE_PTE_NR 500 +#else +#define tlb_fast_mode(tlb) 1 +#define FREE_PTE_NR 0 +#endif /* * TLB handling. This allows us to remove pages from the page @@ -36,12 +54,58 @@ struct mmu_gather { struct mm_struct *mm; unsigned int fullmm; + struct vm_area_struct *vma; unsigned long range_start; unsigned long range_end; + unsigned int nr; + struct page *pages[FREE_PTE_NR]; }; DECLARE_PER_CPU(struct mmu_gather, mmu_gathers); +/* + * This is unnecessarily complex. There's three ways the TLB shootdown + * code is used: + * 1. Unmapping a range of vmas. See zap_page_range(), unmap_region(). + * tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called. + * tlb->vma will be non-NULL. + * 2. Unmapping all vmas. See exit_mmap(). + * tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called. + * tlb->vma will be non-NULL. Additionally, page tables will be freed. + * 3. Unmapping argument pages. See shift_arg_pages(). + * tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called. + * tlb->vma will be NULL. + */ +static inline void tlb_flush(struct mmu_gather *tlb) +{ + if (tlb->fullmm || !tlb->vma) + flush_tlb_mm(tlb->mm); + else if (tlb->range_end > 0) { + flush_tlb_range(tlb->vma, tlb->range_start, tlb->range_end); + tlb->range_start = TASK_SIZE; + tlb->range_end = 0; + } +} + +static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr) +{ + if (!tlb->fullmm) { + if (addr < tlb->range_start) + tlb->range_start = addr; + if (addr + PAGE_SIZE > tlb->range_end) + tlb->range_end = addr + PAGE_SIZE; + } +} + +static inline void tlb_flush_mmu(struct mmu_gather *tlb) +{ + tlb_flush(tlb); + if (!tlb_fast_mode(tlb)) { + free_pages_and_swap_cache(tlb->pages, tlb->nr); + tlb->nr = 0; + } +} + static inline struct mmu_gather * tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) { @@ -49,6 +113,8 @@ tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) tlb->mm = mm; tlb->fullmm = full_mm_flush; + tlb->vma = NULL; + tlb->nr = 0; return tlb; } @@ -56,8 +122,7 @@ tlb_gather_mmu(struct mm_struct *mm, unsigned int full_mm_flush) static inline void tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) { - if (tlb->fullmm) - flush_tlb_mm(tlb->mm); + tlb_flush_mmu(tlb); /* keep the page table cache within bounds */ check_pgt_cache(); @@ -71,12 +136,7 @@ tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end) static inline void tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr) { - if (!tlb->fullmm) { - if (addr < tlb->range_start) - tlb->range_start = addr; - if (addr + PAGE_SIZE > tlb->range_end) - tlb->range_end = addr + PAGE_SIZE; - } + tlb_add_flush(tlb, addr); } /* @@ -89,6 +149,7 @@ tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) { if (!tlb->fullmm) { flush_cache_range(vma, vma->vm_start, vma->vm_end); + tlb->vma = vma; tlb->range_start = TASK_SIZE; tlb->range_end = 0; } @@ -97,12 +158,30 @@ tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) static inline void tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma) { - if (!tlb->fullmm && tlb->range_end > 0) - flush_tlb_range(vma, tlb->range_start, tlb->range_end); + if (!tlb->fullmm) + tlb_flush(tlb); +} + +static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page) +{ + if (tlb_fast_mode(tlb)) { + free_page_and_swap_cache(page); + } else { + tlb->pages[tlb->nr++] = page; + if (tlb->nr >= FREE_PTE_NR) + tlb_flush_mmu(tlb); + } +} + +static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte, + unsigned long addr) +{ + pgtable_page_dtor(pte); + tlb_add_flush(tlb, addr); + tlb_remove_page(tlb, pte); } -#define tlb_remove_page(tlb,page) free_page_and_swap_cache(page) -#define pte_free_tlb(tlb, ptep, addr) pte_free((tlb)->mm, ptep) +#define pte_free_tlb(tlb, ptep, addr) __pte_free_tlb(tlb, ptep, addr) #define pmd_free_tlb(tlb, pmdp, addr) pmd_free((tlb)->mm, pmdp) #define tlb_migrate_finish(mm) do { } while (0) diff --git a/arch/arm/include/asm/tlbflush.h b/arch/arm/include/asm/tlbflush.h index ce7378ea15a..d2005de383b 100644 --- a/arch/arm/include/asm/tlbflush.h +++ b/arch/arm/include/asm/tlbflush.h @@ -10,12 +10,7 @@ #ifndef _ASMARM_TLBFLUSH_H #define _ASMARM_TLBFLUSH_H - -#ifndef CONFIG_MMU - -#define tlb_flush(tlb) ((void) tlb) - -#else /* CONFIG_MMU */ +#ifdef CONFIG_MMU #include <asm/glue.h> diff --git a/arch/arm/kernel/kprobes-decode.c b/arch/arm/kernel/kprobes-decode.c index 2c1f0050c9c..8f6ed43861f 100644 --- a/arch/arm/kernel/kprobes-decode.c +++ b/arch/arm/kernel/kprobes-decode.c @@ -1437,7 +1437,7 @@ arm_kprobe_decode_insn(kprobe_opcode_t insn, struct arch_specific_insn *asi) return space_cccc_1100_010x(insn, asi); - } else if ((insn & 0x0e000000) == 0x0c400000) { + } else if ((insn & 0x0e000000) == 0x0c000000) { return space_cccc_110x(insn, asi); diff --git a/arch/arm/kernel/pmu.c b/arch/arm/kernel/pmu.c index b8af96ea62e..2c79eec1926 100644 --- a/arch/arm/kernel/pmu.c +++ b/arch/arm/kernel/pmu.c @@ -97,28 +97,34 @@ set_irq_affinity(int irq, irq, cpu); return err; #else - return 0; + return -EINVAL; #endif } static int init_cpu_pmu(void) { - int i, err = 0; + int i, irqs, err = 0; struct platform_device *pdev = pmu_devices[ARM_PMU_DEVICE_CPU]; - if (!pdev) { - err = -ENODEV; - goto out; - } + if (!pdev) + return -ENODEV; + + irqs = pdev->num_resources; + + /* + * If we have a single PMU interrupt that we can't shift, assume that + * we're running on a uniprocessor machine and continue. + */ + if (irqs == 1 && !irq_can_set_affinity(platform_get_irq(pdev, 0))) + return 0; - for (i = 0; i < pdev->num_resources; ++i) { + for (i = 0; i < irqs; ++i) { err = set_irq_affinity(platform_get_irq(pdev, i), i); if (err) break; } -out: return err; } diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index 420b8d6485d..5ea4fb718b9 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -226,8 +226,8 @@ int cpu_architecture(void) * Register 0 and check for VMSAv7 or PMSAv7 */ asm("mrc p15, 0, %0, c0, c1, 4" : "=r" (mmfr0)); - if ((mmfr0 & 0x0000000f) == 0x00000003 || - (mmfr0 & 0x000000f0) == 0x00000030) + if ((mmfr0 & 0x0000000f) >= 0x00000003 || + (mmfr0 & 0x000000f0) >= 0x00000030) cpu_arch = CPU_ARCH_ARMv7; else if ((mmfr0 & 0x0000000f) == 0x00000002 || (mmfr0 & 0x000000f0) == 0x00000020) diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c index 907d5a620bc..abaf8445ce2 100644 --- a/arch/arm/kernel/signal.c +++ b/arch/arm/kernel/signal.c @@ -474,7 +474,9 @@ setup_return(struct pt_regs *regs, struct k_sigaction *ka, unsigned long handler = (unsigned long)ka->sa.sa_handler; unsigned long retcode; int thumb = 0; - unsigned long cpsr = regs->ARM_cpsr & ~PSR_f; + unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); + + cpsr |= PSR_ENDSTATE; /* * Maybe we need to deliver a 32-bit signal to a 26-bit task. diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 86b66f3f203..61462790757 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -21,6 +21,12 @@ #define ARM_CPU_KEEP(x) #endif +#if defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK) +#define ARM_EXIT_KEEP(x) x +#else +#define ARM_EXIT_KEEP(x) +#endif + OUTPUT_ARCH(arm) ENTRY(stext) @@ -43,6 +49,7 @@ SECTIONS _sinittext = .; HEAD_TEXT INIT_TEXT + ARM_EXIT_KEEP(EXIT_TEXT) _einittext = .; ARM_CPU_DISCARD(PROC_INFO) __arch_info_begin = .; @@ -67,6 +74,7 @@ SECTIONS #ifndef CONFIG_XIP_KERNEL __init_begin = _stext; INIT_DATA + ARM_EXIT_KEEP(EXIT_DATA) #endif } @@ -162,6 +170,7 @@ SECTIONS . = ALIGN(PAGE_SIZE); __init_begin = .; INIT_DATA + ARM_EXIT_KEEP(EXIT_DATA) . = ALIGN(PAGE_SIZE); __init_end = .; #endif @@ -247,6 +256,8 @@ SECTIONS } #endif + NOTES + BSS_SECTION(0, 0, 0) _end = .; diff --git a/arch/arm/mach-omap2/clkt_dpll.c b/arch/arm/mach-omap2/clkt_dpll.c index 337392c3f54..acb7ae5b0a2 100644 --- a/arch/arm/mach-omap2/clkt_dpll.c +++ b/arch/arm/mach-omap2/clkt_dpll.c @@ -77,7 +77,7 @@ static int _dpll_test_fint(struct clk *clk, u8 n) dd = clk->dpll_data; /* DPLL divider must result in a valid jitter correction val */ - fint = clk->parent->rate / (n + 1); + fint = clk->parent->rate / n; if (fint < DPLL_FINT_BAND1_MIN) { pr_debug("rejecting n=%d due to Fint failure, " diff --git a/arch/arm/mach-omap2/mailbox.c b/arch/arm/mach-omap2/mailbox.c index 394413dc7de..0a585dfa987 100644 --- a/arch/arm/mach-omap2/mailbox.c +++ b/arch/arm/mach-omap2/mailbox.c @@ -334,7 +334,7 @@ static struct omap_mbox mbox_iva_info = { .priv = &omap2_mbox_iva_priv, }; -struct omap_mbox *omap2_mboxes[] = { &mbox_iva_info, &mbox_dsp_info, NULL }; +struct omap_mbox *omap2_mboxes[] = { &mbox_dsp_info, &mbox_iva_info, NULL }; #endif #if defined(CONFIG_ARCH_OMAP4) diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index 98148b6c36e..6c84659cf84 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -605,7 +605,7 @@ static void __init omap_mux_dbg_create_entry( list_for_each_entry(e, &partition->muxmodes, node) { struct omap_mux *m = &e->mux; - (void)debugfs_create_file(m->muxnames[0], S_IWUGO, mux_dbg_dir, + (void)debugfs_create_file(m->muxnames[0], S_IWUSR, mux_dbg_dir, m, &omap_mux_dbg_signal_fops); } } diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index 125f56591fb..a5a83b358dd 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -637,14 +637,14 @@ static int __init pm_dbg_init(void) } - (void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUGO, d, + (void) debugfs_create_file("enable_off_mode", S_IRUGO | S_IWUSR, d, &enable_off_mode, &pm_dbg_option_fops); - (void) debugfs_create_file("sleep_while_idle", S_IRUGO | S_IWUGO, d, + (void) debugfs_create_file("sleep_while_idle", S_IRUGO | S_IWUSR, d, &sleep_while_idle, &pm_dbg_option_fops); - (void) debugfs_create_file("wakeup_timer_seconds", S_IRUGO | S_IWUGO, d, + (void) debugfs_create_file("wakeup_timer_seconds", S_IRUGO | S_IWUSR, d, &wakeup_timer_seconds, &pm_dbg_option_fops); (void) debugfs_create_file("wakeup_timer_milliseconds", - S_IRUGO | S_IWUGO, d, &wakeup_timer_milliseconds, + S_IRUGO | S_IWUSR, d, &wakeup_timer_milliseconds, &pm_dbg_option_fops); pm_dbg_init_done = 1; diff --git a/arch/arm/mach-omap2/prcm_mpu44xx.h b/arch/arm/mach-omap2/prcm_mpu44xx.h index 729a644ce85..3300ff6e3cf 100644 --- a/arch/arm/mach-omap2/prcm_mpu44xx.h +++ b/arch/arm/mach-omap2/prcm_mpu44xx.h @@ -38,8 +38,8 @@ #define OMAP4430_PRCM_MPU_CPU1_INST 0x0800 /* PRCM_MPU clockdomain register offsets (from instance start) */ -#define OMAP4430_PRCM_MPU_CPU0_MPU_CDOFFS 0x0000 -#define OMAP4430_PRCM_MPU_CPU1_MPU_CDOFFS 0x0000 +#define OMAP4430_PRCM_MPU_CPU0_MPU_CDOFFS 0x0018 +#define OMAP4430_PRCM_MPU_CPU1_MPU_CDOFFS 0x0018 /* diff --git a/arch/arm/mach-omap2/smartreflex.c b/arch/arm/mach-omap2/smartreflex.c index c37e823266d..95ac336fe3f 100644 --- a/arch/arm/mach-omap2/smartreflex.c +++ b/arch/arm/mach-omap2/smartreflex.c @@ -900,7 +900,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) return PTR_ERR(dbg_dir); } - (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUGO, dbg_dir, + (void) debugfs_create_file("autocomp", S_IRUGO | S_IWUSR, dbg_dir, (void *)sr_info, &pm_sr_fops); (void) debugfs_create_x32("errweight", S_IRUGO, dbg_dir, &sr_info->err_weight); @@ -939,7 +939,7 @@ static int __init omap_sr_probe(struct platform_device *pdev) strcpy(name, "volt_"); sprintf(volt_name, "%d", volt_data[i].volt_nominal); strcat(name, volt_name); - (void) debugfs_create_x32(name, S_IRUGO | S_IWUGO, nvalue_dir, + (void) debugfs_create_x32(name, S_IRUGO | S_IWUSR, nvalue_dir, &(sr_info->nvalue_table[i].nvalue)); } diff --git a/arch/arm/mach-omap2/timer-gp.c b/arch/arm/mach-omap2/timer-gp.c index 7b7c2683ae7..0fc550e7e48 100644 --- a/arch/arm/mach-omap2/timer-gp.c +++ b/arch/arm/mach-omap2/timer-gp.c @@ -39,6 +39,7 @@ #include <asm/mach/time.h> #include <plat/dmtimer.h> #include <asm/localtimer.h> +#include <asm/sched_clock.h> #include "timer-gp.h" @@ -190,6 +191,7 @@ static void __init omap2_gp_clocksource_init(void) /* * clocksource */ +static DEFINE_CLOCK_DATA(cd); static struct omap_dm_timer *gpt_clocksource; static cycle_t clocksource_read_cycles(struct clocksource *cs) { @@ -204,6 +206,15 @@ static struct clocksource clocksource_gpt = { .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; +static void notrace dmtimer_update_sched_clock(void) +{ + u32 cyc; + + cyc = omap_dm_timer_read_counter(gpt_clocksource); + + update_sched_clock(&cd, cyc, (u32)~0); +} + /* Setup free-running counter for clocksource */ static void __init omap2_gp_clocksource_init(void) { @@ -224,6 +235,8 @@ static void __init omap2_gp_clocksource_init(void) omap_dm_timer_set_load_start(gpt, 1, 0); + init_sched_clock(&cd, dmtimer_update_sched_clock, 32, tick_rate); + if (clocksource_register_hz(&clocksource_gpt, tick_rate)) printk(err2, clocksource_gpt.name); } diff --git a/arch/arm/mach-s5p6442/include/mach/map.h b/arch/arm/mach-s5p6442/include/mach/map.h index 203dd5a18bd..058dab4482a 100644 --- a/arch/arm/mach-s5p6442/include/mach/map.h +++ b/arch/arm/mach-s5p6442/include/mach/map.h @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s5p6442/include/mach/map.h * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * S5P6442 - Memory map definitions @@ -16,56 +16,61 @@ #include <plat/map-base.h> #include <plat/map-s5p.h> -#define S5P6442_PA_CHIPID (0xE0000000) -#define S5P_PA_CHIPID S5P6442_PA_CHIPID +#define S5P6442_PA_SDRAM 0x20000000 -#define S5P6442_PA_SYSCON (0xE0100000) -#define S5P_PA_SYSCON S5P6442_PA_SYSCON +#define S5P6442_PA_I2S0 0xC0B00000 +#define S5P6442_PA_I2S1 0xF2200000 -#define S5P6442_PA_GPIO (0xE0200000) +#define S5P6442_PA_CHIPID 0xE0000000 -#define S5P6442_PA_VIC0 (0xE4000000) -#define S5P6442_PA_VIC1 (0xE4100000) -#define S5P6442_PA_VIC2 (0xE4200000) +#define S5P6442_PA_SYSCON 0xE0100000 -#define S5P6442_PA_SROMC (0xE7000000) -#define S5P_PA_SROMC S5P6442_PA_SROMC +#define S5P6442_PA_GPIO 0xE0200000 -#define S5P6442_PA_MDMA 0xE8000000 -#define S5P6442_PA_PDMA 0xE9000000 +#define S5P6442_PA_VIC0 0xE4000000 +#define S5P6442_PA_VIC1 0xE4100000 +#define S5P6442_PA_VIC2 0xE4200000 -#define S5P6442_PA_TIMER (0xEA000000) -#define S5P_PA_TIMER S5P6442_PA_TIMER +#define S5P6442_PA_SROMC 0xE7000000 -#define S5P6442_PA_SYSTIMER (0xEA100000) +#define S5P6442_PA_MDMA 0xE8000000 +#define S5P6442_PA_PDMA 0xE9000000 -#define S5P6442_PA_WATCHDOG (0xEA200000) +#define S5P6442_PA_TIMER 0xEA000000 -#define S5P6442_PA_UART (0xEC000000) +#define S5P6442_PA_SYSTIMER 0xEA100000 -#define S5P_PA_UART0 (S5P6442_PA_UART + 0x0) -#define S5P_PA_UART1 (S5P6442_PA_UART + 0x400) -#define S5P_PA_UART2 (S5P6442_PA_UART + 0x800) -#define S5P_SZ_UART SZ_256 +#define S5P6442_PA_WATCHDOG 0xEA200000 -#define S5P6442_PA_IIC0 (0xEC100000) +#define S5P6442_PA_UART 0xEC000000 -#define S5P6442_PA_SDRAM (0x20000000) -#define S5P_PA_SDRAM S5P6442_PA_SDRAM +#define S5P6442_PA_IIC0 0xEC100000 #define S5P6442_PA_SPI 0xEC300000 -/* I2S */ -#define S5P6442_PA_I2S0 0xC0B00000 -#define S5P6442_PA_I2S1 0xF2200000 - -/* PCM */ #define S5P6442_PA_PCM0 0xF2400000 #define S5P6442_PA_PCM1 0xF2500000 -/* compatibiltiy defines. */ +/* Compatibiltiy Defines */ + +#define S3C_PA_IIC S5P6442_PA_IIC0 #define S3C_PA_WDT S5P6442_PA_WATCHDOG + +#define S5P_PA_CHIPID S5P6442_PA_CHIPID +#define S5P_PA_SDRAM S5P6442_PA_SDRAM +#define S5P_PA_SROMC S5P6442_PA_SROMC +#define S5P_PA_SYSCON S5P6442_PA_SYSCON +#define S5P_PA_TIMER S5P6442_PA_TIMER + +/* UART */ + #define S3C_PA_UART S5P6442_PA_UART -#define S3C_PA_IIC S5P6442_PA_IIC0 + +#define S5P_PA_UART(x) (S3C_PA_UART + ((x) * S3C_UART_OFFSET)) +#define S5P_PA_UART0 S5P_PA_UART(0) +#define S5P_PA_UART1 S5P_PA_UART(1) +#define S5P_PA_UART2 S5P_PA_UART(2) + +#define S5P_SZ_UART SZ_256 #endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-s5p64x0/include/mach/map.h b/arch/arm/mach-s5p64x0/include/mach/map.h index a9365e5ba61..95c91257c7c 100644 --- a/arch/arm/mach-s5p64x0/include/mach/map.h +++ b/arch/arm/mach-s5p64x0/include/mach/map.h @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s5p64x0/include/mach/map.h * - * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd. + * Copyright (c) 2009-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * S5P64X0 - Memory map definitions @@ -16,64 +16,46 @@ #include <plat/map-base.h> #include <plat/map-s5p.h> -#define S5P64X0_PA_SDRAM (0x20000000) +#define S5P64X0_PA_SDRAM 0x20000000 -#define S5P64X0_PA_CHIPID (0xE0000000) -#define S5P_PA_CHIPID S5P64X0_PA_CHIPID - -#define S5P64X0_PA_SYSCON (0xE0100000) -#define S5P_PA_SYSCON S5P64X0_PA_SYSCON - -#define S5P64X0_PA_GPIO (0xE0308000) - -#define S5P64X0_PA_VIC0 (0xE4000000) -#define S5P64X0_PA_VIC1 (0xE4100000) +#define S5P64X0_PA_CHIPID 0xE0000000 -#define S5P64X0_PA_SROMC (0xE7000000) -#define S5P_PA_SROMC S5P64X0_PA_SROMC - -#define S5P64X0_PA_PDMA (0xE9000000) - -#define S5P64X0_PA_TIMER (0xEA000000) -#define S5P_PA_TIMER S5P64X0_PA_TIMER +#define S5P64X0_PA_SYSCON 0xE0100000 -#define S5P64X0_PA_RTC (0xEA100000) +#define S5P64X0_PA_GPIO 0xE0308000 -#define S5P64X0_PA_WDT (0xEA200000) +#define S5P64X0_PA_VIC0 0xE4000000 +#define S5P64X0_PA_VIC1 0xE4100000 -#define S5P6440_PA_UART(x) (0xEC000000 + ((x) * S3C_UART_OFFSET)) -#define S5P6450_PA_UART(x) ((x < 5) ? (0xEC800000 + ((x) * S3C_UART_OFFSET)) : (0xEC000000)) +#define S5P64X0_PA_SROMC 0xE7000000 -#define S5P_PA_UART0 S5P6450_PA_UART(0) -#define S5P_PA_UART1 S5P6450_PA_UART(1) -#define S5P_PA_UART2 S5P6450_PA_UART(2) -#define S5P_PA_UART3 S5P6450_PA_UART(3) -#define S5P_PA_UART4 S5P6450_PA_UART(4) -#define S5P_PA_UART5 S5P6450_PA_UART(5) +#define S5P64X0_PA_PDMA 0xE9000000 -#define S5P_SZ_UART SZ_256 +#define S5P64X0_PA_TIMER 0xEA000000 +#define S5P64X0_PA_RTC 0xEA100000 +#define S5P64X0_PA_WDT 0xEA200000 -#define S5P6440_PA_IIC0 (0xEC104000) -#define S5P6440_PA_IIC1 (0xEC20F000) -#define S5P6450_PA_IIC0 (0xEC100000) -#define S5P6450_PA_IIC1 (0xEC200000) +#define S5P6440_PA_IIC0 0xEC104000 +#define S5P6440_PA_IIC1 0xEC20F000 +#define S5P6450_PA_IIC0 0xEC100000 +#define S5P6450_PA_IIC1 0xEC200000 -#define S5P64X0_PA_SPI0 (0xEC400000) -#define S5P64X0_PA_SPI1 (0xEC500000) +#define S5P64X0_PA_SPI0 0xEC400000 +#define S5P64X0_PA_SPI1 0xEC500000 -#define S5P64X0_PA_HSOTG (0xED100000) +#define S5P64X0_PA_HSOTG 0xED100000 #define S5P64X0_PA_HSMMC(x) (0xED800000 + ((x) * 0x100000)) -#define S5P64X0_PA_I2S (0xF2000000) +#define S5P64X0_PA_I2S 0xF2000000 #define S5P6450_PA_I2S1 0xF2800000 #define S5P6450_PA_I2S2 0xF2900000 -#define S5P64X0_PA_PCM (0xF2100000) +#define S5P64X0_PA_PCM 0xF2100000 -#define S5P64X0_PA_ADC (0xF3000000) +#define S5P64X0_PA_ADC 0xF3000000 -/* compatibiltiy defines. */ +/* Compatibiltiy Defines */ #define S3C_PA_HSMMC0 S5P64X0_PA_HSMMC(0) #define S3C_PA_HSMMC1 S5P64X0_PA_HSMMC(1) @@ -83,6 +65,25 @@ #define S3C_PA_RTC S5P64X0_PA_RTC #define S3C_PA_WDT S5P64X0_PA_WDT +#define S5P_PA_CHIPID S5P64X0_PA_CHIPID +#define S5P_PA_SROMC S5P64X0_PA_SROMC +#define S5P_PA_SYSCON S5P64X0_PA_SYSCON +#define S5P_PA_TIMER S5P64X0_PA_TIMER + #define SAMSUNG_PA_ADC S5P64X0_PA_ADC +/* UART */ + +#define S5P6440_PA_UART(x) (0xEC000000 + ((x) * S3C_UART_OFFSET)) +#define S5P6450_PA_UART(x) ((x < 5) ? (0xEC800000 + ((x) * S3C_UART_OFFSET)) : (0xEC000000)) + +#define S5P_PA_UART0 S5P6450_PA_UART(0) +#define S5P_PA_UART1 S5P6450_PA_UART(1) +#define S5P_PA_UART2 S5P6450_PA_UART(2) +#define S5P_PA_UART3 S5P6450_PA_UART(3) +#define S5P_PA_UART4 S5P6450_PA_UART(4) +#define S5P_PA_UART5 S5P6450_PA_UART(5) + +#define S5P_SZ_UART SZ_256 + #endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-s5pc100/include/mach/map.h b/arch/arm/mach-s5pc100/include/mach/map.h index 328467b346a..ccbe6b767f7 100644 --- a/arch/arm/mach-s5pc100/include/mach/map.h +++ b/arch/arm/mach-s5pc100/include/mach/map.h @@ -1,5 +1,8 @@ /* linux/arch/arm/mach-s5pc100/include/mach/map.h * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * * Copyright 2009 Samsung Electronics Co. * Byungho Min <bhmin@samsung.com> * @@ -16,145 +19,115 @@ #include <plat/map-base.h> #include <plat/map-s5p.h> -/* - * map-base.h has already defined virtual memory address - * S3C_VA_IRQ S3C_ADDR(0x00000000) irq controller(s) - * S3C_VA_SYS S3C_ADDR(0x00100000) system control - * S3C_VA_MEM S3C_ADDR(0x00200000) system control (not used) - * S3C_VA_TIMER S3C_ADDR(0x00300000) timer block - * S3C_VA_WATCHDOG S3C_ADDR(0x00400000) watchdog - * S3C_VA_UART S3C_ADDR(0x01000000) UART - * - * S5PC100 specific virtual memory address can be defined here - * S5PC1XX_VA_GPIO S3C_ADDR(0x00500000) GPIO - * - */ +#define S5PC100_PA_SDRAM 0x20000000 + +#define S5PC100_PA_ONENAND 0xE7100000 +#define S5PC100_PA_ONENAND_BUF 0xB0000000 + +#define S5PC100_PA_CHIPID 0xE0000000 -#define S5PC100_PA_ONENAND_BUF (0xB0000000) -#define S5PC100_SZ_ONENAND_BUF (SZ_256M - SZ_32M) +#define S5PC100_PA_SYSCON 0xE0100000 -/* Chip ID */ +#define S5PC100_PA_OTHERS 0xE0200000 -#define S5PC100_PA_CHIPID (0xE0000000) -#define S5P_PA_CHIPID S5PC100_PA_CHIPID +#define S5PC100_PA_GPIO 0xE0300000 -#define S5PC100_PA_SYSCON (0xE0100000) -#define S5P_PA_SYSCON S5PC100_PA_SYSCON +#define S5PC100_PA_VIC0 0xE4000000 +#define S5PC100_PA_VIC1 0xE4100000 +#define S5PC100_PA_VIC2 0xE4200000 -#define S5PC100_PA_OTHERS (0xE0200000) -#define S5PC100_VA_OTHERS (S3C_VA_SYS + 0x10000) +#define S5PC100_PA_SROMC 0xE7000000 -#define S5PC100_PA_GPIO (0xE0300000) -#define S5PC1XX_VA_GPIO S3C_ADDR(0x00500000) +#define S5PC100_PA_CFCON 0xE7800000 -/* Interrupt */ -#define S5PC100_PA_VIC0 (0xE4000000) -#define S5PC100_PA_VIC1 (0xE4100000) -#define S5PC100_PA_VIC2 (0xE4200000) -#define S5PC100_VA_VIC S3C_VA_IRQ -#define S5PC100_VA_VIC_OFFSET 0x10000 -#define S5PC1XX_VA_VIC(x) (S5PC100_VA_VIC + ((x) * S5PC100_VA_VIC_OFFSET)) +#define S5PC100_PA_MDMA 0xE8100000 +#define S5PC100_PA_PDMA0 0xE9000000 +#define S5PC100_PA_PDMA1 0xE9200000 -#define S5PC100_PA_SROMC (0xE7000000) -#define S5P_PA_SROMC S5PC100_PA_SROMC +#define S5PC100_PA_TIMER 0xEA000000 +#define S5PC100_PA_SYSTIMER 0xEA100000 +#define S5PC100_PA_WATCHDOG 0xEA200000 +#define S5PC100_PA_RTC 0xEA300000 -#define S5PC100_PA_ONENAND (0xE7100000) +#define S5PC100_PA_UART 0xEC000000 -#define S5PC100_PA_CFCON (0xE7800000) +#define S5PC100_PA_IIC0 0xEC100000 +#define S5PC100_PA_IIC1 0xEC200000 -/* DMA */ -#define S5PC100_PA_MDMA (0xE8100000) -#define S5PC100_PA_PDMA0 (0xE9000000) -#define S5PC100_PA_PDMA1 (0xE9200000) +#define S5PC100_PA_SPI0 0xEC300000 +#define S5PC100_PA_SPI1 0xEC400000 +#define S5PC100_PA_SPI2 0xEC500000 -/* Timer */ -#define S5PC100_PA_TIMER (0xEA000000) -#define S5P_PA_TIMER S5PC100_PA_TIMER +#define S5PC100_PA_USB_HSOTG 0xED200000 +#define S5PC100_PA_USB_HSPHY 0xED300000 -#define S5PC100_PA_SYSTIMER (0xEA100000) +#define S5PC100_PA_HSMMC(x) (0xED800000 + ((x) * 0x100000)) -#define S5PC100_PA_WATCHDOG (0xEA200000) -#define S5PC100_PA_RTC (0xEA300000) +#define S5PC100_PA_FB 0xEE000000 -#define S5PC100_PA_UART (0xEC000000) +#define S5PC100_PA_FIMC0 0xEE200000 +#define S5PC100_PA_FIMC1 0xEE300000 +#define S5PC100_PA_FIMC2 0xEE400000 -#define S5P_PA_UART0 (S5PC100_PA_UART + 0x0) -#define S5P_PA_UART1 (S5PC100_PA_UART + 0x400) -#define S5P_PA_UART2 (S5PC100_PA_UART + 0x800) -#define S5P_PA_UART3 (S5PC100_PA_UART + 0xC00) -#define S5P_SZ_UART SZ_256 +#define S5PC100_PA_I2S0 0xF2000000 +#define S5PC100_PA_I2S1 0xF2100000 +#define S5PC100_PA_I2S2 0xF2200000 -#define S5PC100_PA_IIC0 (0xEC100000) -#define S5PC100_PA_IIC1 (0xEC200000) +#define S5PC100_PA_AC97 0xF2300000 -/* SPI */ -#define S5PC100_PA_SPI0 0xEC300000 -#define S5PC100_PA_SPI1 0xEC400000 -#define S5PC100_PA_SPI2 0xEC500000 +#define S5PC100_PA_PCM0 0xF2400000 +#define S5PC100_PA_PCM1 0xF2500000 -/* USB HS OTG */ -#define S5PC100_PA_USB_HSOTG (0xED200000) -#define S5PC100_PA_USB_HSPHY (0xED300000) +#define S5PC100_PA_SPDIF 0xF2600000 -#define S5PC100_PA_FB (0xEE000000) +#define S5PC100_PA_TSADC 0xF3000000 -#define S5PC100_PA_FIMC0 (0xEE200000) -#define S5PC100_PA_FIMC1 (0xEE300000) -#define S5PC100_PA_FIMC2 (0xEE400000) +#define S5PC100_PA_KEYPAD 0xF3100000 -#define S5PC100_PA_I2S0 (0xF2000000) -#define S5PC100_PA_I2S1 (0xF2100000) -#define S5PC100_PA_I2S2 (0xF2200000) +/* Compatibiltiy Defines */ -#define S5PC100_PA_AC97 0xF2300000 +#define S3C_PA_FB S5PC100_PA_FB +#define S3C_PA_HSMMC0 S5PC100_PA_HSMMC(0) +#define S3C_PA_HSMMC1 S5PC100_PA_HSMMC(1) +#define S3C_PA_HSMMC2 S5PC100_PA_HSMMC(2) +#define S3C_PA_IIC S5PC100_PA_IIC0 +#define S3C_PA_IIC1 S5PC100_PA_IIC1 +#define S3C_PA_KEYPAD S5PC100_PA_KEYPAD +#define S3C_PA_ONENAND S5PC100_PA_ONENAND +#define S3C_PA_ONENAND_BUF S5PC100_PA_ONENAND_BUF +#define S3C_PA_RTC S5PC100_PA_RTC +#define S3C_PA_TSADC S5PC100_PA_TSADC +#define S3C_PA_USB_HSOTG S5PC100_PA_USB_HSOTG +#define S3C_PA_USB_HSPHY S5PC100_PA_USB_HSPHY +#define S3C_PA_WDT S5PC100_PA_WATCHDOG -/* PCM */ -#define S5PC100_PA_PCM0 0xF2400000 -#define S5PC100_PA_PCM1 0xF2500000 +#define S5P_PA_CHIPID S5PC100_PA_CHIPID +#define S5P_PA_FIMC0 S5PC100_PA_FIMC0 +#define S5P_PA_FIMC1 S5PC100_PA_FIMC1 +#define S5P_PA_FIMC2 S5PC100_PA_FIMC2 +#define S5P_PA_SDRAM S5PC100_PA_SDRAM +#define S5P_PA_SROMC S5PC100_PA_SROMC +#define S5P_PA_SYSCON S5PC100_PA_SYSCON +#define S5P_PA_TIMER S5PC100_PA_TIMER -#define S5PC100_PA_SPDIF 0xF2600000 +#define SAMSUNG_PA_ADC S5PC100_PA_TSADC +#define SAMSUNG_PA_CFCON S5PC100_PA_CFCON +#define SAMSUNG_PA_KEYPAD S5PC100_PA_KEYPAD -#define S5PC100_PA_TSADC (0xF3000000) +#define S5PC100_VA_OTHERS (S3C_VA_SYS + 0x10000) -/* KEYPAD */ -#define S5PC100_PA_KEYPAD (0xF3100000) +#define S3C_SZ_ONENAND_BUF (SZ_256M - SZ_32M) -#define S5PC100_PA_HSMMC(x) (0xED800000 + ((x) * 0x100000)) +/* UART */ -#define S5PC100_PA_SDRAM (0x20000000) -#define S5P_PA_SDRAM S5PC100_PA_SDRAM +#define S3C_PA_UART S5PC100_PA_UART -/* compatibiltiy defines. */ -#define S3C_PA_UART S5PC100_PA_UART -#define S3C_PA_IIC S5PC100_PA_IIC0 -#define S3C_PA_IIC1 S5PC100_PA_IIC1 -#define S3C_PA_FB S5PC100_PA_FB -#define S3C_PA_G2D S5PC100_PA_G2D -#define S3C_PA_G3D S5PC100_PA_G3D -#define S3C_PA_JPEG S5PC100_PA_JPEG -#define S3C_PA_ROTATOR S5PC100_PA_ROTATOR -#define S5P_VA_VIC0 S5PC1XX_VA_VIC(0) -#define S5P_VA_VIC1 S5PC1XX_VA_VIC(1) -#define S5P_VA_VIC2 S5PC1XX_VA_VIC(2) -#define S3C_PA_USB_HSOTG S5PC100_PA_USB_HSOTG -#define S3C_PA_USB_HSPHY S5PC100_PA_USB_HSPHY -#define S3C_PA_HSMMC0 S5PC100_PA_HSMMC(0) -#define S3C_PA_HSMMC1 S5PC100_PA_HSMMC(1) -#define S3C_PA_HSMMC2 S5PC100_PA_HSMMC(2) -#define S3C_PA_KEYPAD S5PC100_PA_KEYPAD -#define S3C_PA_WDT S5PC100_PA_WATCHDOG -#define S3C_PA_TSADC S5PC100_PA_TSADC -#define S3C_PA_ONENAND S5PC100_PA_ONENAND -#define S3C_PA_ONENAND_BUF S5PC100_PA_ONENAND_BUF -#define S3C_SZ_ONENAND_BUF S5PC100_SZ_ONENAND_BUF -#define S3C_PA_RTC S5PC100_PA_RTC - -#define SAMSUNG_PA_ADC S5PC100_PA_TSADC -#define SAMSUNG_PA_CFCON S5PC100_PA_CFCON -#define SAMSUNG_PA_KEYPAD S5PC100_PA_KEYPAD +#define S5P_PA_UART(x) (S3C_PA_UART + ((x) * S3C_UART_OFFSET)) +#define S5P_PA_UART0 S5P_PA_UART(0) +#define S5P_PA_UART1 S5P_PA_UART(1) +#define S5P_PA_UART2 S5P_PA_UART(2) +#define S5P_PA_UART3 S5P_PA_UART(3) -#define S5P_PA_FIMC0 S5PC100_PA_FIMC0 -#define S5P_PA_FIMC1 S5PC100_PA_FIMC1 -#define S5P_PA_FIMC2 S5PC100_PA_FIMC2 +#define S5P_SZ_UART SZ_256 -#endif /* __ASM_ARCH_C100_MAP_H */ +#endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-s5pv210/include/mach/map.h b/arch/arm/mach-s5pv210/include/mach/map.h index 3611492ad68..1dd58836fd4 100644 --- a/arch/arm/mach-s5pv210/include/mach/map.h +++ b/arch/arm/mach-s5pv210/include/mach/map.h @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s5pv210/include/mach/map.h * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * S5PV210 - Memory map definitions @@ -16,122 +16,120 @@ #include <plat/map-base.h> #include <plat/map-s5p.h> -#define S5PV210_PA_SROM_BANK5 (0xA8000000) +#define S5PV210_PA_SDRAM 0x20000000 -#define S5PC110_PA_ONENAND (0xB0000000) -#define S5P_PA_ONENAND S5PC110_PA_ONENAND +#define S5PV210_PA_SROM_BANK5 0xA8000000 -#define S5PC110_PA_ONENAND_DMA (0xB0600000) -#define S5P_PA_ONENAND_DMA S5PC110_PA_ONENAND_DMA +#define S5PC110_PA_ONENAND 0xB0000000 +#define S5PC110_PA_ONENAND_DMA 0xB0600000 -#define S5PV210_PA_CHIPID (0xE0000000) -#define S5P_PA_CHIPID S5PV210_PA_CHIPID +#define S5PV210_PA_CHIPID 0xE0000000 -#define S5PV210_PA_SYSCON (0xE0100000) -#define S5P_PA_SYSCON S5PV210_PA_SYSCON +#define S5PV210_PA_SYSCON 0xE0100000 -#define S5PV210_PA_GPIO (0xE0200000) +#define S5PV210_PA_GPIO 0xE0200000 -/* SPI */ -#define S5PV210_PA_SPI0 0xE1300000 -#define S5PV210_PA_SPI1 0xE1400000 +#define S5PV210_PA_SPDIF 0xE1100000 -#define S5PV210_PA_KEYPAD (0xE1600000) +#define S5PV210_PA_SPI0 0xE1300000 +#define S5PV210_PA_SPI1 0xE1400000 -#define S5PV210_PA_IIC0 (0xE1800000) -#define S5PV210_PA_IIC1 (0xFAB00000) -#define S5PV210_PA_IIC2 (0xE1A00000) +#define S5PV210_PA_KEYPAD 0xE1600000 -#define S5PV210_PA_TIMER (0xE2500000) -#define S5P_PA_TIMER S5PV210_PA_TIMER +#define S5PV210_PA_ADC 0xE1700000 -#define S5PV210_PA_SYSTIMER (0xE2600000) +#define S5PV210_PA_IIC0 0xE1800000 +#define S5PV210_PA_IIC1 0xFAB00000 +#define S5PV210_PA_IIC2 0xE1A00000 -#define S5PV210_PA_WATCHDOG (0xE2700000) +#define S5PV210_PA_AC97 0xE2200000 -#define S5PV210_PA_RTC (0xE2800000) -#define S5PV210_PA_UART (0xE2900000) +#define S5PV210_PA_PCM0 0xE2300000 +#define S5PV210_PA_PCM1 0xE1200000 +#define S5PV210_PA_PCM2 0xE2B00000 -#define S5P_PA_UART0 (S5PV210_PA_UART + 0x0) -#define S5P_PA_UART1 (S5PV210_PA_UART + 0x400) -#define S5P_PA_UART2 (S5PV210_PA_UART + 0x800) -#define S5P_PA_UART3 (S5PV210_PA_UART + 0xC00) +#define S5PV210_PA_TIMER 0xE2500000 +#define S5PV210_PA_SYSTIMER 0xE2600000 +#define S5PV210_PA_WATCHDOG 0xE2700000 +#define S5PV210_PA_RTC 0xE2800000 -#define S5P_SZ_UART SZ_256 +#define S5PV210_PA_UART 0xE2900000 -#define S3C_VA_UARTx(x) (S3C_VA_UART + ((x) * S3C_UART_OFFSET)) +#define S5PV210_PA_SROMC 0xE8000000 -#define S5PV210_PA_SROMC (0xE8000000) -#define S5P_PA_SROMC S5PV210_PA_SROMC +#define S5PV210_PA_CFCON 0xE8200000 -#define S5PV210_PA_CFCON (0xE8200000) +#define S5PV210_PA_HSMMC(x) (0xEB000000 + ((x) * 0x100000)) -#define S5PV210_PA_MDMA 0xFA200000 -#define S5PV210_PA_PDMA0 0xE0900000 -#define S5PV210_PA_PDMA1 0xE0A00000 +#define S5PV210_PA_HSOTG 0xEC000000 +#define S5PV210_PA_HSPHY 0xEC100000 -#define S5PV210_PA_FB (0xF8000000) +#define S5PV210_PA_IIS0 0xEEE30000 +#define S5PV210_PA_IIS1 0xE2100000 +#define S5PV210_PA_IIS2 0xE2A00000 -#define S5PV210_PA_FIMC0 (0xFB200000) -#define S5PV210_PA_FIMC1 (0xFB300000) -#define S5PV210_PA_FIMC2 (0xFB400000) +#define S5PV210_PA_DMC0 0xF0000000 +#define S5PV210_PA_DMC1 0xF1400000 -#define S5PV210_PA_HSMMC(x) (0xEB000000 + ((x) * 0x100000)) +#define S5PV210_PA_VIC0 0xF2000000 +#define S5PV210_PA_VIC1 0xF2100000 +#define S5PV210_PA_VIC2 0xF2200000 +#define S5PV210_PA_VIC3 0xF2300000 -#define S5PV210_PA_HSOTG (0xEC000000) -#define S5PV210_PA_HSPHY (0xEC100000) +#define S5PV210_PA_FB 0xF8000000 -#define S5PV210_PA_VIC0 (0xF2000000) -#define S5PV210_PA_VIC1 (0xF2100000) -#define S5PV210_PA_VIC2 (0xF2200000) -#define S5PV210_PA_VIC3 (0xF2300000) +#define S5PV210_PA_MDMA 0xFA200000 +#define S5PV210_PA_PDMA0 0xE0900000 +#define S5PV210_PA_PDMA1 0xE0A00000 -#define S5PV210_PA_SDRAM (0x20000000) -#define S5P_PA_SDRAM S5PV210_PA_SDRAM +#define S5PV210_PA_MIPI_CSIS 0xFA600000 -/* S/PDIF */ -#define S5PV210_PA_SPDIF 0xE1100000 +#define S5PV210_PA_FIMC0 0xFB200000 +#define S5PV210_PA_FIMC1 0xFB300000 +#define S5PV210_PA_FIMC2 0xFB400000 -/* I2S */ -#define S5PV210_PA_IIS0 0xEEE30000 -#define S5PV210_PA_IIS1 0xE2100000 -#define S5PV210_PA_IIS2 0xE2A00000 +/* Compatibiltiy Defines */ -/* PCM */ -#define S5PV210_PA_PCM0 0xE2300000 -#define S5PV210_PA_PCM1 0xE1200000 -#define S5PV210_PA_PCM2 0xE2B00000 +#define S3C_PA_FB S5PV210_PA_FB +#define S3C_PA_HSMMC0 S5PV210_PA_HSMMC(0) +#define S3C_PA_HSMMC1 S5PV210_PA_HSMMC(1) +#define S3C_PA_HSMMC2 S5PV210_PA_HSMMC(2) +#define S3C_PA_HSMMC3 S5PV210_PA_HSMMC(3) +#define S3C_PA_IIC S5PV210_PA_IIC0 +#define S3C_PA_IIC1 S5PV210_PA_IIC1 +#define S3C_PA_IIC2 S5PV210_PA_IIC2 +#define S3C_PA_RTC S5PV210_PA_RTC +#define S3C_PA_USB_HSOTG S5PV210_PA_HSOTG +#define S3C_PA_WDT S5PV210_PA_WATCHDOG -/* AC97 */ -#define S5PV210_PA_AC97 0xE2200000 +#define S5P_PA_CHIPID S5PV210_PA_CHIPID +#define S5P_PA_FIMC0 S5PV210_PA_FIMC0 +#define S5P_PA_FIMC1 S5PV210_PA_FIMC1 +#define S5P_PA_FIMC2 S5PV210_PA_FIMC2 +#define S5P_PA_MIPI_CSIS0 S5PV210_PA_MIPI_CSIS +#define S5P_PA_ONENAND S5PC110_PA_ONENAND +#define S5P_PA_ONENAND_DMA S5PC110_PA_ONENAND_DMA +#define S5P_PA_SDRAM S5PV210_PA_SDRAM +#define S5P_PA_SROMC S5PV210_PA_SROMC +#define S5P_PA_SYSCON S5PV210_PA_SYSCON +#define S5P_PA_TIMER S5PV210_PA_TIMER -#define S5PV210_PA_ADC (0xE1700000) +#define SAMSUNG_PA_ADC S5PV210_PA_ADC +#define SAMSUNG_PA_CFCON S5PV210_PA_CFCON +#define SAMSUNG_PA_KEYPAD S5PV210_PA_KEYPAD -#define S5PV210_PA_DMC0 (0xF0000000) -#define S5PV210_PA_DMC1 (0xF1400000) +/* UART */ -#define S5PV210_PA_MIPI_CSIS 0xFA600000 +#define S3C_VA_UARTx(x) (S3C_VA_UART + ((x) * S3C_UART_OFFSET)) -/* compatibiltiy defines. */ -#define S3C_PA_UART S5PV210_PA_UART -#define S3C_PA_HSMMC0 S5PV210_PA_HSMMC(0) -#define S3C_PA_HSMMC1 S5PV210_PA_HSMMC(1) -#define S3C_PA_HSMMC2 S5PV210_PA_HSMMC(2) -#define S3C_PA_HSMMC3 S5PV210_PA_HSMMC(3) -#define S3C_PA_IIC S5PV210_PA_IIC0 -#define S3C_PA_IIC1 S5PV210_PA_IIC1 -#define S3C_PA_IIC2 S5PV210_PA_IIC2 -#define S3C_PA_FB S5PV210_PA_FB -#define S3C_PA_RTC S5PV210_PA_RTC -#define S3C_PA_WDT S5PV210_PA_WATCHDOG -#define S3C_PA_USB_HSOTG S5PV210_PA_HSOTG -#define S5P_PA_FIMC0 S5PV210_PA_FIMC0 -#define S5P_PA_FIMC1 S5PV210_PA_FIMC1 -#define S5P_PA_FIMC2 S5PV210_PA_FIMC2 -#define S5P_PA_MIPI_CSIS0 S5PV210_PA_MIPI_CSIS +#define S3C_PA_UART S5PV210_PA_UART -#define SAMSUNG_PA_ADC S5PV210_PA_ADC -#define SAMSUNG_PA_CFCON S5PV210_PA_CFCON -#define SAMSUNG_PA_KEYPAD S5PV210_PA_KEYPAD +#define S5P_PA_UART(x) (S3C_PA_UART + ((x) * S3C_UART_OFFSET)) +#define S5P_PA_UART0 S5P_PA_UART(0) +#define S5P_PA_UART1 S5P_PA_UART(1) +#define S5P_PA_UART2 S5P_PA_UART(2) +#define S5P_PA_UART3 S5P_PA_UART(3) + +#define S5P_SZ_UART SZ_256 #endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c index 461aa035afc..557add4fc56 100644 --- a/arch/arm/mach-s5pv210/mach-aquila.c +++ b/arch/arm/mach-s5pv210/mach-aquila.c @@ -149,7 +149,7 @@ static struct regulator_init_data aquila_ldo2_data = { static struct regulator_init_data aquila_ldo3_data = { .constraints = { - .name = "VUSB/MIPI_1.1V", + .name = "VUSB+MIPI_1.1V", .min_uV = 1100000, .max_uV = 1100000, .apply_uV = 1, @@ -197,7 +197,7 @@ static struct regulator_init_data aquila_ldo7_data = { static struct regulator_init_data aquila_ldo8_data = { .constraints = { - .name = "VUSB/VADC_3.3V", + .name = "VUSB+VADC_3.3V", .min_uV = 3300000, .max_uV = 3300000, .apply_uV = 1, @@ -207,7 +207,7 @@ static struct regulator_init_data aquila_ldo8_data = { static struct regulator_init_data aquila_ldo9_data = { .constraints = { - .name = "VCC/VCAM_2.8V", + .name = "VCC+VCAM_2.8V", .min_uV = 2800000, .max_uV = 2800000, .apply_uV = 1, @@ -381,9 +381,12 @@ static struct max8998_platform_data aquila_max8998_pdata = { .buck1_set1 = S5PV210_GPH0(3), .buck1_set2 = S5PV210_GPH0(4), .buck2_set3 = S5PV210_GPH0(5), - .buck1_max_voltage1 = 1200000, - .buck1_max_voltage2 = 1200000, - .buck2_max_voltage = 1200000, + .buck1_voltage1 = 1200000, + .buck1_voltage2 = 1200000, + .buck1_voltage3 = 1200000, + .buck1_voltage4 = 1200000, + .buck2_voltage1 = 1200000, + .buck2_voltage2 = 1200000, }; #endif diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index e22d5112fd4..056f5c769b0 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -288,7 +288,7 @@ static struct regulator_init_data goni_ldo2_data = { static struct regulator_init_data goni_ldo3_data = { .constraints = { - .name = "VUSB/MIPI_1.1V", + .name = "VUSB+MIPI_1.1V", .min_uV = 1100000, .max_uV = 1100000, .apply_uV = 1, @@ -337,7 +337,7 @@ static struct regulator_init_data goni_ldo7_data = { static struct regulator_init_data goni_ldo8_data = { .constraints = { - .name = "VUSB/VADC_3.3V", + .name = "VUSB+VADC_3.3V", .min_uV = 3300000, .max_uV = 3300000, .apply_uV = 1, @@ -347,7 +347,7 @@ static struct regulator_init_data goni_ldo8_data = { static struct regulator_init_data goni_ldo9_data = { .constraints = { - .name = "VCC/VCAM_2.8V", + .name = "VCC+VCAM_2.8V", .min_uV = 2800000, .max_uV = 2800000, .apply_uV = 1, @@ -521,9 +521,12 @@ static struct max8998_platform_data goni_max8998_pdata = { .buck1_set1 = S5PV210_GPH0(3), .buck1_set2 = S5PV210_GPH0(4), .buck2_set3 = S5PV210_GPH0(5), - .buck1_max_voltage1 = 1200000, - .buck1_max_voltage2 = 1200000, - .buck2_max_voltage = 1200000, + .buck1_voltage1 = 1200000, + .buck1_voltage2 = 1200000, + .buck1_voltage3 = 1200000, + .buck1_voltage4 = 1200000, + .buck2_voltage1 = 1200000, + .buck2_voltage2 = 1200000, }; #endif diff --git a/arch/arm/mach-s5pv310/include/mach/map.h b/arch/arm/mach-s5pv310/include/mach/map.h index 3060f78e12a..901657fa7a1 100644 --- a/arch/arm/mach-s5pv310/include/mach/map.h +++ b/arch/arm/mach-s5pv310/include/mach/map.h @@ -1,6 +1,6 @@ /* linux/arch/arm/mach-s5pv310/include/mach/map.h * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com/ * * S5PV310 - Memory map definitions @@ -23,90 +23,43 @@ #include <plat/map-s5p.h> -#define S5PV310_PA_SYSRAM (0x02025000) +#define S5PV310_PA_SYSRAM 0x02025000 -#define S5PV310_PA_SROM_BANK(x) (0x04000000 + ((x) * 0x01000000)) - -#define S5PC210_PA_ONENAND (0x0C000000) -#define S5P_PA_ONENAND S5PC210_PA_ONENAND - -#define S5PC210_PA_ONENAND_DMA (0x0C600000) -#define S5P_PA_ONENAND_DMA S5PC210_PA_ONENAND_DMA - -#define S5PV310_PA_CHIPID (0x10000000) -#define S5P_PA_CHIPID S5PV310_PA_CHIPID - -#define S5PV310_PA_SYSCON (0x10010000) -#define S5P_PA_SYSCON S5PV310_PA_SYSCON +#define S5PV310_PA_I2S0 0x03830000 +#define S5PV310_PA_I2S1 0xE3100000 +#define S5PV310_PA_I2S2 0xE2A00000 -#define S5PV310_PA_PMU (0x10020000) +#define S5PV310_PA_PCM0 0x03840000 +#define S5PV310_PA_PCM1 0x13980000 +#define S5PV310_PA_PCM2 0x13990000 -#define S5PV310_PA_CMU (0x10030000) - -#define S5PV310_PA_WATCHDOG (0x10060000) -#define S5PV310_PA_RTC (0x10070000) - -#define S5PV310_PA_DMC0 (0x10400000) - -#define S5PV310_PA_COMBINER (0x10448000) - -#define S5PV310_PA_COREPERI (0x10500000) -#define S5PV310_PA_GIC_CPU (0x10500100) -#define S5PV310_PA_TWD (0x10500600) -#define S5PV310_PA_GIC_DIST (0x10501000) -#define S5PV310_PA_L2CC (0x10502000) - -/* DMA */ -#define S5PV310_PA_MDMA 0x10810000 -#define S5PV310_PA_PDMA0 0x12680000 -#define S5PV310_PA_PDMA1 0x12690000 - -#define S5PV310_PA_GPIO1 (0x11400000) -#define S5PV310_PA_GPIO2 (0x11000000) -#define S5PV310_PA_GPIO3 (0x03860000) - -#define S5PV310_PA_MIPI_CSIS0 0x11880000 -#define S5PV310_PA_MIPI_CSIS1 0x11890000 +#define S5PV310_PA_SROM_BANK(x) (0x04000000 + ((x) * 0x01000000)) -#define S5PV310_PA_HSMMC(x) (0x12510000 + ((x) * 0x10000)) +#define S5PC210_PA_ONENAND 0x0C000000 +#define S5PC210_PA_ONENAND_DMA 0x0C600000 -#define S5PV310_PA_SROMC (0x12570000) -#define S5P_PA_SROMC S5PV310_PA_SROMC +#define S5PV310_PA_CHIPID 0x10000000 -/* S/PDIF */ -#define S5PV310_PA_SPDIF 0xE1100000 +#define S5PV310_PA_SYSCON 0x10010000 +#define S5PV310_PA_PMU 0x10020000 +#define S5PV310_PA_CMU 0x10030000 -/* I2S */ -#define S5PV310_PA_I2S0 0x03830000 -#define S5PV310_PA_I2S1 0xE3100000 -#define S5PV310_PA_I2S2 0xE2A00000 +#define S5PV310_PA_WATCHDOG 0x10060000 +#define S5PV310_PA_RTC 0x10070000 -/* PCM */ -#define S5PV310_PA_PCM0 0x03840000 -#define S5PV310_PA_PCM1 0x13980000 -#define S5PV310_PA_PCM2 0x13990000 +#define S5PV310_PA_DMC0 0x10400000 -/* AC97 */ -#define S5PV310_PA_AC97 0x139A0000 +#define S5PV310_PA_COMBINER 0x10448000 -#define S5PV310_PA_UART (0x13800000) +#define S5PV310_PA_COREPERI 0x10500000 +#define S5PV310_PA_GIC_CPU 0x10500100 +#define S5PV310_PA_TWD 0x10500600 +#define S5PV310_PA_GIC_DIST 0x10501000 +#define S5PV310_PA_L2CC 0x10502000 -#define S5P_PA_UART(x) (S5PV310_PA_UART + ((x) * S3C_UART_OFFSET)) -#define S5P_PA_UART0 S5P_PA_UART(0) -#define S5P_PA_UART1 S5P_PA_UART(1) -#define S5P_PA_UART2 S5P_PA_UART(2) -#define S5P_PA_UART3 S5P_PA_UART(3) -#define S5P_PA_UART4 S5P_PA_UART(4) - -#define S5P_SZ_UART SZ_256 - -#define S5PV310_PA_IIC(x) (0x13860000 + ((x) * 0x10000)) - -#define S5PV310_PA_TIMER (0x139D0000) -#define S5P_PA_TIMER S5PV310_PA_TIMER - -#define S5PV310_PA_SDRAM (0x40000000) -#define S5P_PA_SDRAM S5PV310_PA_SDRAM +#define S5PV310_PA_MDMA 0x10810000 +#define S5PV310_PA_PDMA0 0x12680000 +#define S5PV310_PA_PDMA1 0x12690000 #define S5PV310_PA_SYSMMU_MDMA 0x10A40000 #define S5PV310_PA_SYSMMU_SSS 0x10A50000 @@ -125,8 +78,31 @@ #define S5PV310_PA_SYSMMU_MFC_L 0x13620000 #define S5PV310_PA_SYSMMU_MFC_R 0x13630000 -/* compatibiltiy defines. */ -#define S3C_PA_UART S5PV310_PA_UART +#define S5PV310_PA_GPIO1 0x11400000 +#define S5PV310_PA_GPIO2 0x11000000 +#define S5PV310_PA_GPIO3 0x03860000 + +#define S5PV310_PA_MIPI_CSIS0 0x11880000 +#define S5PV310_PA_MIPI_CSIS1 0x11890000 + +#define S5PV310_PA_HSMMC(x) (0x12510000 + ((x) * 0x10000)) + +#define S5PV310_PA_SROMC 0x12570000 + +#define S5PV310_PA_UART 0x13800000 + +#define S5PV310_PA_IIC(x) (0x13860000 + ((x) * 0x10000)) + +#define S5PV310_PA_AC97 0x139A0000 + +#define S5PV310_PA_TIMER 0x139D0000 + +#define S5PV310_PA_SDRAM 0x40000000 + +#define S5PV310_PA_SPDIF 0xE1100000 + +/* Compatibiltiy Defines */ + #define S3C_PA_HSMMC0 S5PV310_PA_HSMMC(0) #define S3C_PA_HSMMC1 S5PV310_PA_HSMMC(1) #define S3C_PA_HSMMC2 S5PV310_PA_HSMMC(2) @@ -141,7 +117,28 @@ #define S3C_PA_IIC7 S5PV310_PA_IIC(7) #define S3C_PA_RTC S5PV310_PA_RTC #define S3C_PA_WDT S5PV310_PA_WATCHDOG + +#define S5P_PA_CHIPID S5PV310_PA_CHIPID #define S5P_PA_MIPI_CSIS0 S5PV310_PA_MIPI_CSIS0 #define S5P_PA_MIPI_CSIS1 S5PV310_PA_MIPI_CSIS1 +#define S5P_PA_ONENAND S5PC210_PA_ONENAND +#define S5P_PA_ONENAND_DMA S5PC210_PA_ONENAND_DMA +#define S5P_PA_SDRAM S5PV310_PA_SDRAM +#define S5P_PA_SROMC S5PV310_PA_SROMC +#define S5P_PA_SYSCON S5PV310_PA_SYSCON +#define S5P_PA_TIMER S5PV310_PA_TIMER + +/* UART */ + +#define S3C_PA_UART S5PV310_PA_UART + +#define S5P_PA_UART(x) (S3C_PA_UART + ((x) * S3C_UART_OFFSET)) +#define S5P_PA_UART0 S5P_PA_UART(0) +#define S5P_PA_UART1 S5P_PA_UART(1) +#define S5P_PA_UART2 S5P_PA_UART(2) +#define S5P_PA_UART3 S5P_PA_UART(3) +#define S5P_PA_UART4 S5P_PA_UART(4) + +#define S5P_SZ_UART SZ_256 #endif /* __ASM_ARCH_MAP_H */ diff --git a/arch/arm/mach-spear3xx/include/mach/spear320.h b/arch/arm/mach-spear3xx/include/mach/spear320.h index cacf17a958c..53677e464d4 100644 --- a/arch/arm/mach-spear3xx/include/mach/spear320.h +++ b/arch/arm/mach-spear3xx/include/mach/spear320.h @@ -62,7 +62,7 @@ #define SPEAR320_SMII1_BASE 0xAB000000 #define SPEAR320_SMII1_SIZE 0x01000000 -#define SPEAR320_SOC_CONFIG_BASE 0xB4000000 +#define SPEAR320_SOC_CONFIG_BASE 0xB3000000 #define SPEAR320_SOC_CONFIG_SIZE 0x00000070 /* Interrupt registers offsets and masks */ #define INT_STS_MASK_REG 0x04 diff --git a/arch/arm/mach-tegra/include/mach/kbc.h b/arch/arm/mach-tegra/include/mach/kbc.h index 66ad2760c62..04c779832c7 100644 --- a/arch/arm/mach-tegra/include/mach/kbc.h +++ b/arch/arm/mach-tegra/include/mach/kbc.h @@ -57,5 +57,6 @@ struct tegra_kbc_platform_data { const struct matrix_keymap_data *keymap_data; bool wakeup; + bool use_fn_map; }; #endif diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c index 170c9bb9586..f2ce38e085d 100644 --- a/arch/arm/mm/cache-l2x0.c +++ b/arch/arm/mm/cache-l2x0.c @@ -49,7 +49,13 @@ static inline void cache_wait(void __iomem *reg, unsigned long mask) static inline void cache_sync(void) { void __iomem *base = l2x0_base; + +#ifdef CONFIG_ARM_ERRATA_753970 + /* write to an unmmapped register */ + writel_relaxed(0, base + L2X0_DUMMY_REG); +#else writel_relaxed(0, base + L2X0_CACHE_SYNC); +#endif cache_wait(base + L2X0_CACHE_SYNC, 1); } diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 0c1172b56b4..8e335623913 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -264,6 +264,12 @@ __v7_setup: orreq r10, r10, #1 << 6 @ set bit #6 mcreq p15, 0, r10, c15, c0, 1 @ write diagnostic register #endif +#ifdef CONFIG_ARM_ERRATA_751472 + cmp r6, #0x30 @ present prior to r3p0 + mrclt p15, 0, r10, c15, c0, 1 @ read diagnostic register + orrlt r10, r10, #1 << 11 @ set bit #11 + mcrlt p15, 0, r10, c15, c0, 1 @ write diagnostic register +#endif 3: mov r10, #0 #ifdef HARVARD_CACHE diff --git a/arch/arm/plat-omap/mailbox.c b/arch/arm/plat-omap/mailbox.c index 459b319a9fa..49d3208793e 100644 --- a/arch/arm/plat-omap/mailbox.c +++ b/arch/arm/plat-omap/mailbox.c @@ -322,15 +322,18 @@ static void omap_mbox_fini(struct omap_mbox *mbox) struct omap_mbox *omap_mbox_get(const char *name, struct notifier_block *nb) { - struct omap_mbox *mbox; - int ret; + struct omap_mbox *_mbox, *mbox = NULL; + int i, ret; if (!mboxes) return ERR_PTR(-EINVAL); - for (mbox = *mboxes; mbox; mbox++) - if (!strcmp(mbox->name, name)) + for (i = 0; (_mbox = mboxes[i]); i++) { + if (!strcmp(_mbox->name, name)) { + mbox = _mbox; break; + } + } if (!mbox) return ERR_PTR(-ENOENT); diff --git a/arch/arm/plat-s5p/dev-uart.c b/arch/arm/plat-s5p/dev-uart.c index 6a734288617..afaf87fdb93 100644 --- a/arch/arm/plat-s5p/dev-uart.c +++ b/arch/arm/plat-s5p/dev-uart.c @@ -28,7 +28,7 @@ static struct resource s5p_uart0_resource[] = { [0] = { .start = S5P_PA_UART0, - .end = S5P_PA_UART0 + S5P_SZ_UART, + .end = S5P_PA_UART0 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -51,7 +51,7 @@ static struct resource s5p_uart0_resource[] = { static struct resource s5p_uart1_resource[] = { [0] = { .start = S5P_PA_UART1, - .end = S5P_PA_UART1 + S5P_SZ_UART, + .end = S5P_PA_UART1 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -74,7 +74,7 @@ static struct resource s5p_uart1_resource[] = { static struct resource s5p_uart2_resource[] = { [0] = { .start = S5P_PA_UART2, - .end = S5P_PA_UART2 + S5P_SZ_UART, + .end = S5P_PA_UART2 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -98,7 +98,7 @@ static struct resource s5p_uart3_resource[] = { #if CONFIG_SERIAL_SAMSUNG_UARTS > 3 [0] = { .start = S5P_PA_UART3, - .end = S5P_PA_UART3 + S5P_SZ_UART, + .end = S5P_PA_UART3 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -123,7 +123,7 @@ static struct resource s5p_uart4_resource[] = { #if CONFIG_SERIAL_SAMSUNG_UARTS > 4 [0] = { .start = S5P_PA_UART4, - .end = S5P_PA_UART4 + S5P_SZ_UART, + .end = S5P_PA_UART4 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { @@ -148,7 +148,7 @@ static struct resource s5p_uart5_resource[] = { #if CONFIG_SERIAL_SAMSUNG_UARTS > 5 [0] = { .start = S5P_PA_UART5, - .end = S5P_PA_UART5 + S5P_SZ_UART, + .end = S5P_PA_UART5 + S5P_SZ_UART - 1, .flags = IORESOURCE_MEM, }, [1] = { diff --git a/arch/arm/plat-samsung/dev-ts.c b/arch/arm/plat-samsung/dev-ts.c index 236ef8427d7..3e4bd8147bf 100644 --- a/arch/arm/plat-samsung/dev-ts.c +++ b/arch/arm/plat-samsung/dev-ts.c @@ -58,4 +58,3 @@ void __init s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *pd) s3c_device_ts.dev.platform_data = npd; } -EXPORT_SYMBOL(s3c24xx_ts_set_platdata); diff --git a/arch/arm/plat-spear/include/plat/uncompress.h b/arch/arm/plat-spear/include/plat/uncompress.h index 99ba6789cc9..6dd455bafdf 100644 --- a/arch/arm/plat-spear/include/plat/uncompress.h +++ b/arch/arm/plat-spear/include/plat/uncompress.h @@ -24,10 +24,10 @@ static inline void putc(int c) { void __iomem *base = (void __iomem *)SPEAR_DBG_UART_BASE; - while (readl(base + UART01x_FR) & UART01x_FR_TXFF) + while (readl_relaxed(base + UART01x_FR) & UART01x_FR_TXFF) barrier(); - writel(c, base + UART01x_DR); + writel_relaxed(c, base + UART01x_DR); } static inline void flush(void) diff --git a/arch/arm/plat-spear/include/plat/vmalloc.h b/arch/arm/plat-spear/include/plat/vmalloc.h index 09e9372aea2..8c8b24d0704 100644 --- a/arch/arm/plat-spear/include/plat/vmalloc.h +++ b/arch/arm/plat-spear/include/plat/vmalloc.h @@ -14,6 +14,6 @@ #ifndef __PLAT_VMALLOC_H #define __PLAT_VMALLOC_H -#define VMALLOC_END 0xF0000000 +#define VMALLOC_END 0xF0000000UL #endif /* __PLAT_VMALLOC_H */ diff --git a/arch/cris/kernel/vmlinux.lds.S b/arch/cris/kernel/vmlinux.lds.S index 442218980db..c49be845f96 100644 --- a/arch/cris/kernel/vmlinux.lds.S +++ b/arch/cris/kernel/vmlinux.lds.S @@ -72,11 +72,6 @@ SECTIONS INIT_TEXT_SECTION(PAGE_SIZE) .init.data : { INIT_DATA } .init.setup : { INIT_SETUP(16) } -#ifdef CONFIG_ETRAX_ARCH_V32 - __start___param = .; - __param : { *(__param) } - __stop___param = .; -#endif .initcall.init : { INIT_CALLS } diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h index 991d5998d6b..fe56a23e1ff 100644 --- a/arch/powerpc/include/asm/machdep.h +++ b/arch/powerpc/include/asm/machdep.h @@ -240,6 +240,12 @@ struct machdep_calls { * claims to support kexec. */ int (*machine_kexec_prepare)(struct kimage *image); + + /* Called to perform the _real_ kexec. + * Do NOT allocate memory or fail here. We are past the point of + * no return. + */ + void (*machine_kexec)(struct kimage *image); #endif /* CONFIG_KEXEC */ #ifdef CONFIG_SUSPEND diff --git a/arch/powerpc/kernel/machine_kexec.c b/arch/powerpc/kernel/machine_kexec.c index 49a170af814..a5f8672eeff 100644 --- a/arch/powerpc/kernel/machine_kexec.c +++ b/arch/powerpc/kernel/machine_kexec.c @@ -87,7 +87,10 @@ void machine_kexec(struct kimage *image) save_ftrace_enabled = __ftrace_enabled_save(); - default_machine_kexec(image); + if (ppc_md.machine_kexec) + ppc_md.machine_kexec(image); + else + default_machine_kexec(image); __ftrace_enabled_restore(save_ftrace_enabled); diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c index 7a1d5cb7693..8303a6c65ef 100644 --- a/arch/powerpc/kernel/process.c +++ b/arch/powerpc/kernel/process.c @@ -353,6 +353,7 @@ static void switch_booke_debug_regs(struct thread_struct *new_thread) prime_debug_regs(new_thread); } #else /* !CONFIG_PPC_ADV_DEBUG_REGS */ +#ifndef CONFIG_HAVE_HW_BREAKPOINT static void set_debug_reg_defaults(struct thread_struct *thread) { if (thread->dabr) { @@ -360,6 +361,7 @@ static void set_debug_reg_defaults(struct thread_struct *thread) set_dabr(0); } } +#endif /* !CONFIG_HAVE_HW_BREAKPOINT */ #endif /* CONFIG_PPC_ADV_DEBUG_REGS */ int set_dabr(unsigned long dabr) @@ -670,11 +672,11 @@ void flush_thread(void) { discard_lazy_cpu_state(); -#ifdef CONFIG_HAVE_HW_BREAKPOINTS +#ifdef CONFIG_HAVE_HW_BREAKPOINT flush_ptrace_hw_breakpoint(current); -#else /* CONFIG_HAVE_HW_BREAKPOINTS */ +#else /* CONFIG_HAVE_HW_BREAKPOINT */ set_debug_reg_defaults(¤t->thread); -#endif /* CONFIG_HAVE_HW_BREAKPOINTS */ +#endif /* CONFIG_HAVE_HW_BREAKPOINT */ } void diff --git a/arch/powerpc/mm/tlb_hash64.c b/arch/powerpc/mm/tlb_hash64.c index 1ec06576f61..c14d09f614f 100644 --- a/arch/powerpc/mm/tlb_hash64.c +++ b/arch/powerpc/mm/tlb_hash64.c @@ -38,13 +38,11 @@ DEFINE_PER_CPU(struct ppc64_tlb_batch, ppc64_tlb_batch); * neesd to be flushed. This function will either perform the flush * immediately or will batch it up if the current CPU has an active * batch on it. - * - * Must be called from within some kind of spinlock/non-preempt region... */ void hpte_need_flush(struct mm_struct *mm, unsigned long addr, pte_t *ptep, unsigned long pte, int huge) { - struct ppc64_tlb_batch *batch = &__get_cpu_var(ppc64_tlb_batch); + struct ppc64_tlb_batch *batch = &get_cpu_var(ppc64_tlb_batch); unsigned long vsid, vaddr; unsigned int psize; int ssize; @@ -99,6 +97,7 @@ void hpte_need_flush(struct mm_struct *mm, unsigned long addr, */ if (!batch->active) { flush_hash_page(vaddr, rpte, psize, ssize, 0); + put_cpu_var(ppc64_tlb_batch); return; } @@ -127,6 +126,7 @@ void hpte_need_flush(struct mm_struct *mm, unsigned long addr, batch->index = ++i; if (i >= PPC64_TLB_BATCH_NR) __flush_tlb_pending(batch); + put_cpu_var(ppc64_tlb_batch); } /* diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c index 0851eb1e919..2751b3a8a66 100644 --- a/arch/s390/boot/compressed/misc.c +++ b/arch/s390/boot/compressed/misc.c @@ -133,11 +133,12 @@ unsigned long decompress_kernel(void) unsigned long output_addr; unsigned char *output; - check_ipl_parmblock((void *) 0, (unsigned long) output + SZ__bss_start); + output_addr = ((unsigned long) &_end + HEAP_SIZE + 4095UL) & -4096UL; + check_ipl_parmblock((void *) 0, output_addr + SZ__bss_start); memset(&_bss, 0, &_ebss - &_bss); free_mem_ptr = (unsigned long)&_end; free_mem_end_ptr = free_mem_ptr + HEAP_SIZE; - output = (unsigned char *) ((free_mem_end_ptr + 4095UL) & -4096UL); + output = (unsigned char *) output_addr; #ifdef CONFIG_BLK_DEV_INITRD /* diff --git a/arch/s390/include/asm/atomic.h b/arch/s390/include/asm/atomic.h index 76daea11718..5c5ba10384c 100644 --- a/arch/s390/include/asm/atomic.h +++ b/arch/s390/include/asm/atomic.h @@ -36,14 +36,19 @@ static inline int atomic_read(const atomic_t *v) { - barrier(); - return v->counter; + int c; + + asm volatile( + " l %0,%1\n" + : "=d" (c) : "Q" (v->counter)); + return c; } static inline void atomic_set(atomic_t *v, int i) { - v->counter = i; - barrier(); + asm volatile( + " st %1,%0\n" + : "=Q" (v->counter) : "d" (i)); } static inline int atomic_add_return(int i, atomic_t *v) @@ -128,14 +133,19 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) static inline long long atomic64_read(const atomic64_t *v) { - barrier(); - return v->counter; + long long c; + + asm volatile( + " lg %0,%1\n" + : "=d" (c) : "Q" (v->counter)); + return c; } static inline void atomic64_set(atomic64_t *v, long long i) { - v->counter = i; - barrier(); + asm volatile( + " stg %1,%0\n" + : "=Q" (v->counter) : "d" (i)); } static inline long long atomic64_add_return(long long i, atomic64_t *v) diff --git a/arch/s390/include/asm/cache.h b/arch/s390/include/asm/cache.h index 24aafa68b64..2a30d5ac066 100644 --- a/arch/s390/include/asm/cache.h +++ b/arch/s390/include/asm/cache.h @@ -13,6 +13,7 @@ #define L1_CACHE_BYTES 256 #define L1_CACHE_SHIFT 8 +#define NET_SKB_PAD 32 #define __read_mostly __attribute__((__section__(".data..read_mostly"))) diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 211ca3f7fd1..4ea15ca89b2 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -88,6 +88,7 @@ extern int acpi_disabled; extern int acpi_pci_disabled; extern int acpi_skip_timer_override; extern int acpi_use_timer_override; +extern int acpi_fix_pin2_polarity; extern u8 acpi_sci_flags; extern int acpi_sci_override_gsi; diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 4d0dfa0d998..43a18c77676 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -36,6 +36,11 @@ #define MSR_IA32_PERFCTR1 0x000000c2 #define MSR_FSB_FREQ 0x000000cd +#define MSR_NHM_SNB_PKG_CST_CFG_CTL 0x000000e2 +#define NHM_C3_AUTO_DEMOTE (1UL << 25) +#define NHM_C1_AUTO_DEMOTE (1UL << 26) +#define ATM_LNC_C6_AUTO_DEMOTE (1UL << 25) + #define MSR_MTRRcap 0x000000fe #define MSR_IA32_BBL_CR_CTL 0x00000119 diff --git a/arch/x86/include/asm/perf_event_p4.h b/arch/x86/include/asm/perf_event_p4.h index e2f6a99f14a..cc29086e30c 100644 --- a/arch/x86/include/asm/perf_event_p4.h +++ b/arch/x86/include/asm/perf_event_p4.h @@ -22,6 +22,7 @@ #define ARCH_P4_CNTRVAL_BITS (40) #define ARCH_P4_CNTRVAL_MASK ((1ULL << ARCH_P4_CNTRVAL_BITS) - 1) +#define ARCH_P4_UNFLAGGED_BIT ((1ULL) << (ARCH_P4_CNTRVAL_BITS - 1)) #define P4_ESCR_EVENT_MASK 0x7e000000U #define P4_ESCR_EVENT_SHIFT 25 diff --git a/arch/x86/include/asm/smpboot_hooks.h b/arch/x86/include/asm/smpboot_hooks.h index 6c22bf353f2..725b7783199 100644 --- a/arch/x86/include/asm/smpboot_hooks.h +++ b/arch/x86/include/asm/smpboot_hooks.h @@ -34,7 +34,7 @@ static inline void smpboot_restore_warm_reset_vector(void) */ CMOS_WRITE(0, 0xf); - *((volatile long *)phys_to_virt(apic->trampoline_phys_low)) = 0; + *((volatile u32 *)phys_to_virt(apic->trampoline_phys_low)) = 0; } static inline void __init smpboot_setup_io_apic(void) diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index b3a71137983..3e6e2d68f76 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -72,6 +72,7 @@ u8 acpi_sci_flags __initdata; int acpi_sci_override_gsi __initdata; int acpi_skip_timer_override __initdata; int acpi_use_timer_override __initdata; +int acpi_fix_pin2_polarity __initdata; #ifdef CONFIG_X86_LOCAL_APIC static u64 acpi_lapic_addr __initdata = APIC_DEFAULT_PHYS_BASE; @@ -415,10 +416,15 @@ acpi_parse_int_src_ovr(struct acpi_subtable_header * header, return 0; } - if (acpi_skip_timer_override && - intsrc->source_irq == 0 && intsrc->global_irq == 2) { - printk(PREFIX "BIOS IRQ0 pin2 override ignored.\n"); - return 0; + if (intsrc->source_irq == 0 && intsrc->global_irq == 2) { + if (acpi_skip_timer_override) { + printk(PREFIX "BIOS IRQ0 pin2 override ignored.\n"); + return 0; + } + if (acpi_fix_pin2_polarity && (intsrc->inti_flags & ACPI_MADT_POLARITY_MASK)) { + intsrc->inti_flags &= ~ACPI_MADT_POLARITY_MASK; + printk(PREFIX "BIOS IRQ0 pin2 override: forcing polarity to high active.\n"); + } } mp_override_legacy_irq(intsrc->source_irq, diff --git a/arch/x86/kernel/apb_timer.c b/arch/x86/kernel/apb_timer.c index 51ef31a89be..51d4e166306 100644 --- a/arch/x86/kernel/apb_timer.c +++ b/arch/x86/kernel/apb_timer.c @@ -284,7 +284,7 @@ static int __init apbt_clockevent_register(void) memcpy(&adev->evt, &apbt_clockevent, sizeof(struct clock_event_device)); if (mrst_timer_options == MRST_TIMER_LAPIC_APBT) { - apbt_clockevent.rating = APBT_CLOCKEVENT_RATING - 100; + adev->evt.rating = APBT_CLOCKEVENT_RATING - 100; global_clock_event = &adev->evt; printk(KERN_DEBUG "%s clockevent registered as global\n", global_clock_event->name); diff --git a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c index bd1cac747f6..52c93648e49 100644 --- a/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c +++ b/arch/x86/kernel/cpu/cpufreq/p4-clockmod.c @@ -158,9 +158,9 @@ static unsigned int cpufreq_p4_get_frequency(struct cpuinfo_x86 *c) { if (c->x86 == 0x06) { if (cpu_has(c, X86_FEATURE_EST)) - printk(KERN_WARNING PFX "Warning: EST-capable CPU " - "detected. The acpi-cpufreq module offers " - "voltage scaling in addition of frequency " + printk_once(KERN_WARNING PFX "Warning: EST-capable " + "CPU detected. The acpi-cpufreq module offers " + "voltage scaling in addition to frequency " "scaling. You should use that instead of " "p4-clockmod, if possible.\n"); switch (c->x86_model) { diff --git a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c index 35c7e65e59b..c567dec854f 100644 --- a/arch/x86/kernel/cpu/cpufreq/powernow-k8.c +++ b/arch/x86/kernel/cpu/cpufreq/powernow-k8.c @@ -1537,6 +1537,7 @@ static struct notifier_block cpb_nb = { static int __cpuinit powernowk8_init(void) { unsigned int i, supported_cpus = 0, cpu; + int rv; for_each_online_cpu(i) { int rc; @@ -1555,14 +1556,14 @@ static int __cpuinit powernowk8_init(void) cpb_capable = true; - register_cpu_notifier(&cpb_nb); - msrs = msrs_alloc(); if (!msrs) { printk(KERN_ERR "%s: Error allocating msrs!\n", __func__); return -ENOMEM; } + register_cpu_notifier(&cpb_nb); + rdmsr_on_cpus(cpu_online_mask, MSR_K7_HWCR, msrs); for_each_cpu(cpu, cpu_online_mask) { @@ -1574,7 +1575,13 @@ static int __cpuinit powernowk8_init(void) (cpb_enabled ? "on" : "off")); } - return cpufreq_register_driver(&cpufreq_amd64_driver); + rv = cpufreq_register_driver(&cpufreq_amd64_driver); + if (rv < 0 && boot_cpu_has(X86_FEATURE_CPB)) { + unregister_cpu_notifier(&cpb_nb); + msrs_free(msrs); + msrs = NULL; + } + return rv; } /* driver entry point for term */ diff --git a/arch/x86/kernel/cpu/perf_event_p4.c b/arch/x86/kernel/cpu/perf_event_p4.c index f7a0993c1e7..ff751a9f182 100644 --- a/arch/x86/kernel/cpu/perf_event_p4.c +++ b/arch/x86/kernel/cpu/perf_event_p4.c @@ -770,9 +770,14 @@ static inline int p4_pmu_clear_cccr_ovf(struct hw_perf_event *hwc) return 1; } - /* it might be unflagged overflow */ - rdmsrl(hwc->event_base + hwc->idx, v); - if (!(v & ARCH_P4_CNTRVAL_MASK)) + /* + * In some circumstances the overflow might issue an NMI but did + * not set P4_CCCR_OVF bit. Because a counter holds a negative value + * we simply check for high bit being set, if it's cleared it means + * the counter has reached zero value and continued counting before + * real NMI signal was received: + */ + if (!(v & ARCH_P4_UNFLAGGED_BIT)) return 1; return 0; diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 76b8cd953de..9efbdcc5642 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -143,15 +143,10 @@ static void __init ati_bugs(int num, int slot, int func) static u32 __init ati_sbx00_rev(int num, int slot, int func) { - u32 old, d; + u32 d; - d = read_pci_config(num, slot, func, 0x70); - old = d; - d &= ~(1<<8); - write_pci_config(num, slot, func, 0x70, d); d = read_pci_config(num, slot, func, 0x8); d &= 0xff; - write_pci_config(num, slot, func, 0x70, old); return d; } @@ -160,13 +155,16 @@ static void __init ati_bugs_contd(int num, int slot, int func) { u32 d, rev; - if (acpi_use_timer_override) - return; - rev = ati_sbx00_rev(num, slot, func); + if (rev >= 0x40) + acpi_fix_pin2_polarity = 1; + if (rev > 0x13) return; + if (acpi_use_timer_override) + return; + /* check for IRQ0 interrupt swap */ d = read_pci_config(num, slot, func, 0x64); if (!(d & (1<<14))) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index fc7aae1e2bc..715037caeb4 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -285,6 +285,14 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P4S800"), }, }, + { /* Handle problems with rebooting on VersaLogic Menlow boards */ + .callback = set_bios_reboot, + .ident = "VersaLogic Menlow based board", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "VersaLogic Corporation"), + DMI_MATCH(DMI_BOARD_NAME, "VersaLogic Menlow board"), + }, + }, { } }; diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 54ce246a383..63fec1531e8 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -2777,6 +2777,8 @@ static int dr_interception(struct vcpu_svm *svm) kvm_register_write(&svm->vcpu, reg, val); } + skip_emulated_instruction(&svm->vcpu); + return 1; } diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c index dab87464753..044bda5b317 100644 --- a/arch/x86/platform/olpc/olpc_dt.c +++ b/arch/x86/platform/olpc/olpc_dt.c @@ -140,8 +140,7 @@ void * __init prom_early_alloc(unsigned long size) * wasted bootmem) and hand off chunks of it to callers. */ res = alloc_bootmem(chunk_size); - if (!res) - return NULL; + BUG_ON(!res); prom_early_allocated += chunk_size; memset(res, 0, chunk_size); free_mem = chunk_size; diff --git a/block/blk-core.c b/block/blk-core.c index 2f4002f79a2..518dd423a5f 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -352,7 +352,7 @@ void blk_start_queue(struct request_queue *q) WARN_ON(!irqs_disabled()); queue_flag_clear(QUEUE_FLAG_STOPPED, q); - __blk_run_queue(q); + __blk_run_queue(q, false); } EXPORT_SYMBOL(blk_start_queue); @@ -403,13 +403,14 @@ EXPORT_SYMBOL(blk_sync_queue); /** * __blk_run_queue - run a single device queue * @q: The queue to run + * @force_kblockd: Don't run @q->request_fn directly. Use kblockd. * * Description: * See @blk_run_queue. This variant must be called with the queue lock * held and interrupts disabled. * */ -void __blk_run_queue(struct request_queue *q) +void __blk_run_queue(struct request_queue *q, bool force_kblockd) { blk_remove_plug(q); @@ -423,7 +424,7 @@ void __blk_run_queue(struct request_queue *q) * Only recurse once to avoid overrunning the stack, let the unplug * handling reinvoke the handler shortly if we already got there. */ - if (!queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { + if (!force_kblockd && !queue_flag_test_and_set(QUEUE_FLAG_REENTER, q)) { q->request_fn(q); queue_flag_clear(QUEUE_FLAG_REENTER, q); } else { @@ -446,7 +447,7 @@ void blk_run_queue(struct request_queue *q) unsigned long flags; spin_lock_irqsave(q->queue_lock, flags); - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irqrestore(q->queue_lock, flags); } EXPORT_SYMBOL(blk_run_queue); @@ -1053,7 +1054,7 @@ void blk_insert_request(struct request_queue *q, struct request *rq, drive_stat_acct(rq, 1); __elv_add_request(q, rq, where, 0); - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irqrestore(q->queue_lock, flags); } EXPORT_SYMBOL(blk_insert_request); @@ -2610,13 +2611,6 @@ int kblockd_schedule_work(struct request_queue *q, struct work_struct *work) } EXPORT_SYMBOL(kblockd_schedule_work); -int kblockd_schedule_delayed_work(struct request_queue *q, - struct delayed_work *dwork, unsigned long delay) -{ - return queue_delayed_work(kblockd_workqueue, dwork, delay); -} -EXPORT_SYMBOL(kblockd_schedule_delayed_work); - int __init blk_dev_init(void) { BUILD_BUG_ON(__REQ_NR_BITS > 8 * diff --git a/block/blk-flush.c b/block/blk-flush.c index 54b123d6563..b27d0208611 100644 --- a/block/blk-flush.c +++ b/block/blk-flush.c @@ -66,10 +66,12 @@ static void blk_flush_complete_seq_end_io(struct request_queue *q, /* * Moving a request silently to empty queue_head may stall the - * queue. Kick the queue in those cases. + * queue. Kick the queue in those cases. This function is called + * from request completion path and calling directly into + * request_fn may confuse the driver. Always use kblockd. */ if (was_empty && next_rq) - __blk_run_queue(q); + __blk_run_queue(q, true); } static void pre_flush_end_io(struct request *rq, int error) @@ -130,7 +132,7 @@ static struct request *queue_next_fseq(struct request_queue *q) BUG(); } - elv_insert(q, rq, ELEVATOR_INSERT_FRONT); + elv_insert(q, rq, ELEVATOR_INSERT_REQUEUE); return rq; } diff --git a/block/blk-lib.c b/block/blk-lib.c index 1a320d2406b..eec78becb35 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -132,7 +132,7 @@ static void bio_batch_end_io(struct bio *bio, int err) } /** - * blkdev_issue_zeroout generate number of zero filed write bios + * blkdev_issue_zeroout - generate number of zero filed write bios * @bdev: blockdev to issue * @sector: start sector * @nr_sects: number of sectors to write diff --git a/block/blk-throttle.c b/block/blk-throttle.c index a89043a3caa..e36cc10a346 100644 --- a/block/blk-throttle.c +++ b/block/blk-throttle.c @@ -20,6 +20,11 @@ static int throtl_quantum = 32; /* Throttling is performed over 100ms slice and after that slice is renewed */ static unsigned long throtl_slice = HZ/10; /* 100 ms */ +/* A workqueue to queue throttle related work */ +static struct workqueue_struct *kthrotld_workqueue; +static void throtl_schedule_delayed_work(struct throtl_data *td, + unsigned long delay); + struct throtl_rb_root { struct rb_root rb; struct rb_node *left; @@ -345,10 +350,9 @@ static void throtl_schedule_next_dispatch(struct throtl_data *td) update_min_dispatch_time(st); if (time_before_eq(st->min_disptime, jiffies)) - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); else - throtl_schedule_delayed_work(td->queue, - (st->min_disptime - jiffies)); + throtl_schedule_delayed_work(td, (st->min_disptime - jiffies)); } static inline void @@ -815,10 +819,10 @@ void blk_throtl_work(struct work_struct *work) } /* Call with queue lock held */ -void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) +static void +throtl_schedule_delayed_work(struct throtl_data *td, unsigned long delay) { - struct throtl_data *td = q->td; struct delayed_work *dwork = &td->throtl_work; if (total_nr_queued(td) > 0) { @@ -827,12 +831,11 @@ void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) * Cancel that and schedule a new one. */ __cancel_delayed_work(dwork); - kblockd_schedule_delayed_work(q, dwork, delay); + queue_delayed_work(kthrotld_workqueue, dwork, delay); throtl_log(td, "schedule work. delay=%lu jiffies=%lu", delay, jiffies); } } -EXPORT_SYMBOL(throtl_schedule_delayed_work); static void throtl_destroy_tg(struct throtl_data *td, struct throtl_grp *tg) @@ -920,7 +923,7 @@ static void throtl_update_blkio_group_read_bps(void *key, smp_mb__after_atomic_inc(); /* Schedule a work now to process the limit change */ - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_write_bps(void *key, @@ -934,7 +937,7 @@ static void throtl_update_blkio_group_write_bps(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_read_iops(void *key, @@ -948,7 +951,7 @@ static void throtl_update_blkio_group_read_iops(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } static void throtl_update_blkio_group_write_iops(void *key, @@ -962,7 +965,7 @@ static void throtl_update_blkio_group_write_iops(void *key, smp_mb__before_atomic_inc(); atomic_inc(&td->limits_changed); smp_mb__after_atomic_inc(); - throtl_schedule_delayed_work(td->queue, 0); + throtl_schedule_delayed_work(td, 0); } void throtl_shutdown_timer_wq(struct request_queue *q) @@ -1135,6 +1138,10 @@ void blk_throtl_exit(struct request_queue *q) static int __init throtl_init(void) { + kthrotld_workqueue = alloc_workqueue("kthrotld", WQ_MEM_RECLAIM, 0); + if (!kthrotld_workqueue) + panic("Failed to create kthrotld\n"); + blkio_policy_register(&blkio_policy_throtl); return 0; } diff --git a/block/cfq-iosched.c b/block/cfq-iosched.c index 7be4c795962..ea83a4f0c27 100644 --- a/block/cfq-iosched.c +++ b/block/cfq-iosched.c @@ -3355,7 +3355,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, cfqd->busy_queues > 1) { cfq_del_timer(cfqd, cfqq); cfq_clear_cfqq_wait_request(cfqq); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); } else { cfq_blkiocg_update_idle_time_stats( &cfqq->cfqg->blkg); @@ -3370,7 +3370,7 @@ cfq_rq_enqueued(struct cfq_data *cfqd, struct cfq_queue *cfqq, * this new queue is RT and the current one is BE */ cfq_preempt_queue(cfqd, cfqq); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); } } @@ -3731,7 +3731,7 @@ static void cfq_kick_queue(struct work_struct *work) struct request_queue *q = cfqd->queue; spin_lock_irq(q->queue_lock); - __blk_run_queue(cfqd->queue); + __blk_run_queue(cfqd->queue, false); spin_unlock_irq(q->queue_lock); } diff --git a/block/elevator.c b/block/elevator.c index 2569512830d..236e93c1f46 100644 --- a/block/elevator.c +++ b/block/elevator.c @@ -602,7 +602,7 @@ void elv_quiesce_start(struct request_queue *q) */ elv_drain_elevator(q); while (q->rq.elvpriv) { - __blk_run_queue(q); + __blk_run_queue(q, false); spin_unlock_irq(q->queue_lock); msleep(10); spin_lock_irq(q->queue_lock); @@ -651,7 +651,7 @@ void elv_insert(struct request_queue *q, struct request *rq, int where) * with anything. There's no point in delaying queue * processing. */ - __blk_run_queue(q); + __blk_run_queue(q, false); break; case ELEVATOR_INSERT_SORT: diff --git a/block/genhd.c b/block/genhd.c index 6a5b772aa20..cbf1112a885 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1355,7 +1355,7 @@ int invalidate_partition(struct gendisk *disk, int partno) struct block_device *bdev = bdget_disk(disk, partno); if (bdev) { fsync_bdev(bdev); - res = __invalidate_device(bdev); + res = __invalidate_device(bdev, true); bdput(bdev); } return res; diff --git a/block/ioctl.c b/block/ioctl.c index 9049d460fa8..1124cd29726 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -294,9 +294,11 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd, return -EINVAL; if (get_user(n, (int __user *) arg)) return -EFAULT; - if (!(mode & FMODE_EXCL) && - blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0) - return -EBUSY; + if (!(mode & FMODE_EXCL)) { + bdgrab(bdev); + if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0) + return -EBUSY; + } ret = set_blocksize(bdev, n); if (!(mode & FMODE_EXCL)) blkdev_put(bdev, mode | FMODE_EXCL); diff --git a/drivers/acpi/acpica/aclocal.h b/drivers/acpi/acpica/aclocal.h index 54784bb42ce..edc25867ad9 100644 --- a/drivers/acpi/acpica/aclocal.h +++ b/drivers/acpi/acpica/aclocal.h @@ -416,10 +416,15 @@ struct acpi_gpe_handler_info { u8 originally_enabled; /* True if GPE was originally enabled */ }; +struct acpi_gpe_notify_object { + struct acpi_namespace_node *node; + struct acpi_gpe_notify_object *next; +}; + union acpi_gpe_dispatch_info { struct acpi_namespace_node *method_node; /* Method node for this GPE level */ struct acpi_gpe_handler_info *handler; /* Installed GPE handler */ - struct acpi_namespace_node *device_node; /* Parent _PRW device for implicit notify */ + struct acpi_gpe_notify_object device; /* List of _PRW devices for implicit notify */ }; /* diff --git a/drivers/acpi/acpica/evgpe.c b/drivers/acpi/acpica/evgpe.c index 14988a86066..f4725212eb4 100644 --- a/drivers/acpi/acpica/evgpe.c +++ b/drivers/acpi/acpica/evgpe.c @@ -457,6 +457,7 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) acpi_status status; struct acpi_gpe_event_info *local_gpe_event_info; struct acpi_evaluate_info *info; + struct acpi_gpe_notify_object *notify_object; ACPI_FUNCTION_TRACE(ev_asynch_execute_gpe_method); @@ -508,10 +509,18 @@ static void ACPI_SYSTEM_XFACE acpi_ev_asynch_execute_gpe_method(void *context) * from this thread -- because handlers may in turn run other * control methods. */ - status = - acpi_ev_queue_notify_request(local_gpe_event_info->dispatch. - device_node, - ACPI_NOTIFY_DEVICE_WAKE); + status = acpi_ev_queue_notify_request( + local_gpe_event_info->dispatch.device.node, + ACPI_NOTIFY_DEVICE_WAKE); + + notify_object = local_gpe_event_info->dispatch.device.next; + while (ACPI_SUCCESS(status) && notify_object) { + status = acpi_ev_queue_notify_request( + notify_object->node, + ACPI_NOTIFY_DEVICE_WAKE); + notify_object = notify_object->next; + } + break; case ACPI_GPE_DISPATCH_METHOD: diff --git a/drivers/acpi/acpica/evxfgpe.c b/drivers/acpi/acpica/evxfgpe.c index 3b20a3401b6..52aaff3df56 100644 --- a/drivers/acpi/acpica/evxfgpe.c +++ b/drivers/acpi/acpica/evxfgpe.c @@ -198,7 +198,9 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, acpi_status status = AE_BAD_PARAMETER; struct acpi_gpe_event_info *gpe_event_info; struct acpi_namespace_node *device_node; + struct acpi_gpe_notify_object *notify_object; acpi_cpu_flags flags; + u8 gpe_dispatch_mask; ACPI_FUNCTION_TRACE(acpi_setup_gpe_for_wake); @@ -221,27 +223,49 @@ acpi_setup_gpe_for_wake(acpi_handle wake_device, goto unlock_and_exit; } + if (wake_device == ACPI_ROOT_OBJECT) { + goto out; + } + /* * If there is no method or handler for this GPE, then the * wake_device will be notified whenever this GPE fires (aka * "implicit notify") Note: The GPE is assumed to be * level-triggered (for windows compatibility). */ - if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == - ACPI_GPE_DISPATCH_NONE) && (wake_device != ACPI_ROOT_OBJECT)) { + gpe_dispatch_mask = gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK; + if (gpe_dispatch_mask != ACPI_GPE_DISPATCH_NONE + && gpe_dispatch_mask != ACPI_GPE_DISPATCH_NOTIFY) { + goto out; + } - /* Validate wake_device is of type Device */ + /* Validate wake_device is of type Device */ - device_node = ACPI_CAST_PTR(struct acpi_namespace_node, - wake_device); - if (device_node->type != ACPI_TYPE_DEVICE) { - goto unlock_and_exit; - } + device_node = ACPI_CAST_PTR(struct acpi_namespace_node, wake_device); + if (device_node->type != ACPI_TYPE_DEVICE) { + goto unlock_and_exit; + } + + if (gpe_dispatch_mask == ACPI_GPE_DISPATCH_NONE) { gpe_event_info->flags = (ACPI_GPE_DISPATCH_NOTIFY | ACPI_GPE_LEVEL_TRIGGERED); - gpe_event_info->dispatch.device_node = device_node; + gpe_event_info->dispatch.device.node = device_node; + gpe_event_info->dispatch.device.next = NULL; + } else { + /* There are multiple devices to notify implicitly. */ + + notify_object = ACPI_ALLOCATE_ZEROED(sizeof(*notify_object)); + if (!notify_object) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + notify_object->node = device_node; + notify_object->next = gpe_event_info->dispatch.device.next; + gpe_event_info->dispatch.device.next = notify_object; } + out: gpe_event_info->flags |= ACPI_GPE_CAN_WAKE; status = AE_OK; diff --git a/drivers/acpi/debugfs.c b/drivers/acpi/debugfs.c index 5df67f1d6c6..384f7abcff7 100644 --- a/drivers/acpi/debugfs.c +++ b/drivers/acpi/debugfs.c @@ -26,7 +26,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, size_t count, loff_t *ppos) { static char *buf; - static int uncopied_bytes; + static u32 max_size; + static u32 uncopied_bytes; + struct acpi_table_header table; acpi_status status; @@ -37,19 +39,24 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (copy_from_user(&table, user_buf, sizeof(struct acpi_table_header))) return -EFAULT; - uncopied_bytes = table.length; - buf = kzalloc(uncopied_bytes, GFP_KERNEL); + uncopied_bytes = max_size = table.length; + buf = kzalloc(max_size, GFP_KERNEL); if (!buf) return -ENOMEM; } - if (uncopied_bytes < count) { - kfree(buf); + if (buf == NULL) + return -EINVAL; + + if ((*ppos > max_size) || + (*ppos + count > max_size) || + (*ppos + count < count) || + (count > uncopied_bytes)) return -EINVAL; - } if (copy_from_user(buf + (*ppos), user_buf, count)) { kfree(buf); + buf = NULL; return -EFAULT; } @@ -59,6 +66,7 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, if (!uncopied_bytes) { status = acpi_install_method(buf); kfree(buf); + buf = NULL; if (ACPI_FAILURE(status)) return -EINVAL; add_taint(TAINT_OVERRIDDEN_ACPI_TABLE); diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 8cbfaa687d7..fe81c851ca8 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -2177,7 +2177,7 @@ static void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms return; } - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) { + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) { retcode = ERR_PERM; goto fail; } diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index b9ba04fc2b3..77fc76f8aea 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -3281,7 +3281,7 @@ static int set_geometry(unsigned int cmd, struct floppy_struct *g, struct block_device *bdev = opened_bdev[cnt]; if (!bdev || ITYPE(drive_state[cnt].fd_device) != type) continue; - __invalidate_device(bdev); + __invalidate_device(bdev, true); } mutex_unlock(&open_lock); } else { diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 49e6a545eb6..dbf31ec9114 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -78,7 +78,6 @@ #include <asm/uaccess.h> -static DEFINE_MUTEX(loop_mutex); static LIST_HEAD(loop_devices); static DEFINE_MUTEX(loop_devices_mutex); @@ -1501,11 +1500,9 @@ static int lo_open(struct block_device *bdev, fmode_t mode) { struct loop_device *lo = bdev->bd_disk->private_data; - mutex_lock(&loop_mutex); mutex_lock(&lo->lo_ctl_mutex); lo->lo_refcnt++; mutex_unlock(&lo->lo_ctl_mutex); - mutex_unlock(&loop_mutex); return 0; } @@ -1515,7 +1512,6 @@ static int lo_release(struct gendisk *disk, fmode_t mode) struct loop_device *lo = disk->private_data; int err; - mutex_lock(&loop_mutex); mutex_lock(&lo->lo_ctl_mutex); if (--lo->lo_refcnt) @@ -1540,7 +1536,6 @@ static int lo_release(struct gendisk *disk, fmode_t mode) out: mutex_unlock(&lo->lo_ctl_mutex); out_unlocked: - mutex_unlock(&loop_mutex); return 0; } diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 411ae9c9b38..7e0ebd4a1a7 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1043,8 +1043,6 @@ static int btusb_probe(struct usb_interface *intf, usb_set_intfdata(intf, data); - usb_enable_autosuspend(interface_to_usbdev(intf)); - return 0; } diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 9252e85706e..780498d7658 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -773,18 +773,23 @@ int __init agp_amd64_init(void) #else printk(KERN_INFO PFX "You can boot with agp=try_unsupported\n"); #endif + pci_unregister_driver(&agp_amd64_pci_driver); return -ENODEV; } /* First check that we have at least one AMD64 NB */ - if (!pci_dev_present(amd_nb_misc_ids)) + if (!pci_dev_present(amd_nb_misc_ids)) { + pci_unregister_driver(&agp_amd64_pci_driver); return -ENODEV; + } /* Look for any AGP bridge */ agp_amd64_pci_driver.id_table = agp_amd64_pci_promisc_table; err = driver_attach(&agp_amd64_pci_driver.driver); - if (err == 0 && agp_bridges_found == 0) + if (err == 0 && agp_bridges_found == 0) { + pci_unregister_driver(&agp_amd64_pci_driver); err = -ENODEV; + } } return err; } diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h index c195bfeade1..5feebe2800e 100644 --- a/drivers/char/agp/intel-agp.h +++ b/drivers/char/agp/intel-agp.h @@ -130,6 +130,7 @@ #define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) #define I915_IFPADDR 0x60 +#define I830_HIC 0x70 /* Intel 965G registers */ #define I965_MSAC 0x62 diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c index fab3d3265ad..0d09b537bb9 100644 --- a/drivers/char/agp/intel-gtt.c +++ b/drivers/char/agp/intel-gtt.c @@ -21,6 +21,7 @@ #include <linux/kernel.h> #include <linux/pagemap.h> #include <linux/agp_backend.h> +#include <linux/delay.h> #include <asm/smp.h> #include "agp.h" #include "intel-agp.h" @@ -70,12 +71,8 @@ static struct _intel_private { u32 __iomem *gtt; /* I915G */ bool clear_fake_agp; /* on first access via agp, fill with scratch */ int num_dcache_entries; - union { - void __iomem *i9xx_flush_page; - void *i8xx_flush_page; - }; + void __iomem *i9xx_flush_page; char *i81x_gtt_table; - struct page *i8xx_page; struct resource ifp_resource; int resource_valid; struct page *scratch_page; @@ -722,28 +719,6 @@ static int intel_fake_agp_fetch_size(void) static void i830_cleanup(void) { - if (intel_private.i8xx_flush_page) { - kunmap(intel_private.i8xx_flush_page); - intel_private.i8xx_flush_page = NULL; - } - - __free_page(intel_private.i8xx_page); - intel_private.i8xx_page = NULL; -} - -static void intel_i830_setup_flush(void) -{ - /* return if we've already set the flush mechanism up */ - if (intel_private.i8xx_page) - return; - - intel_private.i8xx_page = alloc_page(GFP_KERNEL); - if (!intel_private.i8xx_page) - return; - - intel_private.i8xx_flush_page = kmap(intel_private.i8xx_page); - if (!intel_private.i8xx_flush_page) - i830_cleanup(); } /* The chipset_flush interface needs to get data that has already been @@ -758,14 +733,27 @@ static void intel_i830_setup_flush(void) */ static void i830_chipset_flush(void) { - unsigned int *pg = intel_private.i8xx_flush_page; + unsigned long timeout = jiffies + msecs_to_jiffies(1000); + + /* Forcibly evict everything from the CPU write buffers. + * clflush appears to be insufficient. + */ + wbinvd_on_all_cpus(); + + /* Now we've only seen documents for this magic bit on 855GM, + * we hope it exists for the other gen2 chipsets... + * + * Also works as advertised on my 845G. + */ + writel(readl(intel_private.registers+I830_HIC) | (1<<31), + intel_private.registers+I830_HIC); - memset(pg, 0, 1024); + while (readl(intel_private.registers+I830_HIC) & (1<<31)) { + if (time_after(jiffies, timeout)) + break; - if (cpu_has_clflush) - clflush_cache_range(pg, 1024); - else if (wbinvd_on_all_cpus() != 0) - printk(KERN_ERR "Timed out waiting for cache flush.\n"); + udelay(50); + } } static void i830_write_entry(dma_addr_t addr, unsigned int entry, @@ -849,8 +837,6 @@ static int i830_setup(void) intel_private.gtt_bus_addr = reg_addr + I810_PTE_BASE; - intel_i830_setup_flush(); - return 0; } diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index 777181a2e60..bcbbc71febb 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -830,8 +830,7 @@ static void monitor_card(unsigned long p) test_bit(IS_ANY_T1, &dev->flags))) { DEBUGP(4, dev, "Perform AUTOPPS\n"); set_bit(IS_AUTOPPS_ACT, &dev->flags); - ptsreq.protocol = ptsreq.protocol = - (0x01 << dev->proto); + ptsreq.protocol = (0x01 << dev->proto); ptsreq.flags = 0x01; ptsreq.pts1 = 0x00; ptsreq.pts2 = 0x00; diff --git a/drivers/char/pcmcia/ipwireless/main.c b/drivers/char/pcmcia/ipwireless/main.c index 94b8eb4d691..444155a305a 100644 --- a/drivers/char/pcmcia/ipwireless/main.c +++ b/drivers/char/pcmcia/ipwireless/main.c @@ -78,7 +78,6 @@ static void signalled_reboot_callback(void *callback_data) static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) { struct ipw_dev *ipw = priv_data; - struct resource *io_resource; int ret; p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH; @@ -92,9 +91,12 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) if (ret) return ret; - io_resource = request_region(p_dev->resource[0]->start, - resource_size(p_dev->resource[0]), - IPWIRELESS_PCCARD_NAME); + if (!request_region(p_dev->resource[0]->start, + resource_size(p_dev->resource[0]), + IPWIRELESS_PCCARD_NAME)) { + ret = -EBUSY; + goto exit; + } p_dev->resource[2]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE; @@ -105,22 +107,25 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], p_dev->card_addr); if (ret != 0) - goto exit2; + goto exit1; ipw->is_v2_card = resource_size(p_dev->resource[2]) == 0x100; - ipw->attr_memory = ioremap(p_dev->resource[2]->start, + ipw->common_memory = ioremap(p_dev->resource[2]->start, resource_size(p_dev->resource[2])); - request_mem_region(p_dev->resource[2]->start, - resource_size(p_dev->resource[2]), - IPWIRELESS_PCCARD_NAME); + if (!request_mem_region(p_dev->resource[2]->start, + resource_size(p_dev->resource[2]), + IPWIRELESS_PCCARD_NAME)) { + ret = -EBUSY; + goto exit2; + } p_dev->resource[3]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM | WIN_ENABLE; p_dev->resource[3]->end = 0; /* this used to be 0x1000 */ ret = pcmcia_request_window(p_dev, p_dev->resource[3], 0); if (ret != 0) - goto exit2; + goto exit3; ret = pcmcia_map_mem_page(p_dev, p_dev->resource[3], 0); if (ret != 0) @@ -128,23 +133,28 @@ static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data) ipw->attr_memory = ioremap(p_dev->resource[3]->start, resource_size(p_dev->resource[3])); - request_mem_region(p_dev->resource[3]->start, - resource_size(p_dev->resource[3]), - IPWIRELESS_PCCARD_NAME); + if (!request_mem_region(p_dev->resource[3]->start, + resource_size(p_dev->resource[3]), + IPWIRELESS_PCCARD_NAME)) { + ret = -EBUSY; + goto exit4; + } return 0; +exit4: + iounmap(ipw->attr_memory); exit3: + release_mem_region(p_dev->resource[2]->start, + resource_size(p_dev->resource[2])); exit2: - if (ipw->common_memory) { - release_mem_region(p_dev->resource[2]->start, - resource_size(p_dev->resource[2])); - iounmap(ipw->common_memory); - } + iounmap(ipw->common_memory); exit1: - release_resource(io_resource); + release_region(p_dev->resource[0]->start, + resource_size(p_dev->resource[0])); +exit: pcmcia_disable_device(p_dev); - return -1; + return ret; } static int config_ipwireless(struct ipw_dev *ipw) @@ -219,6 +229,8 @@ exit: static void release_ipwireless(struct ipw_dev *ipw) { + release_region(ipw->link->resource[0]->start, + resource_size(ipw->link->resource[0])); if (ipw->common_memory) { release_mem_region(ipw->link->resource[2]->start, resource_size(ipw->link->resource[2])); diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index faf5a2c6592..1f46f1cd922 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -364,14 +364,12 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip, tpm_protected_ordinal_duration[ordinal & TPM_PROTECTED_ORDINAL_MASK]; - if (duration_idx != TPM_UNDEFINED) { + if (duration_idx != TPM_UNDEFINED) duration = chip->vendor.duration[duration_idx]; - /* if duration is 0, it's because chip->vendor.duration wasn't */ - /* filled yet, so we set the lowest timeout just to give enough */ - /* time for tpm_get_timeouts() to succeed */ - return (duration <= 0 ? HZ : duration); - } else + if (duration <= 0) return 2 * 60 * HZ; + else + return duration; } EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); @@ -577,11 +575,9 @@ duration: if (rc) return; - if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || - be32_to_cpu(tpm_cmd.header.out.length) - != sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32)) + if (be32_to_cpu(tpm_cmd.header.out.return_code) + != 3 * sizeof(u32)) return; - duration_cap = &tpm_cmd.params.getcap_out.cap.duration; chip->vendor.duration[TPM_SHORT] = usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); @@ -941,18 +937,6 @@ ssize_t tpm_show_caps_1_2(struct device * dev, } EXPORT_SYMBOL_GPL(tpm_show_caps_1_2); -ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d %d %d\n", - jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), - jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), - jiffies_to_usecs(chip->vendor.duration[TPM_LONG])); -} -EXPORT_SYMBOL_GPL(tpm_show_timeouts); - ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index d84ff772c26..72ddb031b69 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -56,8 +56,6 @@ extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr, char *); extern ssize_t tpm_show_temp_deactivated(struct device *, struct device_attribute *attr, char *); -extern ssize_t tpm_show_timeouts(struct device *, - struct device_attribute *attr, char *); struct tpm_chip; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 0d1d38e5f26..dd21df55689 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -376,7 +376,6 @@ static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); -static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); static struct attribute *tis_attrs[] = { &dev_attr_pubek.attr, @@ -386,8 +385,7 @@ static struct attribute *tis_attrs[] = { &dev_attr_owned.attr, &dev_attr_temp_deactivated.attr, &dev_attr_caps.attr, - &dev_attr_cancel.attr, - &dev_attr_timeouts.attr, NULL, + &dev_attr_cancel.attr, NULL, }; static struct attribute_group tis_attr_grp = { diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1109f6848a4..5cb4d09919d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1919,8 +1919,10 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) ret = sysdev_driver_register(&cpu_sysdev_class, &cpufreq_sysdev_driver); + if (ret) + goto err_null_driver; - if ((!ret) && !(cpufreq_driver->flags & CPUFREQ_STICKY)) { + if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) { int i; ret = -ENODEV; @@ -1935,21 +1937,22 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) if (ret) { dprintk("no CPU initialized for driver %s\n", driver_data->name); - sysdev_driver_unregister(&cpu_sysdev_class, - &cpufreq_sysdev_driver); - - spin_lock_irqsave(&cpufreq_driver_lock, flags); - cpufreq_driver = NULL; - spin_unlock_irqrestore(&cpufreq_driver_lock, flags); + goto err_sysdev_unreg; } } - if (!ret) { - register_hotcpu_notifier(&cpufreq_cpu_notifier); - dprintk("driver %s up and running\n", driver_data->name); - cpufreq_debug_enable_ratelimit(); - } + register_hotcpu_notifier(&cpufreq_cpu_notifier); + dprintk("driver %s up and running\n", driver_data->name); + cpufreq_debug_enable_ratelimit(); + return 0; +err_sysdev_unreg: + sysdev_driver_unregister(&cpu_sysdev_class, + &cpufreq_sysdev_driver); +err_null_driver: + spin_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_driver = NULL; + spin_unlock_irqrestore(&cpufreq_driver_lock, flags); return ret; } EXPORT_SYMBOL_GPL(cpufreq_register_driver); diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c index 3dadfa2a852..28d1d3c24d6 100644 --- a/drivers/gpu/drm/drm_irq.c +++ b/drivers/gpu/drm/drm_irq.c @@ -164,8 +164,10 @@ static void vblank_disable_and_save(struct drm_device *dev, int crtc) * available. In that case we can't account for this and just * hope for the best. */ - if ((vblrc > 0) && (abs(diff_ns) > 1000000)) + if ((vblrc > 0) && (abs64(diff_ns) > 1000000)) { atomic_inc(&dev->_vblank_count[crtc]); + smp_mb__after_atomic_inc(); + } /* Invalidate all timestamps while vblank irq's are off. */ clear_vblank_timestamps(dev, crtc); @@ -491,6 +493,12 @@ void drm_calc_timestamping_constants(struct drm_crtc *crtc) /* Dot clock in Hz: */ dotclock = (u64) crtc->hwmode.clock * 1000; + /* Fields of interlaced scanout modes are only halve a frame duration. + * Double the dotclock to get halve the frame-/line-/pixelduration. + */ + if (crtc->hwmode.flags & DRM_MODE_FLAG_INTERLACE) + dotclock *= 2; + /* Valid dotclock? */ if (dotclock > 0) { /* Convert scanline length in pixels and video dot clock to @@ -603,14 +611,6 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, return -EAGAIN; } - /* Don't know yet how to handle interlaced or - * double scan modes. Just no-op for now. - */ - if (mode->flags & (DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLSCAN)) { - DRM_DEBUG("crtc %d: Noop due to unsupported mode.\n", crtc); - return -ENOTSUPP; - } - /* Get current scanout position with system timestamp. * Repeat query up to DRM_TIMESTAMP_MAXRETRIES times * if single query takes longer than max_error nanoseconds. @@ -858,10 +858,11 @@ static void drm_update_vblank_count(struct drm_device *dev, int crtc) if (rc) { tslot = atomic_read(&dev->_vblank_count[crtc]) + diff; vblanktimestamp(dev, crtc, tslot) = t_vblank; - smp_wmb(); } + smp_mb__before_atomic_inc(); atomic_add(diff, &dev->_vblank_count[crtc]); + smp_mb__after_atomic_inc(); } /** @@ -1011,7 +1012,8 @@ int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_modeset_ctl *modeset = data; - int crtc, ret = 0; + int ret = 0; + unsigned int crtc; /* If drm_vblank_init() hasn't been called yet, just no-op */ if (!dev->num_crtcs) @@ -1293,15 +1295,16 @@ bool drm_handle_vblank(struct drm_device *dev, int crtc) * e.g., due to spurious vblank interrupts. We need to * ignore those for accounting. */ - if (abs(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { + if (abs64(diff_ns) > DRM_REDUNDANT_VBLIRQ_THRESH_NS) { /* Store new timestamp in ringbuffer. */ vblanktimestamp(dev, crtc, vblcount + 1) = tvblank; - smp_wmb(); /* Increment cooked vblank count. This also atomically commits * the timestamp computed above. */ + smp_mb__before_atomic_inc(); atomic_inc(&dev->_vblank_count[crtc]); + smp_mb__after_atomic_inc(); } else { DRM_DEBUG("crtc %d: Redundant vblirq ignored. diff_ns = %d\n", crtc, (int) diff_ns); diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 17bd766f208..e33d9be7df3 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1895,6 +1895,17 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) if (IS_GEN2(dev)) dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(30)); + /* 965GM sometimes incorrectly writes to hardware status page (HWS) + * using 32bit addressing, overwriting memory if HWS is located + * above 4GB. + * + * The documentation also mentions an issue with undefined + * behaviour if any general state is accessed within a page above 4GB, + * which also needs to be handled carefully. + */ + if (IS_BROADWATER(dev) || IS_CRESTLINE(dev)) + dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32)); + mmio_bar = IS_GEN2(dev) ? 1 : 0; dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, 0); if (!dev_priv->regs) { diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c index 22a32b9932c..79a04fde69b 100644 --- a/drivers/gpu/drm/i915/i915_gem_tiling.c +++ b/drivers/gpu/drm/i915/i915_gem_tiling.c @@ -184,7 +184,7 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev) static bool i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) { - int tile_width; + int tile_width, tile_height; /* Linear is always fine */ if (tiling_mode == I915_TILING_NONE) @@ -215,6 +215,20 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) } } + if (IS_GEN2(dev) || + (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev))) + tile_height = 32; + else + tile_height = 8; + /* i8xx is strange: It has 2 interleaved rows of tiles, so needs an even + * number of tile rows. */ + if (IS_GEN2(dev)) + tile_height *= 2; + + /* Size needs to be aligned to a full tile row */ + if (size & (tile_height * stride - 1)) + return false; + /* 965+ just needs multiples of tile width */ if (INTEL_INFO(dev)->gen >= 4) { if (stride & (tile_width - 1)) diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 97f946dcc1a..8a9e08bf1cf 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c @@ -316,6 +316,8 @@ static void i915_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct intel_encoder *encoder; + DRM_DEBUG_KMS("running encoder hotplug functions\n"); + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) if (encoder->hot_plug) encoder->hot_plug(encoder); @@ -1649,9 +1651,7 @@ static int ironlake_irq_postinstall(struct drm_device *dev) } else { hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG | SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG; - hotplug_mask |= SDE_AUX_MASK | SDE_FDI_MASK | SDE_TRANS_MASK; - I915_WRITE(FDI_RXA_IMR, 0); - I915_WRITE(FDI_RXB_IMR, 0); + hotplug_mask |= SDE_AUX_MASK; } dev_priv->pch_irq_mask = ~hotplug_mask; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 15d94c63918..729d4233b76 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1553,17 +1553,7 @@ /* Backlight control */ #define BLC_PWM_CTL 0x61254 -#define BACKLIGHT_MODULATION_FREQ_SHIFT (17) #define BLC_PWM_CTL2 0x61250 /* 965+ only */ -#define BLM_COMBINATION_MODE (1 << 30) -/* - * This is the most significant 15 bits of the number of backlight cycles in a - * complete cycle of the modulated backlight control. - * - * The actual value is this field multiplied by two. - */ -#define BACKLIGHT_MODULATION_FREQ_MASK (0x7fff << 17) -#define BLM_LEGACY_MODE (1 << 16) /* * This is the number of cycles out of the backlight modulation cycle for which * the backlight is on. diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 3b006536b3d..e79b25bbee6 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -1630,19 +1630,19 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y, struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj; wait_event(dev_priv->pending_flip_queue, + atomic_read(&dev_priv->mm.wedged) || atomic_read(&obj->pending_flip) == 0); /* Big Hammer, we also need to ensure that any pending * MI_WAIT_FOR_EVENT inside a user batch buffer on the * current scanout is retired before unpinning the old * framebuffer. + * + * This should only fail upon a hung GPU, in which case we + * can safely continue. */ ret = i915_gem_object_flush_gpu(obj, false); - if (ret) { - i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj); - mutex_unlock(&dev->struct_mutex); - return ret; - } + (void) ret; } ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y, @@ -2045,6 +2045,31 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc) atomic_read(&obj->pending_flip) == 0); } +static bool intel_crtc_driving_pch(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + struct intel_encoder *encoder; + + /* + * If there's a non-PCH eDP on this crtc, it must be DP_A, and that + * must be driven by its own crtc; no sharing is possible. + */ + list_for_each_entry(encoder, &mode_config->encoder_list, base.head) { + if (encoder->base.crtc != crtc) + continue; + + switch (encoder->type) { + case INTEL_OUTPUT_EDP: + if (!intel_encoder_is_pch_edp(&encoder->base)) + return false; + continue; + } + } + + return true; +} + static void ironlake_crtc_enable(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; @@ -2053,6 +2078,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) int pipe = intel_crtc->pipe; int plane = intel_crtc->plane; u32 reg, temp; + bool is_pch_port = false; if (intel_crtc->active) return; @@ -2066,7 +2092,56 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN); } - ironlake_fdi_enable(crtc); + is_pch_port = intel_crtc_driving_pch(crtc); + + if (is_pch_port) + ironlake_fdi_enable(crtc); + else { + /* disable CPU FDI tx and PCH FDI rx */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + I915_WRITE(reg, temp & ~FDI_TX_ENABLE); + POSTING_READ(reg); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~(0x7 << 16); + temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + I915_WRITE(reg, temp & ~FDI_RX_ENABLE); + + POSTING_READ(reg); + udelay(100); + + /* Ironlake workaround, disable clock pointer after downing FDI */ + if (HAS_PCH_IBX(dev)) + I915_WRITE(FDI_RX_CHICKEN(pipe), + I915_READ(FDI_RX_CHICKEN(pipe) & + ~FDI_RX_PHASE_SYNC_POINTER_ENABLE)); + + /* still set train pattern 1 */ + reg = FDI_TX_CTL(pipe); + temp = I915_READ(reg); + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + I915_WRITE(reg, temp); + + reg = FDI_RX_CTL(pipe); + temp = I915_READ(reg); + if (HAS_PCH_CPT(dev)) { + temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; + temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; + } else { + temp &= ~FDI_LINK_TRAIN_NONE; + temp |= FDI_LINK_TRAIN_PATTERN_1; + } + /* BPC in FDI rx is consistent with that in PIPECONF */ + temp &= ~(0x07 << 16); + temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11; + I915_WRITE(reg, temp); + + POSTING_READ(reg); + udelay(100); + } /* Enable panel fitting for LVDS */ if (dev_priv->pch_pf_size && @@ -2100,6 +2175,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) intel_flush_display_plane(dev, plane); } + /* Skip the PCH stuff if possible */ + if (!is_pch_port) + goto done; + /* For PCH output, training FDI link */ if (IS_GEN6(dev)) gen6_fdi_link_train(crtc); @@ -2184,7 +2263,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc) I915_WRITE(reg, temp | TRANS_ENABLE); if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100)) DRM_ERROR("failed to enable transcoder %d\n", pipe); - +done: intel_crtc_load_lut(crtc); intel_update_fbc(dev); intel_crtc_update_cursor(crtc, true); @@ -6496,7 +6575,7 @@ static void ironlake_disable_rc6(struct drm_device *dev) POSTING_READ(RSTDBYCTL); } - ironlake_disable_rc6(dev); + ironlake_teardown_rc6(dev); } static int ironlake_setup_rc6(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index c65992df458..d860abeda70 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -30,8 +30,6 @@ #include "intel_drv.h" -#define PCI_LBPC 0xf4 /* legacy/combination backlight modes */ - void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode, struct drm_display_mode *adjusted_mode) @@ -112,19 +110,6 @@ done: dev_priv->pch_pf_size = (width << 16) | height; } -static int is_backlight_combination_mode(struct drm_device *dev) -{ - struct drm_i915_private *dev_priv = dev->dev_private; - - if (INTEL_INFO(dev)->gen >= 4) - return I915_READ(BLC_PWM_CTL2) & BLM_COMBINATION_MODE; - - if (IS_GEN2(dev)) - return I915_READ(BLC_PWM_CTL) & BLM_LEGACY_MODE; - - return 0; -} - static u32 i915_read_blc_pwm_ctl(struct drm_i915_private *dev_priv) { u32 val; @@ -181,9 +166,6 @@ u32 intel_panel_get_max_backlight(struct drm_device *dev) if (INTEL_INFO(dev)->gen < 4) max &= ~1; } - - if (is_backlight_combination_mode(dev)) - max *= 0xff; } DRM_DEBUG_DRIVER("max backlight PWM = %d\n", max); @@ -201,15 +183,6 @@ u32 intel_panel_get_backlight(struct drm_device *dev) val = I915_READ(BLC_PWM_CTL) & BACKLIGHT_DUTY_CYCLE_MASK; if (IS_PINEVIEW(dev)) val >>= 1; - - if (is_backlight_combination_mode(dev)){ - u8 lbpc; - - val &= ~1; - pci_read_config_byte(dev->pdev, PCI_LBPC, &lbpc); - val *= lbpc; - val >>= 1; - } } DRM_DEBUG_DRIVER("get backlight PWM = %d\n", val); @@ -232,16 +205,6 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level) if (HAS_PCH_SPLIT(dev)) return intel_pch_panel_set_backlight(dev, level); - - if (is_backlight_combination_mode(dev)){ - u32 max = intel_panel_get_max_backlight(dev); - u8 lpbc; - - lpbc = level * 0xfe / max + 1; - level /= lpbc; - pci_write_config_byte(dev->pdev, PCI_LBPC, lpbc); - } - tmp = I915_READ(BLC_PWM_CTL); if (IS_PINEVIEW(dev)) { tmp &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1); diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index d38a4d9f9b0..a52184007f5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -49,7 +49,10 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo) DRM_ERROR("bo %p still attached to GEM object\n", bo); nv10_mem_put_tile_region(dev, nvbo->tile, NULL); - nouveau_vm_put(&nvbo->vma); + if (nvbo->vma.node) { + nouveau_vm_unmap(&nvbo->vma); + nouveau_vm_put(&nvbo->vma); + } kfree(nvbo); } diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 56deae5bf02..93fa735c8c1 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -3490,7 +3490,7 @@ void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track track->num_texture = 16; track->maxy = 4096; track->separate_cube = 0; - track->aaresolve = true; + track->aaresolve = false; track->aa.robj = NULL; } @@ -3801,8 +3801,6 @@ static int r100_startup(struct radeon_device *rdev) r100_mc_program(rdev); /* Resume clock */ r100_clock_startup(rdev); - /* Initialize GPU configuration (# pipes, ...) */ -// r100_gpu_init(rdev); /* Initialize GART (initialize after TTM so we can allocate * memory through TTM but finalize after TTM) */ r100_enable_bm(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c index 0e657095de7..3e7e7f9eb78 100644 --- a/drivers/gpu/drm/radeon/radeon_display.c +++ b/drivers/gpu/drm/radeon/radeon_display.c @@ -971,7 +971,7 @@ void radeon_compute_pll_legacy(struct radeon_pll *pll, max_fractional_feed_div = pll->max_frac_feedback_div; } - for (post_div = min_post_div; post_div <= max_post_div; ++post_div) { + for (post_div = max_post_div; post_div >= min_post_div; --post_div) { uint32_t ref_div; if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1)) diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 66324b5bb5b..cc44bdfec80 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -113,11 +113,14 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev, u32 tiling_flags = 0; int ret; int aligned_size, size; + int height = mode_cmd->height; /* need to align pitch with crtc limits */ mode_cmd->pitch = radeon_align_pitch(rdev, mode_cmd->width, mode_cmd->bpp, fb_tiled) * ((mode_cmd->bpp + 1) / 8); - size = mode_cmd->pitch * mode_cmd->height; + if (rdev->family >= CHIP_R600) + height = ALIGN(mode_cmd->height, 8); + size = mode_cmd->pitch * height; aligned_size = ALIGN(size, PAGE_SIZE); ret = radeon_gem_object_create(rdev, aligned_size, 0, RADEON_GEM_DOMAIN_VRAM, diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 773e484f164..297bc9a7d6e 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -238,13 +238,13 @@ config SENSORS_K8TEMP will be called k8temp. config SENSORS_K10TEMP - tristate "AMD Phenom/Sempron/Turion/Opteron temperature sensor" + tristate "AMD Family 10h/11h/12h/14h temperature sensor" depends on X86 && PCI help If you say yes here you get support for the temperature sensor(s) inside your CPU. Supported are later revisions of - the AMD Family 10h and all revisions of the AMD Family 11h - microarchitectures. + the AMD Family 10h and all revisions of the AMD Family 11h, + 12h (Llano), and 14h (Brazos) microarchitectures. This driver can also be built as a module. If so, the module will be called k10temp. @@ -455,13 +455,14 @@ config SENSORS_JZ4740 called jz4740-hwmon. config SENSORS_JC42 - tristate "JEDEC JC42.4 compliant temperature sensors" + tristate "JEDEC JC42.4 compliant memory module temperature sensors" depends on I2C help - If you say yes here you get support for Jedec JC42.4 compliant - temperature sensors. Support will include, but not be limited to, - ADT7408, CAT34TS02,, CAT6095, MAX6604, MCP9805, MCP98242, MCP98243, - MCP9843, SE97, SE98, STTS424, TSE2002B3, and TS3000B3. + If you say yes here, you get support for JEDEC JC42.4 compliant + temperature sensors, which are used on many DDR3 memory modules for + mobile devices and servers. Support will include, but not be limited + to, ADT7408, CAT34TS02, CAT6095, MAX6604, MCP9805, MCP98242, MCP98243, + MCP9843, SE97, SE98, STTS424(E), TSE2002B3, and TS3000B3. This driver can also be built as a module. If so, the module will be called jc42. @@ -574,7 +575,7 @@ config SENSORS_LM85 help If you say yes here you get support for National Semiconductor LM85 sensor chips and clones: ADM1027, ADT7463, ADT7468, EMC6D100, - EMC6D101 and EMC6D102. + EMC6D101, EMC6D102, and EMC6D103. This driver can also be built as a module. If so, the module will be called lm85. diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c index 86d822aa9bb..d46c0c758dd 100644 --- a/drivers/hwmon/ad7414.c +++ b/drivers/hwmon/ad7414.c @@ -242,6 +242,7 @@ static const struct i2c_device_id ad7414_id[] = { { "ad7414", 0 }, {} }; +MODULE_DEVICE_TABLE(i2c, ad7414_id); static struct i2c_driver ad7414_driver = { .driver = { diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c index f13c843a296..5cc3e3784b4 100644 --- a/drivers/hwmon/adt7411.c +++ b/drivers/hwmon/adt7411.c @@ -334,6 +334,7 @@ static const struct i2c_device_id adt7411_id[] = { { "adt7411", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, adt7411_id); static struct i2c_driver adt7411_driver = { .driver = { diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 340fc78c8dd..93499123706 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -53,6 +53,8 @@ static const unsigned short normal_i2c[] = { /* Configuration register defines */ #define JC42_CFG_CRIT_ONLY (1 << 2) +#define JC42_CFG_TCRIT_LOCK (1 << 6) +#define JC42_CFG_EVENT_LOCK (1 << 7) #define JC42_CFG_SHUTDOWN (1 << 8) #define JC42_CFG_HYST_SHIFT 9 #define JC42_CFG_HYST_MASK 0x03 @@ -332,7 +334,7 @@ static ssize_t set_temp_crit_hyst(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct jc42_data *data = i2c_get_clientdata(client); - long val; + unsigned long val; int diff, hyst; int err; int ret = count; @@ -380,14 +382,14 @@ static ssize_t show_alarm(struct device *dev, static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit, set_temp_crit); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_min, S_IRUGO, show_temp_min, set_temp_min); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_max, set_temp_max); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, +static DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, show_temp_crit_hyst, set_temp_crit_hyst); static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp_max_hyst, NULL); @@ -412,8 +414,31 @@ static struct attribute *jc42_attributes[] = { NULL }; +static mode_t jc42_attribute_mode(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct i2c_client *client = to_i2c_client(dev); + struct jc42_data *data = i2c_get_clientdata(client); + unsigned int config = data->config; + bool readonly; + + if (attr == &dev_attr_temp1_crit.attr) + readonly = config & JC42_CFG_TCRIT_LOCK; + else if (attr == &dev_attr_temp1_min.attr || + attr == &dev_attr_temp1_max.attr) + readonly = config & JC42_CFG_EVENT_LOCK; + else if (attr == &dev_attr_temp1_crit_hyst.attr) + readonly = config & (JC42_CFG_EVENT_LOCK | JC42_CFG_TCRIT_LOCK); + else + readonly = true; + + return S_IRUGO | (readonly ? 0 : S_IWUSR); +} + static const struct attribute_group jc42_group = { .attrs = jc42_attributes, + .is_visible = jc42_attribute_mode, }; /* Return 0 if detection is successful, -ENODEV otherwise */ diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index da5a2404cd3..82bf65aa296 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -1,5 +1,5 @@ /* - * k10temp.c - AMD Family 10h/11h processor hardware monitoring + * k10temp.c - AMD Family 10h/11h/12h/14h processor hardware monitoring * * Copyright (c) 2009 Clemens Ladisch <clemens@ladisch.de> * @@ -25,7 +25,7 @@ #include <linux/pci.h> #include <asm/processor.h> -MODULE_DESCRIPTION("AMD Family 10h/11h CPU core temperature monitor"); +MODULE_DESCRIPTION("AMD Family 10h/11h/12h/14h CPU core temperature monitor"); MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>"); MODULE_LICENSE("GPL"); @@ -208,6 +208,7 @@ static void __devexit k10temp_remove(struct pci_dev *pdev) static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_11H_NB_MISC) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, {} }; MODULE_DEVICE_TABLE(pci, k10temp_id_table); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 1e229847f37..d2cc2866081 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -41,7 +41,7 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; enum chips { any_chip, lm85b, lm85c, adm1027, adt7463, adt7468, - emc6d100, emc6d102 + emc6d100, emc6d102, emc6d103 }; /* The LM85 registers */ @@ -90,6 +90,9 @@ enum chips { #define LM85_VERSTEP_EMC6D100_A0 0x60 #define LM85_VERSTEP_EMC6D100_A1 0x61 #define LM85_VERSTEP_EMC6D102 0x65 +#define LM85_VERSTEP_EMC6D103_A0 0x68 +#define LM85_VERSTEP_EMC6D103_A1 0x69 +#define LM85_VERSTEP_EMC6D103S 0x6A /* Also known as EMC6D103:A2 */ #define LM85_REG_CONFIG 0x40 @@ -348,6 +351,7 @@ static const struct i2c_device_id lm85_id[] = { { "emc6d100", emc6d100 }, { "emc6d101", emc6d100 }, { "emc6d102", emc6d102 }, + { "emc6d103", emc6d103 }, { } }; MODULE_DEVICE_TABLE(i2c, lm85_id); @@ -1250,6 +1254,20 @@ static int lm85_detect(struct i2c_client *client, struct i2c_board_info *info) case LM85_VERSTEP_EMC6D102: type_name = "emc6d102"; break; + case LM85_VERSTEP_EMC6D103_A0: + case LM85_VERSTEP_EMC6D103_A1: + type_name = "emc6d103"; + break; + /* + * Registers apparently missing in EMC6D103S/EMC6D103:A2 + * compared to EMC6D103:A0, EMC6D103:A1, and EMC6D102 + * (according to the data sheets), but used unconditionally + * in the driver: 62[5:7], 6D[0:7], and 6E[0:7]. + * So skip EMC6D103S for now. + case LM85_VERSTEP_EMC6D103S: + type_name = "emc6d103s"; + break; + */ } } else { dev_dbg(&adapter->dev, @@ -1283,6 +1301,7 @@ static int lm85_probe(struct i2c_client *client, case adt7468: case emc6d100: case emc6d102: + case emc6d103: data->freq_map = adm1027_freq_map; break; default: @@ -1468,7 +1487,7 @@ static struct lm85_data *lm85_update_device(struct device *dev) /* More alarm bits */ data->alarms |= lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; - } else if (data->type == emc6d102) { + } else if (data->type == emc6d102 || data->type == emc6d103) { /* Have to read LSB bits after the MSB ones because the reading of the MSB bits has frozen the LSBs (backward from the ADM1027). diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index b605ff3a1fa..829a2a1029f 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -847,11 +847,15 @@ complete: dev_err(dev->dev, "Arbitration lost\n"); err |= OMAP_I2C_STAT_AL; } + /* + * ProDB0017052: Clear ARDY bit twice + */ if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | - OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); + OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR | + OMAP_I2C_STAT_ARDY)); omap_i2c_complete_cmd(dev, err); return IRQ_HANDLED; } @@ -1137,12 +1141,41 @@ omap_i2c_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_SUSPEND +static int omap_i2c_suspend(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) + dev->bus->pm->runtime_suspend(dev); + + return 0; +} + +static int omap_i2c_resume(struct device *dev) +{ + if (!pm_runtime_suspended(dev)) + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) + dev->bus->pm->runtime_resume(dev); + + return 0; +} + +static struct dev_pm_ops omap_i2c_pm_ops = { + .suspend = omap_i2c_suspend, + .resume = omap_i2c_resume, +}; +#define OMAP_I2C_PM_OPS (&omap_i2c_pm_ops) +#else +#define OMAP_I2C_PM_OPS NULL +#endif + static struct platform_driver omap_i2c_driver = { .probe = omap_i2c_probe, .remove = omap_i2c_remove, .driver = { .name = "omap_i2c", .owner = THIS_MODULE, + .pm = OMAP_I2C_PM_OPS, }, }; diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 495be451d32..266135ddf7f 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -942,7 +942,7 @@ stu300_probe(struct platform_device *pdev) adap->owner = THIS_MODULE; /* DDC class but actually often used for more generic I2C */ adap->class = I2C_CLASS_DDC; - strncpy(adap->name, "ST Microelectronics DDC I2C adapter", + strlcpy(adap->name, "ST Microelectronics DDC I2C adapter", sizeof(adap->name)); adap->nr = bus_nr; adap->algo = &stu300_algo; diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 1fa091e0569..4a5c4a44ffb 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -62,6 +62,7 @@ #include <linux/notifier.h> #include <linux/cpu.h> #include <asm/mwait.h> +#include <asm/msr.h> #define INTEL_IDLE_VERSION "0.4" #define PREFIX "intel_idle: " @@ -85,6 +86,12 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state); static struct cpuidle_state *cpuidle_state_table; /* + * Hardware C-state auto-demotion may not always be optimal. + * Indicate which enable bits to clear here. + */ +static unsigned long long auto_demotion_disable_flags; + +/* * Set this flag for states where the HW flushes the TLB for us * and so we don't need cross-calls to keep it consistent. * If this flag is set, SW flushes the TLB, so even if the @@ -281,6 +288,15 @@ static struct notifier_block setup_broadcast_notifier = { .notifier_call = setup_broadcast_cpuhp_notify, }; +static void auto_demotion_disable(void *dummy) +{ + unsigned long long msr_bits; + + rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); + msr_bits &= ~auto_demotion_disable_flags; + wrmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr_bits); +} + /* * intel_idle_probe() */ @@ -324,11 +340,17 @@ static int intel_idle_probe(void) case 0x25: /* Westmere */ case 0x2C: /* Westmere */ cpuidle_state_table = nehalem_cstates; + auto_demotion_disable_flags = + (NHM_C1_AUTO_DEMOTE | NHM_C3_AUTO_DEMOTE); break; case 0x1C: /* 28 - Atom Processor */ + cpuidle_state_table = atom_cstates; + break; + case 0x26: /* 38 - Lincroft Atom Processor */ cpuidle_state_table = atom_cstates; + auto_demotion_disable_flags = ATM_LNC_C6_AUTO_DEMOTE; break; case 0x2A: /* SNB */ @@ -436,6 +458,8 @@ static int intel_idle_cpuidle_devices_init(void) return -EIO; } } + if (auto_demotion_disable_flags) + smp_call_function(auto_demotion_disable, NULL, 1); return 0; } diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index 8aba0ba57de..2d749937a96 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -193,10 +193,11 @@ static int addr4_resolve(struct sockaddr_in *src_in, fl.nl_u.ip4_u.saddr = src_ip; fl.oif = addr->bound_dev_if; - ret = ip_route_output_key(&init_net, &rt, &fl); - if (ret) + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { + ret = PTR_ERR(rt); goto out; - + } src_in->sin_family = AF_INET; src_in->sin_addr.s_addr = rt->rt_src; diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index d02dcc6e596..e0ccbc53fbc 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -354,7 +354,8 @@ static struct rtable *find_route(struct t3cdev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c index 8b00e6c46f0..77b0eef2aad 100644 --- a/drivers/infiniband/hw/cxgb4/cm.c +++ b/drivers/infiniband/hw/cxgb4/cm.c @@ -331,7 +331,8 @@ static struct rtable *find_route(struct c4iw_dev *dev, __be32 local_ip, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; } diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index ec3aa11c36c..e81599cb1fe 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1112,7 +1112,8 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi memset(&fl, 0, sizeof fl); fl.nl_u.ip4_u.daddr = htonl(dst_ip); - if (ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { printk(KERN_ERR "%s: ip_route_output_key failed for 0x%08X\n", __func__, dst_ip); return rc; diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index 23cf8fc933e..5b8f59d6c3e 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -360,7 +360,7 @@ static int gameport_queue_event(void *object, struct module *owner, event->owner = owner; list_add_tail(&event->node, &gameport_event_list); - schedule_work(&gameport_event_work); + queue_work(system_long_wq, &gameport_event_work); out: spin_unlock_irqrestore(&gameport_event_lock, flags); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index ac471b77c18..99ce9032d08 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -71,8 +71,9 @@ struct tegra_kbc { spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; + bool use_fn_map; const struct tegra_kbc_platform_data *pdata; - unsigned short keycode[KBC_MAX_KEY]; + unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; unsigned int num_pressed_keys; struct timer_list timer; @@ -178,6 +179,40 @@ static const u32 tegra_kbc_default_keymap[] = { KEY(15, 5, KEY_F2), KEY(15, 6, KEY_CAPSLOCK), KEY(15, 7, KEY_F6), + + /* Software Handled Function Keys */ + KEY(20, 0, KEY_KP7), + + KEY(21, 0, KEY_KP9), + KEY(21, 1, KEY_KP8), + KEY(21, 2, KEY_KP4), + KEY(21, 4, KEY_KP1), + + KEY(22, 1, KEY_KPSLASH), + KEY(22, 2, KEY_KP6), + KEY(22, 3, KEY_KP5), + KEY(22, 4, KEY_KP3), + KEY(22, 5, KEY_KP2), + KEY(22, 7, KEY_KP0), + + KEY(27, 1, KEY_KPASTERISK), + KEY(27, 3, KEY_KPMINUS), + KEY(27, 4, KEY_KPPLUS), + KEY(27, 5, KEY_KPDOT), + + KEY(28, 5, KEY_VOLUMEUP), + + KEY(29, 3, KEY_HOME), + KEY(29, 4, KEY_END), + KEY(29, 5, KEY_BRIGHTNESSDOWN), + KEY(29, 6, KEY_VOLUMEDOWN), + KEY(29, 7, KEY_BRIGHTNESSUP), + + KEY(30, 0, KEY_NUMLOCK), + KEY(30, 1, KEY_SCROLLLOCK), + KEY(30, 2, KEY_MUTE), + + KEY(31, 4, KEY_HELP), }; static const struct matrix_keymap_data tegra_kbc_default_keymap_data = { @@ -224,6 +259,7 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) unsigned int i; unsigned int num_down = 0; unsigned long flags; + bool fn_keypress = false; spin_lock_irqsave(&kbc->lock, flags); for (i = 0; i < KBC_MAX_KPENT; i++) { @@ -237,11 +273,28 @@ static void tegra_kbc_report_keys(struct tegra_kbc *kbc) MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); scancodes[num_down] = scancode; - keycodes[num_down++] = kbc->keycode[scancode]; + keycodes[num_down] = kbc->keycode[scancode]; + /* If driver uses Fn map, do not report the Fn key. */ + if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map) + fn_keypress = true; + else + num_down++; } val >>= 8; } + + /* + * If the platform uses Fn keymaps, translate keys on a Fn keypress. + * Function keycodes are KBC_MAX_KEY apart from the plain keycodes. + */ + if (fn_keypress) { + for (i = 0; i < num_down; i++) { + scancodes[i] += KBC_MAX_KEY; + keycodes[i] = kbc->keycode[scancodes[i]]; + } + } + spin_unlock_irqrestore(&kbc->lock, flags); tegra_kbc_report_released_keys(kbc->idev, @@ -594,8 +647,11 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) input_dev->keycode = kbc->keycode; input_dev->keycodesize = sizeof(kbc->keycode[0]); - input_dev->keycodemax = ARRAY_SIZE(kbc->keycode); + input_dev->keycodemax = KBC_MAX_KEY; + if (pdata->use_fn_map) + input_dev->keycodemax *= 2; + kbc->use_fn_map = pdata->use_fn_map; keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, input_dev->keycode, input_dev->keybit); diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index 25e5d042a72..7453938bf5e 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -51,6 +51,29 @@ #define SYN_EXT_CAP_REQUESTS(c) (((c) & 0x700000) >> 20) #define SYN_CAP_MULTI_BUTTON_NO(ec) (((ec) & 0x00f000) >> 12) #define SYN_CAP_PRODUCT_ID(ec) (((ec) & 0xff0000) >> 16) + +/* + * The following describes response for the 0x0c query. + * + * byte mask name meaning + * ---- ---- ------- ------------ + * 1 0x01 adjustable threshold capacitive button sensitivity + * can be adjusted + * 1 0x02 report max query 0x0d gives max coord reported + * 1 0x04 clearpad sensor is ClearPad product + * 1 0x08 advanced gesture not particularly meaningful + * 1 0x10 clickpad bit 0 1-button ClickPad + * 1 0x60 multifinger mode identifies firmware finger counting + * (not reporting!) algorithm. + * Not particularly meaningful + * 1 0x80 covered pad W clipped to 14, 15 == pad mostly covered + * 2 0x01 clickpad bit 1 2-button ClickPad + * 2 0x02 deluxe LED controls touchpad support LED commands + * ala multimedia control bar + * 2 0x04 reduced filtering firmware does less filtering on + * position data, driver should watch + * for noise. + */ #define SYN_CAP_CLICKPAD(ex0c) ((ex0c) & 0x100000) /* 1-button ClickPad */ #define SYN_CAP_CLICKPAD2BTN(ex0c) ((ex0c) & 0x000100) /* 2-button ClickPad */ #define SYN_CAP_MAX_DIMENSIONS(ex0c) ((ex0c) & 0x020000) diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index 7c38d1fbabf..ba70058e2be 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -299,7 +299,7 @@ static int serio_queue_event(void *object, struct module *owner, event->owner = owner; list_add_tail(&event->node, &serio_event_list); - schedule_work(&serio_event_work); + queue_work(system_long_wq, &serio_event_work); out: spin_unlock_irqrestore(&serio_event_lock, flags); diff --git a/drivers/isdn/hardware/eicon/istream.c b/drivers/isdn/hardware/eicon/istream.c index 18f8798442f..7bd5baa547b 100644 --- a/drivers/isdn/hardware/eicon/istream.c +++ b/drivers/isdn/hardware/eicon/istream.c @@ -62,7 +62,7 @@ void diva_xdi_provide_istream_info (ADAPTER* a, stream interface. If synchronous service was requested, then function does return amount of data written to stream. - 'final' does indicate that pice of data to be written is + 'final' does indicate that piece of data to be written is final part of frame (necessary only by structured datatransfer) return 0 if zero lengh packet was written return -1 if stream is full diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 049eaf12aaa..1f23e048f07 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -134,7 +134,7 @@ static void cn_ulog_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp) { struct dm_ulog_request *tfr = (struct dm_ulog_request *)(msg + 1); - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; spin_lock(&receiving_list_lock); diff --git a/drivers/md/linear.c b/drivers/md/linear.c index 8a2f767f26d..0ed7f6bc2a7 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -216,7 +216,6 @@ static int linear_run (mddev_t *mddev) if (md_check_no_bitmap(mddev)) return -EINVAL; - mddev->queue->queue_lock = &mddev->queue->__queue_lock; conf = linear_conf(mddev, mddev->raid_disks); if (!conf) diff --git a/drivers/md/md.c b/drivers/md/md.c index 0cc30ecda4c..818313e277e 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -553,6 +553,9 @@ static mddev_t * mddev_find(dev_t unit) { mddev_t *mddev, *new = NULL; + if (unit && MAJOR(unit) != MD_MAJOR) + unit &= ~((1<<MdpMinorShift)-1); + retry: spin_lock(&all_mddevs_lock); @@ -4138,10 +4141,10 @@ array_size_store(mddev_t *mddev, const char *buf, size_t len) } mddev->array_sectors = sectors; - set_capacity(mddev->gendisk, mddev->array_sectors); - if (mddev->pers) + if (mddev->pers) { + set_capacity(mddev->gendisk, mddev->array_sectors); revalidate_disk(mddev->gendisk); - + } return len; } @@ -4624,6 +4627,7 @@ static int do_md_run(mddev_t *mddev) } set_capacity(mddev->gendisk, mddev->array_sectors); revalidate_disk(mddev->gendisk); + mddev->changed = 1; kobject_uevent(&disk_to_dev(mddev->gendisk)->kobj, KOBJ_CHANGE); out: return err; @@ -4712,6 +4716,7 @@ static void md_clean(mddev_t *mddev) mddev->sync_speed_min = mddev->sync_speed_max = 0; mddev->recovery = 0; mddev->in_sync = 0; + mddev->changed = 0; mddev->degraded = 0; mddev->safemode = 0; mddev->bitmap_info.offset = 0; @@ -4827,6 +4832,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) set_capacity(disk, 0); mutex_unlock(&mddev->open_mutex); + mddev->changed = 1; revalidate_disk(disk); if (mddev->ro) @@ -6011,7 +6017,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) atomic_inc(&mddev->openers); mutex_unlock(&mddev->open_mutex); - check_disk_size_change(mddev->gendisk, bdev); + check_disk_change(bdev); out: return err; } @@ -6026,6 +6032,21 @@ static int md_release(struct gendisk *disk, fmode_t mode) return 0; } + +static int md_media_changed(struct gendisk *disk) +{ + mddev_t *mddev = disk->private_data; + + return mddev->changed; +} + +static int md_revalidate(struct gendisk *disk) +{ + mddev_t *mddev = disk->private_data; + + mddev->changed = 0; + return 0; +} static const struct block_device_operations md_fops = { .owner = THIS_MODULE, @@ -6036,6 +6057,8 @@ static const struct block_device_operations md_fops = .compat_ioctl = md_compat_ioctl, #endif .getgeo = md_getgeo, + .media_changed = md_media_changed, + .revalidate_disk= md_revalidate, }; static int md_thread(void * arg) diff --git a/drivers/md/md.h b/drivers/md/md.h index 7e90b8593b2..12215d437fc 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -274,6 +274,8 @@ struct mddev_s atomic_t active; /* general refcount */ atomic_t openers; /* number of active opens */ + int changed; /* True if we might need to + * reread partition info */ int degraded; /* whether md should consider * adding a spare */ diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c index 6d7ddf32ef2..3a62d440e27 100644 --- a/drivers/md/multipath.c +++ b/drivers/md/multipath.c @@ -435,7 +435,6 @@ static int multipath_run (mddev_t *mddev) * bookkeeping area. [whatever we allocate in multipath_run(), * should be freed in multipath_stop()] */ - mddev->queue->queue_lock = &mddev->queue->__queue_lock; conf = kzalloc(sizeof(multipath_conf_t), GFP_KERNEL); mddev->private = conf; diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c index 637a96855ed..c0ac457f121 100644 --- a/drivers/md/raid0.c +++ b/drivers/md/raid0.c @@ -361,7 +361,6 @@ static int raid0_run(mddev_t *mddev) if (md_check_no_bitmap(mddev)) return -EINVAL; blk_queue_max_hw_sectors(mddev->queue, mddev->chunk_sectors); - mddev->queue->queue_lock = &mddev->queue->__queue_lock; /* if private is not null, we are here after takeover */ if (mddev->private == NULL) { @@ -670,6 +669,7 @@ static void *raid0_takeover_raid1(mddev_t *mddev) mddev->new_layout = 0; mddev->new_chunk_sectors = 128; /* by default set chunk size to 64k */ mddev->delta_disks = 1 - mddev->raid_disks; + mddev->raid_disks = 1; /* make sure it will be not marked as dirty */ mddev->recovery_cp = MaxSector; diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index a23ffa397ba..06cd712807d 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -593,7 +593,10 @@ static int flush_pending_writes(conf_t *conf) if (conf->pending_bio_list.head) { struct bio *bio; bio = bio_list_get(&conf->pending_bio_list); + /* Only take the spinlock to quiet a warning */ + spin_lock(conf->mddev->queue->queue_lock); blk_remove_plug(conf->mddev->queue); + spin_unlock(conf->mddev->queue->queue_lock); spin_unlock_irq(&conf->device_lock); /* flush any pending bitmap writes to * disk before proceeding w/ I/O */ @@ -959,7 +962,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) atomic_inc(&r1_bio->remaining); spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); - blk_plug_device(mddev->queue); + blk_plug_device_unlocked(mddev->queue); spin_unlock_irqrestore(&conf->device_lock, flags); } r1_bio_write_done(r1_bio, bio->bi_vcnt, behind_pages, behind_pages != NULL); @@ -2021,7 +2024,6 @@ static int run(mddev_t *mddev) if (IS_ERR(conf)) return PTR_ERR(conf); - mddev->queue->queue_lock = &conf->device_lock; list_for_each_entry(rdev, &mddev->disks, same_set) { disk_stack_limits(mddev->gendisk, rdev->bdev, rdev->data_offset << 9); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 3b607b28741..747d061d8e0 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -662,7 +662,10 @@ static int flush_pending_writes(conf_t *conf) if (conf->pending_bio_list.head) { struct bio *bio; bio = bio_list_get(&conf->pending_bio_list); + /* Spinlock only taken to quiet a warning */ + spin_lock(conf->mddev->queue->queue_lock); blk_remove_plug(conf->mddev->queue); + spin_unlock(conf->mddev->queue->queue_lock); spin_unlock_irq(&conf->device_lock); /* flush any pending bitmap writes to disk * before proceeding w/ I/O */ @@ -971,7 +974,7 @@ static int make_request(mddev_t *mddev, struct bio * bio) atomic_inc(&r10_bio->remaining); spin_lock_irqsave(&conf->device_lock, flags); bio_list_add(&conf->pending_bio_list, mbio); - blk_plug_device(mddev->queue); + blk_plug_device_unlocked(mddev->queue); spin_unlock_irqrestore(&conf->device_lock, flags); } @@ -2304,8 +2307,6 @@ static int run(mddev_t *mddev) if (!conf) goto out; - mddev->queue->queue_lock = &conf->device_lock; - mddev->thread = conf->thread; conf->thread = NULL; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 70281282419..78536fdbd87 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -5204,7 +5204,6 @@ static int run(mddev_t *mddev) mddev->queue->backing_dev_info.congested_data = mddev; mddev->queue->backing_dev_info.congested_fn = raid5_congested; - mddev->queue->queue_lock = &conf->device_lock; mddev->queue->unplug_fn = raid5_unplug_queue; chunk_size = mddev->chunk_sectors << 9; diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 6a1f9404261..c45e6305b26 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -143,9 +143,9 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) unsigned long flags; struct asic3 *asic; - desc->chip->ack(irq); + desc->irq_data.chip->irq_ack(&desc->irq_data); - asic = desc->handler_data; + asic = get_irq_data(irq); for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { u32 status; diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index 33c923d215c..fdd8a1b8bc6 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -118,12 +118,12 @@ static int __init davinci_vc_probe(struct platform_device *pdev) /* Voice codec interface client */ cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; - cell->name = "davinci_vcif"; + cell->name = "davinci-vcif"; cell->driver_data = davinci_vc; /* Voice codec CQ93VC client */ cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; - cell->name = "cq93vc"; + cell->name = "cq93vc-codec"; cell->driver_data = davinci_vc; ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index 627cf577b16..e9018d1394e 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -150,12 +150,12 @@ static inline int __tps6586x_write(struct i2c_client *client, static inline int __tps6586x_writes(struct i2c_client *client, int reg, int len, uint8_t *val) { - int ret; + int ret, i; - ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); - if (ret < 0) { - dev_err(&client->dev, "failed writings to 0x%02x\n", reg); - return ret; + for (i = 0; i < len; i++) { + ret = __tps6586x_write(client, reg + i, *(val + i)); + if (ret < 0) + return ret; } return 0; diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 000cb414a78..92b85e28a15 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -385,12 +385,18 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) idev->close = ucb1x00_ts_close; __set_bit(EV_ABS, idev->evbit); - __set_bit(ABS_X, idev->absbit); - __set_bit(ABS_Y, idev->absbit); - __set_bit(ABS_PRESSURE, idev->absbit); input_set_drvdata(idev, ts); + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); + err = input_register_device(idev); if (err) goto fail; diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 41233c7fa58..f4016a075fd 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -246,6 +246,16 @@ static int wm8994_suspend(struct device *dev) struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; + /* Don't actually go through with the suspend if the CODEC is + * still active (eg, for audio passthrough from CP. */ + ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_1); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & WM8994_VMID_SEL_MASK) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + /* GPIO configuration state is saved here since we may be configuring * the GPIO alternate functions even if we're not using the gpiolib * driver for them. @@ -261,6 +271,8 @@ static int wm8994_suspend(struct device *dev) if (ret < 0) dev_err(dev, "Failed to save LDO registers: %d\n", ret); + wm8994->suspended = true; + ret = regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { @@ -276,6 +288,10 @@ static int wm8994_resume(struct device *dev) struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; + /* We may have lied to the PM core about suspending */ + if (!wm8994->suspended) + return 0; + ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { @@ -298,6 +314,8 @@ static int wm8994_resume(struct device *dev) if (ret < 0) dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + wm8994->suspended = false; + return 0; } #endif diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 8d0d16f9357..7ce3dd01d6e 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -2008,6 +2008,15 @@ config BCM63XX_ENET This driver supports the ethernet MACs in the Broadcom 63xx MIPS chipset family (BCM63XX). +config FTMAC100 + tristate "Faraday FTMAC100 10/100 Ethernet support" + depends on ARM + select MII + help + This driver supports the FTMAC100 10/100 Ethernet controller + from Faraday. It is used on Faraday A320, Andes AG101 and some + other ARM/NDS32 SoC's. + source "drivers/net/fs_enet/Kconfig" source "drivers/net/octeon/Kconfig" @@ -2586,14 +2595,9 @@ config CHELSIO_T1_1G Enables support for Chelsio's gigabit Ethernet PCI cards. If you are using only 10G cards say 'N' here. -config CHELSIO_T3_DEPENDS - tristate - depends on PCI && INET - default y - config CHELSIO_T3 tristate "Chelsio Communications T3 10Gb Ethernet support" - depends on CHELSIO_T3_DEPENDS + depends on PCI && INET select FW_LOADER select MDIO help @@ -2611,14 +2615,9 @@ config CHELSIO_T3 To compile this driver as a module, choose M here: the module will be called cxgb3. -config CHELSIO_T4_DEPENDS - tristate - depends on PCI && INET - default y - config CHELSIO_T4 tristate "Chelsio Communications T4 Ethernet support" - depends on CHELSIO_T4_DEPENDS + depends on PCI select FW_LOADER select MDIO help @@ -2636,14 +2635,9 @@ config CHELSIO_T4 To compile this driver as a module choose M here; the module will be called cxgb4. -config CHELSIO_T4VF_DEPENDS - tristate - depends on PCI && INET - default y - config CHELSIO_T4VF tristate "Chelsio Communications T4 Virtual Function Ethernet support" - depends on CHELSIO_T4VF_DEPENDS + depends on PCI help This driver supports Chelsio T4-based gigabit and 10Gb Ethernet adapters with PCI-E SR-IOV Virtual Functions. diff --git a/drivers/net/Makefile b/drivers/net/Makefile index b90738d1399..7c2171179f9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -147,6 +147,7 @@ obj-$(CONFIG_FORCEDETH) += forcedeth.o obj-$(CONFIG_NE_H8300) += ne-h8300.o 8390.o obj-$(CONFIG_AX88796) += ax88796.o obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o +obj-$(CONFIG_FTMAC100) += ftmac100.o obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index ed709a5d07d..4ac0d72660f 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -220,9 +220,7 @@ struct be_rx_obj { struct be_rx_stats stats; u8 rss_id; bool rx_post_starved; /* Zero rx frags have been posted to BE */ - u16 last_frag_index; - u16 rsvd; - u32 cache_line_barrier[15]; + u32 cache_line_barrier[16]; }; struct be_drv_stats { diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index 1822ecdadc7..cc3a235475b 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -726,7 +726,7 @@ int be_cmd_cq_create(struct be_adapter *adapter, req->num_pages = cpu_to_le16(PAGES_4K_SPANNED(q_mem->va, q_mem->size)); if (lancer_chip(adapter)) { - req->hdr.version = 1; + req->hdr.version = 2; req->page_size = 1; /* 1 for 4K */ AMAP_SET_BITS(struct amap_cq_context_lancer, coalescwm, ctxt, coalesce_wm); @@ -862,6 +862,12 @@ int be_cmd_txq_create(struct be_adapter *adapter, be_cmd_hdr_prepare(&req->hdr, CMD_SUBSYSTEM_ETH, OPCODE_ETH_TX_CREATE, sizeof(*req)); + if (lancer_chip(adapter)) { + req->hdr.version = 1; + AMAP_SET_BITS(struct amap_tx_context, if_id, ctxt, + adapter->if_handle); + } + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); req->ulp_num = BE_ULP1_NUM; req->type = BE_ETH_TX_RING_TYPE_STANDARD; diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index 93e5768fc70..b4ac3938b29 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -430,7 +430,7 @@ struct be_cmd_resp_mcc_create { /* Pseudo amap definition in which each bit of the actual structure is defined * as a byte: used to calculate offset/shift/mask of each field */ struct amap_tx_context { - u8 rsvd0[16]; /* dword 0 */ + u8 if_id[16]; /* dword 0 */ u8 tx_ring_size[4]; /* dword 0 */ u8 rsvd1[26]; /* dword 0 */ u8 pci_func_id[8]; /* dword 1 */ @@ -518,7 +518,8 @@ enum be_if_flags { BE_IF_FLAGS_VLAN = 0x100, BE_IF_FLAGS_MCAST_PROMISCUOUS = 0x200, BE_IF_FLAGS_PASS_L2_ERRORS = 0x400, - BE_IF_FLAGS_PASS_L3L4_ERRORS = 0x800 + BE_IF_FLAGS_PASS_L3L4_ERRORS = 0x800, + BE_IF_FLAGS_MULTICAST = 0x1000 }; /* An RX interface is an object with one or more MAC addresses and diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h index 3f459f76cd1..dbe67f353e8 100644 --- a/drivers/net/benet/be_hw.h +++ b/drivers/net/benet/be_hw.h @@ -44,6 +44,18 @@ #define POST_STAGE_BE_RESET 0x3 /* Host wants to reset chip */ #define POST_STAGE_ARMFW_RDY 0xc000 /* FW is done with POST */ + +/* Lancer SLIPORT_CONTROL SLIPORT_STATUS registers */ +#define SLIPORT_STATUS_OFFSET 0x404 +#define SLIPORT_CONTROL_OFFSET 0x408 + +#define SLIPORT_STATUS_ERR_MASK 0x80000000 +#define SLIPORT_STATUS_RN_MASK 0x01000000 +#define SLIPORT_STATUS_RDY_MASK 0x00800000 + + +#define SLI_PORT_CONTROL_IP_MASK 0x08000000 + /********* Memory BAR register ************/ #define PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET 0xfc /* Host Interrupt Enable, if set interrupts are enabled although "PCI Interrupt diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 0bdccb10aac..68f10781732 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -865,14 +865,17 @@ static void be_rx_stats_update(struct be_rx_obj *rxo, static inline bool csum_passed(struct be_eth_rx_compl *rxcp) { - u8 l4_cksm, ipv6, ipcksm; + u8 l4_cksm, ipv6, ipcksm, tcpf, udpf; l4_cksm = AMAP_GET_BITS(struct amap_eth_rx_compl, l4_cksm, rxcp); ipcksm = AMAP_GET_BITS(struct amap_eth_rx_compl, ipcksm, rxcp); ipv6 = AMAP_GET_BITS(struct amap_eth_rx_compl, ip_version, rxcp); + tcpf = AMAP_GET_BITS(struct amap_eth_rx_compl, tcpf, rxcp); + udpf = AMAP_GET_BITS(struct amap_eth_rx_compl, udpf, rxcp); - /* Ignore ipcksm for ipv6 pkts */ - return l4_cksm && (ipcksm || ipv6); + /* L4 checksum is not reliable for non TCP/UDP packets. + * Also ignore ipcksm for ipv6 pkts */ + return (tcpf || udpf) && l4_cksm && (ipcksm || ipv6); } static struct be_rx_page_info * @@ -909,17 +912,11 @@ static void be_rx_compl_discard(struct be_adapter *adapter, rxq_idx = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, rxcp); num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp); - /* Skip out-of-buffer compl(lancer) or flush compl(BE) */ - if (likely(rxq_idx != rxo->last_frag_index && num_rcvd != 0)) { - - rxo->last_frag_index = rxq_idx; - - for (i = 0; i < num_rcvd; i++) { - page_info = get_rx_page_info(adapter, rxo, rxq_idx); - put_page(page_info->page); - memset(page_info, 0, sizeof(*page_info)); - index_inc(&rxq_idx, rxq->len); - } + for (i = 0; i < num_rcvd; i++) { + page_info = get_rx_page_info(adapter, rxo, rxq_idx); + put_page(page_info->page); + memset(page_info, 0, sizeof(*page_info)); + index_inc(&rxq_idx, rxq->len); } } @@ -1169,20 +1166,20 @@ static inline void be_rx_compl_reset(struct be_eth_rx_compl *rxcp) rxcp->dw[offsetof(struct amap_eth_rx_compl, valid) / 32] = 0; } -static inline struct page *be_alloc_pages(u32 size) +static inline struct page *be_alloc_pages(u32 size, gfp_t gfp) { - gfp_t alloc_flags = GFP_ATOMIC; u32 order = get_order(size); + if (order > 0) - alloc_flags |= __GFP_COMP; - return alloc_pages(alloc_flags, order); + gfp |= __GFP_COMP; + return alloc_pages(gfp, order); } /* * Allocate a page, split it to fragments of size rx_frag_size and post as * receive buffers to BE */ -static void be_post_rx_frags(struct be_rx_obj *rxo) +static void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp) { struct be_adapter *adapter = rxo->adapter; struct be_rx_page_info *page_info_tbl = rxo->page_info_tbl; @@ -1196,7 +1193,7 @@ static void be_post_rx_frags(struct be_rx_obj *rxo) page_info = &rxo->page_info_tbl[rxq->head]; for (posted = 0; posted < MAX_RX_POST && !page_info->page; posted++) { if (!pagep) { - pagep = be_alloc_pages(adapter->big_page_size); + pagep = be_alloc_pages(adapter->big_page_size, gfp); if (unlikely(!pagep)) { rxo->stats.rx_post_fail++; break; @@ -1579,9 +1576,6 @@ static int be_rx_queues_create(struct be_adapter *adapter) adapter->big_page_size = (1 << get_order(rx_frag_size)) * PAGE_SIZE; for_all_rx_queues(adapter, rxo, i) { rxo->adapter = adapter; - /* Init last_frag_index so that the frag index in the first - * completion will never match */ - rxo->last_frag_index = 0xffff; rxo->rx_eq.max_eqd = BE_MAX_EQD; rxo->rx_eq.enable_aic = true; @@ -1722,7 +1716,7 @@ static int be_poll_rx(struct napi_struct *napi, int budget) struct be_queue_info *rx_cq = &rxo->cq; struct be_eth_rx_compl *rxcp; u32 work_done; - u16 frag_index, num_rcvd; + u16 num_rcvd; u8 err; rxo->stats.rx_polls++; @@ -1732,16 +1726,10 @@ static int be_poll_rx(struct napi_struct *napi, int budget) break; err = AMAP_GET_BITS(struct amap_eth_rx_compl, err, rxcp); - frag_index = AMAP_GET_BITS(struct amap_eth_rx_compl, fragndx, - rxcp); num_rcvd = AMAP_GET_BITS(struct amap_eth_rx_compl, numfrags, rxcp); - - /* Skip out-of-buffer compl(lancer) or flush compl(BE) */ - if (likely(frag_index != rxo->last_frag_index && - num_rcvd != 0)) { - rxo->last_frag_index = frag_index; - + /* Ignore flush completions */ + if (num_rcvd) { if (do_gro(rxo, rxcp, err)) be_rx_compl_process_gro(adapter, rxo, rxcp); else @@ -1753,7 +1741,7 @@ static int be_poll_rx(struct napi_struct *napi, int budget) /* Refill the queue */ if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM) - be_post_rx_frags(rxo); + be_post_rx_frags(rxo, GFP_ATOMIC); /* All consumed */ if (work_done < budget) { @@ -1890,7 +1878,7 @@ static void be_worker(struct work_struct *work) if (rxo->rx_post_starved) { rxo->rx_post_starved = false; - be_post_rx_frags(rxo); + be_post_rx_frags(rxo, GFP_KERNEL); } } if (!adapter->ue_detected && !lancer_chip(adapter)) @@ -2094,13 +2082,24 @@ static int be_close(struct net_device *netdev) be_async_mcc_disable(adapter); - netif_stop_queue(netdev); netif_carrier_off(netdev); adapter->link_up = false; if (!lancer_chip(adapter)) be_intr_set(adapter, false); + for_all_rx_queues(adapter, rxo, i) + napi_disable(&rxo->rx_eq.napi); + + napi_disable(&tx_eq->napi); + + if (lancer_chip(adapter)) { + be_cq_notify(adapter, adapter->tx_obj.cq.id, false, 0); + be_cq_notify(adapter, adapter->mcc_obj.cq.id, false, 0); + for_all_rx_queues(adapter, rxo, i) + be_cq_notify(adapter, rxo->cq.id, false, 0); + } + if (adapter->msix_enabled) { vec = be_msix_vec_get(adapter, tx_eq); synchronize_irq(vec); @@ -2114,11 +2113,6 @@ static int be_close(struct net_device *netdev) } be_irq_unregister(adapter); - for_all_rx_queues(adapter, rxo, i) - napi_disable(&rxo->rx_eq.napi); - - napi_disable(&tx_eq->napi); - /* Wait for all pending tx completions to arrive so that * all tx skbs are freed. */ @@ -2138,7 +2132,7 @@ static int be_open(struct net_device *netdev) u16 link_speed; for_all_rx_queues(adapter, rxo, i) { - be_post_rx_frags(rxo); + be_post_rx_frags(rxo, GFP_KERNEL); napi_enable(&rxo->rx_eq.napi); } napi_enable(&tx_eq->napi); @@ -2269,7 +2263,9 @@ static int be_setup(struct be_adapter *adapter) int status; u8 mac[ETH_ALEN]; - cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST; + cap_flags = en_flags = BE_IF_FLAGS_UNTAGGED | + BE_IF_FLAGS_BROADCAST | + BE_IF_FLAGS_MULTICAST; if (be_physfn(adapter)) { cap_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS | @@ -2913,6 +2909,54 @@ static int be_dev_family_check(struct be_adapter *adapter) return 0; } +static int lancer_wait_ready(struct be_adapter *adapter) +{ +#define SLIPORT_READY_TIMEOUT 500 + u32 sliport_status; + int status = 0, i; + + for (i = 0; i < SLIPORT_READY_TIMEOUT; i++) { + sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); + if (sliport_status & SLIPORT_STATUS_RDY_MASK) + break; + + msleep(20); + } + + if (i == SLIPORT_READY_TIMEOUT) + status = -1; + + return status; +} + +static int lancer_test_and_set_rdy_state(struct be_adapter *adapter) +{ + int status; + u32 sliport_status, err, reset_needed; + status = lancer_wait_ready(adapter); + if (!status) { + sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); + err = sliport_status & SLIPORT_STATUS_ERR_MASK; + reset_needed = sliport_status & SLIPORT_STATUS_RN_MASK; + if (err && reset_needed) { + iowrite32(SLI_PORT_CONTROL_IP_MASK, + adapter->db + SLIPORT_CONTROL_OFFSET); + + /* check adapter has corrected the error */ + status = lancer_wait_ready(adapter); + sliport_status = ioread32(adapter->db + + SLIPORT_STATUS_OFFSET); + sliport_status &= (SLIPORT_STATUS_ERR_MASK | + SLIPORT_STATUS_RN_MASK); + if (status || sliport_status) + status = -1; + } else if (err || reset_needed) { + status = -1; + } + } + return status; +} + static int __devinit be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) { @@ -2962,6 +3006,14 @@ static int __devinit be_probe(struct pci_dev *pdev, if (status) goto free_netdev; + if (lancer_chip(adapter)) { + status = lancer_test_and_set_rdy_state(adapter); + if (status) { + dev_err(&pdev->dev, "Adapter in non recoverable error\n"); + goto free_netdev; + } + } + /* sync up with fw's ready state */ if (be_physfn(adapter)) { status = be_cmd_POST(adapter); diff --git a/drivers/net/bnx2x/bnx2x.h b/drivers/net/bnx2x/bnx2x.h index 1914026a7b6..50d1e079309 100644 --- a/drivers/net/bnx2x/bnx2x.h +++ b/drivers/net/bnx2x/bnx2x.h @@ -1631,19 +1631,23 @@ static inline u32 reg_poll(struct bnx2x *bp, u32 reg, u32 expected, int ms, #define BNX2X_BTR 4 #define MAX_SPQ_PENDING 8 - -/* CMNG constants - derived from lab experiments, and not from system spec calculations !!! */ -#define DEF_MIN_RATE 100 +/* CMNG constants, as derived from system spec calculations */ +/* default MIN rate in case VNIC min rate is configured to zero - 100Mbps */ +#define DEF_MIN_RATE 100 /* resolution of the rate shaping timer - 100 usec */ -#define RS_PERIODIC_TIMEOUT_USEC 100 -/* resolution of fairness algorithm in usecs - - coefficient for calculating the actual t fair */ -#define T_FAIR_COEF 10000000 +#define RS_PERIODIC_TIMEOUT_USEC 100 /* number of bytes in single QM arbitration cycle - - coefficient for calculating the fairness timer */ -#define QM_ARB_BYTES 40000 -#define FAIR_MEM 2 + * coefficient for calculating the fairness timer */ +#define QM_ARB_BYTES 160000 +/* resolution of Min algorithm 1:100 */ +#define MIN_RES 100 +/* how many bytes above threshold for the minimal credit of Min algorithm*/ +#define MIN_ABOVE_THRESH 32768 +/* Fairness algorithm integration time coefficient - + * for calculating the actual Tfair */ +#define T_FAIR_COEF ((MIN_ABOVE_THRESH + QM_ARB_BYTES) * 8 * MIN_RES) +/* Memory of fairness algorithm . 2 cycles */ +#define FAIR_MEM 2 #define ATTN_NIG_FOR_FUNC (1L << 8) diff --git a/drivers/net/bnx2x/bnx2x_cmn.c b/drivers/net/bnx2x/bnx2x_cmn.c index 6fac8e183c5..b01b622f4e1 100644 --- a/drivers/net/bnx2x/bnx2x_cmn.c +++ b/drivers/net/bnx2x/bnx2x_cmn.c @@ -259,10 +259,44 @@ static void bnx2x_tpa_start(struct bnx2x_fastpath *fp, u16 queue, #endif } +/* Timestamp option length allowed for TPA aggregation: + * + * nop nop kind length echo val + */ +#define TPA_TSTAMP_OPT_LEN 12 +/** + * Calculate the approximate value of the MSS for this + * aggregation using the first packet of it. + * + * @param bp + * @param parsing_flags Parsing flags from the START CQE + * @param len_on_bd Total length of the first packet for the + * aggregation. + */ +static inline u16 bnx2x_set_lro_mss(struct bnx2x *bp, u16 parsing_flags, + u16 len_on_bd) +{ + /* TPA arrgregation won't have an IP options and TCP options + * other than timestamp. + */ + u16 hdrs_len = ETH_HLEN + sizeof(struct iphdr) + sizeof(struct tcphdr); + + + /* Check if there was a TCP timestamp, if there is it's will + * always be 12 bytes length: nop nop kind length echo val. + * + * Otherwise FW would close the aggregation. + */ + if (parsing_flags & PARSING_FLAGS_TIME_STAMP_EXIST_FLAG) + hdrs_len += TPA_TSTAMP_OPT_LEN; + + return len_on_bd - hdrs_len; +} + static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp, struct sk_buff *skb, struct eth_fast_path_rx_cqe *fp_cqe, - u16 cqe_idx) + u16 cqe_idx, u16 parsing_flags) { struct sw_rx_page *rx_pg, old_rx_pg; u16 len_on_bd = le16_to_cpu(fp_cqe->len_on_bd); @@ -275,8 +309,8 @@ static int bnx2x_fill_frag_skb(struct bnx2x *bp, struct bnx2x_fastpath *fp, /* This is needed in order to enable forwarding support */ if (frag_size) - skb_shinfo(skb)->gso_size = min((u32)SGE_PAGE_SIZE, - max(frag_size, (u32)len_on_bd)); + skb_shinfo(skb)->gso_size = bnx2x_set_lro_mss(bp, parsing_flags, + len_on_bd); #ifdef BNX2X_STOP_ON_ERROR if (pages > min_t(u32, 8, MAX_SKB_FRAGS)*SGE_PAGE_SIZE*PAGES_PER_SGE) { @@ -344,6 +378,8 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, if (likely(new_skb)) { /* fix ip xsum and give it to the stack */ /* (no need to map the new skb) */ + u16 parsing_flags = + le16_to_cpu(cqe->fast_path_cqe.pars_flags.flags); prefetch(skb); prefetch(((char *)(skb)) + L1_CACHE_BYTES); @@ -373,9 +409,9 @@ static void bnx2x_tpa_stop(struct bnx2x *bp, struct bnx2x_fastpath *fp, } if (!bnx2x_fill_frag_skb(bp, fp, skb, - &cqe->fast_path_cqe, cqe_idx)) { - if ((le16_to_cpu(cqe->fast_path_cqe. - pars_flags.flags) & PARSING_FLAGS_VLAN)) + &cqe->fast_path_cqe, cqe_idx, + parsing_flags)) { + if (parsing_flags & PARSING_FLAGS_VLAN) __vlan_hwaccel_put_tag(skb, le16_to_cpu(cqe->fast_path_cqe. vlan_tag)); @@ -703,19 +739,20 @@ u16 bnx2x_get_mf_speed(struct bnx2x *bp) { u16 line_speed = bp->link_vars.line_speed; if (IS_MF(bp)) { - u16 maxCfg = (bp->mf_config[BP_VN(bp)] & - FUNC_MF_CFG_MAX_BW_MASK) >> - FUNC_MF_CFG_MAX_BW_SHIFT; - /* Calculate the current MAX line speed limit for the DCC - * capable devices + u16 maxCfg = bnx2x_extract_max_cfg(bp, + bp->mf_config[BP_VN(bp)]); + + /* Calculate the current MAX line speed limit for the MF + * devices */ - if (IS_MF_SD(bp)) { + if (IS_MF_SI(bp)) + line_speed = (line_speed * maxCfg) / 100; + else { /* SD mode */ u16 vn_max_rate = maxCfg * 100; if (vn_max_rate < line_speed) line_speed = vn_max_rate; - } else /* IS_MF_SI(bp)) */ - line_speed = (line_speed * maxCfg) / 100; + } } return line_speed; diff --git a/drivers/net/bnx2x/bnx2x_cmn.h b/drivers/net/bnx2x/bnx2x_cmn.h index f062d5d20fa..8c401c990bf 100644 --- a/drivers/net/bnx2x/bnx2x_cmn.h +++ b/drivers/net/bnx2x/bnx2x_cmn.h @@ -1044,4 +1044,24 @@ static inline void storm_memset_cmng(struct bnx2x *bp, void bnx2x_acquire_phy_lock(struct bnx2x *bp); void bnx2x_release_phy_lock(struct bnx2x *bp); +/** + * Extracts MAX BW part from MF configuration. + * + * @param bp + * @param mf_cfg + * + * @return u16 + */ +static inline u16 bnx2x_extract_max_cfg(struct bnx2x *bp, u32 mf_cfg) +{ + u16 max_cfg = (mf_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> + FUNC_MF_CFG_MAX_BW_SHIFT; + if (!max_cfg) { + BNX2X_ERR("Illegal configuration detected for Max BW - " + "using 100 instead\n"); + max_cfg = 100; + } + return max_cfg; +} + #endif /* BNX2X_CMN_H */ diff --git a/drivers/net/bnx2x/bnx2x_ethtool.c b/drivers/net/bnx2x/bnx2x_ethtool.c index 8d19d127f79..85291d8b332 100644 --- a/drivers/net/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/bnx2x/bnx2x_ethtool.c @@ -238,7 +238,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) speed |= (cmd->speed_hi << 16); if (IS_MF_SI(bp)) { - u32 param = 0; + u32 param = 0, part; u32 line_speed = bp->link_vars.line_speed; /* use 10G if no link detected */ @@ -251,9 +251,11 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) REQ_BC_VER_4_SET_MF_BW); return -EINVAL; } - if (line_speed < speed) { - BNX2X_DEV_INFO("New speed should be less or equal " - "to actual line speed\n"); + part = (speed * 100) / line_speed; + if (line_speed < speed || !part) { + BNX2X_DEV_INFO("Speed setting should be in a range " + "from 1%% to 100%% " + "of actual line speed\n"); return -EINVAL; } /* load old values */ @@ -263,8 +265,7 @@ static int bnx2x_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) param &= FUNC_MF_CFG_MIN_BW_MASK; /* set new MAX value */ - param |= (((speed * 100) / line_speed) - << FUNC_MF_CFG_MAX_BW_SHIFT) + param |= (part << FUNC_MF_CFG_MAX_BW_SHIFT) & FUNC_MF_CFG_MAX_BW_MASK; bnx2x_fw_command(bp, DRV_MSG_CODE_SET_MF_BW, param); @@ -1781,9 +1782,7 @@ static int bnx2x_test_nvram(struct bnx2x *bp) { 0x100, 0x350 }, /* manuf_info */ { 0x450, 0xf0 }, /* feature_info */ { 0x640, 0x64 }, /* upgrade_key_info */ - { 0x6a4, 0x64 }, { 0x708, 0x70 }, /* manuf_key_info */ - { 0x778, 0x70 }, { 0, 0 } }; __be32 buf[0x350 / 4]; @@ -1933,11 +1932,11 @@ static void bnx2x_self_test(struct net_device *dev, buf[4] = 1; etest->flags |= ETH_TEST_FL_FAILED; } - if (bp->port.pmf) - if (bnx2x_link_test(bp, is_serdes) != 0) { - buf[5] = 1; - etest->flags |= ETH_TEST_FL_FAILED; - } + + if (bnx2x_link_test(bp, is_serdes) != 0) { + buf[5] = 1; + etest->flags |= ETH_TEST_FL_FAILED; + } #ifdef BNX2X_EXTRA_DEBUG bnx2x_panic_dump(bp); diff --git a/drivers/net/bnx2x/bnx2x_init.h b/drivers/net/bnx2x/bnx2x_init.h index 5a268e9a089..fa6dbe3f205 100644 --- a/drivers/net/bnx2x/bnx2x_init.h +++ b/drivers/net/bnx2x/bnx2x_init.h @@ -241,7 +241,7 @@ static const struct { /* Block IGU, MISC, PXP and PXP2 parity errors as long as we don't * want to handle "system kill" flow at the moment. */ - BLOCK_PRTY_INFO(PXP, 0x3ffffff, 0x3ffffff, 0x3ffffff, 0x3ffffff), + BLOCK_PRTY_INFO(PXP, 0x7ffffff, 0x3ffffff, 0x3ffffff, 0x7ffffff), BLOCK_PRTY_INFO_0(PXP2, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff), BLOCK_PRTY_INFO_1(PXP2, 0x7ff, 0x7f, 0x7f, 0x7ff), BLOCK_PRTY_INFO(HC, 0x7, 0x7, 0x7, 0), diff --git a/drivers/net/bnx2x/bnx2x_main.c b/drivers/net/bnx2x/bnx2x_main.c index 061733e5e5c..30b21d2f26f 100644 --- a/drivers/net/bnx2x/bnx2x_main.c +++ b/drivers/net/bnx2x/bnx2x_main.c @@ -1974,13 +1974,22 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int vn) vn_max_rate = 0; } else { + u32 maxCfg = bnx2x_extract_max_cfg(bp, vn_cfg); + vn_min_rate = ((vn_cfg & FUNC_MF_CFG_MIN_BW_MASK) >> FUNC_MF_CFG_MIN_BW_SHIFT) * 100; - /* If min rate is zero - set it to 1 */ + /* If fairness is enabled (not all min rates are zeroes) and + if current min rate is zero - set it to 1. + This is a requirement of the algorithm. */ if (bp->vn_weight_sum && (vn_min_rate == 0)) vn_min_rate = DEF_MIN_RATE; - vn_max_rate = ((vn_cfg & FUNC_MF_CFG_MAX_BW_MASK) >> - FUNC_MF_CFG_MAX_BW_SHIFT) * 100; + + if (IS_MF_SI(bp)) + /* maxCfg in percents of linkspeed */ + vn_max_rate = (bp->link_vars.line_speed * maxCfg) / 100; + else + /* maxCfg is absolute in 100Mb units */ + vn_max_rate = maxCfg * 100; } DP(NETIF_MSG_IFUP, @@ -2006,7 +2015,8 @@ static void bnx2x_init_vn_minmax(struct bnx2x *bp, int vn) m_fair_vn.vn_credit_delta = max_t(u32, (vn_min_rate * (T_FAIR_COEF / (8 * bp->vn_weight_sum))), - (bp->cmng.fair_vars.fair_threshold * 2)); + (bp->cmng.fair_vars.fair_threshold + + MIN_ABOVE_THRESH)); DP(NETIF_MSG_IFUP, "m_fair_vn.vn_credit_delta %d\n", m_fair_vn.vn_credit_delta); } diff --git a/drivers/net/bnx2x/bnx2x_stats.c b/drivers/net/bnx2x/bnx2x_stats.c index bda60d590fa..3445ded6674 100644 --- a/drivers/net/bnx2x/bnx2x_stats.c +++ b/drivers/net/bnx2x/bnx2x_stats.c @@ -1239,14 +1239,14 @@ void bnx2x_stats_handle(struct bnx2x *bp, enum bnx2x_stats_event event) if (unlikely(bp->panic)) return; + bnx2x_stats_stm[bp->stats_state][event].action(bp); + /* Protect a state change flow */ spin_lock_bh(&bp->stats_lock); state = bp->stats_state; bp->stats_state = bnx2x_stats_stm[state][event].next_state; spin_unlock_bh(&bp->stats_lock); - bnx2x_stats_stm[state][event].action(bp); - if ((event != STATS_EVENT_UPDATE) || netif_msg_timer(bp)) DP(BNX2X_MSG_STATS, "state %d -> event %d -> state %d\n", state, event, bp->stats_state); diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 5c6fba802f2..9bc5de3e04a 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -604,7 +604,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon _lock_rx_hashtbl(bond); - hash_index = _simple_hash((u8 *)&arp->ip_dst, sizeof(arp->ip_src)); + hash_index = _simple_hash((u8 *)&arp->ip_dst, sizeof(arp->ip_dst)); client_info = &(bond_info->rx_hashtbl[hash_index]); if (client_info->assigned) { diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 584f97b7306..0592e6da15a 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -2681,7 +2681,7 @@ static void bond_arp_send(struct net_device *slave_dev, int arp_op, __be32 dest_ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) { - int i, vlan_id, rv; + int i, vlan_id; __be32 *targets = bond->params.arp_targets; struct vlan_entry *vlan; struct net_device *vlan_dev; @@ -2708,8 +2708,8 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) fl.fl4_dst = targets[i]; fl.fl4_tos = RTO_ONLINK; - rv = ip_route_output_key(dev_net(bond->dev), &rt, &fl); - if (rv) { + rt = ip_route_output_key(dev_net(bond->dev), &fl); + if (IS_ERR(rt)) { if (net_ratelimit()) { pr_warning("%s: no route to arp_ip_target %pI4\n", bond->dev->name, &fl.fl4_dst); diff --git a/drivers/net/can/softing/softing_main.c b/drivers/net/can/softing/softing_main.c index 5157e15e96e..aeea9f9ff6e 100644 --- a/drivers/net/can/softing/softing_main.c +++ b/drivers/net/can/softing/softing_main.c @@ -633,6 +633,7 @@ static const struct net_device_ops softing_netdev_ops = { }; static const struct can_bittiming_const softing_btr_const = { + .name = "softing", .tseg1_min = 1, .tseg1_max = 16, .tseg2_min = 1, diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 5274de3e1bb..271a1f00c22 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -2767,6 +2767,8 @@ static u32 cnic_service_bnx2_queues(struct cnic_dev *dev) u32 status_idx = (u16) *cp->kcq1.status_idx_ptr; int kcqe_cnt; + /* status block index must be read before reading other fields */ + rmb(); cp->kwq_con_idx = *cp->kwq_con_idx_ptr; while ((kcqe_cnt = cnic_get_kcqes(dev, &cp->kcq1))) { @@ -2777,6 +2779,8 @@ static u32 cnic_service_bnx2_queues(struct cnic_dev *dev) barrier(); if (status_idx != *cp->kcq1.status_idx_ptr) { status_idx = (u16) *cp->kcq1.status_idx_ptr; + /* status block index must be read first */ + rmb(); cp->kwq_con_idx = *cp->kwq_con_idx_ptr; } else break; @@ -2895,6 +2899,8 @@ static u32 cnic_service_bnx2x_kcq(struct cnic_dev *dev, struct kcq_info *info) u32 last_status = *info->status_idx_ptr; int kcqe_cnt; + /* status block index must be read before reading the KCQ */ + rmb(); while ((kcqe_cnt = cnic_get_kcqes(dev, info))) { service_kcqes(dev, kcqe_cnt); @@ -2905,6 +2911,8 @@ static u32 cnic_service_bnx2x_kcq(struct cnic_dev *dev, struct kcq_info *info) break; last_status = *info->status_idx_ptr; + /* status block index must be read before reading the KCQ */ + rmb(); } return last_status; } @@ -2913,26 +2921,35 @@ static void cnic_service_bnx2x_bh(unsigned long data) { struct cnic_dev *dev = (struct cnic_dev *) data; struct cnic_local *cp = dev->cnic_priv; - u32 status_idx; + u32 status_idx, new_status_idx; if (unlikely(!test_bit(CNIC_F_CNIC_UP, &dev->flags))) return; - status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq1); + while (1) { + status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq1); - CNIC_WR16(dev, cp->kcq1.io_addr, cp->kcq1.sw_prod_idx + MAX_KCQ_IDX); + CNIC_WR16(dev, cp->kcq1.io_addr, + cp->kcq1.sw_prod_idx + MAX_KCQ_IDX); - if (BNX2X_CHIP_IS_E2(cp->chip_id)) { - status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq2); + if (!BNX2X_CHIP_IS_E2(cp->chip_id)) { + cnic_ack_bnx2x_int(dev, cp->bnx2x_igu_sb_id, USTORM_ID, + status_idx, IGU_INT_ENABLE, 1); + break; + } + + new_status_idx = cnic_service_bnx2x_kcq(dev, &cp->kcq2); + + if (new_status_idx != status_idx) + continue; CNIC_WR16(dev, cp->kcq2.io_addr, cp->kcq2.sw_prod_idx + MAX_KCQ_IDX); cnic_ack_igu_sb(dev, cp->bnx2x_igu_sb_id, IGU_SEG_ACCESS_DEF, status_idx, IGU_INT_ENABLE, 1); - } else { - cnic_ack_bnx2x_int(dev, cp->bnx2x_igu_sb_id, USTORM_ID, - status_idx, IGU_INT_ENABLE, 1); + + break; } } @@ -3397,9 +3414,12 @@ static int cnic_get_v4_route(struct sockaddr_in *dst_addr, memset(&fl, 0, sizeof(fl)); fl.nl_u.ip4_u.daddr = dst_addr->sin_addr.s_addr; - err = ip_route_output_key(&init_net, &rt, &fl); - if (!err) + rt = ip_route_output_key(&init_net, &fl); + err = 0; + if (!IS_ERR(rt)) *dst = &rt->dst; + else + err = PTR_ERR(rt); return err; #else return -ENETUNREACH; diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index 2a628d17d17..7018bfe408a 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -1008,7 +1008,7 @@ static void emac_rx_handler(void *token, int len, int status) int ret; /* free and bail if we are shutting down */ - if (unlikely(!netif_running(ndev))) { + if (unlikely(!netif_running(ndev) || !netif_carrier_ok(ndev))) { dev_kfree_skb_any(skb); return; } diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index c3093558720..31770811360 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -802,10 +802,7 @@ dm9000_init_dm9000(struct net_device *dev) /* Checksum mode */ dm9000_set_rx_csum_unlocked(dev, db->rx_csum); - /* GPIO0 on pre-activate PHY */ - iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */ iow(db, DM9000_GPCR, GPCR_GEP_CNTL); /* Let GPIO0 output */ - iow(db, DM9000_GPR, 0); /* Enable PHY */ ncr = (db->flags & DM9000_PLATF_EXT_PHY) ? NCR_EXT_PHY : 0; @@ -852,8 +849,8 @@ static void dm9000_timeout(struct net_device *dev) unsigned long flags; /* Save previous register address */ - reg_save = readb(db->io_addr); spin_lock_irqsave(&db->lock, flags); + reg_save = readb(db->io_addr); netif_stop_queue(dev); dm9000_reset(db); @@ -1194,6 +1191,10 @@ dm9000_open(struct net_device *dev) if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; + /* GPIO0 on pre-activate PHY, Reg 1F is not set by reset */ + iow(db, DM9000_GPR, 0); /* REG_1F bit0 activate phyxcer */ + mdelay(1); /* delay needs by DM9000B */ + /* Initialize DM9000 board */ dm9000_reset(db); dm9000_init_dm9000(dev); diff --git a/drivers/net/dnet.c b/drivers/net/dnet.c index 9d8a20b72fa..8318ea06cb6 100644 --- a/drivers/net/dnet.c +++ b/drivers/net/dnet.c @@ -337,8 +337,6 @@ static int dnet_mii_init(struct dnet *bp) for (i = 0; i < PHY_MAX_ADDR; i++) bp->mii_bus->irq[i] = PHY_POLL; - platform_set_drvdata(bp->dev, bp->mii_bus); - if (mdiobus_register(bp->mii_bus)) { err = -ENXIO; goto err_out_free_mdio_irq; @@ -863,6 +861,7 @@ static int __devinit dnet_probe(struct platform_device *pdev) bp = netdev_priv(dev); bp->dev = dev; + platform_set_drvdata(pdev, dev); SET_NETDEV_DEV(dev, &pdev->dev); spin_lock_init(&bp->lock); diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h index 55c1711f168..33e7c45a4fe 100644 --- a/drivers/net/e1000/e1000_osdep.h +++ b/drivers/net/e1000/e1000_osdep.h @@ -42,7 +42,8 @@ #define GBE_CONFIG_RAM_BASE \ ((unsigned int)(CONFIG_RAM_BASE + GBE_CONFIG_OFFSET)) -#define GBE_CONFIG_BASE_VIRT phys_to_virt(GBE_CONFIG_RAM_BASE) +#define GBE_CONFIG_BASE_VIRT \ + ((void __iomem *)phys_to_virt(GBE_CONFIG_RAM_BASE)) #define GBE_CONFIG_FLASH_WRITE(base, offset, count, data) \ (iowrite16_rep(base + offset, data, count)) diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index ec0b803c501..455d5a1101e 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -6012,7 +6012,8 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* APME bit in EEPROM is mapped to WUC.APME */ eeprom_data = er32(WUC); eeprom_apme_mask = E1000_WUC_APME; - if (eeprom_data & E1000_WUC_PHY_WAKE) + if ((hw->mac.type > e1000_ich10lan) && + (eeprom_data & E1000_WUC_PHY_WAKE)) adapter->flags2 |= FLAG2_HAS_PHY_WAKEUP; } else if (adapter->flags & FLAG_APME_IN_CTRL3) { if (adapter->flags & FLAG_APME_CHECK_PORT_B && diff --git a/drivers/net/eql.c b/drivers/net/eql.c index 0cb1cf9cf4b..a59cf961a43 100644 --- a/drivers/net/eql.c +++ b/drivers/net/eql.c @@ -111,6 +111,8 @@ * Sorry, I had to rewrite most of this for 2.5.x -DaveM */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/capability.h> #include <linux/module.h> #include <linux/kernel.h> @@ -162,7 +164,7 @@ static void eql_timer(unsigned long param) } static const char version[] __initconst = - "Equalizer2002: Simon Janes (simon@ncm.com) and David S. Miller (davem@redhat.com)\n"; + "Equalizer2002: Simon Janes (simon@ncm.com) and David S. Miller (davem@redhat.com)"; static const struct net_device_ops eql_netdev_ops = { .ndo_open = eql_open, @@ -204,8 +206,8 @@ static int eql_open(struct net_device *dev) equalizer_t *eql = netdev_priv(dev); /* XXX We should force this off automatically for the user. */ - printk(KERN_INFO "%s: remember to turn off Van-Jacobson compression on " - "your slave devices.\n", dev->name); + netdev_info(dev, + "remember to turn off Van-Jacobson compression on your slave devices\n"); BUG_ON(!list_empty(&eql->queue.all_slaves)); @@ -591,7 +593,7 @@ static int __init eql_init_module(void) { int err; - printk(version); + pr_info("%s\n", version); dev_eql = alloc_netdev(sizeof(equalizer_t), "eql", eql_setup); if (!dev_eql) diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 634c0daeece..885d8baff7d 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -74,7 +74,8 @@ static struct platform_device_id fec_devtype[] = { }, { .name = "imx28-fec", .driver_data = FEC_QUIRK_ENET_MAC | FEC_QUIRK_SWAP_FRAME, - } + }, + { } }; static unsigned char macaddr[ETH_ALEN]; diff --git a/drivers/net/ftmac100.c b/drivers/net/ftmac100.c new file mode 100644 index 00000000000..df70368bf31 --- /dev/null +++ b/drivers/net/ftmac100.c @@ -0,0 +1,1196 @@ +/* + * Faraday FTMAC100 10/100 Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang <ratbert@faraday-tech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dma-mapping.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/platform_device.h> + +#include "ftmac100.h" + +#define DRV_NAME "ftmac100" +#define DRV_VERSION "0.2" + +#define RX_QUEUE_ENTRIES 128 /* must be power of 2 */ +#define TX_QUEUE_ENTRIES 16 /* must be power of 2 */ + +#define MAX_PKT_SIZE 1518 +#define RX_BUF_SIZE 2044 /* must be smaller than 0x7ff */ + +#if MAX_PKT_SIZE > 0x7ff +#error invalid MAX_PKT_SIZE +#endif + +#if RX_BUF_SIZE > 0x7ff || RX_BUF_SIZE > PAGE_SIZE +#error invalid RX_BUF_SIZE +#endif + +/****************************************************************************** + * private data + *****************************************************************************/ +struct ftmac100_descs { + struct ftmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; + struct ftmac100_txdes txdes[TX_QUEUE_ENTRIES]; +}; + +struct ftmac100 { + struct resource *res; + void __iomem *base; + int irq; + + struct ftmac100_descs *descs; + dma_addr_t descs_dma_addr; + + unsigned int rx_pointer; + unsigned int tx_clean_pointer; + unsigned int tx_pointer; + unsigned int tx_pending; + + spinlock_t tx_lock; + + struct net_device *netdev; + struct device *dev; + struct napi_struct napi; + + struct mii_if_info mii; +}; + +static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes); + +/****************************************************************************** + * internal functions (hardware register access) + *****************************************************************************/ +#define INT_MASK_ALL_ENABLED (FTMAC100_INT_RPKT_FINISH | \ + FTMAC100_INT_NORXBUF | \ + FTMAC100_INT_XPKT_OK | \ + FTMAC100_INT_XPKT_LOST | \ + FTMAC100_INT_RPKT_LOST | \ + FTMAC100_INT_AHB_ERR | \ + FTMAC100_INT_PHYSTS_CHG) + +#define INT_MASK_ALL_DISABLED 0 + +static void ftmac100_enable_all_int(struct ftmac100 *priv) +{ + iowrite32(INT_MASK_ALL_ENABLED, priv->base + FTMAC100_OFFSET_IMR); +} + +static void ftmac100_disable_all_int(struct ftmac100 *priv) +{ + iowrite32(INT_MASK_ALL_DISABLED, priv->base + FTMAC100_OFFSET_IMR); +} + +static void ftmac100_set_rx_ring_base(struct ftmac100 *priv, dma_addr_t addr) +{ + iowrite32(addr, priv->base + FTMAC100_OFFSET_RXR_BADR); +} + +static void ftmac100_set_tx_ring_base(struct ftmac100 *priv, dma_addr_t addr) +{ + iowrite32(addr, priv->base + FTMAC100_OFFSET_TXR_BADR); +} + +static void ftmac100_txdma_start_polling(struct ftmac100 *priv) +{ + iowrite32(1, priv->base + FTMAC100_OFFSET_TXPD); +} + +static int ftmac100_reset(struct ftmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + int i; + + /* NOTE: reset clears all registers */ + iowrite32(FTMAC100_MACCR_SW_RST, priv->base + FTMAC100_OFFSET_MACCR); + + for (i = 0; i < 5; i++) { + unsigned int maccr; + + maccr = ioread32(priv->base + FTMAC100_OFFSET_MACCR); + if (!(maccr & FTMAC100_MACCR_SW_RST)) { + /* + * FTMAC100_MACCR_SW_RST cleared does not indicate + * that hardware reset completed (what the f*ck). + * We still need to wait for a while. + */ + usleep_range(500, 1000); + return 0; + } + + usleep_range(1000, 10000); + } + + netdev_err(netdev, "software reset failed\n"); + return -EIO; +} + +static void ftmac100_set_mac(struct ftmac100 *priv, const unsigned char *mac) +{ + unsigned int maddr = mac[0] << 8 | mac[1]; + unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5]; + + iowrite32(maddr, priv->base + FTMAC100_OFFSET_MAC_MADR); + iowrite32(laddr, priv->base + FTMAC100_OFFSET_MAC_LADR); +} + +#define MACCR_ENABLE_ALL (FTMAC100_MACCR_XMT_EN | \ + FTMAC100_MACCR_RCV_EN | \ + FTMAC100_MACCR_XDMA_EN | \ + FTMAC100_MACCR_RDMA_EN | \ + FTMAC100_MACCR_CRC_APD | \ + FTMAC100_MACCR_FULLDUP | \ + FTMAC100_MACCR_RX_RUNT | \ + FTMAC100_MACCR_RX_BROADPKT) + +static int ftmac100_start_hw(struct ftmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + + if (ftmac100_reset(priv)) + return -EIO; + + /* setup ring buffer base registers */ + ftmac100_set_rx_ring_base(priv, + priv->descs_dma_addr + + offsetof(struct ftmac100_descs, rxdes)); + ftmac100_set_tx_ring_base(priv, + priv->descs_dma_addr + + offsetof(struct ftmac100_descs, txdes)); + + iowrite32(FTMAC100_APTC_RXPOLL_CNT(1), priv->base + FTMAC100_OFFSET_APTC); + + ftmac100_set_mac(priv, netdev->dev_addr); + + iowrite32(MACCR_ENABLE_ALL, priv->base + FTMAC100_OFFSET_MACCR); + return 0; +} + +static void ftmac100_stop_hw(struct ftmac100 *priv) +{ + iowrite32(0, priv->base + FTMAC100_OFFSET_MACCR); +} + +/****************************************************************************** + * internal functions (receive descriptor) + *****************************************************************************/ +static bool ftmac100_rxdes_first_segment(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FRS); +} + +static bool ftmac100_rxdes_last_segment(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_LRS); +} + +static bool ftmac100_rxdes_owned_by_dma(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN); +} + +static void ftmac100_rxdes_set_dma_own(struct ftmac100_rxdes *rxdes) +{ + /* clear status bits */ + rxdes->rxdes0 = cpu_to_le32(FTMAC100_RXDES0_RXDMA_OWN); +} + +static bool ftmac100_rxdes_rx_error(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ERR); +} + +static bool ftmac100_rxdes_crc_error(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_CRC_ERR); +} + +static bool ftmac100_rxdes_frame_too_long(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_FTL); +} + +static bool ftmac100_rxdes_runt(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RUNT); +} + +static bool ftmac100_rxdes_odd_nibble(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_RX_ODD_NB); +} + +static unsigned int ftmac100_rxdes_frame_length(struct ftmac100_rxdes *rxdes) +{ + return le32_to_cpu(rxdes->rxdes0) & FTMAC100_RXDES0_RFL; +} + +static bool ftmac100_rxdes_multicast(struct ftmac100_rxdes *rxdes) +{ + return rxdes->rxdes0 & cpu_to_le32(FTMAC100_RXDES0_MULTICAST); +} + +static void ftmac100_rxdes_set_buffer_size(struct ftmac100_rxdes *rxdes, + unsigned int size) +{ + rxdes->rxdes1 &= cpu_to_le32(FTMAC100_RXDES1_EDORR); + rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_RXBUF_SIZE(size)); +} + +static void ftmac100_rxdes_set_end_of_ring(struct ftmac100_rxdes *rxdes) +{ + rxdes->rxdes1 |= cpu_to_le32(FTMAC100_RXDES1_EDORR); +} + +static void ftmac100_rxdes_set_dma_addr(struct ftmac100_rxdes *rxdes, + dma_addr_t addr) +{ + rxdes->rxdes2 = cpu_to_le32(addr); +} + +static dma_addr_t ftmac100_rxdes_get_dma_addr(struct ftmac100_rxdes *rxdes) +{ + return le32_to_cpu(rxdes->rxdes2); +} + +/* + * rxdes3 is not used by hardware. We use it to keep track of page. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftmac100_rxdes_set_page(struct ftmac100_rxdes *rxdes, struct page *page) +{ + rxdes->rxdes3 = (unsigned int)page; +} + +static struct page *ftmac100_rxdes_get_page(struct ftmac100_rxdes *rxdes) +{ + return (struct page *)rxdes->rxdes3; +} + +/****************************************************************************** + * internal functions (receive) + *****************************************************************************/ +static int ftmac100_next_rx_pointer(int pointer) +{ + return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); +} + +static void ftmac100_rx_pointer_advance(struct ftmac100 *priv) +{ + priv->rx_pointer = ftmac100_next_rx_pointer(priv->rx_pointer); +} + +static struct ftmac100_rxdes *ftmac100_current_rxdes(struct ftmac100 *priv) +{ + return &priv->descs->rxdes[priv->rx_pointer]; +} + +static struct ftmac100_rxdes * +ftmac100_rx_locate_first_segment(struct ftmac100 *priv) +{ + struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); + + while (!ftmac100_rxdes_owned_by_dma(rxdes)) { + if (ftmac100_rxdes_first_segment(rxdes)) + return rxdes; + + ftmac100_rxdes_set_dma_own(rxdes); + ftmac100_rx_pointer_advance(priv); + rxdes = ftmac100_current_rxdes(priv); + } + + return NULL; +} + +static bool ftmac100_rx_packet_error(struct ftmac100 *priv, + struct ftmac100_rxdes *rxdes) +{ + struct net_device *netdev = priv->netdev; + bool error = false; + + if (unlikely(ftmac100_rxdes_rx_error(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx err\n"); + + netdev->stats.rx_errors++; + error = true; + } + + if (unlikely(ftmac100_rxdes_crc_error(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx crc err\n"); + + netdev->stats.rx_crc_errors++; + error = true; + } + + if (unlikely(ftmac100_rxdes_frame_too_long(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx frame too long\n"); + + netdev->stats.rx_length_errors++; + error = true; + } else if (unlikely(ftmac100_rxdes_runt(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx runt\n"); + + netdev->stats.rx_length_errors++; + error = true; + } else if (unlikely(ftmac100_rxdes_odd_nibble(rxdes))) { + if (net_ratelimit()) + netdev_info(netdev, "rx odd nibble\n"); + + netdev->stats.rx_length_errors++; + error = true; + } + + return error; +} + +static void ftmac100_rx_drop_packet(struct ftmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + struct ftmac100_rxdes *rxdes = ftmac100_current_rxdes(priv); + bool done = false; + + if (net_ratelimit()) + netdev_dbg(netdev, "drop packet %p\n", rxdes); + + do { + if (ftmac100_rxdes_last_segment(rxdes)) + done = true; + + ftmac100_rxdes_set_dma_own(rxdes); + ftmac100_rx_pointer_advance(priv); + rxdes = ftmac100_current_rxdes(priv); + } while (!done && !ftmac100_rxdes_owned_by_dma(rxdes)); + + netdev->stats.rx_dropped++; +} + +static bool ftmac100_rx_packet(struct ftmac100 *priv, int *processed) +{ + struct net_device *netdev = priv->netdev; + struct ftmac100_rxdes *rxdes; + struct sk_buff *skb; + struct page *page; + dma_addr_t map; + int length; + + rxdes = ftmac100_rx_locate_first_segment(priv); + if (!rxdes) + return false; + + if (unlikely(ftmac100_rx_packet_error(priv, rxdes))) { + ftmac100_rx_drop_packet(priv); + return true; + } + + /* + * It is impossible to get multi-segment packets + * because we always provide big enough receive buffers. + */ + if (unlikely(!ftmac100_rxdes_last_segment(rxdes))) + BUG(); + + /* start processing */ + skb = netdev_alloc_skb_ip_align(netdev, 128); + if (unlikely(!skb)) { + if (net_ratelimit()) + netdev_err(netdev, "rx skb alloc failed\n"); + + ftmac100_rx_drop_packet(priv); + return true; + } + + if (unlikely(ftmac100_rxdes_multicast(rxdes))) + netdev->stats.multicast++; + + map = ftmac100_rxdes_get_dma_addr(rxdes); + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + + length = ftmac100_rxdes_frame_length(rxdes); + page = ftmac100_rxdes_get_page(rxdes); + skb_fill_page_desc(skb, 0, page, 0, length); + skb->len += length; + skb->data_len += length; + skb->truesize += length; + __pskb_pull_tail(skb, min(length, 64)); + + ftmac100_alloc_rx_page(priv, rxdes); + + ftmac100_rx_pointer_advance(priv); + + skb->protocol = eth_type_trans(skb, netdev); + + netdev->stats.rx_packets++; + netdev->stats.rx_bytes += skb->len; + + /* push packet to protocol stack */ + netif_receive_skb(skb); + + (*processed)++; + return true; +} + +/****************************************************************************** + * internal functions (transmit descriptor) + *****************************************************************************/ +static void ftmac100_txdes_reset(struct ftmac100_txdes *txdes) +{ + /* clear all except end of ring bit */ + txdes->txdes0 = 0; + txdes->txdes1 &= cpu_to_le32(FTMAC100_TXDES1_EDOTR); + txdes->txdes2 = 0; + txdes->txdes3 = 0; +} + +static bool ftmac100_txdes_owned_by_dma(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); +} + +static void ftmac100_txdes_set_dma_own(struct ftmac100_txdes *txdes) +{ + /* + * Make sure dma own bit will not be set before any other + * descriptor fields. + */ + wmb(); + txdes->txdes0 |= cpu_to_le32(FTMAC100_TXDES0_TXDMA_OWN); +} + +static bool ftmac100_txdes_excessive_collision(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_EXSCOL); +} + +static bool ftmac100_txdes_late_collision(struct ftmac100_txdes *txdes) +{ + return txdes->txdes0 & cpu_to_le32(FTMAC100_TXDES0_TXPKT_LATECOL); +} + +static void ftmac100_txdes_set_end_of_ring(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_EDOTR); +} + +static void ftmac100_txdes_set_first_segment(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_FTS); +} + +static void ftmac100_txdes_set_last_segment(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_LTS); +} + +static void ftmac100_txdes_set_txint(struct ftmac100_txdes *txdes) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXIC); +} + +static void ftmac100_txdes_set_buffer_size(struct ftmac100_txdes *txdes, + unsigned int len) +{ + txdes->txdes1 |= cpu_to_le32(FTMAC100_TXDES1_TXBUF_SIZE(len)); +} + +static void ftmac100_txdes_set_dma_addr(struct ftmac100_txdes *txdes, + dma_addr_t addr) +{ + txdes->txdes2 = cpu_to_le32(addr); +} + +static dma_addr_t ftmac100_txdes_get_dma_addr(struct ftmac100_txdes *txdes) +{ + return le32_to_cpu(txdes->txdes2); +} + +/* + * txdes3 is not used by hardware. We use it to keep track of socket buffer. + * Since hardware does not touch it, we can skip cpu_to_le32()/le32_to_cpu(). + */ +static void ftmac100_txdes_set_skb(struct ftmac100_txdes *txdes, struct sk_buff *skb) +{ + txdes->txdes3 = (unsigned int)skb; +} + +static struct sk_buff *ftmac100_txdes_get_skb(struct ftmac100_txdes *txdes) +{ + return (struct sk_buff *)txdes->txdes3; +} + +/****************************************************************************** + * internal functions (transmit) + *****************************************************************************/ +static int ftmac100_next_tx_pointer(int pointer) +{ + return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); +} + +static void ftmac100_tx_pointer_advance(struct ftmac100 *priv) +{ + priv->tx_pointer = ftmac100_next_tx_pointer(priv->tx_pointer); +} + +static void ftmac100_tx_clean_pointer_advance(struct ftmac100 *priv) +{ + priv->tx_clean_pointer = ftmac100_next_tx_pointer(priv->tx_clean_pointer); +} + +static struct ftmac100_txdes *ftmac100_current_txdes(struct ftmac100 *priv) +{ + return &priv->descs->txdes[priv->tx_pointer]; +} + +static struct ftmac100_txdes *ftmac100_current_clean_txdes(struct ftmac100 *priv) +{ + return &priv->descs->txdes[priv->tx_clean_pointer]; +} + +static bool ftmac100_tx_complete_packet(struct ftmac100 *priv) +{ + struct net_device *netdev = priv->netdev; + struct ftmac100_txdes *txdes; + struct sk_buff *skb; + dma_addr_t map; + + if (priv->tx_pending == 0) + return false; + + txdes = ftmac100_current_clean_txdes(priv); + + if (ftmac100_txdes_owned_by_dma(txdes)) + return false; + + skb = ftmac100_txdes_get_skb(txdes); + map = ftmac100_txdes_get_dma_addr(txdes); + + if (unlikely(ftmac100_txdes_excessive_collision(txdes) || + ftmac100_txdes_late_collision(txdes))) { + /* + * packet transmitted to ethernet lost due to late collision + * or excessive collision + */ + netdev->stats.tx_aborted_errors++; + } else { + netdev->stats.tx_packets++; + netdev->stats.tx_bytes += skb->len; + } + + dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); + dev_kfree_skb(skb); + + ftmac100_txdes_reset(txdes); + + ftmac100_tx_clean_pointer_advance(priv); + + spin_lock(&priv->tx_lock); + priv->tx_pending--; + spin_unlock(&priv->tx_lock); + netif_wake_queue(netdev); + + return true; +} + +static void ftmac100_tx_complete(struct ftmac100 *priv) +{ + while (ftmac100_tx_complete_packet(priv)) + ; +} + +static int ftmac100_xmit(struct ftmac100 *priv, struct sk_buff *skb, + dma_addr_t map) +{ + struct net_device *netdev = priv->netdev; + struct ftmac100_txdes *txdes; + unsigned int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; + + txdes = ftmac100_current_txdes(priv); + ftmac100_tx_pointer_advance(priv); + + /* setup TX descriptor */ + ftmac100_txdes_set_skb(txdes, skb); + ftmac100_txdes_set_dma_addr(txdes, map); + + ftmac100_txdes_set_first_segment(txdes); + ftmac100_txdes_set_last_segment(txdes); + ftmac100_txdes_set_txint(txdes); + ftmac100_txdes_set_buffer_size(txdes, len); + + spin_lock(&priv->tx_lock); + priv->tx_pending++; + if (priv->tx_pending == TX_QUEUE_ENTRIES) + netif_stop_queue(netdev); + + /* start transmit */ + ftmac100_txdes_set_dma_own(txdes); + spin_unlock(&priv->tx_lock); + + ftmac100_txdma_start_polling(priv); + return NETDEV_TX_OK; +} + +/****************************************************************************** + * internal functions (buffer) + *****************************************************************************/ +static int ftmac100_alloc_rx_page(struct ftmac100 *priv, struct ftmac100_rxdes *rxdes) +{ + struct net_device *netdev = priv->netdev; + struct page *page; + dma_addr_t map; + + page = alloc_page(GFP_KERNEL); + if (!page) { + if (net_ratelimit()) + netdev_err(netdev, "failed to allocate rx page\n"); + return -ENOMEM; + } + + map = dma_map_page(priv->dev, page, 0, RX_BUF_SIZE, DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + if (net_ratelimit()) + netdev_err(netdev, "failed to map rx page\n"); + __free_page(page); + return -ENOMEM; + } + + ftmac100_rxdes_set_page(rxdes, page); + ftmac100_rxdes_set_dma_addr(rxdes, map); + ftmac100_rxdes_set_buffer_size(rxdes, RX_BUF_SIZE); + ftmac100_rxdes_set_dma_own(rxdes); + return 0; +} + +static void ftmac100_free_buffers(struct ftmac100 *priv) +{ + int i; + + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + struct page *page = ftmac100_rxdes_get_page(rxdes); + dma_addr_t map = ftmac100_rxdes_get_dma_addr(rxdes); + + if (!page) + continue; + + dma_unmap_page(priv->dev, map, RX_BUF_SIZE, DMA_FROM_DEVICE); + __free_page(page); + } + + for (i = 0; i < TX_QUEUE_ENTRIES; i++) { + struct ftmac100_txdes *txdes = &priv->descs->txdes[i]; + struct sk_buff *skb = ftmac100_txdes_get_skb(txdes); + dma_addr_t map = ftmac100_txdes_get_dma_addr(txdes); + + if (!skb) + continue; + + dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); + dev_kfree_skb(skb); + } + + dma_free_coherent(priv->dev, sizeof(struct ftmac100_descs), + priv->descs, priv->descs_dma_addr); +} + +static int ftmac100_alloc_buffers(struct ftmac100 *priv) +{ + int i; + + priv->descs = dma_alloc_coherent(priv->dev, sizeof(struct ftmac100_descs), + &priv->descs_dma_addr, GFP_KERNEL); + if (!priv->descs) + return -ENOMEM; + + memset(priv->descs, 0, sizeof(struct ftmac100_descs)); + + /* initialize RX ring */ + ftmac100_rxdes_set_end_of_ring(&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]); + + for (i = 0; i < RX_QUEUE_ENTRIES; i++) { + struct ftmac100_rxdes *rxdes = &priv->descs->rxdes[i]; + + if (ftmac100_alloc_rx_page(priv, rxdes)) + goto err; + } + + /* initialize TX ring */ + ftmac100_txdes_set_end_of_ring(&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]); + return 0; + +err: + ftmac100_free_buffers(priv); + return -ENOMEM; +} + +/****************************************************************************** + * struct mii_if_info functions + *****************************************************************************/ +static int ftmac100_mdio_read(struct net_device *netdev, int phy_id, int reg) +{ + struct ftmac100 *priv = netdev_priv(netdev); + unsigned int phycr; + int i; + + phycr = FTMAC100_PHYCR_PHYAD(phy_id) | + FTMAC100_PHYCR_REGAD(reg) | + FTMAC100_PHYCR_MIIRD; + + iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR); + + for (i = 0; i < 10; i++) { + phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR); + + if ((phycr & FTMAC100_PHYCR_MIIRD) == 0) + return phycr & FTMAC100_PHYCR_MIIRDATA; + + usleep_range(100, 1000); + } + + netdev_err(netdev, "mdio read timed out\n"); + return 0; +} + +static void ftmac100_mdio_write(struct net_device *netdev, int phy_id, int reg, + int data) +{ + struct ftmac100 *priv = netdev_priv(netdev); + unsigned int phycr; + int i; + + phycr = FTMAC100_PHYCR_PHYAD(phy_id) | + FTMAC100_PHYCR_REGAD(reg) | + FTMAC100_PHYCR_MIIWR; + + data = FTMAC100_PHYWDATA_MIIWDATA(data); + + iowrite32(data, priv->base + FTMAC100_OFFSET_PHYWDATA); + iowrite32(phycr, priv->base + FTMAC100_OFFSET_PHYCR); + + for (i = 0; i < 10; i++) { + phycr = ioread32(priv->base + FTMAC100_OFFSET_PHYCR); + + if ((phycr & FTMAC100_PHYCR_MIIWR) == 0) + return; + + usleep_range(100, 1000); + } + + netdev_err(netdev, "mdio write timed out\n"); +} + +/****************************************************************************** + * struct ethtool_ops functions + *****************************************************************************/ +static void ftmac100_get_drvinfo(struct net_device *netdev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, dev_name(&netdev->dev)); +} + +static int ftmac100_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +{ + struct ftmac100 *priv = netdev_priv(netdev); + return mii_ethtool_gset(&priv->mii, cmd); +} + +static int ftmac100_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd) +{ + struct ftmac100 *priv = netdev_priv(netdev); + return mii_ethtool_sset(&priv->mii, cmd); +} + +static int ftmac100_nway_reset(struct net_device *netdev) +{ + struct ftmac100 *priv = netdev_priv(netdev); + return mii_nway_restart(&priv->mii); +} + +static u32 ftmac100_get_link(struct net_device *netdev) +{ + struct ftmac100 *priv = netdev_priv(netdev); + return mii_link_ok(&priv->mii); +} + +static const struct ethtool_ops ftmac100_ethtool_ops = { + .set_settings = ftmac100_set_settings, + .get_settings = ftmac100_get_settings, + .get_drvinfo = ftmac100_get_drvinfo, + .nway_reset = ftmac100_nway_reset, + .get_link = ftmac100_get_link, +}; + +/****************************************************************************** + * interrupt handler + *****************************************************************************/ +static irqreturn_t ftmac100_interrupt(int irq, void *dev_id) +{ + struct net_device *netdev = dev_id; + struct ftmac100 *priv = netdev_priv(netdev); + + if (likely(netif_running(netdev))) { + /* Disable interrupts for polling */ + ftmac100_disable_all_int(priv); + napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +/****************************************************************************** + * struct napi_struct functions + *****************************************************************************/ +static int ftmac100_poll(struct napi_struct *napi, int budget) +{ + struct ftmac100 *priv = container_of(napi, struct ftmac100, napi); + struct net_device *netdev = priv->netdev; + unsigned int status; + bool completed = true; + int rx = 0; + + status = ioread32(priv->base + FTMAC100_OFFSET_ISR); + + if (status & (FTMAC100_INT_RPKT_FINISH | FTMAC100_INT_NORXBUF)) { + /* + * FTMAC100_INT_RPKT_FINISH: + * RX DMA has received packets into RX buffer successfully + * + * FTMAC100_INT_NORXBUF: + * RX buffer unavailable + */ + bool retry; + + do { + retry = ftmac100_rx_packet(priv, &rx); + } while (retry && rx < budget); + + if (retry && rx == budget) + completed = false; + } + + if (status & (FTMAC100_INT_XPKT_OK | FTMAC100_INT_XPKT_LOST)) { + /* + * FTMAC100_INT_XPKT_OK: + * packet transmitted to ethernet successfully + * + * FTMAC100_INT_XPKT_LOST: + * packet transmitted to ethernet lost due to late + * collision or excessive collision + */ + ftmac100_tx_complete(priv); + } + + if (status & (FTMAC100_INT_NORXBUF | FTMAC100_INT_RPKT_LOST | + FTMAC100_INT_AHB_ERR | FTMAC100_INT_PHYSTS_CHG)) { + if (net_ratelimit()) + netdev_info(netdev, "[ISR] = 0x%x: %s%s%s%s\n", status, + status & FTMAC100_INT_NORXBUF ? "NORXBUF " : "", + status & FTMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "", + status & FTMAC100_INT_AHB_ERR ? "AHB_ERR " : "", + status & FTMAC100_INT_PHYSTS_CHG ? "PHYSTS_CHG" : ""); + + if (status & FTMAC100_INT_NORXBUF) { + /* RX buffer unavailable */ + netdev->stats.rx_over_errors++; + } + + if (status & FTMAC100_INT_RPKT_LOST) { + /* received packet lost due to RX FIFO full */ + netdev->stats.rx_fifo_errors++; + } + + if (status & FTMAC100_INT_PHYSTS_CHG) { + /* PHY link status change */ + mii_check_link(&priv->mii); + } + } + + if (completed) { + /* stop polling */ + napi_complete(napi); + ftmac100_enable_all_int(priv); + } + + return rx; +} + +/****************************************************************************** + * struct net_device_ops functions + *****************************************************************************/ +static int ftmac100_open(struct net_device *netdev) +{ + struct ftmac100 *priv = netdev_priv(netdev); + int err; + + err = ftmac100_alloc_buffers(priv); + if (err) { + netdev_err(netdev, "failed to allocate buffers\n"); + goto err_alloc; + } + + err = request_irq(priv->irq, ftmac100_interrupt, 0, netdev->name, netdev); + if (err) { + netdev_err(netdev, "failed to request irq %d\n", priv->irq); + goto err_irq; + } + + priv->rx_pointer = 0; + priv->tx_clean_pointer = 0; + priv->tx_pointer = 0; + priv->tx_pending = 0; + + err = ftmac100_start_hw(priv); + if (err) + goto err_hw; + + napi_enable(&priv->napi); + netif_start_queue(netdev); + + ftmac100_enable_all_int(priv); + + return 0; + +err_hw: + free_irq(priv->irq, netdev); +err_irq: + ftmac100_free_buffers(priv); +err_alloc: + return err; +} + +static int ftmac100_stop(struct net_device *netdev) +{ + struct ftmac100 *priv = netdev_priv(netdev); + + ftmac100_disable_all_int(priv); + netif_stop_queue(netdev); + napi_disable(&priv->napi); + ftmac100_stop_hw(priv); + free_irq(priv->irq, netdev); + ftmac100_free_buffers(priv); + + return 0; +} + +static int ftmac100_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) +{ + struct ftmac100 *priv = netdev_priv(netdev); + dma_addr_t map; + + if (unlikely(skb->len > MAX_PKT_SIZE)) { + if (net_ratelimit()) + netdev_dbg(netdev, "tx packet too big\n"); + + netdev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + map = dma_map_single(priv->dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(priv->dev, map))) { + /* drop packet */ + if (net_ratelimit()) + netdev_err(netdev, "map socket buffer failed\n"); + + netdev->stats.tx_dropped++; + dev_kfree_skb(skb); + return NETDEV_TX_OK; + } + + return ftmac100_xmit(priv, skb, map); +} + +/* optional */ +static int ftmac100_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) +{ + struct ftmac100 *priv = netdev_priv(netdev); + struct mii_ioctl_data *data = if_mii(ifr); + + return generic_mii_ioctl(&priv->mii, data, cmd, NULL); +} + +static const struct net_device_ops ftmac100_netdev_ops = { + .ndo_open = ftmac100_open, + .ndo_stop = ftmac100_stop, + .ndo_start_xmit = ftmac100_hard_start_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_do_ioctl = ftmac100_do_ioctl, +}; + +/****************************************************************************** + * struct platform_driver functions + *****************************************************************************/ +static int ftmac100_probe(struct platform_device *pdev) +{ + struct resource *res; + int irq; + struct net_device *netdev; + struct ftmac100 *priv; + int err; + + if (!pdev) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + /* setup net_device */ + netdev = alloc_etherdev(sizeof(*priv)); + if (!netdev) { + err = -ENOMEM; + goto err_alloc_etherdev; + } + + SET_NETDEV_DEV(netdev, &pdev->dev); + SET_ETHTOOL_OPS(netdev, &ftmac100_ethtool_ops); + netdev->netdev_ops = &ftmac100_netdev_ops; + + platform_set_drvdata(pdev, netdev); + + /* setup private data */ + priv = netdev_priv(netdev); + priv->netdev = netdev; + priv->dev = &pdev->dev; + + spin_lock_init(&priv->tx_lock); + + /* initialize NAPI */ + netif_napi_add(netdev, &priv->napi, ftmac100_poll, 64); + + /* map io memory */ + priv->res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!priv->res) { + dev_err(&pdev->dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto err_req_mem; + } + + priv->base = ioremap(res->start, res->end - res->start); + if (!priv->base) { + dev_err(&pdev->dev, "Failed to ioremap ethernet registers\n"); + err = -EIO; + goto err_ioremap; + } + + priv->irq = irq; + + /* initialize struct mii_if_info */ + priv->mii.phy_id = 0; + priv->mii.phy_id_mask = 0x1f; + priv->mii.reg_num_mask = 0x1f; + priv->mii.dev = netdev; + priv->mii.mdio_read = ftmac100_mdio_read; + priv->mii.mdio_write = ftmac100_mdio_write; + + /* register network device */ + err = register_netdev(netdev); + if (err) { + dev_err(&pdev->dev, "Failed to register netdev\n"); + goto err_register_netdev; + } + + netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); + + if (!is_valid_ether_addr(netdev->dev_addr)) { + random_ether_addr(netdev->dev_addr); + netdev_info(netdev, "generated random MAC address %pM\n", + netdev->dev_addr); + } + + return 0; + +err_register_netdev: + iounmap(priv->base); +err_ioremap: + release_resource(priv->res); +err_req_mem: + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); +err_alloc_etherdev: + return err; +} + +static int __exit ftmac100_remove(struct platform_device *pdev) +{ + struct net_device *netdev; + struct ftmac100 *priv; + + netdev = platform_get_drvdata(pdev); + priv = netdev_priv(netdev); + + unregister_netdev(netdev); + + iounmap(priv->base); + release_resource(priv->res); + + netif_napi_del(&priv->napi); + platform_set_drvdata(pdev, NULL); + free_netdev(netdev); + return 0; +} + +static struct platform_driver ftmac100_driver = { + .probe = ftmac100_probe, + .remove = __exit_p(ftmac100_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +/****************************************************************************** + * initialization / finalization + *****************************************************************************/ +static int __init ftmac100_init(void) +{ + pr_info("Loading version " DRV_VERSION " ...\n"); + return platform_driver_register(&ftmac100_driver); +} + +static void __exit ftmac100_exit(void) +{ + platform_driver_unregister(&ftmac100_driver); +} + +module_init(ftmac100_init); +module_exit(ftmac100_exit); + +MODULE_AUTHOR("Po-Yu Chuang <ratbert@faraday-tech.com>"); +MODULE_DESCRIPTION("FTMAC100 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ftmac100.h b/drivers/net/ftmac100.h new file mode 100644 index 00000000000..46a0c47b1ee --- /dev/null +++ b/drivers/net/ftmac100.h @@ -0,0 +1,180 @@ +/* + * Faraday FTMAC100 10/100 Ethernet + * + * (C) Copyright 2009-2011 Faraday Technology + * Po-Yu Chuang <ratbert@faraday-tech.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __FTMAC100_H +#define __FTMAC100_H + +#define FTMAC100_OFFSET_ISR 0x00 +#define FTMAC100_OFFSET_IMR 0x04 +#define FTMAC100_OFFSET_MAC_MADR 0x08 +#define FTMAC100_OFFSET_MAC_LADR 0x0c +#define FTMAC100_OFFSET_MAHT0 0x10 +#define FTMAC100_OFFSET_MAHT1 0x14 +#define FTMAC100_OFFSET_TXPD 0x18 +#define FTMAC100_OFFSET_RXPD 0x1c +#define FTMAC100_OFFSET_TXR_BADR 0x20 +#define FTMAC100_OFFSET_RXR_BADR 0x24 +#define FTMAC100_OFFSET_ITC 0x28 +#define FTMAC100_OFFSET_APTC 0x2c +#define FTMAC100_OFFSET_DBLAC 0x30 +#define FTMAC100_OFFSET_MACCR 0x88 +#define FTMAC100_OFFSET_MACSR 0x8c +#define FTMAC100_OFFSET_PHYCR 0x90 +#define FTMAC100_OFFSET_PHYWDATA 0x94 +#define FTMAC100_OFFSET_FCR 0x98 +#define FTMAC100_OFFSET_BPR 0x9c +#define FTMAC100_OFFSET_TS 0xc4 +#define FTMAC100_OFFSET_DMAFIFOS 0xc8 +#define FTMAC100_OFFSET_TM 0xcc +#define FTMAC100_OFFSET_TX_MCOL_SCOL 0xd4 +#define FTMAC100_OFFSET_RPF_AEP 0xd8 +#define FTMAC100_OFFSET_XM_PG 0xdc +#define FTMAC100_OFFSET_RUNT_TLCC 0xe0 +#define FTMAC100_OFFSET_CRCER_FTL 0xe4 +#define FTMAC100_OFFSET_RLC_RCC 0xe8 +#define FTMAC100_OFFSET_BROC 0xec +#define FTMAC100_OFFSET_MULCA 0xf0 +#define FTMAC100_OFFSET_RP 0xf4 +#define FTMAC100_OFFSET_XP 0xf8 + +/* + * Interrupt status register & interrupt mask register + */ +#define FTMAC100_INT_RPKT_FINISH (1 << 0) +#define FTMAC100_INT_NORXBUF (1 << 1) +#define FTMAC100_INT_XPKT_FINISH (1 << 2) +#define FTMAC100_INT_NOTXBUF (1 << 3) +#define FTMAC100_INT_XPKT_OK (1 << 4) +#define FTMAC100_INT_XPKT_LOST (1 << 5) +#define FTMAC100_INT_RPKT_SAV (1 << 6) +#define FTMAC100_INT_RPKT_LOST (1 << 7) +#define FTMAC100_INT_AHB_ERR (1 << 8) +#define FTMAC100_INT_PHYSTS_CHG (1 << 9) + +/* + * Interrupt timer control register + */ +#define FTMAC100_ITC_RXINT_CNT(x) (((x) & 0xf) << 0) +#define FTMAC100_ITC_RXINT_THR(x) (((x) & 0x7) << 4) +#define FTMAC100_ITC_RXINT_TIME_SEL (1 << 7) +#define FTMAC100_ITC_TXINT_CNT(x) (((x) & 0xf) << 8) +#define FTMAC100_ITC_TXINT_THR(x) (((x) & 0x7) << 12) +#define FTMAC100_ITC_TXINT_TIME_SEL (1 << 15) + +/* + * Automatic polling timer control register + */ +#define FTMAC100_APTC_RXPOLL_CNT(x) (((x) & 0xf) << 0) +#define FTMAC100_APTC_RXPOLL_TIME_SEL (1 << 4) +#define FTMAC100_APTC_TXPOLL_CNT(x) (((x) & 0xf) << 8) +#define FTMAC100_APTC_TXPOLL_TIME_SEL (1 << 12) + +/* + * DMA burst length and arbitration control register + */ +#define FTMAC100_DBLAC_INCR4_EN (1 << 0) +#define FTMAC100_DBLAC_INCR8_EN (1 << 1) +#define FTMAC100_DBLAC_INCR16_EN (1 << 2) +#define FTMAC100_DBLAC_RXFIFO_LTHR(x) (((x) & 0x7) << 3) +#define FTMAC100_DBLAC_RXFIFO_HTHR(x) (((x) & 0x7) << 6) +#define FTMAC100_DBLAC_RX_THR_EN (1 << 9) + +/* + * MAC control register + */ +#define FTMAC100_MACCR_XDMA_EN (1 << 0) +#define FTMAC100_MACCR_RDMA_EN (1 << 1) +#define FTMAC100_MACCR_SW_RST (1 << 2) +#define FTMAC100_MACCR_LOOP_EN (1 << 3) +#define FTMAC100_MACCR_CRC_DIS (1 << 4) +#define FTMAC100_MACCR_XMT_EN (1 << 5) +#define FTMAC100_MACCR_ENRX_IN_HALFTX (1 << 6) +#define FTMAC100_MACCR_RCV_EN (1 << 8) +#define FTMAC100_MACCR_HT_MULTI_EN (1 << 9) +#define FTMAC100_MACCR_RX_RUNT (1 << 10) +#define FTMAC100_MACCR_RX_FTL (1 << 11) +#define FTMAC100_MACCR_RCV_ALL (1 << 12) +#define FTMAC100_MACCR_CRC_APD (1 << 14) +#define FTMAC100_MACCR_FULLDUP (1 << 15) +#define FTMAC100_MACCR_RX_MULTIPKT (1 << 16) +#define FTMAC100_MACCR_RX_BROADPKT (1 << 17) + +/* + * PHY control register + */ +#define FTMAC100_PHYCR_MIIRDATA 0xffff +#define FTMAC100_PHYCR_PHYAD(x) (((x) & 0x1f) << 16) +#define FTMAC100_PHYCR_REGAD(x) (((x) & 0x1f) << 21) +#define FTMAC100_PHYCR_MIIRD (1 << 26) +#define FTMAC100_PHYCR_MIIWR (1 << 27) + +/* + * PHY write data register + */ +#define FTMAC100_PHYWDATA_MIIWDATA(x) ((x) & 0xffff) + +/* + * Transmit descriptor, aligned to 16 bytes + */ +struct ftmac100_txdes { + unsigned int txdes0; + unsigned int txdes1; + unsigned int txdes2; /* TXBUF_BADR */ + unsigned int txdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_TXDES0_TXPKT_LATECOL (1 << 0) +#define FTMAC100_TXDES0_TXPKT_EXSCOL (1 << 1) +#define FTMAC100_TXDES0_TXDMA_OWN (1 << 31) + +#define FTMAC100_TXDES1_TXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_TXDES1_LTS (1 << 27) +#define FTMAC100_TXDES1_FTS (1 << 28) +#define FTMAC100_TXDES1_TX2FIC (1 << 29) +#define FTMAC100_TXDES1_TXIC (1 << 30) +#define FTMAC100_TXDES1_EDOTR (1 << 31) + +/* + * Receive descriptor, aligned to 16 bytes + */ +struct ftmac100_rxdes { + unsigned int rxdes0; + unsigned int rxdes1; + unsigned int rxdes2; /* RXBUF_BADR */ + unsigned int rxdes3; /* not used by HW */ +} __attribute__ ((aligned(16))); + +#define FTMAC100_RXDES0_RFL 0x7ff +#define FTMAC100_RXDES0_MULTICAST (1 << 16) +#define FTMAC100_RXDES0_BROADCAST (1 << 17) +#define FTMAC100_RXDES0_RX_ERR (1 << 18) +#define FTMAC100_RXDES0_CRC_ERR (1 << 19) +#define FTMAC100_RXDES0_FTL (1 << 20) +#define FTMAC100_RXDES0_RUNT (1 << 21) +#define FTMAC100_RXDES0_RX_ODD_NB (1 << 22) +#define FTMAC100_RXDES0_LRS (1 << 28) +#define FTMAC100_RXDES0_FRS (1 << 29) +#define FTMAC100_RXDES0_RXDMA_OWN (1 << 31) + +#define FTMAC100_RXDES1_RXBUF_SIZE(x) ((x) & 0x7ff) +#define FTMAC100_RXDES1_EDORR (1 << 31) + +#endif /* __FTMAC100_H */ diff --git a/drivers/net/igb/igb_ethtool.c b/drivers/net/igb/igb_ethtool.c index a70e16bcfa7..61f7849cb5a 100644 --- a/drivers/net/igb/igb_ethtool.c +++ b/drivers/net/igb/igb_ethtool.c @@ -727,8 +727,9 @@ static void igb_get_drvinfo(struct net_device *netdev, char firmware_version[32]; u16 eeprom_data; - strncpy(drvinfo->driver, igb_driver_name, 32); - strncpy(drvinfo->version, igb_driver_version, 32); + strncpy(drvinfo->driver, igb_driver_name, sizeof(drvinfo->driver) - 1); + strncpy(drvinfo->version, igb_driver_version, + sizeof(drvinfo->version) - 1); /* EEPROM image version # is reported as firmware version # for * 82575 controllers */ @@ -738,8 +739,10 @@ static void igb_get_drvinfo(struct net_device *netdev, (eeprom_data & 0x0FF0) >> 4, eeprom_data & 0x000F); - strncpy(drvinfo->fw_version, firmware_version, 32); - strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); + strncpy(drvinfo->fw_version, firmware_version, + sizeof(drvinfo->fw_version) - 1); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), + sizeof(drvinfo->bus_info) - 1); drvinfo->n_stats = IGB_STATS_LEN; drvinfo->testinfo_len = IGB_TEST_LEN; drvinfo->regdump_len = igb_get_regs_len(netdev); @@ -1070,7 +1073,7 @@ static bool reg_pattern_test(struct igb_adapter *adapter, u64 *data, {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; for (pat = 0; pat < ARRAY_SIZE(_test); pat++) { wr32(reg, (_test[pat] & write)); - val = rd32(reg); + val = rd32(reg) & mask; if (val != (_test[pat] & write & mask)) { dev_err(&adapter->pdev->dev, "pattern test reg %04X " "failed: got 0x%08X expected 0x%08X\n", diff --git a/drivers/net/igb/igb_main.c b/drivers/net/igb/igb_main.c index 579dbba5f9e..eef380af053 100644 --- a/drivers/net/igb/igb_main.c +++ b/drivers/net/igb/igb_main.c @@ -2291,7 +2291,12 @@ static int __devinit igb_sw_init(struct igb_adapter *adapter) switch (hw->mac.type) { case e1000_82576: case e1000_i350: - adapter->vfs_allocated_count = (max_vfs > 7) ? 7 : max_vfs; + if (max_vfs > 7) { + dev_warn(&pdev->dev, + "Maximum of 7 VFs per PF, using max\n"); + adapter->vfs_allocated_count = 7; + } else + adapter->vfs_allocated_count = max_vfs; break; default: break; diff --git a/drivers/net/igbvf/vf.c b/drivers/net/igbvf/vf.c index 74486a8b009..af3822f9ea9 100644 --- a/drivers/net/igbvf/vf.c +++ b/drivers/net/igbvf/vf.c @@ -220,7 +220,7 @@ static u32 e1000_hash_mc_addr_vf(struct e1000_hw *hw, u8 *mc_addr) * The parameter rar_count will usually be hw->mac.rar_entry_count * unless there are workarounds that change this. **/ -void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, +static void e1000_update_mc_addr_list_vf(struct e1000_hw *hw, u8 *mc_addr_list, u32 mc_addr_count, u32 rar_used_count, u32 rar_count) { diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index 12769b58c2e..b60b81bc2b1 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_82598.c b/drivers/net/ixgbe/ixgbe_82598.c index d0f1d9d2c41..fc41329399b 100644 --- a/drivers/net/ixgbe/ixgbe_82598.c +++ b/drivers/net/ixgbe/ixgbe_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -627,7 +627,6 @@ out: return 0; } - /** * ixgbe_setup_mac_link_82598 - Set MAC link speed * @hw: pointer to hardware structure @@ -698,7 +697,6 @@ static s32 ixgbe_setup_copper_link_82598(struct ixgbe_hw *hw, /* Setup the PHY according to input speed */ status = hw->phy.ops.setup_link_speed(hw, speed, autoneg, autoneg_wait_to_complete); - /* Set up MAC */ ixgbe_start_mac_link_82598(hw, autoneg_wait_to_complete); @@ -770,7 +768,6 @@ static s32 ixgbe_reset_hw_82598(struct ixgbe_hw *hw) else if (phy_status == IXGBE_ERR_SFP_NOT_PRESENT) goto no_phy_reset; - hw->phy.ops.reset(hw); } @@ -779,12 +776,9 @@ no_phy_reset: * Prevent the PCI-E bus from from hanging by disabling PCI-E master * access and verify no pending requests before reset */ - status = ixgbe_disable_pcie_master(hw); - if (status != 0) { - status = IXGBE_ERR_MASTER_REQUESTS_PENDING; - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); - } + ixgbe_disable_pcie_master(hw); +mac_reset_top: /* * Issue global reset to the MAC. This needs to be a SW reset. * If link reset is used, it might reset the MAC when mng is using it @@ -805,6 +799,19 @@ no_phy_reset: hw_dbg(hw, "Reset polling failed to complete.\n"); } + /* + * Double resets are required for recovery from certain error + * conditions. Between resets, it is necessary to stall to allow time + * for any pending HW events to complete. We use 1usec since that is + * what is needed for ixgbe_disable_pcie_master(). The second reset + * then clears out any effects of those events. + */ + if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) { + hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + udelay(1); + goto mac_reset_top; + } + msleep(50); gheccr = IXGBE_READ_REG(hw, IXGBE_GHECCR); @@ -824,15 +831,15 @@ no_phy_reset: IXGBE_WRITE_REG(hw, IXGBE_AUTOC, hw->mac.orig_autoc); } + /* Store the permanent mac address */ + hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr); + /* * Store MAC address from RAR0, clear receive address registers, and * clear the multicast table */ hw->mac.ops.init_rx_addrs(hw); - /* Store the permanent mac address */ - hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr); - reset_hw_out: if (phy_status) status = phy_status; @@ -849,6 +856,13 @@ reset_hw_out: static s32 ixgbe_set_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) { u32 rar_high; + u32 rar_entries = hw->mac.num_rar_entries; + + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries) { + hw_dbg(hw, "RAR index %d is out of range.\n", rar); + return IXGBE_ERR_INVALID_ARGUMENT; + } rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(rar)); rar_high &= ~IXGBE_RAH_VIND_MASK; @@ -868,14 +882,17 @@ static s32 ixgbe_clear_vmdq_82598(struct ixgbe_hw *hw, u32 rar, u32 vmdq) u32 rar_high; u32 rar_entries = hw->mac.num_rar_entries; - if (rar < rar_entries) { - rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(rar)); - if (rar_high & IXGBE_RAH_VIND_MASK) { - rar_high &= ~IXGBE_RAH_VIND_MASK; - IXGBE_WRITE_REG(hw, IXGBE_RAH(rar), rar_high); - } - } else { + + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries) { hw_dbg(hw, "RAR index %d is out of range.\n", rar); + return IXGBE_ERR_INVALID_ARGUMENT; + } + + rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(rar)); + if (rar_high & IXGBE_RAH_VIND_MASK) { + rar_high &= ~IXGBE_RAH_VIND_MASK; + IXGBE_WRITE_REG(hw, IXGBE_RAH(rar), rar_high); } return 0; @@ -994,13 +1011,12 @@ static s32 ixgbe_write_analog_reg8_82598(struct ixgbe_hw *hw, u32 reg, u8 val) } /** - * ixgbe_read_i2c_eeprom_82598 - Read 8 bit EEPROM word of an SFP+ module - * over I2C interface through an intermediate phy. + * ixgbe_read_i2c_eeprom_82598 - Reads 8 bit word over I2C interface. * @hw: pointer to hardware structure * @byte_offset: EEPROM byte offset to read * @eeprom_data: value read * - * Performs byte read operation to SFP module's EEPROM over I2C interface. + * Performs 8 byte read operation to SFP module's EEPROM over I2C interface. **/ static s32 ixgbe_read_i2c_eeprom_82598(struct ixgbe_hw *hw, u8 byte_offset, u8 *eeprom_data) @@ -1179,13 +1195,14 @@ static struct ixgbe_mac_operations mac_ops_82598 = { .set_vmdq = &ixgbe_set_vmdq_82598, .clear_vmdq = &ixgbe_clear_vmdq_82598, .init_rx_addrs = &ixgbe_init_rx_addrs_generic, - .update_uc_addr_list = &ixgbe_update_uc_addr_list_generic, .update_mc_addr_list = &ixgbe_update_mc_addr_list_generic, .enable_mc = &ixgbe_enable_mc_generic, .disable_mc = &ixgbe_disable_mc_generic, .clear_vfta = &ixgbe_clear_vfta_82598, .set_vfta = &ixgbe_set_vfta_82598, .fc_enable = &ixgbe_fc_enable_82598, + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync, + .release_swfw_sync = &ixgbe_release_swfw_sync, }; static struct ixgbe_eeprom_operations eeprom_ops_82598 = { diff --git a/drivers/net/ixgbe/ixgbe_82599.c b/drivers/net/ixgbe/ixgbe_82599.c index a21f5817685..5ef968a10d4 100644 --- a/drivers/net/ixgbe/ixgbe_82599.c +++ b/drivers/net/ixgbe/ixgbe_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -112,7 +112,8 @@ static s32 ixgbe_setup_sfp_modules_82599(struct ixgbe_hw *hw) goto setup_sfp_out; /* PHY config will finish before releasing the semaphore */ - ret_val = ixgbe_acquire_swfw_sync(hw, IXGBE_GSSR_MAC_CSR_SM); + ret_val = hw->mac.ops.acquire_swfw_sync(hw, + IXGBE_GSSR_MAC_CSR_SM); if (ret_val != 0) { ret_val = IXGBE_ERR_SWFW_SYNC; goto setup_sfp_out; @@ -329,11 +330,14 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) enum ixgbe_media_type media_type; /* Detect if there is a copper PHY attached. */ - if (hw->phy.type == ixgbe_phy_cu_unknown || - hw->phy.type == ixgbe_phy_tn || - hw->phy.type == ixgbe_phy_aq) { + switch (hw->phy.type) { + case ixgbe_phy_cu_unknown: + case ixgbe_phy_tn: + case ixgbe_phy_aq: media_type = ixgbe_media_type_copper; goto out; + default: + break; } switch (hw->device_id) { @@ -354,6 +358,9 @@ static enum ixgbe_media_type ixgbe_get_media_type_82599(struct ixgbe_hw *hw) case IXGBE_DEV_ID_82599_CX4: media_type = ixgbe_media_type_cx4; break; + case IXGBE_DEV_ID_82599_T3_LOM: + media_type = ixgbe_media_type_copper; + break; default: media_type = ixgbe_media_type_unknown; break; @@ -411,14 +418,14 @@ static s32 ixgbe_start_mac_link_82599(struct ixgbe_hw *hw, return status; } - /** - * ixgbe_disable_tx_laser_multispeed_fiber - Disable Tx laser - * @hw: pointer to hardware structure - * - * The base drivers may require better control over SFP+ module - * PHY states. This includes selectively shutting down the Tx - * laser on the PHY, effectively halting physical link. - **/ +/** + * ixgbe_disable_tx_laser_multispeed_fiber - Disable Tx laser + * @hw: pointer to hardware structure + * + * The base drivers may require better control over SFP+ module + * PHY states. This includes selectively shutting down the Tx + * laser on the PHY, effectively halting physical link. + **/ static void ixgbe_disable_tx_laser_multispeed_fiber(struct ixgbe_hw *hw) { u32 esdp_reg = IXGBE_READ_REG(hw, IXGBE_ESDP); @@ -536,7 +543,6 @@ s32 ixgbe_setup_mac_link_multispeed_fiber(struct ixgbe_hw *hw, * Section 73.10.2, we may have to wait up to 500ms if KR is * attempted. 82599 uses the same timing for 10g SFI. */ - for (i = 0; i < 5; i++) { /* Wait for the link partner to also set speed */ msleep(100); @@ -761,7 +767,6 @@ static s32 ixgbe_setup_mac_link_82599(struct ixgbe_hw *hw, else orig_autoc = autoc; - if (link_mode == IXGBE_AUTOC_LMS_KX4_KX_KR || link_mode == IXGBE_AUTOC_LMS_KX4_KX_KR_1G_AN || link_mode == IXGBE_AUTOC_LMS_KX4_KX_KR_SGMII) { @@ -898,12 +903,9 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) * Prevent the PCI-E bus from from hanging by disabling PCI-E master * access and verify no pending requests before reset */ - status = ixgbe_disable_pcie_master(hw); - if (status != 0) { - status = IXGBE_ERR_MASTER_REQUESTS_PENDING; - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); - } + ixgbe_disable_pcie_master(hw); +mac_reset_top: /* * Issue global reset to the MAC. This needs to be a SW reset. * If link reset is used, it might reset the MAC when mng is using it @@ -924,6 +926,19 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) hw_dbg(hw, "Reset polling failed to complete.\n"); } + /* + * Double resets are required for recovery from certain error + * conditions. Between resets, it is necessary to stall to allow time + * for any pending HW events to complete. We use 1usec since that is + * what is needed for ixgbe_disable_pcie_master(). The second reset + * then clears out any effects of those events. + */ + if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) { + hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + udelay(1); + goto mac_reset_top; + } + msleep(50); /* @@ -951,6 +966,9 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) } } + /* Store the permanent mac address */ + hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr); + /* * Store MAC address from RAR0, clear receive address registers, and * clear the multicast table. Also reset num_rar_entries to 128, @@ -959,9 +977,6 @@ static s32 ixgbe_reset_hw_82599(struct ixgbe_hw *hw) hw->mac.num_rar_entries = 128; hw->mac.ops.init_rx_addrs(hw); - /* Store the permanent mac address */ - hw->mac.ops.get_mac_addr(hw, hw->mac.perm_addr); - /* Store the permanent SAN mac address */ hw->mac.ops.get_san_mac_addr(hw, hw->mac.san_addr); @@ -1733,13 +1748,34 @@ static s32 ixgbe_start_hw_82599(struct ixgbe_hw *hw) * @hw: pointer to hardware structure * * Determines the physical layer module found on the current adapter. + * If PHY already detected, maintains current PHY type in hw struct, + * otherwise executes the PHY detection routine. **/ -static s32 ixgbe_identify_phy_82599(struct ixgbe_hw *hw) +s32 ixgbe_identify_phy_82599(struct ixgbe_hw *hw) { s32 status = IXGBE_ERR_PHY_ADDR_INVALID; + + /* Detect PHY if not unknown - returns success if already detected. */ status = ixgbe_identify_phy_generic(hw); - if (status != 0) - status = ixgbe_identify_sfp_module_generic(hw); + if (status != 0) { + /* 82599 10GBASE-T requires an external PHY */ + if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_copper) + goto out; + else + status = ixgbe_identify_sfp_module_generic(hw); + } + + /* Set PHY type none if no PHY detected */ + if (hw->phy.type == ixgbe_phy_unknown) { + hw->phy.type = ixgbe_phy_none; + status = 0; + } + + /* Return error if SFP module has been detected but is not supported */ + if (hw->phy.type == ixgbe_phy_sfp_unsupported) + status = IXGBE_ERR_SFP_NOT_SUPPORTED; + +out: return status; } @@ -1763,11 +1799,12 @@ static u32 ixgbe_get_supported_physical_layer_82599(struct ixgbe_hw *hw) hw->phy.ops.identify(hw); - if (hw->phy.type == ixgbe_phy_tn || - hw->phy.type == ixgbe_phy_aq || - hw->phy.type == ixgbe_phy_cu_unknown) { + switch (hw->phy.type) { + case ixgbe_phy_tn: + case ixgbe_phy_aq: + case ixgbe_phy_cu_unknown: hw->phy.ops.read_reg(hw, MDIO_PMA_EXTABLE, MDIO_MMD_PMAPMD, - &ext_ability); + &ext_ability); if (ext_ability & MDIO_PMA_EXTABLE_10GBT) physical_layer |= IXGBE_PHYSICAL_LAYER_10GBASE_T; if (ext_ability & MDIO_PMA_EXTABLE_1000BT) @@ -1775,6 +1812,8 @@ static u32 ixgbe_get_supported_physical_layer_82599(struct ixgbe_hw *hw) if (ext_ability & MDIO_PMA_EXTABLE_100BTX) physical_layer |= IXGBE_PHYSICAL_LAYER_100BASE_TX; goto out; + default: + break; } switch (autoc & IXGBE_AUTOC_LMS_MASK) { @@ -1886,6 +1925,7 @@ static s32 ixgbe_enable_rx_dma_82599(struct ixgbe_hw *hw, u32 regval) if (secrxreg & IXGBE_SECRXSTAT_SECRX_RDY) break; else + /* Use interrupt-safe sleep just in case */ udelay(10); } @@ -1995,7 +2035,6 @@ static struct ixgbe_mac_operations mac_ops_82599 = { .set_vmdq = &ixgbe_set_vmdq_generic, .clear_vmdq = &ixgbe_clear_vmdq_generic, .init_rx_addrs = &ixgbe_init_rx_addrs_generic, - .update_uc_addr_list = &ixgbe_update_uc_addr_list_generic, .update_mc_addr_list = &ixgbe_update_mc_addr_list_generic, .enable_mc = &ixgbe_enable_mc_generic, .disable_mc = &ixgbe_disable_mc_generic, @@ -2006,6 +2045,9 @@ static struct ixgbe_mac_operations mac_ops_82599 = { .setup_sfp = &ixgbe_setup_sfp_modules_82599, .set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing, .set_vlan_anti_spoofing = &ixgbe_set_vlan_anti_spoofing, + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync, + .release_swfw_sync = &ixgbe_release_swfw_sync, + }; static struct ixgbe_eeprom_operations eeprom_ops_82599 = { diff --git a/drivers/net/ixgbe/ixgbe_common.c b/drivers/net/ixgbe/ixgbe_common.c index ebbda7d1525..a7fb2e00f76 100644 --- a/drivers/net/ixgbe/ixgbe_common.c +++ b/drivers/net/ixgbe/ixgbe_common.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -46,10 +46,7 @@ static void ixgbe_raise_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); static void ixgbe_lower_eeprom_clk(struct ixgbe_hw *hw, u32 *eec); static void ixgbe_release_eeprom(struct ixgbe_hw *hw); -static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index); -static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index); static s32 ixgbe_mta_vector(struct ixgbe_hw *hw, u8 *mc_addr); -static void ixgbe_add_uc_addr(struct ixgbe_hw *hw, u8 *addr, u32 vmdq); static s32 ixgbe_setup_fc(struct ixgbe_hw *hw, s32 packetbuf_num); /** @@ -454,8 +451,7 @@ s32 ixgbe_stop_adapter_generic(struct ixgbe_hw *hw) * Prevent the PCI-E bus from from hanging by disabling PCI-E master * access and verify no pending requests */ - if (ixgbe_disable_pcie_master(hw) != 0) - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); + ixgbe_disable_pcie_master(hw); return 0; } @@ -603,7 +599,6 @@ s32 ixgbe_write_eeprom_generic(struct ixgbe_hw *hw, u16 offset, u16 data) ixgbe_shift_out_eeprom_bits(hw, data, 16); ixgbe_standby_eeprom(hw); - msleep(hw->eeprom.semaphore_delay); /* Done with writing - release the EEPROM */ ixgbe_release_eeprom(hw); } @@ -747,10 +742,10 @@ s32 ixgbe_poll_eerd_eewr_done(struct ixgbe_hw *hw, u32 ee_reg) static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw) { s32 status = 0; - u32 eec = 0; + u32 eec; u32 i; - if (ixgbe_acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) != 0) + if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) != 0) status = IXGBE_ERR_SWFW_SYNC; if (status == 0) { @@ -773,18 +768,18 @@ static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw) IXGBE_WRITE_REG(hw, IXGBE_EEC, eec); hw_dbg(hw, "Could not acquire EEPROM grant\n"); - ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM); + hw->mac.ops.release_swfw_sync(hw, IXGBE_GSSR_EEP_SM); status = IXGBE_ERR_EEPROM; } - } - /* Setup EEPROM for Read/Write */ - if (status == 0) { - /* Clear CS and SK */ - eec &= ~(IXGBE_EEC_CS | IXGBE_EEC_SK); - IXGBE_WRITE_REG(hw, IXGBE_EEC, eec); - IXGBE_WRITE_FLUSH(hw); - udelay(1); + /* Setup EEPROM for Read/Write */ + if (status == 0) { + /* Clear CS and SK */ + eec &= ~(IXGBE_EEC_CS | IXGBE_EEC_SK); + IXGBE_WRITE_REG(hw, IXGBE_EEC, eec); + IXGBE_WRITE_FLUSH(hw); + udelay(1); + } } return status; } @@ -798,13 +793,10 @@ static s32 ixgbe_acquire_eeprom(struct ixgbe_hw *hw) static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) { s32 status = IXGBE_ERR_EEPROM; - u32 timeout; + u32 timeout = 2000; u32 i; u32 swsm; - /* Set timeout value based on size of EEPROM */ - timeout = hw->eeprom.word_size + 1; - /* Get SMBI software semaphore between device drivers first */ for (i = 0; i < timeout; i++) { /* @@ -816,7 +808,7 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) status = 0; break; } - msleep(1); + udelay(50); } /* Now get the semaphore between SW/FW through the SWESMBI bit */ @@ -844,11 +836,14 @@ static s32 ixgbe_get_eeprom_semaphore(struct ixgbe_hw *hw) * was not granted because we don't have access to the EEPROM */ if (i >= timeout) { - hw_dbg(hw, "Driver can't access the Eeprom - Semaphore " + hw_dbg(hw, "SWESMBI Software EEPROM semaphore " "not granted.\n"); ixgbe_release_eeprom_semaphore(hw); status = IXGBE_ERR_EEPROM; } + } else { + hw_dbg(hw, "Software semaphore SMBI between device drivers " + "not granted.\n"); } return status; @@ -1081,10 +1076,13 @@ static void ixgbe_release_eeprom(struct ixgbe_hw *hw) IXGBE_WRITE_REG(hw, IXGBE_EEC, eec); ixgbe_release_swfw_sync(hw, IXGBE_GSSR_EEP_SM); + + /* Delay before attempt to obtain semaphore again to allow FW access */ + msleep(hw->eeprom.semaphore_delay); } /** - * ixgbe_calc_eeprom_checksum - Calculates and returns the checksum + * ixgbe_calc_eeprom_checksum_generic - Calculates and returns the checksum * @hw: pointer to hardware structure **/ u16 ixgbe_calc_eeprom_checksum_generic(struct ixgbe_hw *hw) @@ -1190,7 +1188,7 @@ s32 ixgbe_update_eeprom_checksum_generic(struct ixgbe_hw *hw) if (status == 0) { checksum = hw->eeprom.ops.calc_checksum(hw); status = hw->eeprom.ops.write(hw, IXGBE_EEPROM_CHECKSUM, - checksum); + checksum); } else { hw_dbg(hw, "EEPROM read failed\n"); } @@ -1238,37 +1236,37 @@ s32 ixgbe_set_rar_generic(struct ixgbe_hw *hw, u32 index, u8 *addr, u32 vmdq, u32 rar_low, rar_high; u32 rar_entries = hw->mac.num_rar_entries; + /* Make sure we are using a valid rar index range */ + if (index >= rar_entries) { + hw_dbg(hw, "RAR index %d is out of range.\n", index); + return IXGBE_ERR_INVALID_ARGUMENT; + } + /* setup VMDq pool selection before this RAR gets enabled */ hw->mac.ops.set_vmdq(hw, index, vmdq); - /* Make sure we are using a valid rar index range */ - if (index < rar_entries) { - /* - * HW expects these in little endian so we reverse the byte - * order from network order (big endian) to little endian - */ - rar_low = ((u32)addr[0] | - ((u32)addr[1] << 8) | - ((u32)addr[2] << 16) | - ((u32)addr[3] << 24)); - /* - * Some parts put the VMDq setting in the extra RAH bits, - * so save everything except the lower 16 bits that hold part - * of the address and the address valid bit. - */ - rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index)); - rar_high &= ~(0x0000FFFF | IXGBE_RAH_AV); - rar_high |= ((u32)addr[4] | ((u32)addr[5] << 8)); + /* + * HW expects these in little endian so we reverse the byte + * order from network order (big endian) to little endian + */ + rar_low = ((u32)addr[0] | + ((u32)addr[1] << 8) | + ((u32)addr[2] << 16) | + ((u32)addr[3] << 24)); + /* + * Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ + rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index)); + rar_high &= ~(0x0000FFFF | IXGBE_RAH_AV); + rar_high |= ((u32)addr[4] | ((u32)addr[5] << 8)); - if (enable_addr != 0) - rar_high |= IXGBE_RAH_AV; + if (enable_addr != 0) + rar_high |= IXGBE_RAH_AV; - IXGBE_WRITE_REG(hw, IXGBE_RAL(index), rar_low); - IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high); - } else { - hw_dbg(hw, "RAR index %d is out of range.\n", index); - return IXGBE_ERR_RAR_INDEX; - } + IXGBE_WRITE_REG(hw, IXGBE_RAL(index), rar_low); + IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high); return 0; } @@ -1286,58 +1284,26 @@ s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index) u32 rar_entries = hw->mac.num_rar_entries; /* Make sure we are using a valid rar index range */ - if (index < rar_entries) { - /* - * Some parts put the VMDq setting in the extra RAH bits, - * so save everything except the lower 16 bits that hold part - * of the address and the address valid bit. - */ - rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index)); - rar_high &= ~(0x0000FFFF | IXGBE_RAH_AV); - - IXGBE_WRITE_REG(hw, IXGBE_RAL(index), 0); - IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high); - } else { + if (index >= rar_entries) { hw_dbg(hw, "RAR index %d is out of range.\n", index); - return IXGBE_ERR_RAR_INDEX; + return IXGBE_ERR_INVALID_ARGUMENT; } - /* clear VMDq pool/queue selection for this RAR */ - hw->mac.ops.clear_vmdq(hw, index, IXGBE_CLEAR_VMDQ_ALL); - - return 0; -} - -/** - * ixgbe_enable_rar - Enable Rx address register - * @hw: pointer to hardware structure - * @index: index into the RAR table - * - * Enables the select receive address register. - **/ -static void ixgbe_enable_rar(struct ixgbe_hw *hw, u32 index) -{ - u32 rar_high; - + /* + * Some parts put the VMDq setting in the extra RAH bits, + * so save everything except the lower 16 bits that hold part + * of the address and the address valid bit. + */ rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index)); - rar_high |= IXGBE_RAH_AV; + rar_high &= ~(0x0000FFFF | IXGBE_RAH_AV); + + IXGBE_WRITE_REG(hw, IXGBE_RAL(index), 0); IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high); -} -/** - * ixgbe_disable_rar - Disable Rx address register - * @hw: pointer to hardware structure - * @index: index into the RAR table - * - * Disables the select receive address register. - **/ -static void ixgbe_disable_rar(struct ixgbe_hw *hw, u32 index) -{ - u32 rar_high; + /* clear VMDq pool/queue selection for this RAR */ + hw->mac.ops.clear_vmdq(hw, index, IXGBE_CLEAR_VMDQ_ALL); - rar_high = IXGBE_READ_REG(hw, IXGBE_RAH(index)); - rar_high &= (~IXGBE_RAH_AV); - IXGBE_WRITE_REG(hw, IXGBE_RAH(index), rar_high); + return 0; } /** @@ -1386,7 +1352,6 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) } /* Clear the MTA */ - hw->addr_ctrl.mc_addr_in_rar_count = 0; hw->addr_ctrl.mta_in_use = 0; IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type); @@ -1401,105 +1366,6 @@ s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw) } /** - * ixgbe_add_uc_addr - Adds a secondary unicast address. - * @hw: pointer to hardware structure - * @addr: new address - * - * Adds it to unused receive address register or goes into promiscuous mode. - **/ -static void ixgbe_add_uc_addr(struct ixgbe_hw *hw, u8 *addr, u32 vmdq) -{ - u32 rar_entries = hw->mac.num_rar_entries; - u32 rar; - - hw_dbg(hw, " UC Addr = %.2X %.2X %.2X %.2X %.2X %.2X\n", - addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); - - /* - * Place this address in the RAR if there is room, - * else put the controller into promiscuous mode - */ - if (hw->addr_ctrl.rar_used_count < rar_entries) { - rar = hw->addr_ctrl.rar_used_count - - hw->addr_ctrl.mc_addr_in_rar_count; - hw->mac.ops.set_rar(hw, rar, addr, vmdq, IXGBE_RAH_AV); - hw_dbg(hw, "Added a secondary address to RAR[%d]\n", rar); - hw->addr_ctrl.rar_used_count++; - } else { - hw->addr_ctrl.overflow_promisc++; - } - - hw_dbg(hw, "ixgbe_add_uc_addr Complete\n"); -} - -/** - * ixgbe_update_uc_addr_list_generic - Updates MAC list of secondary addresses - * @hw: pointer to hardware structure - * @netdev: pointer to net device structure - * - * The given list replaces any existing list. Clears the secondary addrs from - * receive address registers. Uses unused receive address registers for the - * first secondary addresses, and falls back to promiscuous mode as needed. - * - * Drivers using secondary unicast addresses must set user_set_promisc when - * manually putting the device into promiscuous mode. - **/ -s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw, - struct net_device *netdev) -{ - u32 i; - u32 old_promisc_setting = hw->addr_ctrl.overflow_promisc; - u32 uc_addr_in_use; - u32 fctrl; - struct netdev_hw_addr *ha; - - /* - * Clear accounting of old secondary address list, - * don't count RAR[0] - */ - uc_addr_in_use = hw->addr_ctrl.rar_used_count - 1; - hw->addr_ctrl.rar_used_count -= uc_addr_in_use; - hw->addr_ctrl.overflow_promisc = 0; - - /* Zero out the other receive addresses */ - hw_dbg(hw, "Clearing RAR[1-%d]\n", uc_addr_in_use + 1); - for (i = 0; i < uc_addr_in_use; i++) { - IXGBE_WRITE_REG(hw, IXGBE_RAL(1+i), 0); - IXGBE_WRITE_REG(hw, IXGBE_RAH(1+i), 0); - } - - /* Add the new addresses */ - netdev_for_each_uc_addr(ha, netdev) { - hw_dbg(hw, " Adding the secondary addresses:\n"); - ixgbe_add_uc_addr(hw, ha->addr, 0); - } - - if (hw->addr_ctrl.overflow_promisc) { - /* enable promisc if not already in overflow or set by user */ - if (!old_promisc_setting && !hw->addr_ctrl.user_set_promisc) { - hw_dbg(hw, " Entering address overflow promisc mode\n"); - fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); - fctrl |= IXGBE_FCTRL_UPE; - IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); - hw->addr_ctrl.uc_set_promisc = true; - } - } else { - /* only disable if set by overflow, not by user */ - if ((old_promisc_setting && hw->addr_ctrl.uc_set_promisc) && - !(hw->addr_ctrl.user_set_promisc)) { - hw_dbg(hw, " Leaving address overflow promisc mode\n"); - fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); - fctrl &= ~IXGBE_FCTRL_UPE; - IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); - hw->addr_ctrl.uc_set_promisc = false; - } - } - - hw_dbg(hw, "ixgbe_update_uc_addr_list_generic Complete\n"); - return 0; -} - -/** * ixgbe_mta_vector - Determines bit-vector in multicast table to set * @hw: pointer to hardware structure * @mc_addr: the multicast address @@ -1550,7 +1416,6 @@ static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr) u32 vector; u32 vector_bit; u32 vector_reg; - u32 mta_reg; hw->addr_ctrl.mta_in_use++; @@ -1568,9 +1433,7 @@ static void ixgbe_set_mta(struct ixgbe_hw *hw, u8 *mc_addr) */ vector_reg = (vector >> 5) & 0x7F; vector_bit = vector & 0x1F; - mta_reg = IXGBE_READ_REG(hw, IXGBE_MTA(vector_reg)); - mta_reg |= (1 << vector_bit); - IXGBE_WRITE_REG(hw, IXGBE_MTA(vector_reg), mta_reg); + hw->mac.mta_shadow[vector_reg] |= (1 << vector_bit); } /** @@ -1596,18 +1459,21 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, hw->addr_ctrl.num_mc_addrs = netdev_mc_count(netdev); hw->addr_ctrl.mta_in_use = 0; - /* Clear the MTA */ + /* Clear mta_shadow */ hw_dbg(hw, " Clearing MTA\n"); - for (i = 0; i < hw->mac.mcft_size; i++) - IXGBE_WRITE_REG(hw, IXGBE_MTA(i), 0); + memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow)); - /* Add the new addresses */ + /* Update mta shadow */ netdev_for_each_mc_addr(ha, netdev) { hw_dbg(hw, " Adding the multicast addresses:\n"); ixgbe_set_mta(hw, ha->addr); } /* Enable mta */ + for (i = 0; i < hw->mac.mcft_size; i++) + IXGBE_WRITE_REG_ARRAY(hw, IXGBE_MTA(0), i, + hw->mac.mta_shadow[i]); + if (hw->addr_ctrl.mta_in_use > 0) IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type); @@ -1624,15 +1490,8 @@ s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, **/ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw) { - u32 i; - u32 rar_entries = hw->mac.num_rar_entries; struct ixgbe_addr_filter_info *a = &hw->addr_ctrl; - if (a->mc_addr_in_rar_count > 0) - for (i = (rar_entries - a->mc_addr_in_rar_count); - i < rar_entries; i++) - ixgbe_enable_rar(hw, i); - if (a->mta_in_use > 0) IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, IXGBE_MCSTCTRL_MFE | hw->mac.mc_filter_type); @@ -1648,15 +1507,8 @@ s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw) **/ s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw) { - u32 i; - u32 rar_entries = hw->mac.num_rar_entries; struct ixgbe_addr_filter_info *a = &hw->addr_ctrl; - if (a->mc_addr_in_rar_count > 0) - for (i = (rar_entries - a->mc_addr_in_rar_count); - i < rar_entries; i++) - ixgbe_disable_rar(hw, i); - if (a->mta_in_use > 0) IXGBE_WRITE_REG(hw, IXGBE_MCSTCTRL, hw->mac.mc_filter_type); @@ -1703,7 +1555,9 @@ s32 ixgbe_fc_enable_generic(struct ixgbe_hw *hw, s32 packetbuf_num) * 2: Tx flow control is enabled (we can send pause frames but * we do not support receiving pause frames). * 3: Both Rx and Tx flow control (symmetric) are enabled. +#ifdef CONFIG_DCB * 4: Priority Flow Control is enabled. +#endif * other: Invalid. */ switch (hw->fc.current_mode) { @@ -2159,10 +2013,16 @@ out: **/ s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) { + struct ixgbe_adapter *adapter = hw->back; u32 i; u32 reg_val; u32 number_of_queues; - s32 status = IXGBE_ERR_MASTER_REQUESTS_PENDING; + s32 status = 0; + u16 dev_status = 0; + + /* Just jump out if bus mastering is already disabled */ + if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) + goto out; /* Disable the receive unit by stopping each queue */ number_of_queues = hw->mac.max_rx_queues; @@ -2179,13 +2039,43 @@ s32 ixgbe_disable_pcie_master(struct ixgbe_hw *hw) IXGBE_WRITE_REG(hw, IXGBE_CTRL, reg_val); for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) { - if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) { - status = 0; + if (!(IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_GIO)) + goto check_device_status; + udelay(100); + } + + hw_dbg(hw, "GIO Master Disable bit didn't clear - requesting resets\n"); + status = IXGBE_ERR_MASTER_REQUESTS_PENDING; + + /* + * Before proceeding, make sure that the PCIe block does not have + * transactions pending. + */ +check_device_status: + for (i = 0; i < IXGBE_PCI_MASTER_DISABLE_TIMEOUT; i++) { + pci_read_config_word(adapter->pdev, IXGBE_PCI_DEVICE_STATUS, + &dev_status); + if (!(dev_status & IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING)) break; - } udelay(100); } + if (i == IXGBE_PCI_MASTER_DISABLE_TIMEOUT) + hw_dbg(hw, "PCIe transaction pending bit also did not clear.\n"); + else + goto out; + + /* + * Two consecutive resets are required via CTRL.RST per datasheet + * 5.2.5.3.2 Master Disable. We set a flag to inform the reset routine + * of this need. The first reset prevents new master requests from + * being issued by our device. We then must wait 1usec for any + * remaining completions from the PCIe bus to trickle in, and then reset + * again to clear out any effects they may have had on our device. + */ + hw->mac.flags |= IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + +out: return status; } @@ -2206,6 +2096,10 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask) s32 timeout = 200; while (timeout) { + /* + * SW EEPROM semaphore bit is used for access to all + * SW_FW_SYNC/GSSR bits (not just EEPROM) + */ if (ixgbe_get_eeprom_semaphore(hw)) return IXGBE_ERR_SWFW_SYNC; @@ -2223,7 +2117,7 @@ s32 ixgbe_acquire_swfw_sync(struct ixgbe_hw *hw, u16 mask) } if (!timeout) { - hw_dbg(hw, "Driver can't access resource, GSSR timeout.\n"); + hw_dbg(hw, "Driver can't access resource, SW_FW_SYNC timeout.\n"); return IXGBE_ERR_SWFW_SYNC; } @@ -2427,37 +2321,38 @@ s32 ixgbe_clear_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) u32 mpsar_lo, mpsar_hi; u32 rar_entries = hw->mac.num_rar_entries; - if (rar < rar_entries) { - mpsar_lo = IXGBE_READ_REG(hw, IXGBE_MPSAR_LO(rar)); - mpsar_hi = IXGBE_READ_REG(hw, IXGBE_MPSAR_HI(rar)); + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries) { + hw_dbg(hw, "RAR index %d is out of range.\n", rar); + return IXGBE_ERR_INVALID_ARGUMENT; + } - if (!mpsar_lo && !mpsar_hi) - goto done; + mpsar_lo = IXGBE_READ_REG(hw, IXGBE_MPSAR_LO(rar)); + mpsar_hi = IXGBE_READ_REG(hw, IXGBE_MPSAR_HI(rar)); - if (vmdq == IXGBE_CLEAR_VMDQ_ALL) { - if (mpsar_lo) { - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), 0); - mpsar_lo = 0; - } - if (mpsar_hi) { - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), 0); - mpsar_hi = 0; - } - } else if (vmdq < 32) { - mpsar_lo &= ~(1 << vmdq); - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), mpsar_lo); - } else { - mpsar_hi &= ~(1 << (vmdq - 32)); - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), mpsar_hi); - } + if (!mpsar_lo && !mpsar_hi) + goto done; - /* was that the last pool using this rar? */ - if (mpsar_lo == 0 && mpsar_hi == 0 && rar != 0) - hw->mac.ops.clear_rar(hw, rar); + if (vmdq == IXGBE_CLEAR_VMDQ_ALL) { + if (mpsar_lo) { + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), 0); + mpsar_lo = 0; + } + if (mpsar_hi) { + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), 0); + mpsar_hi = 0; + } + } else if (vmdq < 32) { + mpsar_lo &= ~(1 << vmdq); + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), mpsar_lo); } else { - hw_dbg(hw, "RAR index %d is out of range.\n", rar); + mpsar_hi &= ~(1 << (vmdq - 32)); + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), mpsar_hi); } + /* was that the last pool using this rar? */ + if (mpsar_lo == 0 && mpsar_hi == 0 && rar != 0) + hw->mac.ops.clear_rar(hw, rar); done: return 0; } @@ -2473,18 +2368,20 @@ s32 ixgbe_set_vmdq_generic(struct ixgbe_hw *hw, u32 rar, u32 vmdq) u32 mpsar; u32 rar_entries = hw->mac.num_rar_entries; - if (rar < rar_entries) { - if (vmdq < 32) { - mpsar = IXGBE_READ_REG(hw, IXGBE_MPSAR_LO(rar)); - mpsar |= 1 << vmdq; - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), mpsar); - } else { - mpsar = IXGBE_READ_REG(hw, IXGBE_MPSAR_HI(rar)); - mpsar |= 1 << (vmdq - 32); - IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), mpsar); - } - } else { + /* Make sure we are using a valid rar index range */ + if (rar >= rar_entries) { hw_dbg(hw, "RAR index %d is out of range.\n", rar); + return IXGBE_ERR_INVALID_ARGUMENT; + } + + if (vmdq < 32) { + mpsar = IXGBE_READ_REG(hw, IXGBE_MPSAR_LO(rar)); + mpsar |= 1 << vmdq; + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_LO(rar), mpsar); + } else { + mpsar = IXGBE_READ_REG(hw, IXGBE_MPSAR_HI(rar)); + mpsar |= 1 << (vmdq - 32); + IXGBE_WRITE_REG(hw, IXGBE_MPSAR_HI(rar), mpsar); } return 0; } @@ -2497,7 +2394,6 @@ s32 ixgbe_init_uta_tables_generic(struct ixgbe_hw *hw) { int i; - for (i = 0; i < 128; i++) IXGBE_WRITE_REG(hw, IXGBE_UTA(i), 0); @@ -2726,12 +2622,21 @@ s32 ixgbe_clear_vfta_generic(struct ixgbe_hw *hw) * Reads the links register to determine if link is up and the current speed **/ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, - bool *link_up, bool link_up_wait_to_complete) + bool *link_up, bool link_up_wait_to_complete) { - u32 links_reg; + u32 links_reg, links_orig; u32 i; + /* clear the old state */ + links_orig = IXGBE_READ_REG(hw, IXGBE_LINKS); + links_reg = IXGBE_READ_REG(hw, IXGBE_LINKS); + + if (links_orig != links_reg) { + hw_dbg(hw, "LINKS changed from %08X to %08X\n", + links_orig, links_reg); + } + if (link_up_wait_to_complete) { for (i = 0; i < IXGBE_LINK_UP_TIME; i++) { if (links_reg & IXGBE_LINKS_UP) { @@ -2754,10 +2659,13 @@ s32 ixgbe_check_mac_link_generic(struct ixgbe_hw *hw, ixgbe_link_speed *speed, IXGBE_LINKS_SPEED_10G_82599) *speed = IXGBE_LINK_SPEED_10GB_FULL; else if ((links_reg & IXGBE_LINKS_SPEED_82599) == - IXGBE_LINKS_SPEED_1G_82599) + IXGBE_LINKS_SPEED_1G_82599) *speed = IXGBE_LINK_SPEED_1GB_FULL; - else + else if ((links_reg & IXGBE_LINKS_SPEED_82599) == + IXGBE_LINKS_SPEED_100_82599) *speed = IXGBE_LINK_SPEED_100_FULL; + else + *speed = IXGBE_LINK_SPEED_UNKNOWN; /* if link is down, zero out the current_mode */ if (*link_up == false) { diff --git a/drivers/net/ixgbe/ixgbe_common.h b/drivers/net/ixgbe/ixgbe_common.h index 90cceb4a631..508f635fc2c 100644 --- a/drivers/net/ixgbe/ixgbe_common.h +++ b/drivers/net/ixgbe/ixgbe_common.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -63,8 +63,6 @@ s32 ixgbe_clear_rar_generic(struct ixgbe_hw *hw, u32 index); s32 ixgbe_init_rx_addrs_generic(struct ixgbe_hw *hw); s32 ixgbe_update_mc_addr_list_generic(struct ixgbe_hw *hw, struct net_device *netdev); -s32 ixgbe_update_uc_addr_list_generic(struct ixgbe_hw *hw, - struct net_device *netdev); s32 ixgbe_enable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_disable_mc_generic(struct ixgbe_hw *hw); s32 ixgbe_enable_rx_dma_generic(struct ixgbe_hw *hw, u32 regval); diff --git a/drivers/net/ixgbe/ixgbe_dcb.c b/drivers/net/ixgbe/ixgbe_dcb.c index 13c962efbfc..c2ee6fcb4e9 100644 --- a/drivers/net/ixgbe/ixgbe_dcb.c +++ b/drivers/net/ixgbe/ixgbe_dcb.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb.h b/drivers/net/ixgbe/ixgbe_dcb.h index e5935114815..515bc27477f 100644 --- a/drivers/net/ixgbe/ixgbe_dcb.h +++ b/drivers/net/ixgbe/ixgbe_dcb.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.c b/drivers/net/ixgbe/ixgbe_dcb_82598.c index 2965edcdac7..c97cf9160dc 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_82598.c +++ b/drivers/net/ixgbe/ixgbe_dcb_82598.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb_82598.h b/drivers/net/ixgbe/ixgbe_dcb_82598.h index 0d2a758effc..1e9750c2b46 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_82598.h +++ b/drivers/net/ixgbe/ixgbe_dcb_82598.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb_82599.c b/drivers/net/ixgbe/ixgbe_dcb_82599.c index b0d97a98c84..beaa1c1c1e6 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_82599.c +++ b/drivers/net/ixgbe/ixgbe_dcb_82599.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb_82599.h b/drivers/net/ixgbe/ixgbe_dcb_82599.h index 5b0ca85614d..0b39ab4ffc7 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_82599.h +++ b/drivers/net/ixgbe/ixgbe_dcb_82599.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_dcb_nl.c b/drivers/net/ixgbe/ixgbe_dcb_nl.c index a977df3fe81..d7f0024014b 100644 --- a/drivers/net/ixgbe/ixgbe_dcb_nl.c +++ b/drivers/net/ixgbe/ixgbe_dcb_nl.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 309272f8f10..83511c02292 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c index c54a88274d5..27203c87ea1 100644 --- a/drivers/net/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ixgbe/ixgbe_fcoe.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_fcoe.h b/drivers/net/ixgbe/ixgbe_fcoe.h index 65cc8fb14fe..02a00d2415d 100644 --- a/drivers/net/ixgbe/ixgbe_fcoe.h +++ b/drivers/net/ixgbe/ixgbe_fcoe.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index f0d0c5aad2b..32231ffe071 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -54,7 +54,8 @@ static const char ixgbe_driver_string[] = #define DRV_VERSION "3.2.9-k2" const char ixgbe_driver_version[] = DRV_VERSION; -static char ixgbe_copyright[] = "Copyright (c) 1999-2010 Intel Corporation."; +static const char ixgbe_copyright[] = + "Copyright (c) 1999-2011 Intel Corporation."; static const struct ixgbe_info *ixgbe_info_tbl[] = { [board_82598] = &ixgbe_82598_info, @@ -2597,6 +2598,11 @@ static void ixgbe_free_irq(struct ixgbe_adapter *adapter) i--; for (; i >= 0; i--) { + /* free only the irqs that were actually requested */ + if (!adapter->q_vector[i]->rxr_count && + !adapter->q_vector[i]->txr_count) + continue; + free_irq(adapter->msix_entries[i].vector, adapter->q_vector[i]); } @@ -3884,7 +3890,7 @@ static int ixgbe_up_complete(struct ixgbe_adapter *adapter) * If we're not hot-pluggable SFP+, we just need to configure link * and bring it up. */ - if (hw->phy.type == ixgbe_phy_unknown) + if (hw->phy.type == ixgbe_phy_none) schedule_work(&adapter->sfp_config_module_task); /* enable transmits */ diff --git a/drivers/net/ixgbe/ixgbe_mbx.c b/drivers/net/ixgbe/ixgbe_mbx.c index f215c4c296c..2acacfa5e37 100644 --- a/drivers/net/ixgbe/ixgbe_mbx.c +++ b/drivers/net/ixgbe/ixgbe_mbx.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_mbx.h b/drivers/net/ixgbe/ixgbe_mbx.h index ada0ce32a7a..fe6ea81dc7f 100644 --- a/drivers/net/ixgbe/ixgbe_mbx.h +++ b/drivers/net/ixgbe/ixgbe_mbx.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_phy.c b/drivers/net/ixgbe/ixgbe_phy.c index 8f7123e8fc0..197230b2d1a 100644 --- a/drivers/net/ixgbe/ixgbe_phy.c +++ b/drivers/net/ixgbe/ixgbe_phy.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -138,17 +138,51 @@ static enum ixgbe_phy_type ixgbe_get_phy_type_from_id(u32 phy_id) **/ s32 ixgbe_reset_phy_generic(struct ixgbe_hw *hw) { + u32 i; + u16 ctrl = 0; + s32 status = 0; + + if (hw->phy.type == ixgbe_phy_unknown) + status = ixgbe_identify_phy_generic(hw); + + if (status != 0 || hw->phy.type == ixgbe_phy_none) + goto out; + /* Don't reset PHY if it's shut down due to overtemp. */ if (!hw->phy.reset_if_overtemp && (IXGBE_ERR_OVERTEMP == hw->phy.ops.check_overtemp(hw))) - return 0; + goto out; /* * Perform soft PHY reset to the PHY_XS. * This will cause a soft reset to the PHY */ - return hw->phy.ops.write_reg(hw, MDIO_CTRL1, MDIO_MMD_PHYXS, - MDIO_CTRL1_RESET); + hw->phy.ops.write_reg(hw, MDIO_CTRL1, + MDIO_MMD_PHYXS, + MDIO_CTRL1_RESET); + + /* + * Poll for reset bit to self-clear indicating reset is complete. + * Some PHYs could take up to 3 seconds to complete and need about + * 1.7 usec delay after the reset is complete. + */ + for (i = 0; i < 30; i++) { + msleep(100); + hw->phy.ops.read_reg(hw, MDIO_CTRL1, + MDIO_MMD_PHYXS, &ctrl); + if (!(ctrl & MDIO_CTRL1_RESET)) { + udelay(2); + break; + } + } + + if (ctrl & MDIO_CTRL1_RESET) { + status = IXGBE_ERR_RESET_FAILED; + hw_dbg(hw, "PHY reset polling failed to complete.\n"); + } + +out: + return status; } /** @@ -171,7 +205,7 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, else gssr = IXGBE_GSSR_PHY0_SM; - if (ixgbe_acquire_swfw_sync(hw, gssr) != 0) + if (hw->mac.ops.acquire_swfw_sync(hw, gssr) != 0) status = IXGBE_ERR_SWFW_SYNC; if (status == 0) { @@ -243,7 +277,7 @@ s32 ixgbe_read_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, } } - ixgbe_release_swfw_sync(hw, gssr); + hw->mac.ops.release_swfw_sync(hw, gssr); } return status; @@ -269,7 +303,7 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, else gssr = IXGBE_GSSR_PHY0_SM; - if (ixgbe_acquire_swfw_sync(hw, gssr) != 0) + if (hw->mac.ops.acquire_swfw_sync(hw, gssr) != 0) status = IXGBE_ERR_SWFW_SYNC; if (status == 0) { @@ -336,7 +370,7 @@ s32 ixgbe_write_phy_reg_generic(struct ixgbe_hw *hw, u32 reg_addr, } } - ixgbe_release_swfw_sync(hw, gssr); + hw->mac.ops.release_swfw_sync(hw, gssr); } return status; @@ -556,11 +590,10 @@ out: } /** - * ixgbe_identify_sfp_module_generic - Identifies SFP module and assigns - * the PHY type. + * ixgbe_identify_sfp_module_generic - Identifies SFP modules * @hw: pointer to hardware structure * - * Searches for and indentifies the SFP module. Assings appropriate PHY type. + * Searches for and identifies the SFP module and assigns appropriate PHY type. **/ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) { @@ -581,41 +614,62 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) goto out; } - status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_IDENTIFIER, + status = hw->phy.ops.read_i2c_eeprom(hw, + IXGBE_SFF_IDENTIFIER, &identifier); - if (status == IXGBE_ERR_SFP_NOT_PRESENT || status == IXGBE_ERR_I2C) { - status = IXGBE_ERR_SFP_NOT_PRESENT; - hw->phy.sfp_type = ixgbe_sfp_type_not_present; - if (hw->phy.type != ixgbe_phy_nl) { - hw->phy.id = 0; - hw->phy.type = ixgbe_phy_unknown; - } - goto out; - } + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; - if (identifier == IXGBE_SFF_IDENTIFIER_SFP) { - hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_1GBE_COMP_CODES, - &comp_codes_1g); - hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_10GBE_COMP_CODES, - &comp_codes_10g); - hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_CABLE_TECHNOLOGY, - &cable_tech); - - /* ID Module - * ========= - * 0 SFP_DA_CU - * 1 SFP_SR - * 2 SFP_LR - * 3 SFP_DA_CORE0 - 82599-specific - * 4 SFP_DA_CORE1 - 82599-specific - * 5 SFP_SR/LR_CORE0 - 82599-specific - * 6 SFP_SR/LR_CORE1 - 82599-specific - * 7 SFP_act_lmt_DA_CORE0 - 82599-specific - * 8 SFP_act_lmt_DA_CORE1 - 82599-specific - * 9 SFP_1g_cu_CORE0 - 82599-specific - * 10 SFP_1g_cu_CORE1 - 82599-specific - */ + /* LAN ID is needed for sfp_type determination */ + hw->mac.ops.set_lan_id(hw); + + if (identifier != IXGBE_SFF_IDENTIFIER_SFP) { + hw->phy.type = ixgbe_phy_sfp_unsupported; + status = IXGBE_ERR_SFP_NOT_SUPPORTED; + } else { + status = hw->phy.ops.read_i2c_eeprom(hw, + IXGBE_SFF_1GBE_COMP_CODES, + &comp_codes_1g); + + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + + status = hw->phy.ops.read_i2c_eeprom(hw, + IXGBE_SFF_10GBE_COMP_CODES, + &comp_codes_10g); + + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + status = hw->phy.ops.read_i2c_eeprom(hw, + IXGBE_SFF_CABLE_TECHNOLOGY, + &cable_tech); + + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + + /* ID Module + * ========= + * 0 SFP_DA_CU + * 1 SFP_SR + * 2 SFP_LR + * 3 SFP_DA_CORE0 - 82599-specific + * 4 SFP_DA_CORE1 - 82599-specific + * 5 SFP_SR/LR_CORE0 - 82599-specific + * 6 SFP_SR/LR_CORE1 - 82599-specific + * 7 SFP_act_lmt_DA_CORE0 - 82599-specific + * 8 SFP_act_lmt_DA_CORE1 - 82599-specific + * 9 SFP_1g_cu_CORE0 - 82599-specific + * 10 SFP_1g_cu_CORE1 - 82599-specific + */ if (hw->mac.type == ixgbe_mac_82598EB) { if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) hw->phy.sfp_type = ixgbe_sfp_type_da_cu; @@ -647,31 +701,27 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) ixgbe_sfp_type_da_act_lmt_core1; } else { hw->phy.sfp_type = - ixgbe_sfp_type_unknown; + ixgbe_sfp_type_unknown; } - } else if (comp_codes_10g & IXGBE_SFF_10GBASESR_CAPABLE) + } else if (comp_codes_10g & + (IXGBE_SFF_10GBASESR_CAPABLE | + IXGBE_SFF_10GBASELR_CAPABLE)) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = ixgbe_sfp_type_srlr_core0; else hw->phy.sfp_type = ixgbe_sfp_type_srlr_core1; - else if (comp_codes_10g & IXGBE_SFF_10GBASELR_CAPABLE) - if (hw->bus.lan_id == 0) - hw->phy.sfp_type = - ixgbe_sfp_type_srlr_core0; - else - hw->phy.sfp_type = - ixgbe_sfp_type_srlr_core1; - else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) + } else if (comp_codes_1g & IXGBE_SFF_1GBASET_CAPABLE) { if (hw->bus.lan_id == 0) hw->phy.sfp_type = ixgbe_sfp_type_1g_cu_core0; else hw->phy.sfp_type = ixgbe_sfp_type_1g_cu_core1; - else + } else { hw->phy.sfp_type = ixgbe_sfp_type_unknown; + } } if (hw->phy.sfp_type != stored_sfp_type) @@ -688,16 +738,33 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) /* Determine PHY vendor */ if (hw->phy.type != ixgbe_phy_nl) { hw->phy.id = identifier; - hw->phy.ops.read_i2c_eeprom(hw, + status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_VENDOR_OUI_BYTE0, &oui_bytes[0]); - hw->phy.ops.read_i2c_eeprom(hw, + + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + + status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_VENDOR_OUI_BYTE1, &oui_bytes[1]); - hw->phy.ops.read_i2c_eeprom(hw, + + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + + status = hw->phy.ops.read_i2c_eeprom(hw, IXGBE_SFF_VENDOR_OUI_BYTE2, &oui_bytes[2]); + if (status == IXGBE_ERR_SWFW_SYNC || + status == IXGBE_ERR_I2C || + status == IXGBE_ERR_SFP_NOT_PRESENT) + goto err_read_i2c_eeprom; + vendor_oui = ((oui_bytes[0] << IXGBE_SFF_VENDOR_OUI_BYTE0_SHIFT) | (oui_bytes[1] << IXGBE_SFF_VENDOR_OUI_BYTE1_SHIFT) | @@ -707,7 +774,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) case IXGBE_SFF_VENDOR_OUI_TYCO: if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) hw->phy.type = - ixgbe_phy_sfp_passive_tyco; + ixgbe_phy_sfp_passive_tyco; break; case IXGBE_SFF_VENDOR_OUI_FTL: if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) @@ -724,7 +791,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) default: if (cable_tech & IXGBE_SFF_DA_PASSIVE_CABLE) hw->phy.type = - ixgbe_phy_sfp_passive_unknown; + ixgbe_phy_sfp_passive_unknown; else if (cable_tech & IXGBE_SFF_DA_ACTIVE_CABLE) hw->phy.type = ixgbe_phy_sfp_active_unknown; @@ -734,7 +801,7 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) } } - /* All passive DA cables are supported */ + /* Allow any DA cable vendor */ if (cable_tech & (IXGBE_SFF_DA_PASSIVE_CABLE | IXGBE_SFF_DA_ACTIVE_CABLE)) { status = 0; @@ -776,15 +843,24 @@ s32 ixgbe_identify_sfp_module_generic(struct ixgbe_hw *hw) out: return status; + +err_read_i2c_eeprom: + hw->phy.sfp_type = ixgbe_sfp_type_not_present; + if (hw->phy.type != ixgbe_phy_nl) { + hw->phy.id = 0; + hw->phy.type = ixgbe_phy_unknown; + } + return IXGBE_ERR_SFP_NOT_PRESENT; } /** - * ixgbe_get_sfp_init_sequence_offsets - Checks the MAC's EEPROM to see - * if it supports a given SFP+ module type, if so it returns the offsets to the - * phy init sequence block. + * ixgbe_get_sfp_init_sequence_offsets - Provides offset of PHY init sequence * @hw: pointer to hardware structure * @list_offset: offset to the SFP ID list * @data_offset: offset to the SFP data block + * + * Checks the MAC's EEPROM to see if it supports a given SFP+ module type, if + * so it returns the offsets to the phy init sequence block. **/ s32 ixgbe_get_sfp_init_sequence_offsets(struct ixgbe_hw *hw, u16 *list_offset, @@ -899,11 +975,22 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, u8 dev_addr, u8 *data) { s32 status = 0; - u32 max_retry = 1; + u32 max_retry = 10; u32 retry = 0; + u16 swfw_mask = 0; bool nack = 1; + if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1) + swfw_mask = IXGBE_GSSR_PHY1_SM; + else + swfw_mask = IXGBE_GSSR_PHY0_SM; + do { + if (ixgbe_acquire_swfw_sync(hw, swfw_mask) != 0) { + status = IXGBE_ERR_SWFW_SYNC; + goto read_byte_out; + } + ixgbe_i2c_start(hw); /* Device Address and write indication */ @@ -946,6 +1033,8 @@ s32 ixgbe_read_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, break; fail: + ixgbe_release_swfw_sync(hw, swfw_mask); + msleep(100); ixgbe_i2c_bus_clear(hw); retry++; if (retry < max_retry) @@ -955,6 +1044,9 @@ fail: } while (retry < max_retry); + ixgbe_release_swfw_sync(hw, swfw_mask); + +read_byte_out: return status; } @@ -973,6 +1065,17 @@ s32 ixgbe_write_i2c_byte_generic(struct ixgbe_hw *hw, u8 byte_offset, s32 status = 0; u32 max_retry = 1; u32 retry = 0; + u16 swfw_mask = 0; + + if (IXGBE_READ_REG(hw, IXGBE_STATUS) & IXGBE_STATUS_LAN_ID_1) + swfw_mask = IXGBE_GSSR_PHY1_SM; + else + swfw_mask = IXGBE_GSSR_PHY0_SM; + + if (ixgbe_acquire_swfw_sync(hw, swfw_mask) != 0) { + status = IXGBE_ERR_SWFW_SYNC; + goto write_byte_out; + } do { ixgbe_i2c_start(hw); @@ -1013,6 +1116,9 @@ fail: hw_dbg(hw, "I2C byte write error.\n"); } while (retry < max_retry); + ixgbe_release_swfw_sync(hw, swfw_mask); + +write_byte_out: return status; } @@ -1331,6 +1437,8 @@ static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw) u32 i2cctl = IXGBE_READ_REG(hw, IXGBE_I2CCTL); u32 i; + ixgbe_i2c_start(hw); + ixgbe_set_i2c_data(hw, &i2cctl, 1); for (i = 0; i < 9; i++) { @@ -1345,6 +1453,8 @@ static void ixgbe_i2c_bus_clear(struct ixgbe_hw *hw) udelay(IXGBE_I2C_T_LOW); } + ixgbe_i2c_start(hw); + /* Put the i2c bus back to default state */ ixgbe_i2c_stop(hw); } diff --git a/drivers/net/ixgbe/ixgbe_phy.h b/drivers/net/ixgbe/ixgbe_phy.h index e2c6b7eac64..2327baf0442 100644 --- a/drivers/net/ixgbe/ixgbe_phy.h +++ b/drivers/net/ixgbe/ixgbe_phy.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_sriov.c b/drivers/net/ixgbe/ixgbe_sriov.c index fb4868d0a32..58c9b45989f 100644 --- a/drivers/net/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ixgbe/ixgbe_sriov.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_sriov.h b/drivers/net/ixgbe/ixgbe_sriov.h index 49dc14debef..e7dd029d576 100644 --- a/drivers/net/ixgbe/ixgbe_sriov.h +++ b/drivers/net/ixgbe/ixgbe_sriov.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, diff --git a/drivers/net/ixgbe/ixgbe_type.h b/drivers/net/ixgbe/ixgbe_type.h index ab65d13969f..013751db5fc 100644 --- a/drivers/net/ixgbe/ixgbe_type.h +++ b/drivers/net/ixgbe/ixgbe_type.h @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -91,7 +91,7 @@ /* General Receive Control */ #define IXGBE_GRC_MNG 0x00000001 /* Manageability Enable */ -#define IXGBE_GRC_APME 0x00000002 /* Advanced Power Management Enable */ +#define IXGBE_GRC_APME 0x00000002 /* APM enabled in EEPROM */ #define IXGBE_VPDDIAG0 0x10204 #define IXGBE_VPDDIAG1 0x10208 @@ -342,7 +342,7 @@ /* Wake Up Control */ #define IXGBE_WUC_PME_EN 0x00000002 /* PME Enable */ #define IXGBE_WUC_PME_STATUS 0x00000004 /* PME Status */ -#define IXGBE_WUC_ADVD3WUC 0x00000010 /* D3Cold wake up cap. enable*/ +#define IXGBE_WUC_WKEN 0x00000010 /* Enable PE_WAKE_N pin assertion */ /* Wake Up Filter Control */ #define IXGBE_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */ @@ -1614,6 +1614,8 @@ #define IXGBE_ALT_SAN_MAC_ADDR_CAPS_ALTWWN 0x1 /* Alt. WWN base exists */ /* PCI Bus Info */ +#define IXGBE_PCI_DEVICE_STATUS 0xAA +#define IXGBE_PCI_DEVICE_STATUS_TRANSACTION_PENDING 0x0020 #define IXGBE_PCI_LINK_STATUS 0xB2 #define IXGBE_PCI_DEVICE_CONTROL2 0xC8 #define IXGBE_PCI_LINK_WIDTH 0x3F0 @@ -2242,6 +2244,7 @@ enum ixgbe_mac_type { enum ixgbe_phy_type { ixgbe_phy_unknown = 0, + ixgbe_phy_none, ixgbe_phy_tn, ixgbe_phy_aq, ixgbe_phy_cu_unknown, @@ -2330,32 +2333,31 @@ enum ixgbe_bus_type { /* PCI bus speeds */ enum ixgbe_bus_speed { ixgbe_bus_speed_unknown = 0, - ixgbe_bus_speed_33, - ixgbe_bus_speed_66, - ixgbe_bus_speed_100, - ixgbe_bus_speed_120, - ixgbe_bus_speed_133, - ixgbe_bus_speed_2500, - ixgbe_bus_speed_5000, + ixgbe_bus_speed_33 = 33, + ixgbe_bus_speed_66 = 66, + ixgbe_bus_speed_100 = 100, + ixgbe_bus_speed_120 = 120, + ixgbe_bus_speed_133 = 133, + ixgbe_bus_speed_2500 = 2500, + ixgbe_bus_speed_5000 = 5000, ixgbe_bus_speed_reserved }; /* PCI bus widths */ enum ixgbe_bus_width { ixgbe_bus_width_unknown = 0, - ixgbe_bus_width_pcie_x1, - ixgbe_bus_width_pcie_x2, + ixgbe_bus_width_pcie_x1 = 1, + ixgbe_bus_width_pcie_x2 = 2, ixgbe_bus_width_pcie_x4 = 4, ixgbe_bus_width_pcie_x8 = 8, - ixgbe_bus_width_32, - ixgbe_bus_width_64, + ixgbe_bus_width_32 = 32, + ixgbe_bus_width_64 = 64, ixgbe_bus_width_reserved }; struct ixgbe_addr_filter_info { u32 num_mc_addrs; u32 rar_used_count; - u32 mc_addr_in_rar_count; u32 mta_in_use; u32 overflow_promisc; bool uc_set_promisc; @@ -2493,6 +2495,8 @@ struct ixgbe_mac_operations { s32 (*write_analog_reg8)(struct ixgbe_hw*, u32, u8); s32 (*setup_sfp)(struct ixgbe_hw *); s32 (*enable_rx_dma)(struct ixgbe_hw *, u32); + s32 (*acquire_swfw_sync)(struct ixgbe_hw *, u16); + void (*release_swfw_sync)(struct ixgbe_hw *, u16); /* Link */ void (*disable_tx_laser)(struct ixgbe_hw *); @@ -2515,7 +2519,6 @@ struct ixgbe_mac_operations { s32 (*set_vmdq)(struct ixgbe_hw *, u32, u32); s32 (*clear_vmdq)(struct ixgbe_hw *, u32, u32); s32 (*init_rx_addrs)(struct ixgbe_hw *); - s32 (*update_uc_addr_list)(struct ixgbe_hw *, struct net_device *); s32 (*update_mc_addr_list)(struct ixgbe_hw *, struct net_device *); s32 (*enable_mc)(struct ixgbe_hw *); s32 (*disable_mc)(struct ixgbe_hw *); @@ -2556,6 +2559,7 @@ struct ixgbe_eeprom_info { u16 address_bits; }; +#define IXGBE_FLAGS_DOUBLE_RESET_REQUIRED 0x01 struct ixgbe_mac_info { struct ixgbe_mac_operations ops; enum ixgbe_mac_type type; @@ -2566,6 +2570,8 @@ struct ixgbe_mac_info { u16 wwnn_prefix; /* prefix for World Wide Port Name (WWPN) */ u16 wwpn_prefix; +#define IXGBE_MAX_MTA 128 + u32 mta_shadow[IXGBE_MAX_MTA]; s32 mc_filter_type; u32 mcft_size; u32 vft_size; @@ -2578,6 +2584,7 @@ struct ixgbe_mac_info { u32 orig_autoc2; bool orig_link_settings_stored; bool autotry_restart; + u8 flags; }; struct ixgbe_phy_info { @@ -2684,7 +2691,6 @@ struct ixgbe_info { #define IXGBE_ERR_EEPROM_VERSION -24 #define IXGBE_ERR_NO_SPACE -25 #define IXGBE_ERR_OVERTEMP -26 -#define IXGBE_ERR_RAR_INDEX -27 #define IXGBE_ERR_SFP_SETUP_NOT_COMPLETE -30 #define IXGBE_ERR_PBA_SECTION -31 #define IXGBE_ERR_INVALID_ARGUMENT -32 diff --git a/drivers/net/ixgbe/ixgbe_x540.c b/drivers/net/ixgbe/ixgbe_x540.c index f2518b01067..2e3a2b4fa8b 100644 --- a/drivers/net/ixgbe/ixgbe_x540.c +++ b/drivers/net/ixgbe/ixgbe_x540.c @@ -1,7 +1,7 @@ /******************************************************************************* Intel 10 Gigabit PCI Express Linux driver - Copyright(c) 1999 - 2010 Intel Corporation. + Copyright(c) 1999 - 2011 Intel Corporation. This program is free software; you can redistribute it and/or modify it under the terms and conditions of the GNU General Public License, @@ -31,7 +31,6 @@ #include "ixgbe.h" #include "ixgbe_phy.h" -//#include "ixgbe_mbx.h" #define IXGBE_X540_MAX_TX_QUEUES 128 #define IXGBE_X540_MAX_RX_QUEUES 128 @@ -110,12 +109,9 @@ static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) * Prevent the PCI-E bus from from hanging by disabling PCI-E master * access and verify no pending requests before reset */ - status = ixgbe_disable_pcie_master(hw); - if (status != 0) { - status = IXGBE_ERR_MASTER_REQUESTS_PENDING; - hw_dbg(hw, "PCI-E Master disable polling has failed.\n"); - } + ixgbe_disable_pcie_master(hw); +mac_reset_top: /* * Issue global reset to the MAC. Needs to be SW reset if link is up. * If link reset is used when link is up, it might reset the PHY when @@ -148,6 +144,19 @@ static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) hw_dbg(hw, "Reset polling failed to complete.\n"); } + /* + * Double resets are required for recovery from certain error + * conditions. Between resets, it is necessary to stall to allow time + * for any pending HW events to complete. We use 1usec since that is + * what is needed for ixgbe_disable_pcie_master(). The second reset + * then clears out any effects of those events. + */ + if (hw->mac.flags & IXGBE_FLAGS_DOUBLE_RESET_REQUIRED) { + hw->mac.flags &= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; + udelay(1); + goto mac_reset_top; + } + /* Clear PF Reset Done bit so PF/VF Mail Ops can work */ ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD; @@ -191,7 +200,7 @@ static s32 ixgbe_reset_hw_X540(struct ixgbe_hw *hw) * clear the multicast table. Also reset num_rar_entries to 128, * since we modify this value when programming the SAN MAC address. */ - hw->mac.num_rar_entries = 128; + hw->mac.num_rar_entries = IXGBE_X540_MAX_TX_QUEUES; hw->mac.ops.init_rx_addrs(hw); /* Store the permanent mac address */ @@ -278,7 +287,7 @@ static s32 ixgbe_read_eerd_X540(struct ixgbe_hw *hw, u16 offset, u16 *data) { s32 status; - if (ixgbe_acquire_swfw_sync_X540(hw, IXGBE_GSSR_EEP_SM) == 0) + if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) == 0) status = ixgbe_read_eerd_generic(hw, offset, data); else status = IXGBE_ERR_SWFW_SYNC; @@ -311,7 +320,7 @@ static s32 ixgbe_write_eewr_X540(struct ixgbe_hw *hw, u16 offset, u16 data) (data << IXGBE_EEPROM_RW_REG_DATA) | IXGBE_EEPROM_RW_REG_START; - if (ixgbe_acquire_swfw_sync_X540(hw, IXGBE_GSSR_EEP_SM) == 0) { + if (hw->mac.ops.acquire_swfw_sync(hw, IXGBE_GSSR_EEP_SM) == 0) { status = ixgbe_poll_eerd_eewr_done(hw, IXGBE_NVM_POLL_WRITE); if (status != 0) { hw_dbg(hw, "Eeprom write EEWR timed out\n"); @@ -676,7 +685,6 @@ static struct ixgbe_mac_operations mac_ops_X540 = { .set_vmdq = &ixgbe_set_vmdq_generic, .clear_vmdq = &ixgbe_clear_vmdq_generic, .init_rx_addrs = &ixgbe_init_rx_addrs_generic, - .update_uc_addr_list = &ixgbe_update_uc_addr_list_generic, .update_mc_addr_list = &ixgbe_update_mc_addr_list_generic, .enable_mc = &ixgbe_enable_mc_generic, .disable_mc = &ixgbe_disable_mc_generic, @@ -687,6 +695,8 @@ static struct ixgbe_mac_operations mac_ops_X540 = { .setup_sfp = NULL, .set_mac_anti_spoofing = &ixgbe_set_mac_anti_spoofing, .set_vlan_anti_spoofing = &ixgbe_set_vlan_anti_spoofing, + .acquire_swfw_sync = &ixgbe_acquire_swfw_sync_X540, + .release_swfw_sync = &ixgbe_release_swfw_sync_X540, }; static struct ixgbe_eeprom_operations eeprom_ops_X540 = { @@ -702,7 +712,7 @@ static struct ixgbe_phy_operations phy_ops_X540 = { .identify = &ixgbe_identify_phy_generic, .identify_sfp = &ixgbe_identify_sfp_module_generic, .init = NULL, - .reset = &ixgbe_reset_phy_generic, + .reset = NULL, .read_reg = &ixgbe_read_phy_reg_generic, .write_reg = &ixgbe_write_phy_reg_generic, .setup_link = &ixgbe_setup_phy_link_generic, diff --git a/drivers/net/ixgbevf/ixgbevf_main.c b/drivers/net/ixgbevf/ixgbevf_main.c index 1e735a14091..82768812552 100644 --- a/drivers/net/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ixgbevf/ixgbevf_main.c @@ -178,8 +178,6 @@ static inline bool ixgbevf_check_tx_hang(struct ixgbevf_adapter *adapter, tx_ring->tx_buffer_info[eop].time_stamp && time_after(jiffies, tx_ring->tx_buffer_info[eop].time_stamp + HZ)) { /* detected Tx unit hang */ - union ixgbe_adv_tx_desc *tx_desc; - tx_desc = IXGBE_TX_DESC_ADV(*tx_ring, eop); printk(KERN_ERR "Detected Tx Unit Hang\n" " Tx Queue <%d>\n" " TDH, TDT <%x>, <%x>\n" @@ -334,7 +332,6 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector, struct ixgbevf_adapter *adapter = q_vector->adapter; bool is_vlan = (status & IXGBE_RXD_STAT_VP); u16 tag = le16_to_cpu(rx_desc->wb.upper.vlan); - int ret; if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) { if (adapter->vlgrp && is_vlan) @@ -345,9 +342,9 @@ static void ixgbevf_receive_skb(struct ixgbevf_q_vector *q_vector, napi_gro_receive(&q_vector->napi, skb); } else { if (adapter->vlgrp && is_vlan) - ret = vlan_hwaccel_rx(skb, adapter->vlgrp, tag); + vlan_hwaccel_rx(skb, adapter->vlgrp, tag); else - ret = netif_rx(skb); + netif_rx(skb); } } @@ -3287,8 +3284,6 @@ static const struct net_device_ops ixgbe_netdev_ops = { static void ixgbevf_assign_netdev_ops(struct net_device *dev) { - struct ixgbevf_adapter *adapter; - adapter = netdev_priv(dev); dev->netdev_ops = &ixgbe_netdev_ops; ixgbevf_set_ethtool_ops(dev); dev->watchdog_timeo = 5 * HZ; diff --git a/drivers/net/macb.c b/drivers/net/macb.c index f69e73e2191..79ccb54ab00 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -260,7 +260,7 @@ static int macb_mii_init(struct macb *bp) for (i = 0; i < PHY_MAX_ADDR; i++) bp->mii_bus->irq[i] = PHY_POLL; - platform_set_drvdata(bp->dev, bp->mii_bus); + dev_set_drvdata(&bp->dev->dev, bp->mii_bus); if (mdiobus_register(bp->mii_bus)) goto err_out_free_mdio_irq; diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 210b2b164b3..0a6c6a2e755 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -354,7 +354,7 @@ unsigned int mii_check_media (struct mii_if_info *mii, if (!new_carrier) { netif_carrier_off(mii->dev); if (ok_to_print) - printk(KERN_INFO "%s: link down\n", mii->dev->name); + netdev_info(mii->dev, "link down\n"); return 0; /* duplex did not change */ } @@ -381,12 +381,12 @@ unsigned int mii_check_media (struct mii_if_info *mii, duplex = 1; if (ok_to_print) - printk(KERN_INFO "%s: link up, %sMbps, %s-duplex, lpa 0x%04X\n", - mii->dev->name, - lpa2 & (LPA_1000FULL | LPA_1000HALF) ? "1000" : - media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? "100" : "10", - duplex ? "full" : "half", - lpa); + netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n", + lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 : + media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? + 100 : 10, + duplex ? "full" : "half", + lpa); if ((init_media) || (mii->full_duplex != duplex)) { mii->full_duplex = duplex; diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 02076e16542..34425b94452 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -35,6 +35,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/dma-mapping.h> #include <linux/in.h> @@ -627,9 +629,8 @@ err: if ((cmd_sts & (RX_FIRST_DESC | RX_LAST_DESC)) != (RX_FIRST_DESC | RX_LAST_DESC)) { if (net_ratelimit()) - dev_printk(KERN_ERR, &mp->dev->dev, - "received packet spanning " - "multiple descriptors\n"); + netdev_err(mp->dev, + "received packet spanning multiple descriptors\n"); } if (cmd_sts & ERROR_SUMMARY) @@ -868,15 +869,14 @@ static netdev_tx_t mv643xx_eth_xmit(struct sk_buff *skb, struct net_device *dev) if (has_tiny_unaligned_frags(skb) && __skb_linearize(skb)) { txq->tx_dropped++; - dev_printk(KERN_DEBUG, &dev->dev, - "failed to linearize skb with tiny " - "unaligned fragment\n"); + netdev_printk(KERN_DEBUG, dev, + "failed to linearize skb with tiny unaligned fragment\n"); return NETDEV_TX_BUSY; } if (txq->tx_ring_size - txq->tx_desc_count < MAX_SKB_FRAGS + 1) { if (net_ratelimit()) - dev_printk(KERN_ERR, &dev->dev, "tx queue full?!\n"); + netdev_err(dev, "tx queue full?!\n"); kfree_skb(skb); return NETDEV_TX_OK; } @@ -959,7 +959,7 @@ static int txq_reclaim(struct tx_queue *txq, int budget, int force) skb = __skb_dequeue(&txq->tx_skb); if (cmd_sts & ERROR_SUMMARY) { - dev_printk(KERN_INFO, &mp->dev->dev, "tx error\n"); + netdev_info(mp->dev, "tx error\n"); mp->dev->stats.tx_errors++; } @@ -1122,20 +1122,20 @@ static int smi_bus_read(struct mii_bus *bus, int addr, int reg) int ret; if (smi_wait_ready(msp)) { - printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + pr_warn("SMI bus busy timeout\n"); return -ETIMEDOUT; } writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg); if (smi_wait_ready(msp)) { - printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + pr_warn("SMI bus busy timeout\n"); return -ETIMEDOUT; } ret = readl(smi_reg); if (!(ret & SMI_READ_VALID)) { - printk(KERN_WARNING "mv643xx_eth: SMI bus read not valid\n"); + pr_warn("SMI bus read not valid\n"); return -ENODEV; } @@ -1148,7 +1148,7 @@ static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val) void __iomem *smi_reg = msp->base + SMI_REG; if (smi_wait_ready(msp)) { - printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + pr_warn("SMI bus busy timeout\n"); return -ETIMEDOUT; } @@ -1156,7 +1156,7 @@ static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val) (addr << 16) | (val & 0xffff), smi_reg); if (smi_wait_ready(msp)) { - printk(KERN_WARNING "mv643xx_eth: SMI bus busy timeout\n"); + pr_warn("SMI bus busy timeout\n"); return -ETIMEDOUT; } @@ -1566,9 +1566,8 @@ mv643xx_eth_set_ringparam(struct net_device *dev, struct ethtool_ringparam *er) if (netif_running(dev)) { mv643xx_eth_stop(dev); if (mv643xx_eth_open(dev)) { - dev_printk(KERN_ERR, &dev->dev, - "fatal error on re-opening device after " - "ring param change\n"); + netdev_err(dev, + "fatal error on re-opening device after ring param change\n"); return -ENOMEM; } } @@ -1874,7 +1873,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) } if (rxq->rx_desc_area == NULL) { - dev_printk(KERN_ERR, &mp->dev->dev, + netdev_err(mp->dev, "can't allocate rx ring (%d bytes)\n", size); goto out; } @@ -1884,8 +1883,7 @@ static int rxq_init(struct mv643xx_eth_private *mp, int index) rxq->rx_skb = kmalloc(rxq->rx_ring_size * sizeof(*rxq->rx_skb), GFP_KERNEL); if (rxq->rx_skb == NULL) { - dev_printk(KERN_ERR, &mp->dev->dev, - "can't allocate rx skb ring\n"); + netdev_err(mp->dev, "can't allocate rx skb ring\n"); goto out_free; } @@ -1944,8 +1942,7 @@ static void rxq_deinit(struct rx_queue *rxq) } if (rxq->rx_desc_count) { - dev_printk(KERN_ERR, &mp->dev->dev, - "error freeing rx ring -- %d skbs stuck\n", + netdev_err(mp->dev, "error freeing rx ring -- %d skbs stuck\n", rxq->rx_desc_count); } @@ -1987,7 +1984,7 @@ static int txq_init(struct mv643xx_eth_private *mp, int index) } if (txq->tx_desc_area == NULL) { - dev_printk(KERN_ERR, &mp->dev->dev, + netdev_err(mp->dev, "can't allocate tx ring (%d bytes)\n", size); return -ENOMEM; } @@ -2093,7 +2090,7 @@ static void handle_link_event(struct mv643xx_eth_private *mp) if (netif_carrier_ok(dev)) { int i; - printk(KERN_INFO "%s: link down\n", dev->name); + netdev_info(dev, "link down\n"); netif_carrier_off(dev); @@ -2124,10 +2121,8 @@ static void handle_link_event(struct mv643xx_eth_private *mp) duplex = (port_status & FULL_DUPLEX) ? 1 : 0; fc = (port_status & FLOW_CONTROL_ENABLED) ? 1 : 0; - printk(KERN_INFO "%s: link up, %d Mb/s, %s duplex, " - "flow control %sabled\n", dev->name, - speed, duplex ? "full" : "half", - fc ? "en" : "dis"); + netdev_info(dev, "link up, %d Mb/s, %s duplex, flow control %sabled\n", + speed, duplex ? "full" : "half", fc ? "en" : "dis"); if (!netif_carrier_ok(dev)) netif_carrier_on(dev); @@ -2337,7 +2332,7 @@ static int mv643xx_eth_open(struct net_device *dev) err = request_irq(dev->irq, mv643xx_eth_irq, IRQF_SHARED, dev->name, dev); if (err) { - dev_printk(KERN_ERR, &dev->dev, "can't assign irq\n"); + netdev_err(dev, "can't assign irq\n"); return -EAGAIN; } @@ -2483,9 +2478,8 @@ static int mv643xx_eth_change_mtu(struct net_device *dev, int new_mtu) */ mv643xx_eth_stop(dev); if (mv643xx_eth_open(dev)) { - dev_printk(KERN_ERR, &dev->dev, - "fatal error on re-opening device after " - "MTU change\n"); + netdev_err(dev, + "fatal error on re-opening device after MTU change\n"); } return 0; @@ -2508,7 +2502,7 @@ static void mv643xx_eth_tx_timeout(struct net_device *dev) { struct mv643xx_eth_private *mp = netdev_priv(dev); - dev_printk(KERN_INFO, &dev->dev, "tx timeout\n"); + netdev_info(dev, "tx timeout\n"); schedule_work(&mp->tx_timeout_task); } @@ -2603,8 +2597,8 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev) int ret; if (!mv643xx_eth_version_printed++) - printk(KERN_NOTICE "MV-643xx 10/100/1000 ethernet " - "driver version %s\n", mv643xx_eth_driver_version); + pr_notice("MV-643xx 10/100/1000 ethernet driver version %s\n", + mv643xx_eth_driver_version); ret = -EINVAL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -2871,14 +2865,12 @@ static int mv643xx_eth_probe(struct platform_device *pdev) pd = pdev->dev.platform_data; if (pd == NULL) { - dev_printk(KERN_ERR, &pdev->dev, - "no mv643xx_eth_platform_data\n"); + dev_err(&pdev->dev, "no mv643xx_eth_platform_data\n"); return -ENODEV; } if (pd->shared == NULL) { - dev_printk(KERN_ERR, &pdev->dev, - "no mv643xx_eth_platform_data->shared\n"); + dev_err(&pdev->dev, "no mv643xx_eth_platform_data->shared\n"); return -ENODEV; } @@ -2957,11 +2949,11 @@ static int mv643xx_eth_probe(struct platform_device *pdev) if (err) goto out; - dev_printk(KERN_NOTICE, &dev->dev, "port %d with MAC address %pM\n", - mp->port_num, dev->dev_addr); + netdev_notice(dev, "port %d with MAC address %pM\n", + mp->port_num, dev->dev_addr); if (mp->tx_desc_sram_size > 0) - dev_printk(KERN_NOTICE, &dev->dev, "configured with sram\n"); + netdev_notice(dev, "configured with sram\n"); return 0; diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 9226cda4d05..530ab5a10bd 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -691,6 +691,7 @@ static struct pcmcia_device_id fmvj18x_ids[] = { PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0105, 0x0e0a), PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0e01), PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x0b05), PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0032, 0x1101), PCMCIA_DEVICE_NULL, }; diff --git a/drivers/net/pptp.c b/drivers/net/pptp.c index 164cfad6ce7..1af549c89d5 100644 --- a/drivers/net/pptp.c +++ b/drivers/net/pptp.c @@ -175,7 +175,6 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) struct pptp_opt *opt = &po->proto.pptp; struct pptp_gre_header *hdr; unsigned int header_len = sizeof(*hdr); - int err = 0; int islcp; int len; unsigned char *data; @@ -198,8 +197,8 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb) .saddr = opt->src_addr.sin_addr.s_addr, .tos = RT_TOS(0) } }, .proto = IPPROTO_GRE }; - err = ip_route_output_key(&init_net, &rt, &fl); - if (err) + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) goto tx_error; } tdev = rt->dst.dev; @@ -477,7 +476,8 @@ static int pptp_connect(struct socket *sock, struct sockaddr *uservaddr, .tos = RT_CONN_FLAGS(sk) } }, .proto = IPPROTO_GRE }; security_sk_classify_flow(sk, &fl); - if (ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { error = -EHOSTUNREACH; goto end; } diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 52e20d5c8e1..5e403511289 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -25,6 +25,7 @@ #include <linux/dma-mapping.h> #include <linux/pm_runtime.h> #include <linux/firmware.h> +#include <linux/pci-aspm.h> #include <asm/system.h> #include <asm/io.h> @@ -626,8 +627,9 @@ static void ocp_write(struct rtl8169_private *tp, u8 mask, u16 reg, u32 data) } } -static void rtl8168_oob_notify(void __iomem *ioaddr, u8 cmd) +static void rtl8168_oob_notify(struct rtl8169_private *tp, u8 cmd) { + void __iomem *ioaddr = tp->mmio_addr; int i; RTL_W8(ERIDR, cmd); @@ -639,7 +641,7 @@ static void rtl8168_oob_notify(void __iomem *ioaddr, u8 cmd) break; } - ocp_write(ioaddr, 0x1, 0x30, 0x00000001); + ocp_write(tp, 0x1, 0x30, 0x00000001); } #define OOB_CMD_RESET 0x00 @@ -2913,8 +2915,11 @@ static void r8168_pll_power_down(struct rtl8169_private *tp) { void __iomem *ioaddr = tp->mmio_addr; - if (tp->mac_version == RTL_GIGA_MAC_VER_27) + if (((tp->mac_version == RTL_GIGA_MAC_VER_27) || + (tp->mac_version == RTL_GIGA_MAC_VER_28)) && + (ocp_read(tp, 0x0f, 0x0010) & 0x00008000)) { return; + } if (((tp->mac_version == RTL_GIGA_MAC_VER_23) || (tp->mac_version == RTL_GIGA_MAC_VER_24)) && @@ -2936,6 +2941,8 @@ static void r8168_pll_power_down(struct rtl8169_private *tp) switch (tp->mac_version) { case RTL_GIGA_MAC_VER_25: case RTL_GIGA_MAC_VER_26: + case RTL_GIGA_MAC_VER_27: + case RTL_GIGA_MAC_VER_28: RTL_W8(PMCH, RTL_R8(PMCH) & ~0x80); break; } @@ -2945,12 +2952,17 @@ static void r8168_pll_power_up(struct rtl8169_private *tp) { void __iomem *ioaddr = tp->mmio_addr; - if (tp->mac_version == RTL_GIGA_MAC_VER_27) + if (((tp->mac_version == RTL_GIGA_MAC_VER_27) || + (tp->mac_version == RTL_GIGA_MAC_VER_28)) && + (ocp_read(tp, 0x0f, 0x0010) & 0x00008000)) { return; + } switch (tp->mac_version) { case RTL_GIGA_MAC_VER_25: case RTL_GIGA_MAC_VER_26: + case RTL_GIGA_MAC_VER_27: + case RTL_GIGA_MAC_VER_28: RTL_W8(PMCH, RTL_R8(PMCH) | 0x80); break; } @@ -3056,6 +3068,11 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) mii->reg_num_mask = 0x1f; mii->supports_gmii = !!(cfg->features & RTL_FEATURE_GMII); + /* disable ASPM completely as that cause random device stop working + * problems as well as full system hangs for some PCIe devices users */ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + /* enable device (incl. PCI PM wakeup and hotplug setup) */ rc = pci_enable_device(pdev); if (rc < 0) { @@ -3089,7 +3106,7 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_mwi_2; } - tp->cp_cmd = PCIMulRW | RxChkSum; + tp->cp_cmd = RxChkSum; if ((sizeof(dma_addr_t) > 4) && !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) && use_dac) { @@ -3364,7 +3381,8 @@ static void rtl8169_hw_reset(struct rtl8169_private *tp) /* Disable interrupts */ rtl8169_irq_mask_and_ack(ioaddr); - if (tp->mac_version == RTL_GIGA_MAC_VER_28) { + if (tp->mac_version == RTL_GIGA_MAC_VER_27 || + tp->mac_version == RTL_GIGA_MAC_VER_28) { while (RTL_R8(TxPoll) & NPQ) udelay(20); @@ -3893,8 +3911,7 @@ static void rtl_hw_start_8168(struct net_device *dev) Cxpl_dbg_sel | \ ASF | \ PktCntrDisable | \ - PCIDAC | \ - PCIMulRW) + Mac_dbgo_sel) static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev) { @@ -3924,8 +3941,6 @@ static void rtl_hw_start_8102e_1(void __iomem *ioaddr, struct pci_dev *pdev) if ((cfg1 & LEDS0) && (cfg1 & LEDS1)) RTL_W8(Config1, cfg1 & ~LEDS0); - RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); - rtl_ephy_init(ioaddr, e_info_8102e_1, ARRAY_SIZE(e_info_8102e_1)); } @@ -3937,8 +3952,6 @@ static void rtl_hw_start_8102e_2(void __iomem *ioaddr, struct pci_dev *pdev) RTL_W8(Config1, MEMMAP | IOMAP | VPD | PMEnable); RTL_W8(Config3, RTL_R8(Config3) & ~Beacon_en); - - RTL_W16(CPlusCmd, RTL_R16(CPlusCmd) & ~R810X_CPCMD_QUIRK_MASK); } static void rtl_hw_start_8102e_3(void __iomem *ioaddr, struct pci_dev *pdev) @@ -3995,6 +4008,8 @@ static void rtl_hw_start_8101(struct net_device *dev) } } + RTL_W8(Cfg9346, Cfg9346_Unlock); + switch (tp->mac_version) { case RTL_GIGA_MAC_VER_07: rtl_hw_start_8102e_1(ioaddr, pdev); @@ -4016,14 +4031,13 @@ static void rtl_hw_start_8101(struct net_device *dev) break; } - RTL_W8(Cfg9346, Cfg9346_Unlock); + RTL_W8(Cfg9346, Cfg9346_Lock); RTL_W8(MaxTxPacketSize, TxPacketMax); rtl_set_rx_max_size(ioaddr, rx_buf_sz); - tp->cp_cmd |= rtl_rw_cpluscmd(ioaddr) | PCIMulRW; - + tp->cp_cmd &= ~R810X_CPCMD_QUIRK_MASK; RTL_W16(CPlusCmd, tp->cp_cmd); RTL_W16(IntrMitigate, 0x0000); @@ -4033,14 +4047,10 @@ static void rtl_hw_start_8101(struct net_device *dev) RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); rtl_set_rx_tx_config_registers(tp); - RTL_W8(Cfg9346, Cfg9346_Lock); - RTL_R8(IntrMask); rtl_set_rx_mode(dev); - RTL_W8(ChipCmd, CmdTxEnb | CmdRxEnb); - RTL_W16(MultiIntr, RTL_R16(MultiIntr) & 0xf000); RTL_W16(IntrMask, tp->intr_event); diff --git a/drivers/net/sfc/efx.c b/drivers/net/sfc/efx.c index 35b7bc52a2d..d563049859a 100644 --- a/drivers/net/sfc/efx.c +++ b/drivers/net/sfc/efx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2009 Solarflare Communications Inc. + * Copyright 2005-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/efx.h b/drivers/net/sfc/efx.h index cbce62b9c99..3d83a1f74fe 100644 --- a/drivers/net/sfc/efx.h +++ b/drivers/net/sfc/efx.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/ethtool.c b/drivers/net/sfc/ethtool.c index 272cfe724e1..807178ef65a 100644 --- a/drivers/net/sfc/ethtool.c +++ b/drivers/net/sfc/ethtool.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -28,7 +28,8 @@ struct efx_ethtool_stat { enum { EFX_ETHTOOL_STAT_SOURCE_mac_stats, EFX_ETHTOOL_STAT_SOURCE_nic, - EFX_ETHTOOL_STAT_SOURCE_channel + EFX_ETHTOOL_STAT_SOURCE_channel, + EFX_ETHTOOL_STAT_SOURCE_tx_queue } source; unsigned offset; u64(*get_stat) (void *field); /* Reader function */ @@ -86,6 +87,10 @@ static u64 efx_get_atomic_stat(void *field) EFX_ETHTOOL_STAT(field, channel, n_##field, \ unsigned int, efx_get_uint_stat) +#define EFX_ETHTOOL_UINT_TXQ_STAT(field) \ + EFX_ETHTOOL_STAT(tx_##field, tx_queue, field, \ + unsigned int, efx_get_uint_stat) + static struct efx_ethtool_stat efx_ethtool_stats[] = { EFX_ETHTOOL_U64_MAC_STAT(tx_bytes), EFX_ETHTOOL_U64_MAC_STAT(tx_good_bytes), @@ -116,6 +121,10 @@ static struct efx_ethtool_stat efx_ethtool_stats[] = { EFX_ETHTOOL_ULONG_MAC_STAT(tx_non_tcpudp), EFX_ETHTOOL_ULONG_MAC_STAT(tx_mac_src_error), EFX_ETHTOOL_ULONG_MAC_STAT(tx_ip_src_error), + EFX_ETHTOOL_UINT_TXQ_STAT(tso_bursts), + EFX_ETHTOOL_UINT_TXQ_STAT(tso_long_headers), + EFX_ETHTOOL_UINT_TXQ_STAT(tso_packets), + EFX_ETHTOOL_UINT_TXQ_STAT(pushes), EFX_ETHTOOL_U64_MAC_STAT(rx_bytes), EFX_ETHTOOL_U64_MAC_STAT(rx_good_bytes), EFX_ETHTOOL_U64_MAC_STAT(rx_bad_bytes), @@ -237,8 +246,8 @@ static void efx_ethtool_get_drvinfo(struct net_device *net_dev, strlcpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); strlcpy(info->version, EFX_DRIVER_VERSION, sizeof(info->version)); if (efx_nic_rev(efx) >= EFX_REV_SIENA_A0) - siena_print_fwver(efx, info->fw_version, - sizeof(info->fw_version)); + efx_mcdi_print_fwver(efx, info->fw_version, + sizeof(info->fw_version)); strlcpy(info->bus_info, pci_name(efx->pci_dev), sizeof(info->bus_info)); } @@ -470,6 +479,7 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, struct efx_mac_stats *mac_stats = &efx->mac_stats; struct efx_ethtool_stat *stat; struct efx_channel *channel; + struct efx_tx_queue *tx_queue; struct rtnl_link_stats64 temp; int i; @@ -495,6 +505,15 @@ static void efx_ethtool_get_stats(struct net_device *net_dev, data[i] += stat->get_stat((void *)channel + stat->offset); break; + case EFX_ETHTOOL_STAT_SOURCE_tx_queue: + data[i] = 0; + efx_for_each_channel(channel, efx) { + efx_for_each_channel_tx_queue(tx_queue, channel) + data[i] += + stat->get_stat((void *)tx_queue + + stat->offset); + } + break; } } } @@ -569,9 +588,14 @@ static void efx_ethtool_self_test(struct net_device *net_dev, struct ethtool_test *test, u64 *data) { struct efx_nic *efx = netdev_priv(net_dev); - struct efx_self_tests efx_tests; + struct efx_self_tests *efx_tests; int already_up; - int rc; + int rc = -ENOMEM; + + efx_tests = kzalloc(sizeof(*efx_tests), GFP_KERNEL); + if (!efx_tests) + goto fail; + ASSERT_RTNL(); if (efx->state != STATE_RUNNING) { @@ -589,13 +613,11 @@ static void efx_ethtool_self_test(struct net_device *net_dev, if (rc) { netif_err(efx, drv, efx->net_dev, "failed opening device.\n"); - goto fail2; + goto fail1; } } - memset(&efx_tests, 0, sizeof(efx_tests)); - - rc = efx_selftest(efx, &efx_tests, test->flags); + rc = efx_selftest(efx, efx_tests, test->flags); if (!already_up) dev_close(efx->net_dev); @@ -604,10 +626,11 @@ static void efx_ethtool_self_test(struct net_device *net_dev, rc == 0 ? "passed" : "failed", (test->flags & ETH_TEST_FL_OFFLINE) ? "off" : "on"); - fail2: - fail1: +fail1: /* Fill ethtool results structures */ - efx_ethtool_fill_self_tests(efx, &efx_tests, NULL, data); + efx_ethtool_fill_self_tests(efx, efx_tests, NULL, data); + kfree(efx_tests); +fail: if (rc) test->flags |= ETH_TEST_FL_FAILED; } diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 61ddd2c6e75..734fcfb52e8 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -1478,36 +1478,26 @@ static void falcon_init_rx_cfg(struct efx_nic *efx) /* RX control FIFO thresholds (32 entries) */ const unsigned ctrl_xon_thr = 20; const unsigned ctrl_xoff_thr = 25; - /* RX data FIFO thresholds (256-byte units; size varies) */ - int data_xon_thr = efx_nic_rx_xon_thresh >> 8; - int data_xoff_thr = efx_nic_rx_xoff_thresh >> 8; efx_oword_t reg; efx_reado(efx, ®, FR_AZ_RX_CFG); if (efx_nic_rev(efx) <= EFX_REV_FALCON_A1) { /* Data FIFO size is 5.5K */ - if (data_xon_thr < 0) - data_xon_thr = 512 >> 8; - if (data_xoff_thr < 0) - data_xoff_thr = 2048 >> 8; EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_DESC_PUSH_EN, 0); EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_USR_BUF_SIZE, huge_buf_size); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, data_xon_thr); - EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, data_xoff_thr); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_MAC_TH, 512 >> 8); + EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_MAC_TH, 2048 >> 8); EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XON_TX_TH, ctrl_xon_thr); EFX_SET_OWORD_FIELD(reg, FRF_AA_RX_XOFF_TX_TH, ctrl_xoff_thr); } else { /* Data FIFO size is 80K; register fields moved */ - if (data_xon_thr < 0) - data_xon_thr = 27648 >> 8; /* ~3*max MTU */ - if (data_xoff_thr < 0) - data_xoff_thr = 54272 >> 8; /* ~80Kb - 3*max MTU */ EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_DESC_PUSH_EN, 0); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_USR_BUF_SIZE, huge_buf_size); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, data_xon_thr); - EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, data_xoff_thr); + /* Send XON and XOFF at ~3 * max MTU away from empty/full */ + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_MAC_TH, 27648 >> 8); + EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_MAC_TH, 54272 >> 8); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XON_TX_TH, ctrl_xon_thr); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_XOFF_TX_TH, ctrl_xoff_thr); EFX_SET_OWORD_FIELD(reg, FRF_BZ_RX_INGR_EN, 1); diff --git a/drivers/net/sfc/falcon_boards.c b/drivers/net/sfc/falcon_boards.c index 2dd16f0b3ce..b9cc846811d 100644 --- a/drivers/net/sfc/falcon_boards.c +++ b/drivers/net/sfc/falcon_boards.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2009 Solarflare Communications Inc. + * Copyright 2007-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/falcon_xmac.c b/drivers/net/sfc/falcon_xmac.c index b49e8439464..2c9ee5db3bf 100644 --- a/drivers/net/sfc/falcon_xmac.c +++ b/drivers/net/sfc/falcon_xmac.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/io.h b/drivers/net/sfc/io.h index 6da4ae20a03..dc45110b245 100644 --- a/drivers/net/sfc/io.h +++ b/drivers/net/sfc/io.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/mcdi.c b/drivers/net/sfc/mcdi.c index b716e827b29..8bba8955f31 100644 --- a/drivers/net/sfc/mcdi.c +++ b/drivers/net/sfc/mcdi.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2008-2009 Solarflare Communications Inc. + * Copyright 2008-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -602,7 +602,7 @@ void efx_mcdi_process_event(struct efx_channel *channel, ************************************************************************** */ -int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) +void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len) { u8 outbuf[ALIGN(MC_CMD_GET_VERSION_V1_OUT_LEN, 4)]; size_t outlength; @@ -616,29 +616,20 @@ int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build) if (rc) goto fail; - if (outlength == MC_CMD_GET_VERSION_V0_OUT_LEN) { - *version = 0; - *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); - return 0; - } - if (outlength < MC_CMD_GET_VERSION_V1_OUT_LEN) { rc = -EIO; goto fail; } ver_words = (__le16 *)MCDI_PTR(outbuf, GET_VERSION_OUT_VERSION); - *version = (((u64)le16_to_cpu(ver_words[0]) << 48) | - ((u64)le16_to_cpu(ver_words[1]) << 32) | - ((u64)le16_to_cpu(ver_words[2]) << 16) | - le16_to_cpu(ver_words[3])); - *build = MCDI_DWORD(outbuf, GET_VERSION_OUT_FIRMWARE); - - return 0; + snprintf(buf, len, "%u.%u.%u.%u", + le16_to_cpu(ver_words[0]), le16_to_cpu(ver_words[1]), + le16_to_cpu(ver_words[2]), le16_to_cpu(ver_words[3])); + return; fail: netif_err(efx, probe, efx->net_dev, "%s: failed rc=%d\n", __func__, rc); - return rc; + buf[0] = 0; } int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, diff --git a/drivers/net/sfc/mcdi.h b/drivers/net/sfc/mcdi.h index c792f1d65e4..aced2a7856f 100644 --- a/drivers/net/sfc/mcdi.h +++ b/drivers/net/sfc/mcdi.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2008-2009 Solarflare Communications Inc. + * Copyright 2008-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -93,7 +93,7 @@ extern void efx_mcdi_process_event(struct efx_channel *channel, #define MCDI_EVENT_FIELD(_ev, _field) \ EFX_QWORD_FIELD(_ev, MCDI_EVENT_ ## _field) -extern int efx_mcdi_fwver(struct efx_nic *efx, u64 *version, u32 *build); +extern void efx_mcdi_print_fwver(struct efx_nic *efx, char *buf, size_t len); extern int efx_mcdi_drv_attach(struct efx_nic *efx, bool driver_operating, bool *was_attached_out); extern int efx_mcdi_get_board_cfg(struct efx_nic *efx, u8 *mac_address, diff --git a/drivers/net/sfc/mcdi_mac.c b/drivers/net/sfc/mcdi_mac.c index f88f4bf986f..33f7294edb4 100644 --- a/drivers/net/sfc/mcdi_mac.c +++ b/drivers/net/sfc/mcdi_mac.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2009 Solarflare Communications Inc. + * Copyright 2009-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/mcdi_pcol.h b/drivers/net/sfc/mcdi_pcol.h index 90359e64400..b86a15f221a 100644 --- a/drivers/net/sfc/mcdi_pcol.h +++ b/drivers/net/sfc/mcdi_pcol.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2009 Solarflare Communications Inc. + * Copyright 2009-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/mcdi_phy.c b/drivers/net/sfc/mcdi_phy.c index 0e97eed663c..ec3f740f546 100644 --- a/drivers/net/sfc/mcdi_phy.c +++ b/drivers/net/sfc/mcdi_phy.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2009 Solarflare Communications Inc. + * Copyright 2009-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/mdio_10g.c b/drivers/net/sfc/mdio_10g.c index 56b0266b441..19e68c26d10 100644 --- a/drivers/net/sfc/mdio_10g.c +++ b/drivers/net/sfc/mdio_10g.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -51,13 +51,10 @@ int efx_mdio_reset_mmd(struct efx_nic *port, int mmd, return spins ? spins : -ETIMEDOUT; } -static int efx_mdio_check_mmd(struct efx_nic *efx, int mmd, int fault_fatal) +static int efx_mdio_check_mmd(struct efx_nic *efx, int mmd) { int status; - if (LOOPBACK_INTERNAL(efx)) - return 0; - if (mmd != MDIO_MMD_AN) { /* Read MMD STATUS2 to check it is responding. */ status = efx_mdio_read(efx, mmd, MDIO_STAT2); @@ -68,20 +65,6 @@ static int efx_mdio_check_mmd(struct efx_nic *efx, int mmd, int fault_fatal) } } - /* Read MMD STATUS 1 to check for fault. */ - status = efx_mdio_read(efx, mmd, MDIO_STAT1); - if (status & MDIO_STAT1_FAULT) { - if (fault_fatal) { - netif_err(efx, hw, efx->net_dev, - "PHY MMD %d reporting fatal" - " fault: status %x\n", mmd, status); - return -EIO; - } else { - netif_dbg(efx, hw, efx->net_dev, - "PHY MMD %d reporting status" - " %x (expected)\n", mmd, status); - } - } return 0; } @@ -130,8 +113,7 @@ int efx_mdio_wait_reset_mmds(struct efx_nic *efx, unsigned int mmd_mask) return rc; } -int efx_mdio_check_mmds(struct efx_nic *efx, - unsigned int mmd_mask, unsigned int fatal_mask) +int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask) { int mmd = 0, probe_mmd, devs1, devs2; u32 devices; @@ -161,13 +143,9 @@ int efx_mdio_check_mmds(struct efx_nic *efx, /* Check all required MMDs are responding and happy. */ while (mmd_mask) { - if (mmd_mask & 1) { - int fault_fatal = fatal_mask & 1; - if (efx_mdio_check_mmd(efx, mmd, fault_fatal)) - return -EIO; - } + if ((mmd_mask & 1) && efx_mdio_check_mmd(efx, mmd)) + return -EIO; mmd_mask = mmd_mask >> 1; - fatal_mask = fatal_mask >> 1; mmd++; } @@ -337,7 +315,7 @@ int efx_mdio_test_alive(struct efx_nic *efx) "no MDIO PHY present with ID %d\n", efx->mdio.prtad); rc = -EINVAL; } else { - rc = efx_mdio_check_mmds(efx, efx->mdio.mmds, 0); + rc = efx_mdio_check_mmds(efx, efx->mdio.mmds); } mutex_unlock(&efx->mac_lock); diff --git a/drivers/net/sfc/mdio_10g.h b/drivers/net/sfc/mdio_10g.h index 75791d3d496..df0703940c8 100644 --- a/drivers/net/sfc/mdio_10g.h +++ b/drivers/net/sfc/mdio_10g.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -68,8 +68,7 @@ extern int efx_mdio_reset_mmd(struct efx_nic *efx, int mmd, int spins, int spintime); /* As efx_mdio_check_mmd but for multiple MMDs */ -int efx_mdio_check_mmds(struct efx_nic *efx, - unsigned int mmd_mask, unsigned int fatal_mask); +int efx_mdio_check_mmds(struct efx_nic *efx, unsigned int mmd_mask); /* Check the link status of specified mmds in bit mask */ extern bool efx_mdio_links_ok(struct efx_nic *efx, unsigned int mmd_mask); diff --git a/drivers/net/sfc/mtd.c b/drivers/net/sfc/mtd.c index d38627448c2..e646bfce2d8 100644 --- a/drivers/net/sfc/mtd.c +++ b/drivers/net/sfc/mtd.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/net_driver.h b/drivers/net/sfc/net_driver.h index 15b9068e5b8..215d5c51bfa 100644 --- a/drivers/net/sfc/net_driver.h +++ b/drivers/net/sfc/net_driver.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2009 Solarflare Communications Inc. + * Copyright 2005-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -41,7 +41,7 @@ * **************************************************************************/ -#define EFX_DRIVER_VERSION "3.0" +#define EFX_DRIVER_VERSION "3.1" #ifdef EFX_ENABLE_DEBUG #define EFX_BUG_ON_PARANOID(x) BUG_ON(x) @@ -214,15 +214,17 @@ struct efx_tx_queue { * If both this and page are %NULL, the buffer slot is currently free. * @page: The associated page buffer, if any. * If both this and skb are %NULL, the buffer slot is currently free. - * @data: Pointer to ethernet header * @len: Buffer length, in bytes. + * @is_page: Indicates if @page is valid. If false, @skb is valid. */ struct efx_rx_buffer { dma_addr_t dma_addr; - struct sk_buff *skb; - struct page *page; - char *data; + union { + struct sk_buff *skb; + struct page *page; + } u; unsigned int len; + bool is_page; }; /** diff --git a/drivers/net/sfc/nic.c b/drivers/net/sfc/nic.c index 1d0b8b6f25c..e8396614daf 100644 --- a/drivers/net/sfc/nic.c +++ b/drivers/net/sfc/nic.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -41,26 +41,6 @@ #define RX_DC_ENTRIES 64 #define RX_DC_ENTRIES_ORDER 3 -/* RX FIFO XOFF watermark - * - * When the amount of the RX FIFO increases used increases past this - * watermark send XOFF. Only used if RX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -int efx_nic_rx_xoff_thresh = -1; -module_param_named(rx_xoff_thresh_bytes, efx_nic_rx_xoff_thresh, int, 0644); -MODULE_PARM_DESC(rx_xoff_thresh_bytes, "RX fifo XOFF threshold"); - -/* RX FIFO XON watermark - * - * When the amount of the RX FIFO used decreases below this - * watermark send XON. Only used if TX flow control is enabled (ethtool -A) - * This also has an effect on RX/TX arbitration - */ -int efx_nic_rx_xon_thresh = -1; -module_param_named(rx_xon_thresh_bytes, efx_nic_rx_xon_thresh, int, 0644); -MODULE_PARM_DESC(rx_xon_thresh_bytes, "RX fifo XON threshold"); - /* If EFX_MAX_INT_ERRORS internal errors occur within * EFX_INT_ERROR_EXPIRE seconds, we consider the NIC broken and * disable it. diff --git a/drivers/net/sfc/nic.h b/drivers/net/sfc/nic.h index eb0586925b5..d9de1b647d4 100644 --- a/drivers/net/sfc/nic.h +++ b/drivers/net/sfc/nic.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -142,20 +142,14 @@ static inline struct falcon_board *falcon_board(struct efx_nic *efx) /** * struct siena_nic_data - Siena NIC state - * @fw_version: Management controller firmware version - * @fw_build: Firmware build number * @mcdi: Management-Controller-to-Driver Interface * @wol_filter_id: Wake-on-LAN packet filter id */ struct siena_nic_data { - u64 fw_version; - u32 fw_build; struct efx_mcdi_iface mcdi; int wol_filter_id; }; -extern void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len); - extern struct efx_nic_type falcon_a1_nic_type; extern struct efx_nic_type falcon_b0_nic_type; extern struct efx_nic_type siena_a0_nic_type; @@ -194,7 +188,6 @@ extern void efx_nic_eventq_read_ack(struct efx_channel *channel); /* MAC/PHY */ extern void falcon_drain_tx_fifo(struct efx_nic *efx); extern void falcon_reconfigure_mac_wrapper(struct efx_nic *efx); -extern int efx_nic_rx_xoff_thresh, efx_nic_rx_xon_thresh; /* Interrupts and test events */ extern int efx_nic_init_interrupt(struct efx_nic *efx); diff --git a/drivers/net/sfc/phy.h b/drivers/net/sfc/phy.h index 1dab609757f..b3b79472421 100644 --- a/drivers/net/sfc/phy.h +++ b/drivers/net/sfc/phy.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2009 Solarflare Communications Inc. + * Copyright 2007-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/qt202x_phy.c b/drivers/net/sfc/qt202x_phy.c index ea3ae008931..55f90924247 100644 --- a/drivers/net/sfc/qt202x_phy.c +++ b/drivers/net/sfc/qt202x_phy.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/regs.h b/drivers/net/sfc/regs.h index 8227de62014..cc2c86b76a7 100644 --- a/drivers/net/sfc/regs.h +++ b/drivers/net/sfc/regs.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/rx.c b/drivers/net/sfc/rx.c index 3925fd62117..c0fdb59030f 100644 --- a/drivers/net/sfc/rx.c +++ b/drivers/net/sfc/rx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2009 Solarflare Communications Inc. + * Copyright 2005-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -89,24 +89,37 @@ static unsigned int rx_refill_limit = 95; */ #define EFX_RXD_HEAD_ROOM 2 -static inline unsigned int efx_rx_buf_offset(struct efx_rx_buffer *buf) +/* Offset of ethernet header within page */ +static inline unsigned int efx_rx_buf_offset(struct efx_nic *efx, + struct efx_rx_buffer *buf) { /* Offset is always within one page, so we don't need to consider * the page order. */ - return (__force unsigned long) buf->data & (PAGE_SIZE - 1); + return (((__force unsigned long) buf->dma_addr & (PAGE_SIZE - 1)) + + efx->type->rx_buffer_hash_size); } static inline unsigned int efx_rx_buf_size(struct efx_nic *efx) { return PAGE_SIZE << efx->rx_buffer_order; } -static inline u32 efx_rx_buf_hash(struct efx_rx_buffer *buf) +static u8 *efx_rx_buf_eh(struct efx_nic *efx, struct efx_rx_buffer *buf) { + if (buf->is_page) + return page_address(buf->u.page) + efx_rx_buf_offset(efx, buf); + else + return ((u8 *)buf->u.skb->data + + efx->type->rx_buffer_hash_size); +} + +static inline u32 efx_rx_buf_hash(const u8 *eh) +{ + /* The ethernet header is always directly after any hash. */ #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) || NET_IP_ALIGN % 4 == 0 - return __le32_to_cpup((const __le32 *)(buf->data - 4)); + return __le32_to_cpup((const __le32 *)(eh - 4)); #else - const u8 *data = (const u8 *)(buf->data - 4); + const u8 *data = eh - 4; return ((u32)data[0] | (u32)data[1] << 8 | (u32)data[2] << 16 | @@ -129,6 +142,7 @@ static int efx_init_rx_buffers_skb(struct efx_rx_queue *rx_queue) struct efx_nic *efx = rx_queue->efx; struct net_device *net_dev = efx->net_dev; struct efx_rx_buffer *rx_buf; + struct sk_buff *skb; int skb_len = efx->rx_buffer_len; unsigned index, count; @@ -136,24 +150,23 @@ static int efx_init_rx_buffers_skb(struct efx_rx_queue *rx_queue) index = rx_queue->added_count & rx_queue->ptr_mask; rx_buf = efx_rx_buffer(rx_queue, index); - rx_buf->skb = netdev_alloc_skb(net_dev, skb_len); - if (unlikely(!rx_buf->skb)) + rx_buf->u.skb = skb = netdev_alloc_skb(net_dev, skb_len); + if (unlikely(!skb)) return -ENOMEM; - rx_buf->page = NULL; /* Adjust the SKB for padding and checksum */ - skb_reserve(rx_buf->skb, NET_IP_ALIGN); + skb_reserve(skb, NET_IP_ALIGN); rx_buf->len = skb_len - NET_IP_ALIGN; - rx_buf->data = (char *)rx_buf->skb->data; - rx_buf->skb->ip_summed = CHECKSUM_UNNECESSARY; + rx_buf->is_page = false; + skb->ip_summed = CHECKSUM_UNNECESSARY; rx_buf->dma_addr = pci_map_single(efx->pci_dev, - rx_buf->data, rx_buf->len, + skb->data, rx_buf->len, PCI_DMA_FROMDEVICE); if (unlikely(pci_dma_mapping_error(efx->pci_dev, rx_buf->dma_addr))) { - dev_kfree_skb_any(rx_buf->skb); - rx_buf->skb = NULL; + dev_kfree_skb_any(skb); + rx_buf->u.skb = NULL; return -EIO; } @@ -211,10 +224,9 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue) index = rx_queue->added_count & rx_queue->ptr_mask; rx_buf = efx_rx_buffer(rx_queue, index); rx_buf->dma_addr = dma_addr + EFX_PAGE_IP_ALIGN; - rx_buf->skb = NULL; - rx_buf->page = page; - rx_buf->data = page_addr + EFX_PAGE_IP_ALIGN; + rx_buf->u.page = page; rx_buf->len = efx->rx_buffer_len - EFX_PAGE_IP_ALIGN; + rx_buf->is_page = true; ++rx_queue->added_count; ++rx_queue->alloc_page_count; ++state->refcnt; @@ -235,19 +247,17 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue) static void efx_unmap_rx_buffer(struct efx_nic *efx, struct efx_rx_buffer *rx_buf) { - if (rx_buf->page) { + if (rx_buf->is_page && rx_buf->u.page) { struct efx_rx_page_state *state; - EFX_BUG_ON_PARANOID(rx_buf->skb); - - state = page_address(rx_buf->page); + state = page_address(rx_buf->u.page); if (--state->refcnt == 0) { pci_unmap_page(efx->pci_dev, state->dma_addr, efx_rx_buf_size(efx), PCI_DMA_FROMDEVICE); } - } else if (likely(rx_buf->skb)) { + } else if (!rx_buf->is_page && rx_buf->u.skb) { pci_unmap_single(efx->pci_dev, rx_buf->dma_addr, rx_buf->len, PCI_DMA_FROMDEVICE); } @@ -256,12 +266,12 @@ static void efx_unmap_rx_buffer(struct efx_nic *efx, static void efx_free_rx_buffer(struct efx_nic *efx, struct efx_rx_buffer *rx_buf) { - if (rx_buf->page) { - __free_pages(rx_buf->page, efx->rx_buffer_order); - rx_buf->page = NULL; - } else if (likely(rx_buf->skb)) { - dev_kfree_skb_any(rx_buf->skb); - rx_buf->skb = NULL; + if (rx_buf->is_page && rx_buf->u.page) { + __free_pages(rx_buf->u.page, efx->rx_buffer_order); + rx_buf->u.page = NULL; + } else if (!rx_buf->is_page && rx_buf->u.skb) { + dev_kfree_skb_any(rx_buf->u.skb); + rx_buf->u.skb = NULL; } } @@ -277,7 +287,7 @@ static void efx_fini_rx_buffer(struct efx_rx_queue *rx_queue, static void efx_resurrect_rx_buffer(struct efx_rx_queue *rx_queue, struct efx_rx_buffer *rx_buf) { - struct efx_rx_page_state *state = page_address(rx_buf->page); + struct efx_rx_page_state *state = page_address(rx_buf->u.page); struct efx_rx_buffer *new_buf; unsigned fill_level, index; @@ -292,16 +302,14 @@ static void efx_resurrect_rx_buffer(struct efx_rx_queue *rx_queue, } ++state->refcnt; - get_page(rx_buf->page); + get_page(rx_buf->u.page); index = rx_queue->added_count & rx_queue->ptr_mask; new_buf = efx_rx_buffer(rx_queue, index); new_buf->dma_addr = rx_buf->dma_addr ^ (PAGE_SIZE >> 1); - new_buf->skb = NULL; - new_buf->page = rx_buf->page; - new_buf->data = (void *) - ((__force unsigned long)rx_buf->data ^ (PAGE_SIZE >> 1)); + new_buf->u.page = rx_buf->u.page; new_buf->len = rx_buf->len; + new_buf->is_page = true; ++rx_queue->added_count; } @@ -315,16 +323,15 @@ static void efx_recycle_rx_buffer(struct efx_channel *channel, struct efx_rx_buffer *new_buf; unsigned index; - if (rx_buf->page != NULL && efx->rx_buffer_len <= EFX_RX_HALF_PAGE && - page_count(rx_buf->page) == 1) + if (rx_buf->is_page && efx->rx_buffer_len <= EFX_RX_HALF_PAGE && + page_count(rx_buf->u.page) == 1) efx_resurrect_rx_buffer(rx_queue, rx_buf); index = rx_queue->added_count & rx_queue->ptr_mask; new_buf = efx_rx_buffer(rx_queue, index); memcpy(new_buf, rx_buf, sizeof(*new_buf)); - rx_buf->page = NULL; - rx_buf->skb = NULL; + rx_buf->u.page = NULL; ++rx_queue->added_count; } @@ -428,7 +435,7 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, * data at the end of the skb will be trashed. So * we have no choice but to leak the fragment. */ - *leak_packet = (rx_buf->skb != NULL); + *leak_packet = !rx_buf->is_page; efx_schedule_reset(efx, RESET_TYPE_RX_RECOVERY); } else { if (net_ratelimit()) @@ -448,19 +455,18 @@ static void efx_rx_packet__check_len(struct efx_rx_queue *rx_queue, */ static void efx_rx_packet_gro(struct efx_channel *channel, struct efx_rx_buffer *rx_buf, - bool checksummed) + const u8 *eh, bool checksummed) { struct napi_struct *napi = &channel->napi_str; gro_result_t gro_result; /* Pass the skb/page into the GRO engine */ - if (rx_buf->page) { + if (rx_buf->is_page) { struct efx_nic *efx = channel->efx; - struct page *page = rx_buf->page; + struct page *page = rx_buf->u.page; struct sk_buff *skb; - EFX_BUG_ON_PARANOID(rx_buf->skb); - rx_buf->page = NULL; + rx_buf->u.page = NULL; skb = napi_get_frags(napi); if (!skb) { @@ -469,11 +475,11 @@ static void efx_rx_packet_gro(struct efx_channel *channel, } if (efx->net_dev->features & NETIF_F_RXHASH) - skb->rxhash = efx_rx_buf_hash(rx_buf); + skb->rxhash = efx_rx_buf_hash(eh); skb_shinfo(skb)->frags[0].page = page; skb_shinfo(skb)->frags[0].page_offset = - efx_rx_buf_offset(rx_buf); + efx_rx_buf_offset(efx, rx_buf); skb_shinfo(skb)->frags[0].size = rx_buf->len; skb_shinfo(skb)->nr_frags = 1; @@ -487,11 +493,10 @@ static void efx_rx_packet_gro(struct efx_channel *channel, gro_result = napi_gro_frags(napi); } else { - struct sk_buff *skb = rx_buf->skb; + struct sk_buff *skb = rx_buf->u.skb; - EFX_BUG_ON_PARANOID(!skb); EFX_BUG_ON_PARANOID(!checksummed); - rx_buf->skb = NULL; + rx_buf->u.skb = NULL; gro_result = napi_gro_receive(napi, skb); } @@ -513,9 +518,6 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, bool leak_packet = false; rx_buf = efx_rx_buffer(rx_queue, index); - EFX_BUG_ON_PARANOID(!rx_buf->data); - EFX_BUG_ON_PARANOID(rx_buf->skb && rx_buf->page); - EFX_BUG_ON_PARANOID(!(rx_buf->skb || rx_buf->page)); /* This allows the refill path to post another buffer. * EFX_RXD_HEAD_ROOM ensures that the slot we are using @@ -554,12 +556,12 @@ void efx_rx_packet(struct efx_rx_queue *rx_queue, unsigned int index, /* Prefetch nice and early so data will (hopefully) be in cache by * the time we look at it. */ - prefetch(rx_buf->data); + prefetch(efx_rx_buf_eh(efx, rx_buf)); /* Pipeline receives so that we give time for packet headers to be * prefetched into cache. */ - rx_buf->len = len; + rx_buf->len = len - efx->type->rx_buffer_hash_size; out: if (channel->rx_pkt) __efx_rx_packet(channel, @@ -574,45 +576,43 @@ void __efx_rx_packet(struct efx_channel *channel, { struct efx_nic *efx = channel->efx; struct sk_buff *skb; - - rx_buf->data += efx->type->rx_buffer_hash_size; - rx_buf->len -= efx->type->rx_buffer_hash_size; + u8 *eh = efx_rx_buf_eh(efx, rx_buf); /* If we're in loopback test, then pass the packet directly to the * loopback layer, and free the rx_buf here */ if (unlikely(efx->loopback_selftest)) { - efx_loopback_rx_packet(efx, rx_buf->data, rx_buf->len); + efx_loopback_rx_packet(efx, eh, rx_buf->len); efx_free_rx_buffer(efx, rx_buf); return; } - if (rx_buf->skb) { - prefetch(skb_shinfo(rx_buf->skb)); + if (!rx_buf->is_page) { + skb = rx_buf->u.skb; - skb_reserve(rx_buf->skb, efx->type->rx_buffer_hash_size); - skb_put(rx_buf->skb, rx_buf->len); + prefetch(skb_shinfo(skb)); + + skb_reserve(skb, efx->type->rx_buffer_hash_size); + skb_put(skb, rx_buf->len); if (efx->net_dev->features & NETIF_F_RXHASH) - rx_buf->skb->rxhash = efx_rx_buf_hash(rx_buf); + skb->rxhash = efx_rx_buf_hash(eh); /* Move past the ethernet header. rx_buf->data still points * at the ethernet header */ - rx_buf->skb->protocol = eth_type_trans(rx_buf->skb, - efx->net_dev); + skb->protocol = eth_type_trans(skb, efx->net_dev); - skb_record_rx_queue(rx_buf->skb, channel->channel); + skb_record_rx_queue(skb, channel->channel); } - if (likely(checksummed || rx_buf->page)) { - efx_rx_packet_gro(channel, rx_buf, checksummed); + if (likely(checksummed || rx_buf->is_page)) { + efx_rx_packet_gro(channel, rx_buf, eh, checksummed); return; } /* We now own the SKB */ - skb = rx_buf->skb; - rx_buf->skb = NULL; - EFX_BUG_ON_PARANOID(!skb); + skb = rx_buf->u.skb; + rx_buf->u.skb = NULL; /* Set the SKB flags */ skb_checksum_none_assert(skb); diff --git a/drivers/net/sfc/selftest.c b/drivers/net/sfc/selftest.c index f936892aa42..a0f49b348d6 100644 --- a/drivers/net/sfc/selftest.c +++ b/drivers/net/sfc/selftest.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/selftest.h b/drivers/net/sfc/selftest.h index aed495a4dad..dba5456e70f 100644 --- a/drivers/net/sfc/selftest.h +++ b/drivers/net/sfc/selftest.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2008 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/siena.c b/drivers/net/sfc/siena.c index bf845617644..e4dd8986b1f 100644 --- a/drivers/net/sfc/siena.c +++ b/drivers/net/sfc/siena.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -227,13 +227,6 @@ static int siena_probe_nic(struct efx_nic *efx) if (rc) goto fail1; - rc = efx_mcdi_fwver(efx, &nic_data->fw_version, &nic_data->fw_build); - if (rc) { - netif_err(efx, probe, efx->net_dev, - "Failed to read MCPU firmware version - rc %d\n", rc); - goto fail1; /* MCPU absent? */ - } - /* Let the BMC know that the driver is now in charge of link and * filter settings. We must do this before we reset the NIC */ rc = efx_mcdi_drv_attach(efx, true, &already_attached); @@ -348,11 +341,6 @@ static int siena_init_nic(struct efx_nic *efx) FRF_CZ_RX_RSS_IPV6_TKEY_HI_WIDTH / 8); efx_writeo(efx, &temp, FR_CZ_RX_RSS_IPV6_REG3); - if (efx_nic_rx_xoff_thresh >= 0 || efx_nic_rx_xon_thresh >= 0) - /* No MCDI operation has been defined to set thresholds */ - netif_err(efx, hw, efx->net_dev, - "ignoring RX flow control thresholds\n"); - /* Enable event logging */ rc = efx_mcdi_log_ctrl(efx, true, false, 0); if (rc) @@ -514,16 +502,6 @@ static void siena_stop_nic_stats(struct efx_nic *efx) efx_mcdi_mac_stats(efx, efx->stats_buffer.dma_addr, 0, 0, 0); } -void siena_print_fwver(struct efx_nic *efx, char *buf, size_t len) -{ - struct siena_nic_data *nic_data = efx->nic_data; - snprintf(buf, len, "%u.%u.%u.%u", - (unsigned int)(nic_data->fw_version >> 48), - (unsigned int)(nic_data->fw_version >> 32 & 0xffff), - (unsigned int)(nic_data->fw_version >> 16 & 0xffff), - (unsigned int)(nic_data->fw_version & 0xffff)); -} - /************************************************************************** * * Wake on LAN diff --git a/drivers/net/sfc/spi.h b/drivers/net/sfc/spi.h index 879b7f6bde3..71f2e3ebe1c 100644 --- a/drivers/net/sfc/spi.h +++ b/drivers/net/sfc/spi.h @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005 Fen Systems Ltd. - * Copyright 2006 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/tenxpress.c b/drivers/net/sfc/tenxpress.c index f102912eba9..efdceb35aaa 100644 --- a/drivers/net/sfc/tenxpress.c +++ b/drivers/net/sfc/tenxpress.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2007-2009 Solarflare Communications Inc. + * Copyright 2007-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -196,7 +196,7 @@ static int tenxpress_phy_init(struct efx_nic *efx) if (rc < 0) return rc; - rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS, 0); + rc = efx_mdio_check_mmds(efx, TENXPRESS_REQUIRED_DEVS); if (rc < 0) return rc; } diff --git a/drivers/net/sfc/tx.c b/drivers/net/sfc/tx.c index 1a51653bb92..13980190821 100644 --- a/drivers/net/sfc/tx.c +++ b/drivers/net/sfc/tx.c @@ -1,7 +1,7 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards * Copyright 2005-2006 Fen Systems Ltd. - * Copyright 2005-2009 Solarflare Communications Inc. + * Copyright 2005-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/sfc/txc43128_phy.c b/drivers/net/sfc/txc43128_phy.c index 351794a7921..d9886addcc9 100644 --- a/drivers/net/sfc/txc43128_phy.c +++ b/drivers/net/sfc/txc43128_phy.c @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2010 Solarflare Communications Inc. + * Copyright 2006-2011 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published @@ -193,7 +193,7 @@ static int txc_reset_phy(struct efx_nic *efx) goto fail; /* Check that all the MMDs we expect are present and responding. */ - rc = efx_mdio_check_mmds(efx, TXC_REQUIRED_DEVS, 0); + rc = efx_mdio_check_mmds(efx, TXC_REQUIRED_DEVS); if (rc < 0) goto fail; diff --git a/drivers/net/sfc/workarounds.h b/drivers/net/sfc/workarounds.h index e0d63083c3a..e4dd3a7f304 100644 --- a/drivers/net/sfc/workarounds.h +++ b/drivers/net/sfc/workarounds.h @@ -1,6 +1,6 @@ /**************************************************************************** * Driver for Solarflare Solarstorm network controllers and boards - * Copyright 2006-2009 Solarflare Communications Inc. + * Copyright 2006-2010 Solarflare Communications Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published diff --git a/drivers/net/skge.c b/drivers/net/skge.c index 42daf98ba73..35b28f42d20 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -3856,9 +3856,6 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port*8, ETH_ALEN); memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); - /* device is off until link detection */ - netif_carrier_off(dev); - return dev; } diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index e48a8088534..ace6404e2fa 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -25,153 +25,10 @@ * Microchip Technology, 24C01A/02A/04A Data Sheet * available in PDF format from www.microchip.com * - * Change History - * - * Tigran Aivazian <tigran@sco.com>: TLan_PciProbe() now uses - * new PCI BIOS interface. - * Alan Cox <alan@lxorguk.ukuu.org.uk>: - * Fixed the out of memory - * handling. - * - * Torben Mathiasen <torben.mathiasen@compaq.com> New Maintainer! - * - * v1.1 Dec 20, 1999 - Removed linux version checking - * Patch from Tigran Aivazian. - * - v1.1 includes Alan's SMP updates. - * - We still have problems on SMP though, - * but I'm looking into that. - * - * v1.2 Jan 02, 2000 - Hopefully fixed the SMP deadlock. - * - Removed dependency of HZ being 100. - * - We now allow higher priority timers to - * overwrite timers like TLAN_TIMER_ACTIVITY - * Patch from John Cagle <john.cagle@compaq.com>. - * - Fixed a few compiler warnings. - * - * v1.3 Feb 04, 2000 - Fixed the remaining HZ issues. - * - Removed call to pci_present(). - * - Removed SA_INTERRUPT flag from irq handler. - * - Added __init and __initdata to reduce resisdent - * code size. - * - Driver now uses module_init/module_exit. - * - Rewrote init_module and tlan_probe to - * share a lot more code. We now use tlan_probe - * with builtin and module driver. - * - Driver ported to new net API. - * - tlan.txt has been reworked to reflect current - * driver (almost) - * - Other minor stuff - * - * v1.4 Feb 10, 2000 - Updated with more changes required after Dave's - * network cleanup in 2.3.43pre7 (Tigran & myself) - * - Minor stuff. - * - * v1.5 March 22, 2000 - Fixed another timer bug that would hang the - * driver if no cable/link were present. - * - Cosmetic changes. - * - TODO: Port completely to new PCI/DMA API - * Auto-Neg fallback. - * - * v1.6 April 04, 2000 - Fixed driver support for kernel-parameters. - * Haven't tested it though, as the kernel support - * is currently broken (2.3.99p4p3). - * - Updated tlan.txt accordingly. - * - Adjusted minimum/maximum frame length. - * - There is now a TLAN website up at - * http://hp.sourceforge.net/ - * - * v1.7 April 07, 2000 - Started to implement custom ioctls. Driver now - * reports PHY information when used with Donald - * Beckers userspace MII diagnostics utility. - * - * v1.8 April 23, 2000 - Fixed support for forced speed/duplex settings. - * - Added link information to Auto-Neg and forced - * modes. When NIC operates with auto-neg the driver - * will report Link speed & duplex modes as well as - * link partner abilities. When forced link is used, - * the driver will report status of the established - * link. - * Please read tlan.txt for additional information. - * - Removed call to check_region(), and used - * return value of request_region() instead. - * - * v1.8a May 28, 2000 - Minor updates. - * - * v1.9 July 25, 2000 - Fixed a few remaining Full-Duplex issues. - * - Updated with timer fixes from Andrew Morton. - * - Fixed module race in TLan_Open. - * - Added routine to monitor PHY status. - * - Added activity led support for Proliant devices. - * - * v1.10 Aug 30, 2000 - Added support for EISA based tlan controllers - * like the Compaq NetFlex3/E. - * - Rewrote tlan_probe to better handle multiple - * bus probes. Probing and device setup is now - * done through TLan_Probe and TLan_init_one. Actual - * hardware probe is done with kernel API and - * TLan_EisaProbe. - * - Adjusted debug information for probing. - * - Fixed bug that would cause general debug - * information to be printed after driver removal. - * - Added transmit timeout handling. - * - Fixed OOM return values in tlan_probe. - * - Fixed possible mem leak in tlan_exit - * (now tlan_remove_one). - * - Fixed timer bug in TLan_phyMonitor. - * - This driver version is alpha quality, please - * send me any bug issues you may encounter. - * - * v1.11 Aug 31, 2000 - Do not try to register irq 0 if no irq line was - * set for EISA cards. - * - Added support for NetFlex3/E with nibble-rate - * 10Base-T PHY. This is untestet as I haven't got - * one of these cards. - * - Fixed timer being added twice. - * - Disabled PhyMonitoring by default as this is - * work in progress. Define MONITOR to enable it. - * - Now we don't display link info with PHYs that - * doesn't support it (level1). - * - Incresed tx_timeout beacuse of auto-neg. - * - Adjusted timers for forced speeds. - * - * v1.12 Oct 12, 2000 - Minor fixes (memleak, init, etc.) - * - * v1.13 Nov 28, 2000 - Stop flooding console with auto-neg issues - * when link can't be established. - * - Added the bbuf option as a kernel parameter. - * - Fixed ioaddr probe bug. - * - Fixed stupid deadlock with MII interrupts. - * - Added support for speed/duplex selection with - * multiple nics. - * - Added partly fix for TX Channel lockup with - * TLAN v1.0 silicon. This needs to be investigated - * further. - * - * v1.14 Dec 16, 2000 - Added support for servicing multiple frames per. - * interrupt. Thanks goes to - * Adam Keys <adam@ti.com> - * Denis Beaudoin <dbeaudoin@ti.com> - * for providing the patch. - * - Fixed auto-neg output when using multiple - * adapters. - * - Converted to use new taskq interface. - * - * v1.14a Jan 6, 2001 - Minor adjustments (spinlocks, etc.) - * - * Samuel Chessman <chessman@tux.org> New Maintainer! - * - * v1.15 Apr 4, 2002 - Correct operation when aui=1 to be - * 10T half duplex no loopback - * Thanks to Gunnar Eikman - * - * Sakari Ailus <sakari.ailus@iki.fi>: - * - * v1.15a Dec 15 2008 - Remove bbuf support, it doesn't work anyway. - * v1.16 Jan 6 2011 - Make checkpatch.pl happy. - * v1.17 Jan 6 2011 - Add suspend/resume support. - * ******************************************************************************/ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/init.h> #include <linux/ioport.h> @@ -204,7 +61,7 @@ module_param_array(speed, int, NULL, 0); MODULE_PARM_DESC(aui, "ThunderLAN use AUI port(s) (0-1)"); MODULE_PARM_DESC(duplex, "ThunderLAN duplex setting(s) (0-default, 1-half, 2-full)"); -MODULE_PARM_DESC(speed, "ThunderLAN port speen setting(s) (0,10,100)"); +MODULE_PARM_DESC(speed, "ThunderLAN port speed setting(s) (0,10,100)"); MODULE_AUTHOR("Maintainer: Samuel Chessman <chessman@tux.org>"); MODULE_DESCRIPTION("Driver for TI ThunderLAN based ethernet PCI adapters"); @@ -542,7 +399,7 @@ static int __init tlan_probe(void) { int rc = -ENODEV; - printk(KERN_INFO "%s", tlan_banner); + pr_info("%s", tlan_banner); TLAN_DBG(TLAN_DEBUG_PROBE, "Starting PCI Probe....\n"); @@ -551,16 +408,16 @@ static int __init tlan_probe(void) rc = pci_register_driver(&tlan_driver); if (rc != 0) { - printk(KERN_ERR "TLAN: Could not register pci driver.\n"); + pr_err("Could not register pci driver\n"); goto err_out_pci_free; } TLAN_DBG(TLAN_DEBUG_PROBE, "Starting EISA Probe....\n"); tlan_eisa_probe(); - printk(KERN_INFO "TLAN: %d device%s installed, PCI: %d EISA: %d\n", - tlan_devices_installed, tlan_devices_installed == 1 ? "" : "s", - tlan_have_pci, tlan_have_eisa); + pr_info("%d device%s installed, PCI: %d EISA: %d\n", + tlan_devices_installed, tlan_devices_installed == 1 ? "" : "s", + tlan_have_pci, tlan_have_eisa); if (tlan_devices_installed == 0) { rc = -ENODEV; @@ -619,7 +476,7 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, rc = pci_request_regions(pdev, tlan_signature); if (rc) { - printk(KERN_ERR "TLAN: Could not reserve IO regions\n"); + pr_err("Could not reserve IO regions\n"); goto err_out; } } @@ -627,7 +484,7 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, dev = alloc_etherdev(sizeof(struct tlan_priv)); if (dev == NULL) { - printk(KERN_ERR "TLAN: Could not allocate memory for device.\n"); + pr_err("Could not allocate memory for device\n"); rc = -ENOMEM; goto err_out_regions; } @@ -646,8 +503,7 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); if (rc) { - printk(KERN_ERR - "TLAN: No suitable PCI mapping available.\n"); + pr_err("No suitable PCI mapping available\n"); goto err_out_free_dev; } @@ -661,7 +517,7 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, } } if (!pci_io_base) { - printk(KERN_ERR "TLAN: No IO mappings available\n"); + pr_err("No IO mappings available\n"); rc = -EIO; goto err_out_free_dev; } @@ -717,13 +573,13 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, rc = tlan_init(dev); if (rc) { - printk(KERN_ERR "TLAN: Could not set up device.\n"); + pr_err("Could not set up device\n"); goto err_out_free_dev; } rc = register_netdev(dev); if (rc) { - printk(KERN_ERR "TLAN: Could not register device.\n"); + pr_err("Could not register device\n"); goto err_out_uninit; } @@ -740,12 +596,11 @@ static int __devinit tlan_probe1(struct pci_dev *pdev, tlan_have_eisa++; } - printk(KERN_INFO "TLAN: %s irq=%2d, io=%04x, %s, Rev. %d\n", - dev->name, - (int) dev->irq, - (int) dev->base_addr, - priv->adapter->device_label, - priv->adapter_rev); + netdev_info(dev, "irq=%2d, io=%04x, %s, Rev. %d\n", + (int)dev->irq, + (int)dev->base_addr, + priv->adapter->device_label, + priv->adapter_rev); return 0; err_out_uninit: @@ -861,7 +716,7 @@ static void __init tlan_eisa_probe(void) } if (debug == 0x10) - printk(KERN_INFO "Found one\n"); + pr_info("Found one\n"); /* Get irq from board */ @@ -890,12 +745,12 @@ static void __init tlan_eisa_probe(void) out: if (debug == 0x10) - printk(KERN_INFO "None found\n"); + pr_info("None found\n"); continue; out2: if (debug == 0x10) - printk(KERN_INFO "Card found but it is not enabled, skipping\n"); + pr_info("Card found but it is not enabled, skipping\n"); continue; } @@ -963,8 +818,7 @@ static int tlan_init(struct net_device *dev) priv->dma_size = dma_size; if (priv->dma_storage == NULL) { - printk(KERN_ERR - "TLAN: Could not allocate lists and buffers for %s.\n", + pr_err("Could not allocate lists and buffers for %s\n", dev->name); return -ENOMEM; } @@ -982,9 +836,8 @@ static int tlan_init(struct net_device *dev) (u8) priv->adapter->addr_ofs + i, (u8 *) &dev->dev_addr[i]); if (err) { - printk(KERN_ERR "TLAN: %s: Error reading MAC from eeprom: %d\n", - dev->name, - err); + pr_err("%s: Error reading MAC from eeprom: %d\n", + dev->name, err); } dev->addr_len = 6; @@ -1028,8 +881,8 @@ static int tlan_open(struct net_device *dev) dev->name, dev); if (err) { - pr_err("TLAN: Cannot open %s because IRQ %d is already in use.\n", - dev->name, dev->irq); + netdev_err(dev, "Cannot open because IRQ %d is already in use\n", + dev->irq); return err; } @@ -1512,8 +1365,8 @@ static u32 tlan_handle_tx_eof(struct net_device *dev, u16 host_int) } if (!ack) - printk(KERN_INFO - "TLAN: Received interrupt for uncompleted TX frame.\n"); + netdev_info(dev, + "Received interrupt for uncompleted TX frame\n"); if (eoc) { TLAN_DBG(TLAN_DEBUG_TX, @@ -1667,8 +1520,8 @@ drop_and_reuse: } if (!ack) - printk(KERN_INFO - "TLAN: Received interrupt for uncompleted RX frame.\n"); + netdev_info(dev, + "Received interrupt for uncompleted RX frame\n"); if (eoc) { @@ -1724,7 +1577,7 @@ drop_and_reuse: static u32 tlan_handle_dummy(struct net_device *dev, u16 host_int) { - pr_info("TLAN: Test interrupt on %s.\n", dev->name); + netdev_info(dev, "Test interrupt\n"); return 1; } @@ -1818,7 +1671,7 @@ static u32 tlan_handle_status_check(struct net_device *dev, u16 host_int) if (host_int & TLAN_HI_IV_MASK) { netif_stop_queue(dev); error = inl(dev->base_addr + TLAN_CH_PARM); - pr_info("TLAN: %s: Adaptor Error = 0x%x\n", dev->name, error); + netdev_info(dev, "Adaptor Error = 0x%x\n", error); tlan_read_and_clear_stats(dev, TLAN_RECORD); outl(TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD); @@ -2059,7 +1912,7 @@ static void tlan_reset_lists(struct net_device *dev) list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER; skb = netdev_alloc_skb_ip_align(dev, TLAN_MAX_FRAME_SIZE + 5); if (!skb) { - pr_err("TLAN: out of memory for received data.\n"); + netdev_err(dev, "Out of memory for received data\n"); break; } @@ -2143,13 +1996,13 @@ static void tlan_print_dio(u16 io_base) u32 data0, data1; int i; - pr_info("TLAN: Contents of internal registers for io base 0x%04hx.\n", - io_base); - pr_info("TLAN: Off. +0 +4\n"); + pr_info("Contents of internal registers for io base 0x%04hx\n", + io_base); + pr_info("Off. +0 +4\n"); for (i = 0; i < 0x4C; i += 8) { data0 = tlan_dio_read32(io_base, i); data1 = tlan_dio_read32(io_base, i + 0x4); - pr_info("TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1); + pr_info("0x%02x 0x%08x 0x%08x\n", i, data0, data1); } } @@ -2178,14 +2031,14 @@ static void tlan_print_list(struct tlan_list *list, char *type, int num) { int i; - pr_info("TLAN: %s List %d at %p\n", type, num, list); - pr_info("TLAN: Forward = 0x%08x\n", list->forward); - pr_info("TLAN: CSTAT = 0x%04hx\n", list->c_stat); - pr_info("TLAN: Frame Size = 0x%04hx\n", list->frame_size); + pr_info("%s List %d at %p\n", type, num, list); + pr_info(" Forward = 0x%08x\n", list->forward); + pr_info(" CSTAT = 0x%04hx\n", list->c_stat); + pr_info(" Frame Size = 0x%04hx\n", list->frame_size); /* for (i = 0; i < 10; i++) { */ for (i = 0; i < 2; i++) { - pr_info("TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", - i, list->buffer[i].count, list->buffer[i].address); + pr_info(" Buffer[%d].count, addr = 0x%08x, 0x%08x\n", + i, list->buffer[i].count, list->buffer[i].address); } } @@ -2400,7 +2253,7 @@ tlan_finish_reset(struct net_device *dev) if ((priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY) || (priv->aui)) { status = MII_GS_LINK; - pr_info("TLAN: %s: Link forced.\n", dev->name); + netdev_info(dev, "Link forced\n"); } else { tlan_mii_read_reg(dev, phy, MII_GEN_STS, &status); udelay(1000); @@ -2412,24 +2265,21 @@ tlan_finish_reset(struct net_device *dev) tlan_mii_read_reg(dev, phy, MII_AN_LPA, &partner); tlan_mii_read_reg(dev, phy, TLAN_TLPHY_PAR, &tlphy_par); - pr_info("TLAN: %s: Link active with ", dev->name); - if (!(tlphy_par & TLAN_PHY_AN_EN_STAT)) { - pr_info("forced 10%sMbps %s-Duplex\n", - tlphy_par & TLAN_PHY_SPEED_100 - ? "" : "0", - tlphy_par & TLAN_PHY_DUPLEX_FULL - ? "Full" : "Half"); - } else { - pr_info("Autonegotiation enabled, at 10%sMbps %s-Duplex\n", - tlphy_par & TLAN_PHY_SPEED_100 - ? "" : "0", - tlphy_par & TLAN_PHY_DUPLEX_FULL - ? "Full" : "half"); - pr_info("TLAN: Partner capability: "); - for (i = 5; i <= 10; i++) - if (partner & (1<<i)) - printk("%s", media[i-5]); - printk("\n"); + netdev_info(dev, + "Link active with %s %uMbps %s-Duplex\n", + !(tlphy_par & TLAN_PHY_AN_EN_STAT) + ? "forced" : "Autonegotiation enabled,", + tlphy_par & TLAN_PHY_SPEED_100 + ? 100 : 10, + tlphy_par & TLAN_PHY_DUPLEX_FULL + ? "Full" : "Half"); + + if (tlphy_par & TLAN_PHY_AN_EN_STAT) { + netdev_info(dev, "Partner capability:"); + for (i = 5; i < 10; i++) + if (partner & (1 << i)) + pr_cont(" %s", media[i-5]); + pr_cont("\n"); } tlan_dio_write8(dev->base_addr, TLAN_LED_REG, @@ -2441,7 +2291,7 @@ tlan_finish_reset(struct net_device *dev) tlan_set_timer(dev, (10*HZ), TLAN_TIMER_LINK_BEAT); #endif } else if (status & MII_GS_LINK) { - pr_info("TLAN: %s: Link active\n", dev->name); + netdev_info(dev, "Link active\n"); tlan_dio_write8(dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK); } @@ -2467,8 +2317,7 @@ tlan_finish_reset(struct net_device *dev) outl(TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD); netif_carrier_on(dev); } else { - pr_info("TLAN: %s: Link inactive, will retry in 10 secs...\n", - dev->name); + netdev_info(dev, "Link inactive, will retry in 10 secs...\n"); tlan_set_timer(dev, (10*HZ), TLAN_TIMER_FINISH_RESET); return; } @@ -2552,23 +2401,20 @@ static void tlan_phy_print(struct net_device *dev) phy = priv->phy[priv->phy_num]; if (priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY) { - pr_info("TLAN: Device %s, Unmanaged PHY.\n", dev->name); + netdev_info(dev, "Unmanaged PHY\n"); } else if (phy <= TLAN_PHY_MAX_ADDR) { - pr_info("TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy); - pr_info("TLAN: Off. +0 +1 +2 +3\n"); + netdev_info(dev, "PHY 0x%02x\n", phy); + pr_info(" Off. +0 +1 +2 +3\n"); for (i = 0; i < 0x20; i += 4) { - pr_info("TLAN: 0x%02x", i); tlan_mii_read_reg(dev, phy, i, &data0); - printk(" 0x%04hx", data0); tlan_mii_read_reg(dev, phy, i + 1, &data1); - printk(" 0x%04hx", data1); tlan_mii_read_reg(dev, phy, i + 2, &data2); - printk(" 0x%04hx", data2); tlan_mii_read_reg(dev, phy, i + 3, &data3); - printk(" 0x%04hx\n", data3); + pr_info(" 0x%02x 0x%04hx 0x%04hx 0x%04hx 0x%04hx\n", + i, data0, data1, data2, data3); } } else { - pr_info("TLAN: Device %s, Invalid PHY.\n", dev->name); + netdev_info(dev, "Invalid PHY\n"); } } @@ -2635,7 +2481,7 @@ static void tlan_phy_detect(struct net_device *dev) else if (priv->phy[0] != TLAN_PHY_NONE) priv->phy_num = 0; else - pr_info("TLAN: Cannot initialize device, no PHY was found!\n"); + netdev_info(dev, "Cannot initialize device, no PHY was found!\n"); } @@ -2763,8 +2609,7 @@ static void tlan_phy_start_link(struct net_device *dev) * but the card need additional time to start AN. * .5 sec should be plenty extra. */ - pr_info("TLAN: %s: Starting autonegotiation.\n", - dev->name); + netdev_info(dev, "Starting autonegotiation\n"); tlan_set_timer(dev, (2*HZ), TLAN_TIMER_PHY_FINISH_AN); return; } @@ -2827,16 +2672,16 @@ static void tlan_phy_finish_auto_neg(struct net_device *dev) * more time. Perhaps we should fail after a while. */ if (!priv->neg_be_verbose++) { - pr_info("TLAN: Giving autonegotiation more time.\n"); - pr_info("TLAN: Please check that your adapter has\n"); - pr_info("TLAN: been properly connected to a HUB or Switch.\n"); - pr_info("TLAN: Trying to establish link in the background...\n"); + pr_info("Giving autonegotiation more time.\n"); + pr_info("Please check that your adapter has\n"); + pr_info("been properly connected to a HUB or Switch.\n"); + pr_info("Trying to establish link in the background...\n"); } tlan_set_timer(dev, (8*HZ), TLAN_TIMER_PHY_FINISH_AN); return; } - pr_info("TLAN: %s: Autonegotiation complete.\n", dev->name); + netdev_info(dev, "Autonegotiation complete\n"); tlan_mii_read_reg(dev, phy, MII_AN_ADV, &an_adv); tlan_mii_read_reg(dev, phy, MII_AN_LPA, &an_lpa); mode = an_adv & an_lpa & 0x03E0; @@ -2861,11 +2706,11 @@ static void tlan_phy_finish_auto_neg(struct net_device *dev) (an_adv & an_lpa & 0x0040)) { tlan_mii_write_reg(dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX); - pr_info("TLAN: Starting internal PHY with FULL-DUPLEX\n"); + netdev_info(dev, "Starting internal PHY with FULL-DUPLEX\n"); } else { tlan_mii_write_reg(dev, phy, MII_GEN_CTL, MII_GC_AUTOENB); - pr_info("TLAN: Starting internal PHY with HALF-DUPLEX\n"); + netdev_info(dev, "Starting internal PHY with HALF-DUPLEX\n"); } } diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 55786a0efc4..f5e9ac00a07 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -34,6 +34,8 @@ * Modifications for 2.3.99-pre5 kernel. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #define DRV_NAME "tun" #define DRV_VERSION "1.6" #define DRV_DESCRIPTION "Universal TUN/TAP device driver" @@ -76,11 +78,27 @@ #ifdef TUN_DEBUG static int debug; -#define DBG if(tun->debug)printk -#define DBG1 if(debug==2)printk +#define tun_debug(level, tun, fmt, args...) \ +do { \ + if (tun->debug) \ + netdev_printk(level, tun->dev, fmt, ##args); \ +} while (0) +#define DBG1(level, fmt, args...) \ +do { \ + if (debug == 2) \ + printk(level fmt, ##args); \ +} while (0) #else -#define DBG( a... ) -#define DBG1( a... ) +#define tun_debug(level, tun, fmt, args...) \ +do { \ + if (0) \ + netdev_printk(level, tun->dev, fmt, ##args); \ +} while (0) +#define DBG1(level, fmt, args...) \ +do { \ + if (0) \ + printk(level fmt, ##args); \ +} while (0) #endif #define FLT_EXACT_COUNT 8 @@ -205,7 +223,7 @@ static void tun_put(struct tun_struct *tun) tun_detach(tfile->tun); } -/* TAP filterting */ +/* TAP filtering */ static void addr_hash_set(u32 *mask, const u8 *addr) { int n = ether_crc(ETH_ALEN, addr) >> 26; @@ -360,7 +378,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) { struct tun_struct *tun = netdev_priv(dev); - DBG(KERN_INFO "%s: tun_net_xmit %d\n", tun->dev->name, skb->len); + tun_debug(KERN_INFO, tun, "tun_net_xmit %d\n", skb->len); /* Drop packet if interface is not attached */ if (!tun->tfile) @@ -499,7 +517,7 @@ static unsigned int tun_chr_poll(struct file *file, poll_table * wait) sk = tun->socket.sk; - DBG(KERN_INFO "%s: tun_chr_poll\n", tun->dev->name); + tun_debug(KERN_INFO, tun, "tun_chr_poll\n"); poll_wait(file, &tun->wq.wait, wait); @@ -690,7 +708,7 @@ static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv, if (!tun) return -EBADFD; - DBG(KERN_INFO "%s: tun_chr_write %ld\n", tun->dev->name, count); + tun_debug(KERN_INFO, tun, "tun_chr_write %ld\n", count); result = tun_get_user(tun, iv, iov_length(iv, count), file->f_flags & O_NONBLOCK); @@ -739,7 +757,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, else if (sinfo->gso_type & SKB_GSO_UDP) gso.gso_type = VIRTIO_NET_HDR_GSO_UDP; else { - printk(KERN_ERR "tun: unexpected GSO type: " + pr_err("unexpected GSO type: " "0x%x, gso_size %d, hdr_len %d\n", sinfo->gso_type, gso.gso_size, gso.hdr_len); @@ -786,7 +804,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct sk_buff *skb; ssize_t ret = 0; - DBG(KERN_INFO "%s: tun_chr_read\n", tun->dev->name); + tun_debug(KERN_INFO, tun, "tun_chr_read\n"); add_wait_queue(&tun->wq.wait, &wait); while (len) { @@ -1083,7 +1101,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) if (device_create_file(&tun->dev->dev, &dev_attr_tun_flags) || device_create_file(&tun->dev->dev, &dev_attr_owner) || device_create_file(&tun->dev->dev, &dev_attr_group)) - printk(KERN_ERR "Failed to create tun sysfs files\n"); + pr_err("Failed to create tun sysfs files\n"); sk->sk_destruct = tun_sock_destruct; @@ -1092,7 +1110,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) goto failed; } - DBG(KERN_INFO "%s: tun_set_iff\n", tun->dev->name); + tun_debug(KERN_INFO, tun, "tun_set_iff\n"); if (ifr->ifr_flags & IFF_NO_PI) tun->flags |= TUN_NO_PI; @@ -1129,7 +1147,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) static int tun_get_iff(struct net *net, struct tun_struct *tun, struct ifreq *ifr) { - DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name); + tun_debug(KERN_INFO, tun, "tun_get_iff\n"); strcpy(ifr->ifr_name, tun->dev->name); @@ -1229,7 +1247,7 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, if (!tun) goto unlock; - DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd); + tun_debug(KERN_INFO, tun, "tun_chr_ioctl cmd %d\n", cmd); ret = 0; switch (cmd) { @@ -1249,8 +1267,8 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, else tun->flags &= ~TUN_NOCHECKSUM; - DBG(KERN_INFO "%s: checksum %s\n", - tun->dev->name, arg ? "disabled" : "enabled"); + tun_debug(KERN_INFO, tun, "checksum %s\n", + arg ? "disabled" : "enabled"); break; case TUNSETPERSIST: @@ -1260,33 +1278,34 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, else tun->flags &= ~TUN_PERSIST; - DBG(KERN_INFO "%s: persist %s\n", - tun->dev->name, arg ? "enabled" : "disabled"); + tun_debug(KERN_INFO, tun, "persist %s\n", + arg ? "enabled" : "disabled"); break; case TUNSETOWNER: /* Set owner of the device */ tun->owner = (uid_t) arg; - DBG(KERN_INFO "%s: owner set to %d\n", tun->dev->name, tun->owner); + tun_debug(KERN_INFO, tun, "owner set to %d\n", tun->owner); break; case TUNSETGROUP: /* Set group of the device */ tun->group= (gid_t) arg; - DBG(KERN_INFO "%s: group set to %d\n", tun->dev->name, tun->group); + tun_debug(KERN_INFO, tun, "group set to %d\n", tun->group); break; case TUNSETLINK: /* Only allow setting the type when the interface is down */ if (tun->dev->flags & IFF_UP) { - DBG(KERN_INFO "%s: Linktype set failed because interface is up\n", - tun->dev->name); + tun_debug(KERN_INFO, tun, + "Linktype set failed because interface is up\n"); ret = -EBUSY; } else { tun->dev->type = (int) arg; - DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type); + tun_debug(KERN_INFO, tun, "linktype set to %d\n", + tun->dev->type); ret = 0; } break; @@ -1318,8 +1337,8 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd, case SIOCSIFHWADDR: /* Set hw address */ - DBG(KERN_DEBUG "%s: set hw address: %pM\n", - tun->dev->name, ifr.ifr_hwaddr.sa_data); + tun_debug(KERN_DEBUG, tun, "set hw address: %pM\n", + ifr.ifr_hwaddr.sa_data); ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); break; @@ -1433,7 +1452,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on) if (!tun) return -EBADFD; - DBG(KERN_INFO "%s: tun_chr_fasync %d\n", tun->dev->name, on); + tun_debug(KERN_INFO, tun, "tun_chr_fasync %d\n", on); if ((ret = fasync_helper(fd, file, on, &tun->fasync)) < 0) goto out; @@ -1455,7 +1474,7 @@ static int tun_chr_open(struct inode *inode, struct file * file) { struct tun_file *tfile; - DBG1(KERN_INFO "tunX: tun_chr_open\n"); + DBG1(KERN_INFO, "tunX: tun_chr_open\n"); tfile = kmalloc(sizeof(*tfile), GFP_KERNEL); if (!tfile) @@ -1476,7 +1495,7 @@ static int tun_chr_close(struct inode *inode, struct file *file) if (tun) { struct net_device *dev = tun->dev; - DBG(KERN_INFO "%s: tun_chr_close\n", dev->name); + tun_debug(KERN_INFO, tun, "tun_chr_close\n"); __tun_detach(tun); @@ -1607,18 +1626,18 @@ static int __init tun_init(void) { int ret = 0; - printk(KERN_INFO "tun: %s, %s\n", DRV_DESCRIPTION, DRV_VERSION); - printk(KERN_INFO "tun: %s\n", DRV_COPYRIGHT); + pr_info("%s, %s\n", DRV_DESCRIPTION, DRV_VERSION); + pr_info("%s\n", DRV_COPYRIGHT); ret = rtnl_link_register(&tun_link_ops); if (ret) { - printk(KERN_ERR "tun: Can't register link_ops\n"); + pr_err("Can't register link_ops\n"); goto err_linkops; } ret = misc_register(&tun_miscdev); if (ret) { - printk(KERN_ERR "tun: Can't register misc device %d\n", TUN_MINOR); + pr_err("Can't register misc device %d\n", TUN_MINOR); goto err_misc; } return 0; diff --git a/drivers/net/usb/dm9601.c b/drivers/net/usb/dm9601.c index 02b622e3b9f..5002f5be47b 100644 --- a/drivers/net/usb/dm9601.c +++ b/drivers/net/usb/dm9601.c @@ -651,6 +651,10 @@ static const struct usb_device_id products[] = { .driver_info = (unsigned long)&dm9601_info, }, { + USB_DEVICE(0x0fe6, 0x9700), /* DM9601 USB to Fast Ethernet Adapter */ + .driver_info = (unsigned long)&dm9601_info, + }, + { USB_DEVICE(0x0a46, 0x9000), /* DM9000E */ .driver_info = (unsigned long)&dm9601_info, }, diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index b4338f38939..7aeb113cbb9 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -274,6 +274,7 @@ source "drivers/net/wireless/b43legacy/Kconfig" source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/ipw2x00/Kconfig" source "drivers/net/wireless/iwlwifi/Kconfig" +source "drivers/net/wireless/iwlegacy/Kconfig" source "drivers/net/wireless/iwmc3200wifi/Kconfig" source "drivers/net/wireless/libertas/Kconfig" source "drivers/net/wireless/orinoco/Kconfig" diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 9760561a27a..ddd3fb6ba1d 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -24,7 +24,7 @@ obj-$(CONFIG_B43LEGACY) += b43legacy/ obj-$(CONFIG_ZD1211RW) += zd1211rw/ obj-$(CONFIG_RTL8180) += rtl818x/ obj-$(CONFIG_RTL8187) += rtl818x/ -obj-$(CONFIG_RTL8192CE) += rtlwifi/ +obj-$(CONFIG_RTLWIFI) += rtlwifi/ # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o @@ -41,7 +41,8 @@ obj-$(CONFIG_ADM8211) += adm8211.o obj-$(CONFIG_MWL8K) += mwl8k.o -obj-$(CONFIG_IWLWIFI) += iwlwifi/ +obj-$(CONFIG_IWLAGN) += iwlwifi/ +obj-$(CONFIG_IWLWIFI_LEGACY) += iwlegacy/ obj-$(CONFIG_RT2X00) += rt2x00/ obj-$(CONFIG_P54_COMMON) += p54/ diff --git a/drivers/net/wireless/adm8211.c b/drivers/net/wireless/adm8211.c index f9aa1bc0a94..afe2cbc6cb2 100644 --- a/drivers/net/wireless/adm8211.c +++ b/drivers/net/wireless/adm8211.c @@ -1658,7 +1658,7 @@ static void adm8211_tx_raw(struct ieee80211_hw *dev, struct sk_buff *skb, } /* Put adm8211_tx_hdr on skb and transmit */ -static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct adm8211_tx_hdr *txhdr; size_t payload_len, hdrlen; @@ -1707,8 +1707,6 @@ static int adm8211_tx(struct ieee80211_hw *dev, struct sk_buff *skb) txhdr->retry_limit = info->control.rates[0].count; adm8211_tx_raw(dev, skb, plcp_signal, hdrlen); - - return NETDEV_TX_OK; } static int adm8211_alloc_rings(struct ieee80211_hw *dev) diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 1476314afa8..298601436ee 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -1728,7 +1728,7 @@ static void at76_mac80211_tx_callback(struct urb *urb) ieee80211_wake_queues(priv->hw); } -static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void 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; @@ -1741,7 +1741,8 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (priv->tx_urb->status == -EINPROGRESS) { wiphy_err(priv->hw->wiphy, "%s called while tx urb is pending\n", __func__); - return NETDEV_TX_BUSY; + dev_kfree_skb_any(skb); + return; } /* The following code lines are important when the device is going to @@ -1755,7 +1756,8 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (compare_ether_addr(priv->bssid, mgmt->bssid)) { memcpy(priv->bssid, mgmt->bssid, ETH_ALEN); ieee80211_queue_work(hw, &priv->work_join_bssid); - return NETDEV_TX_BUSY; + dev_kfree_skb_any(skb); + return; } } @@ -1795,8 +1797,6 @@ static int at76_mac80211_tx(struct ieee80211_hw *hw, struct sk_buff *skb) priv->tx_urb, priv->tx_urb->hcpriv, priv->tx_urb->complete); } - - return 0; } static int at76_mac80211_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/ath/ar9170/ar9170.h b/drivers/net/wireless/ath/ar9170/ar9170.h index 4f845f80c09..371e4ce4952 100644 --- a/drivers/net/wireless/ath/ar9170/ar9170.h +++ b/drivers/net/wireless/ath/ar9170/ar9170.h @@ -224,7 +224,7 @@ void ar9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); int ar9170_nag_limiter(struct ar9170 *ar); /* MAC */ -int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int ar9170_init_mac(struct ar9170 *ar); int ar9170_set_qos(struct ar9170 *ar); int ar9170_update_multicast(struct ar9170 *ar, const u64 mc_hast); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index a9111e1161f..b761fec0d72 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1475,7 +1475,7 @@ static void ar9170_tx(struct ar9170 *ar) msecs_to_jiffies(AR9170_JANITOR_DELAY)); } -int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; @@ -1493,11 +1493,10 @@ int ar9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) skb_queue_tail(&ar->tx_pending[queue], skb); ar9170_tx(ar); - return NETDEV_TX_OK; + return; err_free: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static int ar9170_op_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/ath/ath5k/ath5k.h b/drivers/net/wireless/ath/ath5k/ath5k.h index 70abb61e9ef..0ee54eb333d 100644 --- a/drivers/net/wireless/ath/ath5k/ath5k.h +++ b/drivers/net/wireless/ath/ath5k/ath5k.h @@ -1164,8 +1164,8 @@ struct ath5k_txq; void set_beacon_filter(struct ieee80211_hw *hw, bool enable); bool ath_any_vif_assoc(struct ath5k_softc *sc); -int ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, - struct ath5k_txq *txq); +void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ath5k_txq *txq); int ath5k_init_hw(struct ath5k_softc *sc); int ath5k_stop_hw(struct ath5k_softc *sc); void ath5k_mode_setup(struct ath5k_softc *sc, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index dbc45e08543..91411e9b4b6 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -1361,7 +1361,7 @@ ath5k_receive_frame(struct ath5k_softc *sc, struct sk_buff *skb, * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(sc->ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_TSFT; + rxs->flag |= RX_FLAG_MACTIME_MPDU; rxs->freq = sc->curchan->center_freq; rxs->band = sc->curchan->band; @@ -1518,7 +1518,7 @@ unlock: * TX Handling * \*************/ -int +void ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, struct ath5k_txq *txq) { @@ -1567,11 +1567,10 @@ ath5k_tx_queue(struct ieee80211_hw *hw, struct sk_buff *skb, spin_unlock_irqrestore(&sc->txbuflock, flags); goto drop_packet; } - return NETDEV_TX_OK; + return; drop_packet: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static void diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index a60a726a140..1fbe3c0b9f0 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -52,7 +52,7 @@ extern int ath5k_modparam_nohwcrypt; * Mac80211 functions * \********************/ -static int +static void ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath5k_softc *sc = hw->priv; @@ -60,10 +60,10 @@ ath5k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (WARN_ON(qnum >= sc->ah->ah_capabilities.cap_queues.q_tx_num)) { dev_kfree_skb_any(skb); - return 0; + return; } - return ath5k_tx_queue(hw, skb, &sc->txqs[qnum]); + ath5k_tx_queue(hw, skb, &sc->txqs[qnum]); } diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 6fa3c24af2d..7f5de6e4448 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -78,15 +78,15 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) /* Awake Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdes, - ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1, - ARRAY_SIZE(ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1), + ar9485_1_1_pcie_phy_clkreq_disable_L1, + ARRAY_SIZE(ar9485_1_1_pcie_phy_clkreq_disable_L1), 2); /* Sleep Setting */ INIT_INI_ARRAY(&ah->iniPcieSerdesLowPower, - ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1, - ARRAY_SIZE(ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1), + ar9485_1_1_pcie_phy_clkreq_disable_L1, + ARRAY_SIZE(ar9485_1_1_pcie_phy_clkreq_disable_L1), 2); } else if (AR_SREV_9485(ah)) { /* mac */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index f9f0389b92a..c718ab512a9 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -449,26 +449,21 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc); #define ATH_LED_PIN_DEF 1 #define ATH_LED_PIN_9287 8 -#define ATH_LED_ON_DURATION_IDLE 350 /* in msecs */ -#define ATH_LED_OFF_DURATION_IDLE 250 /* in msecs */ - -enum ath_led_type { - ATH_LED_RADIO, - ATH_LED_ASSOC, - ATH_LED_TX, - ATH_LED_RX -}; - -struct ath_led { - struct ath_softc *sc; - struct led_classdev led_cdev; - enum ath_led_type led_type; - char name[32]; - bool registered; -}; +#define ATH_LED_PIN_9485 6 +#ifdef CONFIG_MAC80211_LEDS void ath_init_leds(struct ath_softc *sc); void ath_deinit_leds(struct ath_softc *sc); +#else +static inline void ath_init_leds(struct ath_softc *sc) +{ +} + +static inline void ath_deinit_leds(struct ath_softc *sc) +{ +} +#endif + /* Antenna diversity/combining */ #define ATH_ANT_RX_CURRENT_SHIFT 4 @@ -620,15 +615,11 @@ struct ath_softc { struct ath_beacon beacon; struct ieee80211_supported_band sbands[IEEE80211_NUM_BANDS]; - struct ath_led radio_led; - struct ath_led assoc_led; - struct ath_led tx_led; - struct ath_led rx_led; - struct delayed_work ath_led_blink_work; - int led_on_duration; - int led_off_duration; - int led_on_cnt; - int led_off_cnt; +#ifdef CONFIG_MAC80211_LEDS + bool led_registered; + char led_name[32]; + struct led_classdev led_cdev; +#endif struct ath9k_hw_cal_data caldata; int last_rssi; diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index fb4f17a5183..0fb8f8ac275 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -20,121 +20,31 @@ /* LED functions */ /********************************/ -static void ath_led_blink_work(struct work_struct *work) -{ - struct ath_softc *sc = container_of(work, struct ath_softc, - ath_led_blink_work.work); - - if (!(sc->sc_flags & SC_OP_LED_ASSOCIATED)) - return; - - if ((sc->led_on_duration == ATH_LED_ON_DURATION_IDLE) || - (sc->led_off_duration == ATH_LED_OFF_DURATION_IDLE)) - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - else - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (sc->sc_flags & SC_OP_LED_ON) ? 1 : 0); - - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, - (sc->sc_flags & SC_OP_LED_ON) ? - msecs_to_jiffies(sc->led_off_duration) : - msecs_to_jiffies(sc->led_on_duration)); - - sc->led_on_duration = sc->led_on_cnt ? - max((ATH_LED_ON_DURATION_IDLE - sc->led_on_cnt), 25) : - ATH_LED_ON_DURATION_IDLE; - sc->led_off_duration = sc->led_off_cnt ? - max((ATH_LED_OFF_DURATION_IDLE - sc->led_off_cnt), 10) : - ATH_LED_OFF_DURATION_IDLE; - sc->led_on_cnt = sc->led_off_cnt = 0; - if (sc->sc_flags & SC_OP_LED_ON) - sc->sc_flags &= ~SC_OP_LED_ON; - else - sc->sc_flags |= SC_OP_LED_ON; -} - +#ifdef CONFIG_MAC80211_LEDS static void ath_led_brightness(struct led_classdev *led_cdev, enum led_brightness brightness) { - struct ath_led *led = container_of(led_cdev, struct ath_led, led_cdev); - struct ath_softc *sc = led->sc; - - switch (brightness) { - case LED_OFF: - if (led->led_type == ATH_LED_ASSOC || - led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, - (led->led_type == ATH_LED_RADIO)); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - if (led->led_type == ATH_LED_RADIO) - sc->sc_flags &= ~SC_OP_LED_ON; - } else { - sc->led_off_cnt++; - } - break; - case LED_FULL: - if (led->led_type == ATH_LED_ASSOC) { - sc->sc_flags |= SC_OP_LED_ASSOCIATED; - if (led_blink) - ieee80211_queue_delayed_work(sc->hw, - &sc->ath_led_blink_work, 0); - } else if (led->led_type == ATH_LED_RADIO) { - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 0); - sc->sc_flags |= SC_OP_LED_ON; - } else { - sc->led_on_cnt++; - } - break; - default: - break; - } -} - -static int ath_register_led(struct ath_softc *sc, struct ath_led *led, - char *trigger) -{ - int ret; - - led->sc = sc; - led->led_cdev.name = led->name; - led->led_cdev.default_trigger = trigger; - led->led_cdev.brightness_set = ath_led_brightness; - - ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &led->led_cdev); - if (ret) - ath_err(ath9k_hw_common(sc->sc_ah), - "Failed to register led:%s", led->name); - else - led->registered = 1; - return ret; -} - -static void ath_unregister_led(struct ath_led *led) -{ - if (led->registered) { - led_classdev_unregister(&led->led_cdev); - led->registered = 0; - } + struct ath_softc *sc = container_of(led_cdev, struct ath_softc, led_cdev); + ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, (brightness == LED_OFF)); } void ath_deinit_leds(struct ath_softc *sc) { - ath_unregister_led(&sc->assoc_led); - sc->sc_flags &= ~SC_OP_LED_ASSOCIATED; - ath_unregister_led(&sc->tx_led); - ath_unregister_led(&sc->rx_led); - ath_unregister_led(&sc->radio_led); - ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); + if (!sc->led_registered) + return; + + ath_led_brightness(&sc->led_cdev, LED_OFF); + led_classdev_unregister(&sc->led_cdev); } void ath_init_leds(struct ath_softc *sc) { - char *trigger; int ret; if (AR_SREV_9287(sc->sc_ah)) sc->sc_ah->led_pin = ATH_LED_PIN_9287; + else if (AR_SREV_9485(sc->sc_ah)) + sc->sc_ah->led_pin = ATH_LED_PIN_9485; else sc->sc_ah->led_pin = ATH_LED_PIN_DEF; @@ -144,48 +54,22 @@ void ath_init_leds(struct ath_softc *sc) /* LED off, active low */ ath9k_hw_set_gpio(sc->sc_ah, sc->sc_ah->led_pin, 1); - if (led_blink) - INIT_DELAYED_WORK(&sc->ath_led_blink_work, ath_led_blink_work); - - trigger = ieee80211_get_radio_led_name(sc->hw); - snprintf(sc->radio_led.name, sizeof(sc->radio_led.name), - "ath9k-%s::radio", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->radio_led, trigger); - sc->radio_led.led_type = ATH_LED_RADIO; - if (ret) - goto fail; - - trigger = ieee80211_get_assoc_led_name(sc->hw); - snprintf(sc->assoc_led.name, sizeof(sc->assoc_led.name), - "ath9k-%s::assoc", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->assoc_led, trigger); - sc->assoc_led.led_type = ATH_LED_ASSOC; - if (ret) - goto fail; - - trigger = ieee80211_get_tx_led_name(sc->hw); - snprintf(sc->tx_led.name, sizeof(sc->tx_led.name), - "ath9k-%s::tx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->tx_led, trigger); - sc->tx_led.led_type = ATH_LED_TX; - if (ret) - goto fail; - - trigger = ieee80211_get_rx_led_name(sc->hw); - snprintf(sc->rx_led.name, sizeof(sc->rx_led.name), - "ath9k-%s::rx", wiphy_name(sc->hw->wiphy)); - ret = ath_register_led(sc, &sc->rx_led, trigger); - sc->rx_led.led_type = ATH_LED_RX; - if (ret) - goto fail; - - return; - -fail: - if (led_blink) - cancel_delayed_work_sync(&sc->ath_led_blink_work); - ath_deinit_leds(sc); + if (!led_blink) + sc->led_cdev.default_trigger = + ieee80211_get_radio_led_name(sc->hw); + + snprintf(sc->led_name, sizeof(sc->led_name), + "ath9k-%s", wiphy_name(sc->hw->wiphy)); + sc->led_cdev.name = sc->led_name; + sc->led_cdev.brightness_set = ath_led_brightness; + + ret = led_classdev_register(wiphy_dev(sc->hw->wiphy), &sc->led_cdev); + if (ret < 0) + return; + + sc->led_registered = true; } +#endif /*******************/ /* Rfkill */ diff --git a/drivers/net/wireless/ath/ath9k/hif_usb.c b/drivers/net/wireless/ath/ath9k/hif_usb.c index 5ab3084eb9c..f1b8af64569 100644 --- a/drivers/net/wireless/ath/ath9k/hif_usb.c +++ b/drivers/net/wireless/ath/ath9k/hif_usb.c @@ -52,6 +52,9 @@ static struct usb_device_id ath9k_hif_usb_ids[] = { { USB_DEVICE(0x083A, 0xA704), .driver_info = AR9280_USB }, /* SMC Networks */ + { USB_DEVICE(0x0cf3, 0x20ff), + .driver_info = STORAGE_DEVICE }, + { }, }; @@ -219,8 +222,9 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev) struct tx_buf *tx_buf = NULL; struct sk_buff *nskb = NULL; int ret = 0, i; - u16 *hdr, tx_skb_cnt = 0; + u16 tx_skb_cnt = 0; u8 *buf; + __le16 *hdr; if (hif_dev->tx.tx_skb_cnt == 0) return 0; @@ -245,9 +249,9 @@ static int __hif_usb_tx(struct hif_device_usb *hif_dev) buf = tx_buf->buf; buf += tx_buf->offset; - hdr = (u16 *)buf; - *hdr++ = nskb->len; - *hdr++ = ATH_USB_TX_STREAM_MODE_TAG; + hdr = (__le16 *)buf; + *hdr++ = cpu_to_le16(nskb->len); + *hdr++ = cpu_to_le16(ATH_USB_TX_STREAM_MODE_TAG); buf += 4; memcpy(buf, nskb->data, nskb->len); tx_buf->len = nskb->len + 4; @@ -913,13 +917,11 @@ static int ath9k_hif_usb_dev_init(struct hif_device_usb *hif_dev, u32 drv_info) if (ret) { dev_err(&hif_dev->udev->dev, "ath9k_htc: Unable to allocate URBs\n"); - goto err_urb; + goto err_fw_download; } return 0; -err_urb: - ath9k_hif_usb_dealloc_urbs(hif_dev); err_fw_download: release_firmware(hif_dev->firmware); err_fw_req: @@ -934,6 +936,61 @@ static void ath9k_hif_usb_dev_deinit(struct hif_device_usb *hif_dev) release_firmware(hif_dev->firmware); } +/* + * An exact copy of the function from zd1211rw. + */ +static int send_eject_command(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc = &interface->altsetting[0]; + struct usb_endpoint_descriptor *endpoint; + unsigned char *cmd; + u8 bulk_out_ep; + int r; + + /* Find bulk out endpoint */ + for (r = 1; r >= 0; r--) { + endpoint = &iface_desc->endpoint[r].desc; + if (usb_endpoint_dir_out(endpoint) && + usb_endpoint_xfer_bulk(endpoint)) { + bulk_out_ep = endpoint->bEndpointAddress; + break; + } + } + if (r == -1) { + dev_err(&udev->dev, + "ath9k_htc: Could not find bulk out endpoint\n"); + return -ENODEV; + } + + cmd = kzalloc(31, GFP_KERNEL); + if (cmd == NULL) + return -ENODEV; + + /* USB bulk command block */ + cmd[0] = 0x55; /* bulk command signature */ + cmd[1] = 0x53; /* bulk command signature */ + cmd[2] = 0x42; /* bulk command signature */ + cmd[3] = 0x43; /* bulk command signature */ + cmd[14] = 6; /* command length */ + + cmd[15] = 0x1b; /* SCSI command: START STOP UNIT */ + cmd[19] = 0x2; /* eject disc */ + + dev_info(&udev->dev, "Ejecting storage device...\n"); + r = usb_bulk_msg(udev, usb_sndbulkpipe(udev, bulk_out_ep), + cmd, 31, NULL, 2000); + kfree(cmd); + if (r) + return r; + + /* At this point, the device disconnects and reconnects with the real + * ID numbers. */ + + usb_set_intfdata(interface, NULL); + return 0; +} + static int ath9k_hif_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { @@ -941,6 +998,9 @@ static int ath9k_hif_usb_probe(struct usb_interface *interface, struct hif_device_usb *hif_dev; int ret = 0; + if (id->driver_info == STORAGE_DEVICE) + return send_eject_command(interface); + hif_dev = kzalloc(sizeof(struct hif_device_usb), GFP_KERNEL); if (!hif_dev) { ret = -ENOMEM; @@ -1027,12 +1087,13 @@ static void ath9k_hif_usb_disconnect(struct usb_interface *interface) struct hif_device_usb *hif_dev = usb_get_intfdata(interface); bool unplugged = (udev->state == USB_STATE_NOTATTACHED) ? true : false; - if (hif_dev) { - ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); - ath9k_htc_hw_free(hif_dev->htc_handle); - ath9k_hif_usb_dev_deinit(hif_dev); - usb_set_intfdata(interface, NULL); - } + if (!hif_dev) + return; + + ath9k_htc_hw_deinit(hif_dev->htc_handle, unplugged); + ath9k_htc_hw_free(hif_dev->htc_handle); + ath9k_hif_usb_dev_deinit(hif_dev); + usb_set_intfdata(interface, NULL); if (!unplugged && (hif_dev->flags & HIF_USB_START)) ath9k_hif_usb_reboot(udev); diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index 0cb504d7b8c..753a245c5ad 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -32,6 +32,7 @@ #include "wmi.h" #define ATH_STA_SHORT_CALINTERVAL 1000 /* 1 second */ +#define ATH_AP_SHORT_CALINTERVAL 100 /* 100 ms */ #define ATH_ANI_POLLINTERVAL 100 /* 100 ms */ #define ATH_LONG_CALINTERVAL 30000 /* 30 seconds */ #define ATH_RESTART_CALINTERVAL 1200000 /* 20 minutes */ @@ -204,8 +205,50 @@ struct ath9k_htc_target_stats { __be32 ht_tx_xretries; } __packed; +#define ATH9K_HTC_MAX_VIF 2 +#define ATH9K_HTC_MAX_BCN_VIF 2 + +#define INC_VIF(_priv, _type) do { \ + switch (_type) { \ + case NL80211_IFTYPE_STATION: \ + _priv->num_sta_vif++; \ + break; \ + case NL80211_IFTYPE_ADHOC: \ + _priv->num_ibss_vif++; \ + break; \ + case NL80211_IFTYPE_AP: \ + _priv->num_ap_vif++; \ + break; \ + default: \ + break; \ + } \ + } while (0) + +#define DEC_VIF(_priv, _type) do { \ + switch (_type) { \ + case NL80211_IFTYPE_STATION: \ + _priv->num_sta_vif--; \ + break; \ + case NL80211_IFTYPE_ADHOC: \ + _priv->num_ibss_vif--; \ + break; \ + case NL80211_IFTYPE_AP: \ + _priv->num_ap_vif--; \ + break; \ + default: \ + break; \ + } \ + } while (0) + struct ath9k_htc_vif { u8 index; + u16 seq_no; + bool beacon_configured; +}; + +struct ath9k_vif_iter_data { + const u8 *hw_macaddr; + u8 mask[ETH_ALEN]; }; #define ATH9K_HTC_MAX_STA 8 @@ -310,10 +353,8 @@ struct ath_led { struct htc_beacon_config { u16 beacon_interval; - u16 listen_interval; u16 dtim_period; u16 bmiss_timeout; - u8 dtim_count; }; struct ath_btcoex { @@ -333,13 +374,12 @@ void ath_htc_cancel_btcoex_work(struct ath9k_htc_priv *priv); #define OP_SCANNING BIT(1) #define OP_LED_ASSOCIATED BIT(2) #define OP_LED_ON BIT(3) -#define OP_PREAMBLE_SHORT BIT(4) -#define OP_PROTECT_ENABLE BIT(5) -#define OP_ASSOCIATED BIT(6) -#define OP_ENABLE_BEACON BIT(7) -#define OP_LED_DEINIT BIT(8) -#define OP_BT_PRIORITY_DETECTED BIT(9) -#define OP_BT_SCAN BIT(10) +#define OP_ENABLE_BEACON BIT(4) +#define OP_LED_DEINIT BIT(5) +#define OP_BT_PRIORITY_DETECTED BIT(6) +#define OP_BT_SCAN BIT(7) +#define OP_ANI_RUNNING BIT(8) +#define OP_TSF_RESET BIT(9) struct ath9k_htc_priv { struct device *dev; @@ -358,13 +398,22 @@ struct ath9k_htc_priv { enum htc_endpoint_id data_vi_ep; enum htc_endpoint_id data_vo_ep; + u8 vif_slot; + u8 mon_vif_idx; + u8 sta_slot; + u8 vif_sta_pos[ATH9K_HTC_MAX_VIF]; + u8 num_ibss_vif; + u8 num_sta_vif; + u8 num_ap_vif; + u16 op_flags; u16 curtxpow; u16 txpowlimit; u16 nvifs; u16 nstations; - u16 seq_no; u32 bmiss_cnt; + bool rearm_ani; + bool reconfig_beacon; struct ath9k_hw_cal_data caldata; @@ -382,7 +431,7 @@ struct ath9k_htc_priv { struct ath9k_htc_rx rx; struct tasklet_struct tx_tasklet; struct sk_buff_head tx_queue; - struct delayed_work ath9k_ani_work; + struct delayed_work ani_work; struct work_struct ps_work; struct work_struct fatal_work; @@ -424,6 +473,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv); void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv); void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif); +void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv); void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending); void ath9k_htc_rxep(void *priv, struct sk_buff *skb, @@ -436,8 +486,9 @@ void ath9k_htc_beaconep(void *drv_priv, struct sk_buff *skb, int ath9k_htc_update_cap_target(struct ath9k_htc_priv *priv); void ath9k_htc_station_work(struct work_struct *work); void ath9k_htc_aggr_work(struct work_struct *work); -void ath9k_ani_work(struct work_struct *work);; -void ath_start_ani(struct ath9k_htc_priv *priv); +void ath9k_htc_ani_work(struct work_struct *work); +void ath9k_htc_start_ani(struct ath9k_htc_priv *priv); +void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv); int ath9k_tx_init(struct ath9k_htc_priv *priv); void ath9k_tx_tasklet(unsigned long data); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 87cc65a78a3..8d1d8792436 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -123,8 +123,9 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, /* TSF out of range threshold fixed at 1 second */ bs.bs_tsfoor_threshold = ATH9K_TSFOOR_THRESHOLD; - ath_dbg(common, ATH_DBG_BEACON, "tsf: %llu tsftu: %u\n", tsf, tsftu); - ath_dbg(common, ATH_DBG_BEACON, + ath_dbg(common, ATH_DBG_CONFIG, "intval: %u tsf: %llu tsftu: %u\n", + intval, tsf, tsftu); + ath_dbg(common, ATH_DBG_CONFIG, "bmiss: %u sleep: %u cfp-period: %u maxdur: %u next: %u\n", bs.bs_bmissthreshold, bs.bs_sleepduration, bs.bs_cfpperiod, bs.bs_cfpmaxduration, bs.bs_cfpnext); @@ -138,25 +139,81 @@ static void ath9k_htc_beacon_config_sta(struct ath9k_htc_priv *priv, WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); } +static void ath9k_htc_beacon_config_ap(struct ath9k_htc_priv *priv, + struct htc_beacon_config *bss_conf) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + enum ath9k_int imask = 0; + u32 nexttbtt, intval, tsftu; + __be32 htc_imask = 0; + int ret; + u8 cmd_rsp; + u64 tsf; + + intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; + intval /= ATH9K_HTC_MAX_BCN_VIF; + nexttbtt = intval; + + if (priv->op_flags & OP_TSF_RESET) { + intval |= ATH9K_BEACON_RESET_TSF; + priv->op_flags &= ~OP_TSF_RESET; + } else { + /* + * Pull nexttbtt forward to reflect the current TSF. + */ + tsf = ath9k_hw_gettsf64(priv->ah); + tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + } + + intval |= ATH9K_BEACON_ENA; + + if (priv->op_flags & OP_ENABLE_BEACON) + imask |= ATH9K_INT_SWBA; + + ath_dbg(common, ATH_DBG_CONFIG, + "AP Beacon config, intval: %d, nexttbtt: %u imask: 0x%x\n", + bss_conf->beacon_interval, nexttbtt, imask); + + WMI_CMD(WMI_DISABLE_INTR_CMDID); + ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); + priv->bmiss_cnt = 0; + htc_imask = cpu_to_be32(imask); + WMI_CMD_BUF(WMI_ENABLE_INTR_CMDID, &htc_imask); +} + static void ath9k_htc_beacon_config_adhoc(struct ath9k_htc_priv *priv, struct htc_beacon_config *bss_conf) { struct ath_common *common = ath9k_hw_common(priv->ah); enum ath9k_int imask = 0; - u32 nexttbtt, intval; + u32 nexttbtt, intval, tsftu; __be32 htc_imask = 0; int ret; u8 cmd_rsp; + u64 tsf; intval = bss_conf->beacon_interval & ATH9K_BEACON_PERIOD; nexttbtt = intval; + + /* + * Pull nexttbtt forward to reflect the current TSF. + */ + tsf = ath9k_hw_gettsf64(priv->ah); + tsftu = TSF_TO_TU(tsf >> 32, tsf) + FUDGE; + do { + nexttbtt += intval; + } while (nexttbtt < tsftu); + intval |= ATH9K_BEACON_ENA; if (priv->op_flags & OP_ENABLE_BEACON) imask |= ATH9K_INT_SWBA; - ath_dbg(common, ATH_DBG_BEACON, - "IBSS Beacon config, intval: %d, imask: 0x%x\n", - bss_conf->beacon_interval, imask); + ath_dbg(common, ATH_DBG_CONFIG, + "IBSS Beacon config, intval: %d, nexttbtt: %u, imask: 0x%x\n", + bss_conf->beacon_interval, nexttbtt, imask); WMI_CMD(WMI_DISABLE_INTR_CMDID); ath9k_hw_beaconinit(priv->ah, nexttbtt, intval); @@ -207,9 +264,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, u8 beacon_pending) if (info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) beacon->data; - priv->seq_no += 0x10; + avp->seq_no += 0x10; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); - hdr->seq_ctrl |= cpu_to_le16(priv->seq_no); + hdr->seq_ctrl |= cpu_to_le16(avp->seq_no); } tx_ctl.type = ATH9K_HTC_NORMAL; @@ -253,30 +310,123 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) } } +static void ath9k_htc_beacon_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *beacon_configured = (bool *)data; + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; + + if (vif->type == NL80211_IFTYPE_STATION && + avp->beacon_configured) + *beacon_configured = true; +} + +static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + bool beacon_configured; + + /* + * Changing the beacon interval when multiple AP interfaces + * are configured will affect beacon transmission of all + * of them. + */ + if ((priv->ah->opmode == NL80211_IFTYPE_AP) && + (priv->num_ap_vif > 1) && + (vif->type == NL80211_IFTYPE_AP) && + (cur_conf->beacon_interval != bss_conf->beacon_int)) { + ath_dbg(common, ATH_DBG_CONFIG, + "Changing beacon interval of multiple AP interfaces !\n"); + return false; + } + + /* + * If the HW is operating in AP mode, any new station interfaces that + * are added cannot change the beacon parameters. + */ + if (priv->num_ap_vif && + (vif->type != NL80211_IFTYPE_AP)) { + ath_dbg(common, ATH_DBG_CONFIG, + "HW in AP mode, cannot set STA beacon parameters\n"); + return false; + } + + /* + * The beacon parameters are configured only for the first + * station interface. + */ + if ((priv->ah->opmode == NL80211_IFTYPE_STATION) && + (priv->num_sta_vif > 1) && + (vif->type == NL80211_IFTYPE_STATION)) { + beacon_configured = false; + ieee80211_iterate_active_interfaces_atomic(priv->hw, + ath9k_htc_beacon_iter, + &beacon_configured); + + if (beacon_configured) { + ath_dbg(common, ATH_DBG_CONFIG, + "Beacon already configured for a station interface\n"); + return false; + } + } + + return true; +} + void ath9k_htc_beacon_config(struct ath9k_htc_priv *priv, struct ieee80211_vif *vif) { struct ath_common *common = ath9k_hw_common(priv->ah); struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; + + if (!ath9k_htc_check_beacon_config(priv, vif)) + return; cur_conf->beacon_interval = bss_conf->beacon_int; if (cur_conf->beacon_interval == 0) cur_conf->beacon_interval = 100; cur_conf->dtim_period = bss_conf->dtim_period; - cur_conf->listen_interval = 1; - cur_conf->dtim_count = 1; cur_conf->bmiss_timeout = ATH_DEFAULT_BMISS_LIMIT * cur_conf->beacon_interval; switch (vif->type) { case NL80211_IFTYPE_STATION: ath9k_htc_beacon_config_sta(priv, cur_conf); + avp->beacon_configured = true; + break; + case NL80211_IFTYPE_ADHOC: + ath9k_htc_beacon_config_adhoc(priv, cur_conf); + break; + case NL80211_IFTYPE_AP: + ath9k_htc_beacon_config_ap(priv, cur_conf); + break; + default: + ath_dbg(common, ATH_DBG_CONFIG, + "Unsupported beaconing mode\n"); + return; + } +} + +void ath9k_htc_beacon_reconfig(struct ath9k_htc_priv *priv) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct htc_beacon_config *cur_conf = &priv->cur_beacon_conf; + + switch (priv->ah->opmode) { + case NL80211_IFTYPE_STATION: + ath9k_htc_beacon_config_sta(priv, cur_conf); break; case NL80211_IFTYPE_ADHOC: ath9k_htc_beacon_config_adhoc(priv, cur_conf); break; + case NL80211_IFTYPE_AP: + ath9k_htc_beacon_config_ap(priv, cur_conf); + break; default: ath_dbg(common, ATH_DBG_CONFIG, "Unsupported beaconing mode\n"); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index a7bc26d1bd6..fc67c937e17 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -679,7 +679,7 @@ static int ath9k_init_priv(struct ath9k_htc_priv *priv, (unsigned long)priv); tasklet_init(&priv->tx_tasklet, ath9k_tx_tasklet, (unsigned long)priv); - INIT_DELAYED_WORK(&priv->ath9k_ani_work, ath9k_ani_work); + INIT_DELAYED_WORK(&priv->ani_work, ath9k_htc_ani_work); INIT_WORK(&priv->ps_work, ath9k_ps_work); INIT_WORK(&priv->fatal_work, ath9k_fatal_work); @@ -787,6 +787,7 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, struct ath_hw *ah; int error = 0; struct ath_regulatory *reg; + char hw_name[64]; /* Bring up device */ error = ath9k_init_priv(priv, devid, product, drv_info); @@ -827,6 +828,22 @@ static int ath9k_init_device(struct ath9k_htc_priv *priv, goto err_world; } + ath_dbg(common, ATH_DBG_CONFIG, + "WMI:%d, BCN:%d, CAB:%d, UAPSD:%d, MGMT:%d, " + "BE:%d, BK:%d, VI:%d, VO:%d\n", + priv->wmi_cmd_ep, + priv->beacon_ep, + priv->cab_ep, + priv->uapsd_ep, + priv->mgmt_ep, + priv->data_be_ep, + priv->data_bk_ep, + priv->data_vi_ep, + priv->data_vo_ep); + + ath9k_hw_name(priv->ah, hw_name, sizeof(hw_name)); + wiphy_info(hw->wiphy, "%s\n", hw_name); + ath9k_init_leds(priv); ath9k_start_rfkill_poll(priv); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 50fde0e1059..db8c0c044e9 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -105,6 +105,82 @@ void ath9k_ps_work(struct work_struct *work) ath9k_htc_setpower(priv, ATH9K_PM_NETWORK_SLEEP); } +static void ath9k_htc_vif_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath9k_htc_priv *priv = data; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + + if ((vif->type == NL80211_IFTYPE_AP) && bss_conf->enable_beacon) + priv->reconfig_beacon = true; + + if (bss_conf->assoc) { + priv->rearm_ani = true; + priv->reconfig_beacon = true; + } +} + +static void ath9k_htc_vif_reconfig(struct ath9k_htc_priv *priv) +{ + priv->rearm_ani = false; + priv->reconfig_beacon = false; + + ieee80211_iterate_active_interfaces_atomic(priv->hw, + ath9k_htc_vif_iter, priv); + if (priv->rearm_ani) + ath9k_htc_start_ani(priv); + + if (priv->reconfig_beacon) { + ath9k_htc_ps_wakeup(priv); + ath9k_htc_beacon_reconfig(priv); + ath9k_htc_ps_restore(priv); + } +} + +static void ath9k_htc_bssid_iter(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct ath9k_vif_iter_data *iter_data = data; + int i; + + for (i = 0; i < ETH_ALEN; i++) + iter_data->mask[i] &= ~(iter_data->hw_macaddr[i] ^ mac[i]); +} + +static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, + struct ieee80211_vif *vif) +{ + struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_vif_iter_data iter_data; + + /* + * Use the hardware MAC address as reference, the hardware uses it + * together with the BSSID mask when matching addresses. + */ + iter_data.hw_macaddr = common->macaddr; + memset(&iter_data.mask, 0xff, ETH_ALEN); + + if (vif) + ath9k_htc_bssid_iter(&iter_data, vif->addr, vif); + + /* Get list of all active MAC addresses */ + ieee80211_iterate_active_interfaces_atomic(priv->hw, ath9k_htc_bssid_iter, + &iter_data); + + memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); + ath_hw_setbssidmask(common); +} + +static void ath9k_htc_set_opmode(struct ath9k_htc_priv *priv) +{ + if (priv->num_ibss_vif) + priv->ah->opmode = NL80211_IFTYPE_ADHOC; + else if (priv->num_ap_vif) + priv->ah->opmode = NL80211_IFTYPE_AP; + else + priv->ah->opmode = NL80211_IFTYPE_STATION; + + ath9k_hw_setopmode(priv->ah); +} + void ath9k_htc_reset(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; @@ -119,9 +195,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); - if (priv->op_flags & OP_ASSOCIATED) - cancel_delayed_work_sync(&priv->ath9k_ani_work); - + ath9k_htc_stop_ani(priv); ieee80211_stop_queues(priv->hw); htc_stop(priv->htc); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -148,12 +222,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) WMI_CMD(WMI_ENABLE_INTR_CMDID); htc_start(priv->htc); - - if (priv->op_flags & OP_ASSOCIATED) { - ath9k_htc_beacon_config(priv, priv->vif); - ath_start_ani(priv); - } - + ath9k_htc_vif_reconfig(priv); ieee80211_wake_queues(priv->hw); ath9k_htc_ps_restore(priv); @@ -222,11 +291,23 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, goto err; htc_start(priv->htc); + + if (!(priv->op_flags & OP_SCANNING) && + !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL)) + ath9k_htc_vif_reconfig(priv); + err: ath9k_htc_ps_restore(priv); return ret; } +/* + * Monitor mode handling is a tad complicated because the firmware requires + * an interface to be created exclusively, while mac80211 doesn't associate + * an interface with the mode. + * + * So, for now, only one monitor interface can be configured. + */ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); @@ -236,9 +317,10 @@ static void __ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); - hvif.index = 0; /* Should do for now */ + hvif.index = priv->mon_vif_idx; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); priv->nvifs--; + priv->vif_slot &= ~(1 << priv->mon_vif_idx); } static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) @@ -246,70 +328,87 @@ static int ath9k_htc_add_monitor_interface(struct ath9k_htc_priv *priv) struct ath_common *common = ath9k_hw_common(priv->ah); struct ath9k_htc_target_vif hvif; struct ath9k_htc_target_sta tsta; - int ret = 0; + int ret = 0, sta_idx; u8 cmd_rsp; - if (priv->nvifs > 0) - return -ENOBUFS; + if ((priv->nvifs >= ATH9K_HTC_MAX_VIF) || + (priv->nstations >= ATH9K_HTC_MAX_STA)) { + ret = -ENOBUFS; + goto err_vif; + } - if (priv->nstations >= ATH9K_HTC_MAX_STA) - return -ENOBUFS; + sta_idx = ffz(priv->sta_slot); + if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) { + ret = -ENOBUFS; + goto err_vif; + } /* * Add an interface. */ - memset(&hvif, 0, sizeof(struct ath9k_htc_target_vif)); memcpy(&hvif.myaddr, common->macaddr, ETH_ALEN); hvif.opmode = cpu_to_be32(HTC_M_MONITOR); - priv->ah->opmode = NL80211_IFTYPE_MONITOR; - hvif.index = priv->nvifs; + hvif.index = ffz(priv->vif_slot); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) - return ret; + goto err_vif; + + /* + * Assign the monitor interface index as a special case here. + * This is needed when the interface is brought down. + */ + priv->mon_vif_idx = hvif.index; + priv->vif_slot |= (1 << hvif.index); + + /* + * Set the hardware mode to monitor only if there are no + * other interfaces. + */ + if (!priv->nvifs) + priv->ah->opmode = NL80211_IFTYPE_MONITOR; priv->nvifs++; /* * Associate a station with the interface for packet injection. */ - memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); memcpy(&tsta.macaddr, common->macaddr, ETH_ALEN); tsta.is_vif_sta = 1; - tsta.sta_index = priv->nstations; + tsta.sta_index = sta_idx; tsta.vif_index = hvif.index; tsta.maxampdu = 0xffff; WMI_CMD_BUF(WMI_NODE_CREATE_CMDID, &tsta); if (ret) { ath_err(common, "Unable to add station entry for monitor mode\n"); - goto err_vif; + goto err_sta; } + priv->sta_slot |= (1 << sta_idx); priv->nstations++; - - /* - * Set chainmask etc. on the target. - */ - ret = ath9k_htc_update_cap_target(priv); - if (ret) - ath_dbg(common, ATH_DBG_CONFIG, - "Failed to update capability in target\n"); - + priv->vif_sta_pos[priv->mon_vif_idx] = sta_idx; priv->ah->is_monitoring = true; + ath_dbg(common, ATH_DBG_CONFIG, + "Attached a monitor interface at idx: %d, sta idx: %d\n", + priv->mon_vif_idx, sta_idx); + return 0; -err_vif: +err_sta: /* * Remove the interface from the target. */ __ath9k_htc_remove_monitor_interface(priv); +err_vif: + ath_dbg(common, ATH_DBG_FATAL, "Unable to attach a monitor interface\n"); + return ret; } @@ -321,7 +420,7 @@ static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) __ath9k_htc_remove_monitor_interface(priv); - sta_idx = 0; /* Only single interface, for now */ + sta_idx = priv->vif_sta_pos[priv->mon_vif_idx]; WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); if (ret) { @@ -329,9 +428,14 @@ static int ath9k_htc_remove_monitor_interface(struct ath9k_htc_priv *priv) return ret; } + priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; priv->ah->is_monitoring = false; + ath_dbg(common, ATH_DBG_CONFIG, + "Removed a monitor interface at idx: %d, sta idx: %d\n", + priv->mon_vif_idx, sta_idx); + return 0; } @@ -343,12 +447,16 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, struct ath9k_htc_target_sta tsta; struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; - int ret; + int ret, sta_idx; u8 cmd_rsp; if (priv->nstations >= ATH9K_HTC_MAX_STA) return -ENOBUFS; + sta_idx = ffz(priv->sta_slot); + if ((sta_idx < 0) || (sta_idx > ATH9K_HTC_MAX_STA)) + return -ENOBUFS; + memset(&tsta, 0, sizeof(struct ath9k_htc_target_sta)); if (sta) { @@ -358,13 +466,13 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, tsta.associd = common->curaid; tsta.is_vif_sta = 0; tsta.valid = true; - ista->index = priv->nstations; + ista->index = sta_idx; } else { memcpy(&tsta.macaddr, vif->addr, ETH_ALEN); tsta.is_vif_sta = 1; } - tsta.sta_index = priv->nstations; + tsta.sta_index = sta_idx; tsta.vif_index = avp->index; tsta.maxampdu = 0xffff; if (sta && sta->ht_cap.ht_supported) @@ -379,12 +487,21 @@ static int ath9k_htc_add_station(struct ath9k_htc_priv *priv, return ret; } - if (sta) + if (sta) { ath_dbg(common, ATH_DBG_CONFIG, "Added a station entry for: %pM (idx: %d)\n", sta->addr, tsta.sta_index); + } else { + ath_dbg(common, ATH_DBG_CONFIG, + "Added a station entry for VIF %d (idx: %d)\n", + avp->index, tsta.sta_index); + } + priv->sta_slot |= (1 << sta_idx); priv->nstations++; + if (!sta) + priv->vif_sta_pos[avp->index] = sta_idx; + return 0; } @@ -393,6 +510,7 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, struct ieee80211_sta *sta) { struct ath_common *common = ath9k_hw_common(priv->ah); + struct ath9k_htc_vif *avp = (struct ath9k_htc_vif *) vif->drv_priv; struct ath9k_htc_sta *ista; int ret; u8 cmd_rsp, sta_idx; @@ -401,7 +519,7 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { - sta_idx = 0; + sta_idx = priv->vif_sta_pos[avp->index]; } WMI_CMD_BUF(WMI_NODE_REMOVE_CMDID, &sta_idx); @@ -413,12 +531,19 @@ static int ath9k_htc_remove_station(struct ath9k_htc_priv *priv, return ret; } - if (sta) + if (sta) { ath_dbg(common, ATH_DBG_CONFIG, "Removed a station entry for: %pM (idx: %d)\n", sta->addr, sta_idx); + } else { + ath_dbg(common, ATH_DBG_CONFIG, + "Removed a station entry for VIF %d (idx: %d)\n", + avp->index, sta_idx); + } + priv->sta_slot &= ~(1 << sta_idx); priv->nstations--; + return 0; } @@ -800,7 +925,7 @@ void ath9k_htc_debug_remove_root(void) /* ANI */ /*******/ -void ath_start_ani(struct ath9k_htc_priv *priv) +void ath9k_htc_start_ani(struct ath9k_htc_priv *priv) { struct ath_common *common = ath9k_hw_common(priv->ah); unsigned long timestamp = jiffies_to_msecs(jiffies); @@ -809,15 +934,22 @@ void ath_start_ani(struct ath9k_htc_priv *priv) common->ani.shortcal_timer = timestamp; common->ani.checkani_timer = timestamp; - ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + priv->op_flags |= OP_ANI_RUNNING; + + ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(ATH_ANI_POLLINTERVAL)); } -void ath9k_ani_work(struct work_struct *work) +void ath9k_htc_stop_ani(struct ath9k_htc_priv *priv) +{ + cancel_delayed_work_sync(&priv->ani_work); + priv->op_flags &= ~OP_ANI_RUNNING; +} + +void ath9k_htc_ani_work(struct work_struct *work) { struct ath9k_htc_priv *priv = - container_of(work, struct ath9k_htc_priv, - ath9k_ani_work.work); + container_of(work, struct ath9k_htc_priv, ani_work.work); struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); bool longcal = false; @@ -826,7 +958,8 @@ void ath9k_ani_work(struct work_struct *work) unsigned int timestamp = jiffies_to_msecs(jiffies); u32 cal_interval, short_cal_interval; - short_cal_interval = ATH_STA_SHORT_CALINTERVAL; + short_cal_interval = (ah->opmode == NL80211_IFTYPE_AP) ? + ATH_AP_SHORT_CALINTERVAL : ATH_STA_SHORT_CALINTERVAL; /* Only calibrate if awake */ if (ah->power_mode != ATH9K_PM_AWAKE) @@ -895,7 +1028,7 @@ set_timer: if (!common->ani.caldone) cal_interval = min(cal_interval, (u32)short_cal_interval); - ieee80211_queue_delayed_work(common->hw, &priv->ath9k_ani_work, + ieee80211_queue_delayed_work(common->hw, &priv->ani_work, msecs_to_jiffies(cal_interval)); } @@ -903,7 +1036,7 @@ set_timer: /* mac80211 Callbacks */ /**********************/ -static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_hdr *hdr; struct ath9k_htc_priv *priv = hw->priv; @@ -916,7 +1049,7 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) padsize = padpos & 3; if (padsize && skb->len > padpos) { if (skb_headroom(skb) < padsize) - return -1; + goto fail_tx; skb_push(skb, padsize); memmove(skb->data, skb->data + padsize, padpos); } @@ -937,11 +1070,10 @@ static int ath9k_htc_tx(struct ieee80211_hw *hw, struct sk_buff *skb) goto fail_tx; } - return 0; + return; fail_tx: dev_kfree_skb_any(skb); - return 0; } static int ath9k_htc_start(struct ieee80211_hw *hw) @@ -990,6 +1122,11 @@ static int ath9k_htc_start(struct ieee80211_hw *hw) ath9k_host_rx_init(priv); + ret = ath9k_htc_update_cap_target(priv); + if (ret) + ath_dbg(common, ATH_DBG_CONFIG, + "Failed to update capability in target\n"); + priv->op_flags &= ~OP_INVALID; htc_start(priv->htc); @@ -1044,26 +1181,21 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw) cancel_work_sync(&priv->fatal_work); cancel_work_sync(&priv->ps_work); cancel_delayed_work_sync(&priv->ath9k_led_blink_work); - cancel_delayed_work_sync(&priv->ath9k_ani_work); + ath9k_htc_stop_ani(priv); ath9k_led_stop_brightness(priv); mutex_lock(&priv->mutex); - /* Remove monitor interface here */ - if (ah->opmode == NL80211_IFTYPE_MONITOR) { - if (ath9k_htc_remove_monitor_interface(priv)) - ath_err(common, "Unable to remove monitor interface\n"); - else - ath_dbg(common, ATH_DBG_CONFIG, - "Monitor interface removed\n"); - } - if (ah->btcoex_hw.enabled) { ath9k_hw_btcoex_disable(ah); if (ah->btcoex_hw.scheme == ATH_BTCOEX_CFG_3WIRE) ath_htc_cancel_btcoex_work(priv); } + /* Remove a monitor interface if it's present. */ + if (priv->ah->is_monitoring) + ath9k_htc_remove_monitor_interface(priv); + ath9k_hw_phy_disable(ah); ath9k_hw_disable(ah); ath9k_htc_ps_restore(priv); @@ -1087,10 +1219,24 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, mutex_lock(&priv->mutex); - /* Only one interface for now */ - if (priv->nvifs > 0) { - ret = -ENOBUFS; - goto out; + if (priv->nvifs >= ATH9K_HTC_MAX_VIF) { + mutex_unlock(&priv->mutex); + return -ENOBUFS; + } + + if (priv->num_ibss_vif || + (priv->nvifs && vif->type == NL80211_IFTYPE_ADHOC)) { + ath_err(common, "IBSS coexistence with other modes is not allowed\n"); + mutex_unlock(&priv->mutex); + return -ENOBUFS; + } + + if (((vif->type == NL80211_IFTYPE_AP) || + (vif->type == NL80211_IFTYPE_ADHOC)) && + ((priv->num_ap_vif + priv->num_ibss_vif) >= ATH9K_HTC_MAX_BCN_VIF)) { + ath_err(common, "Max. number of beaconing interfaces reached\n"); + mutex_unlock(&priv->mutex); + return -ENOBUFS; } ath9k_htc_ps_wakeup(priv); @@ -1104,6 +1250,9 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_ADHOC: hvif.opmode = cpu_to_be32(HTC_M_IBSS); break; + case NL80211_IFTYPE_AP: + hvif.opmode = cpu_to_be32(HTC_M_HOSTAP); + break; default: ath_err(common, "Interface type %d not yet supported\n", vif->type); @@ -1111,34 +1260,39 @@ static int ath9k_htc_add_interface(struct ieee80211_hw *hw, goto out; } - ath_dbg(common, ATH_DBG_CONFIG, - "Attach a VIF of type: %d\n", vif->type); - - priv->ah->opmode = vif->type; - /* Index starts from zero on the target */ - avp->index = hvif.index = priv->nvifs; + avp->index = hvif.index = ffz(priv->vif_slot); hvif.rtsthreshold = cpu_to_be16(2304); WMI_CMD_BUF(WMI_VAP_CREATE_CMDID, &hvif); if (ret) goto out; - priv->nvifs++; - /* * We need a node in target to tx mgmt frames * before association. */ ret = ath9k_htc_add_station(priv, vif, NULL); - if (ret) + if (ret) { + WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); goto out; + } - ret = ath9k_htc_update_cap_target(priv); - if (ret) - ath_dbg(common, ATH_DBG_CONFIG, - "Failed to update capability in target\n"); + ath9k_htc_set_bssid_mask(priv, vif); + priv->vif_slot |= (1 << avp->index); + priv->nvifs++; priv->vif = vif; + + INC_VIF(priv, vif->type); + ath9k_htc_set_opmode(priv); + + if ((priv->ah->opmode == NL80211_IFTYPE_AP) && + !(priv->op_flags & OP_ANI_RUNNING)) + ath9k_htc_start_ani(priv); + + ath_dbg(common, ATH_DBG_CONFIG, + "Attach a VIF of type: %d at idx: %d\n", vif->type, avp->index); + out: ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); @@ -1156,8 +1310,6 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, int ret = 0; u8 cmd_rsp; - ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface\n"); - mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); @@ -1166,10 +1318,27 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, hvif.index = avp->index; WMI_CMD_BUF(WMI_VAP_REMOVE_CMDID, &hvif); priv->nvifs--; + priv->vif_slot &= ~(1 << avp->index); ath9k_htc_remove_station(priv, vif, NULL); priv->vif = NULL; + DEC_VIF(priv, vif->type); + ath9k_htc_set_opmode(priv); + + /* + * Stop ANI only if there are no associated station interfaces. + */ + if ((vif->type == NL80211_IFTYPE_AP) && (priv->num_ap_vif == 0)) { + priv->rearm_ani = false; + ieee80211_iterate_active_interfaces_atomic(priv->hw, + ath9k_htc_vif_iter, priv); + if (!priv->rearm_ani) + ath9k_htc_stop_ani(priv); + } + + ath_dbg(common, ATH_DBG_CONFIG, "Detach Interface at idx: %d\n", avp->index); + ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } @@ -1205,13 +1374,11 @@ static int ath9k_htc_config(struct ieee80211_hw *hw, u32 changed) * IEEE80211_CONF_CHANGE_CHANNEL is handled. */ if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - if (conf->flags & IEEE80211_CONF_MONITOR) { - if (ath9k_htc_add_monitor_interface(priv)) - ath_err(common, "Failed to set monitor mode\n"); - else - ath_dbg(common, ATH_DBG_CONFIG, - "HW opmode set to Monitor mode\n"); - } + if ((conf->flags & IEEE80211_CONF_MONITOR) && + !priv->ah->is_monitoring) + ath9k_htc_add_monitor_interface(priv); + else if (priv->ah->is_monitoring) + ath9k_htc_remove_monitor_interface(priv); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { @@ -1434,66 +1601,81 @@ static void ath9k_htc_bss_info_changed(struct ieee80211_hw *hw, struct ath9k_htc_priv *priv = hw->priv; struct ath_hw *ah = priv->ah; struct ath_common *common = ath9k_hw_common(ah); + bool set_assoc; mutex_lock(&priv->mutex); ath9k_htc_ps_wakeup(priv); + /* + * Set the HW AID/BSSID only for the first station interface + * or in IBSS mode. + */ + set_assoc = !!((priv->ah->opmode == NL80211_IFTYPE_ADHOC) || + ((priv->ah->opmode == NL80211_IFTYPE_STATION) && + (priv->num_sta_vif == 1))); + + if (changed & BSS_CHANGED_ASSOC) { - common->curaid = bss_conf->assoc ? - bss_conf->aid : 0; - ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", - bss_conf->assoc); - - if (bss_conf->assoc) { - priv->op_flags |= OP_ASSOCIATED; - ath_start_ani(priv); - } else { - priv->op_flags &= ~OP_ASSOCIATED; - cancel_delayed_work_sync(&priv->ath9k_ani_work); + if (set_assoc) { + ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed ASSOC %d\n", + bss_conf->assoc); + + common->curaid = bss_conf->assoc ? + bss_conf->aid : 0; + + if (bss_conf->assoc) + ath9k_htc_start_ani(priv); + else + ath9k_htc_stop_ani(priv); } } if (changed & BSS_CHANGED_BSSID) { - /* Set BSSID */ - memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); - ath9k_hw_write_associd(ah); + if (set_assoc) { + memcpy(common->curbssid, bss_conf->bssid, ETH_ALEN); + ath9k_hw_write_associd(ah); - ath_dbg(common, ATH_DBG_CONFIG, - "BSSID: %pM aid: 0x%x\n", - common->curbssid, common->curaid); + ath_dbg(common, ATH_DBG_CONFIG, + "BSSID: %pM aid: 0x%x\n", + common->curbssid, common->curaid); + } } - if ((changed & BSS_CHANGED_BEACON_INT) || - (changed & BSS_CHANGED_BEACON) || - ((changed & BSS_CHANGED_BEACON_ENABLED) && - bss_conf->enable_beacon)) { + if ((changed & BSS_CHANGED_BEACON_ENABLED) && bss_conf->enable_beacon) { + ath_dbg(common, ATH_DBG_CONFIG, + "Beacon enabled for BSS: %pM\n", bss_conf->bssid); priv->op_flags |= OP_ENABLE_BEACON; ath9k_htc_beacon_config(priv, vif); } - if ((changed & BSS_CHANGED_BEACON_ENABLED) && - !bss_conf->enable_beacon) { - priv->op_flags &= ~OP_ENABLE_BEACON; - ath9k_htc_beacon_config(priv, vif); - } - - if (changed & BSS_CHANGED_ERP_PREAMBLE) { - ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed PREAMBLE %d\n", - bss_conf->use_short_preamble); - if (bss_conf->use_short_preamble) - priv->op_flags |= OP_PREAMBLE_SHORT; - else - priv->op_flags &= ~OP_PREAMBLE_SHORT; + if ((changed & BSS_CHANGED_BEACON_ENABLED) && !bss_conf->enable_beacon) { + /* + * Disable SWBA interrupt only if there are no + * AP/IBSS interfaces. + */ + if ((priv->num_ap_vif <= 1) || priv->num_ibss_vif) { + ath_dbg(common, ATH_DBG_CONFIG, + "Beacon disabled for BSS: %pM\n", + bss_conf->bssid); + priv->op_flags &= ~OP_ENABLE_BEACON; + ath9k_htc_beacon_config(priv, vif); + } } - if (changed & BSS_CHANGED_ERP_CTS_PROT) { - ath_dbg(common, ATH_DBG_CONFIG, "BSS Changed CTS PROT %d\n", - bss_conf->use_cts_prot); - if (bss_conf->use_cts_prot && - hw->conf.channel->band != IEEE80211_BAND_5GHZ) - priv->op_flags |= OP_PROTECT_ENABLE; - else - priv->op_flags &= ~OP_PROTECT_ENABLE; + if (changed & BSS_CHANGED_BEACON_INT) { + /* + * Reset the HW TSF for the first AP interface. + */ + if ((priv->ah->opmode == NL80211_IFTYPE_AP) && + (priv->nvifs == 1) && + (priv->num_ap_vif == 1) && + (vif->type == NL80211_IFTYPE_AP)) { + priv->op_flags |= OP_TSF_RESET; + } + ath_dbg(common, ATH_DBG_CONFIG, + "Beacon interval changed for BSS: %pM\n", + bss_conf->bssid); + ath9k_htc_beacon_config(priv, vif); } if (changed & BSS_CHANGED_ERP_SLOT) { @@ -1558,6 +1740,8 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, struct ath9k_htc_sta *ista; int ret = 0; + mutex_lock(&priv->mutex); + switch (action) { case IEEE80211_AMPDU_RX_START: break; @@ -1582,6 +1766,8 @@ static int ath9k_htc_ampdu_action(struct ieee80211_hw *hw, ath_err(ath9k_hw_common(priv->ah), "Unknown AMPDU action\n"); } + mutex_unlock(&priv->mutex); + return ret; } @@ -1594,8 +1780,7 @@ static void ath9k_htc_sw_scan_start(struct ieee80211_hw *hw) priv->op_flags |= OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); cancel_work_sync(&priv->ps_work); - if (priv->op_flags & OP_ASSOCIATED) - cancel_delayed_work_sync(&priv->ath9k_ani_work); + ath9k_htc_stop_ani(priv); mutex_unlock(&priv->mutex); } @@ -1604,14 +1789,11 @@ static void ath9k_htc_sw_scan_complete(struct ieee80211_hw *hw) struct ath9k_htc_priv *priv = hw->priv; mutex_lock(&priv->mutex); - ath9k_htc_ps_wakeup(priv); spin_lock_bh(&priv->beacon_lock); priv->op_flags &= ~OP_SCANNING; spin_unlock_bh(&priv->beacon_lock); - if (priv->op_flags & OP_ASSOCIATED) { - ath9k_htc_beacon_config(priv, priv->vif); - ath_start_ani(priv); - } + ath9k_htc_ps_wakeup(priv); + ath9k_htc_vif_reconfig(priv); ath9k_htc_ps_restore(priv); mutex_unlock(&priv->mutex); } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 7a5ffca2195..4a4f27ba96a 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -84,7 +84,9 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb) struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = tx_info->control.sta; + struct ieee80211_vif *vif = tx_info->control.vif; struct ath9k_htc_sta *ista; + struct ath9k_htc_vif *avp; struct ath9k_htc_tx_ctl tx_ctl; enum htc_endpoint_id epid; u16 qnum; @@ -95,18 +97,31 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb) hdr = (struct ieee80211_hdr *) skb->data; fc = hdr->frame_control; - if (tx_info->control.vif && - (struct ath9k_htc_vif *) tx_info->control.vif->drv_priv) - vif_idx = ((struct ath9k_htc_vif *) - tx_info->control.vif->drv_priv)->index; - else - vif_idx = priv->nvifs; + /* + * Find out on which interface this packet has to be + * sent out. + */ + if (vif) { + avp = (struct ath9k_htc_vif *) vif->drv_priv; + vif_idx = avp->index; + } else { + if (!priv->ah->is_monitoring) { + ath_dbg(ath9k_hw_common(priv->ah), ATH_DBG_XMIT, + "VIF is null, but no monitor interface !\n"); + return -EINVAL; + } + vif_idx = priv->mon_vif_idx; + } + + /* + * Find out which station this packet is destined for. + */ if (sta) { ista = (struct ath9k_htc_sta *) sta->drv_priv; sta_idx = ista->index; } else { - sta_idx = 0; + sta_idx = priv->vif_sta_pos[vif_idx]; } memset(&tx_ctl, 0, sizeof(struct ath9k_htc_tx_ctl)); @@ -141,7 +156,7 @@ int ath9k_htc_tx_start(struct ath9k_htc_priv *priv, struct sk_buff *skb) /* CTS-to-self */ if (!(flags & ATH9K_HTC_TX_RTSCTS) && - (priv->op_flags & OP_PROTECT_ENABLE)) + (vif && vif->bss_conf.use_cts_prot)) flags |= ATH9K_HTC_TX_CTSONLY; tx_hdr.flags = cpu_to_be32(flags); @@ -217,6 +232,7 @@ static bool ath9k_htc_check_tx_aggr(struct ath9k_htc_priv *priv, void ath9k_tx_tasklet(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; + struct ieee80211_vif *vif; struct ieee80211_sta *sta; struct ieee80211_hdr *hdr; struct ieee80211_tx_info *tx_info; @@ -228,12 +244,16 @@ void ath9k_tx_tasklet(unsigned long data) hdr = (struct ieee80211_hdr *) skb->data; fc = hdr->frame_control; tx_info = IEEE80211_SKB_CB(skb); + vif = tx_info->control.vif; memset(&tx_info->status, 0, sizeof(tx_info->status)); + if (!vif) + goto send_mac80211; + rcu_read_lock(); - sta = ieee80211_find_sta(priv->vif, hdr->addr1); + sta = ieee80211_find_sta(vif, hdr->addr1); if (!sta) { rcu_read_unlock(); ieee80211_tx_status(priv->hw, skb); @@ -263,6 +283,7 @@ void ath9k_tx_tasklet(unsigned long data) rcu_read_unlock(); + send_mac80211: /* Send status to mac80211 */ ieee80211_tx_status(priv->hw, skb); } @@ -386,7 +407,7 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) */ if (((ah->opmode != NL80211_IFTYPE_AP) && (priv->rxfilter & FIF_PROMISC_IN_BSS)) || - (ah->opmode == NL80211_IFTYPE_MONITOR)) + ah->is_monitoring) rfilt |= ATH9K_RX_FILTER_PROM; if (priv->rxfilter & FIF_CONTROL) @@ -398,8 +419,13 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) else rfilt |= ATH9K_RX_FILTER_BEACON; - if (conf_is_ht(&priv->hw->conf)) + if (conf_is_ht(&priv->hw->conf)) { rfilt |= ATH9K_RX_FILTER_COMP_BAR; + rfilt |= ATH9K_RX_FILTER_UNCOMP_BA_BAR; + } + + if (priv->rxfilter & FIF_PSPOLL) + rfilt |= ATH9K_RX_FILTER_PSPOLL; return rfilt; @@ -412,20 +438,12 @@ u32 ath9k_htc_calcrxfilter(struct ath9k_htc_priv *priv) static void ath9k_htc_opmode_init(struct ath9k_htc_priv *priv) { struct ath_hw *ah = priv->ah; - struct ath_common *common = ath9k_hw_common(ah); - u32 rfilt, mfilt[2]; /* configure rx filter */ rfilt = ath9k_htc_calcrxfilter(priv); ath9k_hw_setrxfilter(ah, rfilt); - /* configure bssid mask */ - ath_hw_setbssidmask(common); - - /* configure operational mode */ - ath9k_hw_setopmode(ah); - /* calculate and install multicast filter */ mfilt[0] = mfilt[1] = ~0; ath9k_hw_setmcastfilter(ah, mfilt[0], mfilt[1]); @@ -576,31 +594,29 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, ath9k_process_rate(hw, rx_status, rxbuf->rxstatus.rs_rate, rxbuf->rxstatus.rs_flags); - if (priv->op_flags & OP_ASSOCIATED) { - if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && - !rxbuf->rxstatus.rs_moreaggr) - ATH_RSSI_LPF(priv->rx.last_rssi, - rxbuf->rxstatus.rs_rssi); + if (rxbuf->rxstatus.rs_rssi != ATH9K_RSSI_BAD && + !rxbuf->rxstatus.rs_moreaggr) + ATH_RSSI_LPF(priv->rx.last_rssi, + rxbuf->rxstatus.rs_rssi); - last_rssi = priv->rx.last_rssi; + last_rssi = priv->rx.last_rssi; - if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) - rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi, - ATH_RSSI_EP_MULTIPLIER); + if (likely(last_rssi != ATH_RSSI_DUMMY_MARKER)) + rxbuf->rxstatus.rs_rssi = ATH_EP_RND(last_rssi, + ATH_RSSI_EP_MULTIPLIER); - if (rxbuf->rxstatus.rs_rssi < 0) - rxbuf->rxstatus.rs_rssi = 0; + if (rxbuf->rxstatus.rs_rssi < 0) + rxbuf->rxstatus.rs_rssi = 0; - if (ieee80211_is_beacon(fc)) - priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi; - } + if (ieee80211_is_beacon(fc)) + priv->ah->stats.avgbrssi = rxbuf->rxstatus.rs_rssi; rx_status->mactime = be64_to_cpu(rxbuf->rxstatus.rs_tstamp); rx_status->band = hw->conf.channel->band; rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = rxbuf->rxstatus.rs_rssi + ATH_DEFAULT_NOISE_FLOOR; rx_status->antenna = rxbuf->rxstatus.rs_antenna; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; return true; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f66c882a39e..79aec983279 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -140,6 +140,21 @@ static struct ieee80211_rate ath9k_legacy_rates[] = { RATE(540, 0x0c, 0), }; +#ifdef CONFIG_MAC80211_LEDS +static const struct ieee80211_tpt_blink ath9k_tpt_blink[] = { + { .throughput = 0 * 1024, .blink_time = 334 }, + { .throughput = 1 * 1024, .blink_time = 260 }, + { .throughput = 5 * 1024, .blink_time = 220 }, + { .throughput = 10 * 1024, .blink_time = 190 }, + { .throughput = 20 * 1024, .blink_time = 170 }, + { .throughput = 50 * 1024, .blink_time = 150 }, + { .throughput = 70 * 1024, .blink_time = 130 }, + { .throughput = 100 * 1024, .blink_time = 110 }, + { .throughput = 200 * 1024, .blink_time = 80 }, + { .throughput = 300 * 1024, .blink_time = 50 }, +}; +#endif + static void ath9k_deinit_softc(struct ath_softc *sc); /* @@ -731,6 +746,13 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc, u16 subsysid, ath9k_init_txpower_limits(sc); +#ifdef CONFIG_MAC80211_LEDS + /* must be initialized before ieee80211_register_hw */ + sc->led_cdev.default_trigger = ieee80211_create_tpt_led_trigger(sc->hw, + IEEE80211_TPT_LEDTRIG_FL_RADIO, ath9k_tpt_blink, + ARRAY_SIZE(ath9k_tpt_blink)); +#endif + /* Register with mac80211 */ error = ieee80211_register_hw(hw); if (error) diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index c75d40fb86f..5efc869d65f 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c @@ -891,7 +891,7 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) struct ath_common *common = ath9k_hw_common(ah); if (!(ints & ATH9K_INT_GLOBAL)) - ath9k_hw_enable_interrupts(ah); + ath9k_hw_disable_interrupts(ah); ath_dbg(common, ATH_DBG_INTERRUPT, "0x%x => 0x%x\n", omask, ints); @@ -969,7 +969,8 @@ void ath9k_hw_set_interrupts(struct ath_hw *ah, enum ath9k_int ints) REG_CLR_BIT(ah, AR_IMR_S5, AR_IMR_S5_TIM_TIMER); } - ath9k_hw_enable_interrupts(ah); + if (ints & ATH9K_INT_GLOBAL) + ath9k_hw_enable_interrupts(ah); return; } diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a71550049d8..2e228aada1a 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -910,6 +910,8 @@ void ath_radio_enable(struct ath_softc *sc, struct ieee80211_hw *hw) ath9k_hw_set_gpio(ah, ah->led_pin, 0); ieee80211_wake_queues(hw); + ieee80211_queue_delayed_work(hw, &sc->hw_pll_work, HZ/2); + out: spin_unlock_bh(&sc->sc_pcu_lock); @@ -923,6 +925,8 @@ void ath_radio_disable(struct ath_softc *sc, struct ieee80211_hw *hw) int r; ath9k_ps_wakeup(sc); + cancel_delayed_work_sync(&sc->hw_pll_work); + spin_lock_bh(&sc->sc_pcu_lock); ieee80211_stop_queues(hw); @@ -1142,8 +1146,7 @@ mutex_unlock: return r; } -static int ath9k_tx(struct ieee80211_hw *hw, - struct sk_buff *skb) +static void ath9k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ath_softc *sc = hw->priv; struct ath_common *common = ath9k_hw_common(sc->sc_ah); @@ -1200,10 +1203,9 @@ static int ath9k_tx(struct ieee80211_hw *hw, goto exit; } - return 0; + return; exit: dev_kfree_skb_any(skb); - return 0; } static void ath9k_stop(struct ieee80211_hw *hw) @@ -1214,9 +1216,6 @@ static void ath9k_stop(struct ieee80211_hw *hw) mutex_lock(&sc->mutex); - if (led_blink) - cancel_delayed_work_sync(&sc->ath_led_blink_work); - cancel_delayed_work_sync(&sc->tx_complete_work); cancel_delayed_work_sync(&sc->hw_pll_work); cancel_work_sync(&sc->paprd_work); @@ -2131,7 +2130,7 @@ static void ath9k_flush(struct ieee80211_hw *hw, bool drop) { #define ATH_FLUSH_TIMEOUT 60 /* ms */ struct ath_softc *sc = hw->priv; - struct ath_txq *txq; + struct ath_txq *txq = NULL; struct ath_hw *ah = sc->sc_ah; struct ath_common *common = ath9k_hw_common(ah); int i, j, npend = 0; diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index daf171d2f61..cb559e345b8 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -983,7 +983,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ATH_DEFAULT_NOISE_FLOOR + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; return 0; } diff --git a/drivers/net/wireless/ath/ath9k/reg.h b/drivers/net/wireless/ath/ath9k/reg.h index 64b226a78b2..8fa8acfde62 100644 --- a/drivers/net/wireless/ath/ath9k/reg.h +++ b/drivers/net/wireless/ath/ath9k/reg.h @@ -878,6 +878,7 @@ enum ath_usb_dev { AR9280_USB = 1, /* AR7010 + AR9280, UB94 */ AR9287_USB = 2, /* AR7010 + AR9287, UB95 */ + STORAGE_DEVICE = 3, }; #define AR_DEVID_7010(_ah) \ diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c index dc862f5e116..d3d24904f62 100644 --- a/drivers/net/wireless/ath/ath9k/wmi.c +++ b/drivers/net/wireless/ath/ath9k/wmi.c @@ -123,12 +123,8 @@ void ath9k_deinit_wmi(struct ath9k_htc_priv *priv) void ath9k_swba_tasklet(unsigned long data) { struct ath9k_htc_priv *priv = (struct ath9k_htc_priv *)data; - struct ath_common *common = ath9k_hw_common(priv->ah); - - ath_dbg(common, ATH_DBG_WMI, "SWBA Event received\n"); ath9k_htc_swba(priv, priv->wmi->beacon_pending); - } void ath9k_fatal_work(struct work_struct *work) diff --git a/drivers/net/wireless/ath/carl9170/carl9170.h b/drivers/net/wireless/ath/carl9170/carl9170.h index 420d437f958..c6a5fae634a 100644 --- a/drivers/net/wireless/ath/carl9170/carl9170.h +++ b/drivers/net/wireless/ath/carl9170/carl9170.h @@ -534,7 +534,7 @@ void carl9170_rx(struct ar9170 *ar, void *buf, unsigned int len); void carl9170_handle_command_response(struct ar9170 *ar, void *buf, u32 len); /* TX */ -int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb); void carl9170_tx_janitor(struct work_struct *work); void carl9170_tx_process_status(struct ar9170 *ar, const struct carl9170_rsp *cmd); diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 6f41e21d3a1..0ef70b6fc51 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -1339,7 +1339,7 @@ err_unlock_rcu: return false; } -int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ar9170 *ar = hw->priv; struct ieee80211_tx_info *info; @@ -1373,12 +1373,11 @@ int carl9170_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) } carl9170_tx(ar); - return NETDEV_TX_OK; + return; err_free: ar->tx_dropped++; dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } void carl9170_tx_scheduler(struct ar9170 *ar) diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index 537732e5964..f82c400be28 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -118,6 +118,8 @@ static struct usb_device_id carl9170_usb_ids[] = { { USB_DEVICE(0x057c, 0x8402) }, /* Qwest/Actiontec 802AIN Wireless N USB Network Adapter */ { USB_DEVICE(0x1668, 0x1200) }, + /* Airlive X.USB a/b/g/n */ + { USB_DEVICE(0x1b75, 0x9170) }, /* terminate */ {} diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 22bc9f17f63..57eb5b64973 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -3203,7 +3203,7 @@ static void b43_tx_work(struct work_struct *work) mutex_unlock(&wl->mutex); } -static int b43_op_tx(struct ieee80211_hw *hw, +static void b43_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct b43_wl *wl = hw_to_b43_wl(hw); @@ -3211,14 +3211,12 @@ static int b43_op_tx(struct ieee80211_hw *hw, if (unlikely(skb->len < 2 + 2 + 6)) { /* Too short, this can't be a valid frame. */ dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + return; } B43_WARN_ON(skb_shinfo(skb)->nr_frags); skb_queue_tail(&wl->tx_queue, skb); ieee80211_queue_work(wl->hw, &wl->tx_work); - - return NETDEV_TX_OK; } static void b43_qos_params_upload(struct b43_wldev *dev, diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index ab81ed8b19d..9f5a3c99323 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -430,9 +430,9 @@ static void b43_radio_init2055_post(struct b43_wldev *dev) bool workaround = false; if (sprom->revision < 4) - workaround = (binfo->vendor != PCI_VENDOR_ID_BROADCOM || - binfo->type != 0x46D || - binfo->rev < 0x41); + workaround = (binfo->vendor != PCI_VENDOR_ID_BROADCOM && + binfo->type == 0x46D && + binfo->rev >= 0x41); else workaround = !(sprom->boardflags2_lo & B43_BFL2_RXBB_INT_REG_DIS); @@ -1281,17 +1281,17 @@ static void b43_nphy_gain_ctrl_workarounds(struct b43_wldev *dev) B43_NPHY_TABLE_DATALO, tmp); } } + } - b43_nphy_set_rf_sequence(dev, 5, - rfseq_events, rfseq_delays, 3); - b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, - ~B43_NPHY_OVER_DGAIN_CCKDGECV & 0xFFFF, - 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); + b43_nphy_set_rf_sequence(dev, 5, + rfseq_events, rfseq_delays, 3); + b43_phy_maskset(dev, B43_NPHY_OVER_DGAIN1, + ~B43_NPHY_OVER_DGAIN_CCKDGECV & 0xFFFF, + 0x5A << B43_NPHY_OVER_DGAIN_CCKDGECV_SHIFT); - if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - b43_phy_maskset(dev, B43_PHY_N(0xC5D), - 0xFF80, 4); - } + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) + b43_phy_maskset(dev, B43_PHY_N(0xC5D), + 0xFF80, 4); } } @@ -2128,7 +2128,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf, save_regs_phy[5] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); save_regs_phy[6] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B1S0); save_regs_phy[7] = b43_phy_read(dev, B43_NPHY_TXF_40CO_B32S1); - } else if (dev->phy.rev == 2) { + } else { save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); save_regs_phy[2] = b43_phy_read(dev, B43_NPHY_AFECTL_OVER); @@ -2179,7 +2179,7 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, u8 type, s32 *buf, b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[5]); b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, save_regs_phy[6]); b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, save_regs_phy[7]); - } else if (dev->phy.rev == 2) { + } else { b43_phy_write(dev, B43_NPHY_AFECTL_C1, save_regs_phy[0]); b43_phy_write(dev, B43_NPHY_AFECTL_C2, save_regs_phy[1]); b43_phy_write(dev, B43_NPHY_AFECTL_OVER, save_regs_phy[2]); diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index dc8ef09a855..c42b2acea24 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -1097,6 +1097,1080 @@ static const u32 b43_ntab_tmap[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000, }; +/* static tables, PHY revision >= 3 */ +static const u32 b43_ntab_framestruct_r3[] = { + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x09804506, 0x00100030, 0x09804507, 0x00100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a0c, 0x00100004, 0x01000a0d, 0x00100024, + 0x0980450e, 0x00100034, 0x0980450f, 0x00100034, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, + 0x1980c506, 0x00100030, 0x21810506, 0x00100030, + 0x21810506, 0x00100030, 0x01800504, 0x00100030, + 0x11808505, 0x00100030, 0x29814507, 0x01100030, + 0x00000a04, 0x00100000, 0x11008a05, 0x00100020, + 0x21810506, 0x00100030, 0x21810506, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x1980c50e, 0x00100038, 0x2181050e, 0x00100038, + 0x2181050e, 0x00100038, 0x0180050c, 0x00100038, + 0x1180850d, 0x00100038, 0x2981450f, 0x01100038, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x1980c506, 0x00100030, 0x1980c506, 0x00100030, + 0x11808504, 0x00100030, 0x3981ca05, 0x00100030, + 0x29814507, 0x01100030, 0x00000000, 0x00000000, + 0x10008a04, 0x00100000, 0x3981ca05, 0x00100030, + 0x1980c506, 0x00100030, 0x29814507, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a0c, 0x00100008, 0x01000a0d, 0x00100028, + 0x1980c50e, 0x00100038, 0x1980c50e, 0x00100038, + 0x1180850c, 0x00100038, 0x3981ca0d, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x10008a0c, 0x00100008, 0x3981ca0d, 0x00100038, + 0x1980c50e, 0x00100038, 0x2981450f, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x02001405, 0x00100040, + 0x0b004a06, 0x01900060, 0x13008a06, 0x01900060, + 0x13008a06, 0x01900060, 0x43020a04, 0x00100060, + 0x1b00ca05, 0x00100060, 0x23010a07, 0x01500060, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x13008a06, 0x01900060, 0x13008a06, 0x01900060, + 0x23010a07, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x0200140d, 0x00100050, + 0x0b004a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x13008a0e, 0x01900070, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x23010a0f, 0x01500070, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x0b004a06, 0x01900060, 0x0b004a06, 0x01900060, + 0x5b02ca04, 0x00100060, 0x3b01d405, 0x00100060, + 0x23010a07, 0x01500060, 0x00000000, 0x00000000, + 0x5802d404, 0x00100000, 0x3b01d405, 0x00100060, + 0x0b004a06, 0x01900060, 0x23010a07, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x5002940c, 0x00100010, 0x3201940d, 0x00100050, + 0x0b004a0e, 0x01900070, 0x0b004a0e, 0x01900070, + 0x5b02ca0c, 0x00100070, 0x3b01d40d, 0x00100070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x5802d40c, 0x00100010, 0x3b01d40d, 0x00100070, + 0x0b004a0e, 0x01900070, 0x23010a0f, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x000f4800, 0x62031405, 0x00100040, + 0x53028a06, 0x01900060, 0x53028a07, 0x01900060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x000f4808, 0x6203140d, 0x00100048, + 0x53028a0e, 0x01900068, 0x53028a0f, 0x01900068, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100004, 0x11008a0d, 0x00100024, + 0x1980c50e, 0x00100034, 0x2181050e, 0x00100034, + 0x2181050e, 0x00100034, 0x0180050c, 0x00100038, + 0x1180850d, 0x00100038, 0x1181850d, 0x00100038, + 0x2981450f, 0x01100038, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000a0c, 0x00100008, 0x11008a0d, 0x00100028, + 0x2181050e, 0x00100038, 0x2181050e, 0x00100038, + 0x1181850d, 0x00100038, 0x2981450f, 0x01100038, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x08004a04, 0x00100000, 0x01000a05, 0x00100020, + 0x0180c506, 0x00100030, 0x0180c506, 0x00100030, + 0x2180c50c, 0x00100030, 0x49820a0d, 0x0016a130, + 0x41824a0d, 0x0016a130, 0x2981450f, 0x01100030, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x2000ca0c, 0x00100000, 0x49820a0d, 0x0016a130, + 0x1980c50e, 0x00100030, 0x41824a0d, 0x0016a130, + 0x2981450f, 0x01100030, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100008, 0x0200140d, 0x00100048, + 0x0b004a0e, 0x01900068, 0x13008a0e, 0x01900068, + 0x13008a0e, 0x01900068, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x1b014a0d, 0x00100070, + 0x23010a0f, 0x01500070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x13008a0e, 0x01900070, 0x13008a0e, 0x01900070, + 0x1b014a0d, 0x00100070, 0x23010a0f, 0x01500070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x50029404, 0x00100000, 0x32019405, 0x00100040, + 0x03004a06, 0x01900060, 0x03004a06, 0x01900060, + 0x6b030a0c, 0x00100060, 0x4b02140d, 0x0016a160, + 0x4302540d, 0x0016a160, 0x23010a0f, 0x01500060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x6b03140c, 0x00100060, 0x4b02140d, 0x0016a160, + 0x0b004a0e, 0x01900060, 0x4302540d, 0x0016a160, + 0x23010a0f, 0x01500060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x53028a06, 0x01900060, 0x5b02ca06, 0x01900060, + 0x5b02ca06, 0x01900060, 0x43020a04, 0x00100060, + 0x1b00ca05, 0x00100060, 0x53028a07, 0x0190c060, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x53028a0e, 0x01900070, 0x5b02ca0e, 0x01900070, + 0x5b02ca0e, 0x01900070, 0x43020a0c, 0x00100070, + 0x1b00ca0d, 0x00100070, 0x53028a0f, 0x0190c070, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x40021404, 0x00100000, 0x1a00d405, 0x00100040, + 0x5b02ca06, 0x01900060, 0x5b02ca06, 0x01900060, + 0x53028a07, 0x0190c060, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x4002140c, 0x00100010, 0x1a00d40d, 0x00100050, + 0x5b02ca0e, 0x01900070, 0x5b02ca0e, 0x01900070, + 0x53028a0f, 0x0190c070, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_ntab_pilot_r3[] = { + 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, 0xff08, + 0xff08, 0xff08, 0x80d5, 0x80d5, 0x80d5, 0x80d5, + 0x80d5, 0x80d5, 0x80d5, 0x80d5, 0xff0a, 0xff82, + 0xffa0, 0xff28, 0xffff, 0xffff, 0xffff, 0xffff, + 0xff82, 0xffa0, 0xff28, 0xff0a, 0xffff, 0xffff, + 0xffff, 0xffff, 0xf83f, 0xfa1f, 0xfa97, 0xfab5, + 0xf2bd, 0xf0bf, 0xffff, 0xffff, 0xf017, 0xf815, + 0xf215, 0xf095, 0xf035, 0xf01d, 0xffff, 0xffff, + 0xff08, 0xff02, 0xff80, 0xff20, 0xff08, 0xff02, + 0xff80, 0xff20, 0xf01f, 0xf817, 0xfa15, 0xf295, + 0xf0b5, 0xf03d, 0xffff, 0xffff, 0xf82a, 0xfa0a, + 0xfa82, 0xfaa0, 0xf2a8, 0xf0aa, 0xffff, 0xffff, + 0xf002, 0xf800, 0xf200, 0xf080, 0xf020, 0xf008, + 0xffff, 0xffff, 0xf00a, 0xf802, 0xfa00, 0xf280, + 0xf0a0, 0xf028, 0xffff, 0xffff, +}; + +static const u32 b43_ntab_tmap_r3[] = { + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, + 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, + 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, + 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, + 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, + 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, + 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, + 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, + 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, + 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, + 0x22222222, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x11111111, + 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, + 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, + 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, + 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, + 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, + 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, + 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, + 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, + 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, + 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, + 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, + 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, + 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, + 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, + 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_intlevel_r3[] = { + 0x00802070, 0x0671188d, 0x0a60192c, 0x0a300e46, + 0x00c1188d, 0x080024d2, 0x00000070, +}; + +static const u32 b43_ntab_tdtrn_r3[] = { + 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, + 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, + 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, + 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, + 0x061c061c, 0x0050ee68, 0xf592fe36, 0xfe5212f6, + 0x00000c38, 0xfe5212f6, 0xf592fe36, 0x0050ee68, + 0x061c061c, 0xee680050, 0xfe36f592, 0x12f6fe52, + 0x0c380000, 0x12f6fe52, 0xfe36f592, 0xee680050, + 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, + 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, + 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, + 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, + 0x05e305e3, 0x004def0c, 0xf5f3fe47, 0xfe611246, + 0x00000bc7, 0xfe611246, 0xf5f3fe47, 0x004def0c, + 0x05e305e3, 0xef0c004d, 0xfe47f5f3, 0x1246fe61, + 0x0bc70000, 0x1246fe61, 0xfe47f5f3, 0xef0c004d, + 0xfa58fa58, 0xf895043b, 0xff4c09c0, 0xfbc6ffa8, + 0xfb84f384, 0x0798f6f9, 0x05760122, 0x058409f6, + 0x0b500000, 0x05b7f542, 0x08860432, 0x06ddfee7, + 0xfb84f384, 0xf9d90664, 0xf7e8025c, 0x00fff7bd, + 0x05a805a8, 0xf7bd00ff, 0x025cf7e8, 0x0664f9d9, + 0xf384fb84, 0xfee706dd, 0x04320886, 0xf54205b7, + 0x00000b50, 0x09f60584, 0x01220576, 0xf6f90798, + 0xf384fb84, 0xffa8fbc6, 0x09c0ff4c, 0x043bf895, + 0x02d402d4, 0x07de0270, 0xfc96079c, 0xf90afe94, + 0xfe00ff2c, 0x02d4065d, 0x092a0096, 0x0014fbb8, + 0xfd2cfd2c, 0x076afb3c, 0x0096f752, 0xf991fd87, + 0xfb2c0200, 0xfeb8f960, 0x08e0fc96, 0x049802a8, + 0xfd2cfd2c, 0x02a80498, 0xfc9608e0, 0xf960feb8, + 0x0200fb2c, 0xfd87f991, 0xf7520096, 0xfb3c076a, + 0xfd2cfd2c, 0xfbb80014, 0x0096092a, 0x065d02d4, + 0xff2cfe00, 0xfe94f90a, 0x079cfc96, 0x027007de, + 0x02d402d4, 0x027007de, 0x079cfc96, 0xfe94f90a, + 0xff2cfe00, 0x065d02d4, 0x0096092a, 0xfbb80014, + 0xfd2cfd2c, 0xfb3c076a, 0xf7520096, 0xfd87f991, + 0x0200fb2c, 0xf960feb8, 0xfc9608e0, 0x02a80498, + 0xfd2cfd2c, 0x049802a8, 0x08e0fc96, 0xfeb8f960, + 0xfb2c0200, 0xf991fd87, 0x0096f752, 0x076afb3c, + 0xfd2cfd2c, 0x0014fbb8, 0x092a0096, 0x02d4065d, + 0xfe00ff2c, 0xf90afe94, 0xfc96079c, 0x07de0270, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, + 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, + 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, + 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, + 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, + 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, + 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, + 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, + 0x062a0000, 0xfefa0759, 0x08b80908, 0xf396fc2d, + 0xf9d6045c, 0xfc4ef608, 0xf748f596, 0x07b207bf, + 0x062a062a, 0xf84ef841, 0xf748f596, 0x03b209f8, + 0xf9d6045c, 0x0c6a03d3, 0x08b80908, 0x0106f8a7, + 0x062a0000, 0xfefaf8a7, 0x08b8f6f8, 0xf39603d3, + 0xf9d6fba4, 0xfc4e09f8, 0xf7480a6a, 0x07b2f841, + 0x062af9d6, 0xf84e07bf, 0xf7480a6a, 0x03b2f608, + 0xf9d6fba4, 0x0c6afc2d, 0x08b8f6f8, 0x01060759, + 0x061c061c, 0xff30009d, 0xffb21141, 0xfd87fb54, + 0xf65dfe59, 0x02eef99e, 0x0166f03c, 0xfff809b6, + 0x000008a4, 0x000af42b, 0x00eff577, 0xfa840bf2, + 0xfc02ff51, 0x08260f67, 0xfff0036f, 0x0842f9c3, + 0x00000000, 0x063df7be, 0xfc910010, 0xf099f7da, + 0x00af03fe, 0xf40e057c, 0x0a89ff11, 0x0bd5fff6, + 0xf75c0000, 0xf64a0008, 0x0fc4fe9a, 0x0662fd12, + 0x01a709a3, 0x04ac0279, 0xeebf004e, 0xff6300d0, + 0xf9e4f9e4, 0x00d0ff63, 0x004eeebf, 0x027904ac, + 0x09a301a7, 0xfd120662, 0xfe9a0fc4, 0x0008f64a, + 0x0000f75c, 0xfff60bd5, 0xff110a89, 0x057cf40e, + 0x03fe00af, 0xf7daf099, 0x0010fc91, 0xf7be063d, + 0x00000000, 0xf9c30842, 0x036ffff0, 0x0f670826, + 0xff51fc02, 0x0bf2fa84, 0xf57700ef, 0xf42b000a, + 0x08a40000, 0x09b6fff8, 0xf03c0166, 0xf99e02ee, + 0xfe59f65d, 0xfb54fd87, 0x1141ffb2, 0x009dff30, + 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, + 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, + 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, + 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, + 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, + 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, + 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, + 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, + 0x05e30000, 0xff060705, 0x085408a0, 0xf425fc59, + 0xfa1d042a, 0xfc78f67a, 0xf7acf60e, 0x075a0766, + 0x05e305e3, 0xf8a6f89a, 0xf7acf60e, 0x03880986, + 0xfa1d042a, 0x0bdb03a7, 0x085408a0, 0x00faf8fb, + 0x05e30000, 0xff06f8fb, 0x0854f760, 0xf42503a7, + 0xfa1dfbd6, 0xfc780986, 0xf7ac09f2, 0x075af89a, + 0x05e3fa1d, 0xf8a60766, 0xf7ac09f2, 0x0388f67a, + 0xfa1dfbd6, 0x0bdbfc59, 0x0854f760, 0x00fa0705, + 0xfa58fa58, 0xf8f0fe00, 0x0448073d, 0xfdc9fe46, + 0xf9910258, 0x089d0407, 0xfd5cf71a, 0x02affde0, + 0x083e0496, 0xff5a0740, 0xff7afd97, 0x00fe01f1, + 0x0009082e, 0xfa94ff75, 0xfecdf8ea, 0xffb0f693, + 0xfd2cfa58, 0x0433ff16, 0xfba405dd, 0xfa610341, + 0x06a606cb, 0x0039fd2d, 0x0677fa97, 0x01fa05e0, + 0xf896003e, 0x075a068b, 0x012cfc3e, 0xfa23f98d, + 0xfc7cfd43, 0xff90fc0d, 0x01c10982, 0x00c601d6, + 0xfd2cfd2c, 0x01d600c6, 0x098201c1, 0xfc0dff90, + 0xfd43fc7c, 0xf98dfa23, 0xfc3e012c, 0x068b075a, + 0x003ef896, 0x05e001fa, 0xfa970677, 0xfd2d0039, + 0x06cb06a6, 0x0341fa61, 0x05ddfba4, 0xff160433, + 0xfa58fd2c, 0xf693ffb0, 0xf8eafecd, 0xff75fa94, + 0x082e0009, 0x01f100fe, 0xfd97ff7a, 0x0740ff5a, + 0x0496083e, 0xfde002af, 0xf71afd5c, 0x0407089d, + 0x0258f991, 0xfe46fdc9, 0x073d0448, 0xfe00f8f0, + 0xfd2cfd2c, 0xfce00500, 0xfc09fddc, 0xfe680157, + 0x04c70571, 0xfc3aff21, 0xfcd70228, 0x056d0277, + 0x0200fe00, 0x0022f927, 0xfe3c032b, 0xfc44ff3c, + 0x03e9fbdb, 0x04570313, 0x04c9ff5c, 0x000d03b8, + 0xfa580000, 0xfbe900d2, 0xf9d0fe0b, 0x0125fdf9, + 0x042501bf, 0x0328fa2b, 0xffa902f0, 0xfa250157, + 0x0200fe00, 0x03740438, 0xff0405fd, 0x030cfe52, + 0x0037fb39, 0xff6904c5, 0x04f8fd23, 0xfd31fc1b, + 0xfd2cfd2c, 0xfc1bfd31, 0xfd2304f8, 0x04c5ff69, + 0xfb390037, 0xfe52030c, 0x05fdff04, 0x04380374, + 0xfe000200, 0x0157fa25, 0x02f0ffa9, 0xfa2b0328, + 0x01bf0425, 0xfdf90125, 0xfe0bf9d0, 0x00d2fbe9, + 0x0000fa58, 0x03b8000d, 0xff5c04c9, 0x03130457, + 0xfbdb03e9, 0xff3cfc44, 0x032bfe3c, 0xf9270022, + 0xfe000200, 0x0277056d, 0x0228fcd7, 0xff21fc3a, + 0x057104c7, 0x0157fe68, 0xfddcfc09, 0x0500fce0, + 0xfd2cfd2c, 0x0500fce0, 0xfddcfc09, 0x0157fe68, + 0x057104c7, 0xff21fc3a, 0x0228fcd7, 0x0277056d, + 0xfe000200, 0xf9270022, 0x032bfe3c, 0xff3cfc44, + 0xfbdb03e9, 0x03130457, 0xff5c04c9, 0x03b8000d, + 0x0000fa58, 0x00d2fbe9, 0xfe0bf9d0, 0xfdf90125, + 0x01bf0425, 0xfa2b0328, 0x02f0ffa9, 0x0157fa25, + 0xfe000200, 0x04380374, 0x05fdff04, 0xfe52030c, + 0xfb390037, 0x04c5ff69, 0xfd2304f8, 0xfc1bfd31, + 0xfd2cfd2c, 0xfd31fc1b, 0x04f8fd23, 0xff6904c5, + 0x0037fb39, 0x030cfe52, 0xff0405fd, 0x03740438, + 0x0200fe00, 0xfa250157, 0xffa902f0, 0x0328fa2b, + 0x042501bf, 0x0125fdf9, 0xf9d0fe0b, 0xfbe900d2, + 0xfa580000, 0x000d03b8, 0x04c9ff5c, 0x04570313, + 0x03e9fbdb, 0xfc44ff3c, 0xfe3c032b, 0x0022f927, + 0x0200fe00, 0x056d0277, 0xfcd70228, 0xfc3aff21, + 0x04c70571, 0xfe680157, 0xfc09fddc, 0xfce00500, + 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, + 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, + 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, + 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, + 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, + 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, + 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, + 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, + 0x05a80000, 0xff1006be, 0x0800084a, 0xf49cfc7e, + 0xfa580400, 0xfc9cf6da, 0xf800f672, 0x0710071c, + 0x05a805a8, 0xf8f0f8e4, 0xf800f672, 0x03640926, + 0xfa580400, 0x0b640382, 0x0800084a, 0x00f0f942, + 0x05a80000, 0xff10f942, 0x0800f7b6, 0xf49c0382, + 0xfa58fc00, 0xfc9c0926, 0xf800098e, 0x0710f8e4, + 0x05a8fa58, 0xf8f0071c, 0xf800098e, 0x0364f6da, + 0xfa58fc00, 0x0b64fc7e, 0x0800f7b6, 0x00f006be, +}; + +static const u32 b43_ntab_noisevar0_r3[] = { + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, +}; + +static const u32 b43_ntab_noisevar1_r3[] = { + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, + 0x02110211, 0x0000014d, 0x02110211, 0x0000014d, +}; + +static const u16 b43_ntab_mcs_r3[] = { + 0x0000, 0x0008, 0x000a, 0x0010, 0x0012, 0x0019, + 0x001a, 0x001c, 0x0080, 0x0088, 0x008a, 0x0090, + 0x0092, 0x0099, 0x009a, 0x009c, 0x0100, 0x0108, + 0x010a, 0x0110, 0x0112, 0x0119, 0x011a, 0x011c, + 0x0180, 0x0188, 0x018a, 0x0190, 0x0192, 0x0199, + 0x019a, 0x019c, 0x0000, 0x0098, 0x00a0, 0x00a8, + 0x009a, 0x00a2, 0x00aa, 0x0120, 0x0128, 0x0128, + 0x0130, 0x0138, 0x0138, 0x0140, 0x0122, 0x012a, + 0x012a, 0x0132, 0x013a, 0x013a, 0x0142, 0x01a8, + 0x01b0, 0x01b8, 0x01b0, 0x01b8, 0x01c0, 0x01c8, + 0x01c0, 0x01c8, 0x01d0, 0x01d0, 0x01d8, 0x01aa, + 0x01b2, 0x01ba, 0x01b2, 0x01ba, 0x01c2, 0x01ca, + 0x01c2, 0x01ca, 0x01d2, 0x01d2, 0x01da, 0x0001, + 0x0002, 0x0004, 0x0009, 0x000c, 0x0011, 0x0014, + 0x0018, 0x0020, 0x0021, 0x0022, 0x0024, 0x0081, + 0x0082, 0x0084, 0x0089, 0x008c, 0x0091, 0x0094, + 0x0098, 0x00a0, 0x00a1, 0x00a2, 0x00a4, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, 0x0007, + 0x0007, 0x0007, +}; + +static const u32 b43_ntab_tdi20a0_r3[] = { + 0x00091226, 0x000a1429, 0x000b56ad, 0x000c58b0, + 0x000d5ab3, 0x000e9cb6, 0x000f9eba, 0x0000c13d, + 0x00020301, 0x00030504, 0x00040708, 0x0005090b, + 0x00064b8e, 0x00095291, 0x000a5494, 0x000b9718, + 0x000c9927, 0x000d9b2a, 0x000edd2e, 0x000fdf31, + 0x000101b4, 0x000243b7, 0x000345bb, 0x000447be, + 0x00058982, 0x00068c05, 0x00099309, 0x000a950c, + 0x000bd78f, 0x000cd992, 0x000ddb96, 0x000f1d99, + 0x00005fa8, 0x0001422c, 0x0002842f, 0x00038632, + 0x00048835, 0x0005ca38, 0x0006ccbc, 0x0009d3bf, + 0x000b1603, 0x000c1806, 0x000d1a0a, 0x000e1c0d, + 0x000f5e10, 0x00008093, 0x00018297, 0x0002c49a, + 0x0003c680, 0x0004c880, 0x00060b00, 0x00070d00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi20a1_r3[] = { + 0x00014b26, 0x00028d29, 0x000393ad, 0x00049630, + 0x0005d833, 0x0006da36, 0x00099c3a, 0x000a9e3d, + 0x000bc081, 0x000cc284, 0x000dc488, 0x000f068b, + 0x0000488e, 0x00018b91, 0x0002d214, 0x0003d418, + 0x0004d6a7, 0x000618aa, 0x00071aae, 0x0009dcb1, + 0x000b1eb4, 0x000c0137, 0x000d033b, 0x000e053e, + 0x000f4702, 0x00008905, 0x00020c09, 0x0003128c, + 0x0004148f, 0x00051712, 0x00065916, 0x00091b19, + 0x000a1d28, 0x000b5f2c, 0x000c41af, 0x000d43b2, + 0x000e85b5, 0x000f87b8, 0x0000c9bc, 0x00024cbf, + 0x00035303, 0x00045506, 0x0005978a, 0x0006998d, + 0x00095b90, 0x000a5d93, 0x000b9f97, 0x000c821a, + 0x000d8400, 0x000ec600, 0x000fc800, 0x00010a00, + 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a0_r3[] = { + 0x0011a346, 0x00136ccf, 0x0014f5d9, 0x001641e2, + 0x0017cb6b, 0x00195475, 0x001b2383, 0x001cad0c, + 0x001e7616, 0x0000821f, 0x00020ba8, 0x0003d4b2, + 0x00056447, 0x00072dd0, 0x0008b6da, 0x000a02e3, + 0x000b8c6c, 0x000d15f6, 0x0011e484, 0x0013ae0d, + 0x00153717, 0x00168320, 0x00180ca9, 0x00199633, + 0x001b6548, 0x001ceed1, 0x001eb7db, 0x0000c3e4, + 0x00024d6d, 0x000416f7, 0x0005a585, 0x00076f0f, + 0x0008f818, 0x000a4421, 0x000bcdab, 0x000d9734, + 0x00122649, 0x0013efd2, 0x001578dc, 0x0016c4e5, + 0x00184e6e, 0x001a17f8, 0x001ba686, 0x001d3010, + 0x001ef999, 0x00010522, 0x00028eac, 0x00045835, + 0x0005e74a, 0x0007b0d3, 0x00093a5d, 0x000a85e6, + 0x000c0f6f, 0x000dd8f9, 0x00126787, 0x00143111, + 0x0015ba9a, 0x00170623, 0x00188fad, 0x001a5936, + 0x001be84b, 0x001db1d4, 0x001f3b5e, 0x000146e7, + 0x00031070, 0x000499fa, 0x00062888, 0x0007f212, + 0x00097b9b, 0x000ac7a4, 0x000c50ae, 0x000e1a37, + 0x0012a94c, 0x001472d5, 0x0015fc5f, 0x00174868, + 0x0018d171, 0x001a9afb, 0x001c2989, 0x001df313, + 0x001f7c9c, 0x000188a5, 0x000351af, 0x0004db38, + 0x0006aa4d, 0x000833d7, 0x0009bd60, 0x000b0969, + 0x000c9273, 0x000e5bfc, 0x00132a8a, 0x0014b414, + 0x00163d9d, 0x001789a6, 0x001912b0, 0x001adc39, + 0x001c6bce, 0x001e34d8, 0x001fbe61, 0x0001ca6a, + 0x00039374, 0x00051cfd, 0x0006ec0b, 0x00087515, + 0x0009fe9e, 0x000b4aa7, 0x000cd3b1, 0x000e9d3a, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_tdi40a1_r3[] = { + 0x001edb36, 0x000129ca, 0x0002b353, 0x00047cdd, + 0x0005c8e6, 0x000791ef, 0x00091bf9, 0x000aaa07, + 0x000c3391, 0x000dfd1a, 0x00120923, 0x0013d22d, + 0x00155c37, 0x0016eacb, 0x00187454, 0x001a3dde, + 0x001b89e7, 0x001d12f0, 0x001f1cfa, 0x00016b88, + 0x00033492, 0x0004be1b, 0x00060a24, 0x0007d32e, + 0x00095d38, 0x000aec4c, 0x000c7555, 0x000e3edf, + 0x00124ae8, 0x001413f1, 0x0015a37b, 0x00172c89, + 0x0018b593, 0x001a419c, 0x001bcb25, 0x001d942f, + 0x001f63b9, 0x0001ad4d, 0x00037657, 0x0004c260, + 0x00068be9, 0x000814f3, 0x0009a47c, 0x000b2d8a, + 0x000cb694, 0x000e429d, 0x00128c26, 0x001455b0, + 0x0015e4ba, 0x00176e4e, 0x0018f758, 0x001a8361, + 0x001c0cea, 0x001dd674, 0x001fa57d, 0x0001ee8b, + 0x0003b795, 0x0005039e, 0x0006cd27, 0x000856b1, + 0x0009e5c6, 0x000b6f4f, 0x000cf859, 0x000e8462, + 0x00130deb, 0x00149775, 0x00162603, 0x0017af8c, + 0x00193896, 0x001ac49f, 0x001c4e28, 0x001e17b2, + 0x0000a6c7, 0x00023050, 0x0003f9da, 0x00054563, + 0x00070eec, 0x00089876, 0x000a2704, 0x000bb08d, + 0x000d3a17, 0x001185a0, 0x00134f29, 0x0014d8b3, + 0x001667c8, 0x0017f151, 0x00197adb, 0x001b0664, + 0x001c8fed, 0x001e5977, 0x0000e805, 0x0002718f, + 0x00043b18, 0x000586a1, 0x0007502b, 0x0008d9b4, + 0x000a68c9, 0x000bf252, 0x000dbbdc, 0x0011c7e5, + 0x001390ee, 0x00151a78, 0x0016a906, 0x00183290, + 0x0019bc19, 0x001b4822, 0x001cd12c, 0x001e9ab5, + 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_pilotlt_r3[] = { + 0x76540213, 0x62407351, 0x76543210, 0x76540213, + 0x76540213, 0x76430521, +}; + +static const u32 b43_ntab_channelest_r3[] = { + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x44444444, 0x44444444, 0x44444444, 0x44444444, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, +}; + +static const u8 b43_ntab_framelookup_r3[] = { + 0x02, 0x04, 0x14, 0x14, 0x03, 0x05, 0x16, 0x16, + 0x0a, 0x0c, 0x1c, 0x1c, 0x0b, 0x0d, 0x1e, 0x1e, + 0x06, 0x08, 0x18, 0x18, 0x07, 0x09, 0x1a, 0x1a, + 0x0e, 0x10, 0x20, 0x28, 0x0f, 0x11, 0x22, 0x2a, +}; + +static const u8 b43_ntab_estimatepowerlt0_r3[] = { + 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, + 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, + 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, + 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, + 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, + 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, + 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, +}; + +static const u8 b43_ntab_estimatepowerlt1_r3[] = { + 0x55, 0x54, 0x54, 0x53, 0x52, 0x52, 0x51, 0x51, + 0x50, 0x4f, 0x4f, 0x4e, 0x4e, 0x4d, 0x4c, 0x4c, + 0x4b, 0x4a, 0x49, 0x49, 0x48, 0x47, 0x46, 0x46, + 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, 0x40, 0x3f, + 0x3e, 0x3d, 0x3c, 0x3a, 0x39, 0x38, 0x37, 0x36, + 0x35, 0x33, 0x32, 0x31, 0x2f, 0x2e, 0x2c, 0x2b, + 0x29, 0x27, 0x25, 0x23, 0x21, 0x1f, 0x1d, 0x1a, + 0x18, 0x15, 0x12, 0x0e, 0x0b, 0x07, 0x02, 0xfd, +}; + +static const u8 b43_ntab_adjustpower0_r3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u8 b43_ntab_adjustpower1_r3[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static const u32 b43_ntab_gainctl0_r3[] = { + 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, + 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, + 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, + 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, + 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, + 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, + 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, + 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, + 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, + 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, + 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, + 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, + 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, + 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, + 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, + 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, + 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, + 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, + 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, + 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, + 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, + 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, + 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, + 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, + 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, + 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, + 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, + 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, + 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, + 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, + 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, + 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, +}; + +static const u32 b43_ntab_gainctl1_r3[] = { + 0x5bf70044, 0x5bf70042, 0x5bf70040, 0x5bf7003e, + 0x5bf7003c, 0x5bf7003b, 0x5bf70039, 0x5bf70037, + 0x5bf70036, 0x5bf70034, 0x5bf70033, 0x5bf70031, + 0x5bf70030, 0x5ba70044, 0x5ba70042, 0x5ba70040, + 0x5ba7003e, 0x5ba7003c, 0x5ba7003b, 0x5ba70039, + 0x5ba70037, 0x5ba70036, 0x5ba70034, 0x5ba70033, + 0x5b770044, 0x5b770042, 0x5b770040, 0x5b77003e, + 0x5b77003c, 0x5b77003b, 0x5b770039, 0x5b770037, + 0x5b770036, 0x5b770034, 0x5b770033, 0x5b770031, + 0x5b770030, 0x5b77002f, 0x5b77002d, 0x5b77002c, + 0x5b470044, 0x5b470042, 0x5b470040, 0x5b47003e, + 0x5b47003c, 0x5b47003b, 0x5b470039, 0x5b470037, + 0x5b470036, 0x5b470034, 0x5b470033, 0x5b470031, + 0x5b470030, 0x5b47002f, 0x5b47002d, 0x5b47002c, + 0x5b47002b, 0x5b47002a, 0x5b270044, 0x5b270042, + 0x5b270040, 0x5b27003e, 0x5b27003c, 0x5b27003b, + 0x5b270039, 0x5b270037, 0x5b270036, 0x5b270034, + 0x5b270033, 0x5b270031, 0x5b270030, 0x5b27002f, + 0x5b170044, 0x5b170042, 0x5b170040, 0x5b17003e, + 0x5b17003c, 0x5b17003b, 0x5b170039, 0x5b170037, + 0x5b170036, 0x5b170034, 0x5b170033, 0x5b170031, + 0x5b170030, 0x5b17002f, 0x5b17002d, 0x5b17002c, + 0x5b17002b, 0x5b17002a, 0x5b170028, 0x5b170027, + 0x5b170026, 0x5b170025, 0x5b170024, 0x5b170023, + 0x5b070044, 0x5b070042, 0x5b070040, 0x5b07003e, + 0x5b07003c, 0x5b07003b, 0x5b070039, 0x5b070037, + 0x5b070036, 0x5b070034, 0x5b070033, 0x5b070031, + 0x5b070030, 0x5b07002f, 0x5b07002d, 0x5b07002c, + 0x5b07002b, 0x5b07002a, 0x5b070028, 0x5b070027, + 0x5b070026, 0x5b070025, 0x5b070024, 0x5b070023, + 0x5b070022, 0x5b070021, 0x5b070020, 0x5b07001f, + 0x5b07001e, 0x5b07001d, 0x5b07001d, 0x5b07001c, +}; + +static const u32 b43_ntab_iqlt0_r3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u32 b43_ntab_iqlt1_r3[] = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +static const u16 b43_ntab_loftlt0_r3[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +static const u16 b43_ntab_loftlt1_r3[] = { + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, +}; + +/* TX gain tables */ const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, @@ -1813,7 +2887,6 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, #define ntab_upload(dev, offset, data) do { \ b43_ntab_write_bulk(dev, offset, offset##_SIZE, data); \ } while (0) - void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev) { /* Static tables */ @@ -1847,10 +2920,39 @@ void b43_nphy_rev0_1_2_tables_init(struct b43_wldev *dev) ntab_upload(dev, B43_NTAB_C1_LOFEEDTH, b43_ntab_loftlt1); } +#define ntab_upload_r3(dev, offset, data) do { \ + b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ + } while (0) void b43_nphy_rev3plus_tables_init(struct b43_wldev *dev) { /* Static tables */ - /* TODO */ + ntab_upload_r3(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload_r3(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload_r3(dev, B43_NTAB_TMAP_R3, b43_ntab_tmap_r3); + ntab_upload_r3(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload_r3(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload_r3(dev, B43_NTAB_NOISEVAR0_R3, b43_ntab_noisevar0_r3); + ntab_upload_r3(dev, B43_NTAB_NOISEVAR1_R3, b43_ntab_noisevar1_r3); + ntab_upload_r3(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload_r3(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload_r3(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload_r3(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload_r3(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload_r3(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload_r3(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload_r3(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + ntab_upload_r3(dev, B43_NTAB_C0_ESTPLT_R3, + b43_ntab_estimatepowerlt0_r3); + ntab_upload_r3(dev, B43_NTAB_C1_ESTPLT_R3, + b43_ntab_estimatepowerlt1_r3); + ntab_upload_r3(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload_r3(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload_r3(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload_r3(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload_r3(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload_r3(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload_r3(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload_r3(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); /* Volatile tables */ /* TODO */ diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 4ec593ba3ee..016a480b2dc 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -109,6 +109,33 @@ b43_nphy_get_chantabent_rev3(struct b43_wldev *dev, u16 freq); #define B43_NTAB_C1_LOFEEDTH B43_NTAB16(0x1B, 0x1C0) /* Local Oscillator Feed Through Lookup Table Core 1 */ #define B43_NTAB_C1_LOFEEDTH_SIZE 128 +/* Static N-PHY tables, PHY revision >= 3 */ +#define B43_NTAB_FRAMESTRUCT_R3 B43_NTAB32(10, 000) /* frame struct */ +#define B43_NTAB_PILOT_R3 B43_NTAB16(11, 000) /* pilot */ +#define B43_NTAB_TMAP_R3 B43_NTAB32(12, 000) /* TM AP */ +#define B43_NTAB_INTLEVEL_R3 B43_NTAB32(13, 000) /* INT LV */ +#define B43_NTAB_TDTRN_R3 B43_NTAB32(14, 000) /* TD TRN */ +#define B43_NTAB_NOISEVAR0_R3 B43_NTAB32(16, 000) /* noise variance 0 */ +#define B43_NTAB_NOISEVAR1_R3 B43_NTAB32(16, 128) /* noise variance 1 */ +#define B43_NTAB_MCS_R3 B43_NTAB16(18, 000) /* MCS */ +#define B43_NTAB_TDI20A0_R3 B43_NTAB32(19, 128) /* TDI 20/0 */ +#define B43_NTAB_TDI20A1_R3 B43_NTAB32(19, 256) /* TDI 20/1 */ +#define B43_NTAB_TDI40A0_R3 B43_NTAB32(19, 640) /* TDI 40/0 */ +#define B43_NTAB_TDI40A1_R3 B43_NTAB32(19, 768) /* TDI 40/1 */ +#define B43_NTAB_PILOTLT_R3 B43_NTAB32(20, 000) /* PLT lookup */ +#define B43_NTAB_CHANEST_R3 B43_NTAB32(22, 000) /* channel estimate */ +#define B43_NTAB_FRAMELT_R3 B43_NTAB8 (24, 000) /* frame lookup */ +#define B43_NTAB_C0_ESTPLT_R3 B43_NTAB8 (26, 000) /* estimated power lookup 0 */ +#define B43_NTAB_C1_ESTPLT_R3 B43_NTAB8 (27, 000) /* estimated power lookup 1 */ +#define B43_NTAB_C0_ADJPLT_R3 B43_NTAB8 (26, 064) /* adjusted power lookup 0 */ +#define B43_NTAB_C1_ADJPLT_R3 B43_NTAB8 (27, 064) /* adjusted power lookup 1 */ +#define B43_NTAB_C0_GAINCTL_R3 B43_NTAB32(26, 192) /* gain control lookup 0 */ +#define B43_NTAB_C1_GAINCTL_R3 B43_NTAB32(27, 192) /* gain control lookup 1 */ +#define B43_NTAB_C0_IQLT_R3 B43_NTAB32(26, 320) /* I/Q lookup 0 */ +#define B43_NTAB_C1_IQLT_R3 B43_NTAB32(27, 320) /* I/Q lookup 1 */ +#define B43_NTAB_C0_LOFEEDTH_R3 B43_NTAB16(26, 448) /* Local Oscillator Feed Through lookup 0 */ +#define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ + #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index e6b0528f3b5..e5be381c17b 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -32,6 +32,36 @@ #include "dma.h" #include "pio.h" +static const struct b43_tx_legacy_rate_phy_ctl_entry b43_tx_legacy_rate_phy_ctl[] = { + { B43_CCK_RATE_1MB, 0x0, 0x0 }, + { B43_CCK_RATE_2MB, 0x0, 0x1 }, + { B43_CCK_RATE_5MB, 0x0, 0x2 }, + { B43_CCK_RATE_11MB, 0x0, 0x3 }, + { B43_OFDM_RATE_6MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_BPSK }, + { B43_OFDM_RATE_9MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_BPSK }, + { B43_OFDM_RATE_12MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QPSK }, + { B43_OFDM_RATE_18MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QPSK }, + { B43_OFDM_RATE_24MB, B43_TXH_PHY1_CRATE_1_2, B43_TXH_PHY1_MODUL_QAM16 }, + { B43_OFDM_RATE_36MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM16 }, + { B43_OFDM_RATE_48MB, B43_TXH_PHY1_CRATE_2_3, B43_TXH_PHY1_MODUL_QAM64 }, + { B43_OFDM_RATE_54MB, B43_TXH_PHY1_CRATE_3_4, B43_TXH_PHY1_MODUL_QAM64 }, +}; + +static const struct b43_tx_legacy_rate_phy_ctl_entry * +b43_tx_legacy_rate_phy_ctl_ent(u8 bitrate) +{ + const struct b43_tx_legacy_rate_phy_ctl_entry *e; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(b43_tx_legacy_rate_phy_ctl); i++) { + e = &(b43_tx_legacy_rate_phy_ctl[i]); + if (e->bitrate == bitrate) + return e; + } + + B43_WARN_ON(1); + return NULL; +} /* Extract the bitrate index out of a CCK PLCP header. */ static int b43_plcp_get_bitrate_idx_cck(struct b43_plcp_hdr6 *plcp) @@ -145,6 +175,34 @@ void b43_generate_plcp_hdr(struct b43_plcp_hdr4 *plcp, } } +static u16 b43_generate_tx_phy_ctl1(struct b43_wldev *dev, u8 bitrate) +{ + const struct b43_phy *phy = &dev->phy; + const struct b43_tx_legacy_rate_phy_ctl_entry *e; + u16 control = 0; + u16 bw; + + if (phy->type == B43_PHYTYPE_LP) + bw = B43_TXH_PHY1_BW_20; + else /* FIXME */ + bw = B43_TXH_PHY1_BW_20; + + if (0) { /* FIXME: MIMO */ + } else if (b43_is_cck_rate(bitrate) && phy->type != B43_PHYTYPE_LP) { + control = bw; + } else { + control = bw; + e = b43_tx_legacy_rate_phy_ctl_ent(bitrate); + if (e) { + control |= e->coding_rate; + control |= e->modulation; + } + control |= B43_TXH_PHY1_MODE_SISO; + } + + return control; +} + static u8 b43_calc_fallback_rate(u8 bitrate) { switch (bitrate) { @@ -437,6 +495,14 @@ int b43_generate_txhdr(struct b43_wldev *dev, extra_ft |= B43_TXH_EFT_RTSFB_OFDM; else extra_ft |= B43_TXH_EFT_RTSFB_CCK; + + if (rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS && + phy->type == B43_PHYTYPE_N) { + txhdr->phy_ctl1_rts = cpu_to_le16( + b43_generate_tx_phy_ctl1(dev, rts_rate)); + txhdr->phy_ctl1_rts_fb = cpu_to_le16( + b43_generate_tx_phy_ctl1(dev, rts_rate_fb)); + } } /* Magic cookie */ @@ -445,6 +511,13 @@ int b43_generate_txhdr(struct b43_wldev *dev, else txhdr->new_format.cookie = cpu_to_le16(cookie); + if (phy->type == B43_PHYTYPE_N) { + txhdr->phy_ctl1 = + cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate)); + txhdr->phy_ctl1_fb = + cpu_to_le16(b43_generate_tx_phy_ctl1(dev, rate_fb)); + } + /* Apply the bitfields */ txhdr->mac_ctl = cpu_to_le32(mac_ctl); txhdr->phy_ctl = cpu_to_le16(phy_ctl); @@ -652,7 +725,7 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_TSFT; + status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; diff --git a/drivers/net/wireless/b43/xmit.h b/drivers/net/wireless/b43/xmit.h index d4cf9b390af..42debb5cd6f 100644 --- a/drivers/net/wireless/b43/xmit.h +++ b/drivers/net/wireless/b43/xmit.h @@ -73,6 +73,12 @@ struct b43_txhdr { } __packed; } __packed; +struct b43_tx_legacy_rate_phy_ctl_entry { + u8 bitrate; + u16 coding_rate; + u16 modulation; +}; + /* MAC TX control */ #define B43_TXH_MAC_USEFBR 0x10000000 /* Use fallback rate for this AMPDU */ #define B43_TXH_MAC_KEYIDX 0x0FF00000 /* Security key index */ diff --git a/drivers/net/wireless/b43legacy/main.c b/drivers/net/wireless/b43legacy/main.c index 1f11e1670bf..c7fd73e3ad7 100644 --- a/drivers/net/wireless/b43legacy/main.c +++ b/drivers/net/wireless/b43legacy/main.c @@ -2442,8 +2442,8 @@ static int b43legacy_rng_init(struct b43legacy_wl *wl) return err; } -static int b43legacy_op_tx(struct ieee80211_hw *hw, - struct sk_buff *skb) +static void b43legacy_op_tx(struct ieee80211_hw *hw, + struct sk_buff *skb) { struct b43legacy_wl *wl = hw_to_b43legacy_wl(hw); struct b43legacy_wldev *dev = wl->current_dev; @@ -2466,7 +2466,6 @@ out: /* Drop the packet. */ dev_kfree_skb_any(skb); } - return NETDEV_TX_OK; } static int b43legacy_op_conf_tx(struct ieee80211_hw *hw, u16 queue, diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index 7d177d97f1f..3a95541708a 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -572,7 +572,7 @@ void b43legacy_rx(struct b43legacy_wldev *dev, status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_TSFT; + status.flag |= RX_FLAG_MACTIME_MPDU; } chanid = (chanstat & B43legacy_RX_CHAN_ID) >> diff --git a/drivers/net/wireless/iwlegacy/Kconfig b/drivers/net/wireless/iwlegacy/Kconfig new file mode 100644 index 00000000000..2a45dd44cc1 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/Kconfig @@ -0,0 +1,116 @@ +config IWLWIFI_LEGACY + tristate "Intel Wireless Wifi legacy devices" + depends on PCI && MAC80211 + select FW_LOADER + select NEW_LEDS + select LEDS_CLASS + select LEDS_TRIGGERS + select MAC80211_LEDS + +menu "Debugging Options" + depends on IWLWIFI_LEGACY + +config IWLWIFI_LEGACY_DEBUG + bool "Enable full debugging output in 4965 and 3945 drivers" + depends on IWLWIFI_LEGACY + ---help--- + This option will enable debug tracing output for the iwlwifilegacy + drivers. + + This will result in the kernel module being ~100k larger. You can + control which debug output is sent to the kernel log by setting the + value in + + /sys/class/net/wlan0/device/debug_level + + This entry will only exist if this option is enabled. + + To set a value, simply echo an 8-byte hex value to the same file: + + % echo 0x43fff > /sys/class/net/wlan0/device/debug_level + + You can find the list of debug mask values in: + drivers/net/wireless/iwlwifilegacy/iwl-debug.h + + If this is your first time using this driver, you should say Y here + as the debug information can assist others in helping you resolve + any problems you may encounter. + +config IWLWIFI_LEGACY_DEBUGFS + bool "4965 and 3945 debugfs support" + depends on IWLWIFI_LEGACY && MAC80211_DEBUGFS + ---help--- + Enable creation of debugfs files for the iwlwifilegacy drivers. This + is a low-impact option that allows getting insight into the + driver's state at runtime. + +config IWLWIFI_LEGACY_DEVICE_TRACING + bool "iwlwifilegacy legacy device access tracing" + depends on IWLWIFI_LEGACY + depends on EVENT_TRACING + help + Say Y here to trace all commands, including TX frames and IO + accesses, sent to the device. If you say yes, iwlwifilegacy will + register with the ftrace framework for event tracing and dump + all this information to the ringbuffer, you may need to + increase the ringbuffer size. See the ftrace documentation + for more information. + + When tracing is not enabled, this option still has some + (though rather small) overhead. + + If unsure, say Y so we can help you better when problems + occur. +endmenu + +config IWL4965 + tristate "Intel Wireless WiFi 4965AGN (iwl4965)" + depends on IWLWIFI_LEGACY + ---help--- + This option enables support for + + Select to build the driver supporting the: + + Intel Wireless WiFi Link 4965AGN + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + The microcode is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read <file:Documentation/kbuild/modules.txt>. The + module will be called iwl4965. + +config IWL3945 + tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" + depends on IWLWIFI_LEGACY + ---help--- + Select to build the driver supporting the: + + Intel PRO/Wireless 3945ABG/BG Network Connection + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + The microcode is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read <file:Documentation/kbuild/modules.txt>. The + module will be called iwl3945. diff --git a/drivers/net/wireless/iwlegacy/Makefile b/drivers/net/wireless/iwlegacy/Makefile new file mode 100644 index 00000000000..d56aeb38c21 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/Makefile @@ -0,0 +1,25 @@ +obj-$(CONFIG_IWLWIFI_LEGACY) += iwl-legacy.o +iwl-legacy-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o +iwl-legacy-objs += iwl-rx.o iwl-tx.o iwl-sta.o +iwl-legacy-objs += iwl-scan.o iwl-led.o +iwl-legacy-$(CONFIG_IWLWIFI_LEGACY_DEBUGFS) += iwl-debugfs.o +iwl-legacy-$(CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING) += iwl-devtrace.o + +iwl-legacy-objs += $(iwl-legacy-m) + +CFLAGS_iwl-devtrace.o := -I$(src) + +# 4965 +obj-$(CONFIG_IWL4965) += iwl4965.o +iwl4965-objs := iwl-4965.o iwl4965-base.o iwl-4965-rs.o iwl-4965-led.o +iwl4965-objs += iwl-4965-ucode.o iwl-4965-tx.o +iwl4965-objs += iwl-4965-lib.o iwl-4965-rx.o iwl-4965-calib.o +iwl4965-objs += iwl-4965-sta.o iwl-4965-eeprom.o +iwl4965-$(CONFIG_IWLWIFI_LEGACY_DEBUGFS) += iwl-4965-debugfs.o + +# 3945 +obj-$(CONFIG_IWL3945) += iwl3945.o +iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o +iwl3945-$(CONFIG_IWLWIFI_LEGACY_DEBUGFS) += iwl-3945-debugfs.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.c b/drivers/net/wireless/iwlegacy/iwl-3945-debugfs.c index ef0835b01b6..cfabb38793a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.c +++ b/drivers/net/wireless/iwlegacy/iwl-3945-debugfs.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -60,12 +60,13 @@ ssize_t iwl3945_ucode_rx_stats_read(struct file *file, int bufsz = sizeof(struct iwl39_statistics_rx_phy) * 40 + sizeof(struct iwl39_statistics_rx_non_phy) * 40 + 400; ssize_t ret; - struct iwl39_statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct iwl39_statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, + *max_ofdm; struct iwl39_statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; struct iwl39_statistics_rx_non_phy *general, *accum_general; struct iwl39_statistics_rx_non_phy *delta_general, *max_general; - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); @@ -335,7 +336,7 @@ ssize_t iwl3945_ucode_tx_stats_read(struct file *file, ssize_t ret; struct iwl39_statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); @@ -434,7 +435,7 @@ ssize_t iwl3945_ucode_general_stats_read(struct file *file, struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; struct iwl39_statistics_div *div, *accum_div, *delta_div, *max_div; - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; buf = kzalloc(bufsz, GFP_KERNEL); diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.h b/drivers/net/wireless/iwlegacy/iwl-3945-debugfs.h index 70809c53c21..8fef4b32b44 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-debugfs.h +++ b/drivers/net/wireless/iwlegacy/iwl-3945-debugfs.h @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ #include "iwl-core.h" #include "iwl-debug.h" -#ifdef CONFIG_IWLWIFI_DEBUGFS +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS ssize_t iwl3945_ucode_rx_stats_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos); ssize_t iwl3945_ucode_tx_stats_read(struct file *file, char __user *user_buf, diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-fh.h b/drivers/net/wireless/iwlegacy/iwl-3945-fh.h index 2c9ed2b502a..836c9919f82 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-fh.h +++ b/drivers/net/wireless/iwlegacy/iwl-3945-fh.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -185,4 +185,3 @@ struct iwl3945_tfd { #endif /* __iwl_3945_fh_h__ */ - diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h b/drivers/net/wireless/iwlegacy/iwl-3945-hw.h index 65b5834da28..779d3cb86e2 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-hw.h +++ b/drivers/net/wireless/iwlegacy/iwl-3945-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -164,12 +164,11 @@ struct iwl3945_eeprom { /* * Per-channel regulatory data. * - * Each channel that *might* be supported by 3945 or 4965 has a fixed location + * Each channel that *might* be supported by 3945 has a fixed location * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory * txpower (MSB). * - * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) - * channels (only for 4965, not supported by 3945) appear later in the EEPROM. + * Entries immediately below are for 20 MHz channel width. * * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 */ diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.c b/drivers/net/wireless/iwlegacy/iwl-3945-led.c index dc7c3a4167a..abd923558d4 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.c +++ b/drivers/net/wireless/iwlegacy/iwl-3945-led.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -56,7 +56,7 @@ static int iwl3945_send_led_cmd(struct iwl_priv *priv, .callback = NULL, }; - return iwl_send_cmd(priv, &cmd); + return iwl_legacy_send_cmd(priv, &cmd); } const struct iwl_led_ops iwl3945_led_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-led.h b/drivers/net/wireless/iwlegacy/iwl-3945-led.h index ce990adc51e..96716276eb0 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-led.h +++ b/drivers/net/wireless/iwlegacy/iwl-3945-led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlegacy/iwl-3945-rs.c index 1f3e7e34fbc..977bd2477c6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c +++ b/drivers/net/wireless/iwlegacy/iwl-3945-rs.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -89,7 +89,7 @@ static struct iwl3945_tpt_entry iwl3945_tpt_table_g[] = { }; #define IWL_RATE_MAX_WINDOW 62 -#define IWL_RATE_FLUSH (3*HZ) +#define IWL_RATE_FLUSH (3*HZ) #define IWL_RATE_WIN_FLUSH (HZ/2) #define IWL39_RATE_HIGH_TH 11520 #define IWL_SUCCESS_UP_TH 8960 @@ -394,18 +394,18 @@ out: IWL_DEBUG_INFO(priv, "leave\n"); } -static void *rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +static void *iwl3945_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { return hw->priv; } /* rate scale requires free function to be implemented */ -static void rs_free(void *priv) +static void iwl3945_rs_free(void *priv) { return; } -static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) +static void *iwl3945_rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) { struct iwl3945_rs_sta *rs_sta; struct iwl3945_sta_priv *psta = (void *) sta->drv_priv; @@ -423,7 +423,7 @@ static void *rs_alloc_sta(void *iwl_priv, struct ieee80211_sta *sta, gfp_t gfp) return rs_sta; } -static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, +static void iwl3945_rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, void *priv_sta) { struct iwl3945_rs_sta *rs_sta = priv_sta; @@ -438,12 +438,12 @@ static void rs_free_sta(void *iwl_priv, struct ieee80211_sta *sta, /** - * rs_tx_status - Update rate control values based on Tx results + * iwl3945_rs_tx_status - Update rate control values based on Tx results * * NOTE: Uses iwl_priv->retry_rate for the # of retries attempted by * the hardware for each rate. */ -static void rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband, +static void iwl3945_rs_tx_status(void *priv_rate, struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta, struct sk_buff *skb) { @@ -612,7 +612,7 @@ static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta, } /** - * rs_get_rate - find the rate for the requested packet + * iwl3945_rs_get_rate - find the rate for the requested packet * * Returns the ieee80211_rate structure allocated by the driver. * @@ -627,7 +627,7 @@ static u16 iwl3945_get_adjacent_rate(struct iwl3945_rs_sta *rs_sta, * rate table and must reference the driver allocated rate table * */ -static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, +static void iwl3945_rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, struct ieee80211_tx_rate_control *txrc) { struct ieee80211_supported_band *sband = txrc->sband; @@ -644,7 +644,7 @@ static void rs_get_rate(void *priv_r, struct ieee80211_sta *sta, u32 fail_count; s8 scale_action = 0; unsigned long flags; - u16 rate_mask = sta ? sta->supp_rates[sband->band] : 0; + u16 rate_mask; s8 max_rate_idx = -1; struct iwl_priv *priv __maybe_unused = (struct iwl_priv *)priv_r; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -899,7 +899,8 @@ static void iwl3945_remove_debugfs(void *priv, void *priv_sta) * the station is added. Since mac80211 calls this function before a * station is added we ignore it. */ -static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, +static void iwl3945_rs_rate_init_stub(void *priv_r, + struct ieee80211_supported_band *sband, struct ieee80211_sta *sta, void *priv_sta) { } @@ -907,13 +908,13 @@ static void rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sba static struct rate_control_ops rs_ops = { .module = NULL, .name = RS_NAME, - .tx_status = rs_tx_status, - .get_rate = rs_get_rate, - .rate_init = rs_rate_init_stub, - .alloc = rs_alloc, - .free = rs_free, - .alloc_sta = rs_alloc_sta, - .free_sta = rs_free_sta, + .tx_status = iwl3945_rs_tx_status, + .get_rate = iwl3945_rs_get_rate, + .rate_init = iwl3945_rs_rate_init_stub, + .alloc = iwl3945_rs_alloc, + .free = iwl3945_rs_free, + .alloc_sta = iwl3945_rs_alloc_sta, + .free_sta = iwl3945_rs_free_sta, #ifdef CONFIG_MAC80211_DEBUGFS .add_sta_debugfs = iwl3945_add_debugfs, .remove_sta_debugfs = iwl3945_remove_debugfs, @@ -991,5 +992,3 @@ void iwl3945_rate_control_unregister(void) { ieee80211_rate_control_unregister(&rs_ops); } - - diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlegacy/iwl-3945.c index f4cd9370e7f..d096dc28204 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.c +++ b/drivers/net/wireless/iwlegacy/iwl-3945.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -51,7 +51,6 @@ #include "iwl-led.h" #include "iwl-3945-led.h" #include "iwl-3945-debugfs.h" -#include "iwl-legacy.h" #define IWL_DECLARE_RATE_INFO(r, ip, in, rp, rn, pp, np) \ [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ @@ -172,14 +171,14 @@ void iwl3945_disable_events(struct iwl_priv *priv) return; } - disable_ptr = iwl_read_targ_mem(priv, base + (4 * sizeof(u32))); - array_size = iwl_read_targ_mem(priv, base + (5 * sizeof(u32))); + disable_ptr = iwl_legacy_read_targ_mem(priv, base + (4 * sizeof(u32))); + array_size = iwl_legacy_read_targ_mem(priv, base + (5 * sizeof(u32))); if (IWL_EVT_DISABLE && (array_size == IWL_EVT_DISABLE_SIZE)) { IWL_DEBUG_INFO(priv, "Disabling selected uCode log events at 0x%x\n", disable_ptr); for (i = 0; i < IWL_EVT_DISABLE_SIZE; i++) - iwl_write_targ_mem(priv, + iwl_legacy_write_targ_mem(priv, disable_ptr + (i * sizeof(u32)), evt_disable[i]); @@ -202,7 +201,7 @@ static int iwl3945_hwrate_to_plcp_idx(u8 plcp) return -1; } -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG #define TX_STATUS_ENTRY(x) case TX_3945_STATUS_FAIL_ ## x: return #x static const char *iwl3945_get_tx_fail_reason(u32 status) @@ -255,7 +254,7 @@ int iwl3945_rs_next_rate(struct iwl_priv *priv, int rate) break; case IEEE80211_BAND_2GHZ: if (!(priv->_3945.sta_supp_rates & IWL_OFDM_RATES_MASK) && - iwl_is_associated(priv, IWL_RXON_CTX_BSS)) { + iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) { if (rate == IWL_RATE_11M_INDEX) next_rate = IWL_RATE_5M_INDEX; } @@ -285,8 +284,9 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv, BUG_ON(txq_id == IWL39_CMD_QUEUE_NUM); - for (index = iwl_queue_inc_wrap(index, q->n_bd); q->read_ptr != index; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + for (index = iwl_legacy_queue_inc_wrap(index, q->n_bd); + q->read_ptr != index; + q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd)) { tx_info = &txq->txb[txq->q.read_ptr]; ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb); @@ -294,10 +294,10 @@ static void iwl3945_tx_queue_reclaim(struct iwl_priv *priv, priv->cfg->ops->lib->txq_free_tfd(priv, txq); } - if (iwl_queue_space(q) > q->low_mark && (txq_id >= 0) && + if (iwl_legacy_queue_space(q) > q->low_mark && (txq_id >= 0) && (txq_id != IWL39_CMD_QUEUE_NUM) && priv->mac80211_registered) - iwl_wake_queue(priv, txq); + iwl_legacy_wake_queue(priv, txq); } /** @@ -317,7 +317,7 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv, int rate_idx; int fail; - if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) { + if ((index >= txq->q.n_bd) || (iwl_legacy_queue_used(&txq->q, index) == 0)) { IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d " "is out of range [0-%d] %d %d\n", txq_id, index, txq->q.n_bd, txq->q.write_ptr, @@ -363,12 +363,7 @@ static void iwl3945_rx_reply_tx(struct iwl_priv *priv, * RX handler implementations * *****************************************************************************/ -#ifdef CONFIG_IWLWIFI_DEBUGFS -/* - * based on the assumption of all statistics counter are in DWORD - * FIXME: This function is for debugging, do not deal with - * the case of counters roll-over. - */ +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS static void iwl3945_accumulative_statistics(struct iwl_priv *priv, __le32 *stats) { @@ -410,10 +405,10 @@ void iwl3945_hw_rx_statistics(struct iwl_priv *priv, IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n", (int)sizeof(struct iwl3945_notif_statistics), le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK); -#ifdef CONFIG_IWLWIFI_DEBUGFS +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS iwl3945_accumulative_statistics(priv, (__le32 *)&pkt->u.raw); #endif - iwl_recover_from_statistics(priv, pkt); + iwl_legacy_recover_from_statistics(priv, pkt); memcpy(&priv->_3945.statistics, pkt->u.raw, sizeof(priv->_3945.statistics)); } @@ -425,7 +420,7 @@ void iwl3945_reply_statistics(struct iwl_priv *priv, __le32 *flag = (__le32 *)&pkt->u.raw; if (le32_to_cpu(*flag) & UCODE_STATISTICS_CLEAR_MSK) { -#ifdef CONFIG_IWLWIFI_DEBUGFS +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS memset(&priv->_3945.accum_statistics, 0, sizeof(struct iwl3945_notif_statistics)); memset(&priv->_3945.delta_statistics, 0, @@ -496,14 +491,14 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv, } if (!iwl3945_mod_params.sw_crypto) - iwl_set_decrypted_flag(priv, + iwl_legacy_set_decrypted_flag(priv, (struct ieee80211_hdr *)rxb_addr(rxb), le32_to_cpu(rx_end->status), stats); skb_add_rx_frag(skb, 0, rxb->page, (void *)rx_hdr->payload - (void *)pkt, len); - iwl_update_stats(priv, false, fc, len); + iwl_legacy_update_stats(priv, false, fc, len); memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); ieee80211_rx(priv->hw, skb); @@ -576,7 +571,8 @@ static void iwl3945_rx_reply_rx(struct iwl_priv *priv, rx_status.signal, rx_status.signal, rx_status.rate_idx); - iwl_dbg_log_rx_data_frame(priv, le16_to_cpu(rx_hdr->len), header); + iwl_legacy_dbg_log_rx_data_frame(priv, le16_to_cpu(rx_hdr->len), + header); if (network_packet) { priv->_3945.last_beacon_time = @@ -744,7 +740,7 @@ static u8 iwl3945_sync_sta(struct iwl_priv *priv, int sta_id, u16 tx_rate) station->sta.sta.modify_mask = STA_MODIFY_TX_RATE_MSK; station->sta.rate_n_flags = cpu_to_le16(tx_rate); station->sta.mode = STA_CONTROL_MODIFY_MSK; - iwl_send_add_sta(priv, &station->sta, CMD_ASYNC); + iwl_legacy_send_add_sta(priv, &station->sta, CMD_ASYNC); spin_unlock_irqrestore(&priv->sta_lock, flags_spin); IWL_DEBUG_RATE(priv, "SCALE sync station %d to rate %d\n", @@ -759,7 +755,7 @@ static void iwl3945_set_pwr_vmain(struct iwl_priv *priv) * to set power to V_AUX, do if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) { - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + iwl_legacy_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VAUX, ~APMG_PS_CTRL_MSK_PWR_SRC); @@ -769,7 +765,7 @@ static void iwl3945_set_pwr_vmain(struct iwl_priv *priv) } */ - iwl_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + iwl_legacy_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~APMG_PS_CTRL_MSK_PWR_SRC); @@ -779,10 +775,11 @@ static void iwl3945_set_pwr_vmain(struct iwl_priv *priv) static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) { - iwl_write_direct32(priv, FH39_RCSR_RBD_BASE(0), rxq->bd_dma); - iwl_write_direct32(priv, FH39_RCSR_RPTR_ADDR(0), rxq->rb_stts_dma); - iwl_write_direct32(priv, FH39_RCSR_WPTR(0), 0); - iwl_write_direct32(priv, FH39_RCSR_CONFIG(0), + iwl_legacy_write_direct32(priv, FH39_RCSR_RBD_BASE(0), rxq->bd_dma); + iwl_legacy_write_direct32(priv, FH39_RCSR_RPTR_ADDR(0), + rxq->rb_stts_dma); + iwl_legacy_write_direct32(priv, FH39_RCSR_WPTR(0), 0); + iwl_legacy_write_direct32(priv, FH39_RCSR_CONFIG(0), FH39_RCSR_RX_CONFIG_REG_VAL_DMA_CHNL_EN_ENABLE | FH39_RCSR_RX_CONFIG_REG_VAL_RDRBD_EN_ENABLE | FH39_RCSR_RX_CONFIG_REG_BIT_WR_STTS_EN | @@ -793,7 +790,7 @@ static int iwl3945_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) FH39_RCSR_RX_CONFIG_REG_VAL_MSG_MODE_FH); /* fake read to flush all prev I/O */ - iwl_read_direct32(priv, FH39_RSSR_CTRL); + iwl_legacy_read_direct32(priv, FH39_RSSR_CTRL); return 0; } @@ -802,23 +799,23 @@ static int iwl3945_tx_reset(struct iwl_priv *priv) { /* bypass mode */ - iwl_write_prph(priv, ALM_SCD_MODE_REG, 0x2); + iwl_legacy_write_prph(priv, ALM_SCD_MODE_REG, 0x2); /* RA 0 is active */ - iwl_write_prph(priv, ALM_SCD_ARASTAT_REG, 0x01); + iwl_legacy_write_prph(priv, ALM_SCD_ARASTAT_REG, 0x01); /* all 6 fifo are active */ - iwl_write_prph(priv, ALM_SCD_TXFACT_REG, 0x3f); + iwl_legacy_write_prph(priv, ALM_SCD_TXFACT_REG, 0x3f); - iwl_write_prph(priv, ALM_SCD_SBYP_MODE_1_REG, 0x010000); - iwl_write_prph(priv, ALM_SCD_SBYP_MODE_2_REG, 0x030002); - iwl_write_prph(priv, ALM_SCD_TXF4MF_REG, 0x000004); - iwl_write_prph(priv, ALM_SCD_TXF5MF_REG, 0x000005); + iwl_legacy_write_prph(priv, ALM_SCD_SBYP_MODE_1_REG, 0x010000); + iwl_legacy_write_prph(priv, ALM_SCD_SBYP_MODE_2_REG, 0x030002); + iwl_legacy_write_prph(priv, ALM_SCD_TXF4MF_REG, 0x000004); + iwl_legacy_write_prph(priv, ALM_SCD_TXF5MF_REG, 0x000005); - iwl_write_direct32(priv, FH39_TSSR_CBB_BASE, + iwl_legacy_write_direct32(priv, FH39_TSSR_CBB_BASE, priv->_3945.shared_phys); - iwl_write_direct32(priv, FH39_TSSR_MSG_CONFIG, + iwl_legacy_write_direct32(priv, FH39_TSSR_MSG_CONFIG, FH39_TSSR_TX_MSG_CONFIG_REG_VAL_SNOOP_RD_TXPD_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_ORDER_RD_TXPD_ON | FH39_TSSR_TX_MSG_CONFIG_REG_VAL_MAX_FRAG_SIZE_128B | @@ -844,7 +841,7 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) iwl3945_hw_txq_ctx_free(priv); /* allocate tx queue structure */ - rc = iwl_alloc_txq_mem(priv); + rc = iwl_legacy_alloc_txq_mem(priv); if (rc) return rc; @@ -857,8 +854,8 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { slots_num = (txq_id == IWL39_CMD_QUEUE_NUM) ? TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - rc = iwl_tx_queue_init(priv, &priv->txq[txq_id], slots_num, - txq_id); + rc = iwl_legacy_tx_queue_init(priv, &priv->txq[txq_id], + slots_num, txq_id); if (rc) { IWL_ERR(priv, "Tx %d queue init failed\n", txq_id); goto error; @@ -875,21 +872,23 @@ static int iwl3945_txq_ctx_reset(struct iwl_priv *priv) /* * Start up 3945's basic functionality after it has been reset - * (e.g. after platform boot, or shutdown via iwl_apm_stop()) + * (e.g. after platform boot, or shutdown via iwl_legacy_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ static int iwl3945_apm_init(struct iwl_priv *priv) { - int ret = iwl_apm_init(priv); + int ret = iwl_legacy_apm_init(priv); /* Clear APMG (NIC's internal power management) interrupts */ - iwl_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0); - iwl_write_prph(priv, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); + iwl_legacy_write_prph(priv, APMG_RTC_INT_MSK_REG, 0x0); + iwl_legacy_write_prph(priv, APMG_RTC_INT_STT_REG, 0xFFFFFFFF); /* Reset radio chip */ - iwl_set_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + iwl_legacy_set_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); udelay(5); - iwl_clear_bits_prph(priv, APMG_PS_CTRL_REG, APMG_PS_CTRL_VAL_RESET_REQ); + iwl_legacy_clear_bits_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); return ret; } @@ -909,17 +908,17 @@ static void iwl3945_nic_config(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "RTP type\n"); else if (rev_id & PCI_CFG_REV_ID_BIT_BASIC_SKU) { IWL_DEBUG_INFO(priv, "3945 RADIO-MB type\n"); - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_3945_MB); } else { IWL_DEBUG_INFO(priv, "3945 RADIO-MM type\n"); - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_3945_MM); } if (EEPROM_SKU_CAP_OP_MODE_MRC == eeprom->sku_cap) { IWL_DEBUG_INFO(priv, "SKU OP mode is mrc\n"); - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC); } else IWL_DEBUG_INFO(priv, "SKU OP mode is basic\n"); @@ -927,24 +926,24 @@ static void iwl3945_nic_config(struct iwl_priv *priv) if ((eeprom->board_revision & 0xF0) == 0xD0) { IWL_DEBUG_INFO(priv, "3945ABG revision is 0x%X\n", eeprom->board_revision); - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); } else { IWL_DEBUG_INFO(priv, "3945ABG revision is 0x%X\n", eeprom->board_revision); - iwl_clear_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_clear_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE); } if (eeprom->almgor_m_version <= 1) { - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A); IWL_DEBUG_INFO(priv, "Card M type A version is 0x%X\n", eeprom->almgor_m_version); } else { IWL_DEBUG_INFO(priv, "Card M type B version is 0x%X\n", eeprom->almgor_m_version); - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B); } spin_unlock_irqrestore(&priv->lock, flags); @@ -972,7 +971,7 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv) /* Allocate the RX queue, or reset if it is already allocated */ if (!rxq->bd) { - rc = iwl_rx_queue_alloc(priv); + rc = iwl_legacy_rx_queue_alloc(priv); if (rc) { IWL_ERR(priv, "Unable to initialize Rx queue\n"); return -ENOMEM; @@ -987,10 +986,10 @@ int iwl3945_hw_nic_init(struct iwl_priv *priv) /* Look at using this instead: rxq->need_update = 1; - iwl_rx_queue_update_write_ptr(priv, rxq); + iwl_legacy_rx_queue_update_write_ptr(priv, rxq); */ - iwl_write_direct32(priv, FH39_RCSR_WPTR(0), rxq->write & ~7); + iwl_legacy_write_direct32(priv, FH39_RCSR_WPTR(0), rxq->write & ~7); rc = iwl3945_txq_ctx_reset(priv); if (rc) @@ -1015,12 +1014,12 @@ void iwl3945_hw_txq_ctx_free(struct iwl_priv *priv) for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) if (txq_id == IWL39_CMD_QUEUE_NUM) - iwl_cmd_queue_free(priv); + iwl_legacy_cmd_queue_free(priv); else - iwl_tx_queue_free(priv, txq_id); + iwl_legacy_tx_queue_free(priv, txq_id); /* free tx queue structure */ - iwl_free_txq_mem(priv); + iwl_legacy_txq_mem(priv); } void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) @@ -1028,12 +1027,12 @@ void iwl3945_hw_txq_ctx_stop(struct iwl_priv *priv) int txq_id; /* stop SCD */ - iwl_write_prph(priv, ALM_SCD_MODE_REG, 0); - iwl_write_prph(priv, ALM_SCD_TXFACT_REG, 0); + iwl_legacy_write_prph(priv, ALM_SCD_MODE_REG, 0); + iwl_legacy_write_prph(priv, ALM_SCD_TXFACT_REG, 0); /* reset TFD queues */ for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { - iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0); + iwl_legacy_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), 0x0); iwl_poll_direct_bit(priv, FH39_TSSR_TX_STATUS, FH39_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(txq_id), 1000); @@ -1100,12 +1099,12 @@ static int iwl3945_hw_reg_txpower_get_temperature(struct iwl_priv *priv) #define IWL_TEMPERATURE_LIMIT_TIMER 6 /** - * is_temp_calib_needed - determines if new calibration is needed + * iwl3945_is_temp_calib_needed - determines if new calibration is needed * * records new temperature in tx_mgr->temperature. * replaces tx_mgr->last_temperature *only* if calib needed * (assumes caller will actually do the calibration!). */ -static int is_temp_calib_needed(struct iwl_priv *priv) +static int iwl3945_is_temp_calib_needed(struct iwl_priv *priv) { int temp_diff; @@ -1336,9 +1335,6 @@ static void iwl3945_hw_reg_set_scan_power(struct iwl_priv *priv, u32 scan_tbl_in * based on eeprom channel data) for this channel. */ power = min(ch_info->scan_power, clip_pwrs[IWL_RATE_6M_INDEX_TABLE]); - /* further limit to user's max power preference. - * FIXME: Other spectrum management power limitations do not - * seem to apply?? */ power = min(power, priv->tx_power_user_lmt); scan_power_info->requested_power = power; @@ -1392,7 +1388,7 @@ static int iwl3945_send_tx_power(struct iwl_priv *priv) chan = le16_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.channel); txpower.band = (priv->band == IEEE80211_BAND_5GHZ) ? 0 : 1; - ch_info = iwl_get_channel_info(priv, priv->band, chan); + ch_info = iwl_legacy_get_channel_info(priv, priv->band, chan); if (!ch_info) { IWL_ERR(priv, "Failed to get channel info for channel %d [%d]\n", @@ -1400,7 +1396,7 @@ static int iwl3945_send_tx_power(struct iwl_priv *priv) return -EINVAL; } - if (!is_channel_valid(ch_info)) { + if (!iwl_legacy_is_channel_valid(ch_info)) { IWL_DEBUG_POWER(priv, "Not calling TX_PWR_TABLE_CMD on " "non-Tx channel.\n"); return 0; @@ -1435,7 +1431,7 @@ static int iwl3945_send_tx_power(struct iwl_priv *priv) txpower.power[i].rate); } - return iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, + return iwl_legacy_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(struct iwl3945_txpowertable_cmd), &txpower); @@ -1569,7 +1565,7 @@ static int iwl3945_hw_reg_comp_txpower_temp(struct iwl_priv *priv) /* set up new Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0; i < priv->channel_count; i++) { ch_info = &priv->channel_info[i]; - a_band = is_channel_a_band(ch_info); + a_band = iwl_legacy_is_channel_a_band(ch_info); /* Get this chnlgrp's factory calibration temperature */ ref_temp = (s16)eeprom->groups[ch_info->group_index]. @@ -1635,7 +1631,7 @@ int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) for (i = 0; i < priv->channel_count; i++) { ch_info = &priv->channel_info[i]; - a_band = is_channel_a_band(ch_info); + a_band = iwl_legacy_is_channel_a_band(ch_info); /* find minimum power of all user and regulatory constraints * (does not consider h/w clipping limitations) */ @@ -1651,7 +1647,7 @@ int iwl3945_hw_reg_set_txpower(struct iwl_priv *priv, s8 power) /* update txpower settings for all channels, * send to NIC if associated. */ - is_temp_calib_needed(priv); + iwl3945_is_temp_calib_needed(priv); iwl3945_hw_reg_comp_txpower_temp(priv); return 0; @@ -1669,8 +1665,8 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv, .flags = CMD_WANT_SKB, .data = &rxon_assoc, }; - const struct iwl_rxon_cmd *rxon1 = &ctx->staging; - const struct iwl_rxon_cmd *rxon2 = &ctx->active; + const struct iwl_legacy_rxon_cmd *rxon1 = &ctx->staging; + const struct iwl_legacy_rxon_cmd *rxon2 = &ctx->active; if ((rxon1->flags == rxon2->flags) && (rxon1->filter_flags == rxon2->filter_flags) && @@ -1686,7 +1682,7 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv, rxon_assoc.cck_basic_rates = ctx->staging.cck_basic_rates; rxon_assoc.reserved = 0; - rc = iwl_send_cmd_sync(priv, &cmd); + rc = iwl_legacy_send_cmd_sync(priv, &cmd); if (rc) return rc; @@ -1696,7 +1692,7 @@ static int iwl3945_send_rxon_assoc(struct iwl_priv *priv, rc = -EIO; } - iwl_free_pages(priv, cmd.reply_page); + iwl_legacy_free_pages(priv, cmd.reply_page); return rc; } @@ -1720,7 +1716,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return -EINVAL; - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -1; /* always get timestamp with Rx frame */ @@ -1731,7 +1727,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) ~(RXON_FLG_DIS_DIV_MSK | RXON_FLG_ANT_SEL_MSK); staging_rxon->flags |= iwl3945_get_antenna_flags(priv); - rc = iwl_check_rxon_cmd(priv, ctx); + rc = iwl_legacy_check_rxon_cmd(priv, ctx); if (rc) { IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); return -EINVAL; @@ -1740,8 +1736,9 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) /* If we don't need to send a full RXON, we can use * iwl3945_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ - if (!iwl_full_rxon_required(priv, &priv->contexts[IWL_RXON_CTX_BSS])) { - rc = iwl_send_rxon_assoc(priv, + if (!iwl_legacy_full_rxon_required(priv, + &priv->contexts[IWL_RXON_CTX_BSS])) { + rc = iwl_legacy_send_rxon_assoc(priv, &priv->contexts[IWL_RXON_CTX_BSS]); if (rc) { IWL_ERR(priv, "Error setting RXON_ASSOC " @@ -1758,7 +1755,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration * before we apply the new config */ - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS) && new_assoc) { + if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS) && new_assoc) { IWL_DEBUG_INFO(priv, "Toggling associated bit on current RXON\n"); active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; @@ -1768,7 +1765,7 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) */ active_rxon->reserved4 = 0; active_rxon->reserved5 = 0; - rc = iwl_send_cmd_pdu(priv, REPLY_RXON, + rc = iwl_legacy_send_cmd_pdu(priv, REPLY_RXON, sizeof(struct iwl3945_rxon_cmd), &priv->contexts[IWL_RXON_CTX_BSS].active); @@ -1780,9 +1777,10 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) "configuration (%d).\n", rc); return rc; } - iwl_clear_ucode_stations(priv, + iwl_legacy_clear_ucode_stations(priv, + &priv->contexts[IWL_RXON_CTX_BSS]); + iwl_legacy_restore_stations(priv, &priv->contexts[IWL_RXON_CTX_BSS]); - iwl_restore_stations(priv, &priv->contexts[IWL_RXON_CTX_BSS]); } IWL_DEBUG_INFO(priv, "Sending RXON\n" @@ -1800,10 +1798,10 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) staging_rxon->reserved4 = 0; staging_rxon->reserved5 = 0; - iwl_set_rxon_hwcrypto(priv, ctx, !iwl3945_mod_params.sw_crypto); + iwl_legacy_set_rxon_hwcrypto(priv, ctx, !iwl3945_mod_params.sw_crypto); /* Apply the new configuration */ - rc = iwl_send_cmd_pdu(priv, REPLY_RXON, + rc = iwl_legacy_send_cmd_pdu(priv, REPLY_RXON, sizeof(struct iwl3945_rxon_cmd), staging_rxon); if (rc) { @@ -1814,14 +1812,15 @@ int iwl3945_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) memcpy(active_rxon, staging_rxon, sizeof(*active_rxon)); if (!new_assoc) { - iwl_clear_ucode_stations(priv, + iwl_legacy_clear_ucode_stations(priv, &priv->contexts[IWL_RXON_CTX_BSS]); - iwl_restore_stations(priv, &priv->contexts[IWL_RXON_CTX_BSS]); + iwl_legacy_restore_stations(priv, + &priv->contexts[IWL_RXON_CTX_BSS]); } /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - rc = iwl_set_tx_power(priv, priv->tx_power_next, true); + rc = iwl_legacy_set_tx_power(priv, priv->tx_power_next, true); if (rc) { IWL_ERR(priv, "Error setting Tx power (%d).\n", rc); return rc; @@ -1851,7 +1850,7 @@ void iwl3945_reg_txpower_periodic(struct iwl_priv *priv) { /* This will kick in the "brute force" * iwl3945_hw_reg_comp_txpower_temp() below */ - if (!is_temp_calib_needed(priv)) + if (!iwl3945_is_temp_calib_needed(priv)) goto reschedule; /* Set up a new set of temp-adjusted TxPowers, send to NIC. @@ -1898,7 +1897,7 @@ static u16 iwl3945_hw_reg_get_ch_grp_index(struct iwl_priv *priv, u8 grp_channel; /* Find the group index for the channel ... don't use index 1(?) */ - if (is_channel_a_band(ch_info)) { + if (iwl_legacy_is_channel_a_band(ch_info)) { for (group = 1; group < 5; group++) { grp_channel = ch_grp[group].group_channel; if (ch_info->channel <= grp_channel) { @@ -2078,8 +2077,8 @@ int iwl3945_txpower_set_from_eeprom(struct iwl_priv *priv) /* initialize Tx power info for each and every channel, 2.4 and 5.x */ for (i = 0, ch_info = priv->channel_info; i < priv->channel_count; i++, ch_info++) { - a_band = is_channel_a_band(ch_info); - if (!is_channel_valid(ch_info)) + a_band = iwl_legacy_is_channel_a_band(ch_info); + if (!iwl_legacy_is_channel_valid(ch_info)) continue; /* find this channel's channel group (*not* "band") index */ @@ -2182,7 +2181,7 @@ int iwl3945_hw_rxq_stop(struct iwl_priv *priv) { int rc; - iwl_write_direct32(priv, FH39_RCSR_CONFIG(0), 0); + iwl_legacy_write_direct32(priv, FH39_RCSR_CONFIG(0), 0); rc = iwl_poll_direct_bit(priv, FH39_RSSR_STATUS, FH39_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); if (rc < 0) @@ -2199,10 +2198,10 @@ int iwl3945_hw_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq) shared_data->tx_base_ptr[txq_id] = cpu_to_le32((u32)txq->q.dma_addr); - iwl_write_direct32(priv, FH39_CBCC_CTRL(txq_id), 0); - iwl_write_direct32(priv, FH39_CBCC_BASE(txq_id), 0); + iwl_legacy_write_direct32(priv, FH39_CBCC_CTRL(txq_id), 0); + iwl_legacy_write_direct32(priv, FH39_CBCC_BASE(txq_id), 0); - iwl_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), + iwl_legacy_write_direct32(priv, FH39_TCSR_CONFIG(txq_id), FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT | FH39_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF | FH39_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD | @@ -2231,7 +2230,8 @@ static u16 iwl3945_get_hcmd_size(u8 cmd_id, u16 len) } -static u16 iwl3945_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) +static u16 iwl3945_build_addsta_hcmd(const struct iwl_legacy_addsta_cmd *cmd, + u8 *data) { struct iwl3945_addsta_cmd *addsta = (struct iwl3945_addsta_cmd *)data; addsta->mode = cmd->mode; @@ -2259,7 +2259,7 @@ static int iwl3945_add_bssid_station(struct iwl_priv *priv, if (sta_id_r) *sta_id_r = IWL_INVALID_STATION; - ret = iwl_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); + ret = iwl_legacy_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); if (ret) { IWL_ERR(priv, "Unable to add station %pM\n", addr); return ret; @@ -2294,7 +2294,7 @@ static int iwl3945_manage_ibss_station(struct iwl_priv *priv, return 0; } - return iwl_remove_station(priv, vif_priv->ibss_bssid_sta_id, + return iwl_legacy_remove_station(priv, vif_priv->ibss_bssid_sta_id, vif->bss_conf.bssid); } @@ -2345,7 +2345,7 @@ int iwl3945_init_hw_rate_table(struct iwl_priv *priv) * 1M CCK rates */ if (!(priv->_3945.sta_supp_rates & IWL_OFDM_RATES_MASK) && - iwl_is_associated(priv, IWL_RXON_CTX_BSS)) { + iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) { index = IWL_FIRST_CCK_RATE; for (i = IWL_RATE_6M_INDEX_TABLE; @@ -2366,14 +2366,14 @@ int iwl3945_init_hw_rate_table(struct iwl_priv *priv) /* Update the rate scaling for control frame Tx */ rate_cmd.table_id = 0; - rc = iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd), + rc = iwl_legacy_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); if (rc) return rc; /* Update the rate scaling for data frame Tx */ rate_cmd.table_id = 1; - return iwl_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd), + return iwl_legacy_send_cmd_pdu(priv, REPLY_RATE_SCALE, sizeof(rate_cmd), &rate_cmd); } @@ -2473,11 +2473,11 @@ static int iwl3945_verify_bsm(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "Begin verify bsm\n"); /* verify BSM SRAM contents */ - val = iwl_read_prph(priv, BSM_WR_DWCOUNT_REG); + val = iwl_legacy_read_prph(priv, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; reg += sizeof(u32), image++) { - val = iwl_read_prph(priv, reg); + val = iwl_legacy_read_prph(priv, reg); if (val != le32_to_cpu(*image)) { IWL_ERR(priv, "BSM uCode verification failed at " "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", @@ -2510,7 +2510,7 @@ static int iwl3945_verify_bsm(struct iwl_priv *priv) */ static int iwl3945_eeprom_acquire_semaphore(struct iwl_priv *priv) { - _iwl_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); + _iwl_legacy_clear_bit(priv, CSR_EEPROM_GP, CSR_EEPROM_GP_IF_OWNER_MSK); return 0; } @@ -2581,16 +2581,16 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) inst_len = priv->ucode_init.len; data_len = priv->ucode_init_data.len; - iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); - iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); - iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); - iwl_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); + iwl_legacy_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl_legacy_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); /* Fill BSM memory with bootstrap instructions */ for (reg_offset = BSM_SRAM_LOWER_BOUND; reg_offset < BSM_SRAM_LOWER_BOUND + len; reg_offset += sizeof(u32), image++) - _iwl_write_prph(priv, reg_offset, + _iwl_legacy_write_prph(priv, reg_offset, le32_to_cpu(*image)); rc = iwl3945_verify_bsm(priv); @@ -2598,19 +2598,19 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) return rc; /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ - iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); - iwl_write_prph(priv, BSM_WR_MEM_DST_REG, + iwl_legacy_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); + iwl_legacy_write_prph(priv, BSM_WR_MEM_DST_REG, IWL39_RTC_INST_LOWER_BOUND); - iwl_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + iwl_legacy_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); /* Load bootstrap code into instruction SRAM now, * to prepare to load "initialize" uCode */ - iwl_write_prph(priv, BSM_WR_CTRL_REG, + iwl_legacy_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); /* Wait for load of bootstrap uCode to finish */ for (i = 0; i < 100; i++) { - done = iwl_read_prph(priv, BSM_WR_CTRL_REG); + done = iwl_legacy_read_prph(priv, BSM_WR_CTRL_REG); if (!(done & BSM_WR_CTRL_REG_BIT_START)) break; udelay(10); @@ -2624,7 +2624,7 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) /* Enable future boot loads whenever power management unit triggers it * (e.g. when powering back up after power-save shutdown) */ - iwl_write_prph(priv, BSM_WR_CTRL_REG, + iwl_legacy_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); return 0; @@ -2633,7 +2633,6 @@ static int iwl3945_load_bsm(struct iwl_priv *priv) static struct iwl_hcmd_ops iwl3945_hcmd = { .rxon_assoc = iwl3945_send_rxon_assoc, .commit_rxon = iwl3945_commit_rxon, - .send_bt_config = iwl_send_bt_config, }; static struct iwl_lib_ops iwl3945_lib = { @@ -2659,13 +2658,9 @@ static struct iwl_lib_ops iwl3945_lib = { }, .acquire_semaphore = iwl3945_eeprom_acquire_semaphore, .release_semaphore = iwl3945_eeprom_release_semaphore, - .query_addr = iwlcore_eeprom_query_addr, }, .send_tx_power = iwl3945_send_tx_power, .is_valid_rtc_data_addr = iwl3945_hw_valid_rtc_data_addr, - .isr_ops = { - .isr = iwl_isr_legacy, - }, .debugfs_ops = { .rx_stats_read = iwl3945_ucode_rx_stats_read, @@ -2683,7 +2678,6 @@ static const struct iwl_legacy_ops iwl3945_legacy_ops = { static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = { .get_hcmd_size = iwl3945_get_hcmd_size, .build_addsta_hcmd = iwl3945_build_addsta_hcmd, - .tx_cmd_protection = iwl_legacy_tx_cmd_protection, .request_scan = iwl3945_request_scan, .post_scan = iwl3945_post_scan, }; @@ -2703,13 +2697,10 @@ static struct iwl_base_params iwl3945_base_params = { .pll_cfg_val = CSR39_ANA_PLL_CFG_VAL, .set_l0s = false, .use_bsm = true, - .use_isr_legacy = true, .led_compensation = 64, - .broken_powersave = true, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF, .wd_timeout = IWL_DEF_WD_TIMEOUT, .max_event_log_size = 512, - .tx_power_by_driver = true, }; static struct iwl_cfg iwl3945_bg_cfg = { diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.h b/drivers/net/wireless/iwlegacy/iwl-3945.h index 3eef1eb74a7..b118b59b71d 100644 --- a/drivers/net/wireless/iwlwifi/iwl-3945.h +++ b/drivers/net/wireless/iwlegacy/iwl-3945.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -108,7 +108,7 @@ struct iwl3945_rs_sta { /* * The common struct MUST be first because it is shared between - * 3945 and agn! + * 3945 and 4965! */ struct iwl3945_sta_priv { struct iwl_station_priv_common common; @@ -201,7 +201,7 @@ struct iwl3945_ibss_seq { /****************************************************************************** * - * Functions implemented in iwl-base.c which are forward declared here + * Functions implemented in iwl3945-base.c which are forward declared here * for use by iwl-*.c * *****************************************************************************/ @@ -209,7 +209,7 @@ extern int iwl3945_calc_db_from_ratio(int sig_ratio); extern void iwl3945_rx_replenish(void *data); extern void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); extern unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, - struct ieee80211_hdr *hdr,int left); + struct ieee80211_hdr *hdr, int left); extern int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, char **buf, bool display); extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); @@ -217,7 +217,7 @@ extern void iwl3945_dump_nic_error_log(struct iwl_priv *priv); /****************************************************************************** * * Functions implemented in iwl-[34]*.c which are forward declared here - * for use by iwl-base.c + * for use by iwl3945-base.c * * NOTE: The implementation of these functions are hardware specific * which is why they are in the hardware specific files (vs. iwl-base.c) @@ -283,7 +283,7 @@ extern u8 iwl3945_hw_find_station(struct iwl_priv *priv, const u8 *bssid); extern struct ieee80211_ops iwl3945_hw_ops; /* - * Forward declare iwl-3945.c functions for iwl-base.c + * Forward declare iwl-3945.c functions for iwl3945-base.c */ extern __le32 iwl3945_get_antenna_flags(const struct iwl_priv *priv); extern int iwl3945_init_hw_rate_table(struct iwl_priv *priv); diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-calib.c b/drivers/net/wireless/iwlegacy/iwl-4965-calib.c new file mode 100644 index 00000000000..81d6a25eb04 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-calib.c @@ -0,0 +1,967 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#include <linux/slab.h> +#include <net/mac80211.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-4965-calib.h" + +/***************************************************************************** + * INIT calibrations framework + *****************************************************************************/ + +struct statistics_general_data { + u32 beacon_silence_rssi_a; + u32 beacon_silence_rssi_b; + u32 beacon_silence_rssi_c; + u32 beacon_energy_a; + u32 beacon_energy_b; + u32 beacon_energy_c; +}; + +void iwl4965_calib_free_results(struct iwl_priv *priv) +{ + int i; + + for (i = 0; i < IWL_CALIB_MAX; i++) { + kfree(priv->calib_results[i].buf); + priv->calib_results[i].buf = NULL; + priv->calib_results[i].buf_len = 0; + } +} + +/***************************************************************************** + * RUNTIME calibrations framework + *****************************************************************************/ + +/* "false alarms" are signals that our DSP tries to lock onto, + * but then determines that they are either noise, or transmissions + * from a distant wireless network (also "noise", really) that get + * "stepped on" by stronger transmissions within our own network. + * This algorithm attempts to set a sensitivity level that is high + * enough to receive all of our own network traffic, but not so + * high that our DSP gets too busy trying to lock onto non-network + * activity/noise. */ +static int iwl4965_sens_energy_cck(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time, + struct statistics_general_data *rx_info) +{ + u32 max_nrg_cck = 0; + int i = 0; + u8 max_silence_rssi = 0; + u32 silence_ref = 0; + u8 silence_rssi_a = 0; + u8 silence_rssi_b = 0; + u8 silence_rssi_c = 0; + u32 val; + + /* "false_alarms" values below are cross-multiplications to assess the + * numbers of false alarms within the measured period of actual Rx + * (Rx is off when we're txing), vs the min/max expected false alarms + * (some should be expected if rx is sensitive enough) in a + * hypothetical listening period of 200 time units (TU), 204.8 msec: + * + * MIN_FA/fixed-time < false_alarms/actual-rx-time < MAX_FA/beacon-time + * + * */ + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_CCK * rx_enable_time; + u32 min_false_alarms = MIN_FA_CCK * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + data->nrg_auto_corr_silence_diff = 0; + + /* Find max silence rssi among all 3 receivers. + * This is background noise, which may include transmissions from other + * networks, measured during silence before our network's beacon */ + silence_rssi_a = (u8)((rx_info->beacon_silence_rssi_a & + ALL_BAND_FILTER) >> 8); + silence_rssi_b = (u8)((rx_info->beacon_silence_rssi_b & + ALL_BAND_FILTER) >> 8); + silence_rssi_c = (u8)((rx_info->beacon_silence_rssi_c & + ALL_BAND_FILTER) >> 8); + + val = max(silence_rssi_b, silence_rssi_c); + max_silence_rssi = max(silence_rssi_a, (u8) val); + + /* Store silence rssi in 20-beacon history table */ + data->nrg_silence_rssi[data->nrg_silence_idx] = max_silence_rssi; + data->nrg_silence_idx++; + if (data->nrg_silence_idx >= NRG_NUM_PREV_STAT_L) + data->nrg_silence_idx = 0; + + /* Find max silence rssi across 20 beacon history */ + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) { + val = data->nrg_silence_rssi[i]; + silence_ref = max(silence_ref, val); + } + IWL_DEBUG_CALIB(priv, "silence a %u, b %u, c %u, 20-bcn max %u\n", + silence_rssi_a, silence_rssi_b, silence_rssi_c, + silence_ref); + + /* Find max rx energy (min value!) among all 3 receivers, + * measured during beacon frame. + * Save it in 10-beacon history table. */ + i = data->nrg_energy_idx; + val = min(rx_info->beacon_energy_b, rx_info->beacon_energy_c); + data->nrg_value[i] = min(rx_info->beacon_energy_a, val); + + data->nrg_energy_idx++; + if (data->nrg_energy_idx >= 10) + data->nrg_energy_idx = 0; + + /* Find min rx energy (max value) across 10 beacon history. + * This is the minimum signal level that we want to receive well. + * Add backoff (margin so we don't miss slightly lower energy frames). + * This establishes an upper bound (min value) for energy threshold. */ + max_nrg_cck = data->nrg_value[0]; + for (i = 1; i < 10; i++) + max_nrg_cck = (u32) max(max_nrg_cck, (data->nrg_value[i])); + max_nrg_cck += 6; + + IWL_DEBUG_CALIB(priv, "rx energy a %u, b %u, c %u, 10-bcn max/min %u\n", + rx_info->beacon_energy_a, rx_info->beacon_energy_b, + rx_info->beacon_energy_c, max_nrg_cck - 6); + + /* Count number of consecutive beacons with fewer-than-desired + * false alarms. */ + if (false_alarms < min_false_alarms) + data->num_in_cck_no_fa++; + else + data->num_in_cck_no_fa = 0; + IWL_DEBUG_CALIB(priv, "consecutive bcns with few false alarms = %u\n", + data->num_in_cck_no_fa); + + /* If we got too many false alarms this time, reduce sensitivity */ + if ((false_alarms > max_false_alarms) && + (data->auto_corr_cck > AUTO_CORR_MAX_TH_CCK)) { + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u\n", + false_alarms, max_false_alarms); + IWL_DEBUG_CALIB(priv, "... reducing sensitivity\n"); + data->nrg_curr_state = IWL_FA_TOO_MANY; + /* Store for "fewer than desired" on later beacon */ + data->nrg_silence_ref = silence_ref; + + /* increase energy threshold (reduce nrg value) + * to decrease sensitivity */ + data->nrg_th_cck = data->nrg_th_cck - NRG_STEP_CCK; + /* Else if we got fewer than desired, increase sensitivity */ + } else if (false_alarms < min_false_alarms) { + data->nrg_curr_state = IWL_FA_TOO_FEW; + + /* Compare silence level with silence level for most recent + * healthy number or too many false alarms */ + data->nrg_auto_corr_silence_diff = (s32)data->nrg_silence_ref - + (s32)silence_ref; + + IWL_DEBUG_CALIB(priv, + "norm FA %u < min FA %u, silence diff %d\n", + false_alarms, min_false_alarms, + data->nrg_auto_corr_silence_diff); + + /* Increase value to increase sensitivity, but only if: + * 1a) previous beacon did *not* have *too many* false alarms + * 1b) AND there's a significant difference in Rx levels + * from a previous beacon with too many, or healthy # FAs + * OR 2) We've seen a lot of beacons (100) with too few + * false alarms */ + if ((data->nrg_prev_state != IWL_FA_TOO_MANY) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + IWL_DEBUG_CALIB(priv, "... increasing sensitivity\n"); + /* Increase nrg value to increase sensitivity */ + val = data->nrg_th_cck + NRG_STEP_CCK; + data->nrg_th_cck = min((u32)ranges->min_nrg_cck, val); + } else { + IWL_DEBUG_CALIB(priv, + "... but not changing sensitivity\n"); + } + + /* Else we got a healthy number of false alarms, keep status quo */ + } else { + IWL_DEBUG_CALIB(priv, " FA in safe zone\n"); + data->nrg_curr_state = IWL_FA_GOOD_RANGE; + + /* Store for use in "fewer than desired" with later beacon */ + data->nrg_silence_ref = silence_ref; + + /* If previous beacon had too many false alarms, + * give it some extra margin by reducing sensitivity again + * (but don't go below measured energy of desired Rx) */ + if (IWL_FA_TOO_MANY == data->nrg_prev_state) { + IWL_DEBUG_CALIB(priv, "... increasing margin\n"); + if (data->nrg_th_cck > (max_nrg_cck + NRG_MARGIN)) + data->nrg_th_cck -= NRG_MARGIN; + else + data->nrg_th_cck = max_nrg_cck; + } + } + + /* Make sure the energy threshold does not go above the measured + * energy of the desired Rx signals (reduced by backoff margin), + * or else we might start missing Rx frames. + * Lower value is higher energy, so we use max()! + */ + data->nrg_th_cck = max(max_nrg_cck, data->nrg_th_cck); + IWL_DEBUG_CALIB(priv, "new nrg_th_cck %u\n", data->nrg_th_cck); + + data->nrg_prev_state = data->nrg_curr_state; + + /* Auto-correlation CCK algorithm */ + if (false_alarms > min_false_alarms) { + + /* increase auto_corr values to decrease sensitivity + * so the DSP won't be disturbed by the noise + */ + if (data->auto_corr_cck < AUTO_CORR_MAX_TH_CCK) + data->auto_corr_cck = AUTO_CORR_MAX_TH_CCK + 1; + else { + val = data->auto_corr_cck + AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + min((u32)ranges->auto_corr_max_cck, val); + } + val = data->auto_corr_cck_mrc + AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + min((u32)ranges->auto_corr_max_cck_mrc, val); + } else if ((false_alarms < min_false_alarms) && + ((data->nrg_auto_corr_silence_diff > NRG_DIFF) || + (data->num_in_cck_no_fa > MAX_NUMBER_CCK_NO_FA))) { + + /* Decrease auto_corr values to increase sensitivity */ + val = data->auto_corr_cck - AUTO_CORR_STEP_CCK; + data->auto_corr_cck = + max((u32)ranges->auto_corr_min_cck, val); + val = data->auto_corr_cck_mrc - AUTO_CORR_STEP_CCK; + data->auto_corr_cck_mrc = + max((u32)ranges->auto_corr_min_cck_mrc, val); + } + + return 0; +} + + +static int iwl4965_sens_auto_corr_ofdm(struct iwl_priv *priv, + u32 norm_fa, + u32 rx_enable_time) +{ + u32 val; + u32 false_alarms = norm_fa * 200 * 1024; + u32 max_false_alarms = MAX_FA_OFDM * rx_enable_time; + u32 min_false_alarms = MIN_FA_OFDM * rx_enable_time; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + data = &(priv->sensitivity_data); + + /* If we got too many false alarms this time, reduce sensitivity */ + if (false_alarms > max_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u > max FA %u)\n", + false_alarms, max_false_alarms); + + val = data->auto_corr_ofdm + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + min((u32)ranges->auto_corr_max_ofdm, val); + + val = data->auto_corr_ofdm_mrc + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + min((u32)ranges->auto_corr_max_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + min((u32)ranges->auto_corr_max_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 + AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + min((u32)ranges->auto_corr_max_ofdm_mrc_x1, val); + } + + /* Else if we got fewer than desired, increase sensitivity */ + else if (false_alarms < min_false_alarms) { + + IWL_DEBUG_CALIB(priv, "norm FA %u < min FA %u\n", + false_alarms, min_false_alarms); + + val = data->auto_corr_ofdm - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm = + max((u32)ranges->auto_corr_min_ofdm, val); + + val = data->auto_corr_ofdm_mrc - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc = + max((u32)ranges->auto_corr_min_ofdm_mrc, val); + + val = data->auto_corr_ofdm_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_x1 = + max((u32)ranges->auto_corr_min_ofdm_x1, val); + + val = data->auto_corr_ofdm_mrc_x1 - AUTO_CORR_STEP_OFDM; + data->auto_corr_ofdm_mrc_x1 = + max((u32)ranges->auto_corr_min_ofdm_mrc_x1, val); + } else { + IWL_DEBUG_CALIB(priv, "min FA %u < norm FA %u < max FA %u OK\n", + min_false_alarms, false_alarms, max_false_alarms); + } + return 0; +} + +static void iwl4965_prepare_legacy_sensitivity_tbl(struct iwl_priv *priv, + struct iwl_sensitivity_data *data, + __le16 *tbl) +{ + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm); + tbl[HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_x1); + tbl[HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_ofdm_mrc_x1); + + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck); + tbl[HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16((u16)data->auto_corr_cck_mrc); + + tbl[HD_MIN_ENERGY_CCK_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_cck); + tbl[HD_MIN_ENERGY_OFDM_DET_INDEX] = + cpu_to_le16((u16)data->nrg_th_ofdm); + + tbl[HD_BARKER_CORR_TH_ADD_MIN_INDEX] = + cpu_to_le16(data->barker_corr_th_min); + tbl[HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX] = + cpu_to_le16(data->barker_corr_th_min_mrc); + tbl[HD_OFDM_ENERGY_TH_IN_INDEX] = + cpu_to_le16(data->nrg_th_cca); + + IWL_DEBUG_CALIB(priv, "ofdm: ac %u mrc %u x1 %u mrc_x1 %u thresh %u\n", + data->auto_corr_ofdm, data->auto_corr_ofdm_mrc, + data->auto_corr_ofdm_x1, data->auto_corr_ofdm_mrc_x1, + data->nrg_th_ofdm); + + IWL_DEBUG_CALIB(priv, "cck: ac %u mrc %u thresh %u\n", + data->auto_corr_cck, data->auto_corr_cck_mrc, + data->nrg_th_cck); +} + +/* Prepare a SENSITIVITY_CMD, send to uCode if values have changed */ +static int iwl4965_sensitivity_write(struct iwl_priv *priv) +{ + struct iwl_sensitivity_cmd cmd; + struct iwl_sensitivity_data *data = NULL; + struct iwl_host_cmd cmd_out = { + .id = SENSITIVITY_CMD, + .len = sizeof(struct iwl_sensitivity_cmd), + .flags = CMD_ASYNC, + .data = &cmd, + }; + + data = &(priv->sensitivity_data); + + memset(&cmd, 0, sizeof(cmd)); + + iwl4965_prepare_legacy_sensitivity_tbl(priv, data, &cmd.table[0]); + + /* Update uCode's "work" table, and copy it to DSP */ + cmd.control = SENSITIVITY_CMD_CONTROL_WORK_TABLE; + + /* Don't send command to uCode if nothing has changed */ + if (!memcmp(&cmd.table[0], &(priv->sensitivity_tbl[0]), + sizeof(u16)*HD_TABLE_SIZE)) { + IWL_DEBUG_CALIB(priv, "No change in SENSITIVITY_CMD\n"); + return 0; + } + + /* Copy table for comparison next time */ + memcpy(&(priv->sensitivity_tbl[0]), &(cmd.table[0]), + sizeof(u16)*HD_TABLE_SIZE); + + return iwl_legacy_send_cmd(priv, &cmd_out); +} + +void iwl4965_init_sensitivity(struct iwl_priv *priv) +{ + int ret = 0; + int i; + struct iwl_sensitivity_data *data = NULL; + const struct iwl_sensitivity_ranges *ranges = priv->hw_params.sens; + + if (priv->disable_sens_cal) + return; + + IWL_DEBUG_CALIB(priv, "Start iwl4965_init_sensitivity\n"); + + /* Clear driver's sensitivity algo data */ + data = &(priv->sensitivity_data); + + if (ranges == NULL) + return; + + memset(data, 0, sizeof(struct iwl_sensitivity_data)); + + data->num_in_cck_no_fa = 0; + data->nrg_curr_state = IWL_FA_TOO_MANY; + data->nrg_prev_state = IWL_FA_TOO_MANY; + data->nrg_silence_ref = 0; + data->nrg_silence_idx = 0; + data->nrg_energy_idx = 0; + + for (i = 0; i < 10; i++) + data->nrg_value[i] = 0; + + for (i = 0; i < NRG_NUM_PREV_STAT_L; i++) + data->nrg_silence_rssi[i] = 0; + + data->auto_corr_ofdm = ranges->auto_corr_min_ofdm; + data->auto_corr_ofdm_mrc = ranges->auto_corr_min_ofdm_mrc; + data->auto_corr_ofdm_x1 = ranges->auto_corr_min_ofdm_x1; + data->auto_corr_ofdm_mrc_x1 = ranges->auto_corr_min_ofdm_mrc_x1; + data->auto_corr_cck = AUTO_CORR_CCK_MIN_VAL_DEF; + data->auto_corr_cck_mrc = ranges->auto_corr_min_cck_mrc; + data->nrg_th_cck = ranges->nrg_th_cck; + data->nrg_th_ofdm = ranges->nrg_th_ofdm; + data->barker_corr_th_min = ranges->barker_corr_th_min; + data->barker_corr_th_min_mrc = ranges->barker_corr_th_min_mrc; + data->nrg_th_cca = ranges->nrg_th_cca; + + data->last_bad_plcp_cnt_ofdm = 0; + data->last_fa_cnt_ofdm = 0; + data->last_bad_plcp_cnt_cck = 0; + data->last_fa_cnt_cck = 0; + + ret |= iwl4965_sensitivity_write(priv); + IWL_DEBUG_CALIB(priv, "<<return 0x%X\n", ret); +} + +void iwl4965_sensitivity_calibration(struct iwl_priv *priv, void *resp) +{ + u32 rx_enable_time; + u32 fa_cck; + u32 fa_ofdm; + u32 bad_plcp_cck; + u32 bad_plcp_ofdm; + u32 norm_fa_ofdm; + u32 norm_fa_cck; + struct iwl_sensitivity_data *data = NULL; + struct statistics_rx_non_phy *rx_info; + struct statistics_rx_phy *ofdm, *cck; + unsigned long flags; + struct statistics_general_data statis; + + if (priv->disable_sens_cal) + return; + + data = &(priv->sensitivity_data); + + if (!iwl_legacy_is_any_associated(priv)) { + IWL_DEBUG_CALIB(priv, "<< - not associated\n"); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + rx_info = &(((struct iwl_notif_statistics *)resp)->rx.general); + ofdm = &(((struct iwl_notif_statistics *)resp)->rx.ofdm); + cck = &(((struct iwl_notif_statistics *)resp)->rx.cck); + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, "<< invalid data.\n"); + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + /* Extract Statistics: */ + rx_enable_time = le32_to_cpu(rx_info->channel_load); + fa_cck = le32_to_cpu(cck->false_alarm_cnt); + fa_ofdm = le32_to_cpu(ofdm->false_alarm_cnt); + bad_plcp_cck = le32_to_cpu(cck->plcp_err); + bad_plcp_ofdm = le32_to_cpu(ofdm->plcp_err); + + statis.beacon_silence_rssi_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a); + statis.beacon_silence_rssi_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b); + statis.beacon_silence_rssi_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c); + statis.beacon_energy_a = + le32_to_cpu(rx_info->beacon_energy_a); + statis.beacon_energy_b = + le32_to_cpu(rx_info->beacon_energy_b); + statis.beacon_energy_c = + le32_to_cpu(rx_info->beacon_energy_c); + + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_CALIB(priv, "rx_enable_time = %u usecs\n", rx_enable_time); + + if (!rx_enable_time) { + IWL_DEBUG_CALIB(priv, "<< RX Enable Time == 0!\n"); + return; + } + + /* These statistics increase monotonically, and do not reset + * at each beacon. Calculate difference from last value, or just + * use the new statistics value if it has reset or wrapped around. */ + if (data->last_bad_plcp_cnt_cck > bad_plcp_cck) + data->last_bad_plcp_cnt_cck = bad_plcp_cck; + else { + bad_plcp_cck -= data->last_bad_plcp_cnt_cck; + data->last_bad_plcp_cnt_cck += bad_plcp_cck; + } + + if (data->last_bad_plcp_cnt_ofdm > bad_plcp_ofdm) + data->last_bad_plcp_cnt_ofdm = bad_plcp_ofdm; + else { + bad_plcp_ofdm -= data->last_bad_plcp_cnt_ofdm; + data->last_bad_plcp_cnt_ofdm += bad_plcp_ofdm; + } + + if (data->last_fa_cnt_ofdm > fa_ofdm) + data->last_fa_cnt_ofdm = fa_ofdm; + else { + fa_ofdm -= data->last_fa_cnt_ofdm; + data->last_fa_cnt_ofdm += fa_ofdm; + } + + if (data->last_fa_cnt_cck > fa_cck) + data->last_fa_cnt_cck = fa_cck; + else { + fa_cck -= data->last_fa_cnt_cck; + data->last_fa_cnt_cck += fa_cck; + } + + /* Total aborted signal locks */ + norm_fa_ofdm = fa_ofdm + bad_plcp_ofdm; + norm_fa_cck = fa_cck + bad_plcp_cck; + + IWL_DEBUG_CALIB(priv, + "cck: fa %u badp %u ofdm: fa %u badp %u\n", fa_cck, + bad_plcp_cck, fa_ofdm, bad_plcp_ofdm); + + iwl4965_sens_auto_corr_ofdm(priv, norm_fa_ofdm, rx_enable_time); + iwl4965_sens_energy_cck(priv, norm_fa_cck, rx_enable_time, &statis); + + iwl4965_sensitivity_write(priv); +} + +static inline u8 iwl4965_find_first_chain(u8 mask) +{ + if (mask & ANT_A) + return CHAIN_A; + if (mask & ANT_B) + return CHAIN_B; + return CHAIN_C; +} + +/** + * Run disconnected antenna algorithm to find out which antennas are + * disconnected. + */ +static void +iwl4965_find_disconn_antenna(struct iwl_priv *priv, u32* average_sig, + struct iwl_chain_noise_data *data) +{ + u32 active_chains = 0; + u32 max_average_sig; + u16 max_average_sig_antenna_i; + u8 num_tx_chains; + u8 first_chain; + u16 i = 0; + + average_sig[0] = data->chain_signal_a / + priv->cfg->base_params->chain_noise_num_beacons; + average_sig[1] = data->chain_signal_b / + priv->cfg->base_params->chain_noise_num_beacons; + average_sig[2] = data->chain_signal_c / + priv->cfg->base_params->chain_noise_num_beacons; + + if (average_sig[0] >= average_sig[1]) { + max_average_sig = average_sig[0]; + max_average_sig_antenna_i = 0; + active_chains = (1 << max_average_sig_antenna_i); + } else { + max_average_sig = average_sig[1]; + max_average_sig_antenna_i = 1; + active_chains = (1 << max_average_sig_antenna_i); + } + + if (average_sig[2] >= max_average_sig) { + max_average_sig = average_sig[2]; + max_average_sig_antenna_i = 2; + active_chains = (1 << max_average_sig_antenna_i); + } + + IWL_DEBUG_CALIB(priv, "average_sig: a %d b %d c %d\n", + average_sig[0], average_sig[1], average_sig[2]); + IWL_DEBUG_CALIB(priv, "max_average_sig = %d, antenna %d\n", + max_average_sig, max_average_sig_antenna_i); + + /* Compare signal strengths for all 3 receivers. */ + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (i != max_average_sig_antenna_i) { + s32 rssi_delta = (max_average_sig - average_sig[i]); + + /* If signal is very weak, compared with + * strongest, mark it as disconnected. */ + if (rssi_delta > MAXIMUM_ALLOWED_PATHLOSS) + data->disconn_array[i] = 1; + else + active_chains |= (1 << i); + IWL_DEBUG_CALIB(priv, "i = %d rssiDelta = %d " + "disconn_array[i] = %d\n", + i, rssi_delta, data->disconn_array[i]); + } + } + + /* + * The above algorithm sometimes fails when the ucode + * reports 0 for all chains. It's not clear why that + * happens to start with, but it is then causing trouble + * because this can make us enable more chains than the + * hardware really has. + * + * To be safe, simply mask out any chains that we know + * are not on the device. + */ + active_chains &= priv->hw_params.valid_rx_ant; + + num_tx_chains = 0; + for (i = 0; i < NUM_RX_CHAINS; i++) { + /* loops on all the bits of + * priv->hw_setting.valid_tx_ant */ + u8 ant_msk = (1 << i); + if (!(priv->hw_params.valid_tx_ant & ant_msk)) + continue; + + num_tx_chains++; + if (data->disconn_array[i] == 0) + /* there is a Tx antenna connected */ + break; + if (num_tx_chains == priv->hw_params.tx_chains_num && + data->disconn_array[i]) { + /* + * If all chains are disconnected + * connect the first valid tx chain + */ + first_chain = + iwl4965_find_first_chain(priv->cfg->valid_tx_ant); + data->disconn_array[first_chain] = 0; + active_chains |= BIT(first_chain); + IWL_DEBUG_CALIB(priv, "All Tx chains are disconnected \ + W/A - declare %d as connected\n", + first_chain); + break; + } + } + + if (active_chains != priv->hw_params.valid_rx_ant && + active_chains != priv->chain_noise_data.active_chains) + IWL_DEBUG_CALIB(priv, + "Detected that not all antennas are connected! " + "Connected: %#x, valid: %#x.\n", + active_chains, priv->hw_params.valid_rx_ant); + + /* Save for use within RXON, TX, SCAN commands, etc. */ + data->active_chains = active_chains; + IWL_DEBUG_CALIB(priv, "active_chains (bitwise) = 0x%x\n", + active_chains); +} + +static void iwl4965_gain_computation(struct iwl_priv *priv, + u32 *average_noise, + u16 min_average_noise_antenna_i, + u32 min_average_noise, + u8 default_chain) +{ + int i, ret; + struct iwl_chain_noise_data *data = &priv->chain_noise_data; + + data->delta_gain_code[min_average_noise_antenna_i] = 0; + + for (i = default_chain; i < NUM_RX_CHAINS; i++) { + s32 delta_g = 0; + + if (!(data->disconn_array[i]) && + (data->delta_gain_code[i] == + CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) { + delta_g = average_noise[i] - min_average_noise; + data->delta_gain_code[i] = (u8)((delta_g * 10) / 15); + data->delta_gain_code[i] = + min(data->delta_gain_code[i], + (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); + + data->delta_gain_code[i] = + (data->delta_gain_code[i] | (1 << 2)); + } else { + data->delta_gain_code[i] = 0; + } + } + IWL_DEBUG_CALIB(priv, "delta_gain_codes: a %d b %d c %d\n", + data->delta_gain_code[0], + data->delta_gain_code[1], + data->delta_gain_code[2]); + + /* Differential gain gets sent to uCode only once */ + if (!data->radio_write) { + struct iwl_calib_diff_gain_cmd cmd; + data->radio_write = 1; + + memset(&cmd, 0, sizeof(cmd)); + cmd.hdr.op_code = IWL_PHY_CALIBRATE_DIFF_GAIN_CMD; + cmd.diff_gain_a = data->delta_gain_code[0]; + cmd.diff_gain_b = data->delta_gain_code[1]; + cmd.diff_gain_c = data->delta_gain_code[2]; + ret = iwl_legacy_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, + sizeof(cmd), &cmd); + if (ret) + IWL_DEBUG_CALIB(priv, "fail sending cmd " + "REPLY_PHY_CALIBRATION_CMD\n"); + + /* TODO we might want recalculate + * rx_chain in rxon cmd */ + + /* Mark so we run this algo only once! */ + data->state = IWL_CHAIN_NOISE_CALIBRATED; + } +} + + + +/* + * Accumulate 16 beacons of signal and noise statistics for each of + * 3 receivers/antennas/rx-chains, then figure out: + * 1) Which antennas are connected. + * 2) Differential rx gain settings to balance the 3 receivers. + */ +void iwl4965_chain_noise_calibration(struct iwl_priv *priv, void *stat_resp) +{ + struct iwl_chain_noise_data *data = NULL; + + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_sig_a; + u32 chain_sig_b; + u32 chain_sig_c; + u32 average_sig[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 average_noise[NUM_RX_CHAINS] = {INITIALIZATION_VALUE}; + u32 min_average_noise = MIN_AVERAGE_NOISE_MAX_VALUE; + u16 min_average_noise_antenna_i = INITIALIZATION_VALUE; + u16 i = 0; + u16 rxon_chnum = INITIALIZATION_VALUE; + u16 stat_chnum = INITIALIZATION_VALUE; + u8 rxon_band24; + u8 stat_band24; + unsigned long flags; + struct statistics_rx_non_phy *rx_info; + + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (priv->disable_chain_noise_cal) + return; + + data = &(priv->chain_noise_data); + + /* + * Accumulate just the first "chain_noise_num_beacons" after + * the first association, then we're done forever. + */ + if (data->state != IWL_CHAIN_NOISE_ACCUMULATE) { + if (data->state == IWL_CHAIN_NOISE_ALIVE) + IWL_DEBUG_CALIB(priv, "Wait for noise calib reset\n"); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + rx_info = &(((struct iwl_notif_statistics *)stat_resp)-> + rx.general); + + if (rx_info->interference_data_flag != INTERFERENCE_DATA_AVAILABLE) { + IWL_DEBUG_CALIB(priv, " << Interference data unavailable\n"); + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + rxon_band24 = !!(ctx->staging.flags & RXON_FLG_BAND_24G_MSK); + rxon_chnum = le16_to_cpu(ctx->staging.channel); + + stat_band24 = !!(((struct iwl_notif_statistics *) + stat_resp)->flag & + STATISTICS_REPLY_FLG_BAND_24G_MSK); + stat_chnum = le32_to_cpu(((struct iwl_notif_statistics *) + stat_resp)->flag) >> 16; + + /* Make sure we accumulate data for just the associated channel + * (even if scanning). */ + if ((rxon_chnum != stat_chnum) || (rxon_band24 != stat_band24)) { + IWL_DEBUG_CALIB(priv, "Stats not from chan=%d, band24=%d\n", + rxon_chnum, rxon_band24); + spin_unlock_irqrestore(&priv->lock, flags); + return; + } + + /* + * Accumulate beacon statistics values across + * "chain_noise_num_beacons" + */ + chain_noise_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & + IN_BAND_FILTER; + chain_noise_b = le32_to_cpu(rx_info->beacon_silence_rssi_b) & + IN_BAND_FILTER; + chain_noise_c = le32_to_cpu(rx_info->beacon_silence_rssi_c) & + IN_BAND_FILTER; + + chain_sig_a = le32_to_cpu(rx_info->beacon_rssi_a) & IN_BAND_FILTER; + chain_sig_b = le32_to_cpu(rx_info->beacon_rssi_b) & IN_BAND_FILTER; + chain_sig_c = le32_to_cpu(rx_info->beacon_rssi_c) & IN_BAND_FILTER; + + spin_unlock_irqrestore(&priv->lock, flags); + + data->beacon_count++; + + data->chain_noise_a = (chain_noise_a + data->chain_noise_a); + data->chain_noise_b = (chain_noise_b + data->chain_noise_b); + data->chain_noise_c = (chain_noise_c + data->chain_noise_c); + + data->chain_signal_a = (chain_sig_a + data->chain_signal_a); + data->chain_signal_b = (chain_sig_b + data->chain_signal_b); + data->chain_signal_c = (chain_sig_c + data->chain_signal_c); + + IWL_DEBUG_CALIB(priv, "chan=%d, band24=%d, beacon=%d\n", + rxon_chnum, rxon_band24, data->beacon_count); + IWL_DEBUG_CALIB(priv, "chain_sig: a %d b %d c %d\n", + chain_sig_a, chain_sig_b, chain_sig_c); + IWL_DEBUG_CALIB(priv, "chain_noise: a %d b %d c %d\n", + chain_noise_a, chain_noise_b, chain_noise_c); + + /* If this is the "chain_noise_num_beacons", determine: + * 1) Disconnected antennas (using signal strengths) + * 2) Differential gain (using silence noise) to balance receivers */ + if (data->beacon_count != + priv->cfg->base_params->chain_noise_num_beacons) + return; + + /* Analyze signal for disconnected antenna */ + iwl4965_find_disconn_antenna(priv, average_sig, data); + + /* Analyze noise for rx balance */ + average_noise[0] = data->chain_noise_a / + priv->cfg->base_params->chain_noise_num_beacons; + average_noise[1] = data->chain_noise_b / + priv->cfg->base_params->chain_noise_num_beacons; + average_noise[2] = data->chain_noise_c / + priv->cfg->base_params->chain_noise_num_beacons; + + for (i = 0; i < NUM_RX_CHAINS; i++) { + if (!(data->disconn_array[i]) && + (average_noise[i] <= min_average_noise)) { + /* This means that chain i is active and has + * lower noise values so far: */ + min_average_noise = average_noise[i]; + min_average_noise_antenna_i = i; + } + } + + IWL_DEBUG_CALIB(priv, "average_noise: a %d b %d c %d\n", + average_noise[0], average_noise[1], + average_noise[2]); + + IWL_DEBUG_CALIB(priv, "min_average_noise = %d, antenna %d\n", + min_average_noise, min_average_noise_antenna_i); + + iwl4965_gain_computation(priv, average_noise, + min_average_noise_antenna_i, min_average_noise, + iwl4965_find_first_chain(priv->cfg->valid_rx_ant)); + + /* Some power changes may have been made during the calibration. + * Update and commit the RXON + */ + if (priv->cfg->ops->lib->update_chain_flags) + priv->cfg->ops->lib->update_chain_flags(priv); + + data->state = IWL_CHAIN_NOISE_DONE; + iwl_legacy_power_update_mode(priv, false); +} + +void iwl4965_reset_run_time_calib(struct iwl_priv *priv) +{ + int i; + memset(&(priv->sensitivity_data), 0, + sizeof(struct iwl_sensitivity_data)); + memset(&(priv->chain_noise_data), 0, + sizeof(struct iwl_chain_noise_data)); + for (i = 0; i < NUM_RX_CHAINS; i++) + priv->chain_noise_data.delta_gain_code[i] = + CHAIN_NOISE_DELTA_GAIN_INIT_VAL; + + /* Ask for statistics now, the uCode will send notification + * periodically after association */ + iwl_legacy_send_statistics_request(priv, CMD_ASYNC, true); +} diff --git a/drivers/net/wireless/iwlwifi/iwl-legacy.h b/drivers/net/wireless/iwlegacy/iwl-4965-calib.h index 9f7b2f93596..f46c80e6e00 100644 --- a/drivers/net/wireless/iwlwifi/iwl-legacy.h +++ b/drivers/net/wireless/iwlegacy/iwl-4965-calib.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -59,21 +59,17 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ +#ifndef __iwl_4965_calib_h__ +#define __iwl_4965_calib_h__ -#ifndef __iwl_legacy_h__ -#define __iwl_legacy_h__ +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-commands.h" -/* mac80211 handlers */ -int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed); -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw); -void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes); -void iwl_legacy_tx_cmd_protection(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags); +void iwl4965_chain_noise_calibration(struct iwl_priv *priv, void *stat_resp); +void iwl4965_sensitivity_calibration(struct iwl_priv *priv, void *resp); +void iwl4965_init_sensitivity(struct iwl_priv *priv); +void iwl4965_reset_run_time_calib(struct iwl_priv *priv); +void iwl4965_calib_free_results(struct iwl_priv *priv); -irqreturn_t iwl_isr_legacy(int irq, void *data); - -#endif /* __iwl_legacy_h__ */ +#endif /* __iwl_4965_calib_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.c b/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.c new file mode 100644 index 00000000000..1c93665766e --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.c @@ -0,0 +1,774 @@ +/****************************************************************************** +* +* GPL LICENSE SUMMARY +* +* Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of version 2 of the GNU General Public License as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, +* USA +* +* The full GNU General Public License is included in this distribution +* in the file called LICENSE.GPL. +* +* Contact Information: +* Intel Linux Wireless <ilw@linux.intel.com> +* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 +*****************************************************************************/ +#include "iwl-4965.h" +#include "iwl-4965-debugfs.h" + +static const char *fmt_value = " %-30s %10u\n"; +static const char *fmt_table = " %-30s %10u %10u %10u %10u\n"; +static const char *fmt_header = + "%-32s current cumulative delta max\n"; + +static int iwl4965_statistics_flag(struct iwl_priv *priv, char *buf, int bufsz) +{ + int p = 0; + u32 flag; + + flag = le32_to_cpu(priv->_4965.statistics.flag); + + p += scnprintf(buf + p, bufsz - p, "Statistics Flag(0x%X):\n", flag); + if (flag & UCODE_STATISTICS_CLEAR_MSK) + p += scnprintf(buf + p, bufsz - p, + "\tStatistics have been cleared\n"); + p += scnprintf(buf + p, bufsz - p, "\tOperational Frequency: %s\n", + (flag & UCODE_STATISTICS_FREQUENCY_MSK) + ? "2.4 GHz" : "5.2 GHz"); + p += scnprintf(buf + p, bufsz - p, "\tTGj Narrow Band: %s\n", + (flag & UCODE_STATISTICS_NARROW_BAND_MSK) + ? "enabled" : "disabled"); + + return p; +} + +ssize_t iwl4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_rx_phy) * 40 + + sizeof(struct statistics_rx_non_phy) * 40 + + sizeof(struct statistics_rx_ht_phy) * 40 + 400; + ssize_t ret; + struct statistics_rx_phy *ofdm, *accum_ofdm, *delta_ofdm, *max_ofdm; + struct statistics_rx_phy *cck, *accum_cck, *delta_cck, *max_cck; + struct statistics_rx_non_phy *general, *accum_general; + struct statistics_rx_non_phy *delta_general, *max_general; + struct statistics_rx_ht_phy *ht, *accum_ht, *delta_ht, *max_ht; + + if (!iwl_legacy_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* + * the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + ofdm = &priv->_4965.statistics.rx.ofdm; + cck = &priv->_4965.statistics.rx.cck; + general = &priv->_4965.statistics.rx.general; + ht = &priv->_4965.statistics.rx.ofdm_ht; + accum_ofdm = &priv->_4965.accum_statistics.rx.ofdm; + accum_cck = &priv->_4965.accum_statistics.rx.cck; + accum_general = &priv->_4965.accum_statistics.rx.general; + accum_ht = &priv->_4965.accum_statistics.rx.ofdm_ht; + delta_ofdm = &priv->_4965.delta_statistics.rx.ofdm; + delta_cck = &priv->_4965.delta_statistics.rx.cck; + delta_general = &priv->_4965.delta_statistics.rx.general; + delta_ht = &priv->_4965.delta_statistics.rx.ofdm_ht; + max_ofdm = &priv->_4965.max_delta.rx.ofdm; + max_cck = &priv->_4965.max_delta.rx.cck; + max_general = &priv->_4965.max_delta.rx.general; + max_ht = &priv->_4965.max_delta.rx.ofdm_ht; + + pos += iwl4965_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(ofdm->ina_cnt), + accum_ofdm->ina_cnt, + delta_ofdm->ina_cnt, max_ofdm->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(ofdm->fina_cnt), accum_ofdm->fina_cnt, + delta_ofdm->fina_cnt, max_ofdm->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ofdm->plcp_err), accum_ofdm->plcp_err, + delta_ofdm->plcp_err, max_ofdm->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ofdm->crc32_err), accum_ofdm->crc32_err, + delta_ofdm->crc32_err, max_ofdm->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ofdm->overrun_err), + accum_ofdm->overrun_err, delta_ofdm->overrun_err, + max_ofdm->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ofdm->early_overrun_err), + accum_ofdm->early_overrun_err, + delta_ofdm->early_overrun_err, + max_ofdm->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ofdm->crc32_good), + accum_ofdm->crc32_good, delta_ofdm->crc32_good, + max_ofdm->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(ofdm->false_alarm_cnt), + accum_ofdm->false_alarm_cnt, + delta_ofdm->false_alarm_cnt, + max_ofdm->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(ofdm->fina_sync_err_cnt), + accum_ofdm->fina_sync_err_cnt, + delta_ofdm->fina_sync_err_cnt, + max_ofdm->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(ofdm->sfd_timeout), + accum_ofdm->sfd_timeout, delta_ofdm->sfd_timeout, + max_ofdm->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(ofdm->fina_timeout), + accum_ofdm->fina_timeout, delta_ofdm->fina_timeout, + max_ofdm->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(ofdm->unresponded_rts), + accum_ofdm->unresponded_rts, + delta_ofdm->unresponded_rts, + max_ofdm->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(ofdm->rxe_frame_limit_overrun), + accum_ofdm->rxe_frame_limit_overrun, + delta_ofdm->rxe_frame_limit_overrun, + max_ofdm->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(ofdm->sent_ack_cnt), + accum_ofdm->sent_ack_cnt, delta_ofdm->sent_ack_cnt, + max_ofdm->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(ofdm->sent_cts_cnt), + accum_ofdm->sent_cts_cnt, delta_ofdm->sent_cts_cnt, + max_ofdm->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(ofdm->sent_ba_rsp_cnt), + accum_ofdm->sent_ba_rsp_cnt, + delta_ofdm->sent_ba_rsp_cnt, + max_ofdm->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(ofdm->dsp_self_kill), + accum_ofdm->dsp_self_kill, + delta_ofdm->dsp_self_kill, + max_ofdm->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ofdm->mh_format_err), + accum_ofdm->mh_format_err, + delta_ofdm->mh_format_err, + max_ofdm->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(ofdm->re_acq_main_rssi_sum), + accum_ofdm->re_acq_main_rssi_sum, + delta_ofdm->re_acq_main_rssi_sum, + max_ofdm->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - CCK:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_cnt:", + le32_to_cpu(cck->ina_cnt), accum_cck->ina_cnt, + delta_cck->ina_cnt, max_cck->ina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_cnt:", + le32_to_cpu(cck->fina_cnt), accum_cck->fina_cnt, + delta_cck->fina_cnt, max_cck->fina_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(cck->plcp_err), accum_cck->plcp_err, + delta_cck->plcp_err, max_cck->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(cck->crc32_err), accum_cck->crc32_err, + delta_cck->crc32_err, max_cck->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(cck->overrun_err), + accum_cck->overrun_err, delta_cck->overrun_err, + max_cck->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(cck->early_overrun_err), + accum_cck->early_overrun_err, + delta_cck->early_overrun_err, + max_cck->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(cck->crc32_good), accum_cck->crc32_good, + delta_cck->crc32_good, max_cck->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "false_alarm_cnt:", + le32_to_cpu(cck->false_alarm_cnt), + accum_cck->false_alarm_cnt, + delta_cck->false_alarm_cnt, max_cck->false_alarm_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_sync_err_cnt:", + le32_to_cpu(cck->fina_sync_err_cnt), + accum_cck->fina_sync_err_cnt, + delta_cck->fina_sync_err_cnt, + max_cck->fina_sync_err_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sfd_timeout:", + le32_to_cpu(cck->sfd_timeout), + accum_cck->sfd_timeout, delta_cck->sfd_timeout, + max_cck->sfd_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "fina_timeout:", + le32_to_cpu(cck->fina_timeout), + accum_cck->fina_timeout, delta_cck->fina_timeout, + max_cck->fina_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unresponded_rts:", + le32_to_cpu(cck->unresponded_rts), + accum_cck->unresponded_rts, delta_cck->unresponded_rts, + max_cck->unresponded_rts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rxe_frame_lmt_ovrun:", + le32_to_cpu(cck->rxe_frame_limit_overrun), + accum_cck->rxe_frame_limit_overrun, + delta_cck->rxe_frame_limit_overrun, + max_cck->rxe_frame_limit_overrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ack_cnt:", + le32_to_cpu(cck->sent_ack_cnt), + accum_cck->sent_ack_cnt, delta_cck->sent_ack_cnt, + max_cck->sent_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_cts_cnt:", + le32_to_cpu(cck->sent_cts_cnt), + accum_cck->sent_cts_cnt, delta_cck->sent_cts_cnt, + max_cck->sent_cts_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sent_ba_rsp_cnt:", + le32_to_cpu(cck->sent_ba_rsp_cnt), + accum_cck->sent_ba_rsp_cnt, + delta_cck->sent_ba_rsp_cnt, + max_cck->sent_ba_rsp_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_self_kill:", + le32_to_cpu(cck->dsp_self_kill), + accum_cck->dsp_self_kill, delta_cck->dsp_self_kill, + max_cck->dsp_self_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(cck->mh_format_err), + accum_cck->mh_format_err, delta_cck->mh_format_err, + max_cck->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "re_acq_main_rssi_sum:", + le32_to_cpu(cck->re_acq_main_rssi_sum), + accum_cck->re_acq_main_rssi_sum, + delta_cck->re_acq_main_rssi_sum, + max_cck->re_acq_main_rssi_sum); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - GENERAL:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_cts:", + le32_to_cpu(general->bogus_cts), + accum_general->bogus_cts, delta_general->bogus_cts, + max_general->bogus_cts); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bogus_ack:", + le32_to_cpu(general->bogus_ack), + accum_general->bogus_ack, delta_general->bogus_ack, + max_general->bogus_ack); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_bssid_frames:", + le32_to_cpu(general->non_bssid_frames), + accum_general->non_bssid_frames, + delta_general->non_bssid_frames, + max_general->non_bssid_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "filtered_frames:", + le32_to_cpu(general->filtered_frames), + accum_general->filtered_frames, + delta_general->filtered_frames, + max_general->filtered_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "non_channel_beacons:", + le32_to_cpu(general->non_channel_beacons), + accum_general->non_channel_beacons, + delta_general->non_channel_beacons, + max_general->non_channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_beacons:", + le32_to_cpu(general->channel_beacons), + accum_general->channel_beacons, + delta_general->channel_beacons, + max_general->channel_beacons); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_missed_bcon:", + le32_to_cpu(general->num_missed_bcon), + accum_general->num_missed_bcon, + delta_general->num_missed_bcon, + max_general->num_missed_bcon); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "adc_rx_saturation_time:", + le32_to_cpu(general->adc_rx_saturation_time), + accum_general->adc_rx_saturation_time, + delta_general->adc_rx_saturation_time, + max_general->adc_rx_saturation_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ina_detect_search_tm:", + le32_to_cpu(general->ina_detection_search_time), + accum_general->ina_detection_search_time, + delta_general->ina_detection_search_time, + max_general->ina_detection_search_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_a:", + le32_to_cpu(general->beacon_silence_rssi_a), + accum_general->beacon_silence_rssi_a, + delta_general->beacon_silence_rssi_a, + max_general->beacon_silence_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_b:", + le32_to_cpu(general->beacon_silence_rssi_b), + accum_general->beacon_silence_rssi_b, + delta_general->beacon_silence_rssi_b, + max_general->beacon_silence_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_silence_rssi_c:", + le32_to_cpu(general->beacon_silence_rssi_c), + accum_general->beacon_silence_rssi_c, + delta_general->beacon_silence_rssi_c, + max_general->beacon_silence_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "interference_data_flag:", + le32_to_cpu(general->interference_data_flag), + accum_general->interference_data_flag, + delta_general->interference_data_flag, + max_general->interference_data_flag); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "channel_load:", + le32_to_cpu(general->channel_load), + accum_general->channel_load, + delta_general->channel_load, + max_general->channel_load); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dsp_false_alarms:", + le32_to_cpu(general->dsp_false_alarms), + accum_general->dsp_false_alarms, + delta_general->dsp_false_alarms, + max_general->dsp_false_alarms); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_a:", + le32_to_cpu(general->beacon_rssi_a), + accum_general->beacon_rssi_a, + delta_general->beacon_rssi_a, + max_general->beacon_rssi_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_b:", + le32_to_cpu(general->beacon_rssi_b), + accum_general->beacon_rssi_b, + delta_general->beacon_rssi_b, + max_general->beacon_rssi_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_rssi_c:", + le32_to_cpu(general->beacon_rssi_c), + accum_general->beacon_rssi_c, + delta_general->beacon_rssi_c, + max_general->beacon_rssi_c); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_a:", + le32_to_cpu(general->beacon_energy_a), + accum_general->beacon_energy_a, + delta_general->beacon_energy_a, + max_general->beacon_energy_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_b:", + le32_to_cpu(general->beacon_energy_b), + accum_general->beacon_energy_b, + delta_general->beacon_energy_b, + max_general->beacon_energy_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "beacon_energy_c:", + le32_to_cpu(general->beacon_energy_c), + accum_general->beacon_energy_c, + delta_general->beacon_energy_c, + max_general->beacon_energy_c); + + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Rx - OFDM_HT:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "plcp_err:", + le32_to_cpu(ht->plcp_err), accum_ht->plcp_err, + delta_ht->plcp_err, max_ht->plcp_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "overrun_err:", + le32_to_cpu(ht->overrun_err), accum_ht->overrun_err, + delta_ht->overrun_err, max_ht->overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "early_overrun_err:", + le32_to_cpu(ht->early_overrun_err), + accum_ht->early_overrun_err, + delta_ht->early_overrun_err, + max_ht->early_overrun_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_good:", + le32_to_cpu(ht->crc32_good), accum_ht->crc32_good, + delta_ht->crc32_good, max_ht->crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "crc32_err:", + le32_to_cpu(ht->crc32_err), accum_ht->crc32_err, + delta_ht->crc32_err, max_ht->crc32_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "mh_format_err:", + le32_to_cpu(ht->mh_format_err), + accum_ht->mh_format_err, + delta_ht->mh_format_err, max_ht->mh_format_err); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_crc32_good:", + le32_to_cpu(ht->agg_crc32_good), + accum_ht->agg_crc32_good, + delta_ht->agg_crc32_good, max_ht->agg_crc32_good); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_mpdu_cnt:", + le32_to_cpu(ht->agg_mpdu_cnt), + accum_ht->agg_mpdu_cnt, + delta_ht->agg_mpdu_cnt, max_ht->agg_mpdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg_cnt:", + le32_to_cpu(ht->agg_cnt), accum_ht->agg_cnt, + delta_ht->agg_cnt, max_ht->agg_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "unsupport_mcs:", + le32_to_cpu(ht->unsupport_mcs), + accum_ht->unsupport_mcs, + delta_ht->unsupport_mcs, max_ht->unsupport_mcs); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +ssize_t iwl4965_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = (sizeof(struct statistics_tx) * 48) + 250; + ssize_t ret; + struct statistics_tx *tx, *accum_tx, *delta_tx, *max_tx; + + if (!iwl_legacy_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + tx = &priv->_4965.statistics.tx; + accum_tx = &priv->_4965.accum_statistics.tx; + delta_tx = &priv->_4965.delta_statistics.tx; + max_tx = &priv->_4965.max_delta.tx; + + pos += iwl4965_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_Tx:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "preamble:", + le32_to_cpu(tx->preamble_cnt), + accum_tx->preamble_cnt, + delta_tx->preamble_cnt, max_tx->preamble_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_detected_cnt:", + le32_to_cpu(tx->rx_detected_cnt), + accum_tx->rx_detected_cnt, + delta_tx->rx_detected_cnt, max_tx->rx_detected_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_defer_cnt:", + le32_to_cpu(tx->bt_prio_defer_cnt), + accum_tx->bt_prio_defer_cnt, + delta_tx->bt_prio_defer_cnt, + max_tx->bt_prio_defer_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "bt_prio_kill_cnt:", + le32_to_cpu(tx->bt_prio_kill_cnt), + accum_tx->bt_prio_kill_cnt, + delta_tx->bt_prio_kill_cnt, + max_tx->bt_prio_kill_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "few_bytes_cnt:", + le32_to_cpu(tx->few_bytes_cnt), + accum_tx->few_bytes_cnt, + delta_tx->few_bytes_cnt, max_tx->few_bytes_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout:", + le32_to_cpu(tx->cts_timeout), accum_tx->cts_timeout, + delta_tx->cts_timeout, max_tx->cts_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_timeout:", + le32_to_cpu(tx->ack_timeout), + accum_tx->ack_timeout, + delta_tx->ack_timeout, max_tx->ack_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "expected_ack_cnt:", + le32_to_cpu(tx->expected_ack_cnt), + accum_tx->expected_ack_cnt, + delta_tx->expected_ack_cnt, + max_tx->expected_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "actual_ack_cnt:", + le32_to_cpu(tx->actual_ack_cnt), + accum_tx->actual_ack_cnt, + delta_tx->actual_ack_cnt, + max_tx->actual_ack_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "dump_msdu_cnt:", + le32_to_cpu(tx->dump_msdu_cnt), + accum_tx->dump_msdu_cnt, + delta_tx->dump_msdu_cnt, + max_tx->dump_msdu_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_nxt_frame_mismatch:", + le32_to_cpu(tx->burst_abort_next_frame_mismatch_cnt), + accum_tx->burst_abort_next_frame_mismatch_cnt, + delta_tx->burst_abort_next_frame_mismatch_cnt, + max_tx->burst_abort_next_frame_mismatch_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "abort_missing_nxt_frame:", + le32_to_cpu(tx->burst_abort_missing_next_frame_cnt), + accum_tx->burst_abort_missing_next_frame_cnt, + delta_tx->burst_abort_missing_next_frame_cnt, + max_tx->burst_abort_missing_next_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "cts_timeout_collision:", + le32_to_cpu(tx->cts_timeout_collision), + accum_tx->cts_timeout_collision, + delta_tx->cts_timeout_collision, + max_tx->cts_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "ack_ba_timeout_collision:", + le32_to_cpu(tx->ack_or_ba_timeout_collision), + accum_tx->ack_or_ba_timeout_collision, + delta_tx->ack_or_ba_timeout_collision, + max_tx->ack_or_ba_timeout_collision); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_timeout:", + le32_to_cpu(tx->agg.ba_timeout), + accum_tx->agg.ba_timeout, + delta_tx->agg.ba_timeout, + max_tx->agg.ba_timeout); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg ba_resched_frames:", + le32_to_cpu(tx->agg.ba_reschedule_frames), + accum_tx->agg.ba_reschedule_frames, + delta_tx->agg.ba_reschedule_frames, + max_tx->agg.ba_reschedule_frames); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg_frame:", + le32_to_cpu(tx->agg.scd_query_agg_frame_cnt), + accum_tx->agg.scd_query_agg_frame_cnt, + delta_tx->agg.scd_query_agg_frame_cnt, + max_tx->agg.scd_query_agg_frame_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_no_agg:", + le32_to_cpu(tx->agg.scd_query_no_agg), + accum_tx->agg.scd_query_no_agg, + delta_tx->agg.scd_query_no_agg, + max_tx->agg.scd_query_no_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_agg:", + le32_to_cpu(tx->agg.scd_query_agg), + accum_tx->agg.scd_query_agg, + delta_tx->agg.scd_query_agg, + max_tx->agg.scd_query_agg); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg scd_query_mismatch:", + le32_to_cpu(tx->agg.scd_query_mismatch), + accum_tx->agg.scd_query_mismatch, + delta_tx->agg.scd_query_mismatch, + max_tx->agg.scd_query_mismatch); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg frame_not_ready:", + le32_to_cpu(tx->agg.frame_not_ready), + accum_tx->agg.frame_not_ready, + delta_tx->agg.frame_not_ready, + max_tx->agg.frame_not_ready); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg underrun:", + le32_to_cpu(tx->agg.underrun), + accum_tx->agg.underrun, + delta_tx->agg.underrun, max_tx->agg.underrun); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg bt_prio_kill:", + le32_to_cpu(tx->agg.bt_prio_kill), + accum_tx->agg.bt_prio_kill, + delta_tx->agg.bt_prio_kill, + max_tx->agg.bt_prio_kill); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "agg rx_ba_rsp_cnt:", + le32_to_cpu(tx->agg.rx_ba_rsp_cnt), + accum_tx->agg.rx_ba_rsp_cnt, + delta_tx->agg.rx_ba_rsp_cnt, + max_tx->agg.rx_ba_rsp_cnt); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +ssize_t +iwl4965_ucode_general_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0; + char *buf; + int bufsz = sizeof(struct statistics_general) * 10 + 300; + ssize_t ret; + struct statistics_general_common *general, *accum_general; + struct statistics_general_common *delta_general, *max_general; + struct statistics_dbg *dbg, *accum_dbg, *delta_dbg, *max_dbg; + struct statistics_div *div, *accum_div, *delta_div, *max_div; + + if (!iwl_legacy_is_alive(priv)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + /* the statistic information display here is based on + * the last statistics notification from uCode + * might not reflect the current uCode activity + */ + general = &priv->_4965.statistics.general.common; + dbg = &priv->_4965.statistics.general.common.dbg; + div = &priv->_4965.statistics.general.common.div; + accum_general = &priv->_4965.accum_statistics.general.common; + accum_dbg = &priv->_4965.accum_statistics.general.common.dbg; + accum_div = &priv->_4965.accum_statistics.general.common.div; + delta_general = &priv->_4965.delta_statistics.general.common; + max_general = &priv->_4965.max_delta.general.common; + delta_dbg = &priv->_4965.delta_statistics.general.common.dbg; + max_dbg = &priv->_4965.max_delta.general.common.dbg; + delta_div = &priv->_4965.delta_statistics.general.common.div; + max_div = &priv->_4965.max_delta.general.common.div; + + pos += iwl4965_statistics_flag(priv, buf, bufsz); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_header, "Statistics_General:"); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "temperature:", + le32_to_cpu(general->temperature)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_value, "ttl_timestamp:", + le32_to_cpu(general->ttl_timestamp)); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_check:", + le32_to_cpu(dbg->burst_check), + accum_dbg->burst_check, + delta_dbg->burst_check, max_dbg->burst_check); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "burst_count:", + le32_to_cpu(dbg->burst_count), + accum_dbg->burst_count, + delta_dbg->burst_count, max_dbg->burst_count); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "wait_for_silence_timeout_count:", + le32_to_cpu(dbg->wait_for_silence_timeout_cnt), + accum_dbg->wait_for_silence_timeout_cnt, + delta_dbg->wait_for_silence_timeout_cnt, + max_dbg->wait_for_silence_timeout_cnt); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "sleep_time:", + le32_to_cpu(general->sleep_time), + accum_general->sleep_time, + delta_general->sleep_time, max_general->sleep_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_out:", + le32_to_cpu(general->slots_out), + accum_general->slots_out, + delta_general->slots_out, max_general->slots_out); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "slots_idle:", + le32_to_cpu(general->slots_idle), + accum_general->slots_idle, + delta_general->slots_idle, max_general->slots_idle); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_a:", + le32_to_cpu(div->tx_on_a), accum_div->tx_on_a, + delta_div->tx_on_a, max_div->tx_on_a); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "tx_on_b:", + le32_to_cpu(div->tx_on_b), accum_div->tx_on_b, + delta_div->tx_on_b, max_div->tx_on_b); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "exec_time:", + le32_to_cpu(div->exec_time), accum_div->exec_time, + delta_div->exec_time, max_div->exec_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "probe_time:", + le32_to_cpu(div->probe_time), accum_div->probe_time, + delta_div->probe_time, max_div->probe_time); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "rx_enable_counter:", + le32_to_cpu(general->rx_enable_counter), + accum_general->rx_enable_counter, + delta_general->rx_enable_counter, + max_general->rx_enable_counter); + pos += scnprintf(buf + pos, bufsz - pos, + fmt_table, "num_of_sos_states:", + le32_to_cpu(general->num_of_sos_states), + accum_general->num_of_sos_states, + delta_general->num_of_sos_states, + max_general->num_of_sos_states); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.h b/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.h new file mode 100644 index 00000000000..6c8e35361a9 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-debugfs.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-debug.h" + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS +ssize_t iwl4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t iwl4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +ssize_t iwl4965_ucode_general_stats_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos); +#else +static ssize_t +iwl4965_ucode_rx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return 0; +} +static ssize_t +iwl4965_ucode_tx_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return 0; +} +static ssize_t +iwl4965_ucode_general_stats_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + return 0; +} +#endif diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-eeprom.c b/drivers/net/wireless/iwlegacy/iwl-4965-eeprom.c new file mode 100644 index 00000000000..cb9baab1ff7 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-eeprom.c @@ -0,0 +1,154 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <net/mac80211.h> + +#include "iwl-commands.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-debug.h" +#include "iwl-4965.h" +#include "iwl-io.h" + +/****************************************************************************** + * + * EEPROM related functions + * +******************************************************************************/ + +/* + * The device's EEPROM semaphore prevents conflicts between driver and uCode + * when accessing the EEPROM; each access is a series of pulses to/from the + * EEPROM chip, not a single event, so even reads could conflict if they + * weren't arbitrated by the semaphore. + */ +int iwl4965_eeprom_acquire_semaphore(struct iwl_priv *priv) +{ + u16 count; + int ret; + + for (count = 0; count < EEPROM_SEM_RETRY_LIMIT; count++) { + /* Request semaphore */ + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + + /* See if we got it */ + ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM, + EEPROM_SEM_TIMEOUT); + if (ret >= 0) { + IWL_DEBUG_IO(priv, + "Acquired semaphore after %d tries.\n", + count+1); + return ret; + } + } + + return ret; +} + +void iwl4965_eeprom_release_semaphore(struct iwl_priv *priv) +{ + iwl_legacy_clear_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM); + +} + +int iwl4965_eeprom_check_version(struct iwl_priv *priv) +{ + u16 eeprom_ver; + u16 calib_ver; + + eeprom_ver = iwl_legacy_eeprom_query16(priv, EEPROM_VERSION); + calib_ver = iwl_legacy_eeprom_query16(priv, + EEPROM_4965_CALIB_VERSION_OFFSET); + + if (eeprom_ver < priv->cfg->eeprom_ver || + calib_ver < priv->cfg->eeprom_calib_ver) + goto err; + + IWL_INFO(priv, "device EEPROM VER=0x%x, CALIB=0x%x\n", + eeprom_ver, calib_ver); + + return 0; +err: + IWL_ERR(priv, "Unsupported (too old) EEPROM VER=0x%x < 0x%x " + "CALIB=0x%x < 0x%x\n", + eeprom_ver, priv->cfg->eeprom_ver, + calib_ver, priv->cfg->eeprom_calib_ver); + return -EINVAL; + +} + +void iwl4965_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac) +{ + const u8 *addr = iwl_legacy_eeprom_query_addr(priv, + EEPROM_MAC_ADDRESS); + memcpy(mac, addr, ETH_ALEN); +} diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h b/drivers/net/wireless/iwlegacy/iwl-4965-hw.h index 9166794eda0..08b189c8472 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965-hw.h +++ b/drivers/net/wireless/iwlegacy/iwl-4965-hw.h @@ -5,7 +5,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,7 +30,7 @@ * * BSD LICENSE * - * Copyright(c) 2005 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -789,4 +789,26 @@ struct iwl4965_scd_bc_tbl { u8 pad[1024 - (TFD_QUEUE_BC_SIZE) * sizeof(__le16)]; } __packed; + +#define IWL4965_RTC_INST_LOWER_BOUND (0x000000) + +/* RSSI to dBm */ +#define IWL4965_RSSI_OFFSET 44 + +/* PCI registers */ +#define PCI_CFG_RETRY_TIMEOUT 0x041 + +/* PCI register values */ +#define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 +#define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 + +#define IWL4965_DEFAULT_TX_RETRY 15 + +/* Limit range of txpower output target to be between these values */ +#define IWL4965_TX_POWER_TARGET_POWER_MIN (0) /* 0 dBm: 1 milliwatt */ + +/* EEPROM */ +#define IWL4965_FIRST_AMPDU_QUEUE 10 + + #endif /* !__iwl_4965_hw_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-led.c b/drivers/net/wireless/iwlegacy/iwl-4965-led.c new file mode 100644 index 00000000000..26d324e3069 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-led.c @@ -0,0 +1,74 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include <asm/unaligned.h> + +#include "iwl-commands.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-4965-led.h" + +/* Send led command */ +static int +iwl4965_send_led_cmd(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_LEDS_CMD, + .len = sizeof(struct iwl_led_cmd), + .data = led_cmd, + .flags = CMD_ASYNC, + .callback = NULL, + }; + u32 reg; + + reg = iwl_read32(priv, CSR_LED_REG); + if (reg != (reg & CSR_LED_BSM_CTRL_MSK)) + iwl_write32(priv, CSR_LED_REG, reg & CSR_LED_BSM_CTRL_MSK); + + return iwl_legacy_send_cmd(priv, &cmd); +} + +/* Set led register off */ +void iwl4965_led_enable(struct iwl_priv *priv) +{ + iwl_write32(priv, CSR_LED_REG, CSR_LED_REG_TRUN_ON); +} + +const struct iwl_led_ops iwl4965_led_ops = { + .cmd = iwl4965_send_led_cmd, +}; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/fw.h b/drivers/net/wireless/iwlegacy/iwl-4965-led.h index a3bbac811d0..5ed3615fc33 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/fw.h +++ b/drivers/net/wireless/iwlegacy/iwl-4965-led.h @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2009-2010 Realtek Corporation. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -19,12 +19,15 @@ * file called LICENSE. * * Contact Information: - * wlanfae <wlanfae@realtek.com> - * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, - * Hsinchu 300, Taiwan. - * - * Larry Finger <Larry.Finger@lwfinger.net> + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * *****************************************************************************/ -#include "../rtl8192ce/fw.h" +#ifndef __iwl_4965_led_h__ +#define __iwl_4965_led_h__ + +extern const struct iwl_led_ops iwl4965_led_ops; +void iwl4965_led_enable(struct iwl_priv *priv); + +#endif /* __iwl_4965_led_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-lib.c b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c new file mode 100644 index 00000000000..5a8a3cce27b --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-lib.c @@ -0,0 +1,1260 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include <linux/etherdevice.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +#include "iwl-4965-hw.h" +#include "iwl-4965.h" +#include "iwl-sta.h" + +void iwl4965_check_abort_status(struct iwl_priv *priv, + u8 frame_count, u32 status) +{ + if (frame_count == 1 && status == TX_STATUS_FAIL_RFKILL_FLUSH) { + IWL_ERR(priv, "Tx flush command to flush out all frames\n"); + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) + queue_work(priv->workqueue, &priv->tx_flush); + } +} + +/* + * EEPROM + */ +struct iwl_mod_params iwl4965_mod_params = { + .amsdu_size_8K = 1, + .restart_fw = 1, + /* the rest are 0 by default */ +}; + +void iwl4965_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) +{ + unsigned long flags; + int i; + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) { + /* In the reset function, these buffers may have been allocated + * to an SKB, so we need to unmap and free potential storage */ + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __iwl_legacy_free_pages(priv, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } + + for (i = 0; i < RX_QUEUE_SIZE; i++) + rxq->queue[i] = NULL; + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + spin_unlock_irqrestore(&rxq->lock, flags); +} + +int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq) +{ + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + u32 rb_timeout = 0; + + if (priv->cfg->mod_params->amsdu_size_8K) + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K; + else + rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; + + /* Stop Rx DMA */ + iwl_legacy_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + + /* Reset driver's Rx queue write index */ + iwl_legacy_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl_legacy_write_direct32(priv, FH_RSCSR_CHNL0_RBDCB_BASE_REG, + (u32)(rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + iwl_legacy_write_direct32(priv, FH_RSCSR_CHNL0_STTS_WPTR_REG, + rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + iwl_legacy_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + rb_size| + (rb_timeout << FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS)| + (rfdnlog << FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS)); + + /* Set interrupt coalescing timer to default (2048 usecs) */ + iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + + return 0; +} + +static void iwl4965_set_pwr_vmain(struct iwl_priv *priv) +{ +/* + * (for documentation purposes) + * to set power to V_AUX, do: + + if (pci_pme_capable(priv->pci_dev, PCI_D3cold)) + iwl_legacy_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VAUX, + ~APMG_PS_CTRL_MSK_PWR_SRC); + */ + + iwl_legacy_set_bits_mask_prph(priv, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, + ~APMG_PS_CTRL_MSK_PWR_SRC); +} + +int iwl4965_hw_nic_init(struct iwl_priv *priv) +{ + unsigned long flags; + struct iwl_rx_queue *rxq = &priv->rxq; + int ret; + + /* nic_init */ + spin_lock_irqsave(&priv->lock, flags); + priv->cfg->ops->lib->apm_ops.init(priv); + + /* Set interrupt coalescing calibration timer to default (512 usecs) */ + iwl_write8(priv, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); + + spin_unlock_irqrestore(&priv->lock, flags); + + iwl4965_set_pwr_vmain(priv); + + priv->cfg->ops->lib->apm_ops.config(priv); + + /* Allocate the RX queue, or reset if it is already allocated */ + if (!rxq->bd) { + ret = iwl_legacy_rx_queue_alloc(priv); + if (ret) { + IWL_ERR(priv, "Unable to initialize Rx queue\n"); + return -ENOMEM; + } + } else + iwl4965_rx_queue_reset(priv, rxq); + + iwl4965_rx_replenish(priv); + + iwl4965_rx_init(priv, rxq); + + spin_lock_irqsave(&priv->lock, flags); + + rxq->need_update = 1; + iwl_legacy_rx_queue_update_write_ptr(priv, rxq); + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Allocate or reset and init all Tx and Command queues */ + if (!priv->txq) { + ret = iwl4965_txq_ctx_alloc(priv); + if (ret) + return ret; + } else + iwl4965_txq_ctx_reset(priv); + + set_bit(STATUS_INIT, &priv->status); + + return 0; +} + +/** + * iwl4965_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwl4965_dma_addr2rbd_ptr(struct iwl_priv *priv, + dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)(dma_addr >> 8)); +} + +/** + * iwl4965_rx_queue_restock - refill RX queue from pre-allocated pool + * + * If there are slots in the RX queue that need to be restocked, + * and we have free pre-allocated buffers, fill the ranks as much + * as we can, pulling from rx_free. + * + * This moves the 'write' index forward to catch up with 'processed', and + * also updates the memory address in the firmware to reference the new + * target buffer. + */ +void iwl4965_rx_queue_restock(struct iwl_priv *priv) +{ + struct iwl_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl_rx_mem_buffer *rxb; + unsigned long flags; + + spin_lock_irqsave(&rxq->lock, flags); + while ((iwl_legacy_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + /* The overwritten rxb must be a used one */ + rxb = rxq->queue[rxq->write]; + BUG_ON(rxb && rxb->page); + + /* Get next free Rx buffer, remove from free list */ + element = rxq->rx_free.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + + /* Point to Rx buffer via next RBD in circular buffer */ + rxq->bd[rxq->write] = iwl4965_dma_addr2rbd_ptr(priv, + rxb->page_dma); + rxq->queue[rxq->write] = rxb; + rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; + rxq->free_count--; + } + spin_unlock_irqrestore(&rxq->lock, flags); + /* If the pre-allocated buffer pool is dropping low, schedule to + * refill it */ + if (rxq->free_count <= RX_LOW_WATERMARK) + queue_work(priv->workqueue, &priv->rx_replenish); + + + /* If we've added more space for the firmware to place data, tell it. + * Increment device's write pointer in multiples of 8. */ + if (rxq->write_actual != (rxq->write & ~0x7)) { + spin_lock_irqsave(&rxq->lock, flags); + rxq->need_update = 1; + spin_unlock_irqrestore(&rxq->lock, flags); + iwl_legacy_rx_queue_update_write_ptr(priv, rxq); + } +} + +/** + * iwl4965_rx_replenish - Move all used packet from rx_used to rx_free + * + * When moving to rx_free an SKB is allocated for the slot. + * + * Also restock the Rx queue via iwl_rx_queue_restock. + * This is called as a scheduled work item (except for during initialization) + */ +static void iwl4965_rx_allocate(struct iwl_priv *priv, gfp_t priority) +{ + struct iwl_rx_queue *rxq = &priv->rxq; + struct list_head *element; + struct iwl_rx_mem_buffer *rxb; + struct page *page; + unsigned long flags; + gfp_t gfp_mask = priority; + + while (1) { + spin_lock_irqsave(&rxq->lock, flags); + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + return; + } + spin_unlock_irqrestore(&rxq->lock, flags); + + if (rxq->free_count > RX_LOW_WATERMARK) + gfp_mask |= __GFP_NOWARN; + + if (priv->hw_params.rx_page_order > 0) + gfp_mask |= __GFP_COMP; + + /* Alloc a new receive buffer */ + page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order); + if (!page) { + if (net_ratelimit()) + IWL_DEBUG_INFO(priv, "alloc_pages failed, " + "order: %d\n", + priv->hw_params.rx_page_order); + + if ((rxq->free_count <= RX_LOW_WATERMARK) && + net_ratelimit()) + IWL_CRIT(priv, + "Failed to alloc_pages with %s. " + "Only %u free buffers remaining.\n", + priority == GFP_ATOMIC ? + "GFP_ATOMIC" : "GFP_KERNEL", + rxq->free_count); + /* We don't reschedule replenish work here -- we will + * call the restock method and if it still needs + * more buffers it will schedule replenish */ + return; + } + + spin_lock_irqsave(&rxq->lock, flags); + + if (list_empty(&rxq->rx_used)) { + spin_unlock_irqrestore(&rxq->lock, flags); + __free_pages(page, priv->hw_params.rx_page_order); + return; + } + element = rxq->rx_used.next; + rxb = list_entry(element, struct iwl_rx_mem_buffer, list); + list_del(element); + + spin_unlock_irqrestore(&rxq->lock, flags); + + BUG_ON(rxb->page); + rxb->page = page; + /* Get physical address of the RB */ + rxb->page_dma = pci_map_page(priv->pci_dev, page, 0, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + /* dma address must be no more than 36 bits */ + BUG_ON(rxb->page_dma & ~DMA_BIT_MASK(36)); + /* and also 256 byte aligned! */ + BUG_ON(rxb->page_dma & DMA_BIT_MASK(8)); + + spin_lock_irqsave(&rxq->lock, flags); + + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + priv->alloc_rxb_page++; + + spin_unlock_irqrestore(&rxq->lock, flags); + } +} + +void iwl4965_rx_replenish(struct iwl_priv *priv) +{ + unsigned long flags; + + iwl4965_rx_allocate(priv, GFP_KERNEL); + + spin_lock_irqsave(&priv->lock, flags); + iwl4965_rx_queue_restock(priv); + spin_unlock_irqrestore(&priv->lock, flags); +} + +void iwl4965_rx_replenish_now(struct iwl_priv *priv) +{ + iwl4965_rx_allocate(priv, GFP_ATOMIC); + + iwl4965_rx_queue_restock(priv); +} + +/* Assumes that the skb field of the buffers in 'pool' is kept accurate. + * If an SKB has been detached, the POOL needs to have its SKB set to NULL + * This free routine walks the list of POOL entries and if SKB is set to + * non NULL it is unmapped and freed + */ +void iwl4965_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq) +{ + int i; + for (i = 0; i < RX_QUEUE_SIZE + RX_FREE_BUFFERS; i++) { + if (rxq->pool[i].page != NULL) { + pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + __iwl_legacy_free_pages(priv, rxq->pool[i].page); + rxq->pool[i].page = NULL; + } + } + + dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->bd_dma); + dma_free_coherent(&priv->pci_dev->dev, sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + rxq->bd = NULL; + rxq->rb_stts = NULL; +} + +int iwl4965_rxq_stop(struct iwl_priv *priv) +{ + + /* stop Rx DMA */ + iwl_legacy_write_direct32(priv, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + iwl_poll_direct_bit(priv, FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); + + return 0; +} + +int iwl4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band) +{ + int idx = 0; + int band_offset = 0; + + /* HT rate format: mac80211 wants an MCS number, which is just LSB */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + return idx; + /* Legacy rate format, search for match in table */ + } else { + if (band == IEEE80211_BAND_5GHZ) + band_offset = IWL_FIRST_OFDM_RATE; + for (idx = band_offset; idx < IWL_RATE_COUNT_LEGACY; idx++) + if (iwlegacy_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx - band_offset; + } + + return -1; +} + +static int iwl4965_calc_rssi(struct iwl_priv *priv, + struct iwl_rx_phy_res *rx_resp) +{ + /* data from PHY/DSP regarding signal strength, etc., + * contents are always there, not configurable by host. */ + struct iwl4965_rx_non_cfg_phy *ncphy = + (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; + u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL49_AGC_DB_MASK) + >> IWL49_AGC_DB_POS; + + u32 valid_antennae = + (le16_to_cpu(rx_resp->phy_flags) & IWL49_RX_PHY_FLAGS_ANTENNAE_MASK) + >> IWL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; + u8 max_rssi = 0; + u32 i; + + /* Find max rssi among 3 possible receivers. + * These values are measured by the digital signal processor (DSP). + * They should stay fairly constant even as the signal strength varies, + * if the radio's automatic gain control (AGC) is working right. + * AGC value (see below) will provide the "interesting" info. */ + for (i = 0; i < 3; i++) + if (valid_antennae & (1 << i)) + max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); + + IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", + ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], + max_rssi, agc); + + /* dBm = max_rssi dB - agc dB - constant. + * Higher AGC (higher radio gain) means lower signal. */ + return max_rssi - agc - IWL4965_RSSI_OFFSET; +} + + +static u32 iwl4965_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) +{ + u32 decrypt_out = 0; + + if ((decrypt_in & RX_RES_STATUS_STATION_FOUND) == + RX_RES_STATUS_STATION_FOUND) + decrypt_out |= (RX_RES_STATUS_STATION_FOUND | + RX_RES_STATUS_NO_STATION_INFO_MISMATCH); + + decrypt_out |= (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK); + + /* packet was not encrypted */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_NONE) + return decrypt_out; + + /* packet was encrypted with unknown alg */ + if ((decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) == + RX_RES_STATUS_SEC_TYPE_ERR) + return decrypt_out; + + /* decryption was not done in HW */ + if ((decrypt_in & RX_MPDU_RES_STATUS_DEC_DONE_MSK) != + RX_MPDU_RES_STATUS_DEC_DONE_MSK) + return decrypt_out; + + switch (decrypt_in & RX_RES_STATUS_SEC_TYPE_MSK) { + + case RX_RES_STATUS_SEC_TYPE_CCMP: + /* alg is CCM: check MIC only */ + if (!(decrypt_in & RX_MPDU_RES_STATUS_MIC_OK)) + /* Bad MIC */ + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + + break; + + case RX_RES_STATUS_SEC_TYPE_TKIP: + if (!(decrypt_in & RX_MPDU_RES_STATUS_TTAK_OK)) { + /* Bad TTAK */ + decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; + break; + } + /* fall through if TTAK OK */ + default: + if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) + decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; + else + decrypt_out |= RX_RES_STATUS_DECRYPT_OK; + break; + } + + IWL_DEBUG_RX(priv, "decrypt_in:0x%x decrypt_out = 0x%x\n", + decrypt_in, decrypt_out); + + return decrypt_out; +} + +static void iwl4965_pass_packet_to_mac80211(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u16 len, + u32 ampdu_status, + struct iwl_rx_mem_buffer *rxb, + struct ieee80211_rx_status *stats) +{ + struct sk_buff *skb; + __le16 fc = hdr->frame_control; + + /* We only process data packets if the interface is open */ + if (unlikely(!priv->is_open)) { + IWL_DEBUG_DROP_LIMIT(priv, + "Dropping packet while interface is not open.\n"); + return; + } + + /* In case of HW accelerated crypto and bad decryption, drop */ + if (!priv->cfg->mod_params->sw_crypto && + iwl_legacy_set_decrypted_flag(priv, hdr, ampdu_status, stats)) + return; + + skb = dev_alloc_skb(128); + if (!skb) { + IWL_ERR(priv, "dev_alloc_skb failed\n"); + return; + } + + skb_add_rx_frag(skb, 0, rxb->page, (void *)hdr - rxb_addr(rxb), len); + + iwl_legacy_update_stats(priv, false, fc, len); + memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); + + ieee80211_rx(priv->hw, skb); + priv->alloc_rxb_page--; + rxb->page = NULL; +} + +/* Called for REPLY_RX (legacy ABG frames), or + * REPLY_RX_MPDU_CMD (HT high-throughput N frames). */ +void iwl4965_rx_reply_rx(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct ieee80211_hdr *header; + struct ieee80211_rx_status rx_status; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_phy_res *phy_res; + __le32 rx_pkt_status; + struct iwl_rx_mpdu_res_start *amsdu; + u32 len; + u32 ampdu_status; + u32 rate_n_flags; + + /** + * REPLY_RX and REPLY_RX_MPDU_CMD are handled differently. + * REPLY_RX: physical layer info is in this buffer + * REPLY_RX_MPDU_CMD: physical layer info was sent in separate + * command and cached in priv->last_phy_res + * + * Here we set up local variables depending on which command is + * received. + */ + if (pkt->hdr.cmd == REPLY_RX) { + phy_res = (struct iwl_rx_phy_res *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt); + + len = le16_to_cpu(phy_res->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*phy_res) + + phy_res->cfg_phy_cnt + len); + ampdu_status = le32_to_cpu(rx_pkt_status); + } else { + if (!priv->_4965.last_phy_res_valid) { + IWL_ERR(priv, "MPDU frame without cached PHY data\n"); + return; + } + phy_res = &priv->_4965.last_phy_res; + amsdu = (struct iwl_rx_mpdu_res_start *)pkt->u.raw; + header = (struct ieee80211_hdr *)(pkt->u.raw + sizeof(*amsdu)); + len = le16_to_cpu(amsdu->byte_count); + rx_pkt_status = *(__le32 *)(pkt->u.raw + sizeof(*amsdu) + len); + ampdu_status = iwl4965_translate_rx_status(priv, + le32_to_cpu(rx_pkt_status)); + } + + if ((unlikely(phy_res->cfg_phy_cnt > 20))) { + IWL_DEBUG_DROP(priv, "dsp size out of range [0,20]: %d/n", + phy_res->cfg_phy_cnt); + return; + } + + if (!(rx_pkt_status & RX_RES_STATUS_NO_CRC32_ERROR) || + !(rx_pkt_status & RX_RES_STATUS_NO_RXE_OVERFLOW)) { + IWL_DEBUG_RX(priv, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(rx_pkt_status)); + return; + } + + /* This will be used in several places later */ + rate_n_flags = le32_to_cpu(phy_res->rate_n_flags); + + /* rx_status carries information about the packet to mac80211 */ + rx_status.mactime = le64_to_cpu(phy_res->timestamp); + rx_status.freq = + ieee80211_channel_to_frequency(le16_to_cpu(phy_res->channel), + rx_status.band); + rx_status.band = (phy_res->phy_flags & RX_RES_PHY_FLAGS_BAND_24_MSK) ? + IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + rx_status.rate_idx = + iwl4965_hwrate_to_mac80211_idx(rate_n_flags, rx_status.band); + rx_status.flag = 0; + + /* TSF isn't reliable. In order to allow smooth user experience, + * this W/A doesn't propagate it to the mac80211 */ + /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ + + priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); + + /* Find max signal strength (dBm) among 3 antenna/receiver chains */ + rx_status.signal = iwl4965_calc_rssi(priv, phy_res); + + iwl_legacy_dbg_log_rx_data_frame(priv, len, header); + IWL_DEBUG_STATS_LIMIT(priv, "Rssi %d, TSF %llu\n", + rx_status.signal, (unsigned long long)rx_status.mactime); + + /* + * "antenna number" + * + * It seems that the antenna field in the phy flags value + * is actually a bit field. This is undefined by radiotap, + * it wants an actual antenna number but I always get "7" + * for most legacy frames I receive indicating that the + * same frame was received on all three RX chains. + * + * I think this field should be removed in favor of a + * new 802.11n radiotap field "RX chains" that is defined + * as a bitmask. + */ + rx_status.antenna = + (le16_to_cpu(phy_res->phy_flags) & RX_RES_PHY_FLAGS_ANTENNA_MSK) + >> RX_RES_PHY_FLAGS_ANTENNA_POS; + + /* set the preamble flag if appropriate */ + if (phy_res->phy_flags & RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK) + rx_status.flag |= RX_FLAG_SHORTPRE; + + /* Set up the HT phy flags */ + if (rate_n_flags & RATE_MCS_HT_MSK) + rx_status.flag |= RX_FLAG_HT; + if (rate_n_flags & RATE_MCS_HT40_MSK) + rx_status.flag |= RX_FLAG_40MHZ; + if (rate_n_flags & RATE_MCS_SGI_MSK) + rx_status.flag |= RX_FLAG_SHORT_GI; + + iwl4965_pass_packet_to_mac80211(priv, header, len, ampdu_status, + rxb, &rx_status); +} + +/* Cache phy data (Rx signal strength, etc) for HT frame (REPLY_RX_PHY_CMD). + * This will be used later in iwl_rx_reply_rx() for REPLY_RX_MPDU_CMD. */ +void iwl4965_rx_reply_rx_phy(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + priv->_4965.last_phy_res_valid = true; + memcpy(&priv->_4965.last_phy_res, pkt->u.raw, + sizeof(struct iwl_rx_phy_res)); +} + +static int iwl4965_get_single_channel_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + struct iwl_scan_channel *scan_ch) +{ + const struct ieee80211_supported_band *sband; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added = 0; + u16 channel = 0; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) { + IWL_ERR(priv, "invalid band\n"); + return added; + } + + active_dwell = iwl_legacy_get_active_dwell_time(priv, band, 0); + passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + channel = iwl_legacy_get_single_channel_number(priv, band); + if (channel) { + scan_ch->channel = cpu_to_le16(channel); + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + added++; + } else + IWL_ERR(priv, "no valid channel found\n"); + return added; +} + +static int iwl4965_get_channels_for_scan(struct iwl_priv *priv, + struct ieee80211_vif *vif, + enum ieee80211_band band, + u8 is_active, u8 n_probes, + struct iwl_scan_channel *scan_ch) +{ + struct ieee80211_channel *chan; + const struct ieee80211_supported_band *sband; + const struct iwl_channel_info *ch_info; + u16 passive_dwell = 0; + u16 active_dwell = 0; + int added, i; + u16 channel; + + sband = iwl_get_hw_mode(priv, band); + if (!sband) + return 0; + + active_dwell = iwl_legacy_get_active_dwell_time(priv, band, n_probes); + passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif); + + if (passive_dwell <= active_dwell) + passive_dwell = active_dwell + 1; + + for (i = 0, added = 0; i < priv->scan_request->n_channels; i++) { + chan = priv->scan_request->channels[i]; + + if (chan->band != band) + continue; + + channel = chan->hw_value; + scan_ch->channel = cpu_to_le16(channel); + + ch_info = iwl_legacy_get_channel_info(priv, band, channel); + if (!iwl_legacy_is_channel_valid(ch_info)) { + IWL_DEBUG_SCAN(priv, + "Channel %d is INVALID for this band.\n", + channel); + continue; + } + + if (!is_active || iwl_legacy_is_channel_passive(ch_info) || + (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) + scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE; + else + scan_ch->type = SCAN_CHANNEL_TYPE_ACTIVE; + + if (n_probes) + scan_ch->type |= IWL_SCAN_PROBE_MASK(n_probes); + + scan_ch->active_dwell = cpu_to_le16(active_dwell); + scan_ch->passive_dwell = cpu_to_le16(passive_dwell); + + /* Set txpower levels to defaults */ + scan_ch->dsp_atten = 110; + + /* NOTE: if we were doing 6Mb OFDM for scans we'd use + * power level: + * scan_ch->tx_gain = ((1 << 5) | (2 << 3)) | 3; + */ + if (band == IEEE80211_BAND_5GHZ) + scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3; + else + scan_ch->tx_gain = ((1 << 5) | (5 << 3)); + + IWL_DEBUG_SCAN(priv, "Scanning ch=%d prob=0x%X [%s %d]\n", + channel, le32_to_cpu(scan_ch->type), + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + "ACTIVE" : "PASSIVE", + (scan_ch->type & SCAN_CHANNEL_TYPE_ACTIVE) ? + active_dwell : passive_dwell); + + scan_ch++; + added++; + } + + IWL_DEBUG_SCAN(priv, "total channels to scan %d\n", added); + return added; +} + +int iwl4965_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) +{ + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_CMD, + .len = sizeof(struct iwl_scan_cmd), + .flags = CMD_SIZE_HUGE, + }; + struct iwl_scan_cmd *scan; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u32 rate_flags = 0; + u16 cmd_len; + u16 rx_chain = 0; + enum ieee80211_band band; + u8 n_probes = 0; + u8 rx_ant = priv->hw_params.valid_rx_ant; + u8 rate; + bool is_active = false; + int chan_mod; + u8 active_chains; + u8 scan_tx_antennas = priv->hw_params.valid_tx_ant; + int ret; + + lockdep_assert_held(&priv->mutex); + + if (vif) + ctx = iwl_legacy_rxon_ctx_from_vif(vif); + + if (!priv->scan_cmd) { + priv->scan_cmd = kmalloc(sizeof(struct iwl_scan_cmd) + + IWL_MAX_SCAN_SIZE, GFP_KERNEL); + if (!priv->scan_cmd) { + IWL_DEBUG_SCAN(priv, + "fail to allocate memory for scan\n"); + return -ENOMEM; + } + } + scan = priv->scan_cmd; + memset(scan, 0, sizeof(struct iwl_scan_cmd) + IWL_MAX_SCAN_SIZE); + + scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; + scan->quiet_time = IWL_ACTIVE_QUIET_TIME; + + if (iwl_legacy_is_any_associated(priv)) { + u16 interval = 0; + u32 extra; + u32 suspend_time = 100; + u32 scan_suspend_time = 100; + + IWL_DEBUG_INFO(priv, "Scanning while associated...\n"); + if (priv->is_internal_short_scan) + interval = 0; + else + interval = vif->bss_conf.beacon_int; + + scan->suspend_time = 0; + scan->max_out_time = cpu_to_le32(200 * 1024); + if (!interval) + interval = suspend_time; + + extra = (suspend_time / interval) << 22; + scan_suspend_time = (extra | + ((suspend_time % interval) * 1024)); + scan->suspend_time = cpu_to_le32(scan_suspend_time); + IWL_DEBUG_SCAN(priv, "suspend_time 0x%X beacon interval %d\n", + scan_suspend_time, interval); + } + + if (priv->is_internal_short_scan) { + IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n"); + } else if (priv->scan_request->n_ssids) { + int i, p = 0; + IWL_DEBUG_SCAN(priv, "Kicking off active scan\n"); + for (i = 0; i < priv->scan_request->n_ssids; i++) { + /* always does wildcard anyway */ + if (!priv->scan_request->ssids[i].ssid_len) + continue; + scan->direct_scan[p].id = WLAN_EID_SSID; + scan->direct_scan[p].len = + priv->scan_request->ssids[i].ssid_len; + memcpy(scan->direct_scan[p].ssid, + priv->scan_request->ssids[i].ssid, + priv->scan_request->ssids[i].ssid_len); + n_probes++; + p++; + } + is_active = true; + } else + IWL_DEBUG_SCAN(priv, "Start passive scan.\n"); + + scan->tx_cmd.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK; + scan->tx_cmd.sta_id = ctx->bcast_sta_id; + scan->tx_cmd.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + + switch (priv->scan_band) { + case IEEE80211_BAND_2GHZ: + scan->flags = RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK; + chan_mod = le32_to_cpu( + priv->contexts[IWL_RXON_CTX_BSS].active.flags & + RXON_FLG_CHANNEL_MODE_MSK) + >> RXON_FLG_CHANNEL_MODE_POS; + if (chan_mod == CHANNEL_MODE_PURE_40) { + rate = IWL_RATE_6M_PLCP; + } else { + rate = IWL_RATE_1M_PLCP; + rate_flags = RATE_MCS_CCK_MSK; + } + break; + case IEEE80211_BAND_5GHZ: + rate = IWL_RATE_6M_PLCP; + break; + default: + IWL_WARN(priv, "Invalid scan band\n"); + return -EIO; + } + + /* + * If active scanning is requested but a certain channel is + * marked passive, we can do active scanning if we detect + * transmissions. + * + * There is an issue with some firmware versions that triggers + * a sysassert on a "good CRC threshold" of zero (== disabled), + * on a radar channel even though this means that we should NOT + * send probes. + * + * The "good CRC threshold" is the number of frames that we + * need to receive during our dwell time on a channel before + * sending out probes -- setting this to a huge value will + * mean we never reach it, but at the same time work around + * the aforementioned issue. Thus use IWL_GOOD_CRC_TH_NEVER + * here instead of IWL_GOOD_CRC_TH_DISABLED. + */ + scan->good_CRC_th = is_active ? IWL_GOOD_CRC_TH_DEFAULT : + IWL_GOOD_CRC_TH_NEVER; + + band = priv->scan_band; + + if (priv->cfg->scan_rx_antennas[band]) + rx_ant = priv->cfg->scan_rx_antennas[band]; + + if (priv->cfg->scan_tx_antennas[band]) + scan_tx_antennas = priv->cfg->scan_tx_antennas[band]; + + priv->scan_tx_ant[band] = iwl4965_toggle_tx_ant(priv, + priv->scan_tx_ant[band], + scan_tx_antennas); + rate_flags |= iwl4965_ant_idx_to_flags(priv->scan_tx_ant[band]); + scan->tx_cmd.rate_n_flags = iwl4965_hw_set_rate_n_flags(rate, rate_flags); + + /* In power save mode use one chain, otherwise use all chains */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + /* rx_ant has been set to all valid chains previously */ + active_chains = rx_ant & + ((u8)(priv->chain_noise_data.active_chains)); + if (!active_chains) + active_chains = rx_ant; + + IWL_DEBUG_SCAN(priv, "chain_noise_data.active_chains: %u\n", + priv->chain_noise_data.active_chains); + + rx_ant = iwl4965_first_antenna(active_chains); + } + + /* MIMO is not used here, but value is required */ + rx_chain |= priv->hw_params.valid_rx_ant << RXON_RX_CHAIN_VALID_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_MIMO_SEL_POS; + rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS; + rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS; + scan->rx_chain = cpu_to_le16(rx_chain); + if (!priv->is_internal_short_scan) { + cmd_len = iwl_legacy_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + vif->addr, + priv->scan_request->ie, + priv->scan_request->ie_len, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + } else { + /* use bcast addr, will not be transmitted but must be valid */ + cmd_len = iwl_legacy_fill_probe_req(priv, + (struct ieee80211_mgmt *)scan->data, + iwlegacy_bcast_addr, NULL, 0, + IWL_MAX_SCAN_SIZE - sizeof(*scan)); + + } + scan->tx_cmd.len = cpu_to_le16(cmd_len); + + scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK | + RXON_FILTER_BCON_AWARE_MSK); + + if (priv->is_internal_short_scan) { + scan->channel_count = + iwl4965_get_single_channel_for_scan(priv, vif, band, + (void *)&scan->data[le16_to_cpu( + scan->tx_cmd.len)]); + } else { + scan->channel_count = + iwl4965_get_channels_for_scan(priv, vif, band, + is_active, n_probes, + (void *)&scan->data[le16_to_cpu( + scan->tx_cmd.len)]); + } + if (scan->channel_count == 0) { + IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count); + return -EIO; + } + + cmd.len += le16_to_cpu(scan->tx_cmd.len) + + scan->channel_count * sizeof(struct iwl_scan_channel); + cmd.data = scan; + scan->len = cpu_to_le16(cmd.len); + + set_bit(STATUS_SCAN_HW, &priv->status); + + ret = iwl_legacy_send_cmd_sync(priv, &cmd); + if (ret) + clear_bit(STATUS_SCAN_HW, &priv->status); + + return ret; +} + +int iwl4965_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + if (add) + return iwl4965_add_bssid_station(priv, vif_priv->ctx, + vif->bss_conf.bssid, + &vif_priv->ibss_bssid_sta_id); + return iwl_legacy_remove_station(priv, vif_priv->ibss_bssid_sta_id, + vif->bss_conf.bssid); +} + +void iwl4965_free_tfds_in_queue(struct iwl_priv *priv, + int sta_id, int tid, int freed) +{ + lockdep_assert_held(&priv->sta_lock); + + if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed) + priv->stations[sta_id].tid[tid].tfds_in_queue -= freed; + else { + IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n", + priv->stations[sta_id].tid[tid].tfds_in_queue, + freed); + priv->stations[sta_id].tid[tid].tfds_in_queue = 0; + } +} + +#define IWL_TX_QUEUE_MSK 0xfffff + +static bool iwl4965_is_single_rx_stream(struct iwl_priv *priv) +{ + return priv->current_ht_config.smps == IEEE80211_SMPS_STATIC || + priv->current_ht_config.single_chain_sufficient; +} + +#define IWL_NUM_RX_CHAINS_MULTIPLE 3 +#define IWL_NUM_RX_CHAINS_SINGLE 2 +#define IWL_NUM_IDLE_CHAINS_DUAL 2 +#define IWL_NUM_IDLE_CHAINS_SINGLE 1 + +/* + * Determine how many receiver/antenna chains to use. + * + * More provides better reception via diversity. Fewer saves power + * at the expense of throughput, but only when not in powersave to + * start with. + * + * MIMO (dual stream) requires at least 2, but works better with 3. + * This does not determine *which* chains to use, just how many. + */ +static int iwl4965_get_active_rx_chain_count(struct iwl_priv *priv) +{ + /* # of Rx chains to use when expecting MIMO. */ + if (iwl4965_is_single_rx_stream(priv)) + return IWL_NUM_RX_CHAINS_SINGLE; + else + return IWL_NUM_RX_CHAINS_MULTIPLE; +} + +/* + * When we are in power saving mode, unless device support spatial + * multiplexing power save, use the active count for rx chain count. + */ +static int +iwl4965_get_idle_rx_chain_count(struct iwl_priv *priv, int active_cnt) +{ + /* # Rx chains when idling, depending on SMPS mode */ + switch (priv->current_ht_config.smps) { + case IEEE80211_SMPS_STATIC: + case IEEE80211_SMPS_DYNAMIC: + return IWL_NUM_IDLE_CHAINS_SINGLE; + case IEEE80211_SMPS_OFF: + return active_cnt; + default: + WARN(1, "invalid SMPS mode %d", + priv->current_ht_config.smps); + return active_cnt; + } +} + +/* up to 4 chains */ +static u8 iwl4965_count_chain_bitmap(u32 chain_bitmap) +{ + u8 res; + res = (chain_bitmap & BIT(0)) >> 0; + res += (chain_bitmap & BIT(1)) >> 1; + res += (chain_bitmap & BIT(2)) >> 2; + res += (chain_bitmap & BIT(3)) >> 3; + return res; +} + +/** + * iwl4965_set_rxon_chain - Set up Rx chain usage in "staging" RXON image + * + * Selects how many and which Rx receivers/antennas/chains to use. + * This should not be used for scan command ... it puts data in wrong place. + */ +void iwl4965_set_rxon_chain(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + bool is_single = iwl4965_is_single_rx_stream(priv); + bool is_cam = !test_bit(STATUS_POWER_PMI, &priv->status); + u8 idle_rx_cnt, active_rx_cnt, valid_rx_cnt; + u32 active_chains; + u16 rx_chain; + + /* Tell uCode which antennas are actually connected. + * Before first association, we assume all antennas are connected. + * Just after first association, iwl4965_chain_noise_calibration() + * checks which antennas actually *are* connected. */ + if (priv->chain_noise_data.active_chains) + active_chains = priv->chain_noise_data.active_chains; + else + active_chains = priv->hw_params.valid_rx_ant; + + rx_chain = active_chains << RXON_RX_CHAIN_VALID_POS; + + /* How many receivers should we use? */ + active_rx_cnt = iwl4965_get_active_rx_chain_count(priv); + idle_rx_cnt = iwl4965_get_idle_rx_chain_count(priv, active_rx_cnt); + + + /* correct rx chain count according hw settings + * and chain noise calibration + */ + valid_rx_cnt = iwl4965_count_chain_bitmap(active_chains); + if (valid_rx_cnt < active_rx_cnt) + active_rx_cnt = valid_rx_cnt; + + if (valid_rx_cnt < idle_rx_cnt) + idle_rx_cnt = valid_rx_cnt; + + rx_chain |= active_rx_cnt << RXON_RX_CHAIN_MIMO_CNT_POS; + rx_chain |= idle_rx_cnt << RXON_RX_CHAIN_CNT_POS; + + ctx->staging.rx_chain = cpu_to_le16(rx_chain); + + if (!is_single && (active_rx_cnt >= IWL_NUM_RX_CHAINS_SINGLE) && is_cam) + ctx->staging.rx_chain |= RXON_RX_CHAIN_MIMO_FORCE_MSK; + else + ctx->staging.rx_chain &= ~RXON_RX_CHAIN_MIMO_FORCE_MSK; + + IWL_DEBUG_ASSOC(priv, "rx_chain=0x%X active=%d idle=%d\n", + ctx->staging.rx_chain, + active_rx_cnt, idle_rx_cnt); + + WARN_ON(active_rx_cnt == 0 || idle_rx_cnt == 0 || + active_rx_cnt < idle_rx_cnt); +} + +u8 iwl4965_toggle_tx_ant(struct iwl_priv *priv, u8 ant, u8 valid) +{ + int i; + u8 ind = ant; + + for (i = 0; i < RATE_ANT_NUM - 1; i++) { + ind = (ind + 1) < RATE_ANT_NUM ? ind + 1 : 0; + if (valid & BIT(ind)) + return ind; + } + return ant; +} + +static const char *iwl4965_get_fh_string(int cmd) +{ + switch (cmd) { + IWL_CMD(FH_RSCSR_CHNL0_STTS_WPTR_REG); + IWL_CMD(FH_RSCSR_CHNL0_RBDCB_BASE_REG); + IWL_CMD(FH_RSCSR_CHNL0_WPTR); + IWL_CMD(FH_MEM_RCSR_CHNL0_CONFIG_REG); + IWL_CMD(FH_MEM_RSSR_SHARED_CTRL_REG); + IWL_CMD(FH_MEM_RSSR_RX_STATUS_REG); + IWL_CMD(FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV); + IWL_CMD(FH_TSSR_TX_STATUS_REG); + IWL_CMD(FH_TSSR_TX_ERROR_REG); + default: + return "UNKNOWN"; + } +} + +int iwl4965_dump_fh(struct iwl_priv *priv, char **buf, bool display) +{ + int i; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + int pos = 0; + size_t bufsz = 0; +#endif + static const u32 fh_tbl[] = { + FH_RSCSR_CHNL0_STTS_WPTR_REG, + FH_RSCSR_CHNL0_RBDCB_BASE_REG, + FH_RSCSR_CHNL0_WPTR, + FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_MEM_RSSR_SHARED_CTRL_REG, + FH_MEM_RSSR_RX_STATUS_REG, + FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV, + FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_ERROR_REG + }; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (display) { + bufsz = ARRAY_SIZE(fh_tbl) * 48 + 40; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + pos += scnprintf(*buf + pos, bufsz - pos, + "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + pos += scnprintf(*buf + pos, bufsz - pos, + " %34s: 0X%08x\n", + iwl4965_get_fh_string(fh_tbl[i]), + iwl_legacy_read_direct32(priv, fh_tbl[i])); + } + return pos; + } +#endif + IWL_ERR(priv, "FH register values:\n"); + for (i = 0; i < ARRAY_SIZE(fh_tbl); i++) { + IWL_ERR(priv, " %34s: 0X%08x\n", + iwl4965_get_fh_string(fh_tbl[i]), + iwl_legacy_read_direct32(priv, fh_tbl[i])); + } + return 0; +} diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-rs.c b/drivers/net/wireless/iwlegacy/iwl-4965-rs.c new file mode 100644 index 00000000000..31ac672b64e --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-rs.c @@ -0,0 +1,2870 @@ +/****************************************************************************** + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/skbuff.h> +#include <linux/slab.h> +#include <linux/wireless.h> +#include <net/mac80211.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/delay.h> + +#include <linux/workqueue.h> + +#include "iwl-dev.h" +#include "iwl-sta.h" +#include "iwl-core.h" +#include "iwl-4965.h" + +#define IWL4965_RS_NAME "iwl-4965-rs" + +#define NUM_TRY_BEFORE_ANT_TOGGLE 1 +#define IWL_NUMBER_TRY 1 +#define IWL_HT_NUMBER_TRY 3 + +#define IWL_RATE_MAX_WINDOW 62 /* # tx in history window */ +#define IWL_RATE_MIN_FAILURE_TH 6 /* min failures to calc tpt */ +#define IWL_RATE_MIN_SUCCESS_TH 8 /* min successes to calc tpt */ + +/* max allowed rate miss before sync LQ cmd */ +#define IWL_MISSED_RATE_MAX 15 +/* max time to accum history 2 seconds */ +#define IWL_RATE_SCALE_FLUSH_INTVL (3*HZ) + +static u8 rs_ht_to_legacy[] = { + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_6M_INDEX, IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, IWL_RATE_54M_INDEX +}; + +static const u8 ant_toggle_lookup[] = { + /*ANT_NONE -> */ ANT_NONE, + /*ANT_A -> */ ANT_B, + /*ANT_B -> */ ANT_C, + /*ANT_AB -> */ ANT_BC, + /*ANT_C -> */ ANT_A, + /*ANT_AC -> */ ANT_AB, + /*ANT_BC -> */ ANT_AC, + /*ANT_ABC -> */ ANT_ABC, +}; + +#define IWL_DECLARE_RATE_INFO(r, s, ip, in, rp, rn, pp, np) \ + [IWL_RATE_##r##M_INDEX] = { IWL_RATE_##r##M_PLCP, \ + IWL_RATE_SISO_##s##M_PLCP, \ + IWL_RATE_MIMO2_##s##M_PLCP,\ + IWL_RATE_##r##M_IEEE, \ + IWL_RATE_##ip##M_INDEX, \ + IWL_RATE_##in##M_INDEX, \ + IWL_RATE_##rp##M_INDEX, \ + IWL_RATE_##rn##M_INDEX, \ + IWL_RATE_##pp##M_INDEX, \ + IWL_RATE_##np##M_INDEX } + +/* + * Parameter order: + * rate, ht rate, prev rate, next rate, prev tgg rate, next tgg rate + * + * If there isn't a valid next or previous rate then INV is used which + * maps to IWL_RATE_INVALID + * + */ +const struct iwl_rate_info iwlegacy_rates[IWL_RATE_COUNT] = { + IWL_DECLARE_RATE_INFO(1, INV, INV, 2, INV, 2, INV, 2), /* 1mbps */ + IWL_DECLARE_RATE_INFO(2, INV, 1, 5, 1, 5, 1, 5), /* 2mbps */ + IWL_DECLARE_RATE_INFO(5, INV, 2, 6, 2, 11, 2, 11), /*5.5mbps */ + IWL_DECLARE_RATE_INFO(11, INV, 9, 12, 9, 12, 5, 18), /* 11mbps */ + IWL_DECLARE_RATE_INFO(6, 6, 5, 9, 5, 11, 5, 11), /* 6mbps */ + IWL_DECLARE_RATE_INFO(9, 6, 6, 11, 6, 11, 5, 11), /* 9mbps */ + IWL_DECLARE_RATE_INFO(12, 12, 11, 18, 11, 18, 11, 18), /* 12mbps */ + IWL_DECLARE_RATE_INFO(18, 18, 12, 24, 12, 24, 11, 24), /* 18mbps */ + IWL_DECLARE_RATE_INFO(24, 24, 18, 36, 18, 36, 18, 36), /* 24mbps */ + IWL_DECLARE_RATE_INFO(36, 36, 24, 48, 24, 48, 24, 48), /* 36mbps */ + IWL_DECLARE_RATE_INFO(48, 48, 36, 54, 36, 54, 36, 54), /* 48mbps */ + IWL_DECLARE_RATE_INFO(54, 54, 48, INV, 48, INV, 48, INV),/* 54mbps */ + IWL_DECLARE_RATE_INFO(60, 60, 48, INV, 48, INV, 48, INV),/* 60mbps */ +}; + +static int iwl4965_hwrate_to_plcp_idx(u32 rate_n_flags) +{ + int idx = 0; + + /* HT rate format */ + if (rate_n_flags & RATE_MCS_HT_MSK) { + idx = (rate_n_flags & 0xff); + + if (idx >= IWL_RATE_MIMO2_6M_PLCP) + idx = idx - IWL_RATE_MIMO2_6M_PLCP; + + idx += IWL_FIRST_OFDM_RATE; + /* skip 9M not supported in ht*/ + if (idx >= IWL_RATE_9M_INDEX) + idx += 1; + if ((idx >= IWL_FIRST_OFDM_RATE) && (idx <= IWL_LAST_OFDM_RATE)) + return idx; + + /* legacy rate format, search for match in table */ + } else { + for (idx = 0; idx < ARRAY_SIZE(iwlegacy_rates); idx++) + if (iwlegacy_rates[idx].plcp == (rate_n_flags & 0xFF)) + return idx; + } + + return -1; +} + +static void iwl4965_rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta); +static void iwl4965_rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 rate_n_flags); +static void iwl4965_rs_stay_in_table(struct iwl_lq_sta *lq_sta, + bool force_search); + +#ifdef CONFIG_MAC80211_DEBUGFS +static void iwl4965_rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index); +#else +static void iwl4965_rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{} +#endif + +/** + * The following tables contain the expected throughput metrics for all rates + * + * 1, 2, 5.5, 11, 6, 9, 12, 18, 24, 36, 48, 54, 60 MBits + * + * where invalid entries are zeros. + * + * CCK rates are only valid in legacy table and will only be used in G + * (2.4 GHz) band. + */ + +static s32 expected_tpt_legacy[IWL_RATE_COUNT] = { + 7, 13, 35, 58, 40, 57, 72, 98, 121, 154, 177, 186, 0 +}; + +static s32 expected_tpt_siso20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 42, 0, 76, 102, 124, 158, 183, 193, 202}, /* Norm */ + {0, 0, 0, 0, 46, 0, 82, 110, 132, 167, 192, 202, 210}, /* SGI */ + {0, 0, 0, 0, 48, 0, 93, 135, 176, 251, 319, 351, 381}, /* AGG */ + {0, 0, 0, 0, 53, 0, 102, 149, 193, 275, 348, 381, 413}, /* AGG+SGI */ +}; + +static s32 expected_tpt_siso40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 77, 0, 127, 160, 184, 220, 242, 250, 257}, /* Norm */ + {0, 0, 0, 0, 83, 0, 135, 169, 193, 229, 250, 257, 264}, /* SGI */ + {0, 0, 0, 0, 96, 0, 182, 259, 328, 451, 553, 598, 640}, /* AGG */ + {0, 0, 0, 0, 106, 0, 199, 282, 357, 487, 593, 640, 683}, /* AGG+SGI */ +}; + +static s32 expected_tpt_mimo2_20MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 74, 0, 123, 155, 179, 213, 235, 243, 250}, /* Norm */ + {0, 0, 0, 0, 81, 0, 131, 164, 187, 221, 242, 250, 256}, /* SGI */ + {0, 0, 0, 0, 92, 0, 175, 250, 317, 436, 534, 578, 619}, /* AGG */ + {0, 0, 0, 0, 102, 0, 192, 273, 344, 470, 573, 619, 660}, /* AGG+SGI*/ +}; + +static s32 expected_tpt_mimo2_40MHz[4][IWL_RATE_COUNT] = { + {0, 0, 0, 0, 123, 0, 182, 214, 235, 264, 279, 285, 289}, /* Norm */ + {0, 0, 0, 0, 131, 0, 191, 222, 242, 270, 284, 289, 293}, /* SGI */ + {0, 0, 0, 0, 180, 0, 327, 446, 545, 708, 828, 878, 922}, /* AGG */ + {0, 0, 0, 0, 197, 0, 355, 481, 584, 752, 872, 922, 966}, /* AGG+SGI */ +}; + +/* mbps, mcs */ +static const struct iwl_rate_mcs_info iwl_rate_mcs[IWL_RATE_COUNT] = { + { "1", "BPSK DSSS"}, + { "2", "QPSK DSSS"}, + {"5.5", "BPSK CCK"}, + { "11", "QPSK CCK"}, + { "6", "BPSK 1/2"}, + { "9", "BPSK 1/2"}, + { "12", "QPSK 1/2"}, + { "18", "QPSK 3/4"}, + { "24", "16QAM 1/2"}, + { "36", "16QAM 3/4"}, + { "48", "64QAM 2/3"}, + { "54", "64QAM 3/4"}, + { "60", "64QAM 5/6"}, +}; + +#define MCS_INDEX_PER_STREAM (8) + +static inline u8 iwl4965_rs_extract_rate(u32 rate_n_flags) +{ + return (u8)(rate_n_flags & 0xFF); +} + +static void +iwl4965_rs_rate_scale_clear_window(struct iwl_rate_scale_data *window) +{ + window->data = 0; + window->success_counter = 0; + window->success_ratio = IWL_INVALID_VALUE; + window->counter = 0; + window->average_tpt = IWL_INVALID_VALUE; + window->stamp = 0; +} + +static inline u8 iwl4965_rs_is_valid_ant(u8 valid_antenna, u8 ant_type) +{ + return (ant_type & valid_antenna) == ant_type; +} + +/* + * removes the old data from the statistics. All data that is older than + * TID_MAX_TIME_DIFF, will be deleted. + */ +static void +iwl4965_rs_tl_rm_old_stats(struct iwl_traffic_load *tl, u32 curr_time) +{ + /* The oldest age we want to keep */ + u32 oldest_time = curr_time - TID_MAX_TIME_DIFF; + + while (tl->queue_count && + (tl->time_stamp < oldest_time)) { + tl->total -= tl->packet_count[tl->head]; + tl->packet_count[tl->head] = 0; + tl->time_stamp += TID_QUEUE_CELL_SPACING; + tl->queue_count--; + tl->head++; + if (tl->head >= TID_QUEUE_MAX_SIZE) + tl->head = 0; + } +} + +/* + * increment traffic load value for tid and also remove + * any old values if passed the certain time period + */ +static u8 iwl4965_rs_tl_add_packet(struct iwl_lq_sta *lq_data, + struct ieee80211_hdr *hdr) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + u8 tid; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & 0xf; + } else + return MAX_TID_COUNT; + + if (unlikely(tid >= TID_MAX_LOAD_COUNT)) + return MAX_TID_COUNT; + + tl = &lq_data->load[tid]; + + curr_time -= curr_time % TID_ROUND_VALUE; + + /* Happens only for the first packet. Initialize the data */ + if (!(tl->queue_count)) { + tl->total = 1; + tl->time_stamp = curr_time; + tl->queue_count = 1; + tl->head = 0; + tl->packet_count[0] = 1; + return MAX_TID_COUNT; + } + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + iwl4965_rs_tl_rm_old_stats(tl, curr_time); + + index = (tl->head + index) % TID_QUEUE_MAX_SIZE; + tl->packet_count[index] = tl->packet_count[index] + 1; + tl->total = tl->total + 1; + + if ((index + 1) > tl->queue_count) + tl->queue_count = index + 1; + + return tid; +} + +/* + get the traffic load value for tid +*/ +static u32 iwl4965_rs_tl_get_load(struct iwl_lq_sta *lq_data, u8 tid) +{ + u32 curr_time = jiffies_to_msecs(jiffies); + u32 time_diff; + s32 index; + struct iwl_traffic_load *tl = NULL; + + if (tid >= TID_MAX_LOAD_COUNT) + return 0; + + tl = &(lq_data->load[tid]); + + curr_time -= curr_time % TID_ROUND_VALUE; + + if (!(tl->queue_count)) + return 0; + + time_diff = TIME_WRAP_AROUND(tl->time_stamp, curr_time); + index = time_diff / TID_QUEUE_CELL_SPACING; + + /* The history is too long: remove data that is older than */ + /* TID_MAX_TIME_DIFF */ + if (index >= TID_QUEUE_MAX_SIZE) + iwl4965_rs_tl_rm_old_stats(tl, curr_time); + + return tl->total; +} + +static int iwl4965_rs_tl_turn_on_agg_for_tid(struct iwl_priv *priv, + struct iwl_lq_sta *lq_data, u8 tid, + struct ieee80211_sta *sta) +{ + int ret = -EAGAIN; + u32 load; + + load = iwl4965_rs_tl_get_load(lq_data, tid); + + if (load > IWL_AGG_LOAD_THRESHOLD) { + IWL_DEBUG_HT(priv, "Starting Tx agg: STA: %pM tid: %d\n", + sta->addr, tid); + ret = ieee80211_start_tx_ba_session(sta, tid, 5000); + if (ret == -EAGAIN) { + /* + * driver and mac80211 is out of sync + * this might be cause by reloading firmware + * stop the tx ba session here + */ + IWL_ERR(priv, "Fail start Tx agg on tid: %d\n", + tid); + ieee80211_stop_tx_ba_session(sta, tid); + } + } else { + IWL_ERR(priv, "Aggregation not enabled for tid %d " + "because load = %u\n", tid, load); + } + return ret; +} + +static void iwl4965_rs_tl_turn_on_agg(struct iwl_priv *priv, u8 tid, + struct iwl_lq_sta *lq_data, + struct ieee80211_sta *sta) +{ + if (tid < TID_MAX_LOAD_COUNT) + iwl4965_rs_tl_turn_on_agg_for_tid(priv, lq_data, tid, sta); + else + IWL_ERR(priv, "tid exceeds max load count: %d/%d\n", + tid, TID_MAX_LOAD_COUNT); +} + +static inline int iwl4965_get_iwl4965_num_of_ant_from_rate(u32 rate_n_flags) +{ + return !!(rate_n_flags & RATE_MCS_ANT_A_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_B_MSK) + + !!(rate_n_flags & RATE_MCS_ANT_C_MSK); +} + +/* + * Static function to get the expected throughput from an iwl_scale_tbl_info + * that wraps a NULL pointer check + */ +static s32 +iwl4965_get_expected_tpt(struct iwl_scale_tbl_info *tbl, int rs_index) +{ + if (tbl->expected_tpt) + return tbl->expected_tpt[rs_index]; + return 0; +} + +/** + * iwl4965_rs_collect_tx_data - Update the success/failure sliding window + * + * We keep a sliding window of the last 62 packets transmitted + * at this rate. window->data contains the bitmask of successful + * packets. + */ +static int iwl4965_rs_collect_tx_data(struct iwl_scale_tbl_info *tbl, + int scale_index, int attempts, int successes) +{ + struct iwl_rate_scale_data *window = NULL; + static const u64 mask = (((u64)1) << (IWL_RATE_MAX_WINDOW - 1)); + s32 fail_count, tpt; + + if (scale_index < 0 || scale_index >= IWL_RATE_COUNT) + return -EINVAL; + + /* Select window for current tx bit rate */ + window = &(tbl->win[scale_index]); + + /* Get expected throughput */ + tpt = iwl4965_get_expected_tpt(tbl, scale_index); + + /* + * Keep track of only the latest 62 tx frame attempts in this rate's + * history window; anything older isn't really relevant any more. + * If we have filled up the sliding window, drop the oldest attempt; + * if the oldest attempt (highest bit in bitmap) shows "success", + * subtract "1" from the success counter (this is the main reason + * we keep these bitmaps!). + */ + while (attempts > 0) { + if (window->counter >= IWL_RATE_MAX_WINDOW) { + + /* remove earliest */ + window->counter = IWL_RATE_MAX_WINDOW - 1; + + if (window->data & mask) { + window->data &= ~mask; + window->success_counter--; + } + } + + /* Increment frames-attempted counter */ + window->counter++; + + /* Shift bitmap by one frame to throw away oldest history */ + window->data <<= 1; + + /* Mark the most recent #successes attempts as successful */ + if (successes > 0) { + window->success_counter++; + window->data |= 0x1; + successes--; + } + + attempts--; + } + + /* Calculate current success ratio, avoid divide-by-0! */ + if (window->counter > 0) + window->success_ratio = 128 * (100 * window->success_counter) + / window->counter; + else + window->success_ratio = IWL_INVALID_VALUE; + + fail_count = window->counter - window->success_counter; + + /* Calculate average throughput, if we have enough history. */ + if ((fail_count >= IWL_RATE_MIN_FAILURE_TH) || + (window->success_counter >= IWL_RATE_MIN_SUCCESS_TH)) + window->average_tpt = (window->success_ratio * tpt + 64) / 128; + else + window->average_tpt = IWL_INVALID_VALUE; + + /* Tag this window as having been updated */ + window->stamp = jiffies; + + return 0; +} + +/* + * Fill uCode API rate_n_flags field, based on "search" or "active" table. + */ +static u32 iwl4965_rate_n_flags_from_tbl(struct iwl_priv *priv, + struct iwl_scale_tbl_info *tbl, + int index, u8 use_green) +{ + u32 rate_n_flags = 0; + + if (is_legacy(tbl->lq_type)) { + rate_n_flags = iwlegacy_rates[index].plcp; + if (index >= IWL_FIRST_CCK_RATE && index <= IWL_LAST_CCK_RATE) + rate_n_flags |= RATE_MCS_CCK_MSK; + + } else if (is_Ht(tbl->lq_type)) { + if (index > IWL_LAST_OFDM_RATE) { + IWL_ERR(priv, "Invalid HT rate index %d\n", index); + index = IWL_LAST_OFDM_RATE; + } + rate_n_flags = RATE_MCS_HT_MSK; + + if (is_siso(tbl->lq_type)) + rate_n_flags |= iwlegacy_rates[index].plcp_siso; + else + rate_n_flags |= iwlegacy_rates[index].plcp_mimo2; + } else { + IWL_ERR(priv, "Invalid tbl->lq_type %d\n", tbl->lq_type); + } + + rate_n_flags |= ((tbl->ant_type << RATE_MCS_ANT_POS) & + RATE_MCS_ANT_ABC_MSK); + + if (is_Ht(tbl->lq_type)) { + if (tbl->is_ht40) { + if (tbl->is_dup) + rate_n_flags |= RATE_MCS_DUP_MSK; + else + rate_n_flags |= RATE_MCS_HT40_MSK; + } + if (tbl->is_SGI) + rate_n_flags |= RATE_MCS_SGI_MSK; + + if (use_green) { + rate_n_flags |= RATE_MCS_GF_MSK; + if (is_siso(tbl->lq_type) && tbl->is_SGI) { + rate_n_flags &= ~RATE_MCS_SGI_MSK; + IWL_ERR(priv, "GF was set with SGI:SISO\n"); + } + } + } + return rate_n_flags; +} + +/* + * Interpret uCode API's rate_n_flags format, + * fill "search" or "active" tx mode table. + */ +static int iwl4965_rs_get_tbl_info_from_mcs(const u32 rate_n_flags, + enum ieee80211_band band, + struct iwl_scale_tbl_info *tbl, + int *rate_idx) +{ + u32 ant_msk = (rate_n_flags & RATE_MCS_ANT_ABC_MSK); + u8 iwl4965_num_of_ant = iwl4965_get_iwl4965_num_of_ant_from_rate(rate_n_flags); + u8 mcs; + + memset(tbl, 0, sizeof(struct iwl_scale_tbl_info)); + *rate_idx = iwl4965_hwrate_to_plcp_idx(rate_n_flags); + + if (*rate_idx == IWL_RATE_INVALID) { + *rate_idx = -1; + return -EINVAL; + } + tbl->is_SGI = 0; /* default legacy setup */ + tbl->is_ht40 = 0; + tbl->is_dup = 0; + tbl->ant_type = (ant_msk >> RATE_MCS_ANT_POS); + tbl->lq_type = LQ_NONE; + tbl->max_search = IWL_MAX_SEARCH; + + /* legacy rate format */ + if (!(rate_n_flags & RATE_MCS_HT_MSK)) { + if (iwl4965_num_of_ant == 1) { + if (band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + } + /* HT rate format */ + } else { + if (rate_n_flags & RATE_MCS_SGI_MSK) + tbl->is_SGI = 1; + + if ((rate_n_flags & RATE_MCS_HT40_MSK) || + (rate_n_flags & RATE_MCS_DUP_MSK)) + tbl->is_ht40 = 1; + + if (rate_n_flags & RATE_MCS_DUP_MSK) + tbl->is_dup = 1; + + mcs = iwl4965_rs_extract_rate(rate_n_flags); + + /* SISO */ + if (mcs <= IWL_RATE_SISO_60M_PLCP) { + if (iwl4965_num_of_ant == 1) + tbl->lq_type = LQ_SISO; /*else NONE*/ + /* MIMO2 */ + } else { + if (iwl4965_num_of_ant == 2) + tbl->lq_type = LQ_MIMO2; + } + } + return 0; +} + +/* switch to another antenna/antennas and return 1 */ +/* if no other valid antenna found, return 0 */ +static int iwl4965_rs_toggle_antenna(u32 valid_ant, u32 *rate_n_flags, + struct iwl_scale_tbl_info *tbl) +{ + u8 new_ant_type; + + if (!tbl->ant_type || tbl->ant_type > ANT_ABC) + return 0; + + if (!iwl4965_rs_is_valid_ant(valid_ant, tbl->ant_type)) + return 0; + + new_ant_type = ant_toggle_lookup[tbl->ant_type]; + + while ((new_ant_type != tbl->ant_type) && + !iwl4965_rs_is_valid_ant(valid_ant, new_ant_type)) + new_ant_type = ant_toggle_lookup[new_ant_type]; + + if (new_ant_type == tbl->ant_type) + return 0; + + tbl->ant_type = new_ant_type; + *rate_n_flags &= ~RATE_MCS_ANT_ABC_MSK; + *rate_n_flags |= new_ant_type << RATE_MCS_ANT_POS; + return 1; +} + +/** + * Green-field mode is valid if the station supports it and + * there are no non-GF stations present in the BSS. + */ +static bool iwl4965_rs_use_green(struct ieee80211_sta *sta) +{ + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) && + !(ctx->ht.non_gf_sta_present); +} + +/** + * iwl4965_rs_get_supported_rates - get the available rates + * + * if management frame or broadcast frame only return + * basic available rates. + * + */ +static u16 iwl4965_rs_get_supported_rates(struct iwl_lq_sta *lq_sta, + struct ieee80211_hdr *hdr, + enum iwl_table_type rate_type) +{ + if (is_legacy(rate_type)) { + return lq_sta->active_legacy_rate; + } else { + if (is_siso(rate_type)) + return lq_sta->active_siso_rate; + else + return lq_sta->active_mimo2_rate; + } +} + +static u16 +iwl4965_rs_get_adjacent_rate(struct iwl_priv *priv, u8 index, u16 rate_mask, + int rate_type) +{ + u8 high = IWL_RATE_INVALID; + u8 low = IWL_RATE_INVALID; + + /* 802.11A or ht walks to the next literal adjacent rate in + * the rate table */ + if (is_a_band(rate_type) || !is_legacy(rate_type)) { + int i; + u32 mask; + + /* Find the previous rate that is in the rate mask */ + i = index - 1; + for (mask = (1 << i); i >= 0; i--, mask >>= 1) { + if (rate_mask & mask) { + low = i; + break; + } + } + + /* Find the next rate that is in the rate mask */ + i = index + 1; + for (mask = (1 << i); i < IWL_RATE_COUNT; i++, mask <<= 1) { + if (rate_mask & mask) { + high = i; + break; + } + } + + return (high << 8) | low; + } + + low = index; + while (low != IWL_RATE_INVALID) { + low = iwlegacy_rates[low].prev_rs; + if (low == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << low)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked lower rate: %d\n", low); + } + + high = index; + while (high != IWL_RATE_INVALID) { + high = iwlegacy_rates[high].next_rs; + if (high == IWL_RATE_INVALID) + break; + if (rate_mask & (1 << high)) + break; + IWL_DEBUG_RATE(priv, "Skipping masked higher rate: %d\n", high); + } + + return (high << 8) | low; +} + +static u32 iwl4965_rs_get_lower_rate(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + u8 scale_index, u8 ht_possible) +{ + s32 low; + u16 rate_mask; + u16 high_low; + u8 switch_to_legacy = 0; + u8 is_green = lq_sta->is_green; + struct iwl_priv *priv = lq_sta->drv; + + /* check if we need to switch from HT to legacy rates. + * assumption is that mandatory rates (1Mbps or 6Mbps) + * are always supported (spec demand) */ + if (!is_legacy(tbl->lq_type) && (!ht_possible || !scale_index)) { + switch_to_legacy = 1; + scale_index = rs_ht_to_legacy[scale_index]; + if (lq_sta->band == IEEE80211_BAND_5GHZ) + tbl->lq_type = LQ_A; + else + tbl->lq_type = LQ_G; + + if (iwl4965_num_of_ant(tbl->ant_type) > 1) + tbl->ant_type = + iwl4965_first_antenna(priv->hw_params.valid_tx_ant); + + tbl->is_ht40 = 0; + tbl->is_SGI = 0; + tbl->max_search = IWL_MAX_SEARCH; + } + + rate_mask = iwl4965_rs_get_supported_rates(lq_sta, NULL, tbl->lq_type); + + /* Mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + /* supp_rates has no CCK bits in A mode */ + if (lq_sta->band == IEEE80211_BAND_5GHZ) + rate_mask = (u16)(rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_mask = (u16)(rate_mask & lq_sta->supp_rates); + } + + /* If we switched from HT to legacy, check current rate */ + if (switch_to_legacy && (rate_mask & (1 << scale_index))) { + low = scale_index; + goto out; + } + + high_low = iwl4965_rs_get_adjacent_rate(lq_sta->drv, + scale_index, rate_mask, + tbl->lq_type); + low = high_low & 0xff; + + if (low == IWL_RATE_INVALID) + low = scale_index; + +out: + return iwl4965_rate_n_flags_from_tbl(lq_sta->drv, tbl, low, is_green); +} + +/* + * Simple function to compare two rate scale table types + */ +static bool iwl4965_table_type_matches(struct iwl_scale_tbl_info *a, + struct iwl_scale_tbl_info *b) +{ + return (a->lq_type == b->lq_type) && (a->ant_type == b->ant_type) && + (a->is_SGI == b->is_SGI); +} + +/* + * mac80211 sends us Tx status + */ +static void +iwl4965_rs_tx_status(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta, + struct sk_buff *skb) +{ + int legacy_success; + int retries; + int rs_index, mac_index, i; + struct iwl_lq_sta *lq_sta = priv_sta; + struct iwl_link_quality_cmd *table; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_priv *priv = (struct iwl_priv *)priv_r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + enum mac80211_rate_control_flags mac_flags; + u32 tx_rate; + struct iwl_scale_tbl_info tbl_type; + struct iwl_scale_tbl_info *curr_tbl, *other_tbl, *tmp_tbl; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + IWL_DEBUG_RATE_LIMIT(priv, + "get frame ack response, update rate scale window\n"); + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (!lq_sta) { + IWL_DEBUG_RATE(priv, "Station rate scaling not created yet.\n"); + return; + } else if (!lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + return; + } + + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + /* This packet was aggregated but doesn't carry status info */ + if ((info->flags & IEEE80211_TX_CTL_AMPDU) && + !(info->flags & IEEE80211_TX_STAT_AMPDU)) + return; + + /* + * Ignore this Tx frame response if its initial rate doesn't match + * that of latest Link Quality command. There may be stragglers + * from a previous Link Quality command, but we're no longer interested + * in those; they're either from the "active" mode while we're trying + * to check "search" mode, or a prior "search" mode after we've moved + * to a new "search" mode (which might become the new "active" mode). + */ + table = &lq_sta->lq; + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + iwl4965_rs_get_tbl_info_from_mcs(tx_rate, + priv->band, &tbl_type, &rs_index); + if (priv->band == IEEE80211_BAND_5GHZ) + rs_index -= IWL_FIRST_OFDM_RATE; + mac_flags = info->status.rates[0].flags; + mac_index = info->status.rates[0].idx; + /* For HT packets, map MCS to PLCP */ + if (mac_flags & IEEE80211_TX_RC_MCS) { + mac_index &= RATE_MCS_CODE_MSK; /* Remove # of streams */ + if (mac_index >= (IWL_RATE_9M_INDEX - IWL_FIRST_OFDM_RATE)) + mac_index++; + /* + * mac80211 HT index is always zero-indexed; we need to move + * HT OFDM rates after CCK rates in 2.4 GHz band + */ + if (priv->band == IEEE80211_BAND_2GHZ) + mac_index += IWL_FIRST_OFDM_RATE; + } + /* Here we actually compare this rate to the latest LQ command */ + if ((mac_index < 0) || + (tbl_type.is_SGI != + !!(mac_flags & IEEE80211_TX_RC_SHORT_GI)) || + (tbl_type.is_ht40 != + !!(mac_flags & IEEE80211_TX_RC_40_MHZ_WIDTH)) || + (tbl_type.is_dup != + !!(mac_flags & IEEE80211_TX_RC_DUP_DATA)) || + (tbl_type.ant_type != info->antenna_sel_tx) || + (!!(tx_rate & RATE_MCS_HT_MSK) != + !!(mac_flags & IEEE80211_TX_RC_MCS)) || + (!!(tx_rate & RATE_MCS_GF_MSK) != + !!(mac_flags & IEEE80211_TX_RC_GREEN_FIELD)) || + (rs_index != mac_index)) { + IWL_DEBUG_RATE(priv, + "initial rate %d does not match %d (0x%x)\n", + mac_index, rs_index, tx_rate); + /* + * Since rates mis-match, the last LQ command may have failed. + * After IWL_MISSED_RATE_MAX mis-matches, resync the uCode with + * ... driver. + */ + lq_sta->missed_rate_counter++; + if (lq_sta->missed_rate_counter > IWL_MISSED_RATE_MAX) { + lq_sta->missed_rate_counter = 0; + iwl_legacy_send_lq_cmd(priv, ctx, &lq_sta->lq, + CMD_ASYNC, false); + } + /* Regardless, ignore this status info for outdated rate */ + return; + } else + /* Rate did match, so reset the missed_rate_counter */ + lq_sta->missed_rate_counter = 0; + + /* Figure out if rate scale algorithm is in active or search table */ + if (iwl4965_table_type_matches(&tbl_type, + &(lq_sta->lq_info[lq_sta->active_tbl]))) { + curr_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + } else if (iwl4965_table_type_matches(&tbl_type, + &lq_sta->lq_info[1 - lq_sta->active_tbl])) { + curr_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + other_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + } else { + IWL_DEBUG_RATE(priv, + "Neither active nor search matches tx rate\n"); + tmp_tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "active- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + tmp_tbl = &(lq_sta->lq_info[1 - lq_sta->active_tbl]); + IWL_DEBUG_RATE(priv, "search- lq:%x, ant:%x, SGI:%d\n", + tmp_tbl->lq_type, tmp_tbl->ant_type, tmp_tbl->is_SGI); + IWL_DEBUG_RATE(priv, "actual- lq:%x, ant:%x, SGI:%d\n", + tbl_type.lq_type, tbl_type.ant_type, tbl_type.is_SGI); + /* + * no matching table found, let's by-pass the data collection + * and continue to perform rate scale to find the rate table + */ + iwl4965_rs_stay_in_table(lq_sta, true); + goto done; + } + + /* + * Updating the frame history depends on whether packets were + * aggregated. + * + * For aggregation, all packets were transmitted at the same rate, the + * first index into rate scale table. + */ + if (info->flags & IEEE80211_TX_STAT_AMPDU) { + tx_rate = le32_to_cpu(table->rs_table[0].rate_n_flags); + iwl4965_rs_get_tbl_info_from_mcs(tx_rate, priv->band, &tbl_type, + &rs_index); + iwl4965_rs_collect_tx_data(curr_tbl, rs_index, + info->status.ampdu_len, + info->status.ampdu_ack_len); + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += info->status.ampdu_ack_len; + lq_sta->total_failed += (info->status.ampdu_len - + info->status.ampdu_ack_len); + } + } else { + /* + * For legacy, update frame history with for each Tx retry. + */ + retries = info->status.rates[0].count - 1; + /* HW doesn't send more than 15 retries */ + retries = min(retries, 15); + + /* The last transmission may have been successful */ + legacy_success = !!(info->flags & IEEE80211_TX_STAT_ACK); + /* Collect data for each rate used during failed TX attempts */ + for (i = 0; i <= retries; ++i) { + tx_rate = le32_to_cpu(table->rs_table[i].rate_n_flags); + iwl4965_rs_get_tbl_info_from_mcs(tx_rate, priv->band, + &tbl_type, &rs_index); + /* + * Only collect stats if retried rate is in the same RS + * table as active/search. + */ + if (iwl4965_table_type_matches(&tbl_type, curr_tbl)) + tmp_tbl = curr_tbl; + else if (iwl4965_table_type_matches(&tbl_type, + other_tbl)) + tmp_tbl = other_tbl; + else + continue; + iwl4965_rs_collect_tx_data(tmp_tbl, rs_index, 1, + i < retries ? 0 : legacy_success); + } + + /* Update success/fail counts if not searching for new mode */ + if (lq_sta->stay_in_tbl) { + lq_sta->total_success += legacy_success; + lq_sta->total_failed += retries + (1 - legacy_success); + } + } + /* The last TX rate is cached in lq_sta; it's set in if/else above */ + lq_sta->last_rate_n_flags = tx_rate; +done: + /* See if there's a better rate or modulation mode to try. */ + if (sta && sta->supp_rates[sband->band]) + iwl4965_rs_rate_scale_perform(priv, skb, sta, lq_sta); +} + +/* + * Begin a period of staying with a selected modulation mode. + * Set "stay_in_tbl" flag to prevent any mode switches. + * Set frame tx success limits according to legacy vs. high-throughput, + * and reset overall (spanning all rates) tx success history statistics. + * These control how long we stay using same modulation mode before + * searching for a new mode. + */ +static void iwl4965_rs_set_stay_in_table(struct iwl_priv *priv, u8 is_legacy, + struct iwl_lq_sta *lq_sta) +{ + IWL_DEBUG_RATE(priv, "we are staying in the same table\n"); + lq_sta->stay_in_tbl = 1; /* only place this gets set */ + if (is_legacy) { + lq_sta->table_count_limit = IWL_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_LEGACY_SUCCESS_LIMIT; + } else { + lq_sta->table_count_limit = IWL_NONE_LEGACY_TABLE_COUNT; + lq_sta->max_failure_limit = IWL_NONE_LEGACY_FAILURE_LIMIT; + lq_sta->max_success_limit = IWL_NONE_LEGACY_SUCCESS_LIMIT; + } + lq_sta->table_count = 0; + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = jiffies; + lq_sta->action_counter = 0; +} + +/* + * Find correct throughput table for given mode of modulation + */ +static void iwl4965_rs_set_expected_tpt_table(struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl) +{ + /* Used to choose among HT tables */ + s32 (*ht_tbl_pointer)[IWL_RATE_COUNT]; + + /* Check for invalid LQ type */ + if (WARN_ON_ONCE(!is_legacy(tbl->lq_type) && !is_Ht(tbl->lq_type))) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Legacy rates have only one table */ + if (is_legacy(tbl->lq_type)) { + tbl->expected_tpt = expected_tpt_legacy; + return; + } + + /* Choose among many HT tables depending on number of streams + * (SISO/MIMO2), channel width (20/40), SGI, and aggregation + * status */ + if (is_siso(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_siso20MHz; + else if (is_siso(tbl->lq_type)) + ht_tbl_pointer = expected_tpt_siso40MHz; + else if (is_mimo2(tbl->lq_type) && (!tbl->is_ht40 || lq_sta->is_dup)) + ht_tbl_pointer = expected_tpt_mimo2_20MHz; + else /* if (is_mimo2(tbl->lq_type)) <-- must be true */ + ht_tbl_pointer = expected_tpt_mimo2_40MHz; + + if (!tbl->is_SGI && !lq_sta->is_agg) /* Normal */ + tbl->expected_tpt = ht_tbl_pointer[0]; + else if (tbl->is_SGI && !lq_sta->is_agg) /* SGI */ + tbl->expected_tpt = ht_tbl_pointer[1]; + else if (!tbl->is_SGI && lq_sta->is_agg) /* AGG */ + tbl->expected_tpt = ht_tbl_pointer[2]; + else /* AGG+SGI */ + tbl->expected_tpt = ht_tbl_pointer[3]; +} + +/* + * Find starting rate for new "search" high-throughput mode of modulation. + * Goal is to find lowest expected rate (under perfect conditions) that is + * above the current measured throughput of "active" mode, to give new mode + * a fair chance to prove itself without too many challenges. + * + * This gets called when transitioning to more aggressive modulation + * (i.e. legacy to SISO or MIMO, or SISO to MIMO), as well as less aggressive + * (i.e. MIMO to SISO). When moving to MIMO, bit rate will typically need + * to decrease to match "active" throughput. When moving from MIMO to SISO, + * bit rate will typically need to increase, but not if performance was bad. + */ +static s32 iwl4965_rs_get_best_rate(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, /* "search" */ + u16 rate_mask, s8 index) +{ + /* "active" values */ + struct iwl_scale_tbl_info *active_tbl = + &(lq_sta->lq_info[lq_sta->active_tbl]); + s32 active_sr = active_tbl->win[index].success_ratio; + s32 active_tpt = active_tbl->expected_tpt[index]; + + /* expected "search" throughput */ + s32 *tpt_tbl = tbl->expected_tpt; + + s32 new_rate, high, low, start_hi; + u16 high_low; + s8 rate = index; + + new_rate = high = low = start_hi = IWL_RATE_INVALID; + + for (; ;) { + high_low = iwl4965_rs_get_adjacent_rate(priv, rate, rate_mask, + tbl->lq_type); + + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* + * Lower the "search" bit rate, to give new "search" mode + * approximately the same throughput as "active" if: + * + * 1) "Active" mode has been working modestly well (but not + * great), and expected "search" throughput (under perfect + * conditions) at candidate rate is above the actual + * measured "active" throughput (but less than expected + * "active" throughput under perfect conditions). + * OR + * 2) "Active" mode has been working perfectly or very well + * and expected "search" throughput (under perfect + * conditions) at candidate rate is above expected + * "active" throughput (under perfect conditions). + */ + if ((((100 * tpt_tbl[rate]) > lq_sta->last_tpt) && + ((active_sr > IWL_RATE_DECREASE_TH) && + (active_sr <= IWL_RATE_HIGH_TH) && + (tpt_tbl[rate] <= active_tpt))) || + ((active_sr >= IWL_RATE_SCALE_SWITCH) && + (tpt_tbl[rate] > active_tpt))) { + + /* (2nd or later pass) + * If we've already tried to raise the rate, and are + * now trying to lower it, use the higher rate. */ + if (start_hi != IWL_RATE_INVALID) { + new_rate = start_hi; + break; + } + + new_rate = rate; + + /* Loop again with lower rate */ + if (low != IWL_RATE_INVALID) + rate = low; + + /* Lower rate not available, use the original */ + else + break; + + /* Else try to raise the "search" rate to match "active" */ + } else { + /* (2nd or later pass) + * If we've already tried to lower the rate, and are + * now trying to raise it, use the lower rate. */ + if (new_rate != IWL_RATE_INVALID) + break; + + /* Loop again with higher rate */ + else if (high != IWL_RATE_INVALID) { + start_hi = high; + rate = high; + + /* Higher rate not available, use the original */ + } else { + new_rate = rate; + break; + } + } + } + + return new_rate; +} + +/* + * Set up search table for MIMO2 + */ +static int iwl4965_rs_switch_to_mimo2(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + s32 rate; + s8 is_green = lq_sta->is_green; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + if (((sta->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >> 2) + == WLAN_HT_CAP_SM_PS_STATIC) + return -1; + + /* Need both Tx chains/antennas to support MIMO */ + if (priv->hw_params.tx_chains_num < 2) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to MIMO2\n"); + + tbl->lq_type = LQ_MIMO2; + tbl->is_dup = lq_sta->is_dup; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_mimo2_rate; + + if (iwl_legacy_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + iwl4965_rs_set_expected_tpt_table(lq_sta, tbl); + + rate = iwl4965_rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 best rate %d mask %X\n", + rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, + "Can't switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = iwl4965_rate_n_flags_from_tbl(priv, + tbl, rate, is_green); + + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Set up search table for SISO + */ +static int iwl4965_rs_switch_to_siso(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_scale_tbl_info *tbl, int index) +{ + u16 rate_mask; + u8 is_green = lq_sta->is_green; + s32 rate; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + if (!conf_is_ht(conf) || !sta->ht_cap.ht_supported) + return -1; + + IWL_DEBUG_RATE(priv, "LQ: try to switch to SISO\n"); + + tbl->is_dup = lq_sta->is_dup; + tbl->lq_type = LQ_SISO; + tbl->action = 0; + tbl->max_search = IWL_MAX_SEARCH; + rate_mask = lq_sta->active_siso_rate; + + if (iwl_legacy_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + tbl->is_ht40 = 1; + else + tbl->is_ht40 = 0; + + if (is_green) + tbl->is_SGI = 0; /*11n spec: no SGI in SISO+Greenfield*/ + + iwl4965_rs_set_expected_tpt_table(lq_sta, tbl); + rate = iwl4965_rs_get_best_rate(priv, lq_sta, tbl, rate_mask, index); + + IWL_DEBUG_RATE(priv, "LQ: get best rate %d mask %X\n", rate, rate_mask); + if ((rate == IWL_RATE_INVALID) || !((1 << rate) & rate_mask)) { + IWL_DEBUG_RATE(priv, + "can not switch with index %d rate mask %x\n", + rate, rate_mask); + return -1; + } + tbl->current_rate = iwl4965_rate_n_flags_from_tbl(priv, + tbl, rate, is_green); + IWL_DEBUG_RATE(priv, "LQ: Switch to new mcs %X index is green %X\n", + tbl->current_rate, is_green); + return 0; +} + +/* + * Try to switch to new modulation mode from legacy + */ +static int iwl4965_rs_move_legacy_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + int index) +{ + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + int ret = 0; + u8 update_search_tbl_counter = 0; + + tbl->action = IWL_LEGACY_SWITCH_SISO; + + start_action = tbl->action; + for (; ;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_LEGACY_SWITCH_ANTENNA1: + case IWL_LEGACY_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: Legacy toggle Antenna\n"); + + if ((tbl->action == IWL_LEGACY_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_LEGACY_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + /* Don't change antenna if success has been great */ + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + /* Set up search table to try other antenna */ + memcpy(search_tbl, tbl, sz); + + if (iwl4965_rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + iwl4965_rs_set_expected_tpt_table(lq_sta, + search_tbl); + goto out; + } + break; + case IWL_LEGACY_SWITCH_SISO: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to SISO\n"); + + /* Set up search table to try SISO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + ret = iwl4965_rs_switch_to_siso(priv, lq_sta, conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + + break; + case IWL_LEGACY_SWITCH_MIMO2_AB: + case IWL_LEGACY_SWITCH_MIMO2_AC: + case IWL_LEGACY_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: Legacy switch to MIMO2\n"); + + /* Set up search table to try MIMO */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_LEGACY_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!iwl4965_rs_is_valid_ant(valid_tx_ant, + search_tbl->ant_type)) + break; + + ret = iwl4965_rs_switch_to_mimo2(priv, lq_sta, + conf, sta, + search_tbl, index); + if (!ret) { + lq_sta->action_counter = 0; + goto out; + } + break; + } + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + + } + search_tbl->lq_type = LQ_NONE; + return 0; + +out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_LEGACY_SWITCH_MIMO2_BC) + tbl->action = IWL_LEGACY_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + return 0; + +} + +/* + * Try to switch to new modulation mode from SISO + */ +static int iwl4965_rs_move_siso_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + u8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + start_action = tbl->action; + + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_SISO_SWITCH_ANTENNA1: + case IWL_SISO_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: SISO toggle Antenna\n"); + if ((tbl->action == IWL_SISO_SWITCH_ANTENNA1 && + tx_chains_num <= 1) || + (tbl->action == IWL_SISO_SWITCH_ANTENNA2 && + tx_chains_num <= 2)) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (iwl4965_rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_SISO_SWITCH_MIMO2_AB: + case IWL_SISO_SWITCH_MIMO2_AC: + case IWL_SISO_SWITCH_MIMO2_BC: + IWL_DEBUG_RATE(priv, "LQ: SISO switch to MIMO2\n"); + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = 0; + + if (tbl->action == IWL_SISO_SWITCH_MIMO2_AB) + search_tbl->ant_type = ANT_AB; + else if (tbl->action == IWL_SISO_SWITCH_MIMO2_AC) + search_tbl->ant_type = ANT_AC; + else + search_tbl->ant_type = ANT_BC; + + if (!iwl4965_rs_is_valid_ant(valid_tx_ant, + search_tbl->ant_type)) + break; + + ret = iwl4965_rs_switch_to_mimo2(priv, lq_sta, + conf, sta, + search_tbl, index); + if (!ret) + goto out; + break; + case IWL_SISO_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: SISO toggle SGI/NGI\n"); + + memcpy(search_tbl, tbl, sz); + if (is_green) { + if (!tbl->is_SGI) + break; + else + IWL_ERR(priv, + "SGI was set in GF+SISO\n"); + } + search_tbl->is_SGI = !tbl->is_SGI; + iwl4965_rs_set_expected_tpt_table(lq_sta, search_tbl); + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + iwl4965_rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + } + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_GI) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_SISO_SWITCH_GI) + tbl->action = IWL_SISO_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; +} + +/* + * Try to switch to new modulation mode from MIMO2 + */ +static int iwl4965_rs_move_mimo2_to_other(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, int index) +{ + s8 is_green = lq_sta->is_green; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + struct iwl_scale_tbl_info *search_tbl = + &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + struct iwl_rate_scale_data *window = &(tbl->win[index]); + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + u32 sz = (sizeof(struct iwl_scale_tbl_info) - + (sizeof(struct iwl_rate_scale_data) * IWL_RATE_COUNT)); + u8 start_action; + u8 valid_tx_ant = priv->hw_params.valid_tx_ant; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + u8 update_search_tbl_counter = 0; + int ret; + + start_action = tbl->action; + for (;;) { + lq_sta->action_counter++; + switch (tbl->action) { + case IWL_MIMO2_SWITCH_ANTENNA1: + case IWL_MIMO2_SWITCH_ANTENNA2: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle Antennas\n"); + + if (tx_chains_num <= 2) + break; + + if (window->success_ratio >= IWL_RS_GOOD_RATIO) + break; + + memcpy(search_tbl, tbl, sz); + if (iwl4965_rs_toggle_antenna(valid_tx_ant, + &search_tbl->current_rate, search_tbl)) { + update_search_tbl_counter = 1; + goto out; + } + break; + case IWL_MIMO2_SWITCH_SISO_A: + case IWL_MIMO2_SWITCH_SISO_B: + case IWL_MIMO2_SWITCH_SISO_C: + IWL_DEBUG_RATE(priv, "LQ: MIMO2 switch to SISO\n"); + + /* Set up new search table for SISO */ + memcpy(search_tbl, tbl, sz); + + if (tbl->action == IWL_MIMO2_SWITCH_SISO_A) + search_tbl->ant_type = ANT_A; + else if (tbl->action == IWL_MIMO2_SWITCH_SISO_B) + search_tbl->ant_type = ANT_B; + else + search_tbl->ant_type = ANT_C; + + if (!iwl4965_rs_is_valid_ant(valid_tx_ant, + search_tbl->ant_type)) + break; + + ret = iwl4965_rs_switch_to_siso(priv, lq_sta, + conf, sta, + search_tbl, index); + if (!ret) + goto out; + + break; + + case IWL_MIMO2_SWITCH_GI: + if (!tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_20)) + break; + if (tbl->is_ht40 && !(ht_cap->cap & + IEEE80211_HT_CAP_SGI_40)) + break; + + IWL_DEBUG_RATE(priv, "LQ: MIMO2 toggle SGI/NGI\n"); + + /* Set up new search table for MIMO2 */ + memcpy(search_tbl, tbl, sz); + search_tbl->is_SGI = !tbl->is_SGI; + iwl4965_rs_set_expected_tpt_table(lq_sta, search_tbl); + /* + * If active table already uses the fastest possible + * modulation (dual stream with short guard interval), + * and it's working well, there's no need to look + * for a better type of modulation! + */ + if (tbl->is_SGI) { + s32 tpt = lq_sta->last_tpt / 100; + if (tpt >= search_tbl->expected_tpt[index]) + break; + } + search_tbl->current_rate = + iwl4965_rate_n_flags_from_tbl(priv, search_tbl, + index, is_green); + update_search_tbl_counter = 1; + goto out; + + } + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_GI) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + + if (tbl->action == start_action) + break; + } + search_tbl->lq_type = LQ_NONE; + return 0; + out: + lq_sta->search_better_tbl = 1; + tbl->action++; + if (tbl->action > IWL_MIMO2_SWITCH_GI) + tbl->action = IWL_MIMO2_SWITCH_ANTENNA1; + if (update_search_tbl_counter) + search_tbl->action = tbl->action; + + return 0; + +} + +/* + * Check whether we should continue using same modulation mode, or + * begin search for a new mode, based on: + * 1) # tx successes or failures while using this mode + * 2) # times calling this function + * 3) elapsed time in this mode (not used, for now) + */ +static void +iwl4965_rs_stay_in_table(struct iwl_lq_sta *lq_sta, bool force_search) +{ + struct iwl_scale_tbl_info *tbl; + int i; + int active_tbl; + int flush_interval_passed = 0; + struct iwl_priv *priv; + + priv = lq_sta->drv; + active_tbl = lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + /* If we've been disallowing search, see if we should now allow it */ + if (lq_sta->stay_in_tbl) { + + /* Elapsed time using current modulation mode */ + if (lq_sta->flush_timer) + flush_interval_passed = + time_after(jiffies, + (unsigned long)(lq_sta->flush_timer + + IWL_RATE_SCALE_FLUSH_INTVL)); + + /* + * Check if we should allow search for new modulation mode. + * If many frames have failed or succeeded, or we've used + * this same modulation for a long time, allow search, and + * reset history stats that keep track of whether we should + * allow a new search. Also (below) reset all bitmaps and + * stats in active history. + */ + if (force_search || + (lq_sta->total_failed > lq_sta->max_failure_limit) || + (lq_sta->total_success > lq_sta->max_success_limit) || + ((!lq_sta->search_better_tbl) && (lq_sta->flush_timer) + && (flush_interval_passed))) { + IWL_DEBUG_RATE(priv, "LQ: stay is expired %d %d %d\n:", + lq_sta->total_failed, + lq_sta->total_success, + flush_interval_passed); + + /* Allow search for new mode */ + lq_sta->stay_in_tbl = 0; /* only place reset */ + lq_sta->total_failed = 0; + lq_sta->total_success = 0; + lq_sta->flush_timer = 0; + + /* + * Else if we've used this modulation mode enough repetitions + * (regardless of elapsed time or success/failure), reset + * history bitmaps and rate-specific stats for all rates in + * active table. + */ + } else { + lq_sta->table_count++; + if (lq_sta->table_count >= + lq_sta->table_count_limit) { + lq_sta->table_count = 0; + + IWL_DEBUG_RATE(priv, + "LQ: stay in table clear win\n"); + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl4965_rs_rate_scale_clear_window( + &(tbl->win[i])); + } + } + + /* If transitioning to allow "search", reset all history + * bitmaps and stats in active table (this will become the new + * "search" table). */ + if (!lq_sta->stay_in_tbl) { + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl4965_rs_rate_scale_clear_window( + &(tbl->win[i])); + } + } +} + +/* + * setup rate table in uCode + * return rate_n_flags as used in the table + */ +static u32 iwl4965_rs_update_rate_tbl(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_lq_sta *lq_sta, + struct iwl_scale_tbl_info *tbl, + int index, u8 is_green) +{ + u32 rate; + + /* Update uCode's rate table. */ + rate = iwl4965_rate_n_flags_from_tbl(priv, tbl, index, is_green); + iwl4965_rs_fill_link_cmd(priv, lq_sta, rate); + iwl_legacy_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_ASYNC, false); + + return rate; +} + +/* + * Do rate scaling and search for new modulation mode. + */ +static void iwl4965_rs_rate_scale_perform(struct iwl_priv *priv, + struct sk_buff *skb, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + int low = IWL_RATE_INVALID; + int high = IWL_RATE_INVALID; + int index; + int i; + struct iwl_rate_scale_data *window = NULL; + int current_tpt = IWL_INVALID_VALUE; + int low_tpt = IWL_INVALID_VALUE; + int high_tpt = IWL_INVALID_VALUE; + u32 fail_count; + s8 scale_action = 0; + u16 rate_mask; + u8 update_lq = 0; + struct iwl_scale_tbl_info *tbl, *tbl1; + u16 rate_scale_index_msk = 0; + u32 rate; + u8 is_green = 0; + u8 active_tbl = 0; + u8 done_search = 0; + u16 high_low; + s32 sr; + u8 tid = MAX_TID_COUNT; + struct iwl_tid_data *tid_data; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + IWL_DEBUG_RATE(priv, "rate scale calculate new rate for skb\n"); + + /* Send management frames and NO_ACK data using lowest rate. */ + /* TODO: this could probably be improved.. */ + if (!ieee80211_is_data(hdr->frame_control) || + info->flags & IEEE80211_TX_CTL_NO_ACK) + return; + + if (!sta || !lq_sta) + return; + + lq_sta->supp_rates = sta->supp_rates[lq_sta->band]; + + tid = iwl4965_rs_tl_add_packet(lq_sta, hdr); + if ((tid != MAX_TID_COUNT) && (lq_sta->tx_agg_tid_en & (1 << tid))) { + tid_data = &priv->stations[lq_sta->lq.sta_id].tid[tid]; + if (tid_data->agg.state == IWL_AGG_OFF) + lq_sta->is_agg = 0; + else + lq_sta->is_agg = 1; + } else + lq_sta->is_agg = 0; + + /* + * Select rate-scale / modulation-mode table to work with in + * the rest of this function: "search" if searching for better + * modulation mode, or "active" if doing rate scaling within a mode. + */ + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + if (is_legacy(tbl->lq_type)) + lq_sta->is_green = 0; + else + lq_sta->is_green = iwl4965_rs_use_green(sta); + is_green = lq_sta->is_green; + + /* current tx rate */ + index = lq_sta->last_txrate_idx; + + IWL_DEBUG_RATE(priv, "Rate scale index %d for type %d\n", index, + tbl->lq_type); + + /* rates available for this association, and for modulation mode */ + rate_mask = iwl4965_rs_get_supported_rates(lq_sta, hdr, tbl->lq_type); + + IWL_DEBUG_RATE(priv, "mask 0x%04X\n", rate_mask); + + /* mask with station rate restriction */ + if (is_legacy(tbl->lq_type)) { + if (lq_sta->band == IEEE80211_BAND_5GHZ) + /* supp_rates has no CCK bits in A mode */ + rate_scale_index_msk = (u16) (rate_mask & + (lq_sta->supp_rates << IWL_FIRST_OFDM_RATE)); + else + rate_scale_index_msk = (u16) (rate_mask & + lq_sta->supp_rates); + + } else + rate_scale_index_msk = rate_mask; + + if (!rate_scale_index_msk) + rate_scale_index_msk = rate_mask; + + if (!((1 << index) & rate_scale_index_msk)) { + IWL_ERR(priv, "Current Rate is not valid\n"); + if (lq_sta->search_better_tbl) { + /* revert to active table if search table is not valid*/ + tbl->lq_type = LQ_NONE; + lq_sta->search_better_tbl = 0; + tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + /* get "active" rate info */ + index = iwl4965_hwrate_to_plcp_idx(tbl->current_rate); + rate = iwl4965_rs_update_rate_tbl(priv, ctx, lq_sta, + tbl, index, is_green); + } + return; + } + + /* Get expected throughput table and history window for current rate */ + if (!tbl->expected_tpt) { + IWL_ERR(priv, "tbl->expected_tpt is NULL\n"); + return; + } + + /* force user max rate if set by user */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < index)) { + index = lq_sta->max_rate_idx; + update_lq = 1; + window = &(tbl->win[index]); + goto lq_update; + } + + window = &(tbl->win[index]); + + /* + * If there is not enough history to calculate actual average + * throughput, keep analyzing results of more tx frames, without + * changing rate or mode (bypass most of the rest of this function). + * Set up new rate table in uCode only if old rate is not supported + * in current association (use new rate found above). + */ + fail_count = window->counter - window->success_counter; + if ((fail_count < IWL_RATE_MIN_FAILURE_TH) && + (window->success_counter < IWL_RATE_MIN_SUCCESS_TH)) { + IWL_DEBUG_RATE(priv, "LQ: still below TH. succ=%d total=%d " + "for index %d\n", + window->success_counter, window->counter, index); + + /* Can't calculate this yet; not enough history */ + window->average_tpt = IWL_INVALID_VALUE; + + /* Should we stay with this modulation mode, + * or search for a new one? */ + iwl4965_rs_stay_in_table(lq_sta, false); + + goto out; + } + /* Else we have enough samples; calculate estimate of + * actual average throughput */ + if (window->average_tpt != ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128)) { + IWL_ERR(priv, + "expected_tpt should have been calculated by now\n"); + window->average_tpt = ((window->success_ratio * + tbl->expected_tpt[index] + 64) / 128); + } + + /* If we are searching for better modulation mode, check success. */ + if (lq_sta->search_better_tbl) { + /* If good success, continue using the "search" mode; + * no need to send new link quality command, since we're + * continuing to use the setup that we've been trying. */ + if (window->average_tpt > lq_sta->last_tpt) { + + IWL_DEBUG_RATE(priv, "LQ: SWITCHING TO NEW TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + if (!is_legacy(tbl->lq_type)) + lq_sta->enable_counter = 1; + + /* Swap tables; "search" becomes "active" */ + lq_sta->active_tbl = active_tbl; + current_tpt = window->average_tpt; + + /* Else poor success; go back to mode in "active" table */ + } else { + + IWL_DEBUG_RATE(priv, "LQ: GOING BACK TO THE OLD TABLE " + "suc=%d cur-tpt=%d old-tpt=%d\n", + window->success_ratio, + window->average_tpt, + lq_sta->last_tpt); + + /* Nullify "search" table */ + tbl->lq_type = LQ_NONE; + + /* Revert to "active" table */ + active_tbl = lq_sta->active_tbl; + tbl = &(lq_sta->lq_info[active_tbl]); + + /* Revert to "active" rate and throughput info */ + index = iwl4965_hwrate_to_plcp_idx(tbl->current_rate); + current_tpt = lq_sta->last_tpt; + + /* Need to set up a new rate table in uCode */ + update_lq = 1; + } + + /* Either way, we've made a decision; modulation mode + * search is done, allow rate adjustment next time. */ + lq_sta->search_better_tbl = 0; + done_search = 1; /* Don't switch modes below! */ + goto lq_update; + } + + /* (Else) not in search of better modulation mode, try for better + * starting rate, while staying in this mode. */ + high_low = iwl4965_rs_get_adjacent_rate(priv, index, + rate_scale_index_msk, + tbl->lq_type); + low = high_low & 0xff; + high = (high_low >> 8) & 0xff; + + /* If user set max rate, dont allow higher than user constrain */ + if ((lq_sta->max_rate_idx != -1) && + (lq_sta->max_rate_idx < high)) + high = IWL_RATE_INVALID; + + sr = window->success_ratio; + + /* Collect measured throughputs for current and adjacent rates */ + current_tpt = window->average_tpt; + if (low != IWL_RATE_INVALID) + low_tpt = tbl->win[low].average_tpt; + if (high != IWL_RATE_INVALID) + high_tpt = tbl->win[high].average_tpt; + + scale_action = 0; + + /* Too many failures, decrease rate */ + if ((sr <= IWL_RATE_DECREASE_TH) || (current_tpt == 0)) { + IWL_DEBUG_RATE(priv, + "decrease rate because of low success_ratio\n"); + scale_action = -1; + + /* No throughput measured yet for adjacent rates; try increase. */ + } else if ((low_tpt == IWL_INVALID_VALUE) && + (high_tpt == IWL_INVALID_VALUE)) { + + if (high != IWL_RATE_INVALID && sr >= IWL_RATE_INCREASE_TH) + scale_action = 1; + else if (low != IWL_RATE_INVALID) + scale_action = 0; + } + + /* Both adjacent throughputs are measured, but neither one has better + * throughput; we're using the best rate, don't change it! */ + else if ((low_tpt != IWL_INVALID_VALUE) && + (high_tpt != IWL_INVALID_VALUE) && + (low_tpt < current_tpt) && + (high_tpt < current_tpt)) + scale_action = 0; + + /* At least one adjacent rate's throughput is measured, + * and may have better performance. */ + else { + /* Higher adjacent rate's throughput is measured */ + if (high_tpt != IWL_INVALID_VALUE) { + /* Higher rate has better throughput */ + if (high_tpt > current_tpt && + sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } else { + scale_action = 0; + } + + /* Lower adjacent rate's throughput is measured */ + } else if (low_tpt != IWL_INVALID_VALUE) { + /* Lower rate has better throughput */ + if (low_tpt > current_tpt) { + IWL_DEBUG_RATE(priv, + "decrease rate because of low tpt\n"); + scale_action = -1; + } else if (sr >= IWL_RATE_INCREASE_TH) { + scale_action = 1; + } + } + } + + /* Sanity check; asked for decrease, but success rate or throughput + * has been good at old rate. Don't change it. */ + if ((scale_action == -1) && (low != IWL_RATE_INVALID) && + ((sr > IWL_RATE_HIGH_TH) || + (current_tpt > (100 * tbl->expected_tpt[low])))) + scale_action = 0; + + switch (scale_action) { + case -1: + /* Decrease starting rate, update uCode's rate table */ + if (low != IWL_RATE_INVALID) { + update_lq = 1; + index = low; + } + + break; + case 1: + /* Increase starting rate, update uCode's rate table */ + if (high != IWL_RATE_INVALID) { + update_lq = 1; + index = high; + } + + break; + case 0: + /* No change */ + default: + break; + } + + IWL_DEBUG_RATE(priv, "choose rate scale index %d action %d low %d " + "high %d type %d\n", + index, scale_action, low, high, tbl->lq_type); + +lq_update: + /* Replace uCode's rate table for the destination station. */ + if (update_lq) + rate = iwl4965_rs_update_rate_tbl(priv, ctx, lq_sta, + tbl, index, is_green); + + /* Should we stay with this modulation mode, + * or search for a new one? */ + iwl4965_rs_stay_in_table(lq_sta, false); + + /* + * Search for new modulation mode if we're: + * 1) Not changing rates right now + * 2) Not just finishing up a search + * 3) Allowing a new search + */ + if (!update_lq && !done_search && + !lq_sta->stay_in_tbl && window->counter) { + /* Save current throughput to compare with "search" throughput*/ + lq_sta->last_tpt = current_tpt; + + /* Select a new "search" modulation mode to try. + * If one is found, set up the new "search" table. */ + if (is_legacy(tbl->lq_type)) + iwl4965_rs_move_legacy_other(priv, lq_sta, + conf, sta, index); + else if (is_siso(tbl->lq_type)) + iwl4965_rs_move_siso_to_other(priv, lq_sta, + conf, sta, index); + else /* (is_mimo2(tbl->lq_type)) */ + iwl4965_rs_move_mimo2_to_other(priv, lq_sta, + conf, sta, index); + + /* If new "search" mode was selected, set up in uCode table */ + if (lq_sta->search_better_tbl) { + /* Access the "search" table, clear its history. */ + tbl = &(lq_sta->lq_info[(1 - lq_sta->active_tbl)]); + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl4965_rs_rate_scale_clear_window( + &(tbl->win[i])); + + /* Use new "search" start rate */ + index = iwl4965_hwrate_to_plcp_idx(tbl->current_rate); + + IWL_DEBUG_RATE(priv, + "Switch current mcs: %X index: %d\n", + tbl->current_rate, index); + iwl4965_rs_fill_link_cmd(priv, lq_sta, + tbl->current_rate); + iwl_legacy_send_lq_cmd(priv, ctx, + &lq_sta->lq, CMD_ASYNC, false); + } else + done_search = 1; + } + + if (done_search && !lq_sta->stay_in_tbl) { + /* If the "active" (non-search) mode was legacy, + * and we've tried switching antennas, + * but we haven't been able to try HT modes (not available), + * stay with best antenna legacy modulation for a while + * before next round of mode comparisons. */ + tbl1 = &(lq_sta->lq_info[lq_sta->active_tbl]); + if (is_legacy(tbl1->lq_type) && !conf_is_ht(conf) && + lq_sta->action_counter > tbl1->max_search) { + IWL_DEBUG_RATE(priv, "LQ: STAY in legacy table\n"); + iwl4965_rs_set_stay_in_table(priv, 1, lq_sta); + } + + /* If we're in an HT mode, and all 3 mode switch actions + * have been tried and compared, stay in this best modulation + * mode for a while before next round of mode comparisons. */ + if (lq_sta->enable_counter && + (lq_sta->action_counter >= tbl1->max_search)) { + if ((lq_sta->last_tpt > IWL_AGG_TPT_THREHOLD) && + (lq_sta->tx_agg_tid_en & (1 << tid)) && + (tid != MAX_TID_COUNT)) { + tid_data = + &priv->stations[lq_sta->lq.sta_id].tid[tid]; + if (tid_data->agg.state == IWL_AGG_OFF) { + IWL_DEBUG_RATE(priv, + "try to aggregate tid %d\n", + tid); + iwl4965_rs_tl_turn_on_agg(priv, tid, + lq_sta, sta); + } + } + iwl4965_rs_set_stay_in_table(priv, 0, lq_sta); + } + } + +out: + tbl->current_rate = iwl4965_rate_n_flags_from_tbl(priv, tbl, + index, is_green); + i = index; + lq_sta->last_txrate_idx = i; +} + +/** + * iwl4965_rs_initialize_lq - Initialize a station's hardware rate table + * + * The uCode's station table contains a table of fallback rates + * for automatic fallback during transmission. + * + * NOTE: This sets up a default set of values. These will be replaced later + * if the driver's iwl-4965-rs rate scaling algorithm is used, instead of + * rc80211_simple. + * + * NOTE: Run REPLY_ADD_STA command to set up station table entry, before + * calling this function (which runs REPLY_TX_LINK_QUALITY_CMD, + * which requires station table entry to exist). + */ +static void iwl4965_rs_initialize_lq(struct iwl_priv *priv, + struct ieee80211_conf *conf, + struct ieee80211_sta *sta, + struct iwl_lq_sta *lq_sta) +{ + struct iwl_scale_tbl_info *tbl; + int rate_idx; + int i; + u32 rate; + u8 use_green = iwl4965_rs_use_green(sta); + u8 active_tbl = 0; + u8 valid_tx_ant; + struct iwl_station_priv *sta_priv; + struct iwl_rxon_context *ctx; + + if (!sta || !lq_sta) + return; + + sta_priv = (void *)sta->drv_priv; + ctx = sta_priv->common.ctx; + + i = lq_sta->last_txrate_idx; + + valid_tx_ant = priv->hw_params.valid_tx_ant; + + if (!lq_sta->search_better_tbl) + active_tbl = lq_sta->active_tbl; + else + active_tbl = 1 - lq_sta->active_tbl; + + tbl = &(lq_sta->lq_info[active_tbl]); + + if ((i < 0) || (i >= IWL_RATE_COUNT)) + i = 0; + + rate = iwlegacy_rates[i].plcp; + tbl->ant_type = iwl4965_first_antenna(valid_tx_ant); + rate |= tbl->ant_type << RATE_MCS_ANT_POS; + + if (i >= IWL_FIRST_CCK_RATE && i <= IWL_LAST_CCK_RATE) + rate |= RATE_MCS_CCK_MSK; + + iwl4965_rs_get_tbl_info_from_mcs(rate, priv->band, tbl, &rate_idx); + if (!iwl4965_rs_is_valid_ant(valid_tx_ant, tbl->ant_type)) + iwl4965_rs_toggle_antenna(valid_tx_ant, &rate, tbl); + + rate = iwl4965_rate_n_flags_from_tbl(priv, tbl, rate_idx, use_green); + tbl->current_rate = rate; + iwl4965_rs_set_expected_tpt_table(lq_sta, tbl); + iwl4965_rs_fill_link_cmd(NULL, lq_sta, rate); + priv->stations[lq_sta->lq.sta_id].lq = &lq_sta->lq; + iwl_legacy_send_lq_cmd(priv, ctx, &lq_sta->lq, CMD_SYNC, true); +} + +static void +iwl4965_rs_get_rate(void *priv_r, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) +{ + + struct sk_buff *skb = txrc->skb; + struct ieee80211_supported_band *sband = txrc->sband; + struct iwl_priv *priv __maybe_unused = (struct iwl_priv *)priv_r; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_lq_sta *lq_sta = priv_sta; + int rate_idx; + + IWL_DEBUG_RATE_LIMIT(priv, "rate scale calculate new rate for skb\n"); + + /* Get max rate if user set max rate */ + if (lq_sta) { + lq_sta->max_rate_idx = txrc->max_rate_idx; + if ((sband->band == IEEE80211_BAND_5GHZ) && + (lq_sta->max_rate_idx != -1)) + lq_sta->max_rate_idx += IWL_FIRST_OFDM_RATE; + if ((lq_sta->max_rate_idx < 0) || + (lq_sta->max_rate_idx >= IWL_RATE_COUNT)) + lq_sta->max_rate_idx = -1; + } + + /* Treat uninitialized rate scaling data same as non-existing. */ + if (lq_sta && !lq_sta->drv) { + IWL_DEBUG_RATE(priv, "Rate scaling not initialized yet.\n"); + priv_sta = NULL; + } + + /* Send management frames and NO_ACK data using lowest rate. */ + if (rate_control_send_low(sta, priv_sta, txrc)) + return; + + rate_idx = lq_sta->last_txrate_idx; + + if (lq_sta->last_rate_n_flags & RATE_MCS_HT_MSK) { + rate_idx -= IWL_FIRST_OFDM_RATE; + /* 6M and 9M shared same MCS index */ + rate_idx = (rate_idx > 0) ? (rate_idx - 1) : 0; + if (iwl4965_rs_extract_rate(lq_sta->last_rate_n_flags) >= + IWL_RATE_MIMO2_6M_PLCP) + rate_idx = rate_idx + MCS_INDEX_PER_STREAM; + info->control.rates[0].flags = IEEE80211_TX_RC_MCS; + if (lq_sta->last_rate_n_flags & RATE_MCS_SGI_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_SHORT_GI; + if (lq_sta->last_rate_n_flags & RATE_MCS_DUP_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_DUP_DATA; + if (lq_sta->last_rate_n_flags & RATE_MCS_HT40_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_40_MHZ_WIDTH; + if (lq_sta->last_rate_n_flags & RATE_MCS_GF_MSK) + info->control.rates[0].flags |= + IEEE80211_TX_RC_GREEN_FIELD; + } else { + /* Check for invalid rates */ + if ((rate_idx < 0) || (rate_idx >= IWL_RATE_COUNT_LEGACY) || + ((sband->band == IEEE80211_BAND_5GHZ) && + (rate_idx < IWL_FIRST_OFDM_RATE))) + rate_idx = rate_lowest_index(sband, sta); + /* On valid 5 GHz rate, adjust index */ + else if (sband->band == IEEE80211_BAND_5GHZ) + rate_idx -= IWL_FIRST_OFDM_RATE; + info->control.rates[0].flags = 0; + } + info->control.rates[0].idx = rate_idx; + +} + +static void *iwl4965_rs_alloc_sta(void *priv_rate, struct ieee80211_sta *sta, + gfp_t gfp) +{ + struct iwl_lq_sta *lq_sta; + struct iwl_station_priv *sta_priv = + (struct iwl_station_priv *) sta->drv_priv; + struct iwl_priv *priv; + + priv = (struct iwl_priv *)priv_rate; + IWL_DEBUG_RATE(priv, "create station rate scale window\n"); + + lq_sta = &sta_priv->lq_sta; + + return lq_sta; +} + +/* + * Called after adding a new station to initialize rate scaling + */ +void +iwl4965_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, + u8 sta_id) +{ + int i, j; + struct ieee80211_hw *hw = priv->hw; + struct ieee80211_conf *conf = &priv->hw->conf; + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + struct iwl_station_priv *sta_priv; + struct iwl_lq_sta *lq_sta; + struct ieee80211_supported_band *sband; + + sta_priv = (struct iwl_station_priv *) sta->drv_priv; + lq_sta = &sta_priv->lq_sta; + sband = hw->wiphy->bands[conf->channel->band]; + + + lq_sta->lq.sta_id = sta_id; + + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl4965_rs_rate_scale_clear_window( + &lq_sta->lq_info[j].win[i]); + + lq_sta->flush_timer = 0; + lq_sta->supp_rates = sta->supp_rates[sband->band]; + for (j = 0; j < LQ_SIZE; j++) + for (i = 0; i < IWL_RATE_COUNT; i++) + iwl4965_rs_rate_scale_clear_window( + &lq_sta->lq_info[j].win[i]); + + IWL_DEBUG_RATE(priv, "LQ:" + "*** rate scale station global init for station %d ***\n", + sta_id); + /* TODO: what is a good starting rate for STA? About middle? Maybe not + * the lowest or the highest rate.. Could consider using RSSI from + * previous packets? Need to have IEEE 802.1X auth succeed immediately + * after assoc.. */ + + lq_sta->is_dup = 0; + lq_sta->max_rate_idx = -1; + lq_sta->missed_rate_counter = IWL_MISSED_RATE_MAX; + lq_sta->is_green = iwl4965_rs_use_green(sta); + lq_sta->active_legacy_rate = priv->active_rate & ~(0x1000); + lq_sta->band = priv->band; + /* + * active_siso_rate mask includes 9 MBits (bit 5), and CCK (bits 0-3), + * supp_rates[] does not; shift to convert format, force 9 MBits off. + */ + lq_sta->active_siso_rate = ht_cap->mcs.rx_mask[0] << 1; + lq_sta->active_siso_rate |= ht_cap->mcs.rx_mask[0] & 0x1; + lq_sta->active_siso_rate &= ~((u16)0x2); + lq_sta->active_siso_rate <<= IWL_FIRST_OFDM_RATE; + + /* Same here */ + lq_sta->active_mimo2_rate = ht_cap->mcs.rx_mask[1] << 1; + lq_sta->active_mimo2_rate |= ht_cap->mcs.rx_mask[1] & 0x1; + lq_sta->active_mimo2_rate &= ~((u16)0x2); + lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; + + /* These values will be overridden later */ + lq_sta->lq.general_params.single_stream_ant_msk = + iwl4965_first_antenna(priv->hw_params.valid_tx_ant); + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant & + ~iwl4965_first_antenna(priv->hw_params.valid_tx_ant); + if (!lq_sta->lq.general_params.dual_stream_ant_msk) { + lq_sta->lq.general_params.dual_stream_ant_msk = ANT_AB; + } else if (iwl4965_num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + lq_sta->lq.general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant; + } + + /* as default allow aggregation for all tids */ + lq_sta->tx_agg_tid_en = IWL_AGG_ALL_TID; + lq_sta->drv = priv; + + /* Set last_txrate_idx to lowest rate */ + lq_sta->last_txrate_idx = rate_lowest_index(sband, sta); + if (sband->band == IEEE80211_BAND_5GHZ) + lq_sta->last_txrate_idx += IWL_FIRST_OFDM_RATE; + lq_sta->is_agg = 0; + +#ifdef CONFIG_MAC80211_DEBUGFS + lq_sta->dbg_fixed_rate = 0; +#endif + + iwl4965_rs_initialize_lq(priv, conf, sta, lq_sta); +} + +static void iwl4965_rs_fill_link_cmd(struct iwl_priv *priv, + struct iwl_lq_sta *lq_sta, u32 new_rate) +{ + struct iwl_scale_tbl_info tbl_type; + int index = 0; + int rate_idx; + int repeat_rate = 0; + u8 ant_toggle_cnt = 0; + u8 use_ht_possible = 1; + u8 valid_tx_ant = 0; + struct iwl_link_quality_cmd *lq_cmd = &lq_sta->lq; + + /* Override starting rate (index 0) if needed for debug purposes */ + iwl4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Interpret new_rate (rate_n_flags) */ + iwl4965_rs_get_tbl_info_from_mcs(new_rate, lq_sta->band, + &tbl_type, &rate_idx); + + /* How many times should we repeat the initial rate? */ + if (is_legacy(tbl_type.lq_type)) { + ant_toggle_cnt = 1; + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + lq_cmd->general_params.mimo_delimiter = + is_mimo(tbl_type.lq_type) ? 1 : 0; + + /* Fill 1st table entry (index 0) */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + if (iwl4965_num_of_ant(tbl_type.ant_type) == 1) { + lq_cmd->general_params.single_stream_ant_msk = + tbl_type.ant_type; + } else if (iwl4965_num_of_ant(tbl_type.ant_type) == 2) { + lq_cmd->general_params.dual_stream_ant_msk = + tbl_type.ant_type; + } /* otherwise we don't modify the existing value */ + + index++; + repeat_rate--; + if (priv) + valid_tx_ant = priv->hw_params.valid_tx_ant; + + /* Fill rest of rate table */ + while (index < LINK_QUAL_MAX_RETRY_NUM) { + /* Repeat initial/next rate. + * For legacy IWL_NUMBER_TRY == 1, this loop will not execute. + * For HT IWL_HT_NUMBER_TRY == 3, this executes twice. */ + while (repeat_rate > 0 && (index < LINK_QUAL_MAX_RETRY_NUM)) { + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + iwl4965_rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + } + + /* Override next rate if needed for debug purposes */ + iwl4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = + cpu_to_le32(new_rate); + repeat_rate--; + index++; + } + + iwl4965_rs_get_tbl_info_from_mcs(new_rate, + lq_sta->band, &tbl_type, + &rate_idx); + + /* Indicate to uCode which entries might be MIMO. + * If initial rate was MIMO, this will finally end up + * as (IWL_HT_NUMBER_TRY * 2), after 2nd pass, otherwise 0. */ + if (is_mimo(tbl_type.lq_type)) + lq_cmd->general_params.mimo_delimiter = index; + + /* Get next rate */ + new_rate = iwl4965_rs_get_lower_rate(lq_sta, + &tbl_type, rate_idx, + use_ht_possible); + + /* How many times should we repeat the next rate? */ + if (is_legacy(tbl_type.lq_type)) { + if (ant_toggle_cnt < NUM_TRY_BEFORE_ANT_TOGGLE) + ant_toggle_cnt++; + else if (priv && + iwl4965_rs_toggle_antenna(valid_tx_ant, + &new_rate, &tbl_type)) + ant_toggle_cnt = 1; + + repeat_rate = IWL_NUMBER_TRY; + } else { + repeat_rate = IWL_HT_NUMBER_TRY; + } + + /* Don't allow HT rates after next pass. + * iwl4965_rs_get_lower_rate() will change type to LQ_A or LQ_G. */ + use_ht_possible = 0; + + /* Override next rate if needed for debug purposes */ + iwl4965_rs_dbgfs_set_mcs(lq_sta, &new_rate, index); + + /* Fill next table entry */ + lq_cmd->rs_table[index].rate_n_flags = cpu_to_le32(new_rate); + + index++; + repeat_rate--; + } + + lq_cmd->agg_params.agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; + lq_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + + lq_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); +} + +static void +*iwl4965_rs_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) +{ + return hw->priv; +} +/* rate scale requires free function to be implemented */ +static void iwl4965_rs_free(void *priv_rate) +{ + return; +} + +static void iwl4965_rs_free_sta(void *priv_r, struct ieee80211_sta *sta, + void *priv_sta) +{ + struct iwl_priv *priv __maybe_unused = priv_r; + + IWL_DEBUG_RATE(priv, "enter\n"); + IWL_DEBUG_RATE(priv, "leave\n"); +} + + +#ifdef CONFIG_MAC80211_DEBUGFS +static int iwl4965_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} +static void iwl4965_rs_dbgfs_set_mcs(struct iwl_lq_sta *lq_sta, + u32 *rate_n_flags, int index) +{ + struct iwl_priv *priv; + u8 valid_tx_ant; + u8 ant_sel_tx; + + priv = lq_sta->drv; + valid_tx_ant = priv->hw_params.valid_tx_ant; + if (lq_sta->dbg_fixed_rate) { + ant_sel_tx = + ((lq_sta->dbg_fixed_rate & RATE_MCS_ANT_ABC_MSK) + >> RATE_MCS_ANT_POS); + if ((valid_tx_ant & ant_sel_tx) == ant_sel_tx) { + *rate_n_flags = lq_sta->dbg_fixed_rate; + IWL_DEBUG_RATE(priv, "Fixed rate ON\n"); + } else { + lq_sta->dbg_fixed_rate = 0; + IWL_ERR(priv, + "Invalid antenna selection 0x%X, Valid is 0x%X\n", + ant_sel_tx, valid_tx_ant); + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } + } else { + IWL_DEBUG_RATE(priv, "Fixed rate OFF\n"); + } +} + +static ssize_t iwl4965_rs_sta_dbgfs_scale_table_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *ppos) +{ + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + char buf[64]; + int buf_size; + u32 parsed_rate; + struct iwl_station_priv *sta_priv = + container_of(lq_sta, struct iwl_station_priv, lq_sta); + struct iwl_rxon_context *ctx = sta_priv->common.ctx; + + priv = lq_sta->drv; + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x", &parsed_rate) == 1) + lq_sta->dbg_fixed_rate = parsed_rate; + else + lq_sta->dbg_fixed_rate = 0; + + lq_sta->active_legacy_rate = 0x0FFF; /* 1 - 54 MBits, includes CCK */ + lq_sta->active_siso_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + lq_sta->active_mimo2_rate = 0x1FD0; /* 6 - 60 MBits, no 9, no CCK */ + + IWL_DEBUG_RATE(priv, "sta_id %d rate 0x%X\n", + lq_sta->lq.sta_id, lq_sta->dbg_fixed_rate); + + if (lq_sta->dbg_fixed_rate) { + iwl4965_rs_fill_link_cmd(NULL, lq_sta, lq_sta->dbg_fixed_rate); + iwl_legacy_send_lq_cmd(lq_sta->drv, ctx, &lq_sta->lq, CMD_ASYNC, + false); + } + + return count; +} + +static ssize_t iwl4965_rs_sta_dbgfs_scale_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i = 0; + int index = 0; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + struct iwl_scale_tbl_info *tbl = &(lq_sta->lq_info[lq_sta->active_tbl]); + + priv = lq_sta->drv; + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + desc += sprintf(buff+desc, "sta_id %d\n", lq_sta->lq.sta_id); + desc += sprintf(buff+desc, "failed=%d success=%d rate=0%X\n", + lq_sta->total_failed, lq_sta->total_success, + lq_sta->active_legacy_rate); + desc += sprintf(buff+desc, "fixed rate 0x%X\n", + lq_sta->dbg_fixed_rate); + desc += sprintf(buff+desc, "valid_tx_ant %s%s%s\n", + (priv->hw_params.valid_tx_ant & ANT_A) ? "ANT_A," : "", + (priv->hw_params.valid_tx_ant & ANT_B) ? "ANT_B," : "", + (priv->hw_params.valid_tx_ant & ANT_C) ? "ANT_C" : ""); + desc += sprintf(buff+desc, "lq type %s\n", + (is_legacy(tbl->lq_type)) ? "legacy" : "HT"); + if (is_Ht(tbl->lq_type)) { + desc += sprintf(buff+desc, " %s", + (is_siso(tbl->lq_type)) ? "SISO" : "MIMO2"); + desc += sprintf(buff+desc, " %s", + (tbl->is_ht40) ? "40MHz" : "20MHz"); + desc += sprintf(buff+desc, " %s %s %s\n", + (tbl->is_SGI) ? "SGI" : "", + (lq_sta->is_green) ? "GF enabled" : "", + (lq_sta->is_agg) ? "AGG on" : ""); + } + desc += sprintf(buff+desc, "last tx rate=0x%X\n", + lq_sta->last_rate_n_flags); + desc += sprintf(buff+desc, "general:" + "flags=0x%X mimo-d=%d s-ant0x%x d-ant=0x%x\n", + lq_sta->lq.general_params.flags, + lq_sta->lq.general_params.mimo_delimiter, + lq_sta->lq.general_params.single_stream_ant_msk, + lq_sta->lq.general_params.dual_stream_ant_msk); + + desc += sprintf(buff+desc, "agg:" + "time_limit=%d dist_start_th=%d frame_cnt_limit=%d\n", + le16_to_cpu(lq_sta->lq.agg_params.agg_time_limit), + lq_sta->lq.agg_params.agg_dis_start_th, + lq_sta->lq.agg_params.agg_frame_cnt_limit); + + desc += sprintf(buff+desc, + "Start idx [0]=0x%x [1]=0x%x [2]=0x%x [3]=0x%x\n", + lq_sta->lq.general_params.start_rate_index[0], + lq_sta->lq.general_params.start_rate_index[1], + lq_sta->lq.general_params.start_rate_index[2], + lq_sta->lq.general_params.start_rate_index[3]); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + index = iwl4965_hwrate_to_plcp_idx( + le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags)); + if (is_legacy(tbl->lq_type)) { + desc += sprintf(buff+desc, " rate[%d] 0x%X %smbps\n", + i, + le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps); + } else { + desc += sprintf(buff+desc, + " rate[%d] 0x%X %smbps (%s)\n", + i, + le32_to_cpu(lq_sta->lq.rs_table[i].rate_n_flags), + iwl_rate_mcs[index].mbps, iwl_rate_mcs[index].mcs); + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_scale_table_ops = { + .write = iwl4965_rs_sta_dbgfs_scale_table_write, + .read = iwl4965_rs_sta_dbgfs_scale_table_read, + .open = iwl4965_open_file_generic, + .llseek = default_llseek, +}; +static ssize_t iwl4965_rs_sta_dbgfs_stats_table_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char *buff; + int desc = 0; + int i, j; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + + buff = kmalloc(1024, GFP_KERNEL); + if (!buff) + return -ENOMEM; + + for (i = 0; i < LQ_SIZE; i++) { + desc += sprintf(buff+desc, + "%s type=%d SGI=%d HT40=%d DUP=%d GF=%d\n" + "rate=0x%X\n", + lq_sta->active_tbl == i ? "*" : "x", + lq_sta->lq_info[i].lq_type, + lq_sta->lq_info[i].is_SGI, + lq_sta->lq_info[i].is_ht40, + lq_sta->lq_info[i].is_dup, + lq_sta->is_green, + lq_sta->lq_info[i].current_rate); + for (j = 0; j < IWL_RATE_COUNT; j++) { + desc += sprintf(buff+desc, + "counter=%d success=%d %%=%d\n", + lq_sta->lq_info[i].win[j].counter, + lq_sta->lq_info[i].win[j].success_counter, + lq_sta->lq_info[i].win[j].success_ratio); + } + } + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + kfree(buff); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_stats_table_ops = { + .read = iwl4965_rs_sta_dbgfs_stats_table_read, + .open = iwl4965_open_file_generic, + .llseek = default_llseek, +}; + +static ssize_t iwl4965_rs_sta_dbgfs_rate_scale_data_read(struct file *file, + char __user *user_buf, size_t count, loff_t *ppos) +{ + char buff[120]; + int desc = 0; + ssize_t ret; + + struct iwl_lq_sta *lq_sta = file->private_data; + struct iwl_priv *priv; + struct iwl_scale_tbl_info *tbl = &lq_sta->lq_info[lq_sta->active_tbl]; + + priv = lq_sta->drv; + + if (is_Ht(tbl->lq_type)) + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + tbl->expected_tpt[lq_sta->last_txrate_idx]); + else + desc += sprintf(buff+desc, + "Bit Rate= %d Mb/s\n", + iwlegacy_rates[lq_sta->last_txrate_idx].ieee >> 1); + + ret = simple_read_from_buffer(user_buf, count, ppos, buff, desc); + return ret; +} + +static const struct file_operations rs_sta_dbgfs_rate_scale_data_ops = { + .read = iwl4965_rs_sta_dbgfs_rate_scale_data_read, + .open = iwl4965_open_file_generic, + .llseek = default_llseek, +}; + +static void iwl4965_rs_add_debugfs(void *priv, void *priv_sta, + struct dentry *dir) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + lq_sta->rs_sta_dbgfs_scale_table_file = + debugfs_create_file("rate_scale_table", S_IRUSR | S_IWUSR, dir, + lq_sta, &rs_sta_dbgfs_scale_table_ops); + lq_sta->rs_sta_dbgfs_stats_table_file = + debugfs_create_file("rate_stats_table", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_stats_table_ops); + lq_sta->rs_sta_dbgfs_rate_scale_data_file = + debugfs_create_file("rate_scale_data", S_IRUSR, dir, + lq_sta, &rs_sta_dbgfs_rate_scale_data_ops); + lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file = + debugfs_create_u8("tx_agg_tid_enable", S_IRUSR | S_IWUSR, dir, + &lq_sta->tx_agg_tid_en); + +} + +static void iwl4965_rs_remove_debugfs(void *priv, void *priv_sta) +{ + struct iwl_lq_sta *lq_sta = priv_sta; + debugfs_remove(lq_sta->rs_sta_dbgfs_scale_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_stats_table_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_rate_scale_data_file); + debugfs_remove(lq_sta->rs_sta_dbgfs_tx_agg_tid_en_file); +} +#endif + +/* + * Initialization of rate scaling information is done by driver after + * the station is added. Since mac80211 calls this function before a + * station is added we ignore it. + */ +static void +iwl4965_rs_rate_init_stub(void *priv_r, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) +{ +} +static struct rate_control_ops rs_4965_ops = { + .module = NULL, + .name = IWL4965_RS_NAME, + .tx_status = iwl4965_rs_tx_status, + .get_rate = iwl4965_rs_get_rate, + .rate_init = iwl4965_rs_rate_init_stub, + .alloc = iwl4965_rs_alloc, + .free = iwl4965_rs_free, + .alloc_sta = iwl4965_rs_alloc_sta, + .free_sta = iwl4965_rs_free_sta, +#ifdef CONFIG_MAC80211_DEBUGFS + .add_sta_debugfs = iwl4965_rs_add_debugfs, + .remove_sta_debugfs = iwl4965_rs_remove_debugfs, +#endif +}; + +int iwl4965_rate_control_register(void) +{ + pr_err("Registering 4965 rate control operations\n"); + return ieee80211_rate_control_register(&rs_4965_ops); +} + +void iwl4965_rate_control_unregister(void) +{ + ieee80211_rate_control_unregister(&rs_4965_ops); +} diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c b/drivers/net/wireless/iwlegacy/iwl-4965-rx.c index b192ca842f0..b9fa2f6411a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rx.c +++ b/drivers/net/wireless/iwlegacy/iwl-4965-rx.c @@ -2,7 +2,7 @@ * * GPL LICENSE SUMMARY * - * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -34,14 +34,14 @@ #include "iwl-dev.h" #include "iwl-core.h" -#include "iwl-agn-calib.h" +#include "iwl-4965-calib.h" #include "iwl-sta.h" #include "iwl-io.h" #include "iwl-helpers.h" -#include "iwl-agn-hw.h" -#include "iwl-agn.h" +#include "iwl-4965-hw.h" +#include "iwl-4965.h" -void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, +void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { @@ -58,14 +58,14 @@ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, le32_to_cpu(missed_beacon->num_recvd_beacons), le32_to_cpu(missed_beacon->num_expected_beacons)); if (!test_bit(STATUS_SCANNING, &priv->status)) - iwl_init_sensitivity(priv); + iwl4965_init_sensitivity(priv); } } /* Calculate noise level, based on measurements during network silence just * before arriving beacon. This measurement can be done only if we know * exactly when to expect beacons, therefore only when we're associated. */ -static void iwl_rx_calc_noise(struct iwl_priv *priv) +static void iwl4965_rx_calc_noise(struct iwl_priv *priv) { struct statistics_rx_non_phy *rx_info; int num_active_rx = 0; @@ -73,10 +73,7 @@ static void iwl_rx_calc_noise(struct iwl_priv *priv) int bcn_silence_a, bcn_silence_b, bcn_silence_c; int last_rx_noise; - if (iwl_bt_statistics(priv)) - rx_info = &(priv->_agn.statistics_bt.rx.general.common); - else - rx_info = &(priv->_agn.statistics.rx.general); + rx_info = &(priv->_4965.statistics.rx.general); bcn_silence_a = le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; bcn_silence_b = @@ -108,13 +105,13 @@ static void iwl_rx_calc_noise(struct iwl_priv *priv) last_rx_noise); } -#ifdef CONFIG_IWLWIFI_DEBUGFS +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS /* * based on the assumption of all statistics counter are in DWORD * FIXME: This function is for debugging, do not deal with * the case of counters roll-over. */ -static void iwl_accumulative_statistics(struct iwl_priv *priv, +static void iwl4965_accumulative_statistics(struct iwl_priv *priv, __le32 *stats) { int i, size; @@ -124,27 +121,16 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, struct statistics_general_common *general, *accum_general; struct statistics_tx *tx, *accum_tx; - if (iwl_bt_statistics(priv)) { - prev_stats = (__le32 *)&priv->_agn.statistics_bt; - accum_stats = (u32 *)&priv->_agn.accum_statistics_bt; - size = sizeof(struct iwl_bt_notif_statistics); - general = &priv->_agn.statistics_bt.general.common; - accum_general = &priv->_agn.accum_statistics_bt.general.common; - tx = &priv->_agn.statistics_bt.tx; - accum_tx = &priv->_agn.accum_statistics_bt.tx; - delta = (u32 *)&priv->_agn.delta_statistics_bt; - max_delta = (u32 *)&priv->_agn.max_delta_bt; - } else { - prev_stats = (__le32 *)&priv->_agn.statistics; - accum_stats = (u32 *)&priv->_agn.accum_statistics; - size = sizeof(struct iwl_notif_statistics); - general = &priv->_agn.statistics.general.common; - accum_general = &priv->_agn.accum_statistics.general.common; - tx = &priv->_agn.statistics.tx; - accum_tx = &priv->_agn.accum_statistics.tx; - delta = (u32 *)&priv->_agn.delta_statistics; - max_delta = (u32 *)&priv->_agn.max_delta; - } + prev_stats = (__le32 *)&priv->_4965.statistics; + accum_stats = (u32 *)&priv->_4965.accum_statistics; + size = sizeof(struct iwl_notif_statistics); + general = &priv->_4965.statistics.general.common; + accum_general = &priv->_4965.accum_statistics.general.common; + tx = &priv->_4965.statistics.tx; + accum_tx = &priv->_4965.accum_statistics.tx; + delta = (u32 *)&priv->_4965.delta_statistics; + max_delta = (u32 *)&priv->_4965.max_delta; + for (i = sizeof(__le32); i < size; i += sizeof(__le32), stats++, prev_stats++, delta++, max_delta++, accum_stats++) { @@ -159,23 +145,19 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv, /* reset accumulative statistics for "no-counter" type statistics */ accum_general->temperature = general->temperature; - accum_general->temperature_m = general->temperature_m; accum_general->ttl_timestamp = general->ttl_timestamp; - accum_tx->tx_power.ant_a = tx->tx_power.ant_a; - accum_tx->tx_power.ant_b = tx->tx_power.ant_b; - accum_tx->tx_power.ant_c = tx->tx_power.ant_c; } #endif #define REG_RECALIB_PERIOD (60) /** - * iwl_good_plcp_health - checks for plcp error. + * iwl4965_good_plcp_health - checks for plcp error. * * When the plcp error is exceeding the thresholds, reset the radio * to improve the throughput. */ -bool iwl_good_plcp_health(struct iwl_priv *priv, +bool iwl4965_good_plcp_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) { bool rc = true; @@ -205,27 +187,15 @@ bool iwl_good_plcp_health(struct iwl_priv *priv, struct statistics_rx_phy *ofdm; struct statistics_rx_ht_phy *ofdm_ht; - if (iwl_bt_statistics(priv)) { - ofdm = &pkt->u.stats_bt.rx.ofdm; - ofdm_ht = &pkt->u.stats_bt.rx.ofdm_ht; - combined_plcp_delta = - (le32_to_cpu(ofdm->plcp_err) - - le32_to_cpu(priv->_agn.statistics_bt. - rx.ofdm.plcp_err)) + - (le32_to_cpu(ofdm_ht->plcp_err) - - le32_to_cpu(priv->_agn.statistics_bt. - rx.ofdm_ht.plcp_err)); - } else { - ofdm = &pkt->u.stats.rx.ofdm; - ofdm_ht = &pkt->u.stats.rx.ofdm_ht; - combined_plcp_delta = - (le32_to_cpu(ofdm->plcp_err) - - le32_to_cpu(priv->_agn.statistics. - rx.ofdm.plcp_err)) + - (le32_to_cpu(ofdm_ht->plcp_err) - - le32_to_cpu(priv->_agn.statistics. - rx.ofdm_ht.plcp_err)); - } + ofdm = &pkt->u.stats.rx.ofdm; + ofdm_ht = &pkt->u.stats.rx.ofdm_ht; + combined_plcp_delta = + (le32_to_cpu(ofdm->plcp_err) - + le32_to_cpu(priv->_4965.statistics. + rx.ofdm.plcp_err)) + + (le32_to_cpu(ofdm_ht->plcp_err) - + le32_to_cpu(priv->_4965.statistics. + rx.ofdm_ht.plcp_err)); if ((combined_plcp_delta > 0) && ((combined_plcp_delta * 100) / plcp_msec) > @@ -256,56 +226,32 @@ bool iwl_good_plcp_health(struct iwl_priv *priv, return rc; } -void iwl_rx_statistics(struct iwl_priv *priv, +void iwl4965_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { int change; struct iwl_rx_packet *pkt = rxb_addr(rxb); - if (iwl_bt_statistics(priv)) { - IWL_DEBUG_RX(priv, - "Statistics notification received (%d vs %d).\n", - (int)sizeof(struct iwl_bt_notif_statistics), - le32_to_cpu(pkt->len_n_flags) & - FH_RSCSR_FRAME_SIZE_MSK); - - change = ((priv->_agn.statistics_bt.general.common.temperature != - pkt->u.stats_bt.general.common.temperature) || - ((priv->_agn.statistics_bt.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (pkt->u.stats_bt.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK))); -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats_bt); + IWL_DEBUG_RX(priv, + "Statistics notification received (%d vs %d).\n", + (int)sizeof(struct iwl_notif_statistics), + le32_to_cpu(pkt->len_n_flags) & + FH_RSCSR_FRAME_SIZE_MSK); + + change = ((priv->_4965.statistics.general.common.temperature != + pkt->u.stats.general.common.temperature) || + ((priv->_4965.statistics.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK))); +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + iwl4965_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); #endif - } else { - IWL_DEBUG_RX(priv, - "Statistics notification received (%d vs %d).\n", - (int)sizeof(struct iwl_notif_statistics), - le32_to_cpu(pkt->len_n_flags) & - FH_RSCSR_FRAME_SIZE_MSK); + iwl_legacy_recover_from_statistics(priv, pkt); - change = ((priv->_agn.statistics.general.common.temperature != - pkt->u.stats.general.common.temperature) || - ((priv->_agn.statistics.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK) != - (pkt->u.stats.flag & - STATISTICS_REPLY_FLG_HT40_MODE_MSK))); -#ifdef CONFIG_IWLWIFI_DEBUGFS - iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); -#endif - - } - - iwl_recover_from_statistics(priv, pkt); - - if (iwl_bt_statistics(priv)) - memcpy(&priv->_agn.statistics_bt, &pkt->u.stats_bt, - sizeof(priv->_agn.statistics_bt)); - else - memcpy(&priv->_agn.statistics, &pkt->u.stats, - sizeof(priv->_agn.statistics)); + memcpy(&priv->_4965.statistics, &pkt->u.stats, + sizeof(priv->_4965.statistics)); set_bit(STATUS_STATISTICS, &priv->status); @@ -318,34 +264,28 @@ void iwl_rx_statistics(struct iwl_priv *priv, if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { - iwl_rx_calc_noise(priv); + iwl4965_rx_calc_noise(priv); queue_work(priv->workqueue, &priv->run_time_calib_work); } if (priv->cfg->ops->lib->temp_ops.temperature && change) priv->cfg->ops->lib->temp_ops.temperature(priv); } -void iwl_reply_statistics(struct iwl_priv *priv, +void iwl4965_reply_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { -#ifdef CONFIG_IWLWIFI_DEBUGFS - memset(&priv->_agn.accum_statistics, 0, +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + memset(&priv->_4965.accum_statistics, 0, sizeof(struct iwl_notif_statistics)); - memset(&priv->_agn.delta_statistics, 0, + memset(&priv->_4965.delta_statistics, 0, sizeof(struct iwl_notif_statistics)); - memset(&priv->_agn.max_delta, 0, + memset(&priv->_4965.max_delta, 0, sizeof(struct iwl_notif_statistics)); - memset(&priv->_agn.accum_statistics_bt, 0, - sizeof(struct iwl_bt_notif_statistics)); - memset(&priv->_agn.delta_statistics_bt, 0, - sizeof(struct iwl_bt_notif_statistics)); - memset(&priv->_agn.max_delta_bt, 0, - sizeof(struct iwl_bt_notif_statistics)); #endif IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); } - iwl_rx_statistics(priv, rxb); + iwl4965_rx_statistics(priv, rxb); } diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-sta.c b/drivers/net/wireless/iwlegacy/iwl-4965-sta.c new file mode 100644 index 00000000000..a262c23553d --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-sta.c @@ -0,0 +1,721 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <net/mac80211.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" +#include "iwl-4965.h" + +static struct iwl_link_quality_cmd * +iwl4965_sta_alloc_lq(struct iwl_priv *priv, u8 sta_id) +{ + int i, r; + struct iwl_link_quality_cmd *link_cmd; + u32 rate_flags = 0; + __le32 rate_n_flags; + + link_cmd = kzalloc(sizeof(struct iwl_link_quality_cmd), GFP_KERNEL); + if (!link_cmd) { + IWL_ERR(priv, "Unable to allocate memory for LQ cmd.\n"); + return NULL; + } + /* Set up the rate scaling to start at selected rate, fall back + * all the way down to 1M in IEEE order, and then spin on 1M */ + if (priv->band == IEEE80211_BAND_5GHZ) + r = IWL_RATE_6M_INDEX; + else + r = IWL_RATE_1M_INDEX; + + if (r >= IWL_FIRST_CCK_RATE && r <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_CCK_MSK; + + rate_flags |= iwl4965_first_antenna(priv->hw_params.valid_tx_ant) << + RATE_MCS_ANT_POS; + rate_n_flags = iwl4965_hw_set_rate_n_flags(iwlegacy_rates[r].plcp, + rate_flags); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + link_cmd->rs_table[i].rate_n_flags = rate_n_flags; + + link_cmd->general_params.single_stream_ant_msk = + iwl4965_first_antenna(priv->hw_params.valid_tx_ant); + + link_cmd->general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant & + ~iwl4965_first_antenna(priv->hw_params.valid_tx_ant); + if (!link_cmd->general_params.dual_stream_ant_msk) { + link_cmd->general_params.dual_stream_ant_msk = ANT_AB; + } else if (iwl4965_num_of_ant(priv->hw_params.valid_tx_ant) == 2) { + link_cmd->general_params.dual_stream_ant_msk = + priv->hw_params.valid_tx_ant; + } + + link_cmd->agg_params.agg_dis_start_th = LINK_QUAL_AGG_DISABLE_START_DEF; + link_cmd->agg_params.agg_time_limit = + cpu_to_le16(LINK_QUAL_AGG_TIME_LIMIT_DEF); + + link_cmd->sta_id = sta_id; + + return link_cmd; +} + +/* + * iwl4965_add_bssid_station - Add the special IBSS BSSID station + * + * Function sleeps. + */ +int +iwl4965_add_bssid_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r) +{ + int ret; + u8 sta_id; + struct iwl_link_quality_cmd *link_cmd; + unsigned long flags; + + if (sta_id_r) + *sta_id_r = IWL_INVALID_STATION; + + ret = iwl_legacy_add_station_common(priv, ctx, addr, 0, NULL, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM\n", addr); + return ret; + } + + if (sta_id_r) + *sta_id_r = sta_id; + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].used |= IWL_STA_LOCAL; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + /* Set up default rate scaling table in device's station table */ + link_cmd = iwl4965_sta_alloc_lq(priv, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for station %pM.\n", + addr); + return -ENOMEM; + } + + ret = iwl_legacy_send_lq_cmd(priv, ctx, link_cmd, CMD_SYNC, true); + if (ret) + IWL_ERR(priv, "Link quality command failed (%d)\n", ret); + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return 0; +} + +static int iwl4965_static_wepkey_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + bool send_if_empty) +{ + int i, not_empty = 0; + u8 buff[sizeof(struct iwl_wep_cmd) + + sizeof(struct iwl_wep_key) * WEP_KEYS_MAX]; + struct iwl_wep_cmd *wep_cmd = (struct iwl_wep_cmd *)buff; + size_t cmd_size = sizeof(struct iwl_wep_cmd); + struct iwl_host_cmd cmd = { + .id = ctx->wep_key_cmd, + .data = wep_cmd, + .flags = CMD_SYNC, + }; + + might_sleep(); + + memset(wep_cmd, 0, cmd_size + + (sizeof(struct iwl_wep_key) * WEP_KEYS_MAX)); + + for (i = 0; i < WEP_KEYS_MAX ; i++) { + wep_cmd->key[i].key_index = i; + if (ctx->wep_keys[i].key_size) { + wep_cmd->key[i].key_offset = i; + not_empty = 1; + } else { + wep_cmd->key[i].key_offset = WEP_INVALID_OFFSET; + } + + wep_cmd->key[i].key_size = ctx->wep_keys[i].key_size; + memcpy(&wep_cmd->key[i].key[3], ctx->wep_keys[i].key, + ctx->wep_keys[i].key_size); + } + + wep_cmd->global_key_type = WEP_KEY_WEP_TYPE; + wep_cmd->num_keys = WEP_KEYS_MAX; + + cmd_size += sizeof(struct iwl_wep_key) * WEP_KEYS_MAX; + + cmd.len = cmd_size; + + if (not_empty || send_if_empty) + return iwl_legacy_send_cmd(priv, &cmd); + else + return 0; +} + +int iwl4965_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + lockdep_assert_held(&priv->mutex); + + return iwl4965_static_wepkey_cmd(priv, ctx, false); +} + +int iwl4965_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_WEP(priv, "Removing default WEP key: idx=%d\n", + keyconf->keyidx); + + memset(&ctx->wep_keys[keyconf->keyidx], 0, sizeof(ctx->wep_keys[0])); + if (iwl_legacy_is_rfkill(priv)) { + IWL_DEBUG_WEP(priv, + "Not sending REPLY_WEPKEY command due to RFKILL.\n"); + /* but keys in device are clear anyway so return success */ + return 0; + } + ret = iwl4965_static_wepkey_cmd(priv, ctx, 1); + IWL_DEBUG_WEP(priv, "Remove default WEP key: idx=%d ret=%d\n", + keyconf->keyidx, ret); + + return ret; +} + +int iwl4965_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (keyconf->keylen != WEP_KEY_LEN_128 && + keyconf->keylen != WEP_KEY_LEN_64) { + IWL_DEBUG_WEP(priv, "Bad WEP key length %d\n", keyconf->keylen); + return -EINVAL; + } + + keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; + keyconf->hw_key_idx = HW_KEY_DEFAULT; + priv->stations[ctx->ap_sta_id].keyinfo.cipher = keyconf->cipher; + + ctx->wep_keys[keyconf->keyidx].key_size = keyconf->keylen; + memcpy(&ctx->wep_keys[keyconf->keyidx].key, &keyconf->key, + keyconf->keylen); + + ret = iwl4965_static_wepkey_cmd(priv, ctx, false); + IWL_DEBUG_WEP(priv, "Set default WEP key: len=%d idx=%d ret=%d\n", + keyconf->keylen, keyconf->keyidx, ret); + + return ret; +} + +static int iwl4965_set_wep_dynamic_key_info(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + unsigned long flags; + __le16 key_flags = 0; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + keyconf->flags &= ~IEEE80211_KEY_FLAG_GENERATE_IV; + + key_flags |= (STA_KEY_FLG_WEP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (keyconf->keylen == WEP_KEY_LEN_128) + key_flags |= STA_KEY_FLG_KEY_SIZE_MSK; + + if (sta_id == ctx->bcast_sta_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + spin_lock_irqsave(&priv->sta_lock, flags); + + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; + priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; + priv->stations[sta_id].keyinfo.keyidx = keyconf->keyidx; + + memcpy(priv->stations[sta_id].keyinfo.key, + keyconf->key, keyconf->keylen); + + memcpy(&priv->stations[sta_id].sta.key.key[3], + keyconf->key, keyconf->keylen); + + if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) + == STA_KEY_FLG_NO_ENC) + priv->stations[sta_id].sta.key.key_offset = + iwl_legacy_get_free_ucode_key_index(priv); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + priv->stations[sta_id].sta.key.key_flags = key_flags; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +static int iwl4965_set_ccmp_dynamic_key_info(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + unsigned long flags; + __le16 key_flags = 0; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + key_flags |= (STA_KEY_FLG_CCMP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (sta_id == ctx->bcast_sta_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; + priv->stations[sta_id].keyinfo.keylen = keyconf->keylen; + + memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, + keyconf->keylen); + + memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, + keyconf->keylen); + + if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) + == STA_KEY_FLG_NO_ENC) + priv->stations[sta_id].sta.key.key_offset = + iwl_legacy_get_free_ucode_key_index(priv); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + priv->stations[sta_id].sta.key.key_flags = key_flags; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +static int iwl4965_set_tkip_dynamic_key_info(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + unsigned long flags; + int ret = 0; + __le16 key_flags = 0; + + key_flags |= (STA_KEY_FLG_TKIP | STA_KEY_FLG_MAP_KEY_MSK); + key_flags |= cpu_to_le16(keyconf->keyidx << STA_KEY_FLG_KEYID_POS); + key_flags &= ~STA_KEY_FLG_INVALID; + + if (sta_id == ctx->bcast_sta_id) + key_flags |= STA_KEY_MULTICAST_MSK; + + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_IV; + keyconf->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + + spin_lock_irqsave(&priv->sta_lock, flags); + + priv->stations[sta_id].keyinfo.cipher = keyconf->cipher; + priv->stations[sta_id].keyinfo.keylen = 16; + + if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) + == STA_KEY_FLG_NO_ENC) + priv->stations[sta_id].sta.key.key_offset = + iwl_legacy_get_free_ucode_key_index(priv); + /* else, we are overriding an existing key => no need to allocated room + * in uCode. */ + + WARN(priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET, + "no space for a new key"); + + priv->stations[sta_id].sta.key.key_flags = key_flags; + + + /* This copy is acutally not needed: we get the key with each TX */ + memcpy(priv->stations[sta_id].keyinfo.key, keyconf->key, 16); + + memcpy(priv->stations[sta_id].sta.key.key, keyconf->key, 16); + + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return ret; +} + +void iwl4965_update_tkip_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key) +{ + u8 sta_id; + unsigned long flags; + int i; + + if (iwl_legacy_scan_cancel(priv)) { + /* cancel scan failed, just live w/ bad key and rely + briefly on SW decryption */ + return; + } + + sta_id = iwl_legacy_sta_id_or_broadcast(priv, ctx, sta); + if (sta_id == IWL_INVALID_STATION) + return; + + spin_lock_irqsave(&priv->sta_lock, flags); + + priv->stations[sta_id].sta.key.tkip_rx_tsc_byte2 = (u8) iv32; + + for (i = 0; i < 5; i++) + priv->stations[sta_id].sta.key.tkip_rx_ttak[i] = + cpu_to_le16(phase1key[i]); + + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + iwl_legacy_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); + + spin_unlock_irqrestore(&priv->sta_lock, flags); + +} + +int iwl4965_remove_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + u8 sta_id) +{ + unsigned long flags; + u16 key_flags; + u8 keyidx; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + ctx->key_mapping_keys--; + + spin_lock_irqsave(&priv->sta_lock, flags); + key_flags = le16_to_cpu(priv->stations[sta_id].sta.key.key_flags); + keyidx = (key_flags >> STA_KEY_FLG_KEYID_POS) & 0x3; + + IWL_DEBUG_WEP(priv, "Remove dynamic key: idx=%d sta=%d\n", + keyconf->keyidx, sta_id); + + if (keyconf->keyidx != keyidx) { + /* We need to remove a key with index different that the one + * in the uCode. This means that the key we need to remove has + * been replaced by another one with different index. + * Don't do anything and return ok + */ + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; + } + + if (priv->stations[sta_id].sta.key.key_offset == WEP_INVALID_OFFSET) { + IWL_WARN(priv, "Removing wrong key %d 0x%x\n", + keyconf->keyidx, key_flags); + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; + } + + if (!test_and_clear_bit(priv->stations[sta_id].sta.key.key_offset, + &priv->ucode_key_table)) + IWL_ERR(priv, "index %d not used in uCode key table.\n", + priv->stations[sta_id].sta.key.key_offset); + memset(&priv->stations[sta_id].keyinfo, 0, + sizeof(struct iwl_hw_key)); + memset(&priv->stations[sta_id].sta.key, 0, + sizeof(struct iwl4965_keyinfo)); + priv->stations[sta_id].sta.key.key_flags = + STA_KEY_FLG_NO_ENC | STA_KEY_FLG_INVALID; + priv->stations[sta_id].sta.key.key_offset = WEP_INVALID_OFFSET; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + + if (iwl_legacy_is_rfkill(priv)) { + IWL_DEBUG_WEP(priv, + "Not sending REPLY_ADD_STA command because RFKILL enabled.\n"); + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; + } + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl4965_set_dynamic_key(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, u8 sta_id) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + ctx->key_mapping_keys++; + keyconf->hw_key_idx = HW_KEY_DYNAMIC; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + ret = iwl4965_set_ccmp_dynamic_key_info(priv, ctx, + keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_TKIP: + ret = iwl4965_set_tkip_dynamic_key_info(priv, ctx, + keyconf, sta_id); + break; + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + ret = iwl4965_set_wep_dynamic_key_info(priv, ctx, + keyconf, sta_id); + break; + default: + IWL_ERR(priv, + "Unknown alg: %s cipher = %x\n", __func__, + keyconf->cipher); + ret = -EINVAL; + } + + IWL_DEBUG_WEP(priv, + "Set dynamic key: cipher=%x len=%d idx=%d sta=%d ret=%d\n", + keyconf->cipher, keyconf->keylen, keyconf->keyidx, + sta_id, ret); + + return ret; +} + +/** + * iwl4965_alloc_bcast_station - add broadcast station into driver's station table. + * + * This adds the broadcast station into the driver's station table + * and marks it driver active, so that it will be restored to the + * device at the next best time. + */ +int iwl4965_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_link_quality_cmd *link_cmd; + unsigned long flags; + u8 sta_id; + + spin_lock_irqsave(&priv->sta_lock, flags); + sta_id = iwl_legacy_prep_station(priv, ctx, iwlegacy_bcast_addr, + false, NULL); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare broadcast station\n"); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return -EINVAL; + } + + priv->stations[sta_id].used |= IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used |= IWL_STA_BCAST; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + link_cmd = iwl4965_sta_alloc_lq(priv, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return 0; +} + +/** + * iwl4965_update_bcast_station - update broadcast station's LQ command + * + * Only used by iwl4965. Placed here to have all bcast station management + * code together. + */ +static int iwl4965_update_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + unsigned long flags; + struct iwl_link_quality_cmd *link_cmd; + u8 sta_id = ctx->bcast_sta_id; + + link_cmd = iwl4965_sta_alloc_lq(priv, sta_id); + if (!link_cmd) { + IWL_ERR(priv, + "Unable to initialize rate scaling for bcast station.\n"); + return -ENOMEM; + } + + spin_lock_irqsave(&priv->sta_lock, flags); + if (priv->stations[sta_id].lq) + kfree(priv->stations[sta_id].lq); + else + IWL_DEBUG_INFO(priv, + "Bcast station rate scaling has not been initialized yet.\n"); + priv->stations[sta_id].lq = link_cmd; + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return 0; +} + +int iwl4965_update_bcast_stations(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int ret = 0; + + for_each_context(priv, ctx) { + ret = iwl4965_update_bcast_station(priv, ctx); + if (ret) + break; + } + + return ret; +} + +/** + * iwl4965_sta_tx_modify_enable_tid - Enable Tx for this TID in station table + */ +int iwl4965_sta_tx_modify_enable_tid(struct iwl_priv *priv, int sta_id, int tid) +{ + unsigned long flags; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + /* Remove "disable" flag, to enable Tx for this TID */ + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_TID_DISABLE_TX; + priv->stations[sta_id].sta.tid_disable_tx &= cpu_to_le16(~(1 << tid)); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl4965_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn) +{ + unsigned long flags; + int sta_id; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_legacy_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) + return -ENXIO; + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_ADDBA_TID_MSK; + priv->stations[sta_id].sta.add_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.add_immediate_ba_ssn = cpu_to_le16(ssn); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +int iwl4965_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid) +{ + unsigned long flags; + int sta_id; + struct iwl_legacy_addsta_cmd sta_cmd; + + lockdep_assert_held(&priv->mutex); + + sta_id = iwl_legacy_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].sta.station_flags_msk = 0; + priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_DELBA_TID_MSK; + priv->stations[sta_id].sta.remove_immediate_ba_tid = (u8)tid; + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); +} + +void +iwl4965_sta_modify_sleep_tx_count(struct iwl_priv *priv, int sta_id, int cnt) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->sta_lock, flags); + priv->stations[sta_id].sta.station_flags |= STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.station_flags_msk = STA_FLG_PWR_SAVE_MSK; + priv->stations[sta_id].sta.sta.modify_mask = + STA_MODIFY_SLEEP_TX_COUNT_MSK; + priv->stations[sta_id].sta.sleep_tx_count = cpu_to_le16(cnt); + priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; + iwl_legacy_send_add_sta(priv, + &priv->stations[sta_id].sta, CMD_ASYNC); + spin_unlock_irqrestore(&priv->sta_lock, flags); + +} diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-tx.c b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c new file mode 100644 index 00000000000..5c40502f869 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-tx.c @@ -0,0 +1,1369 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +#include "iwl-4965-hw.h" +#include "iwl-4965.h" + +/* + * mac80211 queues, ACs, hardware queues, FIFOs. + * + * Cf. http://wireless.kernel.org/en/developers/Documentation/mac80211/queues + * + * Mac80211 uses the following numbers, which we get as from it + * by way of skb_get_queue_mapping(skb): + * + * VO 0 + * VI 1 + * BE 2 + * BK 3 + * + * + * Regular (not A-MPDU) frames are put into hardware queues corresponding + * to the FIFOs, see comments in iwl-prph.h. Aggregated frames get their + * own queue per aggregation session (RA/TID combination), such queues are + * set up to map into FIFOs too, for which we need an AC->FIFO mapping. In + * order to map frames to the right queue, we also need an AC->hw queue + * mapping. This is implemented here. + * + * Due to the way hw queues are set up (by the hw specific modules like + * iwl-4965.c), the AC->hw queue mapping is the identity + * mapping. + */ + +static const u8 tid_to_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO +}; + +static inline int iwl4965_get_ac_from_tid(u16 tid) +{ + if (likely(tid < ARRAY_SIZE(tid_to_ac))) + return tid_to_ac[tid]; + + /* no support for TIDs 8-15 yet */ + return -EINVAL; +} + +static inline int +iwl4965_get_fifo_from_tid(struct iwl_rxon_context *ctx, u16 tid) +{ + if (likely(tid < ARRAY_SIZE(tid_to_ac))) + return ctx->ac_to_fifo[tid_to_ac[tid]]; + + /* no support for TIDs 8-15 yet */ + return -EINVAL; +} + +/* + * handle build REPLY_TX command notification. + */ +static void iwl4965_tx_cmd_build_basic(struct iwl_priv *priv, + struct sk_buff *skb, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + struct ieee80211_hdr *hdr, + u8 std_id) +{ + __le16 fc = hdr->frame_control; + __le32 tx_flags = tx_cmd->tx_flags; + + tx_cmd->stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + if (!(info->flags & IEEE80211_TX_CTL_NO_ACK)) { + tx_flags |= TX_CMD_FLG_ACK_MSK; + if (ieee80211_is_mgmt(fc)) + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + if (ieee80211_is_probe_resp(fc) && + !(le16_to_cpu(hdr->seq_ctrl) & 0xf)) + tx_flags |= TX_CMD_FLG_TSF_MSK; + } else { + tx_flags &= (~TX_CMD_FLG_ACK_MSK); + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + if (ieee80211_is_back_req(fc)) + tx_flags |= TX_CMD_FLG_ACK_MSK | TX_CMD_FLG_IMM_BA_RSP_MASK; + + tx_cmd->sta_id = std_id; + if (ieee80211_has_morefrags(fc)) + tx_flags |= TX_CMD_FLG_MORE_FRAG_MSK; + + if (ieee80211_is_data_qos(fc)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + tx_cmd->tid_tspec = qc[0] & 0xf; + tx_flags &= ~TX_CMD_FLG_SEQ_CTL_MSK; + } else { + tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; + } + + iwl_legacy_tx_cmd_protection(priv, info, fc, &tx_flags); + + tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); + if (ieee80211_is_mgmt(fc)) { + if (ieee80211_is_assoc_req(fc) || ieee80211_is_reassoc_req(fc)) + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(3); + else + tx_cmd->timeout.pm_frame_timeout = cpu_to_le16(2); + } else { + tx_cmd->timeout.pm_frame_timeout = 0; + } + + tx_cmd->driver_txop = 0; + tx_cmd->tx_flags = tx_flags; + tx_cmd->next_frame_len = 0; +} + +#define RTS_DFAULT_RETRY_LIMIT 60 + +static void iwl4965_tx_cmd_build_rate(struct iwl_priv *priv, + struct iwl_tx_cmd *tx_cmd, + struct ieee80211_tx_info *info, + __le16 fc) +{ + u32 rate_flags; + int rate_idx; + u8 rts_retry_limit; + u8 data_retry_limit; + u8 rate_plcp; + + /* Set retry limit on DATA packets and Probe Responses*/ + if (ieee80211_is_probe_resp(fc)) + data_retry_limit = 3; + else + data_retry_limit = IWL4965_DEFAULT_TX_RETRY; + tx_cmd->data_retry_limit = data_retry_limit; + + /* Set retry limit on RTS packets */ + rts_retry_limit = RTS_DFAULT_RETRY_LIMIT; + if (data_retry_limit < rts_retry_limit) + rts_retry_limit = data_retry_limit; + tx_cmd->rts_retry_limit = rts_retry_limit; + + /* DATA packets will use the uCode station table for rate/antenna + * selection */ + if (ieee80211_is_data(fc)) { + tx_cmd->initial_rate_index = 0; + tx_cmd->tx_flags |= TX_CMD_FLG_STA_RATE_MSK; + return; + } + + /** + * If the current TX rate stored in mac80211 has the MCS bit set, it's + * not really a TX rate. Thus, we use the lowest supported rate for + * this band. Also use the lowest supported rate if the stored rate + * index is invalid. + */ + rate_idx = info->control.rates[0].idx; + if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS || + (rate_idx < 0) || (rate_idx > IWL_RATE_COUNT_LEGACY)) + rate_idx = rate_lowest_index(&priv->bands[info->band], + info->control.sta); + /* For 5 GHZ band, remap mac80211 rate indices into driver indices */ + if (info->band == IEEE80211_BAND_5GHZ) + rate_idx += IWL_FIRST_OFDM_RATE; + /* Get PLCP rate for tx_cmd->rate_n_flags */ + rate_plcp = iwlegacy_rates[rate_idx].plcp; + /* Zero out flags for this packet */ + rate_flags = 0; + + /* Set CCK flag as needed */ + if ((rate_idx >= IWL_FIRST_CCK_RATE) && (rate_idx <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + + /* Set up antennas */ + priv->mgmt_tx_ant = iwl4965_toggle_tx_ant(priv, priv->mgmt_tx_ant, + priv->hw_params.valid_tx_ant); + + rate_flags |= iwl4965_ant_idx_to_flags(priv->mgmt_tx_ant); + + /* Set the rate in the TX cmd */ + tx_cmd->rate_n_flags = iwl4965_hw_set_rate_n_flags(rate_plcp, rate_flags); +} + +static void iwl4965_tx_cmd_build_hwcrypto(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + struct iwl_tx_cmd *tx_cmd, + struct sk_buff *skb_frag, + int sta_id) +{ + struct ieee80211_key_conf *keyconf = info->control.hw_key; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + tx_cmd->sec_ctl = TX_CMD_SEC_CCM; + memcpy(tx_cmd->key, keyconf->key, keyconf->keylen); + if (info->flags & IEEE80211_TX_CTL_AMPDU) + tx_cmd->tx_flags |= TX_CMD_FLG_AGG_CCMP_MSK; + IWL_DEBUG_TX(priv, "tx_cmd with AES hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_TKIP: + tx_cmd->sec_ctl = TX_CMD_SEC_TKIP; + ieee80211_get_tkip_key(keyconf, skb_frag, + IEEE80211_TKIP_P2_KEY, tx_cmd->key); + IWL_DEBUG_TX(priv, "tx_cmd with tkip hwcrypto\n"); + break; + + case WLAN_CIPHER_SUITE_WEP104: + tx_cmd->sec_ctl |= TX_CMD_SEC_KEY128; + /* fall through */ + case WLAN_CIPHER_SUITE_WEP40: + tx_cmd->sec_ctl |= (TX_CMD_SEC_WEP | + (keyconf->keyidx & TX_CMD_SEC_MSK) << TX_CMD_SEC_SHIFT); + + memcpy(&tx_cmd->key[3], keyconf->key, keyconf->keylen); + + IWL_DEBUG_TX(priv, "Configuring packet for WEP encryption " + "with key %d\n", keyconf->keyidx); + break; + + default: + IWL_ERR(priv, "Unknown encode cipher %x\n", keyconf->cipher); + break; + } +} + +/* + * start REPLY_TX command process + */ +int iwl4965_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = info->control.sta; + struct iwl_station_priv *sta_priv = NULL; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; + struct iwl_tx_cmd *tx_cmd; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + int txq_id; + dma_addr_t phys_addr; + dma_addr_t txcmd_phys; + dma_addr_t scratch_phys; + u16 len, firstlen, secondlen; + u16 seq_number = 0; + __le16 fc; + u8 hdr_len; + u8 sta_id; + u8 wait_write_ptr = 0; + u8 tid = 0; + u8 *qc = NULL; + unsigned long flags; + bool is_agg = false; + + if (info->control.vif) + ctx = iwl_legacy_rxon_ctx_from_vif(info->control.vif); + + spin_lock_irqsave(&priv->lock, flags); + if (iwl_legacy_is_rfkill(priv)) { + IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); + goto drop_unlock; + } + + fc = hdr->frame_control; + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (ieee80211_is_auth(fc)) + IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); + else if (ieee80211_is_assoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending ASSOC frame\n"); + else if (ieee80211_is_reassoc_req(fc)) + IWL_DEBUG_TX(priv, "Sending REASSOC frame\n"); +#endif + + hdr_len = ieee80211_hdrlen(fc); + + /* Find index into station table for destination station */ + sta_id = iwl_legacy_sta_id_or_broadcast(priv, ctx, info->control.sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_DEBUG_DROP(priv, "Dropping - INVALID STATION: %pM\n", + hdr->addr1); + goto drop_unlock; + } + + IWL_DEBUG_TX(priv, "station Id %d\n", sta_id); + + if (sta) + sta_priv = (void *)sta->drv_priv; + + if (sta_priv && sta_priv->asleep && + (info->flags & IEEE80211_TX_CTL_PSPOLL_RESPONSE)) { + /* + * This sends an asynchronous command to the device, + * but we can rely on it being processed before the + * next frame is processed -- and the next frame to + * this station is the one that will consume this + * counter. + * For now set the counter to just 1 since we do not + * support uAPSD yet. + */ + iwl4965_sta_modify_sleep_tx_count(priv, sta_id, 1); + } + + /* + * Send this frame after DTIM -- there's a special queue + * reserved for this for contexts that support AP mode. + */ + if (info->flags & IEEE80211_TX_CTL_SEND_AFTER_DTIM) { + txq_id = ctx->mcast_queue; + /* + * The microcode will clear the more data + * bit in the last frame it transmits. + */ + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_MOREDATA); + } else + txq_id = ctx->ac_to_queue[skb_get_queue_mapping(skb)]; + + /* irqs already disabled/saved above when locking priv->lock */ + spin_lock(&priv->sta_lock); + + if (ieee80211_is_data_qos(fc)) { + qc = ieee80211_get_qos_ctl(hdr); + tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; + if (WARN_ON_ONCE(tid >= MAX_TID_COUNT)) { + spin_unlock(&priv->sta_lock); + goto drop_unlock; + } + seq_number = priv->stations[sta_id].tid[tid].seq_number; + seq_number &= IEEE80211_SCTL_SEQ; + hdr->seq_ctrl = hdr->seq_ctrl & + cpu_to_le16(IEEE80211_SCTL_FRAG); + hdr->seq_ctrl |= cpu_to_le16(seq_number); + seq_number += 0x10; + /* aggregation is on for this <sta,tid> */ + if (info->flags & IEEE80211_TX_CTL_AMPDU && + priv->stations[sta_id].tid[tid].agg.state == IWL_AGG_ON) { + txq_id = priv->stations[sta_id].tid[tid].agg.txq_id; + is_agg = true; + } + } + + txq = &priv->txq[txq_id]; + q = &txq->q; + + if (unlikely(iwl_legacy_queue_space(q) < q->high_mark)) { + spin_unlock(&priv->sta_lock); + goto drop_unlock; + } + + if (ieee80211_is_data_qos(fc)) { + priv->stations[sta_id].tid[tid].tfds_in_queue++; + if (!ieee80211_has_morefrags(fc)) + priv->stations[sta_id].tid[tid].seq_number = seq_number; + } + + spin_unlock(&priv->sta_lock); + + /* Set up driver data for this TFD */ + memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); + txq->txb[q->write_ptr].skb = skb; + txq->txb[q->write_ptr].ctx = ctx; + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_cmd = txq->cmd[q->write_ptr]; + out_meta = &txq->meta[q->write_ptr]; + tx_cmd = &out_cmd->cmd.tx; + memset(&out_cmd->hdr, 0, sizeof(out_cmd->hdr)); + memset(tx_cmd, 0, sizeof(struct iwl_tx_cmd)); + + /* + * Set up the Tx-command (not MAC!) header. + * Store the chosen Tx queue and TFD index within the sequence field; + * after Tx, uCode's Tx response will return this value so driver can + * locate the frame within the tx queue and do post-tx processing. + */ + out_cmd->hdr.cmd = REPLY_TX; + out_cmd->hdr.sequence = cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, hdr_len); + + + /* Total # bytes to be transmitted */ + len = (u16)skb->len; + tx_cmd->len = cpu_to_le16(len); + + if (info->control.hw_key) + iwl4965_tx_cmd_build_hwcrypto(priv, info, tx_cmd, skb, sta_id); + + /* TODO need this for burst mode later on */ + iwl4965_tx_cmd_build_basic(priv, skb, tx_cmd, info, hdr, sta_id); + iwl_legacy_dbg_log_tx_data_frame(priv, len, hdr); + + iwl4965_tx_cmd_build_rate(priv, tx_cmd, info, fc); + + iwl_legacy_update_stats(priv, true, fc, len); + /* + * Use the first empty entry in this queue's command buffer array + * to contain the Tx command and MAC header concatenated together + * (payload data will be in another buffer). + * Size of this varies, due to varying MAC header length. + * If end is not dword aligned, we'll have 2 extra bytes at the end + * of the MAC header (device reads on dword boundaries). + * We'll tell device about this padding later. + */ + len = sizeof(struct iwl_tx_cmd) + + sizeof(struct iwl_cmd_header) + hdr_len; + firstlen = (len + 3) & ~3; + + /* Tell NIC about any 2-byte padding after MAC header */ + if (firstlen != len) + tx_cmd->tx_flags |= TX_CMD_FLG_MH_PAD_MSK; + + /* Physical address of this Tx command's header (not MAC header!), + * within command buffer array. */ + txcmd_phys = pci_map_single(priv->pci_dev, + &out_cmd->hdr, firstlen, + PCI_DMA_BIDIRECTIONAL); + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + /* Add buffer containing Tx command and MAC(!) header to TFD's + * first entry */ + priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, + txcmd_phys, firstlen, 1, 0); + + if (!ieee80211_has_morefrags(hdr->frame_control)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; + } + + /* Set up TFD's 2nd entry to point directly to remainder of skb, + * if any (802.11 null frames have no payload). */ + secondlen = skb->len - hdr_len; + if (secondlen > 0) { + phys_addr = pci_map_single(priv->pci_dev, skb->data + hdr_len, + secondlen, PCI_DMA_TODEVICE); + priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, + phys_addr, secondlen, + 0, 0); + } + + scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); + + /* take back ownership of DMA buffer to enable update */ + pci_dma_sync_single_for_cpu(priv->pci_dev, txcmd_phys, + firstlen, PCI_DMA_BIDIRECTIONAL); + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_legacy_get_dma_hi_addr(scratch_phys); + + IWL_DEBUG_TX(priv, "sequence nr = 0X%x\n", + le16_to_cpu(out_cmd->hdr.sequence)); + IWL_DEBUG_TX(priv, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); + iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd, sizeof(*tx_cmd)); + iwl_print_hex_dump(priv, IWL_DL_TX, (u8 *)tx_cmd->hdr, hdr_len); + + /* Set up entry for this TFD in Tx byte-count array */ + if (info->flags & IEEE80211_TX_CTL_AMPDU) + priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, + le16_to_cpu(tx_cmd->len)); + + pci_dma_sync_single_for_device(priv->pci_dev, txcmd_phys, + firstlen, PCI_DMA_BIDIRECTIONAL); + + trace_iwlwifi_legacy_dev_tx(priv, + &((struct iwl_tfd *)txq->tfds)[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &out_cmd->hdr, firstlen, + skb->data + hdr_len, secondlen); + + /* Tell device the write index *just past* this latest filled TFD */ + q->write_ptr = iwl_legacy_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_legacy_txq_update_write_ptr(priv, txq); + spin_unlock_irqrestore(&priv->lock, flags); + + /* + * At this point the frame is "transmitted" successfully + * and we will get a TX status notification eventually, + * regardless of the value of ret. "ret" only indicates + * whether or not we should update the write pointer. + */ + + /* + * Avoid atomic ops if it isn't an associated client. + * Also, if this is a packet for aggregation, don't + * increase the counter because the ucode will stop + * aggregation queues when their respective station + * goes to sleep. + */ + if (sta_priv && sta_priv->client && !is_agg) + atomic_inc(&sta_priv->pending_frames); + + if ((iwl_legacy_queue_space(q) < q->high_mark) && + priv->mac80211_registered) { + if (wait_write_ptr) { + spin_lock_irqsave(&priv->lock, flags); + txq->need_update = 1; + iwl_legacy_txq_update_write_ptr(priv, txq); + spin_unlock_irqrestore(&priv->lock, flags); + } else { + iwl_legacy_stop_queue(priv, txq); + } + } + + return 0; + +drop_unlock: + spin_unlock_irqrestore(&priv->lock, flags); + return -1; +} + +static inline int iwl4965_alloc_dma_ptr(struct iwl_priv *priv, + struct iwl_dma_ptr *ptr, size_t size) +{ + ptr->addr = dma_alloc_coherent(&priv->pci_dev->dev, size, &ptr->dma, + GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static inline void iwl4965_free_dma_ptr(struct iwl_priv *priv, + struct iwl_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(&priv->pci_dev->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +/** + * iwl4965_hw_txq_ctx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +void iwl4965_hw_txq_ctx_free(struct iwl_priv *priv) +{ + int txq_id; + + /* Tx queues */ + if (priv->txq) { + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) + if (txq_id == priv->cmd_queue) + iwl_legacy_cmd_queue_free(priv); + else + iwl_legacy_tx_queue_free(priv, txq_id); + } + iwl4965_free_dma_ptr(priv, &priv->kw); + + iwl4965_free_dma_ptr(priv, &priv->scd_bc_tbls); + + /* free tx queue structure */ + iwl_legacy_txq_mem(priv); +} + +/** + * iwl4965_txq_ctx_alloc - allocate TX queue context + * Allocate all Tx DMA structures and initialize them + * + * @param priv + * @return error code + */ +int iwl4965_txq_ctx_alloc(struct iwl_priv *priv) +{ + int ret; + int txq_id, slots_num; + unsigned long flags; + + /* Free all tx/cmd queues and keep-warm buffer */ + iwl4965_hw_txq_ctx_free(priv); + + ret = iwl4965_alloc_dma_ptr(priv, &priv->scd_bc_tbls, + priv->hw_params.scd_bc_tbls_size); + if (ret) { + IWL_ERR(priv, "Scheduler BC Table allocation failed\n"); + goto error_bc_tbls; + } + /* Alloc keep-warm buffer */ + ret = iwl4965_alloc_dma_ptr(priv, &priv->kw, IWL_KW_SIZE); + if (ret) { + IWL_ERR(priv, "Keep Warm allocation failed\n"); + goto error_kw; + } + + /* allocate tx queue structure */ + ret = iwl_legacy_alloc_txq_mem(priv); + if (ret) + goto error; + + spin_lock_irqsave(&priv->lock, flags); + + /* Turn off all Tx DMA fifos */ + iwl4965_txq_set_sched(priv, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_legacy_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4); + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { + slots_num = (txq_id == priv->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_legacy_tx_queue_init(priv, + &priv->txq[txq_id], slots_num, + txq_id); + if (ret) { + IWL_ERR(priv, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return ret; + + error: + iwl4965_hw_txq_ctx_free(priv); + iwl4965_free_dma_ptr(priv, &priv->kw); + error_kw: + iwl4965_free_dma_ptr(priv, &priv->scd_bc_tbls); + error_bc_tbls: + return ret; +} + +void iwl4965_txq_ctx_reset(struct iwl_priv *priv) +{ + int txq_id, slots_num; + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + + /* Turn off all Tx DMA fifos */ + iwl4965_txq_set_sched(priv, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_legacy_write_direct32(priv, FH_KW_MEM_ADDR_REG, priv->kw.dma >> 4); + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4) */ + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) { + slots_num = txq_id == priv->cmd_queue ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + iwl_legacy_tx_queue_reset(priv, &priv->txq[txq_id], + slots_num, txq_id); + } +} + +/** + * iwl4965_txq_ctx_stop - Stop all Tx DMA channels + */ +void iwl4965_txq_ctx_stop(struct iwl_priv *priv) +{ + int ch, txq_id; + unsigned long flags; + + /* Turn off all Tx DMA fifos */ + spin_lock_irqsave(&priv->lock, flags); + + iwl4965_txq_set_sched(priv, 0); + + /* Stop each Tx DMA channel, and wait for it to be idle */ + for (ch = 0; ch < priv->hw_params.dma_chnl_num; ch++) { + iwl_legacy_write_direct32(priv, + FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + if (iwl_poll_direct_bit(priv, FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), + 1000)) + IWL_ERR(priv, "Failing on timeout while stopping" + " DMA channel %d [0x%08x]", ch, + iwl_legacy_read_direct32(priv, + FH_TSSR_TX_STATUS_REG)); + } + spin_unlock_irqrestore(&priv->lock, flags); + + if (!priv->txq) + return; + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) + if (txq_id == priv->cmd_queue) + iwl_legacy_cmd_queue_unmap(priv); + else + iwl_legacy_tx_queue_unmap(priv, txq_id); +} + +/* + * Find first available (lowest unused) Tx Queue, mark it "active". + * Called only when finding queue for aggregation. + * Should never return anything < 7, because they should already + * be in use as EDCA AC (0-3), Command (4), reserved (5, 6) + */ +static int iwl4965_txq_ctx_activate_free(struct iwl_priv *priv) +{ + int txq_id; + + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) + if (!test_and_set_bit(txq_id, &priv->txq_ctx_active_msk)) + return txq_id; + return -1; +} + +/** + * iwl4965_tx_queue_stop_scheduler - Stop queue, but keep configuration + */ +static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv, + u16 txq_id) +{ + /* Simply stop the queue, but don't change any configuration; + * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ + iwl_legacy_write_prph(priv, + IWL49_SCD_QUEUE_STATUS_BITS(txq_id), + (0 << IWL49_SCD_QUEUE_STTS_REG_POS_ACTIVE)| + (1 << IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); +} + +/** + * iwl4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue + */ +static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, + u16 txq_id) +{ + u32 tbl_dw_addr; + u32 tbl_dw; + u16 scd_q2ratid; + + scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; + + tbl_dw_addr = priv->scd_base_addr + + IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); + + tbl_dw = iwl_legacy_read_targ_mem(priv, tbl_dw_addr); + + if (txq_id & 0x1) + tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); + else + tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); + + iwl_legacy_write_targ_mem(priv, tbl_dw_addr, tbl_dw); + + return 0; +} + +/** + * iwl4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue + * + * NOTE: txq_id must be greater than IWL49_FIRST_AMPDU_QUEUE, + * i.e. it must be one of the higher queues used for aggregation + */ +static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id, + int tx_fifo, int sta_id, int tid, u16 ssn_idx) +{ + unsigned long flags; + u16 ra_tid; + int ret; + + if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || + (IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { + IWL_WARN(priv, + "queue number out of range: %d, must be %d to %d\n", + txq_id, IWL49_FIRST_AMPDU_QUEUE, + IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->base_params->num_of_ampdu_queues - 1); + return -EINVAL; + } + + ra_tid = BUILD_RAxTID(sta_id, tid); + + /* Modify device's station table to Tx this TID */ + ret = iwl4965_sta_tx_modify_enable_tid(priv, sta_id, tid); + if (ret) + return ret; + + spin_lock_irqsave(&priv->lock, flags); + + /* Stop this Tx queue before configuring it */ + iwl4965_tx_queue_stop_scheduler(priv, txq_id); + + /* Map receiver-address / traffic-ID to this queue */ + iwl4965_tx_queue_set_q2ratid(priv, ra_tid, txq_id); + + /* Set this queue as a chain-building queue */ + iwl_legacy_set_bits_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); + + /* Place first TFD at index corresponding to start sequence number. + * Assumes that ssn_idx is valid (!= 0xFFF) */ + priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); + + /* Set up Tx window size and frame limit for this queue */ + iwl_legacy_write_targ_mem(priv, + priv->scd_base_addr + IWL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), + (SCD_WIN_SIZE << IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & + IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); + + iwl_legacy_write_targ_mem(priv, priv->scd_base_addr + + IWL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), + (SCD_FRAME_LIMIT << IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) + & IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); + + iwl_legacy_set_bits_prph(priv, IWL49_SCD_INTERRUPT_MASK, (1 << txq_id)); + + /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ + iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + + +int iwl4965_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn) +{ + int sta_id; + int tx_fifo; + int txq_id; + int ret; + unsigned long flags; + struct iwl_tid_data *tid_data; + + tx_fifo = iwl4965_get_fifo_from_tid(iwl_legacy_rxon_ctx_from_vif(vif), tid); + if (unlikely(tx_fifo < 0)) + return tx_fifo; + + IWL_WARN(priv, "%s on ra = %pM tid = %d\n", + __func__, sta->addr, tid); + + sta_id = iwl_legacy_sta_id(sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Start AGG on invalid station\n"); + return -ENXIO; + } + if (unlikely(tid >= MAX_TID_COUNT)) + return -EINVAL; + + if (priv->stations[sta_id].tid[tid].agg.state != IWL_AGG_OFF) { + IWL_ERR(priv, "Start AGG when state is not IWL_AGG_OFF !\n"); + return -ENXIO; + } + + txq_id = iwl4965_txq_ctx_activate_free(priv); + if (txq_id == -1) { + IWL_ERR(priv, "No free aggregation queue available\n"); + return -ENXIO; + } + + spin_lock_irqsave(&priv->sta_lock, flags); + tid_data = &priv->stations[sta_id].tid[tid]; + *ssn = SEQ_TO_SN(tid_data->seq_number); + tid_data->agg.txq_id = txq_id; + iwl_legacy_set_swq_id(&priv->txq[txq_id], + iwl4965_get_ac_from_tid(tid), txq_id); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + ret = iwl4965_txq_agg_enable(priv, txq_id, tx_fifo, + sta_id, tid, *ssn); + if (ret) + return ret; + + spin_lock_irqsave(&priv->sta_lock, flags); + tid_data = &priv->stations[sta_id].tid[tid]; + if (tid_data->tfds_in_queue == 0) { + IWL_DEBUG_HT(priv, "HW queue is empty\n"); + tid_data->agg.state = IWL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); + } else { + IWL_DEBUG_HT(priv, + "HW queue is NOT empty: %d packets in HW queue\n", + tid_data->tfds_in_queue); + tid_data->agg.state = IWL_EMPTYING_HW_QUEUE_ADDBA; + } + spin_unlock_irqrestore(&priv->sta_lock, flags); + return ret; +} + +/** + * txq_id must be greater than IWL49_FIRST_AMPDU_QUEUE + * priv->lock must be held by the caller + */ +static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, + u16 ssn_idx, u8 tx_fifo) +{ + if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || + (IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { + IWL_WARN(priv, + "queue number out of range: %d, must be %d to %d\n", + txq_id, IWL49_FIRST_AMPDU_QUEUE, + IWL49_FIRST_AMPDU_QUEUE + + priv->cfg->base_params->num_of_ampdu_queues - 1); + return -EINVAL; + } + + iwl4965_tx_queue_stop_scheduler(priv, txq_id); + + iwl_legacy_clear_bits_prph(priv, + IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); + + priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); + priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); + /* supposes that ssn_idx is valid (!= 0xFFF) */ + iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); + + iwl_legacy_clear_bits_prph(priv, + IWL49_SCD_INTERRUPT_MASK, (1 << txq_id)); + iwl_txq_ctx_deactivate(priv, txq_id); + iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0); + + return 0; +} + +int iwl4965_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid) +{ + int tx_fifo_id, txq_id, sta_id, ssn; + struct iwl_tid_data *tid_data; + int write_ptr, read_ptr; + unsigned long flags; + + tx_fifo_id = iwl4965_get_fifo_from_tid(iwl_legacy_rxon_ctx_from_vif(vif), tid); + if (unlikely(tx_fifo_id < 0)) + return tx_fifo_id; + + sta_id = iwl_legacy_sta_id(sta); + + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Invalid station for AGG tid %d\n", tid); + return -ENXIO; + } + + spin_lock_irqsave(&priv->sta_lock, flags); + + tid_data = &priv->stations[sta_id].tid[tid]; + ssn = (tid_data->seq_number & IEEE80211_SCTL_SEQ) >> 4; + txq_id = tid_data->agg.txq_id; + + switch (priv->stations[sta_id].tid[tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* + * This can happen if the peer stops aggregation + * again before we've had a chance to drain the + * queue we selected previously, i.e. before the + * session was really started completely. + */ + IWL_DEBUG_HT(priv, "AGG stop before setup done\n"); + goto turn_off; + case IWL_AGG_ON: + break; + default: + IWL_WARN(priv, "Stopping AGG while state not ON or starting\n"); + } + + write_ptr = priv->txq[txq_id].q.write_ptr; + read_ptr = priv->txq[txq_id].q.read_ptr; + + /* The queue is not empty */ + if (write_ptr != read_ptr) { + IWL_DEBUG_HT(priv, "Stopping a non empty AGG HW QUEUE\n"); + priv->stations[sta_id].tid[tid].agg.state = + IWL_EMPTYING_HW_QUEUE_DELBA; + spin_unlock_irqrestore(&priv->sta_lock, flags); + return 0; + } + + IWL_DEBUG_HT(priv, "HW queue is empty\n"); + turn_off: + priv->stations[sta_id].tid[tid].agg.state = IWL_AGG_OFF; + + /* do not restore/save irqs */ + spin_unlock(&priv->sta_lock); + spin_lock(&priv->lock); + + /* + * the only reason this call can fail is queue number out of range, + * which can happen if uCode is reloaded and all the station + * information are lost. if it is outside the range, there is no need + * to deactivate the uCode queue, just return "success" to allow + * mac80211 to clean up it own data. + */ + iwl4965_txq_agg_disable(priv, txq_id, ssn, tx_fifo_id); + spin_unlock_irqrestore(&priv->lock, flags); + + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + + return 0; +} + +int iwl4965_txq_check_empty(struct iwl_priv *priv, + int sta_id, u8 tid, int txq_id) +{ + struct iwl_queue *q = &priv->txq[txq_id].q; + u8 *addr = priv->stations[sta_id].sta.sta.addr; + struct iwl_tid_data *tid_data = &priv->stations[sta_id].tid[tid]; + struct iwl_rxon_context *ctx; + + ctx = &priv->contexts[priv->stations[sta_id].ctxid]; + + lockdep_assert_held(&priv->sta_lock); + + switch (priv->stations[sta_id].tid[tid].agg.state) { + case IWL_EMPTYING_HW_QUEUE_DELBA: + /* We are reclaiming the last packet of the */ + /* aggregated HW queue */ + if ((txq_id == tid_data->agg.txq_id) && + (q->read_ptr == q->write_ptr)) { + u16 ssn = SEQ_TO_SN(tid_data->seq_number); + int tx_fifo = iwl4965_get_fifo_from_tid(ctx, tid); + IWL_DEBUG_HT(priv, + "HW queue empty: continue DELBA flow\n"); + iwl4965_txq_agg_disable(priv, txq_id, ssn, tx_fifo); + tid_data->agg.state = IWL_AGG_OFF; + ieee80211_stop_tx_ba_cb_irqsafe(ctx->vif, addr, tid); + } + break; + case IWL_EMPTYING_HW_QUEUE_ADDBA: + /* We are reclaiming the last packet of the queue */ + if (tid_data->tfds_in_queue == 0) { + IWL_DEBUG_HT(priv, + "HW queue empty: continue ADDBA flow\n"); + tid_data->agg.state = IWL_AGG_ON; + ieee80211_start_tx_ba_cb_irqsafe(ctx->vif, addr, tid); + } + break; + } + + return 0; +} + +static void iwl4965_non_agg_tx_status(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr1) +{ + struct ieee80211_sta *sta; + struct iwl_station_priv *sta_priv; + + rcu_read_lock(); + sta = ieee80211_find_sta(ctx->vif, addr1); + if (sta) { + sta_priv = (void *)sta->drv_priv; + /* avoid atomic ops if this isn't a client */ + if (sta_priv->client && + atomic_dec_return(&sta_priv->pending_frames) == 0) + ieee80211_sta_block_awake(priv->hw, sta, false); + } + rcu_read_unlock(); +} + +static void +iwl4965_tx_status(struct iwl_priv *priv, struct iwl_tx_info *tx_info, + bool is_agg) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) tx_info->skb->data; + + if (!is_agg) + iwl4965_non_agg_tx_status(priv, tx_info->ctx, hdr->addr1); + + ieee80211_tx_status_irqsafe(priv->hw, tx_info->skb); +} + +int iwl4965_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index) +{ + struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct iwl_queue *q = &txq->q; + struct iwl_tx_info *tx_info; + int nfreed = 0; + struct ieee80211_hdr *hdr; + + if ((index >= q->n_bd) || (iwl_legacy_queue_used(q, index) == 0)) { + IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, " + "is out of range [0-%d] %d %d.\n", txq_id, + index, q->n_bd, q->write_ptr, q->read_ptr); + return 0; + } + + for (index = iwl_legacy_queue_inc_wrap(index, q->n_bd); + q->read_ptr != index; + q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + tx_info = &txq->txb[txq->q.read_ptr]; + iwl4965_tx_status(priv, tx_info, + txq_id >= IWL4965_FIRST_AMPDU_QUEUE); + + hdr = (struct ieee80211_hdr *)tx_info->skb->data; + if (hdr && ieee80211_is_data_qos(hdr->frame_control)) + nfreed++; + tx_info->skb = NULL; + + priv->cfg->ops->lib->txq_free_tfd(priv, txq); + } + return nfreed; +} + +/** + * iwl4965_tx_status_reply_compressed_ba - Update tx status from block-ack + * + * Go through block-ack's bitmap of ACK'd frames, update driver's record of + * ACK vs. not. This gets sent to mac80211, then to rate scaling algo. + */ +static int iwl4965_tx_status_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_ht_agg *agg, + struct iwl_compressed_ba_resp *ba_resp) + +{ + int i, sh, ack; + u16 seq_ctl = le16_to_cpu(ba_resp->seq_ctl); + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + int successes = 0; + struct ieee80211_tx_info *info; + u64 bitmap, sent_bitmap; + + if (unlikely(!agg->wait_for_ba)) { + if (unlikely(ba_resp->bitmap)) + IWL_ERR(priv, "Received BA when not expected\n"); + return -EINVAL; + } + + /* Mark that the expected block-ack response arrived */ + agg->wait_for_ba = 0; + IWL_DEBUG_TX_REPLY(priv, "BA %d %d\n", agg->start_idx, + ba_resp->seq_ctl); + + /* Calculate shift to align block-ack bits with our Tx window bits */ + sh = agg->start_idx - SEQ_TO_INDEX(seq_ctl >> 4); + if (sh < 0) /* tbw something is wrong with indices */ + sh += 0x100; + + if (agg->frame_count > (64 - sh)) { + IWL_DEBUG_TX_REPLY(priv, "more frames than bitmap size"); + return -1; + } + + /* don't use 64-bit values for now */ + bitmap = le64_to_cpu(ba_resp->bitmap) >> sh; + + /* check for success or failure according to the + * transmitted bitmap and block-ack bitmap */ + sent_bitmap = bitmap & agg->bitmap; + + /* For each frame attempted in aggregation, + * update driver's record of tx frame's status. */ + i = 0; + while (sent_bitmap) { + ack = sent_bitmap & 1ULL; + successes += ack; + IWL_DEBUG_TX_REPLY(priv, "%s ON i=%d idx=%d raw=%d\n", + ack ? "ACK" : "NACK", i, + (agg->start_idx + i) & 0xff, + agg->start_idx + i); + sent_bitmap >>= 1; + ++i; + } + + IWL_DEBUG_TX_REPLY(priv, "Bitmap %llx\n", + (unsigned long long)bitmap); + + info = IEEE80211_SKB_CB(priv->txq[scd_flow].txb[agg->start_idx].skb); + memset(&info->status, 0, sizeof(info->status)); + info->flags |= IEEE80211_TX_STAT_ACK; + info->flags |= IEEE80211_TX_STAT_AMPDU; + info->status.ampdu_ack_len = successes; + info->status.ampdu_len = agg->frame_count; + iwl4965_hwrate_to_tx_control(priv, agg->rate_n_flags, info); + + return 0; +} + +/** + * translate ucode response to mac80211 tx status control values + */ +void iwl4965_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, + struct ieee80211_tx_info *info) +{ + struct ieee80211_tx_rate *r = &info->control.rates[0]; + + info->antenna_sel_tx = + ((rate_n_flags & RATE_MCS_ANT_ABC_MSK) >> RATE_MCS_ANT_POS); + if (rate_n_flags & RATE_MCS_HT_MSK) + r->flags |= IEEE80211_TX_RC_MCS; + if (rate_n_flags & RATE_MCS_GF_MSK) + r->flags |= IEEE80211_TX_RC_GREEN_FIELD; + if (rate_n_flags & RATE_MCS_HT40_MSK) + r->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + if (rate_n_flags & RATE_MCS_DUP_MSK) + r->flags |= IEEE80211_TX_RC_DUP_DATA; + if (rate_n_flags & RATE_MCS_SGI_MSK) + r->flags |= IEEE80211_TX_RC_SHORT_GI; + r->idx = iwl4965_hwrate_to_mac80211_idx(rate_n_flags, info->band); +} + +/** + * iwl4965_rx_reply_compressed_ba - Handler for REPLY_COMPRESSED_BA + * + * Handles block-acknowledge notification from device, which reports success + * of frames sent via aggregation. + */ +void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_compressed_ba_resp *ba_resp = &pkt->u.compressed_ba; + struct iwl_tx_queue *txq = NULL; + struct iwl_ht_agg *agg; + int index; + int sta_id; + int tid; + unsigned long flags; + + /* "flow" corresponds to Tx queue */ + u16 scd_flow = le16_to_cpu(ba_resp->scd_flow); + + /* "ssn" is start of block-ack Tx window, corresponds to index + * (in Tx queue's circular buffer) of first TFD/frame in window */ + u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); + + if (scd_flow >= priv->hw_params.max_txq_num) { + IWL_ERR(priv, + "BUG_ON scd_flow is bigger than number of queues\n"); + return; + } + + txq = &priv->txq[scd_flow]; + sta_id = ba_resp->sta_id; + tid = ba_resp->tid; + agg = &priv->stations[sta_id].tid[tid].agg; + if (unlikely(agg->txq_id != scd_flow)) { + /* + * FIXME: this is a uCode bug which need to be addressed, + * log the information and return for now! + * since it is possible happen very often and in order + * not to fill the syslog, don't enable the logging by default + */ + IWL_DEBUG_TX_REPLY(priv, + "BA scd_flow %d does not match txq_id %d\n", + scd_flow, agg->txq_id); + return; + } + + /* Find index just before block-ack window */ + index = iwl_legacy_queue_dec_wrap(ba_resp_scd_ssn & 0xff, txq->q.n_bd); + + spin_lock_irqsave(&priv->sta_lock, flags); + + IWL_DEBUG_TX_REPLY(priv, "REPLY_COMPRESSED_BA [%d] Received from %pM, " + "sta_id = %d\n", + agg->wait_for_ba, + (u8 *) &ba_resp->sta_addr_lo32, + ba_resp->sta_id); + IWL_DEBUG_TX_REPLY(priv, "TID = %d, SeqCtl = %d, bitmap = 0x%llx," + "scd_flow = " + "%d, scd_ssn = %d\n", + ba_resp->tid, + ba_resp->seq_ctl, + (unsigned long long)le64_to_cpu(ba_resp->bitmap), + ba_resp->scd_flow, + ba_resp->scd_ssn); + IWL_DEBUG_TX_REPLY(priv, "DAT start_idx = %d, bitmap = 0x%llx\n", + agg->start_idx, + (unsigned long long)agg->bitmap); + + /* Update driver's record of ACK vs. not for each frame in window */ + iwl4965_tx_status_reply_compressed_ba(priv, agg, ba_resp); + + /* Release all TFDs before the SSN, i.e. all TFDs in front of + * block-ack window (we assume that they've been successfully + * transmitted ... if not, it's too late anyway). */ + if (txq->q.read_ptr != (ba_resp_scd_ssn & 0xff)) { + /* calculate mac80211 ampdu sw queue to wake */ + int freed = iwl4965_tx_queue_reclaim(priv, scd_flow, index); + iwl4965_free_tfds_in_queue(priv, sta_id, tid, freed); + + if ((iwl_legacy_queue_space(&txq->q) > txq->q.low_mark) && + priv->mac80211_registered && + (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) + iwl_legacy_wake_queue(priv, txq); + + iwl4965_txq_check_empty(priv, sta_id, tid, scd_flow); + } + + spin_unlock_irqrestore(&priv->sta_lock, flags); +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +const char *iwl4965_get_tx_fail_reason(u32 status) +{ +#define TX_STATUS_FAIL(x) case TX_STATUS_FAIL_ ## x: return #x +#define TX_STATUS_POSTPONE(x) case TX_STATUS_POSTPONE_ ## x: return #x + + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + return "SUCCESS"; + TX_STATUS_POSTPONE(DELAY); + TX_STATUS_POSTPONE(FEW_BYTES); + TX_STATUS_POSTPONE(QUIET_PERIOD); + TX_STATUS_POSTPONE(CALC_TTAK); + TX_STATUS_FAIL(INTERNAL_CROSSED_RETRY); + TX_STATUS_FAIL(SHORT_LIMIT); + TX_STATUS_FAIL(LONG_LIMIT); + TX_STATUS_FAIL(FIFO_UNDERRUN); + TX_STATUS_FAIL(DRAIN_FLOW); + TX_STATUS_FAIL(RFKILL_FLUSH); + TX_STATUS_FAIL(LIFE_EXPIRE); + TX_STATUS_FAIL(DEST_PS); + TX_STATUS_FAIL(HOST_ABORTED); + TX_STATUS_FAIL(BT_RETRY); + TX_STATUS_FAIL(STA_INVALID); + TX_STATUS_FAIL(FRAG_DROPPED); + TX_STATUS_FAIL(TID_DISABLE); + TX_STATUS_FAIL(FIFO_FLUSHED); + TX_STATUS_FAIL(INSUFFICIENT_CF_POLL); + TX_STATUS_FAIL(PASSIVE_NO_RX); + TX_STATUS_FAIL(NO_BEACON_ON_RADAR); + } + + return "UNKNOWN"; + +#undef TX_STATUS_FAIL +#undef TX_STATUS_POSTPONE +} +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUG */ diff --git a/drivers/net/wireless/iwlegacy/iwl-4965-ucode.c b/drivers/net/wireless/iwlegacy/iwl-4965-ucode.c new file mode 100644 index 00000000000..001d148feb9 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965-ucode.c @@ -0,0 +1,166 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/sched.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +#include "iwl-4965-hw.h" +#include "iwl-4965.h" +#include "iwl-4965-calib.h" + +#define IWL_AC_UNSET -1 + +/** + * iwl_verify_inst_sparse - verify runtime uCode image in card vs. host, + * using sample data 100 bytes apart. If these sample points are good, + * it's a pretty good bet that everything between them is good, too. + */ +static int +iwl4965_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32 len) +{ + u32 val; + int ret = 0; + u32 errcnt = 0; + u32 i; + + IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len); + + for (i = 0; i < len; i += 100, image += 100/sizeof(u32)) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IWL_DL_IO is set */ + iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, + i + IWL4965_RTC_INST_LOWER_BOUND); + val = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + ret = -EIO; + errcnt++; + if (errcnt >= 3) + break; + } + } + + return ret; +} + +/** + * iwl4965_verify_inst_full - verify runtime uCode image in card vs. host, + * looking at all data. + */ +static int iwl4965_verify_inst_full(struct iwl_priv *priv, __le32 *image, + u32 len) +{ + u32 val; + u32 save_len = len; + int ret = 0; + u32 errcnt; + + IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len); + + iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, + IWL4965_RTC_INST_LOWER_BOUND); + + errcnt = 0; + for (; len > 0; len -= sizeof(u32), image++) { + /* read data comes through single port, auto-incr addr */ + /* NOTE: Use the debugless read so we don't flood kernel log + * if IWL_DL_IO is set */ + val = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + if (val != le32_to_cpu(*image)) { + IWL_ERR(priv, "uCode INST section is invalid at " + "offset 0x%x, is 0x%x, s/b 0x%x\n", + save_len - len, val, le32_to_cpu(*image)); + ret = -EIO; + errcnt++; + if (errcnt >= 20) + break; + } + } + + if (!errcnt) + IWL_DEBUG_INFO(priv, + "ucode image in INSTRUCTION memory is good\n"); + + return ret; +} + +/** + * iwl4965_verify_ucode - determine which instruction image is in SRAM, + * and verify its contents + */ +int iwl4965_verify_ucode(struct iwl_priv *priv) +{ + __le32 *image; + u32 len; + int ret; + + /* Try bootstrap */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + ret = iwl4965_verify_inst_sparse(priv, image, len); + if (!ret) { + IWL_DEBUG_INFO(priv, "Bootstrap uCode is good in inst SRAM\n"); + return 0; + } + + /* Try initialize */ + image = (__le32 *)priv->ucode_init.v_addr; + len = priv->ucode_init.len; + ret = iwl4965_verify_inst_sparse(priv, image, len); + if (!ret) { + IWL_DEBUG_INFO(priv, "Initialize uCode is good in inst SRAM\n"); + return 0; + } + + /* Try runtime/protocol */ + image = (__le32 *)priv->ucode_code.v_addr; + len = priv->ucode_code.len; + ret = iwl4965_verify_inst_sparse(priv, image, len); + if (!ret) { + IWL_DEBUG_INFO(priv, "Runtime uCode is good in inst SRAM\n"); + return 0; + } + + IWL_ERR(priv, "NO VALID UCODE IMAGE IN INSTRUCTION SRAM!!\n"); + + /* Since nothing seems to match, show first several data entries in + * instruction SRAM, so maybe visual inspection will give a clue. + * Selection of bootstrap image (vs. other images) is arbitrary. */ + image = (__le32 *)priv->ucode_boot.v_addr; + len = priv->ucode_boot.len; + ret = iwl4965_verify_inst_full(priv, image, len); + + return ret; +} diff --git a/drivers/net/wireless/iwlwifi/iwl-4965.c b/drivers/net/wireless/iwlegacy/iwl-4965.c index 8998ed134d1..f5433c74b84 100644 --- a/drivers/net/wireless/iwlwifi/iwl-4965.c +++ b/drivers/net/wireless/iwlegacy/iwl-4965.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -43,12 +43,11 @@ #include "iwl-core.h" #include "iwl-io.h" #include "iwl-helpers.h" -#include "iwl-agn-calib.h" +#include "iwl-4965-calib.h" #include "iwl-sta.h" -#include "iwl-agn-led.h" -#include "iwl-agn.h" -#include "iwl-agn-debugfs.h" -#include "iwl-legacy.h" +#include "iwl-4965-led.h" +#include "iwl-4965.h" +#include "iwl-4965-debugfs.h" static int iwl4965_send_tx_power(struct iwl_priv *priv); static int iwl4965_hw_get_temperature(struct iwl_priv *priv); @@ -74,11 +73,11 @@ static int iwl4965_verify_bsm(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, "Begin verify bsm\n"); /* verify BSM SRAM contents */ - val = iwl_read_prph(priv, BSM_WR_DWCOUNT_REG); + val = iwl_legacy_read_prph(priv, BSM_WR_DWCOUNT_REG); for (reg = BSM_SRAM_LOWER_BOUND; reg < BSM_SRAM_LOWER_BOUND + len; reg += sizeof(u32), image++) { - val = iwl_read_prph(priv, reg); + val = iwl_legacy_read_prph(priv, reg); if (val != le32_to_cpu(*image)) { IWL_ERR(priv, "BSM uCode verification failed at " "addr 0x%08X+%u (of %u), is 0x%x, s/b 0x%x\n", @@ -158,33 +157,34 @@ static int iwl4965_load_bsm(struct iwl_priv *priv) inst_len = priv->ucode_init.len; data_len = priv->ucode_init_data.len; - iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); - iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); - iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); - iwl_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); + iwl_legacy_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl_legacy_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, inst_len); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, data_len); /* Fill BSM memory with bootstrap instructions */ for (reg_offset = BSM_SRAM_LOWER_BOUND; reg_offset < BSM_SRAM_LOWER_BOUND + len; reg_offset += sizeof(u32), image++) - _iwl_write_prph(priv, reg_offset, le32_to_cpu(*image)); + _iwl_legacy_write_prph(priv, reg_offset, le32_to_cpu(*image)); ret = iwl4965_verify_bsm(priv); if (ret) return ret; /* Tell BSM to copy from BSM SRAM into instruction SRAM, when asked */ - iwl_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); - iwl_write_prph(priv, BSM_WR_MEM_DST_REG, IWL49_RTC_INST_LOWER_BOUND); - iwl_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); + iwl_legacy_write_prph(priv, BSM_WR_MEM_SRC_REG, 0x0); + iwl_legacy_write_prph(priv, + BSM_WR_MEM_DST_REG, IWL49_RTC_INST_LOWER_BOUND); + iwl_legacy_write_prph(priv, BSM_WR_DWCOUNT_REG, len / sizeof(u32)); /* Load bootstrap code into instruction SRAM now, * to prepare to load "initialize" uCode */ - iwl_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); + iwl_legacy_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START); /* Wait for load of bootstrap uCode to finish */ for (i = 0; i < 100; i++) { - done = iwl_read_prph(priv, BSM_WR_CTRL_REG); + done = iwl_legacy_read_prph(priv, BSM_WR_CTRL_REG); if (!(done & BSM_WR_CTRL_REG_BIT_START)) break; udelay(10); @@ -198,7 +198,8 @@ static int iwl4965_load_bsm(struct iwl_priv *priv) /* Enable future boot loads whenever power management unit triggers it * (e.g. when powering back up after power-save shutdown) */ - iwl_write_prph(priv, BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); + iwl_legacy_write_prph(priv, + BSM_WR_CTRL_REG, BSM_WR_CTRL_REG_BIT_START_EN); return 0; @@ -224,14 +225,14 @@ static int iwl4965_set_ucode_ptrs(struct iwl_priv *priv) pdata = priv->ucode_data_backup.p_addr >> 4; /* Tell bootstrap uCode where to find image to load */ - iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); - iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); - iwl_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, + iwl_legacy_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, priv->ucode_data.len); /* Inst byte count must be last to set up, bit 31 signals uCode * that all new ptr/size info is in place */ - iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, + iwl_legacy_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, priv->ucode_code.len | BSM_DRAM_INST_LOAD); IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n"); @@ -254,7 +255,7 @@ static void iwl4965_init_alive_start(struct iwl_priv *priv) /* Bootstrap uCode has loaded initialize uCode ... verify inst image. * This is a paranoid check, because we would not have gotten the * "initialize" alive if code weren't properly loaded. */ - if (iwl_verify_ucode(priv)) { + if (iwl4965_verify_ucode(priv)) { /* Runtime instruction load was bad; * take it all the way back down so we can try again */ IWL_DEBUG_INFO(priv, "Bad \"initialize\" uCode load.\n"); @@ -280,7 +281,7 @@ restart: queue_work(priv->workqueue, &priv->restart); } -static bool is_ht40_channel(__le32 rxon_flags) +static bool iw4965_is_ht40_channel(__le32 rxon_flags) { int chan_mod = le32_to_cpu(rxon_flags & RXON_FLG_CHANNEL_MODE_MSK) >> RXON_FLG_CHANNEL_MODE_POS; @@ -288,23 +289,6 @@ static bool is_ht40_channel(__le32 rxon_flags) (chan_mod == CHANNEL_MODE_MIXED)); } -/* - * EEPROM handlers - */ -static u16 iwl4965_eeprom_calib_version(struct iwl_priv *priv) -{ - return iwl_eeprom_query16(priv, EEPROM_4965_CALIB_VERSION_OFFSET); -} - -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - * must be called under priv->lock and mac access - */ -static void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask) -{ - iwl_write_prph(priv, IWL49_SCD_TXFACT, mask); -} - static void iwl4965_nic_config(struct iwl_priv *priv) { unsigned long flags; @@ -312,22 +296,23 @@ static void iwl4965_nic_config(struct iwl_priv *priv) spin_lock_irqsave(&priv->lock, flags); - radio_cfg = iwl_eeprom_query16(priv, EEPROM_RADIO_CONFIG); + radio_cfg = iwl_legacy_eeprom_query16(priv, EEPROM_RADIO_CONFIG); /* write radio config values to register */ if (EEPROM_RF_CFG_TYPE_MSK(radio_cfg) == EEPROM_4965_RF_CFG_TYPE_MAX) - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, EEPROM_RF_CFG_TYPE_MSK(radio_cfg) | EEPROM_RF_CFG_STEP_MSK(radio_cfg) | EEPROM_RF_CFG_DASH_MSK(radio_cfg)); /* set CSR_HW_CONFIG_REG for uCode use */ - iwl_set_bit(priv, CSR_HW_IF_CONFIG_REG, + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI | CSR_HW_IF_CONFIG_REG_BIT_MAC_SI); priv->calib_info = (struct iwl_eeprom_calib_info *) - iwl_eeprom_query_addr(priv, EEPROM_4965_CALIB_TXPOWER_OFFSET); + iwl_legacy_eeprom_query_addr(priv, + EEPROM_4965_CALIB_TXPOWER_OFFSET); spin_unlock_irqrestore(&priv->lock, flags); } @@ -340,7 +325,7 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv) struct iwl_chain_noise_data *data = &(priv->chain_noise_data); if ((data->state == IWL_CHAIN_NOISE_ALIVE) && - iwl_is_any_associated(priv)) { + iwl_legacy_is_any_associated(priv)) { struct iwl_calib_diff_gain_cmd cmd; /* clear data for chain noise calibration algorithm */ @@ -357,7 +342,7 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv) cmd.diff_gain_a = 0; cmd.diff_gain_b = 0; cmd.diff_gain_c = 0; - if (iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, + if (iwl_legacy_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, sizeof(cmd), &cmd)) IWL_ERR(priv, "Could not send REPLY_PHY_CALIBRATION_CMD\n"); @@ -366,237 +351,6 @@ static void iwl4965_chain_noise_reset(struct iwl_priv *priv) } } -static void iwl4965_gain_computation(struct iwl_priv *priv, - u32 *average_noise, - u16 min_average_noise_antenna_i, - u32 min_average_noise, - u8 default_chain) -{ - int i, ret; - struct iwl_chain_noise_data *data = &priv->chain_noise_data; - - data->delta_gain_code[min_average_noise_antenna_i] = 0; - - for (i = default_chain; i < NUM_RX_CHAINS; i++) { - s32 delta_g = 0; - - if (!(data->disconn_array[i]) && - (data->delta_gain_code[i] == - CHAIN_NOISE_DELTA_GAIN_INIT_VAL)) { - delta_g = average_noise[i] - min_average_noise; - data->delta_gain_code[i] = (u8)((delta_g * 10) / 15); - data->delta_gain_code[i] = - min(data->delta_gain_code[i], - (u8) CHAIN_NOISE_MAX_DELTA_GAIN_CODE); - - data->delta_gain_code[i] = - (data->delta_gain_code[i] | (1 << 2)); - } else { - data->delta_gain_code[i] = 0; - } - } - IWL_DEBUG_CALIB(priv, "delta_gain_codes: a %d b %d c %d\n", - data->delta_gain_code[0], - data->delta_gain_code[1], - data->delta_gain_code[2]); - - /* Differential gain gets sent to uCode only once */ - if (!data->radio_write) { - struct iwl_calib_diff_gain_cmd cmd; - data->radio_write = 1; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.op_code = IWL_PHY_CALIBRATE_DIFF_GAIN_CMD; - cmd.diff_gain_a = data->delta_gain_code[0]; - cmd.diff_gain_b = data->delta_gain_code[1]; - cmd.diff_gain_c = data->delta_gain_code[2]; - ret = iwl_send_cmd_pdu(priv, REPLY_PHY_CALIBRATION_CMD, - sizeof(cmd), &cmd); - if (ret) - IWL_DEBUG_CALIB(priv, "fail sending cmd " - "REPLY_PHY_CALIBRATION_CMD\n"); - - /* TODO we might want recalculate - * rx_chain in rxon cmd */ - - /* Mark so we run this algo only once! */ - data->state = IWL_CHAIN_NOISE_CALIBRATED; - } -} - -static void iwl4965_bg_txpower_work(struct work_struct *work) -{ - struct iwl_priv *priv = container_of(work, struct iwl_priv, - txpower_work); - - /* If a scan happened to start before we got here - * then just return; the statistics notification will - * kick off another scheduled work to compensate for - * any temperature delta we missed here. */ - if (test_bit(STATUS_EXIT_PENDING, &priv->status) || - test_bit(STATUS_SCANNING, &priv->status)) - return; - - mutex_lock(&priv->mutex); - - /* Regardless of if we are associated, we must reconfigure the - * TX power since frames can be sent on non-radar channels while - * not associated */ - iwl4965_send_tx_power(priv); - - /* Update last_temperature to keep is_calib_needed from running - * when it isn't needed... */ - priv->last_temperature = priv->temperature; - - mutex_unlock(&priv->mutex); -} - -/* - * Acquire priv->lock before calling this function ! - */ -static void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index) -{ - iwl_write_direct32(priv, HBUS_TARG_WRPTR, - (index & 0xff) | (txq_id << 8)); - iwl_write_prph(priv, IWL49_SCD_QUEUE_RDPTR(txq_id), index); -} - -/** - * iwl4965_tx_queue_set_status - (optionally) start Tx/Cmd queue - * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed - * @scd_retry: (1) Indicates queue will be used in aggregation mode - * - * NOTE: Acquire priv->lock before calling this function ! - */ -static void iwl4965_tx_queue_set_status(struct iwl_priv *priv, - struct iwl_tx_queue *txq, - int tx_fifo_id, int scd_retry) -{ - int txq_id = txq->q.id; - - /* Find out whether to activate Tx queue */ - int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0; - - /* Set up and activate */ - iwl_write_prph(priv, IWL49_SCD_QUEUE_STATUS_BITS(txq_id), - (active << IWL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (tx_fifo_id << IWL49_SCD_QUEUE_STTS_REG_POS_TXF) | - (scd_retry << IWL49_SCD_QUEUE_STTS_REG_POS_WSL) | - (scd_retry << IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | - IWL49_SCD_QUEUE_STTS_REG_MSK); - - txq->sched_retry = scd_retry; - - IWL_DEBUG_INFO(priv, "%s %s Queue %d on AC %d\n", - active ? "Activate" : "Deactivate", - scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); -} - -static const s8 default_queue_to_tx_fifo[] = { - IWL_TX_FIFO_VO, - IWL_TX_FIFO_VI, - IWL_TX_FIFO_BE, - IWL_TX_FIFO_BK, - IWL49_CMD_FIFO_NUM, - IWL_TX_FIFO_UNUSED, - IWL_TX_FIFO_UNUSED, -}; - -static int iwl4965_alive_notify(struct iwl_priv *priv) -{ - u32 a; - unsigned long flags; - int i, chan; - u32 reg_val; - - spin_lock_irqsave(&priv->lock, flags); - - /* Clear 4965's internal Tx Scheduler data base */ - priv->scd_base_addr = iwl_read_prph(priv, IWL49_SCD_SRAM_BASE_ADDR); - a = priv->scd_base_addr + IWL49_SCD_CONTEXT_DATA_OFFSET; - for (; a < priv->scd_base_addr + IWL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) - iwl_write_targ_mem(priv, a, 0); - for (; a < priv->scd_base_addr + IWL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) - iwl_write_targ_mem(priv, a, 0); - for (; a < priv->scd_base_addr + - IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4) - iwl_write_targ_mem(priv, a, 0); - - /* Tel 4965 where to find Tx byte count tables */ - iwl_write_prph(priv, IWL49_SCD_DRAM_BASE_ADDR, - priv->scd_bc_tbls.dma >> 10); - - /* Enable DMA channel */ - for (chan = 0; chan < FH49_TCSR_CHNL_NUM ; chan++) - iwl_write_direct32(priv, FH_TCSR_CHNL_TX_CONFIG_REG(chan), - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | - FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); - - /* Update FH chicken bits */ - reg_val = iwl_read_direct32(priv, FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(priv, FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - /* Disable chain mode for all queues */ - iwl_write_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, 0); - - /* Initialize each Tx queue (including the command queue) */ - for (i = 0; i < priv->hw_params.max_txq_num; i++) { - - /* TFD circular buffer read/write indexes */ - iwl_write_prph(priv, IWL49_SCD_QUEUE_RDPTR(i), 0); - iwl_write_direct32(priv, HBUS_TARG_WRPTR, 0 | (i << 8)); - - /* Max Tx Window size for Scheduler-ACK mode */ - iwl_write_targ_mem(priv, priv->scd_base_addr + - IWL49_SCD_CONTEXT_QUEUE_OFFSET(i), - (SCD_WIN_SIZE << - IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & - IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); - - /* Frame limit */ - iwl_write_targ_mem(priv, priv->scd_base_addr + - IWL49_SCD_CONTEXT_QUEUE_OFFSET(i) + - sizeof(u32), - (SCD_FRAME_LIMIT << - IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); - - } - iwl_write_prph(priv, IWL49_SCD_INTERRUPT_MASK, - (1 << priv->hw_params.max_txq_num) - 1); - - /* Activate all Tx DMA/FIFO channels */ - priv->cfg->ops->lib->txq_set_sched(priv, IWL_MASK(0, 6)); - - iwl4965_set_wr_ptrs(priv, IWL_DEFAULT_CMD_QUEUE_NUM, 0); - - /* make sure all queue are not stopped */ - memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); - for (i = 0; i < 4; i++) - atomic_set(&priv->queue_stop_count[i], 0); - - /* reset to 0 to enable all the queue first */ - priv->txq_ctx_active_msk = 0; - /* Map each Tx/cmd queue to its corresponding fifo */ - BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); - - for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { - int ac = default_queue_to_tx_fifo[i]; - - iwl_txq_ctx_activate(priv, i); - - if (ac == IWL_TX_FIFO_UNUSED) - continue; - - iwl4965_tx_queue_set_status(priv, &priv->txq[i], ac, 0); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - static struct iwl_sensitivity_ranges iwl4965_sensitivity = { .min_nrg_cck = 97, .max_nrg_cck = 0, /* not used, set to 0 */ @@ -658,15 +412,15 @@ static int iwl4965_hw_set_hw_params(struct iwl_priv *priv) priv->hw_params.rx_wrt_ptr_reg = FH_RSCSR_CHNL0_WPTR; - priv->hw_params.tx_chains_num = num_of_ant(priv->cfg->valid_tx_ant); - priv->hw_params.rx_chains_num = num_of_ant(priv->cfg->valid_rx_ant); + priv->hw_params.tx_chains_num = iwl4965_num_of_ant(priv->cfg->valid_tx_ant); + priv->hw_params.rx_chains_num = iwl4965_num_of_ant(priv->cfg->valid_rx_ant); priv->hw_params.valid_tx_ant = priv->cfg->valid_tx_ant; priv->hw_params.valid_rx_ant = priv->cfg->valid_rx_ant; iwl4965_set_ct_threshold(priv); priv->hw_params.sens = &iwl4965_sensitivity; - priv->hw_params.beacon_time_tsf_bits = IWLAGN_EXT_BEACON_TIME_POS; + priv->hw_params.beacon_time_tsf_bits = IWL4965_EXT_BEACON_TIME_POS; return 0; } @@ -1150,9 +904,9 @@ static int iwl4965_fill_txpower_tbl(struct iwl_priv *priv, u8 band, u16 channel, IWL_DEBUG_TXPOWER(priv, "chan %d band %d is_ht40 %d\n", channel, band, is_ht40); - ch_info = iwl_get_channel_info(priv, priv->band, channel); + ch_info = iwl_legacy_get_channel_info(priv, priv->band, channel); - if (!is_channel_valid(ch_info)) + if (!iwl_legacy_is_channel_valid(ch_info)) return -EINVAL; /* get txatten group, used to select 1) thermal txpower adjustment @@ -1376,7 +1130,7 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) band = priv->band == IEEE80211_BAND_2GHZ; - is_ht40 = is_ht40_channel(ctx->active.flags); + is_ht40 = iw4965_is_ht40_channel(ctx->active.flags); if (is_ht40 && (ctx->active.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) ctrl_chan_high = 1; @@ -1390,7 +1144,8 @@ static int iwl4965_send_tx_power(struct iwl_priv *priv) if (ret) goto out; - ret = iwl_send_cmd_pdu(priv, REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd); + ret = iwl_legacy_send_cmd_pdu(priv, + REPLY_TX_PWR_TABLE_CMD, sizeof(cmd), &cmd); out: return ret; @@ -1401,8 +1156,8 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv, { int ret = 0; struct iwl4965_rxon_assoc_cmd rxon_assoc; - const struct iwl_rxon_cmd *rxon1 = &ctx->staging; - const struct iwl_rxon_cmd *rxon2 = &ctx->active; + const struct iwl_legacy_rxon_cmd *rxon1 = &ctx->staging; + const struct iwl_legacy_rxon_cmd *rxon2 = &ctx->active; if ((rxon1->flags == rxon2->flags) && (rxon1->filter_flags == rxon2->filter_flags) && @@ -1428,7 +1183,7 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv, ctx->staging.ofdm_ht_dual_stream_basic_rates; rxon_assoc.rx_chain_select_flags = ctx->staging.rx_chain; - ret = iwl_send_cmd_pdu_async(priv, REPLY_RXON_ASSOC, + ret = iwl_legacy_send_cmd_pdu_async(priv, REPLY_RXON_ASSOC, sizeof(rxon_assoc), &rxon_assoc, NULL); if (ret) return ret; @@ -1439,12 +1194,12 @@ static int iwl4965_send_rxon_assoc(struct iwl_priv *priv, static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { /* cast away the const for active_rxon in this function */ - struct iwl_rxon_cmd *active_rxon = (void *)&ctx->active; + struct iwl_legacy_rxon_cmd *active_rxon = (void *)&ctx->active; int ret; bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EBUSY; if (!ctx->is_active) @@ -1453,7 +1208,7 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c /* always get timestamp with Rx frame */ ctx->staging.flags |= RXON_FLG_TSF2HOST_MSK; - ret = iwl_check_rxon_cmd(priv, ctx); + ret = iwl_legacy_check_rxon_cmd(priv, ctx); if (ret) { IWL_ERR(priv, "Invalid RXON configuration. Not committing.\n"); return -EINVAL; @@ -1467,21 +1222,21 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c (priv->switch_rxon.channel != ctx->staging.channel)) { IWL_DEBUG_11H(priv, "abort channel switch on %d\n", le16_to_cpu(priv->switch_rxon.channel)); - iwl_chswitch_done(priv, false); + iwl_legacy_chswitch_done(priv, false); } /* If we don't need to send a full RXON, we can use * iwl_rxon_assoc_cmd which is used to reconfigure filter * and other flags for the current radio configuration. */ - if (!iwl_full_rxon_required(priv, ctx)) { - ret = iwl_send_rxon_assoc(priv, ctx); + if (!iwl_legacy_full_rxon_required(priv, ctx)) { + ret = iwl_legacy_send_rxon_assoc(priv, ctx); if (ret) { IWL_ERR(priv, "Error setting RXON_ASSOC (%d)\n", ret); return ret; } memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon)); - iwl_print_rx_config_cmd(priv, ctx); + iwl_legacy_print_rx_config_cmd(priv, ctx); return 0; } @@ -1489,12 +1244,12 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c * an RXON_ASSOC and the new config wants the associated mask enabled, * we must clear the associated from the active configuration * before we apply the new config */ - if (iwl_is_associated_ctx(ctx) && new_assoc) { + if (iwl_legacy_is_associated_ctx(ctx) && new_assoc) { IWL_DEBUG_INFO(priv, "Toggling associated bit on current RXON\n"); active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, - sizeof(struct iwl_rxon_cmd), + ret = iwl_legacy_send_cmd_pdu(priv, ctx->rxon_cmd, + sizeof(struct iwl_legacy_rxon_cmd), active_rxon); /* If the mask clearing failed then we set @@ -1504,9 +1259,9 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c IWL_ERR(priv, "Error clearing ASSOC_MSK (%d)\n", ret); return ret; } - iwl_clear_ucode_stations(priv, ctx); - iwl_restore_stations(priv, ctx); - ret = iwl_restore_default_wep_keys(priv, ctx); + iwl_legacy_clear_ucode_stations(priv, ctx); + iwl_legacy_restore_stations(priv, ctx); + ret = iwl4965_restore_default_wep_keys(priv, ctx); if (ret) { IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); return ret; @@ -1521,24 +1276,25 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c le16_to_cpu(ctx->staging.channel), ctx->staging.bssid_addr); - iwl_set_rxon_hwcrypto(priv, ctx, !priv->cfg->mod_params->sw_crypto); + iwl_legacy_set_rxon_hwcrypto(priv, ctx, + !priv->cfg->mod_params->sw_crypto); /* Apply the new configuration * RXON unassoc clears the station table in uCode so restoration of * stations is needed after it (the RXON command) completes */ if (!new_assoc) { - ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, - sizeof(struct iwl_rxon_cmd), &ctx->staging); + ret = iwl_legacy_send_cmd_pdu(priv, ctx->rxon_cmd, + sizeof(struct iwl_legacy_rxon_cmd), &ctx->staging); if (ret) { IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); return ret; } IWL_DEBUG_INFO(priv, "Return from !new_assoc RXON.\n"); memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon)); - iwl_clear_ucode_stations(priv, ctx); - iwl_restore_stations(priv, ctx); - ret = iwl_restore_default_wep_keys(priv, ctx); + iwl_legacy_clear_ucode_stations(priv, ctx); + iwl_legacy_restore_stations(priv, ctx); + ret = iwl4965_restore_default_wep_keys(priv, ctx); if (ret) { IWL_ERR(priv, "Failed to restore WEP keys (%d)\n", ret); return ret; @@ -1549,21 +1305,21 @@ static int iwl4965_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *c /* Apply the new configuration * RXON assoc doesn't clear the station table in uCode, */ - ret = iwl_send_cmd_pdu(priv, ctx->rxon_cmd, - sizeof(struct iwl_rxon_cmd), &ctx->staging); + ret = iwl_legacy_send_cmd_pdu(priv, ctx->rxon_cmd, + sizeof(struct iwl_legacy_rxon_cmd), &ctx->staging); if (ret) { IWL_ERR(priv, "Error setting new RXON (%d)\n", ret); return ret; } memcpy(active_rxon, &ctx->staging, sizeof(*active_rxon)); } - iwl_print_rx_config_cmd(priv, ctx); + iwl_legacy_print_rx_config_cmd(priv, ctx); - iwl_init_sensitivity(priv); + iwl4965_init_sensitivity(priv); /* If we issue a new RXON command which required a tune then we must * send a new TXPOWER command or we won't be able to Tx any frames */ - ret = iwl_set_tx_power(priv, priv->tx_power_next, true); + ret = iwl_legacy_set_tx_power(priv, priv->tx_power_next, true); if (ret) { IWL_ERR(priv, "Error sending TX power (%d)\n", ret); return ret; @@ -1590,7 +1346,7 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, struct ieee80211_vif *vif = ctx->vif; band = priv->band == IEEE80211_BAND_2GHZ; - is_ht40 = is_ht40_channel(ctx->staging.flags); + is_ht40 = iw4965_is_ht40_channel(ctx->staging.flags); if (is_ht40 && (ctx->staging.flags & RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK)) @@ -1621,19 +1377,19 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, else { switch_time_in_usec = vif->bss_conf.beacon_int * switch_count * TIME_UNIT; - ucode_switch_time = iwl_usecs_to_beacons(priv, + ucode_switch_time = iwl_legacy_usecs_to_beacons(priv, switch_time_in_usec, beacon_interval); - cmd.switch_time = iwl_add_beacon_time(priv, + cmd.switch_time = iwl_legacy_add_beacon_time(priv, priv->ucode_beacon_time, ucode_switch_time, beacon_interval); } IWL_DEBUG_11H(priv, "uCode time for the switch is 0x%x\n", cmd.switch_time); - ch_info = iwl_get_channel_info(priv, priv->band, ch); + ch_info = iwl_legacy_get_channel_info(priv, priv->band, ch); if (ch_info) - cmd.expect_beacon = is_channel_radar(ch_info); + cmd.expect_beacon = iwl_legacy_is_channel_radar(ch_info); else { IWL_ERR(priv, "invalid channel switch from %u to %u\n", ctx->active.channel, ch); @@ -1650,7 +1406,8 @@ static int iwl4965_hw_channel_switch(struct iwl_priv *priv, priv->switch_rxon.channel = cmd.channel; priv->switch_rxon.switch_in_progress = true; - return iwl_send_cmd_pdu(priv, REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd); + return iwl_legacy_send_cmd_pdu(priv, + REPLY_CHANNEL_SWITCH, sizeof(cmd), &cmd); } /** @@ -1692,7 +1449,7 @@ static int iwl4965_hw_get_temperature(struct iwl_priv *priv) u32 R4; if (test_bit(STATUS_TEMPERATURE, &priv->status) && - (priv->_agn.statistics.flag & + (priv->_4965.statistics.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)) { IWL_DEBUG_TEMP(priv, "Running HT40 temperature calibration\n"); R1 = (s32)le32_to_cpu(priv->card_alive_init.therm_r1[1]); @@ -1717,7 +1474,7 @@ static int iwl4965_hw_get_temperature(struct iwl_priv *priv) if (!test_bit(STATUS_TEMPERATURE, &priv->status)) vt = sign_extend32(R4, 23); else - vt = sign_extend32(le32_to_cpu(priv->_agn.statistics. + vt = sign_extend32(le32_to_cpu(priv->_4965.statistics. general.common.temperature), 23); IWL_DEBUG_TEMP(priv, "Calib values R[1-3]: %d %d %d R4: %d\n", R1, R2, R3, vt); @@ -1802,7 +1559,6 @@ static void iwl4965_temperature_calib(struct iwl_priv *priv) } priv->temperature = temp; - iwl_tt_handler(priv); set_bit(STATUS_TEMPERATURE, &priv->status); if (!priv->disable_tx_power_cal && @@ -1811,152 +1567,6 @@ static void iwl4965_temperature_calib(struct iwl_priv *priv) queue_work(priv->workqueue, &priv->txpower_work); } -/** - * iwl4965_tx_queue_stop_scheduler - Stop queue, but keep configuration - */ -static void iwl4965_tx_queue_stop_scheduler(struct iwl_priv *priv, - u16 txq_id) -{ - /* Simply stop the queue, but don't change any configuration; - * the SCD_ACT_EN bit is the write-enable mask for the ACTIVE bit. */ - iwl_write_prph(priv, - IWL49_SCD_QUEUE_STATUS_BITS(txq_id), - (0 << IWL49_SCD_QUEUE_STTS_REG_POS_ACTIVE)| - (1 << IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); -} - -/** - * txq_id must be greater than IWL49_FIRST_AMPDU_QUEUE - * priv->lock must be held by the caller - */ -static int iwl4965_txq_agg_disable(struct iwl_priv *priv, u16 txq_id, - u16 ssn_idx, u8 tx_fifo) -{ - if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || - (IWL49_FIRST_AMPDU_QUEUE + - priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { - IWL_WARN(priv, - "queue number out of range: %d, must be %d to %d\n", - txq_id, IWL49_FIRST_AMPDU_QUEUE, - IWL49_FIRST_AMPDU_QUEUE + - priv->cfg->base_params->num_of_ampdu_queues - 1); - return -EINVAL; - } - - iwl4965_tx_queue_stop_scheduler(priv, txq_id); - - iwl_clear_bits_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); - - priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); - priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); - /* supposes that ssn_idx is valid (!= 0xFFF) */ - iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); - - iwl_clear_bits_prph(priv, IWL49_SCD_INTERRUPT_MASK, (1 << txq_id)); - iwl_txq_ctx_deactivate(priv, txq_id); - iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 0); - - return 0; -} - -/** - * iwl4965_tx_queue_set_q2ratid - Map unique receiver/tid combination to a queue - */ -static int iwl4965_tx_queue_set_q2ratid(struct iwl_priv *priv, u16 ra_tid, - u16 txq_id) -{ - u32 tbl_dw_addr; - u32 tbl_dw; - u16 scd_q2ratid; - - scd_q2ratid = ra_tid & IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK; - - tbl_dw_addr = priv->scd_base_addr + - IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(txq_id); - - tbl_dw = iwl_read_targ_mem(priv, tbl_dw_addr); - - if (txq_id & 0x1) - tbl_dw = (scd_q2ratid << 16) | (tbl_dw & 0x0000FFFF); - else - tbl_dw = scd_q2ratid | (tbl_dw & 0xFFFF0000); - - iwl_write_targ_mem(priv, tbl_dw_addr, tbl_dw); - - return 0; -} - - -/** - * iwl4965_tx_queue_agg_enable - Set up & enable aggregation for selected queue - * - * NOTE: txq_id must be greater than IWL49_FIRST_AMPDU_QUEUE, - * i.e. it must be one of the higher queues used for aggregation - */ -static int iwl4965_txq_agg_enable(struct iwl_priv *priv, int txq_id, - int tx_fifo, int sta_id, int tid, u16 ssn_idx) -{ - unsigned long flags; - u16 ra_tid; - int ret; - - if ((IWL49_FIRST_AMPDU_QUEUE > txq_id) || - (IWL49_FIRST_AMPDU_QUEUE + - priv->cfg->base_params->num_of_ampdu_queues <= txq_id)) { - IWL_WARN(priv, - "queue number out of range: %d, must be %d to %d\n", - txq_id, IWL49_FIRST_AMPDU_QUEUE, - IWL49_FIRST_AMPDU_QUEUE + - priv->cfg->base_params->num_of_ampdu_queues - 1); - return -EINVAL; - } - - ra_tid = BUILD_RAxTID(sta_id, tid); - - /* Modify device's station table to Tx this TID */ - ret = iwl_sta_tx_modify_enable_tid(priv, sta_id, tid); - if (ret) - return ret; - - spin_lock_irqsave(&priv->lock, flags); - - /* Stop this Tx queue before configuring it */ - iwl4965_tx_queue_stop_scheduler(priv, txq_id); - - /* Map receiver-address / traffic-ID to this queue */ - iwl4965_tx_queue_set_q2ratid(priv, ra_tid, txq_id); - - /* Set this queue as a chain-building queue */ - iwl_set_bits_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, (1 << txq_id)); - - /* Place first TFD at index corresponding to start sequence number. - * Assumes that ssn_idx is valid (!= 0xFFF) */ - priv->txq[txq_id].q.read_ptr = (ssn_idx & 0xff); - priv->txq[txq_id].q.write_ptr = (ssn_idx & 0xff); - iwl4965_set_wr_ptrs(priv, txq_id, ssn_idx); - - /* Set up Tx window size and frame limit for this queue */ - iwl_write_targ_mem(priv, - priv->scd_base_addr + IWL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id), - (SCD_WIN_SIZE << IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & - IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); - - iwl_write_targ_mem(priv, priv->scd_base_addr + - IWL49_SCD_CONTEXT_QUEUE_OFFSET(txq_id) + sizeof(u32), - (SCD_FRAME_LIMIT << IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) - & IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); - - iwl_set_bits_prph(priv, IWL49_SCD_INTERRUPT_MASK, (1 << txq_id)); - - /* Set up Status area in SRAM, map to Tx DMA/FIFO, activate the queue */ - iwl4965_tx_queue_set_status(priv, &priv->txq[txq_id], tx_fifo, 1); - - spin_unlock_irqrestore(&priv->lock, flags); - - return 0; -} - - static u16 iwl4965_get_hcmd_size(u8 cmd_id, u16 len) { switch (cmd_id) { @@ -1967,7 +1577,8 @@ static u16 iwl4965_get_hcmd_size(u8 cmd_id, u16 len) } } -static u16 iwl4965_build_addsta_hcmd(const struct iwl_addsta_cmd *cmd, u8 *data) +static u16 iwl4965_build_addsta_hcmd(const struct iwl_legacy_addsta_cmd *cmd, + u8 *data) { struct iwl4965_addsta_cmd *addsta = (struct iwl4965_addsta_cmd *)data; addsta->mode = cmd->mode; @@ -2020,16 +1631,14 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, status = le16_to_cpu(frame_status[0].status); idx = start_idx; - /* FIXME: code repetition */ IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, StartIdx=%d idx=%d\n", agg->frame_count, agg->start_idx, idx); info = IEEE80211_SKB_CB(priv->txq[txq_id].txb[idx].skb); info->status.rates[0].count = tx_resp->failure_frame + 1; info->flags &= ~IEEE80211_TX_CTL_AMPDU; - info->flags |= iwl_tx_status_to_mac80211(status); - iwlagn_hwrate_to_tx_control(priv, rate_n_flags, info); - /* FIXME: code repetition end */ + info->flags |= iwl4965_tx_status_to_mac80211(status); + iwl4965_hwrate_to_tx_control(priv, rate_n_flags, info); IWL_DEBUG_TX_REPLY(priv, "1 Frame 0x%x failure :%d\n", status & 0xff, tx_resp->failure_frame); @@ -2056,7 +1665,7 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, IWL_DEBUG_TX_REPLY(priv, "FrameCnt = %d, txq_id=%d idx=%d\n", agg->frame_count, txq_id, idx); - hdr = iwl_tx_queue_get_hdr(priv, txq_id, idx); + hdr = iwl_legacy_tx_queue_get_hdr(priv, txq_id, idx); if (!hdr) { IWL_ERR(priv, "BUG_ON idx doesn't point to valid skb" @@ -2107,15 +1716,14 @@ static int iwl4965_tx_status_reply_tx(struct iwl_priv *priv, return 0; } -static u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) +static u8 iwl4965_find_station(struct iwl_priv *priv, const u8 *addr) { int i; int start = 0; int ret = IWL_INVALID_STATION; unsigned long flags; - if ((priv->iw_mode == NL80211_IFTYPE_ADHOC) || - (priv->iw_mode == NL80211_IFTYPE_AP)) + if ((priv->iw_mode == NL80211_IFTYPE_ADHOC)) start = IWL_STA_ID; if (is_broadcast_ether_addr(addr)) @@ -2151,13 +1759,13 @@ static u8 iwl_find_station(struct iwl_priv *priv, const u8 *addr) return ret; } -static int iwl_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) +static int iwl4965_get_ra_sta_id(struct iwl_priv *priv, struct ieee80211_hdr *hdr) { if (priv->iw_mode == NL80211_IFTYPE_STATION) { return IWL_AP_ID; } else { u8 *da = ieee80211_get_DA(hdr); - return iwl_find_station(priv, da); + return iwl4965_find_station(priv, da); } } @@ -2182,7 +1790,7 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, u8 *qc = NULL; unsigned long flags; - if ((index >= txq->q.n_bd) || (iwl_queue_used(&txq->q, index) == 0)) { + if ((index >= txq->q.n_bd) || (iwl_legacy_queue_used(&txq->q, index) == 0)) { IWL_ERR(priv, "Read index for DMA queue txq_id (%d) index %d " "is out of range [0-%d] %d %d\n", txq_id, index, txq->q.n_bd, txq->q.write_ptr, @@ -2194,13 +1802,13 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, info = IEEE80211_SKB_CB(txq->txb[txq->q.read_ptr].skb); memset(&info->status, 0, sizeof(info->status)); - hdr = iwl_tx_queue_get_hdr(priv, txq_id, index); + hdr = iwl_legacy_tx_queue_get_hdr(priv, txq_id, index); if (ieee80211_is_data_qos(hdr->frame_control)) { qc = ieee80211_get_qos_ctl(hdr); tid = qc[0] & 0xf; } - sta_id = iwl_get_ra_sta_id(priv, hdr); + sta_id = iwl4965_get_ra_sta_id(priv, hdr); if (txq->sched_retry && unlikely(sta_id == IWL_INVALID_STATION)) { IWL_ERR(priv, "Station not known\n"); return; @@ -2217,51 +1825,52 @@ static void iwl4965_rx_reply_tx(struct iwl_priv *priv, iwl4965_tx_status_reply_tx(priv, agg, tx_resp, txq_id, index); /* check if BAR is needed */ - if ((tx_resp->frame_count == 1) && !iwl_is_tx_success(status)) + if ((tx_resp->frame_count == 1) && !iwl4965_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; if (txq->q.read_ptr != (scd_ssn & 0xff)) { - index = iwl_queue_dec_wrap(scd_ssn & 0xff, txq->q.n_bd); + index = iwl_legacy_queue_dec_wrap(scd_ssn & 0xff, + txq->q.n_bd); IWL_DEBUG_TX_REPLY(priv, "Retry scheduler reclaim scd_ssn " "%d index %d\n", scd_ssn , index); - freed = iwlagn_tx_queue_reclaim(priv, txq_id, index); + freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); if (qc) - iwl_free_tfds_in_queue(priv, sta_id, + iwl4965_free_tfds_in_queue(priv, sta_id, tid, freed); if (priv->mac80211_registered && - (iwl_queue_space(&txq->q) > txq->q.low_mark) && - (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) - iwl_wake_queue(priv, txq); + (iwl_legacy_queue_space(&txq->q) > txq->q.low_mark) + && (agg->state != IWL_EMPTYING_HW_QUEUE_DELBA)) + iwl_legacy_wake_queue(priv, txq); } } else { info->status.rates[0].count = tx_resp->failure_frame + 1; - info->flags |= iwl_tx_status_to_mac80211(status); - iwlagn_hwrate_to_tx_control(priv, + info->flags |= iwl4965_tx_status_to_mac80211(status); + iwl4965_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); IWL_DEBUG_TX_REPLY(priv, "TXQ %d status %s (0x%08x) " "rate_n_flags 0x%x retries %d\n", txq_id, - iwl_get_tx_fail_reason(status), status, + iwl4965_get_tx_fail_reason(status), status, le32_to_cpu(tx_resp->rate_n_flags), tx_resp->failure_frame); - freed = iwlagn_tx_queue_reclaim(priv, txq_id, index); + freed = iwl4965_tx_queue_reclaim(priv, txq_id, index); if (qc && likely(sta_id != IWL_INVALID_STATION)) - iwl_free_tfds_in_queue(priv, sta_id, tid, freed); + iwl4965_free_tfds_in_queue(priv, sta_id, tid, freed); else if (sta_id == IWL_INVALID_STATION) IWL_DEBUG_TX_REPLY(priv, "Station not known\n"); if (priv->mac80211_registered && - (iwl_queue_space(&txq->q) > txq->q.low_mark)) - iwl_wake_queue(priv, txq); + (iwl_legacy_queue_space(&txq->q) > txq->q.low_mark)) + iwl_legacy_wake_queue(priv, txq); } if (qc && likely(sta_id != IWL_INVALID_STATION)) - iwlagn_txq_check_empty(priv, sta_id, tid, txq_id); + iwl4965_txq_check_empty(priv, sta_id, tid, txq_id); - iwl_check_abort_status(priv, tx_resp->frame_count, status); + iwl4965_check_abort_status(priv, tx_resp->frame_count, status); spin_unlock_irqrestore(&priv->sta_lock, flags); } @@ -2271,8 +1880,8 @@ static void iwl4965_rx_beacon_notif(struct iwl_priv *priv, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl4965_beacon_notif *beacon = (void *)pkt->u.raw; -#ifdef CONFIG_IWLWIFI_DEBUG - u8 rate = iwl_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + u8 rate __maybe_unused = + iwl4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); IWL_DEBUG_RX(priv, "beacon status %#x, retries:%d ibssmgr:%d " "tsf:0x%.8x%.8x rate:%d\n", @@ -2281,79 +1890,24 @@ static void iwl4965_rx_beacon_notif(struct iwl_priv *priv, le32_to_cpu(beacon->ibss_mgr_status), le32_to_cpu(beacon->high_tsf), le32_to_cpu(beacon->low_tsf), rate); -#endif priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); - - if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) - queue_work(priv->workqueue, &priv->beacon_update); -} - -static int iwl4965_calc_rssi(struct iwl_priv *priv, - struct iwl_rx_phy_res *rx_resp) -{ - /* data from PHY/DSP regarding signal strength, etc., - * contents are always there, not configurable by host. */ - struct iwl4965_rx_non_cfg_phy *ncphy = - (struct iwl4965_rx_non_cfg_phy *)rx_resp->non_cfg_phy_buf; - u32 agc = (le16_to_cpu(ncphy->agc_info) & IWL49_AGC_DB_MASK) - >> IWL49_AGC_DB_POS; - - u32 valid_antennae = - (le16_to_cpu(rx_resp->phy_flags) & IWL49_RX_PHY_FLAGS_ANTENNAE_MASK) - >> IWL49_RX_PHY_FLAGS_ANTENNAE_OFFSET; - u8 max_rssi = 0; - u32 i; - - /* Find max rssi among 3 possible receivers. - * These values are measured by the digital signal processor (DSP). - * They should stay fairly constant even as the signal strength varies, - * if the radio's automatic gain control (AGC) is working right. - * AGC value (see below) will provide the "interesting" info. */ - for (i = 0; i < 3; i++) - if (valid_antennae & (1 << i)) - max_rssi = max(ncphy->rssi_info[i << 1], max_rssi); - - IWL_DEBUG_STATS(priv, "Rssi In A %d B %d C %d Max %d AGC dB %d\n", - ncphy->rssi_info[0], ncphy->rssi_info[2], ncphy->rssi_info[4], - max_rssi, agc); - - /* dBm = max_rssi dB - agc dB - constant. - * Higher AGC (higher radio gain) means lower signal. */ - return max_rssi - agc - IWLAGN_RSSI_OFFSET; } - /* Set up 4965-specific Rx frame reply handlers */ static void iwl4965_rx_handler_setup(struct iwl_priv *priv) { /* Legacy Rx frames */ - priv->rx_handlers[REPLY_RX] = iwlagn_rx_reply_rx; + priv->rx_handlers[REPLY_RX] = iwl4965_rx_reply_rx; /* Tx response */ priv->rx_handlers[REPLY_TX] = iwl4965_rx_reply_tx; priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif; - - /* set up notification wait support */ - spin_lock_init(&priv->_agn.notif_wait_lock); - INIT_LIST_HEAD(&priv->_agn.notif_waits); - init_waitqueue_head(&priv->_agn.notif_waitq); -} - -static void iwl4965_setup_deferred_work(struct iwl_priv *priv) -{ - INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work); -} - -static void iwl4965_cancel_deferred_work(struct iwl_priv *priv) -{ - cancel_work_sync(&priv->txpower_work); } static struct iwl_hcmd_ops iwl4965_hcmd = { .rxon_assoc = iwl4965_send_rxon_assoc, .commit_rxon = iwl4965_commit_rxon, - .set_rxon_chain = iwlagn_set_rxon_chain, - .send_bt_config = iwl_send_bt_config, + .set_rxon_chain = iwl4965_set_rxon_chain, }; static void iwl4965_post_scan(struct iwl_priv *priv) @@ -2365,7 +1919,7 @@ static void iwl4965_post_scan(struct iwl_priv *priv) * performing the scan, fire one off if needed */ if (memcmp(&ctx->staging, &ctx->active, sizeof(ctx->staging))) - iwlcore_commit_rxon(priv, ctx); + iwl_legacy_commit_rxon(priv, ctx); } static void iwl4965_post_associate(struct iwl_priv *priv) @@ -2378,29 +1932,24 @@ static void iwl4965_post_associate(struct iwl_priv *priv) if (!vif || !priv->is_open) return; - if (vif->type == NL80211_IFTYPE_AP) { - IWL_ERR(priv, "%s Should not be called in AP mode\n", __func__); - return; - } - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - iwl_scan_cancel_timeout(priv, 200); + iwl_legacy_scan_cancel_timeout(priv, 200); - conf = ieee80211_get_hw_conf(priv->hw); + conf = iwl_legacy_ieee80211_get_hw_conf(priv->hw); ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv, ctx); + iwl_legacy_commit_rxon(priv, ctx); - ret = iwl_send_rxon_timing(priv, ctx); + ret = iwl_legacy_send_rxon_timing(priv, ctx); if (ret) IWL_WARN(priv, "RXON timing - " "Attempting to continue.\n"); ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - iwl_set_rxon_ht(priv, &priv->current_ht_config); + iwl_legacy_set_rxon_ht(priv, &priv->current_ht_config); if (priv->cfg->ops->hcmd->set_rxon_chain) priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); @@ -2422,7 +1971,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; } - iwlcore_commit_rxon(priv, ctx); + iwl_legacy_commit_rxon(priv, ctx); IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", vif->bss_conf.aid, ctx->active.bssid_addr); @@ -2431,7 +1980,7 @@ static void iwl4965_post_associate(struct iwl_priv *priv) case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_ADHOC: - iwlagn_send_beacon_cmd(priv); + iwl4965_send_beacon_cmd(priv); break; default: IWL_ERR(priv, "%s Should not be called in %d mode\n", @@ -2443,10 +1992,10 @@ static void iwl4965_post_associate(struct iwl_priv *priv) * If chain noise has already been run, then we need to enable * power management here */ if (priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE) - iwl_power_update_mode(priv, false); + iwl_legacy_power_update_mode(priv, false); /* Enable Rx differential gain and sensitivity calibrations */ - iwl_chain_noise_reset(priv); + iwl4965_chain_noise_reset(priv); priv->start_calib = 1; } @@ -2462,14 +2011,14 @@ static void iwl4965_config_ap(struct iwl_priv *priv) return; /* The following should be done only at AP bring up */ - if (!iwl_is_associated_ctx(ctx)) { + if (!iwl_legacy_is_associated_ctx(ctx)) { /* RXON - unassoc (to set timing command) */ ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv, ctx); + iwl_legacy_commit_rxon(priv, ctx); /* RXON Timing */ - ret = iwl_send_rxon_timing(priv, ctx); + ret = iwl_legacy_send_rxon_timing(priv, ctx); if (ret) IWL_WARN(priv, "RXON timing failed - " "Attempting to continue.\n"); @@ -2477,7 +2026,7 @@ static void iwl4965_config_ap(struct iwl_priv *priv) /* AP has all antennas */ priv->chain_noise_data.active_chains = priv->hw_params.valid_rx_ant; - iwl_set_rxon_ht(priv, &priv->current_ht_config); + iwl_legacy_set_rxon_ht(priv, &priv->current_ht_config); if (priv->cfg->ops->hcmd->set_rxon_chain) priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); @@ -2499,51 +2048,37 @@ static void iwl4965_config_ap(struct iwl_priv *priv) ~RXON_FLG_SHORT_SLOT_MSK; } /* need to send beacon cmd before committing assoc RXON! */ - iwlagn_send_beacon_cmd(priv); + iwl4965_send_beacon_cmd(priv); /* restore RXON assoc */ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv, ctx); + iwl_legacy_commit_rxon(priv, ctx); } - iwlagn_send_beacon_cmd(priv); - - /* FIXME - we need to add code here to detect a totally new - * configuration, reset the AP, unassoc, rxon timing, assoc, - * clear sta table, add BCAST sta... */ + iwl4965_send_beacon_cmd(priv); } static struct iwl_hcmd_utils_ops iwl4965_hcmd_utils = { .get_hcmd_size = iwl4965_get_hcmd_size, .build_addsta_hcmd = iwl4965_build_addsta_hcmd, - .chain_noise_reset = iwl4965_chain_noise_reset, - .gain_computation = iwl4965_gain_computation, - .tx_cmd_protection = iwl_legacy_tx_cmd_protection, - .calc_rssi = iwl4965_calc_rssi, - .request_scan = iwlagn_request_scan, + .request_scan = iwl4965_request_scan, .post_scan = iwl4965_post_scan, }; static struct iwl_lib_ops iwl4965_lib = { .set_hw_params = iwl4965_hw_set_hw_params, .txq_update_byte_cnt_tbl = iwl4965_txq_update_byte_cnt_tbl, - .txq_set_sched = iwl4965_txq_set_sched, - .txq_agg_enable = iwl4965_txq_agg_enable, - .txq_agg_disable = iwl4965_txq_agg_disable, - .txq_attach_buf_to_tfd = iwl_hw_txq_attach_buf_to_tfd, - .txq_free_tfd = iwl_hw_txq_free_tfd, - .txq_init = iwl_hw_tx_queue_init, + .txq_attach_buf_to_tfd = iwl4965_hw_txq_attach_buf_to_tfd, + .txq_free_tfd = iwl4965_hw_txq_free_tfd, + .txq_init = iwl4965_hw_tx_queue_init, .rx_handler_setup = iwl4965_rx_handler_setup, - .setup_deferred_work = iwl4965_setup_deferred_work, - .cancel_deferred_work = iwl4965_cancel_deferred_work, .is_valid_rtc_data_addr = iwl4965_hw_valid_rtc_data_addr, - .alive_notify = iwl4965_alive_notify, .init_alive_start = iwl4965_init_alive_start, .load_ucode = iwl4965_load_bsm, - .dump_nic_event_log = iwl_dump_nic_event_log, - .dump_nic_error_log = iwl_dump_nic_error_log, - .dump_fh = iwl_dump_fh, + .dump_nic_event_log = iwl4965_dump_nic_event_log, + .dump_nic_error_log = iwl4965_dump_nic_error_log, + .dump_fh = iwl4965_dump_fh, .set_channel_switch = iwl4965_hw_channel_switch, .apm_ops = { - .init = iwl_apm_init, + .init = iwl_legacy_apm_init, .config = iwl4965_nic_config, }, .eeprom_ops = { @@ -2556,64 +2091,56 @@ static struct iwl_lib_ops iwl4965_lib = { EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS, EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS }, - .acquire_semaphore = iwlcore_eeprom_acquire_semaphore, - .release_semaphore = iwlcore_eeprom_release_semaphore, - .calib_version = iwl4965_eeprom_calib_version, - .query_addr = iwlcore_eeprom_query_addr, + .acquire_semaphore = iwl4965_eeprom_acquire_semaphore, + .release_semaphore = iwl4965_eeprom_release_semaphore, }, .send_tx_power = iwl4965_send_tx_power, - .update_chain_flags = iwl_update_chain_flags, - .isr_ops = { - .isr = iwl_isr_legacy, - }, + .update_chain_flags = iwl4965_update_chain_flags, .temp_ops = { .temperature = iwl4965_temperature_calib, }, .debugfs_ops = { - .rx_stats_read = iwl_ucode_rx_stats_read, - .tx_stats_read = iwl_ucode_tx_stats_read, - .general_stats_read = iwl_ucode_general_stats_read, - .bt_stats_read = iwl_ucode_bt_stats_read, - .reply_tx_error = iwl_reply_tx_error_read, + .rx_stats_read = iwl4965_ucode_rx_stats_read, + .tx_stats_read = iwl4965_ucode_tx_stats_read, + .general_stats_read = iwl4965_ucode_general_stats_read, }, - .check_plcp_health = iwl_good_plcp_health, + .check_plcp_health = iwl4965_good_plcp_health, }; static const struct iwl_legacy_ops iwl4965_legacy_ops = { .post_associate = iwl4965_post_associate, .config_ap = iwl4965_config_ap, - .manage_ibss_station = iwlagn_manage_ibss_station, - .update_bcast_stations = iwl_update_bcast_stations, + .manage_ibss_station = iwl4965_manage_ibss_station, + .update_bcast_stations = iwl4965_update_bcast_stations, }; struct ieee80211_ops iwl4965_hw_ops = { - .tx = iwlagn_mac_tx, - .start = iwlagn_mac_start, - .stop = iwlagn_mac_stop, - .add_interface = iwl_mac_add_interface, - .remove_interface = iwl_mac_remove_interface, - .change_interface = iwl_mac_change_interface, + .tx = iwl4965_mac_tx, + .start = iwl4965_mac_start, + .stop = iwl4965_mac_stop, + .add_interface = iwl_legacy_mac_add_interface, + .remove_interface = iwl_legacy_mac_remove_interface, + .change_interface = iwl_legacy_mac_change_interface, .config = iwl_legacy_mac_config, - .configure_filter = iwlagn_configure_filter, - .set_key = iwlagn_mac_set_key, - .update_tkip_key = iwlagn_mac_update_tkip_key, - .conf_tx = iwl_mac_conf_tx, + .configure_filter = iwl4965_configure_filter, + .set_key = iwl4965_mac_set_key, + .update_tkip_key = iwl4965_mac_update_tkip_key, + .conf_tx = iwl_legacy_mac_conf_tx, .reset_tsf = iwl_legacy_mac_reset_tsf, .bss_info_changed = iwl_legacy_mac_bss_info_changed, - .ampdu_action = iwlagn_mac_ampdu_action, - .hw_scan = iwl_mac_hw_scan, - .sta_add = iwlagn_mac_sta_add, - .sta_remove = iwl_mac_sta_remove, - .channel_switch = iwlagn_mac_channel_switch, - .flush = iwlagn_mac_flush, - .tx_last_beacon = iwl_mac_tx_last_beacon, + .ampdu_action = iwl4965_mac_ampdu_action, + .hw_scan = iwl_legacy_mac_hw_scan, + .sta_add = iwl4965_mac_sta_add, + .sta_remove = iwl_legacy_mac_sta_remove, + .channel_switch = iwl4965_mac_channel_switch, + .tx_last_beacon = iwl_legacy_mac_tx_last_beacon, }; static const struct iwl_ops iwl4965_ops = { .lib = &iwl4965_lib, .hcmd = &iwl4965_hcmd, .utils = &iwl4965_hcmd_utils, - .led = &iwlagn_led_ops, + .led = &iwl4965_led_ops, .legacy = &iwl4965_legacy_ops, .ieee80211_ops = &iwl4965_hw_ops, }; @@ -2625,22 +2152,18 @@ static struct iwl_base_params iwl4965_base_params = { .pll_cfg_val = 0, .set_l0s = true, .use_bsm = true, - .use_isr_legacy = true, - .broken_powersave = true, .led_compensation = 61, .chain_noise_num_beacons = IWL4965_CAL_NUM_BEACONS, .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF, .wd_timeout = IWL_DEF_WD_TIMEOUT, .temperature_kelvin = true, .max_event_log_size = 512, - .tx_power_by_driver = true, .ucode_tracing = true, .sensitivity_calib_by_driver = true, .chain_noise_calib_by_driver = true, - .no_agg_framecnt_info = true, }; -struct iwl_cfg iwl4965_agn_cfg = { +struct iwl_cfg iwl4965_cfg = { .name = "Intel(R) Wireless WiFi Link 4965AGN", .fw_name_pre = IWL4965_FW_PRE, .ucode_api_max = IWL4965_UCODE_API_MAX, @@ -2651,7 +2174,7 @@ struct iwl_cfg iwl4965_agn_cfg = { .eeprom_ver = EEPROM_4965_EEPROM_VERSION, .eeprom_calib_ver = EEPROM_4965_TX_POWER_VERSION, .ops = &iwl4965_ops, - .mod_params = &iwlagn_mod_params, + .mod_params = &iwl4965_mod_params, .base_params = &iwl4965_base_params, .led_mode = IWL_LED_BLINK, /* @@ -2663,4 +2186,3 @@ struct iwl_cfg iwl4965_agn_cfg = { /* Module firmware */ MODULE_FIRMWARE(IWL4965_MODULE_FIRMWARE(IWL4965_UCODE_API_MAX)); - diff --git a/drivers/net/wireless/iwlegacy/iwl-4965.h b/drivers/net/wireless/iwlegacy/iwl-4965.h new file mode 100644 index 00000000000..01f8163daf1 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-4965.h @@ -0,0 +1,282 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_4965_h__ +#define __iwl_4965_h__ + +#include "iwl-dev.h" + +/* configuration for the _4965 devices */ +extern struct iwl_cfg iwl4965_cfg; + +extern struct iwl_mod_params iwl4965_mod_params; + +extern struct ieee80211_ops iwl4965_hw_ops; + +/* tx queue */ +void iwl4965_free_tfds_in_queue(struct iwl_priv *priv, + int sta_id, int tid, int freed); + +/* RXON */ +void iwl4965_set_rxon_chain(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); + +/* uCode */ +int iwl4965_verify_ucode(struct iwl_priv *priv); + +/* lib */ +void iwl4965_check_abort_status(struct iwl_priv *priv, + u8 frame_count, u32 status); + +void iwl4965_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq); +int iwl4965_rx_init(struct iwl_priv *priv, struct iwl_rx_queue *rxq); +int iwl4965_hw_nic_init(struct iwl_priv *priv); +int iwl4965_dump_fh(struct iwl_priv *priv, char **buf, bool display); + +/* rx */ +void iwl4965_rx_queue_restock(struct iwl_priv *priv); +void iwl4965_rx_replenish(struct iwl_priv *priv); +void iwl4965_rx_replenish_now(struct iwl_priv *priv); +void iwl4965_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rxq); +int iwl4965_rxq_stop(struct iwl_priv *priv); +int iwl4965_hwrate_to_mac80211_idx(u32 rate_n_flags, enum ieee80211_band band); +void iwl4965_rx_reply_rx(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl4965_rx_reply_rx_phy(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl4965_rx_handle(struct iwl_priv *priv); + +/* tx */ +void iwl4965_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq); +int iwl4965_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + dma_addr_t addr, u16 len, u8 reset, u8 pad); +int iwl4965_hw_tx_queue_init(struct iwl_priv *priv, + struct iwl_tx_queue *txq); +void iwl4965_hwrate_to_tx_control(struct iwl_priv *priv, u32 rate_n_flags, + struct ieee80211_tx_info *info); +int iwl4965_tx_skb(struct iwl_priv *priv, struct sk_buff *skb); +int iwl4965_tx_agg_start(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid, u16 *ssn); +int iwl4965_tx_agg_stop(struct iwl_priv *priv, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 tid); +int iwl4965_txq_check_empty(struct iwl_priv *priv, + int sta_id, u8 tid, int txq_id); +void iwl4965_rx_reply_compressed_ba(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +int iwl4965_tx_queue_reclaim(struct iwl_priv *priv, int txq_id, int index); +void iwl4965_hw_txq_ctx_free(struct iwl_priv *priv); +int iwl4965_txq_ctx_alloc(struct iwl_priv *priv); +void iwl4965_txq_ctx_reset(struct iwl_priv *priv); +void iwl4965_txq_ctx_stop(struct iwl_priv *priv); +void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask); + +/* + * Acquire priv->lock before calling this function ! + */ +void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index); +/** + * iwl4965_tx_queue_set_status - (optionally) start Tx/Cmd queue + * @tx_fifo_id: Tx DMA/FIFO channel (range 0-7) that the queue will feed + * @scd_retry: (1) Indicates queue will be used in aggregation mode + * + * NOTE: Acquire priv->lock before calling this function ! + */ +void iwl4965_tx_queue_set_status(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + int tx_fifo_id, int scd_retry); + +static inline u32 iwl4965_tx_status_to_mac80211(u32 status) +{ + status &= TX_STATUS_MSK; + + switch (status) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + return IEEE80211_TX_STAT_ACK; + case TX_STATUS_FAIL_DEST_PS: + return IEEE80211_TX_STAT_TX_FILTERED; + default: + return 0; + } +} + +static inline bool iwl4965_is_tx_success(u32 status) +{ + status &= TX_STATUS_MSK; + return (status == TX_STATUS_SUCCESS) || + (status == TX_STATUS_DIRECT_DONE); +} + +u8 iwl4965_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); + +/* rx */ +void iwl4965_rx_missed_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +bool iwl4965_good_plcp_health(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); +void iwl4965_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl4965_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); + +/* scan */ +int iwl4965_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif); + +/* station mgmt */ +int iwl4965_manage_ibss_station(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add); + +/* hcmd */ +int iwl4965_send_beacon_cmd(struct iwl_priv *priv); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +const char *iwl4965_get_tx_fail_reason(u32 status); +#else +static inline const char * +iwl4965_get_tx_fail_reason(u32 status) { return ""; } +#endif + +/* station management */ +int iwl4965_alloc_bcast_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl4965_add_bssid_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, u8 *sta_id_r); +int iwl4965_remove_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl4965_set_default_wep_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key); +int iwl4965_restore_default_wep_keys(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl4965_set_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, u8 sta_id); +int iwl4965_remove_dynamic_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *key, u8 sta_id); +void iwl4965_update_tkip_key(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, u32 iv32, u16 *phase1key); +int iwl4965_sta_tx_modify_enable_tid(struct iwl_priv *priv, + int sta_id, int tid); +int iwl4965_sta_rx_agg_start(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid, u16 ssn); +int iwl4965_sta_rx_agg_stop(struct iwl_priv *priv, struct ieee80211_sta *sta, + int tid); +void iwl4965_sta_modify_sleep_tx_count(struct iwl_priv *priv, + int sta_id, int cnt); +int iwl4965_update_bcast_stations(struct iwl_priv *priv); + +/* rate */ +static inline u32 iwl4965_ant_idx_to_flags(u8 ant_idx) +{ + return BIT(ant_idx) << RATE_MCS_ANT_POS; +} + +static inline u8 iwl4965_hw_get_rate(__le32 rate_n_flags) +{ + return le32_to_cpu(rate_n_flags) & 0xFF; +} + +static inline __le32 iwl4965_hw_set_rate_n_flags(u8 rate, u32 flags) +{ + return cpu_to_le32(flags|(u32)rate); +} + +/* eeprom */ +void iwl4965_eeprom_get_mac(const struct iwl_priv *priv, u8 *mac); +int iwl4965_eeprom_acquire_semaphore(struct iwl_priv *priv); +void iwl4965_eeprom_release_semaphore(struct iwl_priv *priv); +int iwl4965_eeprom_check_version(struct iwl_priv *priv); + +/* mac80211 handlers (for 4965) */ +void iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +int iwl4965_mac_start(struct ieee80211_hw *hw); +void iwl4965_mac_stop(struct ieee80211_hw *hw); +void iwl4965_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast); +int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key); +int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size); +int iwl4965_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); +void iwl4965_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch); + +#endif /* __iwl_4965_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-commands.h b/drivers/net/wireless/iwlegacy/iwl-commands.h new file mode 100644 index 00000000000..17a1d504348 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-commands.h @@ -0,0 +1,3405 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +/* + * Please use this file (iwl-commands.h) only for uCode API definitions. + * Please use iwl-xxxx-hw.h for hardware-related definitions. + * Please use iwl-dev.h for driver implementation definitions. + */ + +#ifndef __iwl_legacy_commands_h__ +#define __iwl_legacy_commands_h__ + +struct iwl_priv; + +/* uCode version contains 4 values: Major/Minor/API/Serial */ +#define IWL_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) +#define IWL_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) +#define IWL_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) +#define IWL_UCODE_SERIAL(ver) ((ver) & 0x000000FF) + + +/* Tx rates */ +#define IWL_CCK_RATES 4 +#define IWL_OFDM_RATES 8 +#define IWL_MAX_RATES (IWL_CCK_RATES + IWL_OFDM_RATES) + +enum { + REPLY_ALIVE = 0x1, + REPLY_ERROR = 0x2, + + /* RXON and QOS commands */ + REPLY_RXON = 0x10, + REPLY_RXON_ASSOC = 0x11, + REPLY_QOS_PARAM = 0x13, + REPLY_RXON_TIMING = 0x14, + + /* Multi-Station support */ + REPLY_ADD_STA = 0x18, + REPLY_REMOVE_STA = 0x19, + + /* Security */ + REPLY_WEPKEY = 0x20, + + /* RX, TX, LEDs */ + REPLY_3945_RX = 0x1b, /* 3945 only */ + REPLY_TX = 0x1c, + REPLY_RATE_SCALE = 0x47, /* 3945 only */ + REPLY_LEDS_CMD = 0x48, + REPLY_TX_LINK_QUALITY_CMD = 0x4e, /* for 4965 and up */ + + /* 802.11h related */ + REPLY_CHANNEL_SWITCH = 0x72, + CHANNEL_SWITCH_NOTIFICATION = 0x73, + REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74, + SPECTRUM_MEASURE_NOTIFICATION = 0x75, + + /* Power Management */ + POWER_TABLE_CMD = 0x77, + PM_SLEEP_NOTIFICATION = 0x7A, + PM_DEBUG_STATISTIC_NOTIFIC = 0x7B, + + /* Scan commands and notifications */ + REPLY_SCAN_CMD = 0x80, + REPLY_SCAN_ABORT_CMD = 0x81, + SCAN_START_NOTIFICATION = 0x82, + SCAN_RESULTS_NOTIFICATION = 0x83, + SCAN_COMPLETE_NOTIFICATION = 0x84, + + /* IBSS/AP commands */ + BEACON_NOTIFICATION = 0x90, + REPLY_TX_BEACON = 0x91, + + /* Miscellaneous commands */ + REPLY_TX_PWR_TABLE_CMD = 0x97, + + /* Bluetooth device coexistence config command */ + REPLY_BT_CONFIG = 0x9b, + + /* Statistics */ + REPLY_STATISTICS_CMD = 0x9c, + STATISTICS_NOTIFICATION = 0x9d, + + /* RF-KILL commands and notifications */ + CARD_STATE_NOTIFICATION = 0xa1, + + /* Missed beacons notification */ + MISSED_BEACONS_NOTIFICATION = 0xa2, + + REPLY_CT_KILL_CONFIG_CMD = 0xa4, + SENSITIVITY_CMD = 0xa8, + REPLY_PHY_CALIBRATION_CMD = 0xb0, + REPLY_RX_PHY_CMD = 0xc0, + REPLY_RX_MPDU_CMD = 0xc1, + REPLY_RX = 0xc3, + REPLY_COMPRESSED_BA = 0xc5, + + REPLY_MAX = 0xff +}; + +/****************************************************************************** + * (0) + * Commonly used structures and definitions: + * Command header, rate_n_flags, txpower + * + *****************************************************************************/ + +/* iwl_cmd_header flags value */ +#define IWL_CMD_FAILED_MSK 0x40 + +#define SEQ_TO_QUEUE(s) (((s) >> 8) & 0x1f) +#define QUEUE_TO_SEQ(q) (((q) & 0x1f) << 8) +#define SEQ_TO_INDEX(s) ((s) & 0xff) +#define INDEX_TO_SEQ(i) ((i) & 0xff) +#define SEQ_HUGE_FRAME cpu_to_le16(0x4000) +#define SEQ_RX_FRAME cpu_to_le16(0x8000) + +/** + * struct iwl_cmd_header + * + * This header format appears in the beginning of each command sent from the + * driver, and each response/notification received from uCode. + */ +struct iwl_cmd_header { + u8 cmd; /* Command ID: REPLY_RXON, etc. */ + u8 flags; /* 0:5 reserved, 6 abort, 7 internal */ + /* + * The driver sets up the sequence number to values of its choosing. + * uCode does not use this value, but passes it back to the driver + * when sending the response to each driver-originated command, so + * the driver can match the response to the command. Since the values + * don't get used by uCode, the driver may set up an arbitrary format. + * + * There is one exception: uCode sets bit 15 when it originates + * the response/notification, i.e. when the response/notification + * is not a direct response to a command sent by the driver. For + * example, uCode issues REPLY_3945_RX when it sends a received frame + * to the driver; it is not a direct response to any driver command. + * + * The Linux driver uses the following format: + * + * 0:7 tfd index - position within TX queue + * 8:12 TX queue id + * 13 reserved + * 14 huge - driver sets this to indicate command is in the + * 'huge' storage at the end of the command buffers + * 15 unsolicited RX or uCode-originated notification + */ + __le16 sequence; + + /* command or response/notification data follows immediately */ + u8 data[0]; +} __packed; + + +/** + * struct iwl3945_tx_power + * + * Used in REPLY_TX_PWR_TABLE_CMD, REPLY_SCAN_CMD, REPLY_CHANNEL_SWITCH + * + * Each entry contains two values: + * 1) DSP gain (or sometimes called DSP attenuation). This is a fine-grained + * linear value that multiplies the output of the digital signal processor, + * before being sent to the analog radio. + * 2) Radio gain. This sets the analog gain of the radio Tx path. + * It is a coarser setting, and behaves in a logarithmic (dB) fashion. + * + * Driver obtains values from struct iwl3945_tx_power power_gain_table[][]. + */ +struct iwl3945_tx_power { + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ +} __packed; + +/** + * struct iwl3945_power_per_rate + * + * Used in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + */ +struct iwl3945_power_per_rate { + u8 rate; /* plcp */ + struct iwl3945_tx_power tpc; + u8 reserved; +} __packed; + +/** + * iwl4965 rate_n_flags bit fields + * + * rate_n_flags format is used in following iwl4965 commands: + * REPLY_RX (response only) + * REPLY_RX_MPDU (response only) + * REPLY_TX (both command and response) + * REPLY_TX_LINK_QUALITY_CMD + * + * High-throughput (HT) rate format for bits 7:0 (bit 8 must be "1"): + * 2-0: 0) 6 Mbps + * 1) 12 Mbps + * 2) 18 Mbps + * 3) 24 Mbps + * 4) 36 Mbps + * 5) 48 Mbps + * 6) 54 Mbps + * 7) 60 Mbps + * + * 4-3: 0) Single stream (SISO) + * 1) Dual stream (MIMO) + * 2) Triple stream (MIMO) + * + * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data + * + * Legacy OFDM rate format for bits 7:0 (bit 8 must be "0", bit 9 "0"): + * 3-0: 0xD) 6 Mbps + * 0xF) 9 Mbps + * 0x5) 12 Mbps + * 0x7) 18 Mbps + * 0x9) 24 Mbps + * 0xB) 36 Mbps + * 0x1) 48 Mbps + * 0x3) 54 Mbps + * + * Legacy CCK rate format for bits 7:0 (bit 8 must be "0", bit 9 "1"): + * 6-0: 10) 1 Mbps + * 20) 2 Mbps + * 55) 5.5 Mbps + * 110) 11 Mbps + */ +#define RATE_MCS_CODE_MSK 0x7 +#define RATE_MCS_SPATIAL_POS 3 +#define RATE_MCS_SPATIAL_MSK 0x18 +#define RATE_MCS_HT_DUP_POS 5 +#define RATE_MCS_HT_DUP_MSK 0x20 + +/* Bit 8: (1) HT format, (0) legacy format in bits 7:0 */ +#define RATE_MCS_FLAGS_POS 8 +#define RATE_MCS_HT_POS 8 +#define RATE_MCS_HT_MSK 0x100 + +/* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ +#define RATE_MCS_CCK_POS 9 +#define RATE_MCS_CCK_MSK 0x200 + +/* Bit 10: (1) Use Green Field preamble */ +#define RATE_MCS_GF_POS 10 +#define RATE_MCS_GF_MSK 0x400 + +/* Bit 11: (1) Use 40Mhz HT40 chnl width, (0) use 20 MHz legacy chnl width */ +#define RATE_MCS_HT40_POS 11 +#define RATE_MCS_HT40_MSK 0x800 + +/* Bit 12: (1) Duplicate data on both 20MHz chnls. HT40 (bit 11) must be set. */ +#define RATE_MCS_DUP_POS 12 +#define RATE_MCS_DUP_MSK 0x1000 + +/* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ +#define RATE_MCS_SGI_POS 13 +#define RATE_MCS_SGI_MSK 0x2000 + +/** + * rate_n_flags Tx antenna masks + * 4965 has 2 transmitters + * bit14:16 + */ +#define RATE_MCS_ANT_POS 14 +#define RATE_MCS_ANT_A_MSK 0x04000 +#define RATE_MCS_ANT_B_MSK 0x08000 +#define RATE_MCS_ANT_C_MSK 0x10000 +#define RATE_MCS_ANT_AB_MSK (RATE_MCS_ANT_A_MSK | RATE_MCS_ANT_B_MSK) +#define RATE_MCS_ANT_ABC_MSK (RATE_MCS_ANT_AB_MSK | RATE_MCS_ANT_C_MSK) +#define RATE_ANT_NUM 3 + +#define POWER_TABLE_NUM_ENTRIES 33 +#define POWER_TABLE_NUM_HT_OFDM_ENTRIES 32 +#define POWER_TABLE_CCK_ENTRY 32 + +#define IWL_PWR_NUM_HT_OFDM_ENTRIES 24 +#define IWL_PWR_CCK_ENTRIES 2 + +/** + * union iwl4965_tx_power_dual_stream + * + * Host format used for REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * Use __le32 version (struct tx_power_dual_stream) when building command. + * + * Driver provides radio gain and DSP attenuation settings to device in pairs, + * one value for each transmitter chain. The first value is for transmitter A, + * second for transmitter B. + * + * For SISO bit rates, both values in a pair should be identical. + * For MIMO rates, one value may be different from the other, + * in order to balance the Tx output between the two transmitters. + * + * See more details in doc for TXPOWER in iwl-4965-hw.h. + */ +union iwl4965_tx_power_dual_stream { + struct { + u8 radio_tx_gain[2]; + u8 dsp_predis_atten[2]; + } s; + u32 dw; +}; + +/** + * struct tx_power_dual_stream + * + * Table entries in REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + * + * Same format as iwl_tx_power_dual_stream, but __le32 + */ +struct tx_power_dual_stream { + __le32 dw; +} __packed; + +/** + * struct iwl4965_tx_power_db + * + * Entire table within REPLY_TX_PWR_TABLE_CMD, REPLY_CHANNEL_SWITCH + */ +struct iwl4965_tx_power_db { + struct tx_power_dual_stream power_tbl[POWER_TABLE_NUM_ENTRIES]; +} __packed; + +/****************************************************************************** + * (0a) + * Alive and Error Commands & Responses: + * + *****************************************************************************/ + +#define UCODE_VALID_OK cpu_to_le32(0x1) +#define INITIALIZE_SUBTYPE (9) + +/* + * ("Initialize") REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "initialize alive" notification once the initialization + * uCode image has completed its work, and is ready to load the runtime image. + * This is the *first* "alive" notification that the driver will receive after + * rebooting uCode; the "initialize" alive is indicated by subtype field == 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * For 4965, this notification contains important calibration data for + * calculating txpower settings: + * + * 1) Power supply voltage indication. The voltage sensor outputs higher + * values for lower voltage, and vice verse. + * + * 2) Temperature measurement parameters, for each of two channel widths + * (20 MHz and 40 MHz) supported by the radios. Temperature sensing + * is done via one of the receiver chains, and channel width influences + * the results. + * + * 3) Tx gain compensation to balance 4965's 2 Tx chains for MIMO operation, + * for each of 5 frequency ranges. + */ +struct iwl_init_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* "9" for initialize alive */ + __le16 reserved2; + __le32 log_event_table_ptr; + __le32 error_event_table_ptr; + __le32 timestamp; + __le32 is_valid; + + /* calibration values from "initialize" uCode */ + __le32 voltage; /* signed, higher value is lower voltage */ + __le32 therm_r1[2]; /* signed, 1st for normal, 2nd for HT40 */ + __le32 therm_r2[2]; /* signed */ + __le32 therm_r3[2]; /* signed */ + __le32 therm_r4[2]; /* signed */ + __le32 tx_atten[5][2]; /* signed MIMO gain comp, 5 freq groups, + * 2 Tx chains */ +} __packed; + + +/** + * REPLY_ALIVE = 0x1 (response only, not a command) + * + * uCode issues this "alive" notification once the runtime image is ready + * to receive commands from the driver. This is the *second* "alive" + * notification that the driver will receive after rebooting uCode; + * this "alive" is indicated by subtype field != 9. + * + * See comments documenting "BSM" (bootstrap state machine). + * + * This response includes two pointers to structures within the device's + * data SRAM (access via HBUS_TARG_MEM_* regs) that are useful for debugging: + * + * 1) log_event_table_ptr indicates base of the event log. This traces + * a 256-entry history of uCode execution within a circular buffer. + * Its header format is: + * + * __le32 log_size; log capacity (in number of entries) + * __le32 type; (1) timestamp with each entry, (0) no timestamp + * __le32 wraps; # times uCode has wrapped to top of circular buffer + * __le32 write_index; next circular buffer entry that uCode would fill + * + * The header is followed by the circular buffer of log entries. Entries + * with timestamps have the following format: + * + * __le32 event_id; range 0 - 1500 + * __le32 timestamp; low 32 bits of TSF (of network, if associated) + * __le32 data; event_id-specific data value + * + * Entries without timestamps contain only event_id and data. + * + * + * 2) error_event_table_ptr indicates base of the error log. This contains + * information about any uCode error that occurs. For 4965, the format + * of the error log is: + * + * __le32 valid; (nonzero) valid, (0) log is empty + * __le32 error_id; type of error + * __le32 pc; program counter + * __le32 blink1; branch link + * __le32 blink2; branch link + * __le32 ilink1; interrupt link + * __le32 ilink2; interrupt link + * __le32 data1; error-specific data + * __le32 data2; error-specific data + * __le32 line; source code line of error + * __le32 bcon_time; beacon timer + * __le32 tsf_low; network timestamp function timer + * __le32 tsf_hi; network timestamp function timer + * __le32 gp1; GP1 timer register + * __le32 gp2; GP2 timer register + * __le32 gp3; GP3 timer register + * __le32 ucode_ver; uCode version + * __le32 hw_ver; HW Silicon version + * __le32 brd_ver; HW board version + * __le32 log_pc; log program counter + * __le32 frame_ptr; frame pointer + * __le32 stack_ptr; stack pointer + * __le32 hcmd; last host command + * __le32 isr0; isr status register LMPM_NIC_ISR0: rxtx_flag + * __le32 isr1; isr status register LMPM_NIC_ISR1: host_flag + * __le32 isr2; isr status register LMPM_NIC_ISR2: enc_flag + * __le32 isr3; isr status register LMPM_NIC_ISR3: time_flag + * __le32 isr4; isr status register LMPM_NIC_ISR4: wico interrupt + * __le32 isr_pref; isr status register LMPM_NIC_PREF_STAT + * __le32 wait_event; wait event() caller address + * __le32 l2p_control; L2pControlField + * __le32 l2p_duration; L2pDurationField + * __le32 l2p_mhvalid; L2pMhValidBits + * __le32 l2p_addr_match; L2pAddrMatchStat + * __le32 lmpm_pmg_sel; indicate which clocks are turned on (LMPM_PMG_SEL) + * __le32 u_timestamp; indicate when the date and time of the compilation + * __le32 reserved; + * + * The Linux driver can print both logs to the system log when a uCode error + * occurs. + */ +struct iwl_alive_resp { + u8 ucode_minor; + u8 ucode_major; + __le16 reserved1; + u8 sw_rev[8]; + u8 ver_type; + u8 ver_subtype; /* not "9" for runtime alive */ + __le16 reserved2; + __le32 log_event_table_ptr; /* SRAM address for event log */ + __le32 error_event_table_ptr; /* SRAM address for error log */ + __le32 timestamp; + __le32 is_valid; +} __packed; + +/* + * REPLY_ERROR = 0x2 (response only, not a command) + */ +struct iwl_error_resp { + __le32 error_type; + u8 cmd_id; + u8 reserved1; + __le16 bad_cmd_seq_num; + __le32 error_info; + __le64 timestamp; +} __packed; + +/****************************************************************************** + * (1) + * RXON Commands & Responses: + * + *****************************************************************************/ + +/* + * Rx config defines & structure + */ +/* rx_config device types */ +enum { + RXON_DEV_TYPE_AP = 1, + RXON_DEV_TYPE_ESS = 3, + RXON_DEV_TYPE_IBSS = 4, + RXON_DEV_TYPE_SNIFFER = 6, +}; + + +#define RXON_RX_CHAIN_DRIVER_FORCE_MSK cpu_to_le16(0x1 << 0) +#define RXON_RX_CHAIN_DRIVER_FORCE_POS (0) +#define RXON_RX_CHAIN_VALID_MSK cpu_to_le16(0x7 << 1) +#define RXON_RX_CHAIN_VALID_POS (1) +#define RXON_RX_CHAIN_FORCE_SEL_MSK cpu_to_le16(0x7 << 4) +#define RXON_RX_CHAIN_FORCE_SEL_POS (4) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_MSK cpu_to_le16(0x7 << 7) +#define RXON_RX_CHAIN_FORCE_MIMO_SEL_POS (7) +#define RXON_RX_CHAIN_CNT_MSK cpu_to_le16(0x3 << 10) +#define RXON_RX_CHAIN_CNT_POS (10) +#define RXON_RX_CHAIN_MIMO_CNT_MSK cpu_to_le16(0x3 << 12) +#define RXON_RX_CHAIN_MIMO_CNT_POS (12) +#define RXON_RX_CHAIN_MIMO_FORCE_MSK cpu_to_le16(0x1 << 14) +#define RXON_RX_CHAIN_MIMO_FORCE_POS (14) + +/* rx_config flags */ +/* band & modulation selection */ +#define RXON_FLG_BAND_24G_MSK cpu_to_le32(1 << 0) +#define RXON_FLG_CCK_MSK cpu_to_le32(1 << 1) +/* auto detection enable */ +#define RXON_FLG_AUTO_DETECT_MSK cpu_to_le32(1 << 2) +/* TGg protection when tx */ +#define RXON_FLG_TGG_PROTECT_MSK cpu_to_le32(1 << 3) +/* cck short slot & preamble */ +#define RXON_FLG_SHORT_SLOT_MSK cpu_to_le32(1 << 4) +#define RXON_FLG_SHORT_PREAMBLE_MSK cpu_to_le32(1 << 5) +/* antenna selection */ +#define RXON_FLG_DIS_DIV_MSK cpu_to_le32(1 << 7) +#define RXON_FLG_ANT_SEL_MSK cpu_to_le32(0x0f00) +#define RXON_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define RXON_FLG_ANT_B_MSK cpu_to_le32(1 << 9) +/* radar detection enable */ +#define RXON_FLG_RADAR_DETECT_MSK cpu_to_le32(1 << 12) +#define RXON_FLG_TGJ_NARROW_BAND_MSK cpu_to_le32(1 << 13) +/* rx response to host with 8-byte TSF +* (according to ON_AIR deassertion) */ +#define RXON_FLG_TSF2HOST_MSK cpu_to_le32(1 << 15) + + +/* HT flags */ +#define RXON_FLG_CTRL_CHANNEL_LOC_POS (22) +#define RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK cpu_to_le32(0x1 << 22) + +#define RXON_FLG_HT_OPERATING_MODE_POS (23) + +#define RXON_FLG_HT_PROT_MSK cpu_to_le32(0x1 << 23) +#define RXON_FLG_HT40_PROT_MSK cpu_to_le32(0x2 << 23) + +#define RXON_FLG_CHANNEL_MODE_POS (25) +#define RXON_FLG_CHANNEL_MODE_MSK cpu_to_le32(0x3 << 25) + +/* channel mode */ +enum { + CHANNEL_MODE_LEGACY = 0, + CHANNEL_MODE_PURE_40 = 1, + CHANNEL_MODE_MIXED = 2, + CHANNEL_MODE_RESERVED = 3, +}; +#define RXON_FLG_CHANNEL_MODE_LEGACY \ + cpu_to_le32(CHANNEL_MODE_LEGACY << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_PURE_40 \ + cpu_to_le32(CHANNEL_MODE_PURE_40 << RXON_FLG_CHANNEL_MODE_POS) +#define RXON_FLG_CHANNEL_MODE_MIXED \ + cpu_to_le32(CHANNEL_MODE_MIXED << RXON_FLG_CHANNEL_MODE_POS) + +/* CTS to self (if spec allows) flag */ +#define RXON_FLG_SELF_CTS_EN cpu_to_le32(0x1<<30) + +/* rx_config filter flags */ +/* accept all data frames */ +#define RXON_FILTER_PROMISC_MSK cpu_to_le32(1 << 0) +/* pass control & management to host */ +#define RXON_FILTER_CTL2HOST_MSK cpu_to_le32(1 << 1) +/* accept multi-cast */ +#define RXON_FILTER_ACCEPT_GRP_MSK cpu_to_le32(1 << 2) +/* don't decrypt uni-cast frames */ +#define RXON_FILTER_DIS_DECRYPT_MSK cpu_to_le32(1 << 3) +/* don't decrypt multi-cast frames */ +#define RXON_FILTER_DIS_GRP_DECRYPT_MSK cpu_to_le32(1 << 4) +/* STA is associated */ +#define RXON_FILTER_ASSOC_MSK cpu_to_le32(1 << 5) +/* transfer to host non bssid beacons in associated state */ +#define RXON_FILTER_BCON_AWARE_MSK cpu_to_le32(1 << 6) + +/** + * REPLY_RXON = 0x10 (command, has simple generic response) + * + * RXON tunes the radio tuner to a service channel, and sets up a number + * of parameters that are used primarily for Rx, but also for Tx operations. + * + * NOTE: When tuning to a new channel, driver must set the + * RXON_FILTER_ASSOC_MSK to 0. This will clear station-dependent + * info within the device, including the station tables, tx retry + * rate tables, and txpower tables. Driver must build a new station + * table and txpower table before transmitting anything on the RXON + * channel. + * + * NOTE: All RXONs wipe clean the internal txpower table. Driver must + * issue a new REPLY_TX_PWR_TABLE_CMD after each REPLY_RXON (0x10), + * regardless of whether RXON_FILTER_ASSOC_MSK is set. + */ + +struct iwl3945_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 reserved4; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + __le16 reserved5; +} __packed; + +struct iwl4965_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; +} __packed; + +/* Create a common rxon cmd which will be typecast into the 3945 or 4965 + * specific rxon cmd, depending on where it is called from. + */ +struct iwl_legacy_rxon_cmd { + u8 node_addr[6]; + __le16 reserved1; + u8 bssid_addr[6]; + __le16 reserved2; + u8 wlap_bssid_addr[6]; + __le16 reserved3; + u8 dev_type; + u8 air_propagation; + __le16 rx_chain; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 assoc_id; + __le32 flags; + __le32 filter_flags; + __le16 channel; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + u8 reserved4; + u8 reserved5; +} __packed; + + +/* + * REPLY_RXON_ASSOC = 0x11 (command, has simple generic response) + */ +struct iwl3945_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + __le16 reserved; +} __packed; + +struct iwl4965_rxon_assoc_cmd { + __le32 flags; + __le32 filter_flags; + u8 ofdm_basic_rates; + u8 cck_basic_rates; + u8 ofdm_ht_single_stream_basic_rates; + u8 ofdm_ht_dual_stream_basic_rates; + __le16 rx_chain_select_flags; + __le16 reserved; +} __packed; + +#define IWL_CONN_MAX_LISTEN_INTERVAL 10 +#define IWL_MAX_UCODE_BEACON_INTERVAL 4 /* 4096 */ +#define IWL39_MAX_UCODE_BEACON_INTERVAL 1 /* 1024 */ + +/* + * REPLY_RXON_TIMING = 0x14 (command, has simple generic response) + */ +struct iwl_rxon_time_cmd { + __le64 timestamp; + __le16 beacon_interval; + __le16 atim_window; + __le32 beacon_init_val; + __le16 listen_interval; + u8 dtim_period; + u8 delta_cp_bss_tbtts; +} __packed; + +/* + * REPLY_CHANNEL_SWITCH = 0x72 (command, has simple generic response) + */ +struct iwl3945_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct iwl3945_power_per_rate power[IWL_MAX_RATES]; +} __packed; + +struct iwl4965_channel_switch_cmd { + u8 band; + u8 expect_beacon; + __le16 channel; + __le32 rxon_flags; + __le32 rxon_filter_flags; + __le32 switch_time; + struct iwl4965_tx_power_db tx_power; +} __packed; + +/* + * CHANNEL_SWITCH_NOTIFICATION = 0x73 (notification only, not a command) + */ +struct iwl_csa_notification { + __le16 band; + __le16 channel; + __le32 status; /* 0 - OK, 1 - fail */ +} __packed; + +/****************************************************************************** + * (2) + * Quality-of-Service (QOS) Commands & Responses: + * + *****************************************************************************/ + +/** + * struct iwl_ac_qos -- QOS timing params for REPLY_QOS_PARAM + * One for each of 4 EDCA access categories in struct iwl_qosparam_cmd + * + * @cw_min: Contention window, start value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x0f. + * @cw_max: Contention window, max value in numbers of slots. + * Should be a power-of-2, minus 1. Device's default is 0x3f. + * @aifsn: Number of slots in Arbitration Interframe Space (before + * performing random backoff timing prior to Tx). Device default 1. + * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. + * + * Device will automatically increase contention window by (2*CW) + 1 for each + * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW + * value, to cap the CW value. + */ +struct iwl_ac_qos { + __le16 cw_min; + __le16 cw_max; + u8 aifsn; + u8 reserved1; + __le16 edca_txop; +} __packed; + +/* QoS flags defines */ +#define QOS_PARAM_FLG_UPDATE_EDCA_MSK cpu_to_le32(0x01) +#define QOS_PARAM_FLG_TGN_MSK cpu_to_le32(0x02) +#define QOS_PARAM_FLG_TXOP_TYPE_MSK cpu_to_le32(0x10) + +/* Number of Access Categories (AC) (EDCA), queues 0..3 */ +#define AC_NUM 4 + +/* + * REPLY_QOS_PARAM = 0x13 (command, has simple generic response) + * + * This command sets up timings for each of the 4 prioritized EDCA Tx FIFOs + * 0: Background, 1: Best Effort, 2: Video, 3: Voice. + */ +struct iwl_qosparam_cmd { + __le32 qos_flags; + struct iwl_ac_qos ac[AC_NUM]; +} __packed; + +/****************************************************************************** + * (3) + * Add/Modify Stations Commands & Responses: + * + *****************************************************************************/ +/* + * Multi station support + */ + +/* Special, dedicated locations within device's station table */ +#define IWL_AP_ID 0 +#define IWL_STA_ID 2 +#define IWL3945_BROADCAST_ID 24 +#define IWL3945_STATION_COUNT 25 +#define IWL4965_BROADCAST_ID 31 +#define IWL4965_STATION_COUNT 32 + +#define IWL_STATION_COUNT 32 /* MAX(3945,4965)*/ +#define IWL_INVALID_STATION 255 + +#define STA_FLG_TX_RATE_MSK cpu_to_le32(1 << 2) +#define STA_FLG_PWR_SAVE_MSK cpu_to_le32(1 << 8) +#define STA_FLG_RTS_MIMO_PROT_MSK cpu_to_le32(1 << 17) +#define STA_FLG_AGG_MPDU_8US_MSK cpu_to_le32(1 << 18) +#define STA_FLG_MAX_AGG_SIZE_POS (19) +#define STA_FLG_MAX_AGG_SIZE_MSK cpu_to_le32(3 << 19) +#define STA_FLG_HT40_EN_MSK cpu_to_le32(1 << 21) +#define STA_FLG_MIMO_DIS_MSK cpu_to_le32(1 << 22) +#define STA_FLG_AGG_MPDU_DENSITY_POS (23) +#define STA_FLG_AGG_MPDU_DENSITY_MSK cpu_to_le32(7 << 23) + +/* Use in mode field. 1: modify existing entry, 0: add new station entry */ +#define STA_CONTROL_MODIFY_MSK 0x01 + +/* key flags __le16*/ +#define STA_KEY_FLG_ENCRYPT_MSK cpu_to_le16(0x0007) +#define STA_KEY_FLG_NO_ENC cpu_to_le16(0x0000) +#define STA_KEY_FLG_WEP cpu_to_le16(0x0001) +#define STA_KEY_FLG_CCMP cpu_to_le16(0x0002) +#define STA_KEY_FLG_TKIP cpu_to_le16(0x0003) + +#define STA_KEY_FLG_KEYID_POS 8 +#define STA_KEY_FLG_INVALID cpu_to_le16(0x0800) +/* wep key is either from global key (0) or from station info array (1) */ +#define STA_KEY_FLG_MAP_KEY_MSK cpu_to_le16(0x0008) + +/* wep key in STA: 5-bytes (0) or 13-bytes (1) */ +#define STA_KEY_FLG_KEY_SIZE_MSK cpu_to_le16(0x1000) +#define STA_KEY_MULTICAST_MSK cpu_to_le16(0x4000) +#define STA_KEY_MAX_NUM 8 + +/* Flags indicate whether to modify vs. don't change various station params */ +#define STA_MODIFY_KEY_MASK 0x01 +#define STA_MODIFY_TID_DISABLE_TX 0x02 +#define STA_MODIFY_TX_RATE_MSK 0x04 +#define STA_MODIFY_ADDBA_TID_MSK 0x08 +#define STA_MODIFY_DELBA_TID_MSK 0x10 +#define STA_MODIFY_SLEEP_TX_COUNT_MSK 0x20 + +/* Receiver address (actually, Rx station's index into station table), + * combined with Traffic ID (QOS priority), in format used by Tx Scheduler */ +#define BUILD_RAxTID(sta_id, tid) (((sta_id) << 4) + (tid)) + +struct iwl4965_keyinfo { + __le16 key_flags; + u8 tkip_rx_tsc_byte2; /* TSC[2] for key mix ph1 detection */ + u8 reserved1; + __le16 tkip_rx_ttak[5]; /* 10-byte unicast TKIP TTAK */ + u8 key_offset; + u8 reserved2; + u8 key[16]; /* 16-byte unicast decryption key */ +} __packed; + +/** + * struct sta_id_modify + * @addr[ETH_ALEN]: station's MAC address + * @sta_id: index of station in uCode's station table + * @modify_mask: STA_MODIFY_*, 1: modify, 0: don't change + * + * Driver selects unused table index when adding new station, + * or the index to a pre-existing station entry when modifying that station. + * Some indexes have special purposes (IWL_AP_ID, index 0, is for AP). + * + * modify_mask flags select which parameters to modify vs. leave alone. + */ +struct sta_id_modify { + u8 addr[ETH_ALEN]; + __le16 reserved1; + u8 sta_id; + u8 modify_mask; + __le16 reserved2; +} __packed; + +/* + * REPLY_ADD_STA = 0x18 (command) + * + * The device contains an internal table of per-station information, + * with info on security keys, aggregation parameters, and Tx rates for + * initial Tx attempt and any retries (4965 devices uses + * REPLY_TX_LINK_QUALITY_CMD, + * 3945 uses REPLY_RATE_SCALE to set up rate tables). + * + * REPLY_ADD_STA sets up the table entry for one station, either creating + * a new entry, or modifying a pre-existing one. + * + * NOTE: RXON command (without "associated" bit set) wipes the station table + * clean. Moving into RF_KILL state does this also. Driver must set up + * new station table before transmitting anything on the RXON channel + * (except active scans or active measurements; those commands carry + * their own txpower/rate setup data). + * + * When getting started on a new channel, driver must set up the + * IWL_BROADCAST_ID entry (last entry in the table). For a client + * station in a BSS, once an AP is selected, driver sets up the AP STA + * in the IWL_AP_ID entry (1st entry in the table). BROADCAST and AP + * are all that are needed for a BSS client station. If the device is + * used as AP, or in an IBSS network, driver must set up station table + * entries for all STAs in network, starting with index IWL_STA_ID. + */ + +struct iwl3945_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 rate_n_flags; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; +} __packed; + +struct iwl4965_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 reserved1; + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + +/* Wrapper struct for 3945 and 4965 addsta_cmd structures */ +struct iwl_legacy_addsta_cmd { + u8 mode; /* 1: modify existing, 0: add new station */ + u8 reserved[3]; + struct sta_id_modify sta; + struct iwl4965_keyinfo key; + __le32 station_flags; /* STA_FLG_* */ + __le32 station_flags_msk; /* STA_FLG_* */ + + /* bit field to disable (1) or enable (0) Tx for Traffic ID (TID) + * corresponding to bit (e.g. bit 5 controls TID 5). + * Set modify_mask bit STA_MODIFY_TID_DISABLE_TX to use this field. */ + __le16 tid_disable_tx; + + __le16 rate_n_flags; /* 3945 only */ + + /* TID for which to add block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + u8 add_immediate_ba_tid; + + /* TID for which to remove block-ack support. + * Set modify_mask bit STA_MODIFY_DELBA_TID_MSK to use this field. */ + u8 remove_immediate_ba_tid; + + /* Starting Sequence Number for added block-ack support. + * Set modify_mask bit STA_MODIFY_ADDBA_TID_MSK to use this field. */ + __le16 add_immediate_ba_ssn; + + /* + * Number of packets OK to transmit to station even though + * it is asleep -- used to synchronise PS-poll and u-APSD + * responses while ucode keeps track of STA sleep state. + */ + __le16 sleep_tx_count; + + __le16 reserved2; +} __packed; + + +#define ADD_STA_SUCCESS_MSK 0x1 +#define ADD_STA_NO_ROOM_IN_TABLE 0x2 +#define ADD_STA_NO_BLOCK_ACK_RESOURCE 0x4 +#define ADD_STA_MODIFY_NON_EXIST_STA 0x8 +/* + * REPLY_ADD_STA = 0x18 (response) + */ +struct iwl_add_sta_resp { + u8 status; /* ADD_STA_* */ +} __packed; + +#define REM_STA_SUCCESS_MSK 0x1 +/* + * REPLY_REM_STA = 0x19 (response) + */ +struct iwl_rem_sta_resp { + u8 status; +} __packed; + +/* + * REPLY_REM_STA = 0x19 (command) + */ +struct iwl_rem_sta_cmd { + u8 num_sta; /* number of removed stations */ + u8 reserved[3]; + u8 addr[ETH_ALEN]; /* MAC addr of the first station */ + u8 reserved2[2]; +} __packed; + +#define IWL_TX_FIFO_BK_MSK cpu_to_le32(BIT(0)) +#define IWL_TX_FIFO_BE_MSK cpu_to_le32(BIT(1)) +#define IWL_TX_FIFO_VI_MSK cpu_to_le32(BIT(2)) +#define IWL_TX_FIFO_VO_MSK cpu_to_le32(BIT(3)) +#define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) + +#define IWL_DROP_SINGLE 0 +#define IWL_DROP_SELECTED 1 +#define IWL_DROP_ALL 2 + +/* + * REPLY_WEP_KEY = 0x20 + */ +struct iwl_wep_key { + u8 key_index; + u8 key_offset; + u8 reserved1[2]; + u8 key_size; + u8 reserved2[3]; + u8 key[16]; +} __packed; + +struct iwl_wep_cmd { + u8 num_keys; + u8 global_key_type; + u8 flags; + u8 reserved; + struct iwl_wep_key key[0]; +} __packed; + +#define WEP_KEY_WEP_TYPE 1 +#define WEP_KEYS_MAX 4 +#define WEP_INVALID_OFFSET 0xff +#define WEP_KEY_LEN_64 5 +#define WEP_KEY_LEN_128 13 + +/****************************************************************************** + * (4) + * Rx Responses: + * + *****************************************************************************/ + +#define RX_RES_STATUS_NO_CRC32_ERROR cpu_to_le32(1 << 0) +#define RX_RES_STATUS_NO_RXE_OVERFLOW cpu_to_le32(1 << 1) + +#define RX_RES_PHY_FLAGS_BAND_24_MSK cpu_to_le16(1 << 0) +#define RX_RES_PHY_FLAGS_MOD_CCK_MSK cpu_to_le16(1 << 1) +#define RX_RES_PHY_FLAGS_SHORT_PREAMBLE_MSK cpu_to_le16(1 << 2) +#define RX_RES_PHY_FLAGS_NARROW_BAND_MSK cpu_to_le16(1 << 3) +#define RX_RES_PHY_FLAGS_ANTENNA_MSK 0xf0 +#define RX_RES_PHY_FLAGS_ANTENNA_POS 4 + +#define RX_RES_STATUS_SEC_TYPE_MSK (0x7 << 8) +#define RX_RES_STATUS_SEC_TYPE_NONE (0x0 << 8) +#define RX_RES_STATUS_SEC_TYPE_WEP (0x1 << 8) +#define RX_RES_STATUS_SEC_TYPE_CCMP (0x2 << 8) +#define RX_RES_STATUS_SEC_TYPE_TKIP (0x3 << 8) +#define RX_RES_STATUS_SEC_TYPE_ERR (0x7 << 8) + +#define RX_RES_STATUS_STATION_FOUND (1<<6) +#define RX_RES_STATUS_NO_STATION_INFO_MISMATCH (1<<7) + +#define RX_RES_STATUS_DECRYPT_TYPE_MSK (0x3 << 11) +#define RX_RES_STATUS_NOT_DECRYPT (0x0 << 11) +#define RX_RES_STATUS_DECRYPT_OK (0x3 << 11) +#define RX_RES_STATUS_BAD_ICV_MIC (0x1 << 11) +#define RX_RES_STATUS_BAD_KEY_TTAK (0x2 << 11) + +#define RX_MPDU_RES_STATUS_ICV_OK (0x20) +#define RX_MPDU_RES_STATUS_MIC_OK (0x40) +#define RX_MPDU_RES_STATUS_TTAK_OK (1 << 7) +#define RX_MPDU_RES_STATUS_DEC_DONE_MSK (0x800) + + +struct iwl3945_rx_frame_stats { + u8 phy_count; + u8 id; + u8 rssi; + u8 agc; + __le16 sig_avg; + __le16 noise_diff; + u8 payload[0]; +} __packed; + +struct iwl3945_rx_frame_hdr { + __le16 channel; + __le16 phy_flags; + u8 reserved1; + u8 rate; + __le16 len; + u8 payload[0]; +} __packed; + +struct iwl3945_rx_frame_end { + __le32 status; + __le64 timestamp; + __le32 beacon_timestamp; +} __packed; + +/* + * REPLY_3945_RX = 0x1b (response only, not a command) + * + * NOTE: DO NOT dereference from casts to this structure + * It is provided only for calculating minimum data set size. + * The actual offsets of the hdr and end are dynamic based on + * stats.phy_count + */ +struct iwl3945_rx_frame { + struct iwl3945_rx_frame_stats stats; + struct iwl3945_rx_frame_hdr hdr; + struct iwl3945_rx_frame_end end; +} __packed; + +#define IWL39_RX_FRAME_SIZE (4 + sizeof(struct iwl3945_rx_frame)) + +/* Fixed (non-configurable) rx data from phy */ + +#define IWL49_RX_RES_PHY_CNT 14 +#define IWL49_RX_PHY_FLAGS_ANTENNAE_OFFSET (4) +#define IWL49_RX_PHY_FLAGS_ANTENNAE_MASK (0x70) +#define IWL49_AGC_DB_MASK (0x3f80) /* MASK(7,13) */ +#define IWL49_AGC_DB_POS (7) +struct iwl4965_rx_non_cfg_phy { + __le16 ant_selection; /* ant A bit 4, ant B bit 5, ant C bit 6 */ + __le16 agc_info; /* agc code 0:6, agc dB 7:13, reserved 14:15 */ + u8 rssi_info[6]; /* we use even entries, 0/2/4 for A/B/C rssi */ + u8 pad[0]; +} __packed; + + +/* + * REPLY_RX = 0xc3 (response only, not a command) + * Used only for legacy (non 11n) frames. + */ +struct iwl_rx_phy_res { + u8 non_cfg_phy_cnt; /* non configurable DSP phy data byte count */ + u8 cfg_phy_cnt; /* configurable DSP phy data byte count */ + u8 stat_id; /* configurable DSP phy data set ID */ + u8 reserved1; + __le64 timestamp; /* TSF at on air rise */ + __le32 beacon_time_stamp; /* beacon at on-air rise */ + __le16 phy_flags; /* general phy flags: band, modulation, ... */ + __le16 channel; /* channel number */ + u8 non_cfg_phy_buf[32]; /* for various implementations of non_cfg_phy */ + __le32 rate_n_flags; /* RATE_MCS_* */ + __le16 byte_count; /* frame's byte-count */ + __le16 frame_time; /* frame's time on the air */ +} __packed; + +struct iwl_rx_mpdu_res_start { + __le16 byte_count; + __le16 reserved; +} __packed; + + +/****************************************************************************** + * (5) + * Tx Commands & Responses: + * + * Driver must place each REPLY_TX command into one of the prioritized Tx + * queues in host DRAM, shared between driver and device (see comments for + * SCD registers and Tx/Rx Queues). When the device's Tx scheduler and uCode + * are preparing to transmit, the device pulls the Tx command over the PCI + * bus via one of the device's Tx DMA channels, to fill an internal FIFO + * from which data will be transmitted. + * + * uCode handles all timing and protocol related to control frames + * (RTS/CTS/ACK), based on flags in the Tx command. uCode and Tx scheduler + * handle reception of block-acks; uCode updates the host driver via + * REPLY_COMPRESSED_BA. + * + * uCode handles retrying Tx when an ACK is expected but not received. + * This includes trying lower data rates than the one requested in the Tx + * command, as set up by the REPLY_RATE_SCALE (for 3945) or + * REPLY_TX_LINK_QUALITY_CMD (4965). + * + * Driver sets up transmit power for various rates via REPLY_TX_PWR_TABLE_CMD. + * This command must be executed after every RXON command, before Tx can occur. + *****************************************************************************/ + +/* REPLY_TX Tx flags field */ + +/* + * 1: Use Request-To-Send protocol before this frame. + * Mutually exclusive vs. TX_CMD_FLG_CTS_MSK. + */ +#define TX_CMD_FLG_RTS_MSK cpu_to_le32(1 << 1) + +/* + * 1: Transmit Clear-To-Send to self before this frame. + * Driver should set this for AUTH/DEAUTH/ASSOC-REQ/REASSOC mgmnt frames. + * Mutually exclusive vs. TX_CMD_FLG_RTS_MSK. + */ +#define TX_CMD_FLG_CTS_MSK cpu_to_le32(1 << 2) + +/* 1: Expect ACK from receiving station + * 0: Don't expect ACK (MAC header's duration field s/b 0) + * Set this for unicast frames, but not broadcast/multicast. */ +#define TX_CMD_FLG_ACK_MSK cpu_to_le32(1 << 3) + +/* For 4965 devices: + * 1: Use rate scale table (see REPLY_TX_LINK_QUALITY_CMD). + * Tx command's initial_rate_index indicates first rate to try; + * uCode walks through table for additional Tx attempts. + * 0: Use Tx rate/MCS from Tx command's rate_n_flags field. + * This rate will be used for all Tx attempts; it will not be scaled. */ +#define TX_CMD_FLG_STA_RATE_MSK cpu_to_le32(1 << 4) + +/* 1: Expect immediate block-ack. + * Set when Txing a block-ack request frame. Also set TX_CMD_FLG_ACK_MSK. */ +#define TX_CMD_FLG_IMM_BA_RSP_MASK cpu_to_le32(1 << 6) + +/* + * 1: Frame requires full Tx-Op protection. + * Set this if either RTS or CTS Tx Flag gets set. + */ +#define TX_CMD_FLG_FULL_TXOP_PROT_MSK cpu_to_le32(1 << 7) + +/* Tx antenna selection field; used only for 3945, reserved (0) for 4965 devices. + * Set field to "0" to allow 3945 uCode to select antenna (normal usage). */ +#define TX_CMD_FLG_ANT_SEL_MSK cpu_to_le32(0xf00) +#define TX_CMD_FLG_ANT_A_MSK cpu_to_le32(1 << 8) +#define TX_CMD_FLG_ANT_B_MSK cpu_to_le32(1 << 9) + +/* 1: uCode overrides sequence control field in MAC header. + * 0: Driver provides sequence control field in MAC header. + * Set this for management frames, non-QOS data frames, non-unicast frames, + * and also in Tx command embedded in REPLY_SCAN_CMD for active scans. */ +#define TX_CMD_FLG_SEQ_CTL_MSK cpu_to_le32(1 << 13) + +/* 1: This frame is non-last MPDU; more fragments are coming. + * 0: Last fragment, or not using fragmentation. */ +#define TX_CMD_FLG_MORE_FRAG_MSK cpu_to_le32(1 << 14) + +/* 1: uCode calculates and inserts Timestamp Function (TSF) in outgoing frame. + * 0: No TSF required in outgoing frame. + * Set this for transmitting beacons and probe responses. */ +#define TX_CMD_FLG_TSF_MSK cpu_to_le32(1 << 16) + +/* 1: Driver inserted 2 bytes pad after the MAC header, for (required) dword + * alignment of frame's payload data field. + * 0: No pad + * Set this for MAC headers with 26 or 30 bytes, i.e. those with QOS or ADDR4 + * field (but not both). Driver must align frame data (i.e. data following + * MAC header) to DWORD boundary. */ +#define TX_CMD_FLG_MH_PAD_MSK cpu_to_le32(1 << 20) + +/* accelerate aggregation support + * 0 - no CCMP encryption; 1 - CCMP encryption */ +#define TX_CMD_FLG_AGG_CCMP_MSK cpu_to_le32(1 << 22) + +/* HCCA-AP - disable duration overwriting. */ +#define TX_CMD_FLG_DUR_MSK cpu_to_le32(1 << 25) + + +/* + * TX command security control + */ +#define TX_CMD_SEC_WEP 0x01 +#define TX_CMD_SEC_CCM 0x02 +#define TX_CMD_SEC_TKIP 0x03 +#define TX_CMD_SEC_MSK 0x03 +#define TX_CMD_SEC_SHIFT 6 +#define TX_CMD_SEC_KEY128 0x08 + +/* + * security overhead sizes + */ +#define WEP_IV_LEN 4 +#define WEP_ICV_LEN 4 +#define CCMP_MIC_LEN 8 +#define TKIP_ICV_LEN 4 + +/* + * REPLY_TX = 0x1c (command) + */ + +struct iwl3945_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + u8 rate; + + /* Index of recipient station in uCode's station table */ + u8 sta_id; + u8 tid_tspec; + u8 sec_ctl; + u8 key[16]; + union { + u8 byte[8]; + __le16 word[4]; + __le32 dw[2]; + } tkip_mic; + __le32 next_frame_info; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + u8 supp_rates[2]; + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* + * REPLY_TX = 0x1c (response) + */ +struct iwl3945_tx_resp { + u8 failure_rts; + u8 failure_frame; + u8 bt_kill_count; + u8 rate; + __le32 wireless_media_time; + __le32 status; /* TX status */ +} __packed; + + +/* + * 4965 uCode updates these Tx attempt count values in host DRAM. + * Used for managing Tx retries when expecting block-acks. + * Driver should set these fields to 0. + */ +struct iwl_dram_scratch { + u8 try_cnt; /* Tx attempts */ + u8 bt_kill_cnt; /* Tx attempts blocked by Bluetooth device */ + __le16 reserved; +} __packed; + +struct iwl_tx_cmd { + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + /* + * MAC header goes here, followed by 2 bytes padding if MAC header + * length is 26 or 30 bytes, followed by payload data + */ + u8 payload[0]; + struct ieee80211_hdr hdr[0]; +} __packed; + +/* TX command response is sent after *3945* transmission attempts. + * + * NOTES: + * + * TX_STATUS_FAIL_NEXT_FRAG + * + * If the fragment flag in the MAC header for the frame being transmitted + * is set and there is insufficient time to transmit the next frame, the + * TX status will be returned with 'TX_STATUS_FAIL_NEXT_FRAG'. + * + * TX_STATUS_FIFO_UNDERRUN + * + * Indicates the host did not provide bytes to the FIFO fast enough while + * a TX was in progress. + * + * TX_STATUS_FAIL_MGMNT_ABORT + * + * This status is only possible if the ABORT ON MGMT RX parameter was + * set to true with the TX command. + * + * If the MSB of the status parameter is set then an abort sequence is + * required. This sequence consists of the host activating the TX Abort + * control line, and then waiting for the TX Abort command response. This + * indicates that a the device is no longer in a transmit state, and that the + * command FIFO has been cleared. The host must then deactivate the TX Abort + * control line. Receiving is still allowed in this case. + */ +enum { + TX_3945_STATUS_SUCCESS = 0x01, + TX_3945_STATUS_DIRECT_DONE = 0x02, + TX_3945_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_3945_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_3945_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_3945_STATUS_FAIL_MGMNT_ABORT = 0x85, + TX_3945_STATUS_FAIL_NEXT_FRAG = 0x86, + TX_3945_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_3945_STATUS_FAIL_DEST_PS = 0x88, + TX_3945_STATUS_FAIL_ABORTED = 0x89, + TX_3945_STATUS_FAIL_BT_RETRY = 0x8a, + TX_3945_STATUS_FAIL_STA_INVALID = 0x8b, + TX_3945_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_3945_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_3945_STATUS_FAIL_FRAME_FLUSHED = 0x8e, + TX_3945_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_3945_STATUS_FAIL_TX_LOCKED = 0x90, + TX_3945_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +/* + * TX command response is sent after *4965* transmission attempts. + * + * both postpone and abort status are expected behavior from uCode. there is + * no special operation required from driver; except for RFKILL_FLUSH, + * which required tx flush host command to flush all the tx frames in queues + */ +enum { + TX_STATUS_SUCCESS = 0x01, + TX_STATUS_DIRECT_DONE = 0x02, + /* postpone TX */ + TX_STATUS_POSTPONE_DELAY = 0x40, + TX_STATUS_POSTPONE_FEW_BYTES = 0x41, + TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, + TX_STATUS_POSTPONE_CALC_TTAK = 0x44, + /* abort TX */ + TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, + TX_STATUS_FAIL_SHORT_LIMIT = 0x82, + TX_STATUS_FAIL_LONG_LIMIT = 0x83, + TX_STATUS_FAIL_FIFO_UNDERRUN = 0x84, + TX_STATUS_FAIL_DRAIN_FLOW = 0x85, + TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, + TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, + TX_STATUS_FAIL_DEST_PS = 0x88, + TX_STATUS_FAIL_HOST_ABORTED = 0x89, + TX_STATUS_FAIL_BT_RETRY = 0x8a, + TX_STATUS_FAIL_STA_INVALID = 0x8b, + TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, + TX_STATUS_FAIL_TID_DISABLE = 0x8d, + TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, + TX_STATUS_FAIL_INSUFFICIENT_CF_POLL = 0x8f, + TX_STATUS_FAIL_PASSIVE_NO_RX = 0x90, + TX_STATUS_FAIL_NO_BEACON_ON_RADAR = 0x91, +}; + +#define TX_PACKET_MODE_REGULAR 0x0000 +#define TX_PACKET_MODE_BURST_SEQ 0x0100 +#define TX_PACKET_MODE_BURST_FIRST 0x0200 + +enum { + TX_POWER_PA_NOT_ACTIVE = 0x0, +}; + +enum { + TX_STATUS_MSK = 0x000000ff, /* bits 0:7 */ + TX_STATUS_DELAY_MSK = 0x00000040, + TX_STATUS_ABORT_MSK = 0x00000080, + TX_PACKET_MODE_MSK = 0x0000ff00, /* bits 8:15 */ + TX_FIFO_NUMBER_MSK = 0x00070000, /* bits 16:18 */ + TX_RESERVED = 0x00780000, /* bits 19:22 */ + TX_POWER_PA_DETECT_MSK = 0x7f800000, /* bits 23:30 */ + TX_ABORT_REQUIRED_MSK = 0x80000000, /* bits 31:31 */ +}; + +/* ******************************* + * TX aggregation status + ******************************* */ + +enum { + AGG_TX_STATE_TRANSMITTED = 0x00, + AGG_TX_STATE_UNDERRUN_MSK = 0x01, + AGG_TX_STATE_FEW_BYTES_MSK = 0x04, + AGG_TX_STATE_ABORT_MSK = 0x08, + AGG_TX_STATE_LAST_SENT_TTL_MSK = 0x10, + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK = 0x20, + AGG_TX_STATE_SCD_QUERY_MSK = 0x80, + AGG_TX_STATE_TEST_BAD_CRC32_MSK = 0x100, + AGG_TX_STATE_RESPONSE_MSK = 0x1ff, + AGG_TX_STATE_DUMP_TX_MSK = 0x200, + AGG_TX_STATE_DELAY_TX_MSK = 0x400 +}; + +#define AGG_TX_STATUS_MSK 0x00000fff /* bits 0:11 */ +#define AGG_TX_TRY_MSK 0x0000f000 /* bits 12:15 */ + +#define AGG_TX_STATE_LAST_SENT_MSK (AGG_TX_STATE_LAST_SENT_TTL_MSK | \ + AGG_TX_STATE_LAST_SENT_TRY_CNT_MSK) + +/* # tx attempts for first frame in aggregation */ +#define AGG_TX_STATE_TRY_CNT_POS 12 +#define AGG_TX_STATE_TRY_CNT_MSK 0xf000 + +/* Command ID and sequence number of Tx command for this frame */ +#define AGG_TX_STATE_SEQ_NUM_POS 16 +#define AGG_TX_STATE_SEQ_NUM_MSK 0xffff0000 + +/* + * REPLY_TX = 0x1c (response) + * + * This response may be in one of two slightly different formats, indicated + * by the frame_count field: + * + * 1) No aggregation (frame_count == 1). This reports Tx results for + * a single frame. Multiple attempts, at various bit rates, may have + * been made for this frame. + * + * 2) Aggregation (frame_count > 1). This reports Tx results for + * 2 or more frames that used block-acknowledge. All frames were + * transmitted at same rate. Rate scaling may have been used if first + * frame in this new agg block failed in previous agg block(s). + * + * Note that, for aggregation, ACK (block-ack) status is not delivered here; + * block-ack has not been received by the time the 4965 device records + * this status. + * This status relates to reasons the tx might have been blocked or aborted + * within the sending station (this 4965 device), rather than whether it was + * received successfully by the destination station. + */ +struct agg_tx_status { + __le16 status; + __le16 sequence; +} __packed; + +struct iwl4965_tx_resp { + u8 frame_count; /* 1 no aggregation, >1 aggregation */ + u8 bt_kill_count; /* # blocked by bluetooth (unused for agg) */ + u8 failure_rts; /* # failures due to unsuccessful RTS */ + u8 failure_frame; /* # failures due to no ACK (unused for agg) */ + + /* For non-agg: Rate at which frame was successful. + * For agg: Rate at which all frames were transmitted. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* For non-agg: RTS + CTS + frame tx attempts time + ACK. + * For agg: RTS + CTS + aggregation tx time + block-ack time. */ + __le16 wireless_media_time; /* uSecs */ + + __le16 reserved; + __le32 pa_power1; /* RF power amplifier measurement (not used) */ + __le32 pa_power2; + + /* + * For non-agg: frame status TX_STATUS_* + * For agg: status of 1st frame, AGG_TX_STATE_*; other frame status + * fields follow this one, up to frame_count. + * Bit fields: + * 11- 0: AGG_TX_STATE_* status code + * 15-12: Retry count for 1st frame in aggregation (retries + * occur if tx failed for this frame when it was a + * member of a previous aggregation block). If rate + * scaling is used, retry count indicates the rate + * table entry used for all frames in the new agg. + * 31-16: Sequence # for this frame's Tx cmd (not SSN!) + */ + union { + __le32 status; + struct agg_tx_status agg_status[0]; /* for each agg frame */ + } u; +} __packed; + +/* + * REPLY_COMPRESSED_BA = 0xc5 (response only, not a command) + * + * Reports Block-Acknowledge from recipient station + */ +struct iwl_compressed_ba_resp { + __le32 sta_addr_lo32; + __le16 sta_addr_hi16; + __le16 reserved; + + /* Index of recipient (BA-sending) station in uCode's station table */ + u8 sta_id; + u8 tid; + __le16 seq_ctl; + __le64 bitmap; + __le16 scd_flow; + __le16 scd_ssn; +} __packed; + +/* + * REPLY_TX_PWR_TABLE_CMD = 0x97 (command, has simple generic response) + * + * See details under "TXPOWER" in iwl-4965-hw.h. + */ + +struct iwl3945_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct iwl3945_power_per_rate power[IWL_MAX_RATES]; +} __packed; + +struct iwl4965_txpowertable_cmd { + u8 band; /* 0: 5 GHz, 1: 2.4 GHz */ + u8 reserved; + __le16 channel; + struct iwl4965_tx_power_db tx_power; +} __packed; + + +/** + * struct iwl3945_rate_scaling_cmd - Rate Scaling Command & Response + * + * REPLY_RATE_SCALE = 0x47 (command, has simple generic response) + * + * NOTE: The table of rates passed to the uCode via the + * RATE_SCALE command sets up the corresponding order of + * rates used for all related commands, including rate + * masks, etc. + * + * For example, if you set 9MB (PLCP 0x0f) as the first + * rate in the rate table, the bit mask for that rate + * when passed through ofdm_basic_rates on the REPLY_RXON + * command would be bit 0 (1 << 0) + */ +struct iwl3945_rate_scaling_info { + __le16 rate_n_flags; + u8 try_cnt; + u8 next_rate_index; +} __packed; + +struct iwl3945_rate_scaling_cmd { + u8 table_id; + u8 reserved[3]; + struct iwl3945_rate_scaling_info table[IWL_MAX_RATES]; +} __packed; + + +/*RS_NEW_API: only TLC_RTS remains and moved to bit 0 */ +#define LINK_QUAL_FLAGS_SET_STA_TLC_RTS_MSK (1 << 0) + +/* # of EDCA prioritized tx fifos */ +#define LINK_QUAL_AC_NUM AC_NUM + +/* # entries in rate scale table to support Tx retries */ +#define LINK_QUAL_MAX_RETRY_NUM 16 + +/* Tx antenna selection values */ +#define LINK_QUAL_ANT_A_MSK (1 << 0) +#define LINK_QUAL_ANT_B_MSK (1 << 1) +#define LINK_QUAL_ANT_MSK (LINK_QUAL_ANT_A_MSK|LINK_QUAL_ANT_B_MSK) + + +/** + * struct iwl_link_qual_general_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_general_params { + u8 flags; + + /* No entries at or above this (driver chosen) index contain MIMO */ + u8 mimo_delimiter; + + /* Best single antenna to use for single stream (legacy, SISO). */ + u8 single_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* Best antennas to use for MIMO (unused for 4965, assumes both). */ + u8 dual_stream_ant_msk; /* LINK_QUAL_ANT_* */ + + /* + * If driver needs to use different initial rates for different + * EDCA QOS access categories (as implemented by tx fifos 0-3), + * this table will set that up, by indicating the indexes in the + * rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table at which to start. + * Otherwise, driver should set all entries to 0. + * + * Entry usage: + * 0 = Background, 1 = Best Effort (normal), 2 = Video, 3 = Voice + * TX FIFOs above 3 use same value (typically 0) as TX FIFO 3. + */ + u8 start_rate_index[LINK_QUAL_AC_NUM]; +} __packed; + +#define LINK_QUAL_AGG_TIME_LIMIT_DEF (4000) /* 4 milliseconds */ +#define LINK_QUAL_AGG_TIME_LIMIT_MAX (8000) +#define LINK_QUAL_AGG_TIME_LIMIT_MIN (100) + +#define LINK_QUAL_AGG_DISABLE_START_DEF (3) +#define LINK_QUAL_AGG_DISABLE_START_MAX (255) +#define LINK_QUAL_AGG_DISABLE_START_MIN (0) + +#define LINK_QUAL_AGG_FRAME_LIMIT_DEF (31) +#define LINK_QUAL_AGG_FRAME_LIMIT_MAX (63) +#define LINK_QUAL_AGG_FRAME_LIMIT_MIN (0) + +/** + * struct iwl_link_qual_agg_params + * + * Used in REPLY_TX_LINK_QUALITY_CMD + */ +struct iwl_link_qual_agg_params { + + /* + *Maximum number of uSec in aggregation. + * default set to 4000 (4 milliseconds) if not configured in .cfg + */ + __le16 agg_time_limit; + + /* + * Number of Tx retries allowed for a frame, before that frame will + * no longer be considered for the start of an aggregation sequence + * (scheduler will then try to tx it as single frame). + * Driver should set this to 3. + */ + u8 agg_dis_start_th; + + /* + * Maximum number of frames in aggregation. + * 0 = no limit (default). 1 = no aggregation. + * Other values = max # frames in aggregation. + */ + u8 agg_frame_cnt_limit; + + __le32 reserved; +} __packed; + +/* + * REPLY_TX_LINK_QUALITY_CMD = 0x4e (command, has simple generic response) + * + * For 4965 devices only; 3945 uses REPLY_RATE_SCALE. + * + * Each station in the 4965 device's internal station table has its own table + * of 16 + * Tx rates and modulation modes (e.g. legacy/SISO/MIMO) for retrying Tx when + * an ACK is not received. This command replaces the entire table for + * one station. + * + * NOTE: Station must already be in 4965 device's station table. + * Use REPLY_ADD_STA. + * + * The rate scaling procedures described below work well. Of course, other + * procedures are possible, and may work better for particular environments. + * + * + * FILLING THE RATE TABLE + * + * Given a particular initial rate and mode, as determined by the rate + * scaling algorithm described below, the Linux driver uses the following + * formula to fill the rs_table[LINK_QUAL_MAX_RETRY_NUM] rate table in the + * Link Quality command: + * + * + * 1) If using High-throughput (HT) (SISO or MIMO) initial rate: + * a) Use this same initial rate for first 3 entries. + * b) Find next lower available rate using same mode (SISO or MIMO), + * use for next 3 entries. If no lower rate available, switch to + * legacy mode (no HT40 channel, no MIMO, no short guard interval). + * c) If using MIMO, set command's mimo_delimiter to number of entries + * using MIMO (3 or 6). + * d) After trying 2 HT rates, switch to legacy mode (no HT40 channel, + * no MIMO, no short guard interval), at the next lower bit rate + * (e.g. if second HT bit rate was 54, try 48 legacy), and follow + * legacy procedure for remaining table entries. + * + * 2) If using legacy initial rate: + * a) Use the initial rate for only one entry. + * b) For each following entry, reduce the rate to next lower available + * rate, until reaching the lowest available rate. + * c) When reducing rate, also switch antenna selection. + * d) Once lowest available rate is reached, repeat this rate until + * rate table is filled (16 entries), switching antenna each entry. + * + * + * ACCUMULATING HISTORY + * + * The rate scaling algorithm for 4965 devices, as implemented in Linux driver, + * uses two sets of frame Tx success history: One for the current/active + * modulation mode, and one for a speculative/search mode that is being + * attempted. If the speculative mode turns out to be more effective (i.e. + * actual transfer rate is better), then the driver continues to use the + * speculative mode as the new current active mode. + * + * Each history set contains, separately for each possible rate, data for a + * sliding window of the 62 most recent tx attempts at that rate. The data + * includes a shifting bitmap of success(1)/failure(0), and sums of successful + * and attempted frames, from which the driver can additionally calculate a + * success ratio (success / attempted) and number of failures + * (attempted - success), and control the size of the window (attempted). + * The driver uses the bit map to remove successes from the success sum, as + * the oldest tx attempts fall out of the window. + * + * When the 4965 device makes multiple tx attempts for a given frame, each + * attempt might be at a different rate, and have different modulation + * characteristics (e.g. antenna, fat channel, short guard interval), as set + * up in the rate scaling table in the Link Quality command. The driver must + * determine which rate table entry was used for each tx attempt, to determine + * which rate-specific history to update, and record only those attempts that + * match the modulation characteristics of the history set. + * + * When using block-ack (aggregation), all frames are transmitted at the same + * rate, since there is no per-attempt acknowledgment from the destination + * station. The Tx response struct iwl_tx_resp indicates the Tx rate in + * rate_n_flags field. After receiving a block-ack, the driver can update + * history for the entire block all at once. + * + * + * FINDING BEST STARTING RATE: + * + * When working with a selected initial modulation mode (see below), the + * driver attempts to find a best initial rate. The initial rate is the + * first entry in the Link Quality command's rate table. + * + * 1) Calculate actual throughput (success ratio * expected throughput, see + * table below) for current initial rate. Do this only if enough frames + * have been attempted to make the value meaningful: at least 6 failed + * tx attempts, or at least 8 successes. If not enough, don't try rate + * scaling yet. + * + * 2) Find available rates adjacent to current initial rate. Available means: + * a) supported by hardware && + * b) supported by association && + * c) within any constraints selected by user + * + * 3) Gather measured throughputs for adjacent rates. These might not have + * enough history to calculate a throughput. That's okay, we might try + * using one of them anyway! + * + * 4) Try decreasing rate if, for current rate: + * a) success ratio is < 15% || + * b) lower adjacent rate has better measured throughput || + * c) higher adjacent rate has worse throughput, and lower is unmeasured + * + * As a sanity check, if decrease was determined above, leave rate + * unchanged if: + * a) lower rate unavailable + * b) success ratio at current rate > 85% (very good) + * c) current measured throughput is better than expected throughput + * of lower rate (under perfect 100% tx conditions, see table below) + * + * 5) Try increasing rate if, for current rate: + * a) success ratio is < 15% || + * b) both adjacent rates' throughputs are unmeasured (try it!) || + * b) higher adjacent rate has better measured throughput || + * c) lower adjacent rate has worse throughput, and higher is unmeasured + * + * As a sanity check, if increase was determined above, leave rate + * unchanged if: + * a) success ratio at current rate < 70%. This is not particularly + * good performance; higher rate is sure to have poorer success. + * + * 6) Re-evaluate the rate after each tx frame. If working with block- + * acknowledge, history and statistics may be calculated for the entire + * block (including prior history that fits within the history windows), + * before re-evaluation. + * + * FINDING BEST STARTING MODULATION MODE: + * + * After working with a modulation mode for a "while" (and doing rate scaling), + * the driver searches for a new initial mode in an attempt to improve + * throughput. The "while" is measured by numbers of attempted frames: + * + * For legacy mode, search for new mode after: + * 480 successful frames, or 160 failed frames + * For high-throughput modes (SISO or MIMO), search for new mode after: + * 4500 successful frames, or 400 failed frames + * + * Mode switch possibilities are (3 for each mode): + * + * For legacy: + * Change antenna, try SISO (if HT association), try MIMO (if HT association) + * For SISO: + * Change antenna, try MIMO, try shortened guard interval (SGI) + * For MIMO: + * Try SISO antenna A, SISO antenna B, try shortened guard interval (SGI) + * + * When trying a new mode, use the same bit rate as the old/current mode when + * trying antenna switches and shortened guard interval. When switching to + * SISO from MIMO or legacy, or to MIMO from SISO or legacy, use a rate + * for which the expected throughput (under perfect conditions) is about the + * same or slightly better than the actual measured throughput delivered by + * the old/current mode. + * + * Actual throughput can be estimated by multiplying the expected throughput + * by the success ratio (successful / attempted tx frames). Frame size is + * not considered in this calculation; it assumes that frame size will average + * out to be fairly consistent over several samples. The following are + * metric values for expected throughput assuming 100% success ratio. + * Only G band has support for CCK rates: + * + * RATE: 1 2 5 11 6 9 12 18 24 36 48 54 60 + * + * G: 7 13 35 58 40 57 72 98 121 154 177 186 186 + * A: 0 0 0 0 40 57 72 98 121 154 177 186 186 + * SISO 20MHz: 0 0 0 0 42 42 76 102 124 159 183 193 202 + * SGI SISO 20MHz: 0 0 0 0 46 46 82 110 132 168 192 202 211 + * MIMO 20MHz: 0 0 0 0 74 74 123 155 179 214 236 244 251 + * SGI MIMO 20MHz: 0 0 0 0 81 81 131 164 188 222 243 251 257 + * SISO 40MHz: 0 0 0 0 77 77 127 160 184 220 242 250 257 + * SGI SISO 40MHz: 0 0 0 0 83 83 135 169 193 229 250 257 264 + * MIMO 40MHz: 0 0 0 0 123 123 182 214 235 264 279 285 289 + * SGI MIMO 40MHz: 0 0 0 0 131 131 191 222 242 270 284 289 293 + * + * After the new mode has been tried for a short while (minimum of 6 failed + * frames or 8 successful frames), compare success ratio and actual throughput + * estimate of the new mode with the old. If either is better with the new + * mode, continue to use the new mode. + * + * Continue comparing modes until all 3 possibilities have been tried. + * If moving from legacy to HT, try all 3 possibilities from the new HT + * mode. After trying all 3, a best mode is found. Continue to use this mode + * for the longer "while" described above (e.g. 480 successful frames for + * legacy), and then repeat the search process. + * + */ +struct iwl_link_quality_cmd { + + /* Index of destination/recipient station in uCode's station table */ + u8 sta_id; + u8 reserved1; + __le16 control; /* not used */ + struct iwl_link_qual_general_params general_params; + struct iwl_link_qual_agg_params agg_params; + + /* + * Rate info; when using rate-scaling, Tx command's initial_rate_index + * specifies 1st Tx rate attempted, via index into this table. + * 4965 devices works its way through table when retrying Tx. + */ + struct { + __le32 rate_n_flags; /* RATE_MCS_*, IWL_RATE_* */ + } rs_table[LINK_QUAL_MAX_RETRY_NUM]; + __le32 reserved2; +} __packed; + +/* + * BT configuration enable flags: + * bit 0 - 1: BT channel announcement enabled + * 0: disable + * bit 1 - 1: priority of BT device enabled + * 0: disable + */ +#define BT_COEX_DISABLE (0x0) +#define BT_ENABLE_CHANNEL_ANNOUNCE BIT(0) +#define BT_ENABLE_PRIORITY BIT(1) + +#define BT_COEX_ENABLE (BT_ENABLE_CHANNEL_ANNOUNCE | BT_ENABLE_PRIORITY) + +#define BT_LEAD_TIME_DEF (0x1E) + +#define BT_MAX_KILL_DEF (0x5) + +/* + * REPLY_BT_CONFIG = 0x9b (command, has simple generic response) + * + * 3945 and 4965 devices support hardware handshake with Bluetooth device on + * same platform. Bluetooth device alerts wireless device when it will Tx; + * wireless device can delay or kill its own Tx to accommodate. + */ +struct iwl_bt_cmd { + u8 flags; + u8 lead_time; + u8 max_kill; + u8 reserved; + __le32 kill_ack_mask; + __le32 kill_cts_mask; +} __packed; + + +/****************************************************************************** + * (6) + * Spectrum Management (802.11h) Commands, Responses, Notifications: + * + *****************************************************************************/ + +/* + * Spectrum Management + */ +#define MEASUREMENT_FILTER_FLAG (RXON_FILTER_PROMISC_MSK | \ + RXON_FILTER_CTL2HOST_MSK | \ + RXON_FILTER_ACCEPT_GRP_MSK | \ + RXON_FILTER_DIS_DECRYPT_MSK | \ + RXON_FILTER_DIS_GRP_DECRYPT_MSK | \ + RXON_FILTER_ASSOC_MSK | \ + RXON_FILTER_BCON_AWARE_MSK) + +struct iwl_measure_channel { + __le32 duration; /* measurement duration in extended beacon + * format */ + u8 channel; /* channel to measure */ + u8 type; /* see enum iwl_measure_type */ + __le16 reserved; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (command) + */ +struct iwl_spectrum_cmd { + __le16 len; /* number of bytes starting from token */ + u8 token; /* token id */ + u8 id; /* measurement id -- 0 or 1 */ + u8 origin; /* 0 = TGh, 1 = other, 2 = TGk */ + u8 periodic; /* 1 = periodic */ + __le16 path_loss_timeout; + __le32 start_time; /* start time in extended beacon format */ + __le32 reserved2; + __le32 flags; /* rxon flags */ + __le32 filter_flags; /* rxon filter flags */ + __le16 channel_count; /* minimum 1, maximum 10 */ + __le16 reserved3; + struct iwl_measure_channel channels[10]; +} __packed; + +/* + * REPLY_SPECTRUM_MEASUREMENT_CMD = 0x74 (response) + */ +struct iwl_spectrum_resp { + u8 token; + u8 id; /* id of the prior command replaced, or 0xff */ + __le16 status; /* 0 - command will be handled + * 1 - cannot handle (conflicts with another + * measurement) */ +} __packed; + +enum iwl_measurement_state { + IWL_MEASUREMENT_START = 0, + IWL_MEASUREMENT_STOP = 1, +}; + +enum iwl_measurement_status { + IWL_MEASUREMENT_OK = 0, + IWL_MEASUREMENT_CONCURRENT = 1, + IWL_MEASUREMENT_CSA_CONFLICT = 2, + IWL_MEASUREMENT_TGH_CONFLICT = 3, + /* 4-5 reserved */ + IWL_MEASUREMENT_STOPPED = 6, + IWL_MEASUREMENT_TIMEOUT = 7, + IWL_MEASUREMENT_PERIODIC_FAILED = 8, +}; + +#define NUM_ELEMENTS_IN_HISTOGRAM 8 + +struct iwl_measurement_histogram { + __le32 ofdm[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 0.8usec counts */ + __le32 cck[NUM_ELEMENTS_IN_HISTOGRAM]; /* in 1usec counts */ +} __packed; + +/* clear channel availability counters */ +struct iwl_measurement_cca_counters { + __le32 ofdm; + __le32 cck; +} __packed; + +enum iwl_measure_type { + IWL_MEASURE_BASIC = (1 << 0), + IWL_MEASURE_CHANNEL_LOAD = (1 << 1), + IWL_MEASURE_HISTOGRAM_RPI = (1 << 2), + IWL_MEASURE_HISTOGRAM_NOISE = (1 << 3), + IWL_MEASURE_FRAME = (1 << 4), + /* bits 5:6 are reserved */ + IWL_MEASURE_IDLE = (1 << 7), +}; + +/* + * SPECTRUM_MEASURE_NOTIFICATION = 0x75 (notification only, not a command) + */ +struct iwl_spectrum_notification { + u8 id; /* measurement id -- 0 or 1 */ + u8 token; + u8 channel_index; /* index in measurement channel list */ + u8 state; /* 0 - start, 1 - stop */ + __le32 start_time; /* lower 32-bits of TSF */ + u8 band; /* 0 - 5.2GHz, 1 - 2.4GHz */ + u8 channel; + u8 type; /* see enum iwl_measurement_type */ + u8 reserved1; + /* NOTE: cca_ofdm, cca_cck, basic_type, and histogram are only only + * valid if applicable for measurement type requested. */ + __le32 cca_ofdm; /* cca fraction time in 40Mhz clock periods */ + __le32 cca_cck; /* cca fraction time in 44Mhz clock periods */ + __le32 cca_time; /* channel load time in usecs */ + u8 basic_type; /* 0 - bss, 1 - ofdm preamble, 2 - + * unidentified */ + u8 reserved2[3]; + struct iwl_measurement_histogram histogram; + __le32 stop_time; /* lower 32-bits of TSF */ + __le32 status; /* see iwl_measurement_status */ +} __packed; + +/****************************************************************************** + * (7) + * Power Management Commands, Responses, Notifications: + * + *****************************************************************************/ + +/** + * struct iwl_powertable_cmd - Power Table Command + * @flags: See below: + * + * POWER_TABLE_CMD = 0x77 (command, has simple generic response) + * + * PM allow: + * bit 0 - '0' Driver not allow power management + * '1' Driver allow PM (use rest of parameters) + * + * uCode send sleep notifications: + * bit 1 - '0' Don't send sleep notification + * '1' send sleep notification (SEND_PM_NOTIFICATION) + * + * Sleep over DTIM + * bit 2 - '0' PM have to walk up every DTIM + * '1' PM could sleep over DTIM till listen Interval. + * + * PCI power managed + * bit 3 - '0' (PCI_CFG_LINK_CTRL & 0x1) + * '1' !(PCI_CFG_LINK_CTRL & 0x1) + * + * Fast PD + * bit 4 - '1' Put radio to sleep when receiving frame for others + * + * Force sleep Modes + * bit 31/30- '00' use both mac/xtal sleeps + * '01' force Mac sleep + * '10' force xtal sleep + * '11' Illegal set + * + * NOTE: if sleep_interval[SLEEP_INTRVL_TABLE_SIZE-1] > DTIM period then + * ucode assume sleep over DTIM is allowed and we don't need to wake up + * for every DTIM. + */ +#define IWL_POWER_VEC_SIZE 5 + +#define IWL_POWER_DRIVER_ALLOW_SLEEP_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_SAVE_ENA_MSK cpu_to_le16(BIT(0)) +#define IWL_POWER_POWER_MANAGEMENT_ENA_MSK cpu_to_le16(BIT(1)) +#define IWL_POWER_SLEEP_OVER_DTIM_MSK cpu_to_le16(BIT(2)) +#define IWL_POWER_PCI_PM_MSK cpu_to_le16(BIT(3)) +#define IWL_POWER_FAST_PD cpu_to_le16(BIT(4)) +#define IWL_POWER_BEACON_FILTERING cpu_to_le16(BIT(5)) +#define IWL_POWER_SHADOW_REG_ENA cpu_to_le16(BIT(6)) +#define IWL_POWER_CT_KILL_SET cpu_to_le16(BIT(7)) + +struct iwl3945_powertable_cmd { + __le16 flags; + u8 reserved[2]; + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; +} __packed; + +struct iwl_powertable_cmd { + __le16 flags; + u8 keep_alive_seconds; /* 3945 reserved */ + u8 debug_flags; /* 3945 reserved */ + __le32 rx_data_timeout; + __le32 tx_data_timeout; + __le32 sleep_interval[IWL_POWER_VEC_SIZE]; + __le32 keep_alive_beacons; +} __packed; + +/* + * PM_SLEEP_NOTIFICATION = 0x7A (notification only, not a command) + * all devices identical. + */ +struct iwl_sleep_notification { + u8 pm_sleep_mode; + u8 pm_wakeup_src; + __le16 reserved; + __le32 sleep_time; + __le32 tsf_low; + __le32 bcon_timer; +} __packed; + +/* Sleep states. all devices identical. */ +enum { + IWL_PM_NO_SLEEP = 0, + IWL_PM_SLP_MAC = 1, + IWL_PM_SLP_FULL_MAC_UNASSOCIATE = 2, + IWL_PM_SLP_FULL_MAC_CARD_STATE = 3, + IWL_PM_SLP_PHY = 4, + IWL_PM_SLP_REPENT = 5, + IWL_PM_WAKEUP_BY_TIMER = 6, + IWL_PM_WAKEUP_BY_DRIVER = 7, + IWL_PM_WAKEUP_BY_RFKILL = 8, + /* 3 reserved */ + IWL_PM_NUM_OF_MODES = 12, +}; + +/* + * CARD_STATE_NOTIFICATION = 0xa1 (notification only, not a command) + */ +struct iwl_card_state_notif { + __le32 flags; +} __packed; + +#define HW_CARD_DISABLED 0x01 +#define SW_CARD_DISABLED 0x02 +#define CT_CARD_DISABLED 0x04 +#define RXON_CARD_DISABLED 0x10 + +struct iwl_ct_kill_config { + __le32 reserved; + __le32 critical_temperature_M; + __le32 critical_temperature_R; +} __packed; + +/****************************************************************************** + * (8) + * Scan Commands, Responses, Notifications: + * + *****************************************************************************/ + +#define SCAN_CHANNEL_TYPE_PASSIVE cpu_to_le32(0) +#define SCAN_CHANNEL_TYPE_ACTIVE cpu_to_le32(1) + +/** + * struct iwl_scan_channel - entry in REPLY_SCAN_CMD channel table + * + * One for each channel in the scan list. + * Each channel can independently select: + * 1) SSID for directed active scans + * 2) Txpower setting (for rate specified within Tx command) + * 3) How long to stay on-channel (behavior may be modified by quiet_time, + * quiet_plcp_th, good_CRC_th) + * + * To avoid uCode errors, make sure the following are true (see comments + * under struct iwl_scan_cmd about max_out_time and quiet_time): + * 1) If using passive_dwell (i.e. passive_dwell != 0): + * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) + * 2) quiet_time <= active_dwell + * 3) If restricting off-channel time (i.e. max_out_time !=0): + * passive_dwell < max_out_time + * active_dwell < max_out_time + */ +struct iwl3945_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:4 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 5:7 reserved + */ + u8 type; + u8 channel; /* band is selected by iwl3945_scan_cmd "flags" field */ + struct iwl3945_tx_power tpc; + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes u8 type */ +#define IWL39_SCAN_PROBE_MASK(n) ((BIT(n) | (BIT(n) - BIT(1)))) + +struct iwl_scan_channel { + /* + * type is defined as: + * 0:0 1 = active, 0 = passive + * 1:20 SSID direct bit map; if a bit is set, then corresponding + * SSID IE is transmitted in probe request. + * 21:31 reserved + */ + __le32 type; + __le16 channel; /* band is selected by iwl_scan_cmd "flags" field */ + u8 tx_gain; /* gain for analog radio */ + u8 dsp_atten; /* gain for DSP */ + __le16 active_dwell; /* in 1024-uSec TU (time units), typ 5-50 */ + __le16 passive_dwell; /* in 1024-uSec TU (time units), typ 20-500 */ +} __packed; + +/* set number of direct probes __le32 type */ +#define IWL_SCAN_PROBE_MASK(n) cpu_to_le32((BIT(n) | (BIT(n) - BIT(1)))) + +/** + * struct iwl_ssid_ie - directed scan network information element + * + * Up to 20 of these may appear in REPLY_SCAN_CMD (Note: Only 4 are in + * 3945 SCAN api), selected by "type" bit field in struct iwl_scan_channel; + * each channel may select different ssids from among the 20 (4) entries. + * SSID IEs get transmitted in reverse order of entry. + */ +struct iwl_ssid_ie { + u8 id; + u8 len; + u8 ssid[32]; +} __packed; + +#define PROBE_OPTION_MAX_3945 4 +#define PROBE_OPTION_MAX 20 +#define TX_CMD_LIFE_TIME_INFINITE cpu_to_le32(0xFFFFFFFF) +#define IWL_GOOD_CRC_TH_DISABLED 0 +#define IWL_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWL_GOOD_CRC_TH_NEVER cpu_to_le16(0xffff) +#define IWL_MAX_SCAN_SIZE 1024 +#define IWL_MAX_CMD_SIZE 4096 + +/* + * REPLY_SCAN_CMD = 0x80 (command) + * + * The hardware scan command is very powerful; the driver can set it up to + * maintain (relatively) normal network traffic while doing a scan in the + * background. The max_out_time and suspend_time control the ratio of how + * long the device stays on an associated network channel ("service channel") + * vs. how long it's away from the service channel, i.e. tuned to other channels + * for scanning. + * + * max_out_time is the max time off-channel (in usec), and suspend_time + * is how long (in "extended beacon" format) that the scan is "suspended" + * after returning to the service channel. That is, suspend_time is the + * time that we stay on the service channel, doing normal work, between + * scan segments. The driver may set these parameters differently to support + * scanning when associated vs. not associated, and light vs. heavy traffic + * loads when associated. + * + * After receiving this command, the device's scan engine does the following; + * + * 1) Sends SCAN_START notification to driver + * 2) Checks to see if it has time to do scan for one channel + * 3) Sends NULL packet, with power-save (PS) bit set to 1, + * to tell AP that we're going off-channel + * 4) Tunes to first channel in scan list, does active or passive scan + * 5) Sends SCAN_RESULT notification to driver + * 6) Checks to see if it has time to do scan on *next* channel in list + * 7) Repeats 4-6 until it no longer has time to scan the next channel + * before max_out_time expires + * 8) Returns to service channel + * 9) Sends NULL packet with PS=0 to tell AP that we're back + * 10) Stays on service channel until suspend_time expires + * 11) Repeats entire process 2-10 until list is complete + * 12) Sends SCAN_COMPLETE notification + * + * For fast, efficient scans, the scan command also has support for staying on + * a channel for just a short time, if doing active scanning and getting no + * responses to the transmitted probe request. This time is controlled by + * quiet_time, and the number of received packets below which a channel is + * considered "quiet" is controlled by quiet_plcp_threshold. + * + * For active scanning on channels that have regulatory restrictions against + * blindly transmitting, the scan can listen before transmitting, to make sure + * that there is already legitimate activity on the channel. If enough + * packets are cleanly received on the channel (controlled by good_CRC_th, + * typical value 1), the scan engine starts transmitting probe requests. + * + * Driver must use separate scan commands for 2.4 vs. 5 GHz bands. + * + * To avoid uCode errors, see timing restrictions described under + * struct iwl_scan_channel. + */ + +struct iwl3945_scan_cmd { + __le16 len; + u8 reserved0; + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 reserved1; + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service channel: + * 3945; 31:24 # beacons, 19:0 additional usec, + * 4965; 31:22 # beacons, 21:0 additional usec. + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct iwl3945_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX_3945]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct iwl3945_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +struct iwl_scan_cmd { + __le16 len; + u8 reserved0; + u8 channel_count; /* # channels in channel list */ + __le16 quiet_time; /* dwell only this # millisecs on quiet channel + * (only for active scan) */ + __le16 quiet_plcp_th; /* quiet chnl is < this # pkts (typ. 1) */ + __le16 good_CRC_th; /* passive -> active promotion threshold */ + __le16 rx_chain; /* RXON_RX_CHAIN_* */ + __le32 max_out_time; /* max usec to be away from associated (service) + * channel */ + __le32 suspend_time; /* pause scan this long (in "extended beacon + * format") when returning to service chnl: + * 3945; 31:24 # beacons, 19:0 additional usec, + * 4965; 31:22 # beacons, 21:0 additional usec. + */ + __le32 flags; /* RXON_FLG_* */ + __le32 filter_flags; /* RXON_FILTER_* */ + + /* For active scans (set to all-0s for passive scans). + * Does not include payload. Must specify Tx rate; no rate scaling. */ + struct iwl_tx_cmd tx_cmd; + + /* For directed active scans (set to all-0s otherwise) */ + struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; + + /* + * Probe request frame, followed by channel list. + * + * Size of probe request frame is specified by byte count in tx_cmd. + * Channel list follows immediately after probe request frame. + * Number of channels in list is specified by channel_count. + * Each channel in list is of type: + * + * struct iwl_scan_channel channels[0]; + * + * NOTE: Only one band of channels can be scanned per pass. You + * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait + * for one scan to complete (i.e. receive SCAN_COMPLETE_NOTIFICATION) + * before requesting another scan. + */ + u8 data[0]; +} __packed; + +/* Can abort will notify by complete notification with abort status. */ +#define CAN_ABORT_STATUS cpu_to_le32(0x1) +/* complete notification statuses */ +#define ABORT_STATUS 0x2 + +/* + * REPLY_SCAN_CMD = 0x80 (response) + */ +struct iwl_scanreq_notification { + __le32 status; /* 1: okay, 2: cannot fulfill request */ +} __packed; + +/* + * SCAN_START_NOTIFICATION = 0x82 (notification only, not a command) + */ +struct iwl_scanstart_notification { + __le32 tsf_low; + __le32 tsf_high; + __le32 beacon_timer; + u8 channel; + u8 band; + u8 reserved[2]; + __le32 status; +} __packed; + +#define SCAN_OWNER_STATUS 0x1; +#define MEASURE_OWNER_STATUS 0x2; + +#define IWL_PROBE_STATUS_OK 0 +#define IWL_PROBE_STATUS_TX_FAILED BIT(0) +/* error statuses combined with TX_FAILED */ +#define IWL_PROBE_STATUS_FAIL_TTL BIT(1) +#define IWL_PROBE_STATUS_FAIL_BT BIT(2) + +#define NUMBER_OF_STATISTICS 1 /* first __le32 is good CRC */ +/* + * SCAN_RESULTS_NOTIFICATION = 0x83 (notification only, not a command) + */ +struct iwl_scanresults_notification { + u8 channel; + u8 band; + u8 probe_status; + u8 num_probe_not_sent; /* not enough time to send */ + __le32 tsf_low; + __le32 tsf_high; + __le32 statistics[NUMBER_OF_STATISTICS]; +} __packed; + +/* + * SCAN_COMPLETE_NOTIFICATION = 0x84 (notification only, not a command) + */ +struct iwl_scancomplete_notification { + u8 scanned_channels; + u8 status; + u8 last_channel; + __le32 tsf_low; + __le32 tsf_high; +} __packed; + + +/****************************************************************************** + * (9) + * IBSS/AP Commands and Notifications: + * + *****************************************************************************/ + +enum iwl_ibss_manager { + IWL_NOT_IBSS_MANAGER = 0, + IWL_IBSS_MANAGER = 1, +}; + +/* + * BEACON_NOTIFICATION = 0x90 (notification only, not a command) + */ + +struct iwl3945_beacon_notif { + struct iwl3945_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +struct iwl4965_beacon_notif { + struct iwl4965_tx_resp beacon_notify_hdr; + __le32 low_tsf; + __le32 high_tsf; + __le32 ibss_mgr_status; +} __packed; + +/* + * REPLY_TX_BEACON = 0x91 (command, has simple generic response) + */ + +struct iwl3945_tx_beacon_cmd { + struct iwl3945_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +struct iwl_tx_beacon_cmd { + struct iwl_tx_cmd tx; + __le16 tim_idx; + u8 tim_size; + u8 reserved1; + struct ieee80211_hdr frame[0]; /* beacon frame */ +} __packed; + +/****************************************************************************** + * (10) + * Statistics Commands and Notifications: + * + *****************************************************************************/ + +#define IWL_TEMP_CONVERT 260 + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/* Used for passing to driver number of successes and failures per rate */ +struct rate_histogram { + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } success; + union { + __le32 a[SUP_RATE_11A_MAX_NUM_CHANNELS]; + __le32 b[SUP_RATE_11B_MAX_NUM_CHANNELS]; + __le32 g[SUP_RATE_11G_MAX_NUM_CHANNELS]; + } failed; +} __packed; + +/* statistics command response */ + +struct iwl39_statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; +} __packed; + +struct iwl39_statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ +} __packed; + +struct iwl39_statistics_rx { + struct iwl39_statistics_rx_phy ofdm; + struct iwl39_statistics_rx_phy cck; + struct iwl39_statistics_rx_non_phy general; +} __packed; + +struct iwl39_statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; +} __packed; + +struct statistics_dbg { + __le32 burst_check; + __le32 burst_count; + __le32 wait_for_silence_timeout_cnt; + __le32 reserved[3]; +} __packed; + +struct iwl39_statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; +} __packed; + +struct iwl39_statistics_general { + __le32 temperature; + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct iwl39_statistics_div div; +} __packed; + +struct statistics_rx_phy { + __le32 ina_cnt; + __le32 fina_cnt; + __le32 plcp_err; + __le32 crc32_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 false_alarm_cnt; + __le32 fina_sync_err_cnt; + __le32 sfd_timeout; + __le32 fina_timeout; + __le32 unresponded_rts; + __le32 rxe_frame_limit_overrun; + __le32 sent_ack_cnt; + __le32 sent_cts_cnt; + __le32 sent_ba_rsp_cnt; + __le32 dsp_self_kill; + __le32 mh_format_err; + __le32 re_acq_main_rssi_sum; + __le32 reserved3; +} __packed; + +struct statistics_rx_ht_phy { + __le32 plcp_err; + __le32 overrun_err; + __le32 early_overrun_err; + __le32 crc32_good; + __le32 crc32_err; + __le32 mh_format_err; + __le32 agg_crc32_good; + __le32 agg_mpdu_cnt; + __le32 agg_cnt; + __le32 unsupport_mcs; +} __packed; + +#define INTERFERENCE_DATA_AVAILABLE cpu_to_le32(1) + +struct statistics_rx_non_phy { + __le32 bogus_cts; /* CTS received when not expecting CTS */ + __le32 bogus_ack; /* ACK received when not expecting ACK */ + __le32 non_bssid_frames; /* number of frames with BSSID that + * doesn't belong to the STA BSSID */ + __le32 filtered_frames; /* count frames that were dumped in the + * filtering process */ + __le32 non_channel_beacons; /* beacons with our bss id but not on + * our serving channel */ + __le32 channel_beacons; /* beacons with our bss id and in our + * serving channel */ + __le32 num_missed_bcon; /* number of missed beacons */ + __le32 adc_rx_saturation_time; /* count in 0.8us units the time the + * ADC was in saturation */ + __le32 ina_detection_search_time;/* total time (in 0.8us) searched + * for INA */ + __le32 beacon_silence_rssi_a; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_b; /* RSSI silence after beacon frame */ + __le32 beacon_silence_rssi_c; /* RSSI silence after beacon frame */ + __le32 interference_data_flag; /* flag for interference data + * availability. 1 when data is + * available. */ + __le32 channel_load; /* counts RX Enable time in uSec */ + __le32 dsp_false_alarms; /* DSP false alarm (both OFDM + * and CCK) counter */ + __le32 beacon_rssi_a; + __le32 beacon_rssi_b; + __le32 beacon_rssi_c; + __le32 beacon_energy_a; + __le32 beacon_energy_b; + __le32 beacon_energy_c; +} __packed; + +struct statistics_rx { + struct statistics_rx_phy ofdm; + struct statistics_rx_phy cck; + struct statistics_rx_non_phy general; + struct statistics_rx_ht_phy ofdm_ht; +} __packed; + +/** + * struct statistics_tx_power - current tx power + * + * @ant_a: current tx power on chain a in 1/2 dB step + * @ant_b: current tx power on chain b in 1/2 dB step + * @ant_c: current tx power on chain c in 1/2 dB step + */ +struct statistics_tx_power { + u8 ant_a; + u8 ant_b; + u8 ant_c; + u8 reserved; +} __packed; + +struct statistics_tx_non_phy_agg { + __le32 ba_timeout; + __le32 ba_reschedule_frames; + __le32 scd_query_agg_frame_cnt; + __le32 scd_query_no_agg; + __le32 scd_query_agg; + __le32 scd_query_mismatch; + __le32 frame_not_ready; + __le32 underrun; + __le32 bt_prio_kill; + __le32 rx_ba_rsp_cnt; +} __packed; + +struct statistics_tx { + __le32 preamble_cnt; + __le32 rx_detected_cnt; + __le32 bt_prio_defer_cnt; + __le32 bt_prio_kill_cnt; + __le32 few_bytes_cnt; + __le32 cts_timeout; + __le32 ack_timeout; + __le32 expected_ack_cnt; + __le32 actual_ack_cnt; + __le32 dump_msdu_cnt; + __le32 burst_abort_next_frame_mismatch_cnt; + __le32 burst_abort_missing_next_frame_cnt; + __le32 cts_timeout_collision; + __le32 ack_or_ba_timeout_collision; + struct statistics_tx_non_phy_agg agg; + + __le32 reserved1; +} __packed; + + +struct statistics_div { + __le32 tx_on_a; + __le32 tx_on_b; + __le32 exec_time; + __le32 probe_time; + __le32 reserved1; + __le32 reserved2; +} __packed; + +struct statistics_general_common { + __le32 temperature; /* radio temperature */ + struct statistics_dbg dbg; + __le32 sleep_time; + __le32 slots_out; + __le32 slots_idle; + __le32 ttl_timestamp; + struct statistics_div div; + __le32 rx_enable_counter; + /* + * num_of_sos_states: + * count the number of times we have to re-tune + * in order to get out of bad PHY status + */ + __le32 num_of_sos_states; +} __packed; + +struct statistics_general { + struct statistics_general_common common; + __le32 reserved2; + __le32 reserved3; +} __packed; + +#define UCODE_STATISTICS_CLEAR_MSK (0x1 << 0) +#define UCODE_STATISTICS_FREQUENCY_MSK (0x1 << 1) +#define UCODE_STATISTICS_NARROW_BAND_MSK (0x1 << 2) + +/* + * REPLY_STATISTICS_CMD = 0x9c, + * all devices identical. + * + * This command triggers an immediate response containing uCode statistics. + * The response is in the same format as STATISTICS_NOTIFICATION 0x9d, below. + * + * If the CLEAR_STATS configuration flag is set, uCode will clear its + * internal copy of the statistics (counters) after issuing the response. + * This flag does not affect STATISTICS_NOTIFICATIONs after beacons (see below). + * + * If the DISABLE_NOTIF configuration flag is set, uCode will not issue + * STATISTICS_NOTIFICATIONs after received beacons (see below). This flag + * does not affect the response to the REPLY_STATISTICS_CMD 0x9c itself. + */ +#define IWL_STATS_CONF_CLEAR_STATS cpu_to_le32(0x1) /* see above */ +#define IWL_STATS_CONF_DISABLE_NOTIF cpu_to_le32(0x2)/* see above */ +struct iwl_statistics_cmd { + __le32 configuration_flags; /* IWL_STATS_CONF_* */ +} __packed; + +/* + * STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) + * + * By default, uCode issues this notification after receiving a beacon + * while associated. To disable this behavior, set DISABLE_NOTIF flag in the + * REPLY_STATISTICS_CMD 0x9c, above. + * + * Statistics counters continue to increment beacon after beacon, but are + * cleared when changing channels or when driver issues REPLY_STATISTICS_CMD + * 0x9c with CLEAR_STATS bit set (see above). + * + * uCode also issues this notification during scans. uCode clears statistics + * appropriately so that each notification contains statistics for only the + * one channel that has just been scanned. + */ +#define STATISTICS_REPLY_FLG_BAND_24G_MSK cpu_to_le32(0x2) +#define STATISTICS_REPLY_FLG_HT40_MODE_MSK cpu_to_le32(0x8) + +struct iwl3945_notif_statistics { + __le32 flag; + struct iwl39_statistics_rx rx; + struct iwl39_statistics_tx tx; + struct iwl39_statistics_general general; +} __packed; + +struct iwl_notif_statistics { + __le32 flag; + struct statistics_rx rx; + struct statistics_tx tx; + struct statistics_general general; +} __packed; + +/* + * MISSED_BEACONS_NOTIFICATION = 0xa2 (notification only, not a command) + * + * uCode send MISSED_BEACONS_NOTIFICATION to driver when detect beacon missed + * in regardless of how many missed beacons, which mean when driver receive the + * notification, inside the command, it can find all the beacons information + * which include number of total missed beacons, number of consecutive missed + * beacons, number of beacons received and number of beacons expected to + * receive. + * + * If uCode detected consecutive_missed_beacons > 5, it will reset the radio + * in order to bring the radio/PHY back to working state; which has no relation + * to when driver will perform sensitivity calibration. + * + * Driver should set it own missed_beacon_threshold to decide when to perform + * sensitivity calibration based on number of consecutive missed beacons in + * order to improve overall performance, especially in noisy environment. + * + */ + +#define IWL_MISSED_BEACON_THRESHOLD_MIN (1) +#define IWL_MISSED_BEACON_THRESHOLD_DEF (5) +#define IWL_MISSED_BEACON_THRESHOLD_MAX IWL_MISSED_BEACON_THRESHOLD_DEF + +struct iwl_missed_beacon_notif { + __le32 consecutive_missed_beacons; + __le32 total_missed_becons; + __le32 num_expected_beacons; + __le32 num_recvd_beacons; +} __packed; + + +/****************************************************************************** + * (11) + * Rx Calibration Commands: + * + * With the uCode used for open source drivers, most Tx calibration (except + * for Tx Power) and most Rx calibration is done by uCode during the + * "initialize" phase of uCode boot. Driver must calibrate only: + * + * 1) Tx power (depends on temperature), described elsewhere + * 2) Receiver gain balance (optimize MIMO, and detect disconnected antennas) + * 3) Receiver sensitivity (to optimize signal detection) + * + *****************************************************************************/ + +/** + * SENSITIVITY_CMD = 0xa8 (command, has simple generic response) + * + * This command sets up the Rx signal detector for a sensitivity level that + * is high enough to lock onto all signals within the associated network, + * but low enough to ignore signals that are below a certain threshold, so as + * not to have too many "false alarms". False alarms are signals that the + * Rx DSP tries to lock onto, but then discards after determining that they + * are noise. + * + * The optimum number of false alarms is between 5 and 50 per 200 TUs + * (200 * 1024 uSecs, i.e. 204.8 milliseconds) of actual Rx time (i.e. + * time listening, not transmitting). Driver must adjust sensitivity so that + * the ratio of actual false alarms to actual Rx time falls within this range. + * + * While associated, uCode delivers STATISTICS_NOTIFICATIONs after each + * received beacon. These provide information to the driver to analyze the + * sensitivity. Don't analyze statistics that come in from scanning, or any + * other non-associated-network source. Pertinent statistics include: + * + * From "general" statistics (struct statistics_rx_non_phy): + * + * (beacon_energy_[abc] & 0x0FF00) >> 8 (unsigned, higher value is lower level) + * Measure of energy of desired signal. Used for establishing a level + * below which the device does not detect signals. + * + * (beacon_silence_rssi_[abc] & 0x0FF00) >> 8 (unsigned, units in dB) + * Measure of background noise in silent period after beacon. + * + * channel_load + * uSecs of actual Rx time during beacon period (varies according to + * how much time was spent transmitting). + * + * From "cck" and "ofdm" statistics (struct statistics_rx_phy), separately: + * + * false_alarm_cnt + * Signal locks abandoned early (before phy-level header). + * + * plcp_err + * Signal locks abandoned late (during phy-level header). + * + * NOTE: Both false_alarm_cnt and plcp_err increment monotonically from + * beacon to beacon, i.e. each value is an accumulation of all errors + * before and including the latest beacon. Values will wrap around to 0 + * after counting up to 2^32 - 1. Driver must differentiate vs. + * previous beacon's values to determine # false alarms in the current + * beacon period. + * + * Total number of false alarms = false_alarms + plcp_errs + * + * For OFDM, adjust the following table entries in struct iwl_sensitivity_cmd + * (notice that the start points for OFDM are at or close to settings for + * maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX 90 / 85 / 120 + * HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX 170 / 170 / 210 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX 105 / 105 / 140 + * HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX 220 / 220 / 270 + * + * If actual rate of OFDM false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), reduce sensitivity + * by *adding* 1 to all 4 of the table entries above, up to the max for + * each entry. Conversely, if false alarm rate is too low (less than 5 + * for each 204.8 msecs listening), *subtract* 1 from each entry to + * increase sensitivity. + * + * For CCK sensitivity, keep track of the following: + * + * 1). 20-beacon history of maximum background noise, indicated by + * (beacon_silence_rssi_[abc] & 0x0FF00), units in dB, across the + * 3 receivers. For any given beacon, the "silence reference" is + * the maximum of last 60 samples (20 beacons * 3 receivers). + * + * 2). 10-beacon history of strongest signal level, as indicated + * by (beacon_energy_[abc] & 0x0FF00) >> 8, across the 3 receivers, + * i.e. the strength of the signal through the best receiver at the + * moment. These measurements are "upside down", with lower values + * for stronger signals, so max energy will be *minimum* value. + * + * Then for any given beacon, the driver must determine the *weakest* + * of the strongest signals; this is the minimum level that needs to be + * successfully detected, when using the best receiver at the moment. + * "Max cck energy" is the maximum (higher value means lower energy!) + * of the last 10 minima. Once this is determined, driver must add + * a little margin by adding "6" to it. + * + * 3). Number of consecutive beacon periods with too few false alarms. + * Reset this to 0 at the first beacon period that falls within the + * "good" range (5 to 50 false alarms per 204.8 milliseconds rx). + * + * Then, adjust the following CCK table entries in struct iwl_sensitivity_cmd + * (notice that the start points for CCK are at maximum sensitivity): + * + * START / MIN / MAX + * HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX 125 / 125 / 200 + * HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX 200 / 200 / 400 + * HD_MIN_ENERGY_CCK_DET_INDEX 100 / 0 / 100 + * + * If actual rate of CCK false alarms (+ plcp_errors) is too high + * (greater than 50 for each 204.8 msecs listening), method for reducing + * sensitivity is: + * + * 1) *Add* 3 to value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * up to max 400. + * + * 2) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is < 160, + * sensitivity has been reduced a significant amount; bring it up to + * a moderate 161. Otherwise, *add* 3, up to max 200. + * + * 3) a) If current value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX is > 160, + * sensitivity has been reduced only a moderate or small amount; + * *subtract* 2 from value in HD_MIN_ENERGY_CCK_DET_INDEX, + * down to min 0. Otherwise (if gain has been significantly reduced), + * don't change the HD_MIN_ENERGY_CCK_DET_INDEX value. + * + * b) Save a snapshot of the "silence reference". + * + * If actual rate of CCK false alarms (+ plcp_errors) is too low + * (less than 5 for each 204.8 msecs listening), method for increasing + * sensitivity is used only if: + * + * 1a) Previous beacon did not have too many false alarms + * 1b) AND difference between previous "silence reference" and current + * "silence reference" (prev - current) is 2 or more, + * OR 2) 100 or more consecutive beacon periods have had rate of + * less than 5 false alarms per 204.8 milliseconds rx time. + * + * Method for increasing sensitivity: + * + * 1) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX, + * down to min 125. + * + * 2) *Subtract* 3 from value in HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX, + * down to min 200. + * + * 3) *Add* 2 to value in HD_MIN_ENERGY_CCK_DET_INDEX, up to max 100. + * + * If actual rate of CCK false alarms (+ plcp_errors) is within good range + * (between 5 and 50 for each 204.8 msecs listening): + * + * 1) Save a snapshot of the silence reference. + * + * 2) If previous beacon had too many CCK false alarms (+ plcp_errors), + * give some extra margin to energy threshold by *subtracting* 8 + * from value in HD_MIN_ENERGY_CCK_DET_INDEX. + * + * For all cases (too few, too many, good range), make sure that the CCK + * detection threshold (energy) is below the energy level for robust + * detection over the past 10 beacon periods, the "Max cck energy". + * Lower values mean higher energy; this means making sure that the value + * in HD_MIN_ENERGY_CCK_DET_INDEX is at or *above* "Max cck energy". + * + */ + +/* + * Table entries in SENSITIVITY_CMD (struct iwl_sensitivity_cmd) + */ +#define HD_TABLE_SIZE (11) /* number of entries */ +#define HD_MIN_ENERGY_CCK_DET_INDEX (0) /* table indexes */ +#define HD_MIN_ENERGY_OFDM_DET_INDEX (1) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_INDEX (2) +#define HD_AUTO_CORR32_X1_TH_ADD_MIN_MRC_INDEX (3) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_MRC_INDEX (4) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_INDEX (5) +#define HD_AUTO_CORR32_X4_TH_ADD_MIN_MRC_INDEX (6) +#define HD_BARKER_CORR_TH_ADD_MIN_INDEX (7) +#define HD_BARKER_CORR_TH_ADD_MIN_MRC_INDEX (8) +#define HD_AUTO_CORR40_X4_TH_ADD_MIN_INDEX (9) +#define HD_OFDM_ENERGY_TH_IN_INDEX (10) + +/* Control field in struct iwl_sensitivity_cmd */ +#define SENSITIVITY_CMD_CONTROL_DEFAULT_TABLE cpu_to_le16(0) +#define SENSITIVITY_CMD_CONTROL_WORK_TABLE cpu_to_le16(1) + +/** + * struct iwl_sensitivity_cmd + * @control: (1) updates working table, (0) updates default table + * @table: energy threshold values, use HD_* as index into table + * + * Always use "1" in "control" to update uCode's working table and DSP. + */ +struct iwl_sensitivity_cmd { + __le16 control; /* always use "1" */ + __le16 table[HD_TABLE_SIZE]; /* use HD_* as index */ +} __packed; + + +/** + * REPLY_PHY_CALIBRATION_CMD = 0xb0 (command, has simple generic response) + * + * This command sets the relative gains of 4965 device's 3 radio receiver chains. + * + * After the first association, driver should accumulate signal and noise + * statistics from the STATISTICS_NOTIFICATIONs that follow the first 20 + * beacons from the associated network (don't collect statistics that come + * in from scanning, or any other non-network source). + * + * DISCONNECTED ANTENNA: + * + * Driver should determine which antennas are actually connected, by comparing + * average beacon signal levels for the 3 Rx chains. Accumulate (add) the + * following values over 20 beacons, one accumulator for each of the chains + * a/b/c, from struct statistics_rx_non_phy: + * + * beacon_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the strongest signal from among a/b/c. Compare the other two to the + * strongest. If any signal is more than 15 dB (times 20, unless you + * divide the accumulated values by 20) below the strongest, the driver + * considers that antenna to be disconnected, and should not try to use that + * antenna/chain for Rx or Tx. If both A and B seem to be disconnected, + * driver should declare the stronger one as connected, and attempt to use it + * (A and B are the only 2 Tx chains!). + * + * + * RX BALANCE: + * + * Driver should balance the 3 receivers (but just the ones that are connected + * to antennas, see above) for gain, by comparing the average signal levels + * detected during the silence after each beacon (background noise). + * Accumulate (add) the following values over 20 beacons, one accumulator for + * each of the chains a/b/c, from struct statistics_rx_non_phy: + * + * beacon_silence_rssi_[abc] & 0x0FF (unsigned, units in dB) + * + * Find the weakest background noise level from among a/b/c. This Rx chain + * will be the reference, with 0 gain adjustment. Attenuate other channels by + * finding noise difference: + * + * (accum_noise[i] - accum_noise[reference]) / 30 + * + * The "30" adjusts the dB in the 20 accumulated samples to units of 1.5 dB. + * For use in diff_gain_[abc] fields of struct iwl_calibration_cmd, the + * driver should limit the difference results to a range of 0-3 (0-4.5 dB), + * and set bit 2 to indicate "reduce gain". The value for the reference + * (weakest) chain should be "0". + * + * diff_gain_[abc] bit fields: + * 2: (1) reduce gain, (0) increase gain + * 1-0: amount of gain, units of 1.5 dB + */ + +/* Phy calibration command for series */ +/* The default calibrate table size if not specified by firmware */ +#define IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 +enum { + IWL_PHY_CALIBRATE_DIFF_GAIN_CMD = 7, + IWL_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE = 19, +}; + +#define IWL_MAX_PHY_CALIBRATE_TBL_SIZE (253) + +struct iwl_calib_hdr { + u8 op_code; + u8 first_group; + u8 groups_num; + u8 data_valid; +} __packed; + +/* IWL_PHY_CALIBRATE_DIFF_GAIN_CMD (7) */ +struct iwl_calib_diff_gain_cmd { + struct iwl_calib_hdr hdr; + s8 diff_gain_a; /* see above */ + s8 diff_gain_b; + s8 diff_gain_c; + u8 reserved1; +} __packed; + +/****************************************************************************** + * (12) + * Miscellaneous Commands: + * + *****************************************************************************/ + +/* + * LEDs Command & Response + * REPLY_LEDS_CMD = 0x48 (command, has simple generic response) + * + * For each of 3 possible LEDs (Activity/Link/Tech, selected by "id" field), + * this command turns it on or off, or sets up a periodic blinking cycle. + */ +struct iwl_led_cmd { + __le32 interval; /* "interval" in uSec */ + u8 id; /* 1: Activity, 2: Link, 3: Tech */ + u8 off; /* # intervals off while blinking; + * "0", with >0 "on" value, turns LED on */ + u8 on; /* # intervals on while blinking; + * "0", regardless of "off", turns LED off */ + u8 reserved; +} __packed; + + +/****************************************************************************** + * (13) + * Union of all expected notifications/responses: + * + *****************************************************************************/ + +struct iwl_rx_packet { + /* + * The first 4 bytes of the RX frame header contain both the RX frame + * size and some flags. + * Bit fields: + * 31: flag flush RB request + * 30: flag ignore TC (terminal counter) request + * 29: flag fast IRQ request + * 28-14: Reserved + * 13-00: RX frame size + */ + __le32 len_n_flags; + struct iwl_cmd_header hdr; + union { + struct iwl3945_rx_frame rx_frame; + struct iwl3945_tx_resp tx_resp; + struct iwl3945_beacon_notif beacon_status; + + struct iwl_alive_resp alive_frame; + struct iwl_spectrum_notification spectrum_notif; + struct iwl_csa_notification csa_notif; + struct iwl_error_resp err_resp; + struct iwl_card_state_notif card_state_notif; + struct iwl_add_sta_resp add_sta; + struct iwl_rem_sta_resp rem_sta; + struct iwl_sleep_notification sleep_notif; + struct iwl_spectrum_resp spectrum; + struct iwl_notif_statistics stats; + struct iwl_compressed_ba_resp compressed_ba; + struct iwl_missed_beacon_notif missed_beacon; + __le32 status; + u8 raw[0]; + } u; +} __packed; + +#endif /* __iwl_legacy_commands_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-core.c b/drivers/net/wireless/iwlegacy/iwl-core.c new file mode 100644 index 00000000000..d418b647be8 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-core.c @@ -0,0 +1,2674 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/etherdevice.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <net/mac80211.h> + +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-debug.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-power.h" +#include "iwl-sta.h" +#include "iwl-helpers.h" + + +MODULE_DESCRIPTION("iwl-legacy: common functions for 3945 and 4965"); +MODULE_VERSION(IWLWIFI_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); + +/* + * set bt_coex_active to true, uCode will do kill/defer + * every time the priority line is asserted (BT is sending signals on the + * priority line in the PCIx). + * set bt_coex_active to false, uCode will ignore the BT activity and + * perform the normal operation + * + * User might experience transmit issue on some platform due to WiFi/BT + * co-exist problem. The possible behaviors are: + * Able to scan and finding all the available AP + * Not able to associate with any AP + * On those platforms, WiFi communication can be restored by set + * "bt_coex_active" module parameter to "false" + * + * default: bt_coex_active = true (BT_COEX_ENABLE) + */ +static bool bt_coex_active = true; +module_param(bt_coex_active, bool, S_IRUGO); +MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); + +u32 iwlegacy_debug_level; +EXPORT_SYMBOL(iwlegacy_debug_level); + +const u8 iwlegacy_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +EXPORT_SYMBOL(iwlegacy_bcast_addr); + + +/* This function both allocates and initializes hw and priv. */ +struct ieee80211_hw *iwl_legacy_alloc_all(struct iwl_cfg *cfg) +{ + struct iwl_priv *priv; + /* mac80211 allocates memory for this device instance, including + * space for this driver's private structure */ + struct ieee80211_hw *hw; + + hw = ieee80211_alloc_hw(sizeof(struct iwl_priv), + cfg->ops->ieee80211_ops); + if (hw == NULL) { + pr_err("%s: Can not allocate network device\n", + cfg->name); + goto out; + } + + priv = hw->priv; + priv->hw = hw; + +out: + return hw; +} +EXPORT_SYMBOL(iwl_legacy_alloc_all); + +#define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ +#define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ +static void iwl_legacy_init_ht_hw_capab(const struct iwl_priv *priv, + struct ieee80211_sta_ht_cap *ht_info, + enum ieee80211_band band) +{ + u16 max_bit_rate = 0; + u8 rx_chains_num = priv->hw_params.rx_chains_num; + u8 tx_chains_num = priv->hw_params.tx_chains_num; + + ht_info->cap = 0; + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); + + ht_info->ht_supported = true; + + ht_info->cap |= IEEE80211_HT_CAP_SGI_20; + max_bit_rate = MAX_BIT_RATE_20_MHZ; + if (priv->hw_params.ht40_channel & BIT(band)) { + ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + ht_info->cap |= IEEE80211_HT_CAP_SGI_40; + ht_info->mcs.rx_mask[4] = 0x01; + max_bit_rate = MAX_BIT_RATE_40_MHZ; + } + + if (priv->cfg->mod_params->amsdu_size_8K) + ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; + + ht_info->ampdu_factor = CFG_HT_RX_AMPDU_FACTOR_DEF; + ht_info->ampdu_density = CFG_HT_MPDU_DENSITY_DEF; + + ht_info->mcs.rx_mask[0] = 0xFF; + if (rx_chains_num >= 2) + ht_info->mcs.rx_mask[1] = 0xFF; + if (rx_chains_num >= 3) + ht_info->mcs.rx_mask[2] = 0xFF; + + /* Highest supported Rx data rate */ + max_bit_rate *= rx_chains_num; + WARN_ON(max_bit_rate & ~IEEE80211_HT_MCS_RX_HIGHEST_MASK); + ht_info->mcs.rx_highest = cpu_to_le16(max_bit_rate); + + /* Tx MCS capabilities */ + ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; + if (tx_chains_num != rx_chains_num) { + ht_info->mcs.tx_params |= IEEE80211_HT_MCS_TX_RX_DIFF; + ht_info->mcs.tx_params |= ((tx_chains_num - 1) << + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT); + } +} + +/** + * iwl_legacy_init_geos - Initialize mac80211's geo/channel info based from eeprom + */ +int iwl_legacy_init_geos(struct iwl_priv *priv) +{ + struct iwl_channel_info *ch; + struct ieee80211_supported_band *sband; + struct ieee80211_channel *channels; + struct ieee80211_channel *geo_ch; + struct ieee80211_rate *rates; + int i = 0; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_bitrates || + priv->bands[IEEE80211_BAND_5GHZ].n_bitrates) { + IWL_DEBUG_INFO(priv, "Geography modes already initialized.\n"); + set_bit(STATUS_GEO_CONFIGURED, &priv->status); + return 0; + } + + channels = kzalloc(sizeof(struct ieee80211_channel) * + priv->channel_count, GFP_KERNEL); + if (!channels) + return -ENOMEM; + + rates = kzalloc((sizeof(struct ieee80211_rate) * IWL_RATE_COUNT_LEGACY), + GFP_KERNEL); + if (!rates) { + kfree(channels); + return -ENOMEM; + } + + /* 5.2GHz channels start after the 2.4GHz channels */ + sband = &priv->bands[IEEE80211_BAND_5GHZ]; + sband->channels = &channels[ARRAY_SIZE(iwlegacy_eeprom_band_1)]; + /* just OFDM */ + sband->bitrates = &rates[IWL_FIRST_OFDM_RATE]; + sband->n_bitrates = IWL_RATE_COUNT_LEGACY - IWL_FIRST_OFDM_RATE; + + if (priv->cfg->sku & IWL_SKU_N) + iwl_legacy_init_ht_hw_capab(priv, &sband->ht_cap, + IEEE80211_BAND_5GHZ); + + sband = &priv->bands[IEEE80211_BAND_2GHZ]; + sband->channels = channels; + /* OFDM & CCK */ + sband->bitrates = rates; + sband->n_bitrates = IWL_RATE_COUNT_LEGACY; + + if (priv->cfg->sku & IWL_SKU_N) + iwl_legacy_init_ht_hw_capab(priv, &sband->ht_cap, + IEEE80211_BAND_2GHZ); + + priv->ieee_channels = channels; + priv->ieee_rates = rates; + + for (i = 0; i < priv->channel_count; i++) { + ch = &priv->channel_info[i]; + + if (!iwl_legacy_is_channel_valid(ch)) + continue; + + if (iwl_legacy_is_channel_a_band(ch)) + sband = &priv->bands[IEEE80211_BAND_5GHZ]; + else + sband = &priv->bands[IEEE80211_BAND_2GHZ]; + + geo_ch = &sband->channels[sband->n_channels++]; + + geo_ch->center_freq = + ieee80211_channel_to_frequency(ch->channel, ch->band); + geo_ch->max_power = ch->max_power_avg; + geo_ch->max_antenna_gain = 0xff; + geo_ch->hw_value = ch->channel; + + if (iwl_legacy_is_channel_valid(ch)) { + if (!(ch->flags & EEPROM_CHANNEL_IBSS)) + geo_ch->flags |= IEEE80211_CHAN_NO_IBSS; + + if (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) + geo_ch->flags |= IEEE80211_CHAN_PASSIVE_SCAN; + + if (ch->flags & EEPROM_CHANNEL_RADAR) + geo_ch->flags |= IEEE80211_CHAN_RADAR; + + geo_ch->flags |= ch->ht40_extension_channel; + + if (ch->max_power_avg > priv->tx_power_device_lmt) + priv->tx_power_device_lmt = ch->max_power_avg; + } else { + geo_ch->flags |= IEEE80211_CHAN_DISABLED; + } + + IWL_DEBUG_INFO(priv, "Channel %d Freq=%d[%sGHz] %s flag=0x%X\n", + ch->channel, geo_ch->center_freq, + iwl_legacy_is_channel_a_band(ch) ? "5.2" : "2.4", + geo_ch->flags & IEEE80211_CHAN_DISABLED ? + "restricted" : "valid", + geo_ch->flags); + } + + if ((priv->bands[IEEE80211_BAND_5GHZ].n_channels == 0) && + priv->cfg->sku & IWL_SKU_A) { + IWL_INFO(priv, "Incorrectly detected BG card as ABG. " + "Please send your PCI ID 0x%04X:0x%04X to maintainer.\n", + priv->pci_dev->device, + priv->pci_dev->subsystem_device); + priv->cfg->sku &= ~IWL_SKU_A; + } + + IWL_INFO(priv, "Tunable channels: %d 802.11bg, %d 802.11a channels\n", + priv->bands[IEEE80211_BAND_2GHZ].n_channels, + priv->bands[IEEE80211_BAND_5GHZ].n_channels); + + set_bit(STATUS_GEO_CONFIGURED, &priv->status); + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_init_geos); + +/* + * iwl_legacy_free_geos - undo allocations in iwl_legacy_init_geos + */ +void iwl_legacy_free_geos(struct iwl_priv *priv) +{ + kfree(priv->ieee_channels); + kfree(priv->ieee_rates); + clear_bit(STATUS_GEO_CONFIGURED, &priv->status); +} +EXPORT_SYMBOL(iwl_legacy_free_geos); + +static bool iwl_legacy_is_channel_extension(struct iwl_priv *priv, + enum ieee80211_band band, + u16 channel, u8 extension_chan_offset) +{ + const struct iwl_channel_info *ch_info; + + ch_info = iwl_legacy_get_channel_info(priv, band, channel); + if (!iwl_legacy_is_channel_valid(ch_info)) + return false; + + if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + return !(ch_info->ht40_extension_channel & + IEEE80211_CHAN_NO_HT40PLUS); + else if (extension_chan_offset == IEEE80211_HT_PARAM_CHA_SEC_BELOW) + return !(ch_info->ht40_extension_channel & + IEEE80211_CHAN_NO_HT40MINUS); + + return false; +} + +bool iwl_legacy_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta_ht_cap *ht_cap) +{ + if (!ctx->ht.enabled || !ctx->ht.is_40mhz) + return false; + + /* + * We do not check for IEEE80211_HT_CAP_SUP_WIDTH_20_40 + * the bit will not set if it is pure 40MHz case + */ + if (ht_cap && !ht_cap->ht_supported) + return false; + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + if (priv->disable_ht40) + return false; +#endif + + return iwl_legacy_is_channel_extension(priv, priv->band, + le16_to_cpu(ctx->staging.channel), + ctx->ht.extension_chan_offset); +} +EXPORT_SYMBOL(iwl_legacy_is_ht40_tx_allowed); + +static u16 iwl_legacy_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) +{ + u16 new_val; + u16 beacon_factor; + + /* + * If mac80211 hasn't given us a beacon interval, program + * the default into the device. + */ + if (!beacon_val) + return DEFAULT_BEACON_INTERVAL; + + /* + * If the beacon interval we obtained from the peer + * is too large, we'll have to wake up more often + * (and in IBSS case, we'll beacon too much) + * + * For example, if max_beacon_val is 4096, and the + * requested beacon interval is 7000, we'll have to + * use 3500 to be able to wake up on the beacons. + * + * This could badly influence beacon detection stats. + */ + + beacon_factor = (beacon_val + max_beacon_val) / max_beacon_val; + new_val = beacon_val / beacon_factor; + + if (!new_val) + new_val = max_beacon_val; + + return new_val; +} + +int +iwl_legacy_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + u64 tsf; + s32 interval_tm, rem; + struct ieee80211_conf *conf = NULL; + u16 beacon_int; + struct ieee80211_vif *vif = ctx->vif; + + conf = iwl_legacy_ieee80211_get_hw_conf(priv->hw); + + lockdep_assert_held(&priv->mutex); + + memset(&ctx->timing, 0, sizeof(struct iwl_rxon_time_cmd)); + + ctx->timing.timestamp = cpu_to_le64(priv->timestamp); + ctx->timing.listen_interval = cpu_to_le16(conf->listen_interval); + + beacon_int = vif ? vif->bss_conf.beacon_int : 0; + + /* + * TODO: For IBSS we need to get atim_window from mac80211, + * for now just always use 0 + */ + ctx->timing.atim_window = 0; + + beacon_int = iwl_legacy_adjust_beacon_interval(beacon_int, + priv->hw_params.max_beacon_itrvl * TIME_UNIT); + ctx->timing.beacon_interval = cpu_to_le16(beacon_int); + + tsf = priv->timestamp; /* tsf is modifed by do_div: copy it */ + interval_tm = beacon_int * TIME_UNIT; + rem = do_div(tsf, interval_tm); + ctx->timing.beacon_init_val = cpu_to_le32(interval_tm - rem); + + ctx->timing.dtim_period = vif ? (vif->bss_conf.dtim_period ?: 1) : 1; + + IWL_DEBUG_ASSOC(priv, + "beacon interval %d beacon timer %d beacon tim %d\n", + le16_to_cpu(ctx->timing.beacon_interval), + le32_to_cpu(ctx->timing.beacon_init_val), + le16_to_cpu(ctx->timing.atim_window)); + + return iwl_legacy_send_cmd_pdu(priv, ctx->rxon_timing_cmd, + sizeof(ctx->timing), &ctx->timing); +} +EXPORT_SYMBOL(iwl_legacy_send_rxon_timing); + +void +iwl_legacy_set_rxon_hwcrypto(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + int hw_decrypt) +{ + struct iwl_legacy_rxon_cmd *rxon = &ctx->staging; + + if (hw_decrypt) + rxon->filter_flags &= ~RXON_FILTER_DIS_DECRYPT_MSK; + else + rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; + +} +EXPORT_SYMBOL(iwl_legacy_set_rxon_hwcrypto); + +/* validate RXON structure is valid */ +int +iwl_legacy_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + struct iwl_legacy_rxon_cmd *rxon = &ctx->staging; + bool error = false; + + if (rxon->flags & RXON_FLG_BAND_24G_MSK) { + if (rxon->flags & RXON_FLG_TGJ_NARROW_BAND_MSK) { + IWL_WARN(priv, "check 2.4G: wrong narrow\n"); + error = true; + } + if (rxon->flags & RXON_FLG_RADAR_DETECT_MSK) { + IWL_WARN(priv, "check 2.4G: wrong radar\n"); + error = true; + } + } else { + if (!(rxon->flags & RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "check 5.2G: not short slot!\n"); + error = true; + } + if (rxon->flags & RXON_FLG_CCK_MSK) { + IWL_WARN(priv, "check 5.2G: CCK!\n"); + error = true; + } + } + if ((rxon->node_addr[0] | rxon->bssid_addr[0]) & 0x1) { + IWL_WARN(priv, "mac/bssid mcast!\n"); + error = true; + } + + /* make sure basic rates 6Mbps and 1Mbps are supported */ + if ((rxon->ofdm_basic_rates & IWL_RATE_6M_MASK) == 0 && + (rxon->cck_basic_rates & IWL_RATE_1M_MASK) == 0) { + IWL_WARN(priv, "neither 1 nor 6 are basic\n"); + error = true; + } + + if (le16_to_cpu(rxon->assoc_id) > 2007) { + IWL_WARN(priv, "aid > 2007\n"); + error = true; + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_SHORT_SLOT_MSK)) { + IWL_WARN(priv, "CCK and short slot\n"); + error = true; + } + + if ((rxon->flags & (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) + == (RXON_FLG_CCK_MSK | RXON_FLG_AUTO_DETECT_MSK)) { + IWL_WARN(priv, "CCK and auto detect"); + error = true; + } + + if ((rxon->flags & (RXON_FLG_AUTO_DETECT_MSK | + RXON_FLG_TGG_PROTECT_MSK)) == + RXON_FLG_TGG_PROTECT_MSK) { + IWL_WARN(priv, "TGg but no auto-detect\n"); + error = true; + } + + if (error) + IWL_WARN(priv, "Tuning to channel %d\n", + le16_to_cpu(rxon->channel)); + + if (error) { + IWL_ERR(priv, "Invalid RXON\n"); + return -EINVAL; + } + return 0; +} +EXPORT_SYMBOL(iwl_legacy_check_rxon_cmd); + +/** + * iwl_legacy_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed + * @priv: staging_rxon is compared to active_rxon + * + * If the RXON structure is changing enough to require a new tune, + * or is clearing the RXON_FILTER_ASSOC_MSK, then return 1 to indicate that + * a new tune (full RXON command, rather than RXON_ASSOC cmd) is required. + */ +int iwl_legacy_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + const struct iwl_legacy_rxon_cmd *staging = &ctx->staging; + const struct iwl_legacy_rxon_cmd *active = &ctx->active; + +#define CHK(cond) \ + if ((cond)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " #cond "\n"); \ + return 1; \ + } + +#define CHK_NEQ(c1, c2) \ + if ((c1) != (c2)) { \ + IWL_DEBUG_INFO(priv, "need full RXON - " \ + #c1 " != " #c2 " - %d != %d\n", \ + (c1), (c2)); \ + return 1; \ + } + + /* These items are only settable from the full RXON command */ + CHK(!iwl_legacy_is_associated_ctx(ctx)); + CHK(compare_ether_addr(staging->bssid_addr, active->bssid_addr)); + CHK(compare_ether_addr(staging->node_addr, active->node_addr)); + CHK(compare_ether_addr(staging->wlap_bssid_addr, + active->wlap_bssid_addr)); + CHK_NEQ(staging->dev_type, active->dev_type); + CHK_NEQ(staging->channel, active->channel); + CHK_NEQ(staging->air_propagation, active->air_propagation); + CHK_NEQ(staging->ofdm_ht_single_stream_basic_rates, + active->ofdm_ht_single_stream_basic_rates); + CHK_NEQ(staging->ofdm_ht_dual_stream_basic_rates, + active->ofdm_ht_dual_stream_basic_rates); + CHK_NEQ(staging->assoc_id, active->assoc_id); + + /* flags, filter_flags, ofdm_basic_rates, and cck_basic_rates can + * be updated with the RXON_ASSOC command -- however only some + * flag transitions are allowed using RXON_ASSOC */ + + /* Check if we are not switching bands */ + CHK_NEQ(staging->flags & RXON_FLG_BAND_24G_MSK, + active->flags & RXON_FLG_BAND_24G_MSK); + + /* Check if we are switching association toggle */ + CHK_NEQ(staging->filter_flags & RXON_FILTER_ASSOC_MSK, + active->filter_flags & RXON_FILTER_ASSOC_MSK); + +#undef CHK +#undef CHK_NEQ + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_full_rxon_required); + +u8 iwl_legacy_get_lowest_plcp(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + /* + * Assign the lowest rate -- should really get this from + * the beacon skb from mac80211. + */ + if (ctx->staging.flags & RXON_FLG_BAND_24G_MSK) + return IWL_RATE_1M_PLCP; + else + return IWL_RATE_6M_PLCP; +} +EXPORT_SYMBOL(iwl_legacy_get_lowest_plcp); + +static void _iwl_legacy_set_rxon_ht(struct iwl_priv *priv, + struct iwl_ht_config *ht_conf, + struct iwl_rxon_context *ctx) +{ + struct iwl_legacy_rxon_cmd *rxon = &ctx->staging; + + if (!ctx->ht.enabled) { + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK | + RXON_FLG_HT40_PROT_MSK | + RXON_FLG_HT_PROT_MSK); + return; + } + + rxon->flags |= cpu_to_le32(ctx->ht.protection << + RXON_FLG_HT_OPERATING_MODE_POS); + + /* Set up channel bandwidth: + * 20 MHz only, 20/40 mixed or pure 40 if ht40 ok */ + /* clear the HT channel mode before set the mode */ + rxon->flags &= ~(RXON_FLG_CHANNEL_MODE_MSK | + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + if (iwl_legacy_is_ht40_tx_allowed(priv, ctx, NULL)) { + /* pure ht40 */ + if (ctx->ht.protection == + IEEE80211_HT_OP_MODE_PROTECTION_20MHZ) { + rxon->flags |= RXON_FLG_CHANNEL_MODE_PURE_40; + /* Note: control channel is opposite of extension channel */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + break; + } + } else { + /* Note: control channel is opposite of extension channel */ + switch (ctx->ht.extension_chan_offset) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + rxon->flags &= + ~(RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK); + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + rxon->flags |= + RXON_FLG_CTRL_CHANNEL_LOC_HI_MSK; + rxon->flags |= RXON_FLG_CHANNEL_MODE_MIXED; + break; + case IEEE80211_HT_PARAM_CHA_SEC_NONE: + default: + /* channel location only valid if in Mixed mode */ + IWL_ERR(priv, + "invalid extension channel offset\n"); + break; + } + } + } else { + rxon->flags |= RXON_FLG_CHANNEL_MODE_LEGACY; + } + + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + + IWL_DEBUG_ASSOC(priv, "rxon flags 0x%X operation mode :0x%X " + "extension channel offset 0x%x\n", + le32_to_cpu(rxon->flags), ctx->ht.protection, + ctx->ht.extension_chan_offset); +} + +void iwl_legacy_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) +{ + struct iwl_rxon_context *ctx; + + for_each_context(priv, ctx) + _iwl_legacy_set_rxon_ht(priv, ht_conf, ctx); +} +EXPORT_SYMBOL(iwl_legacy_set_rxon_ht); + +/* Return valid, unused, channel for a passive scan to reset the RF */ +u8 iwl_legacy_get_single_channel_number(struct iwl_priv *priv, + enum ieee80211_band band) +{ + const struct iwl_channel_info *ch_info; + int i; + u8 channel = 0; + u8 min, max; + struct iwl_rxon_context *ctx; + + if (band == IEEE80211_BAND_5GHZ) { + min = 14; + max = priv->channel_count; + } else { + min = 0; + max = 14; + } + + for (i = min; i < max; i++) { + bool busy = false; + + for_each_context(priv, ctx) { + busy = priv->channel_info[i].channel == + le16_to_cpu(ctx->staging.channel); + if (busy) + break; + } + + if (busy) + continue; + + channel = priv->channel_info[i].channel; + ch_info = iwl_legacy_get_channel_info(priv, band, channel); + if (iwl_legacy_is_channel_valid(ch_info)) + break; + } + + return channel; +} +EXPORT_SYMBOL(iwl_legacy_get_single_channel_number); + +/** + * iwl_legacy_set_rxon_channel - Set the band and channel values in staging RXON + * @ch: requested channel as a pointer to struct ieee80211_channel + + * NOTE: Does not commit to the hardware; it sets appropriate bit fields + * in the staging RXON flag structure based on the ch->band + */ +int +iwl_legacy_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx) +{ + enum ieee80211_band band = ch->band; + u16 channel = ch->hw_value; + + if ((le16_to_cpu(ctx->staging.channel) == channel) && + (priv->band == band)) + return 0; + + ctx->staging.channel = cpu_to_le16(channel); + if (band == IEEE80211_BAND_5GHZ) + ctx->staging.flags &= ~RXON_FLG_BAND_24G_MSK; + else + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + + priv->band = band; + + IWL_DEBUG_INFO(priv, "Staging channel set to %d [%d]\n", channel, band); + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_set_rxon_channel); + +void iwl_legacy_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + if (band == IEEE80211_BAND_5GHZ) { + ctx->staging.flags &= + ~(RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK + | RXON_FLG_CCK_MSK); + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + } else { + /* Copied from iwl_post_associate() */ + if (vif && vif->bss_conf.use_short_slot) + ctx->staging.flags |= RXON_FLG_SHORT_SLOT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_SLOT_MSK; + + ctx->staging.flags |= RXON_FLG_BAND_24G_MSK; + ctx->staging.flags |= RXON_FLG_AUTO_DETECT_MSK; + ctx->staging.flags &= ~RXON_FLG_CCK_MSK; + } +} +EXPORT_SYMBOL(iwl_legacy_set_flags_for_band); + +/* + * initialize rxon structure with default values from eeprom + */ +void iwl_legacy_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + const struct iwl_channel_info *ch_info; + + memset(&ctx->staging, 0, sizeof(ctx->staging)); + + if (!ctx->vif) { + ctx->staging.dev_type = ctx->unused_devtype; + } else + switch (ctx->vif->type) { + + case NL80211_IFTYPE_STATION: + ctx->staging.dev_type = ctx->station_devtype; + ctx->staging.filter_flags = RXON_FILTER_ACCEPT_GRP_MSK; + break; + + case NL80211_IFTYPE_ADHOC: + ctx->staging.dev_type = ctx->ibss_devtype; + ctx->staging.flags = RXON_FLG_SHORT_PREAMBLE_MSK; + ctx->staging.filter_flags = RXON_FILTER_BCON_AWARE_MSK | + RXON_FILTER_ACCEPT_GRP_MSK; + break; + + default: + IWL_ERR(priv, "Unsupported interface type %d\n", + ctx->vif->type); + break; + } + +#if 0 + /* TODO: Figure out when short_preamble would be set and cache from + * that */ + if (!hw_to_local(priv->hw)->short_preamble) + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; +#endif + + ch_info = iwl_legacy_get_channel_info(priv, priv->band, + le16_to_cpu(ctx->active.channel)); + + if (!ch_info) + ch_info = &priv->channel_info[0]; + + ctx->staging.channel = cpu_to_le16(ch_info->channel); + priv->band = ch_info->band; + + iwl_legacy_set_flags_for_band(priv, ctx, priv->band, ctx->vif); + + ctx->staging.ofdm_basic_rates = + (IWL_OFDM_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + ctx->staging.cck_basic_rates = + (IWL_CCK_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; + + /* clear both MIX and PURE40 mode flag */ + ctx->staging.flags &= ~(RXON_FLG_CHANNEL_MODE_MIXED | + RXON_FLG_CHANNEL_MODE_PURE_40); + if (ctx->vif) + memcpy(ctx->staging.node_addr, ctx->vif->addr, ETH_ALEN); + + ctx->staging.ofdm_ht_single_stream_basic_rates = 0xff; + ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; +} +EXPORT_SYMBOL(iwl_legacy_connection_init_rx_config); + +void iwl_legacy_set_rate(struct iwl_priv *priv) +{ + const struct ieee80211_supported_band *hw = NULL; + struct ieee80211_rate *rate; + struct iwl_rxon_context *ctx; + int i; + + hw = iwl_get_hw_mode(priv, priv->band); + if (!hw) { + IWL_ERR(priv, "Failed to set rate: unable to get hw mode\n"); + return; + } + + priv->active_rate = 0; + + for (i = 0; i < hw->n_bitrates; i++) { + rate = &(hw->bitrates[i]); + if (rate->hw_value < IWL_RATE_COUNT_LEGACY) + priv->active_rate |= (1 << rate->hw_value); + } + + IWL_DEBUG_RATE(priv, "Set active_rate = %0x\n", priv->active_rate); + + for_each_context(priv, ctx) { + ctx->staging.cck_basic_rates = + (IWL_CCK_BASIC_RATES_MASK >> IWL_FIRST_CCK_RATE) & 0xF; + + ctx->staging.ofdm_basic_rates = + (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; + } +} +EXPORT_SYMBOL(iwl_legacy_set_rate); + +void iwl_legacy_chswitch_done(struct iwl_priv *priv, bool is_success) +{ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->switch_rxon.switch_in_progress) { + ieee80211_chswitch_done(ctx->vif, is_success); + mutex_lock(&priv->mutex); + priv->switch_rxon.switch_in_progress = false; + mutex_unlock(&priv->mutex); + } +} +EXPORT_SYMBOL(iwl_legacy_chswitch_done); + +void iwl_legacy_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_csa_notification *csa = &(pkt->u.csa_notif); + + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + struct iwl_legacy_rxon_cmd *rxon = (void *)&ctx->active; + + if (priv->switch_rxon.switch_in_progress) { + if (!le32_to_cpu(csa->status) && + (csa->channel == priv->switch_rxon.channel)) { + rxon->channel = csa->channel; + ctx->staging.channel = csa->channel; + IWL_DEBUG_11H(priv, "CSA notif: channel %d\n", + le16_to_cpu(csa->channel)); + iwl_legacy_chswitch_done(priv, true); + } else { + IWL_ERR(priv, "CSA notif (fail) : channel %d\n", + le16_to_cpu(csa->channel)); + iwl_legacy_chswitch_done(priv, false); + } + } +} +EXPORT_SYMBOL(iwl_legacy_rx_csa); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +void iwl_legacy_print_rx_config_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct iwl_legacy_rxon_cmd *rxon = &ctx->staging; + + IWL_DEBUG_RADIO(priv, "RX CONFIG:\n"); + iwl_print_hex_dump(priv, IWL_DL_RADIO, (u8 *) rxon, sizeof(*rxon)); + IWL_DEBUG_RADIO(priv, "u16 channel: 0x%x\n", + le16_to_cpu(rxon->channel)); + IWL_DEBUG_RADIO(priv, "u32 flags: 0x%08X\n", le32_to_cpu(rxon->flags)); + IWL_DEBUG_RADIO(priv, "u32 filter_flags: 0x%08x\n", + le32_to_cpu(rxon->filter_flags)); + IWL_DEBUG_RADIO(priv, "u8 dev_type: 0x%x\n", rxon->dev_type); + IWL_DEBUG_RADIO(priv, "u8 ofdm_basic_rates: 0x%02x\n", + rxon->ofdm_basic_rates); + IWL_DEBUG_RADIO(priv, "u8 cck_basic_rates: 0x%02x\n", + rxon->cck_basic_rates); + IWL_DEBUG_RADIO(priv, "u8[6] node_addr: %pM\n", rxon->node_addr); + IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); + IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", + le16_to_cpu(rxon->assoc_id)); +} +EXPORT_SYMBOL(iwl_legacy_print_rx_config_cmd); +#endif +/** + * iwl_legacy_irq_handle_error - called for HW or SW error interrupt from card + */ +void iwl_legacy_irq_handle_error(struct iwl_priv *priv) +{ + /* Set the FW error flag -- cleared on iwl_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + + /* Cancel currently queued command. */ + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + + IWL_ERR(priv, "Loaded firmware version: %s\n", + priv->hw->wiphy->fw_version); + + priv->cfg->ops->lib->dump_nic_error_log(priv); + if (priv->cfg->ops->lib->dump_fh) + priv->cfg->ops->lib->dump_fh(priv, NULL, false); + priv->cfg->ops->lib->dump_nic_event_log(priv, false, NULL, false); +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) + iwl_legacy_print_rx_config_cmd(priv, + &priv->contexts[IWL_RXON_CTX_BSS]); +#endif + + wake_up_interruptible(&priv->wait_command_queue); + + /* Keep the restart process from trying to send host + * commands by clearing the INIT status bit */ + clear_bit(STATUS_READY, &priv->status); + + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_DEBUG(priv, IWL_DL_FW_ERRORS, + "Restarting adapter due to uCode error.\n"); + + if (priv->cfg->mod_params->restart_fw) + queue_work(priv->workqueue, &priv->restart); + } +} +EXPORT_SYMBOL(iwl_legacy_irq_handle_error); + +static int iwl_legacy_apm_stop_master(struct iwl_priv *priv) +{ + int ret = 0; + + /* stop device's busmaster DMA activity */ + iwl_legacy_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_STOP_MASTER); + + ret = iwl_poll_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_MASTER_DISABLED, + CSR_RESET_REG_FLAG_MASTER_DISABLED, 100); + if (ret) + IWL_WARN(priv, "Master Disable Timed Out, 100 usec\n"); + + IWL_DEBUG_INFO(priv, "stop master\n"); + + return ret; +} + +void iwl_legacy_apm_stop(struct iwl_priv *priv) +{ + IWL_DEBUG_INFO(priv, "Stop card, put in low power state\n"); + + /* Stop device's DMA activity */ + iwl_legacy_apm_stop_master(priv); + + /* Reset the entire device */ + iwl_legacy_set_bit(priv, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + + udelay(10); + + /* + * Clear "initialization complete" bit to move adapter from + * D0A* (powered-up Active) --> D0U* (Uninitialized) state. + */ + iwl_legacy_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); +} +EXPORT_SYMBOL(iwl_legacy_apm_stop); + + +/* + * Start up NIC's basic functionality after it has been reset + * (e.g. after platform boot, or shutdown via iwl_legacy_apm_stop()) + * NOTE: This does not load uCode nor start the embedded processor + */ +int iwl_legacy_apm_init(struct iwl_priv *priv) +{ + int ret = 0; + u16 lctl; + + IWL_DEBUG_INFO(priv, "Init card's basic functions\n"); + + /* + * Use "set_bit" below rather than "write", to preserve any hardware + * bits already set by default after reset. + */ + + /* Disable L0S exit timer (platform NMI Work/Around) */ + iwl_legacy_set_bit(priv, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + + /* + * Disable L0s without affecting L1; + * don't wait for ICH L0s (ICH bug W/A) + */ + iwl_legacy_set_bit(priv, CSR_GIO_CHICKEN_BITS, + CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); + + /* Set FH wait threshold to maximum (HW error during stress W/A) */ + iwl_legacy_set_bit(priv, CSR_DBG_HPET_MEM_REG, + CSR_DBG_HPET_MEM_REG_VAL); + + /* + * Enable HAP INTA (interrupt from management bus) to + * wake device's PCI Express link L1a -> L0s + * NOTE: This is no-op for 3945 (non-existant bit) + */ + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); + + /* + * HW bug W/A for instability in PCIe bus L0->L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + if (priv->cfg->base_params->set_l0s) { + lctl = iwl_legacy_pcie_link_ctl(priv); + if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == + PCI_CFG_LINK_CTRL_VAL_L1_EN) { + /* L1-ASPM enabled; disable(!) L0S */ + iwl_legacy_set_bit(priv, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + IWL_DEBUG_POWER(priv, "L1 Enabled; Disabling L0S\n"); + } else { + /* L1-ASPM disabled; enable(!) L0S */ + iwl_legacy_clear_bit(priv, CSR_GIO_REG, + CSR_GIO_REG_VAL_L0S_ENABLED); + IWL_DEBUG_POWER(priv, "L1 Disabled; Enabling L0S\n"); + } + } + + /* Configure analog phase-lock-loop before activating to D0A */ + if (priv->cfg->base_params->pll_cfg_val) + iwl_legacy_set_bit(priv, CSR_ANA_PLL_CFG, + priv->cfg->base_params->pll_cfg_val); + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_legacy_set_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_legacy_write_prph() + * and accesses to uCode SRAM. + */ + ret = iwl_poll_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000); + if (ret < 0) { + IWL_DEBUG_INFO(priv, "Failed to init the card\n"); + goto out; + } + + /* + * Enable DMA and BSM (if used) clocks, wait for them to stabilize. + * BSM (Boostrap State Machine) is only in 3945 and 4965. + * + * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits + * do not disable clocks. This preserves any hardware bits already + * set by default in "CLK_CTRL_REG" after reset. + */ + if (priv->cfg->base_params->use_bsm) + iwl_legacy_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT | APMG_CLK_VAL_BSM_CLK_RQT); + else + iwl_legacy_write_prph(priv, APMG_CLK_EN_REG, + APMG_CLK_VAL_DMA_CLK_RQT); + udelay(20); + + /* Disable L1-Active */ + iwl_legacy_set_bits_prph(priv, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + +out: + return ret; +} +EXPORT_SYMBOL(iwl_legacy_apm_init); + + +int iwl_legacy_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) +{ + int ret; + s8 prev_tx_power; + bool defer; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + lockdep_assert_held(&priv->mutex); + + if (priv->tx_power_user_lmt == tx_power && !force) + return 0; + + if (!priv->cfg->ops->lib->send_tx_power) + return -EOPNOTSUPP; + + if (tx_power < IWL4965_TX_POWER_TARGET_POWER_MIN) { + IWL_WARN(priv, + "Requested user TXPOWER %d below lower limit %d.\n", + tx_power, + IWL4965_TX_POWER_TARGET_POWER_MIN); + return -EINVAL; + } + + if (tx_power > priv->tx_power_device_lmt) { + IWL_WARN(priv, + "Requested user TXPOWER %d above upper limit %d.\n", + tx_power, priv->tx_power_device_lmt); + return -EINVAL; + } + + if (!iwl_legacy_is_ready_rf(priv)) + return -EIO; + + /* scan complete and commit_rxon use tx_power_next value, + * it always need to be updated for newest request */ + priv->tx_power_next = tx_power; + + /* do not set tx power when scanning or channel changing */ + defer = test_bit(STATUS_SCANNING, &priv->status) || + memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging)); + if (defer && !force) { + IWL_DEBUG_INFO(priv, "Deferring tx power set\n"); + return 0; + } + + prev_tx_power = priv->tx_power_user_lmt; + priv->tx_power_user_lmt = tx_power; + + ret = priv->cfg->ops->lib->send_tx_power(priv); + + /* if fail to set tx_power, restore the orig. tx power */ + if (ret) { + priv->tx_power_user_lmt = prev_tx_power; + priv->tx_power_next = prev_tx_power; + } + return ret; +} +EXPORT_SYMBOL(iwl_legacy_set_tx_power); + +void iwl_legacy_send_bt_config(struct iwl_priv *priv) +{ + struct iwl_bt_cmd bt_cmd = { + .lead_time = BT_LEAD_TIME_DEF, + .max_kill = BT_MAX_KILL_DEF, + .kill_ack_mask = 0, + .kill_cts_mask = 0, + }; + + if (!bt_coex_active) + bt_cmd.flags = BT_COEX_DISABLE; + else + bt_cmd.flags = BT_COEX_ENABLE; + + IWL_DEBUG_INFO(priv, "BT coex %s\n", + (bt_cmd.flags == BT_COEX_DISABLE) ? "disable" : "active"); + + if (iwl_legacy_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(struct iwl_bt_cmd), &bt_cmd)) + IWL_ERR(priv, "failed to send BT Coex Config\n"); +} +EXPORT_SYMBOL(iwl_legacy_send_bt_config); + +int iwl_legacy_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) +{ + struct iwl_statistics_cmd statistics_cmd = { + .configuration_flags = + clear ? IWL_STATS_CONF_CLEAR_STATS : 0, + }; + + if (flags & CMD_ASYNC) + return iwl_legacy_send_cmd_pdu_async(priv, REPLY_STATISTICS_CMD, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd, NULL); + else + return iwl_legacy_send_cmd_pdu(priv, REPLY_STATISTICS_CMD, + sizeof(struct iwl_statistics_cmd), + &statistics_cmd); +} +EXPORT_SYMBOL(iwl_legacy_send_statistics_request); + +void iwl_legacy_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_sleep_notification *sleep = &(pkt->u.sleep_notif); + IWL_DEBUG_RX(priv, "sleep mode: %d, src: %d\n", + sleep->pm_sleep_mode, sleep->pm_wakeup_src); +#endif +} +EXPORT_SYMBOL(iwl_legacy_rx_pm_sleep_notif); + +void iwl_legacy_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + IWL_DEBUG_RADIO(priv, "Dumping %d bytes of unhandled " + "notification for %s:\n", len, + iwl_legacy_get_cmd_string(pkt->hdr.cmd)); + iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len); +} +EXPORT_SYMBOL(iwl_legacy_rx_pm_debug_statistics_notif); + +void iwl_legacy_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + IWL_ERR(priv, "Error Reply type 0x%08X cmd %s (0x%02X) " + "seq 0x%04X ser 0x%08X\n", + le32_to_cpu(pkt->u.err_resp.error_type), + iwl_legacy_get_cmd_string(pkt->u.err_resp.cmd_id), + pkt->u.err_resp.cmd_id, + le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), + le32_to_cpu(pkt->u.err_resp.error_info)); +} +EXPORT_SYMBOL(iwl_legacy_rx_reply_error); + +void iwl_legacy_clear_isr_stats(struct iwl_priv *priv) +{ + memset(&priv->isr_stats, 0, sizeof(priv->isr_stats)); +} + +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx; + unsigned long flags; + int q; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!iwl_legacy_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); + return -EIO; + } + + if (queue >= AC_NUM) { + IWL_DEBUG_MAC80211(priv, "leave - queue >= AC_NUM %d\n", queue); + return 0; + } + + q = AC_NUM - 1 - queue; + + spin_lock_irqsave(&priv->lock, flags); + + for_each_context(priv, ctx) { + ctx->qos_data.def_qos_parm.ac[q].cw_min = + cpu_to_le16(params->cw_min); + ctx->qos_data.def_qos_parm.ac[q].cw_max = + cpu_to_le16(params->cw_max); + ctx->qos_data.def_qos_parm.ac[q].aifsn = params->aifs; + ctx->qos_data.def_qos_parm.ac[q].edca_txop = + cpu_to_le16((params->txop * 32)); + + ctx->qos_data.def_qos_parm.ac[q].reserved1 = 0; + } + + spin_unlock_irqrestore(&priv->lock, flags); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} +EXPORT_SYMBOL(iwl_legacy_mac_conf_tx); + +int iwl_legacy_mac_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + return priv->ibss_manager == IWL_IBSS_MANAGER; +} +EXPORT_SYMBOL_GPL(iwl_legacy_mac_tx_last_beacon); + +static int +iwl_legacy_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + iwl_legacy_connection_init_rx_config(priv, ctx); + + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + + return iwl_legacy_commit_rxon(priv, ctx); +} + +static int iwl_legacy_setup_interface(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + struct ieee80211_vif *vif = ctx->vif; + int err; + + lockdep_assert_held(&priv->mutex); + + /* + * This variable will be correct only when there's just + * a single context, but all code using it is for hardware + * that supports only one context. + */ + priv->iw_mode = vif->type; + + ctx->is_active = true; + + err = iwl_legacy_set_mode(priv, ctx); + if (err) { + if (!ctx->always_active) + ctx->is_active = false; + return err; + } + + return 0; +} + +int +iwl_legacy_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *tmp, *ctx = NULL; + int err; + + IWL_DEBUG_MAC80211(priv, "enter: type %d, addr %pM\n", + vif->type, vif->addr); + + mutex_lock(&priv->mutex); + + if (!iwl_legacy_is_ready_rf(priv)) { + IWL_WARN(priv, "Try to add interface when device not ready\n"); + err = -EINVAL; + goto out; + } + + for_each_context(priv, tmp) { + u32 possible_modes = + tmp->interface_modes | tmp->exclusive_interface_modes; + + if (tmp->vif) { + /* check if this busy context is exclusive */ + if (tmp->exclusive_interface_modes & + BIT(tmp->vif->type)) { + err = -EINVAL; + goto out; + } + continue; + } + + if (!(possible_modes & BIT(vif->type))) + continue; + + /* have maybe usable context w/o interface */ + ctx = tmp; + break; + } + + if (!ctx) { + err = -EOPNOTSUPP; + goto out; + } + + vif_priv->ctx = ctx; + ctx->vif = vif; + + err = iwl_legacy_setup_interface(priv, ctx); + if (!err) + goto out; + + ctx->vif = NULL; + priv->iw_mode = NL80211_IFTYPE_STATION; + out: + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + return err; +} +EXPORT_SYMBOL(iwl_legacy_mac_add_interface); + +static void iwl_legacy_teardown_interface(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool mode_change) +{ + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + + lockdep_assert_held(&priv->mutex); + + if (priv->scan_vif == vif) { + iwl_legacy_scan_cancel_timeout(priv, 200); + iwl_legacy_force_scan_end(priv); + } + + if (!mode_change) { + iwl_legacy_set_mode(priv, ctx); + if (!ctx->always_active) + ctx->is_active = false; + } +} + +void iwl_legacy_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + mutex_lock(&priv->mutex); + + WARN_ON(ctx->vif != vif); + ctx->vif = NULL; + + iwl_legacy_teardown_interface(priv, vif, false); + + memset(priv->bssid, 0, ETH_ALEN); + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + +} +EXPORT_SYMBOL(iwl_legacy_mac_remove_interface); + +int iwl_legacy_alloc_txq_mem(struct iwl_priv *priv) +{ + if (!priv->txq) + priv->txq = kzalloc( + sizeof(struct iwl_tx_queue) * + priv->cfg->base_params->num_of_queues, + GFP_KERNEL); + if (!priv->txq) { + IWL_ERR(priv, "Not enough memory for txq\n"); + return -ENOMEM; + } + return 0; +} +EXPORT_SYMBOL(iwl_legacy_alloc_txq_mem); + +void iwl_legacy_txq_mem(struct iwl_priv *priv) +{ + kfree(priv->txq); + priv->txq = NULL; +} +EXPORT_SYMBOL(iwl_legacy_txq_mem); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + +#define IWL_TRAFFIC_DUMP_SIZE (IWL_TRAFFIC_ENTRY_SIZE * IWL_TRAFFIC_ENTRIES) + +void iwl_legacy_reset_traffic_log(struct iwl_priv *priv) +{ + priv->tx_traffic_idx = 0; + priv->rx_traffic_idx = 0; + if (priv->tx_traffic) + memset(priv->tx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); + if (priv->rx_traffic) + memset(priv->rx_traffic, 0, IWL_TRAFFIC_DUMP_SIZE); +} + +int iwl_legacy_alloc_traffic_mem(struct iwl_priv *priv) +{ + u32 traffic_size = IWL_TRAFFIC_DUMP_SIZE; + + if (iwlegacy_debug_level & IWL_DL_TX) { + if (!priv->tx_traffic) { + priv->tx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->tx_traffic) + return -ENOMEM; + } + } + if (iwlegacy_debug_level & IWL_DL_RX) { + if (!priv->rx_traffic) { + priv->rx_traffic = + kzalloc(traffic_size, GFP_KERNEL); + if (!priv->rx_traffic) + return -ENOMEM; + } + } + iwl_legacy_reset_traffic_log(priv); + return 0; +} +EXPORT_SYMBOL(iwl_legacy_alloc_traffic_mem); + +void iwl_legacy_free_traffic_mem(struct iwl_priv *priv) +{ + kfree(priv->tx_traffic); + priv->tx_traffic = NULL; + + kfree(priv->rx_traffic); + priv->rx_traffic = NULL; +} +EXPORT_SYMBOL(iwl_legacy_free_traffic_mem); + +void iwl_legacy_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwlegacy_debug_level & IWL_DL_TX))) + return; + + if (!priv->tx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->tx_traffic + + (priv->tx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->tx_traffic_idx = + (priv->tx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_legacy_dbg_log_tx_data_frame); + +void iwl_legacy_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ + __le16 fc; + u16 len; + + if (likely(!(iwlegacy_debug_level & IWL_DL_RX))) + return; + + if (!priv->rx_traffic) + return; + + fc = header->frame_control; + if (ieee80211_is_data(fc)) { + len = (length > IWL_TRAFFIC_ENTRY_SIZE) + ? IWL_TRAFFIC_ENTRY_SIZE : length; + memcpy((priv->rx_traffic + + (priv->rx_traffic_idx * IWL_TRAFFIC_ENTRY_SIZE)), + header, len); + priv->rx_traffic_idx = + (priv->rx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; + } +} +EXPORT_SYMBOL(iwl_legacy_dbg_log_rx_data_frame); + +const char *iwl_legacy_get_mgmt_string(int cmd) +{ + switch (cmd) { + IWL_CMD(MANAGEMENT_ASSOC_REQ); + IWL_CMD(MANAGEMENT_ASSOC_RESP); + IWL_CMD(MANAGEMENT_REASSOC_REQ); + IWL_CMD(MANAGEMENT_REASSOC_RESP); + IWL_CMD(MANAGEMENT_PROBE_REQ); + IWL_CMD(MANAGEMENT_PROBE_RESP); + IWL_CMD(MANAGEMENT_BEACON); + IWL_CMD(MANAGEMENT_ATIM); + IWL_CMD(MANAGEMENT_DISASSOC); + IWL_CMD(MANAGEMENT_AUTH); + IWL_CMD(MANAGEMENT_DEAUTH); + IWL_CMD(MANAGEMENT_ACTION); + default: + return "UNKNOWN"; + + } +} + +const char *iwl_legacy_get_ctrl_string(int cmd) +{ + switch (cmd) { + IWL_CMD(CONTROL_BACK_REQ); + IWL_CMD(CONTROL_BACK); + IWL_CMD(CONTROL_PSPOLL); + IWL_CMD(CONTROL_RTS); + IWL_CMD(CONTROL_CTS); + IWL_CMD(CONTROL_ACK); + IWL_CMD(CONTROL_CFEND); + IWL_CMD(CONTROL_CFENDACK); + default: + return "UNKNOWN"; + + } +} + +void iwl_legacy_clear_traffic_stats(struct iwl_priv *priv) +{ + memset(&priv->tx_stats, 0, sizeof(struct traffic_stats)); + memset(&priv->rx_stats, 0, sizeof(struct traffic_stats)); +} + +/* + * if CONFIG_IWLWIFI_LEGACY_DEBUGFS defined, + * iwl_legacy_update_stats function will + * record all the MGMT, CTRL and DATA pkt for both TX and Rx pass + * Use debugFs to display the rx/rx_statistics + * if CONFIG_IWLWIFI_LEGACY_DEBUGFS not being defined, then no MGMT and CTRL + * information will be recorded, but DATA pkt still will be recorded + * for the reason of iwl_led.c need to control the led blinking based on + * number of tx and rx data. + * + */ +void +iwl_legacy_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) +{ + struct traffic_stats *stats; + + if (is_tx) + stats = &priv->tx_stats; + else + stats = &priv->rx_stats; + + if (ieee80211_is_mgmt(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + stats->mgmt[MANAGEMENT_ASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ASSOC_RESP): + stats->mgmt[MANAGEMENT_ASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + stats->mgmt[MANAGEMENT_REASSOC_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_REASSOC_RESP): + stats->mgmt[MANAGEMENT_REASSOC_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_REQ): + stats->mgmt[MANAGEMENT_PROBE_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PROBE_RESP): + stats->mgmt[MANAGEMENT_PROBE_RESP]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BEACON): + stats->mgmt[MANAGEMENT_BEACON]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ATIM): + stats->mgmt[MANAGEMENT_ATIM]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DISASSOC): + stats->mgmt[MANAGEMENT_DISASSOC]++; + break; + case cpu_to_le16(IEEE80211_STYPE_AUTH): + stats->mgmt[MANAGEMENT_AUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + stats->mgmt[MANAGEMENT_DEAUTH]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACTION): + stats->mgmt[MANAGEMENT_ACTION]++; + break; + } + } else if (ieee80211_is_ctl(fc)) { + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_BACK_REQ): + stats->ctrl[CONTROL_BACK_REQ]++; + break; + case cpu_to_le16(IEEE80211_STYPE_BACK): + stats->ctrl[CONTROL_BACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_PSPOLL): + stats->ctrl[CONTROL_PSPOLL]++; + break; + case cpu_to_le16(IEEE80211_STYPE_RTS): + stats->ctrl[CONTROL_RTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CTS): + stats->ctrl[CONTROL_CTS]++; + break; + case cpu_to_le16(IEEE80211_STYPE_ACK): + stats->ctrl[CONTROL_ACK]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFEND): + stats->ctrl[CONTROL_CFEND]++; + break; + case cpu_to_le16(IEEE80211_STYPE_CFENDACK): + stats->ctrl[CONTROL_CFENDACK]++; + break; + } + } else { + /* data */ + stats->data_cnt++; + stats->data_bytes += len; + } +} +EXPORT_SYMBOL(iwl_legacy_update_stats); +#endif + +static void _iwl_legacy_force_rf_reset(struct iwl_priv *priv) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!iwl_legacy_is_any_associated(priv)) { + IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n"); + return; + } + /* + * There is no easy and better way to force reset the radio, + * the only known method is switching channel which will force to + * reset and tune the radio. + * Use internal short scan (single channel) operation to should + * achieve this objective. + * Driver should reset the radio when number of consecutive missed + * beacon, or any other uCode error condition detected. + */ + IWL_DEBUG_INFO(priv, "perform radio reset.\n"); + iwl_legacy_internal_short_hw_scan(priv); +} + + +int iwl_legacy_force_reset(struct iwl_priv *priv, int mode, bool external) +{ + struct iwl_force_reset *force_reset; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EINVAL; + + if (mode >= IWL_MAX_FORCE_RESET) { + IWL_DEBUG_INFO(priv, "invalid reset request.\n"); + return -EINVAL; + } + force_reset = &priv->force_reset[mode]; + force_reset->reset_request_count++; + if (!external) { + if (force_reset->last_force_reset_jiffies && + time_after(force_reset->last_force_reset_jiffies + + force_reset->reset_duration, jiffies)) { + IWL_DEBUG_INFO(priv, "force reset rejected\n"); + force_reset->reset_reject_count++; + return -EAGAIN; + } + } + force_reset->reset_success_count++; + force_reset->last_force_reset_jiffies = jiffies; + IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode); + switch (mode) { + case IWL_RF_RESET: + _iwl_legacy_force_rf_reset(priv); + break; + case IWL_FW_RESET: + /* + * if the request is from external(ex: debugfs), + * then always perform the request in regardless the module + * parameter setting + * if the request is from internal (uCode error or driver + * detect failure), then fw_restart module parameter + * need to be check before performing firmware reload + */ + if (!external && !priv->cfg->mod_params->restart_fw) { + IWL_DEBUG_INFO(priv, "Cancel firmware reload based on " + "module parameter setting\n"); + break; + } + IWL_ERR(priv, "On demand firmware reload\n"); + /* Set the FW error flag -- cleared on iwl_down */ + set_bit(STATUS_FW_ERROR, &priv->status); + wake_up_interruptible(&priv->wait_command_queue); + /* + * Keep the restart process from trying to send host + * commands by clearing the INIT status bit + */ + clear_bit(STATUS_READY, &priv->status); + queue_work(priv->workqueue, &priv->restart); + break; + } + return 0; +} + +int +iwl_legacy_mac_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + struct iwl_rxon_context *tmp; + u32 interface_modes; + int err; + + newtype = ieee80211_iftype_p2p(newtype, newp2p); + + mutex_lock(&priv->mutex); + + interface_modes = ctx->interface_modes | ctx->exclusive_interface_modes; + + if (!(interface_modes & BIT(newtype))) { + err = -EBUSY; + goto out; + } + + if (ctx->exclusive_interface_modes & BIT(newtype)) { + for_each_context(priv, tmp) { + if (ctx == tmp) + continue; + + if (!tmp->vif) + continue; + + /* + * The current mode switch would be exclusive, but + * another context is active ... refuse the switch. + */ + err = -EBUSY; + goto out; + } + } + + /* success */ + iwl_legacy_teardown_interface(priv, vif, true); + vif->type = newtype; + err = iwl_legacy_setup_interface(priv, ctx); + WARN_ON(err); + /* + * We've switched internally, but submitting to the + * device may have failed for some reason. Mask this + * error, because otherwise mac80211 will not switch + * (and set the interface type back) and we'll be + * out of sync with it. + */ + err = 0; + + out: + mutex_unlock(&priv->mutex); + return err; +} +EXPORT_SYMBOL(iwl_legacy_mac_change_interface); + +/* + * On every watchdog tick we check (latest) time stamp. If it does not + * change during timeout period and queue is not empty we reset firmware. + */ +static int iwl_legacy_check_stuck_queue(struct iwl_priv *priv, int cnt) +{ + struct iwl_tx_queue *txq = &priv->txq[cnt]; + struct iwl_queue *q = &txq->q; + unsigned long timeout; + int ret; + + if (q->read_ptr == q->write_ptr) { + txq->time_stamp = jiffies; + return 0; + } + + timeout = txq->time_stamp + + msecs_to_jiffies(priv->cfg->base_params->wd_timeout); + + if (time_after(jiffies, timeout)) { + IWL_ERR(priv, "Queue %d stuck for %u ms.\n", + q->id, priv->cfg->base_params->wd_timeout); + ret = iwl_legacy_force_reset(priv, IWL_FW_RESET, false); + return (ret == -EAGAIN) ? 0 : 1; + } + + return 0; +} + +/* + * Making watchdog tick be a quarter of timeout assure we will + * discover the queue hung between timeout and 1.25*timeout + */ +#define IWL_WD_TICK(timeout) ((timeout) / 4) + +/* + * Watchdog timer callback, we check each tx queue for stuck, if if hung + * we reset the firmware. If everything is fine just rearm the timer. + */ +void iwl_legacy_bg_watchdog(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + int cnt; + unsigned long timeout; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + timeout = priv->cfg->base_params->wd_timeout; + if (timeout == 0) + return; + + /* monitor and check for stuck cmd queue */ + if (iwl_legacy_check_stuck_queue(priv, priv->cmd_queue)) + return; + + /* monitor and check for other stuck queues */ + if (iwl_legacy_is_any_associated(priv)) { + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + /* skip as we already checked the command queue */ + if (cnt == priv->cmd_queue) + continue; + if (iwl_legacy_check_stuck_queue(priv, cnt)) + return; + } + } + + mod_timer(&priv->watchdog, jiffies + + msecs_to_jiffies(IWL_WD_TICK(timeout))); +} +EXPORT_SYMBOL(iwl_legacy_bg_watchdog); + +void iwl_legacy_setup_watchdog(struct iwl_priv *priv) +{ + unsigned int timeout = priv->cfg->base_params->wd_timeout; + + if (timeout) + mod_timer(&priv->watchdog, + jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout))); + else + del_timer(&priv->watchdog); +} +EXPORT_SYMBOL(iwl_legacy_setup_watchdog); + +/* + * extended beacon time format + * time in usec will be changed into a 32-bit value in extended:internal format + * the extended part is the beacon counts + * the internal part is the time in usec within one beacon interval + */ +u32 +iwl_legacy_usecs_to_beacons(struct iwl_priv *priv, + u32 usec, u32 beacon_interval) +{ + u32 quot; + u32 rem; + u32 interval = beacon_interval * TIME_UNIT; + + if (!interval || !usec) + return 0; + + quot = (usec / interval) & + (iwl_legacy_beacon_time_mask_high(priv, + priv->hw_params.beacon_time_tsf_bits) >> + priv->hw_params.beacon_time_tsf_bits); + rem = (usec % interval) & iwl_legacy_beacon_time_mask_low(priv, + priv->hw_params.beacon_time_tsf_bits); + + return (quot << priv->hw_params.beacon_time_tsf_bits) + rem; +} +EXPORT_SYMBOL(iwl_legacy_usecs_to_beacons); + +/* base is usually what we get from ucode with each received frame, + * the same as HW timer counter counting down + */ +__le32 iwl_legacy_add_beacon_time(struct iwl_priv *priv, u32 base, + u32 addon, u32 beacon_interval) +{ + u32 base_low = base & iwl_legacy_beacon_time_mask_low(priv, + priv->hw_params.beacon_time_tsf_bits); + u32 addon_low = addon & iwl_legacy_beacon_time_mask_low(priv, + priv->hw_params.beacon_time_tsf_bits); + u32 interval = beacon_interval * TIME_UNIT; + u32 res = (base & iwl_legacy_beacon_time_mask_high(priv, + priv->hw_params.beacon_time_tsf_bits)) + + (addon & iwl_legacy_beacon_time_mask_high(priv, + priv->hw_params.beacon_time_tsf_bits)); + + if (base_low > addon_low) + res += base_low - addon_low; + else if (base_low < addon_low) { + res += interval + base_low - addon_low; + res += (1 << priv->hw_params.beacon_time_tsf_bits); + } else + res += (1 << priv->hw_params.beacon_time_tsf_bits); + + return cpu_to_le32(res); +} +EXPORT_SYMBOL(iwl_legacy_add_beacon_time); + +#ifdef CONFIG_PM + +int iwl_legacy_pci_suspend(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_priv *priv = pci_get_drvdata(pdev); + + /* + * This function is called when system goes into suspend state + * mac80211 will call iwl_mac_stop() from the mac80211 suspend function + * first but since iwl_mac_stop() has no knowledge of who the caller is, + * it will not call apm_ops.stop() to stop the DMA operation. + * Calling apm_ops.stop here to make sure we stop the DMA. + */ + iwl_legacy_apm_stop(priv); + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_pci_suspend); + +int iwl_legacy_pci_resume(struct device *device) +{ + struct pci_dev *pdev = to_pci_dev(device); + struct iwl_priv *priv = pci_get_drvdata(pdev); + bool hw_rfkill = false; + + /* + * We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + iwl_legacy_enable_interrupts(priv); + + if (!(iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rfkill = true; + + if (hw_rfkill) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + wiphy_rfkill_set_hw_state(priv->hw->wiphy, hw_rfkill); + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_pci_resume); + +const struct dev_pm_ops iwl_legacy_pm_ops = { + .suspend = iwl_legacy_pci_suspend, + .resume = iwl_legacy_pci_resume, + .freeze = iwl_legacy_pci_suspend, + .thaw = iwl_legacy_pci_resume, + .poweroff = iwl_legacy_pci_suspend, + .restore = iwl_legacy_pci_resume, +}; +EXPORT_SYMBOL(iwl_legacy_pm_ops); + +#endif /* CONFIG_PM */ + +static void +iwl_legacy_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (!ctx->is_active) + return; + + ctx->qos_data.def_qos_parm.qos_flags = 0; + + if (ctx->qos_data.qos_active) + ctx->qos_data.def_qos_parm.qos_flags |= + QOS_PARAM_FLG_UPDATE_EDCA_MSK; + + if (ctx->ht.enabled) + ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; + + IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", + ctx->qos_data.qos_active, + ctx->qos_data.def_qos_parm.qos_flags); + + iwl_legacy_send_cmd_pdu_async(priv, ctx->qos_cmd, + sizeof(struct iwl_qosparam_cmd), + &ctx->qos_data.def_qos_parm, NULL); +} + +/** + * iwl_legacy_mac_config - mac80211 config callback + */ +int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed) +{ + struct iwl_priv *priv = hw->priv; + const struct iwl_channel_info *ch_info; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = conf->channel; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct iwl_rxon_context *ctx; + unsigned long flags = 0; + int ret = 0; + u16 ch; + int scan_active = 0; + bool ht_changed[NUM_IWL_RXON_CTX] = {}; + + if (WARN_ON(!priv->cfg->ops->legacy)) + return -EOPNOTSUPP; + + mutex_lock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n", + channel->hw_value, changed); + + if (unlikely(!priv->cfg->mod_params->disable_hw_scan && + test_bit(STATUS_SCANNING, &priv->status))) { + scan_active = 1; + IWL_DEBUG_MAC80211(priv, "leave - scanning\n"); + } + + if (changed & (IEEE80211_CONF_CHANGE_SMPS | + IEEE80211_CONF_CHANGE_CHANNEL)) { + /* mac80211 uses static for non-HT which is what we want */ + priv->current_ht_config.smps = conf->smps_mode; + + /* + * Recalculate chain counts. + * + * If monitor mode is enabled then mac80211 will + * set up the SM PS mode to OFF if an HT channel is + * configured. + */ + if (priv->cfg->ops->hcmd->set_rxon_chain) + for_each_context(priv, ctx) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + } + + /* during scanning mac80211 will delay channel setting until + * scan finish with changed = 0 + */ + if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { + if (scan_active) + goto set_ch_out; + + ch = channel->hw_value; + ch_info = iwl_legacy_get_channel_info(priv, channel->band, ch); + if (!iwl_legacy_is_channel_valid(ch_info)) { + IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); + ret = -EINVAL; + goto set_ch_out; + } + + spin_lock_irqsave(&priv->lock, flags); + + for_each_context(priv, ctx) { + /* Configure HT40 channels */ + if (ctx->ht.enabled != conf_is_ht(conf)) { + ctx->ht.enabled = conf_is_ht(conf); + ht_changed[ctx->ctxid] = true; + } + if (ctx->ht.enabled) { + if (conf_is_ht40_minus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + } else { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ctx->ht.is_40mhz = false; + } + } else + ctx->ht.is_40mhz = false; + + /* + * Default to no protection. Protection mode will + * later be set from BSS config in iwl_ht_conf + */ + ctx->ht.protection = + IEEE80211_HT_OP_MODE_PROTECTION_NONE; + + /* if we are switching from ht to 2.4 clear flags + * from any ht related info since 2.4 does not + * support ht */ + if ((le16_to_cpu(ctx->staging.channel) != ch)) + ctx->staging.flags = 0; + + iwl_legacy_set_rxon_channel(priv, channel, ctx); + iwl_legacy_set_rxon_ht(priv, ht_conf); + + iwl_legacy_set_flags_for_band(priv, ctx, channel->band, + ctx->vif); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + if (priv->cfg->ops->legacy->update_bcast_stations) + ret = + priv->cfg->ops->legacy->update_bcast_stations(priv); + + set_ch_out: + /* The list of supported rates and rate mask can be different + * for each band; since the band may have changed, reset + * the rate mask to what mac80211 lists */ + iwl_legacy_set_rate(priv); + } + + if (changed & (IEEE80211_CONF_CHANGE_PS | + IEEE80211_CONF_CHANGE_IDLE)) { + ret = iwl_legacy_power_update_mode(priv, false); + if (ret) + IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); + } + + if (changed & IEEE80211_CONF_CHANGE_POWER) { + IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", + priv->tx_power_user_lmt, conf->power_level); + + iwl_legacy_set_tx_power(priv, conf->power_level, false); + } + + if (!iwl_legacy_is_ready(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + goto out; + } + + if (scan_active) + goto out; + + for_each_context(priv, ctx) { + if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging))) + iwl_legacy_commit_rxon(priv, ctx); + else + IWL_DEBUG_INFO(priv, + "Not re-sending same RXON configuration.\n"); + if (ht_changed[ctx->ctxid]) + iwl_legacy_update_qos(priv, ctx); + } + +out: + IWL_DEBUG_MAC80211(priv, "leave\n"); + mutex_unlock(&priv->mutex); + return ret; +} +EXPORT_SYMBOL(iwl_legacy_mac_config); + +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + /* IBSS can only be the IWL_RXON_CTX_BSS context */ + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + if (WARN_ON(!priv->cfg->ops->legacy)) + return; + + mutex_lock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "enter\n"); + + spin_lock_irqsave(&priv->lock, flags); + memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config)); + spin_unlock_irqrestore(&priv->lock, flags); + + spin_lock_irqsave(&priv->lock, flags); + + /* new association get rid of ibss beacon skb */ + if (priv->beacon_skb) + dev_kfree_skb(priv->beacon_skb); + + priv->beacon_skb = NULL; + + priv->timestamp = 0; + + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_legacy_scan_cancel_timeout(priv, 100); + if (!iwl_legacy_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); + mutex_unlock(&priv->mutex); + return; + } + + /* we are restarting association process + * clear RXON_FILTER_ASSOC_MSK bit + */ + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + iwl_legacy_commit_rxon(priv, ctx); + + iwl_legacy_set_rate(priv); + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} +EXPORT_SYMBOL(iwl_legacy_mac_reset_tsf); + +static void iwl_legacy_ht_conf(struct iwl_priv *priv, + struct ieee80211_vif *vif) +{ + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct ieee80211_sta *sta; + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + + IWL_DEBUG_ASSOC(priv, "enter:\n"); + + if (!ctx->ht.enabled) + return; + + ctx->ht.protection = + bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; + ctx->ht.non_gf_sta_present = + !!(bss_conf->ht_operation_mode & + IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); + + ht_conf->single_chain_sufficient = false; + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + rcu_read_lock(); + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (sta) { + struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; + int maxstreams; + + maxstreams = (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) + >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if ((ht_cap->mcs.rx_mask[1] == 0) && + (ht_cap->mcs.rx_mask[2] == 0)) + ht_conf->single_chain_sufficient = true; + if (maxstreams <= 1) + ht_conf->single_chain_sufficient = true; + } else { + /* + * If at all, this can only happen through a race + * when the AP disconnects us while we're still + * setting up the connection, in that case mac80211 + * will soon tell us about that. + */ + ht_conf->single_chain_sufficient = true; + } + rcu_read_unlock(); + break; + case NL80211_IFTYPE_ADHOC: + ht_conf->single_chain_sufficient = true; + break; + default: + break; + } + + IWL_DEBUG_ASSOC(priv, "leave\n"); +} + +static inline void iwl_legacy_set_no_assoc(struct iwl_priv *priv, + struct ieee80211_vif *vif) +{ + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + + /* + * inform the ucode that there is no longer an + * association and that no more packets should be + * sent + */ + ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; + ctx->staging.assoc_id = 0; + iwl_legacy_commit_rxon(priv, ctx); +} + +static void iwl_legacy_beacon_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_priv *priv = hw->priv; + unsigned long flags; + __le64 timestamp; + struct sk_buff *skb = ieee80211_beacon_get(hw, vif); + + if (!skb) + return; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_ctx) { + IWL_ERR(priv, "update beacon but no beacon context!\n"); + dev_kfree_skb(skb); + return; + } + + spin_lock_irqsave(&priv->lock, flags); + + if (priv->beacon_skb) + dev_kfree_skb(priv->beacon_skb); + + priv->beacon_skb = skb; + + timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; + priv->timestamp = le64_to_cpu(timestamp); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + spin_unlock_irqrestore(&priv->lock, flags); + + if (!iwl_legacy_is_ready_rf(priv)) { + IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); + return; + } + + priv->cfg->ops->legacy->post_associate(priv); +} + +void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_rxon_context *ctx = iwl_legacy_rxon_ctx_from_vif(vif); + int ret; + + if (WARN_ON(!priv->cfg->ops->legacy)) + return; + + IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes); + + if (!iwl_legacy_is_alive(priv)) + return; + + mutex_lock(&priv->mutex); + + if (changes & BSS_CHANGED_QOS) { + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + ctx->qos_data.qos_active = bss_conf->qos; + iwl_legacy_update_qos(priv, ctx); + spin_unlock_irqrestore(&priv->lock, flags); + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + /* + * the add_interface code must make sure we only ever + * have a single interface that could be beaconing at + * any time. + */ + if (vif->bss_conf.enable_beacon) + priv->beacon_ctx = ctx; + else + priv->beacon_ctx = NULL; + } + + if (changes & BSS_CHANGED_BSSID) { + IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid); + + /* + * If there is currently a HW scan going on in the + * background then we need to cancel it else the RXON + * below/in post_associate will fail. + */ + if (iwl_legacy_scan_cancel_timeout(priv, 100)) { + IWL_WARN(priv, + "Aborted scan still in progress after 100ms\n"); + IWL_DEBUG_MAC80211(priv, + "leaving - scan abort failed.\n"); + mutex_unlock(&priv->mutex); + return; + } + + /* mac80211 only sets assoc when in STATION mode */ + if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) { + memcpy(ctx->staging.bssid_addr, + bss_conf->bssid, ETH_ALEN); + + /* currently needed in a few places */ + memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + } else { + ctx->staging.filter_flags &= + ~RXON_FILTER_ASSOC_MSK; + } + + } + + /* + * This needs to be after setting the BSSID in case + * mac80211 decides to do both changes at once because + * it will invoke post_associate. + */ + if (vif->type == NL80211_IFTYPE_ADHOC && changes & BSS_CHANGED_BEACON) + iwl_legacy_beacon_update(hw, vif); + + if (changes & BSS_CHANGED_ERP_PREAMBLE) { + IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", + bss_conf->use_short_preamble); + if (bss_conf->use_short_preamble) + ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; + else + ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; + } + + if (changes & BSS_CHANGED_ERP_CTS_PROT) { + IWL_DEBUG_MAC80211(priv, + "ERP_CTS %d\n", bss_conf->use_cts_prot); + if (bss_conf->use_cts_prot && + (priv->band != IEEE80211_BAND_5GHZ)) + ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; + else + ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; + if (bss_conf->use_cts_prot) + ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; + else + ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; + } + + if (changes & BSS_CHANGED_BASIC_RATES) { + /* XXX use this information + * + * To do that, remove code from iwl_legacy_set_rate() and put something + * like this here: + * + if (A-band) + ctx->staging.ofdm_basic_rates = + bss_conf->basic_rates; + else + ctx->staging.ofdm_basic_rates = + bss_conf->basic_rates >> 4; + ctx->staging.cck_basic_rates = + bss_conf->basic_rates & 0xF; + */ + } + + if (changes & BSS_CHANGED_HT) { + iwl_legacy_ht_conf(priv, vif); + + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + } + + if (changes & BSS_CHANGED_ASSOC) { + IWL_DEBUG_MAC80211(priv, "ASSOC %d\n", bss_conf->assoc); + if (bss_conf->assoc) { + priv->timestamp = bss_conf->timestamp; + + if (!iwl_legacy_is_rfkill(priv)) + priv->cfg->ops->legacy->post_associate(priv); + } else + iwl_legacy_set_no_assoc(priv, vif); + } + + if (changes && iwl_legacy_is_associated_ctx(ctx) && bss_conf->aid) { + IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n", + changes); + ret = iwl_legacy_send_rxon_assoc(priv, ctx); + if (!ret) { + /* Sync active_rxon with latest change. */ + memcpy((void *)&ctx->active, + &ctx->staging, + sizeof(struct iwl_legacy_rxon_cmd)); + } + } + + if (changes & BSS_CHANGED_BEACON_ENABLED) { + if (vif->bss_conf.enable_beacon) { + memcpy(ctx->staging.bssid_addr, + bss_conf->bssid, ETH_ALEN); + memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); + priv->cfg->ops->legacy->config_ap(priv); + } else + iwl_legacy_set_no_assoc(priv, vif); + } + + if (changes & BSS_CHANGED_IBSS) { + ret = priv->cfg->ops->legacy->manage_ibss_station(priv, vif, + bss_conf->ibss_joined); + if (ret) + IWL_ERR(priv, "failed to %s IBSS station %pM\n", + bss_conf->ibss_joined ? "add" : "remove", + bss_conf->bssid); + } + + mutex_unlock(&priv->mutex); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} +EXPORT_SYMBOL(iwl_legacy_mac_bss_info_changed); + +irqreturn_t iwl_legacy_isr(int irq, void *data) +{ + struct iwl_priv *priv = data; + u32 inta, inta_mask; + u32 inta_fh; + unsigned long flags; + if (!priv) + return IRQ_NONE; + + spin_lock_irqsave(&priv->lock, flags); + + /* Disable (but don't clear!) interrupts here to avoid + * back-to-back ISRs and sporadic interrupts from our NIC. + * If we have something to service, the tasklet will re-enable ints. + * If we *don't* have something, we'll re-enable before leaving here. */ + inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ + iwl_write32(priv, CSR_INT_MASK, 0x00000000); + + /* Discover which interrupts are active/pending */ + inta = iwl_read32(priv, CSR_INT); + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + + /* Ignore interrupt if there's nothing in NIC to service. + * This may be due to IRQ shared with another device, + * or due to sporadic interrupts thrown from our NIC. */ + if (!inta && !inta_fh) { + IWL_DEBUG_ISR(priv, + "Ignore interrupt, inta == 0, inta_fh == 0\n"); + goto none; + } + + if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { + /* Hardware disappeared. It might have already raised + * an interrupt */ + IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta); + goto unplugged; + } + + IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + + inta &= ~CSR_INT_BIT_SCD; + + /* iwl_irq_tasklet() will service interrupts and re-enable them */ + if (likely(inta || inta_fh)) + tasklet_schedule(&priv->irq_tasklet); + +unplugged: + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_HANDLED; + +none: + /* re-enable interrupts here since we don't have anything to service. */ + /* only Re-enable if diabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &priv->status)) + iwl_legacy_enable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + return IRQ_NONE; +} +EXPORT_SYMBOL(iwl_legacy_isr); + +/* + * iwl_legacy_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this + * function. + */ +void iwl_legacy_tx_cmd_protection(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags) +{ + if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { + *tx_flags |= TX_CMD_FLG_RTS_MSK; + *tx_flags &= ~TX_CMD_FLG_CTS_MSK; + *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; + + if (!ieee80211_is_mgmt(fc)) + return; + + switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { + case cpu_to_le16(IEEE80211_STYPE_AUTH): + case cpu_to_le16(IEEE80211_STYPE_DEAUTH): + case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): + case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): + *tx_flags &= ~TX_CMD_FLG_RTS_MSK; + *tx_flags |= TX_CMD_FLG_CTS_MSK; + break; + } + } else if (info->control.rates[0].flags & + IEEE80211_TX_RC_USE_CTS_PROTECT) { + *tx_flags &= ~TX_CMD_FLG_RTS_MSK; + *tx_flags |= TX_CMD_FLG_CTS_MSK; + *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; + } +} +EXPORT_SYMBOL(iwl_legacy_tx_cmd_protection); diff --git a/drivers/net/wireless/iwlegacy/iwl-core.h b/drivers/net/wireless/iwlegacy/iwl-core.h new file mode 100644 index 00000000000..f03b463e437 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-core.h @@ -0,0 +1,646 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_legacy_core_h__ +#define __iwl_legacy_core_h__ + +/************************ + * forward declarations * + ************************/ +struct iwl_host_cmd; +struct iwl_cmd; + + +#define IWLWIFI_VERSION "in-tree:" +#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" +#define DRV_AUTHOR "<ilw@linux.intel.com>" + +#define IWL_PCI_DEVICE(dev, subdev, cfg) \ + .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ + .subvendor = PCI_ANY_ID, .subdevice = (subdev), \ + .driver_data = (kernel_ulong_t)&(cfg) + +#define TIME_UNIT 1024 + +#define IWL_SKU_G 0x1 +#define IWL_SKU_A 0x2 +#define IWL_SKU_N 0x8 + +#define IWL_CMD(x) case x: return #x + +struct iwl_hcmd_ops { + int (*rxon_assoc)(struct iwl_priv *priv, struct iwl_rxon_context *ctx); + int (*commit_rxon)(struct iwl_priv *priv, struct iwl_rxon_context *ctx); + void (*set_rxon_chain)(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +}; + +struct iwl_hcmd_utils_ops { + u16 (*get_hcmd_size)(u8 cmd_id, u16 len); + u16 (*build_addsta_hcmd)(const struct iwl_legacy_addsta_cmd *cmd, + u8 *data); + int (*request_scan)(struct iwl_priv *priv, struct ieee80211_vif *vif); + void (*post_scan)(struct iwl_priv *priv); +}; + +struct iwl_apm_ops { + int (*init)(struct iwl_priv *priv); + void (*config)(struct iwl_priv *priv); +}; + +struct iwl_debugfs_ops { + ssize_t (*rx_stats_read)(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + ssize_t (*tx_stats_read)(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); + ssize_t (*general_stats_read)(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos); +}; + +struct iwl_temp_ops { + void (*temperature)(struct iwl_priv *priv); +}; + +struct iwl_lib_ops { + /* set hw dependent parameters */ + int (*set_hw_params)(struct iwl_priv *priv); + /* Handling TX */ + void (*txq_update_byte_cnt_tbl)(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + u16 byte_cnt); + int (*txq_attach_buf_to_tfd)(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + dma_addr_t addr, + u16 len, u8 reset, u8 pad); + void (*txq_free_tfd)(struct iwl_priv *priv, + struct iwl_tx_queue *txq); + int (*txq_init)(struct iwl_priv *priv, + struct iwl_tx_queue *txq); + /* setup Rx handler */ + void (*rx_handler_setup)(struct iwl_priv *priv); + /* alive notification after init uCode load */ + void (*init_alive_start)(struct iwl_priv *priv); + /* check validity of rtc data address */ + int (*is_valid_rtc_data_addr)(u32 addr); + /* 1st ucode load */ + int (*load_ucode)(struct iwl_priv *priv); + int (*dump_nic_event_log)(struct iwl_priv *priv, + bool full_log, char **buf, bool display); + void (*dump_nic_error_log)(struct iwl_priv *priv); + int (*dump_fh)(struct iwl_priv *priv, char **buf, bool display); + int (*set_channel_switch)(struct iwl_priv *priv, + struct ieee80211_channel_switch *ch_switch); + /* power management */ + struct iwl_apm_ops apm_ops; + + /* power */ + int (*send_tx_power) (struct iwl_priv *priv); + void (*update_chain_flags)(struct iwl_priv *priv); + + /* eeprom operations (as defined in iwl-eeprom.h) */ + struct iwl_eeprom_ops eeprom_ops; + + /* temperature */ + struct iwl_temp_ops temp_ops; + /* check for plcp health */ + bool (*check_plcp_health)(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); + + struct iwl_debugfs_ops debugfs_ops; + +}; + +struct iwl_led_ops { + int (*cmd)(struct iwl_priv *priv, struct iwl_led_cmd *led_cmd); +}; + +struct iwl_legacy_ops { + void (*post_associate)(struct iwl_priv *priv); + void (*config_ap)(struct iwl_priv *priv); + /* station management */ + int (*update_bcast_stations)(struct iwl_priv *priv); + int (*manage_ibss_station)(struct iwl_priv *priv, + struct ieee80211_vif *vif, bool add); +}; + +struct iwl_ops { + const struct iwl_lib_ops *lib; + const struct iwl_hcmd_ops *hcmd; + const struct iwl_hcmd_utils_ops *utils; + const struct iwl_led_ops *led; + const struct iwl_nic_ops *nic; + const struct iwl_legacy_ops *legacy; + const struct ieee80211_ops *ieee80211_ops; +}; + +struct iwl_mod_params { + int sw_crypto; /* def: 0 = using hardware encryption */ + int disable_hw_scan; /* def: 0 = use h/w scan */ + int num_of_queues; /* def: HW dependent */ + int disable_11n; /* def: 0 = 11n capabilities enabled */ + int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ + int antenna; /* def: 0 = both antennas (use diversity) */ + int restart_fw; /* def: 1 = restart firmware */ +}; + +/* + * @led_compensation: compensate on the led on/off time per HW according + * to the deviation to achieve the desired led frequency. + * The detail algorithm is described in iwl-led.c + * @chain_noise_num_beacons: number of beacons used to compute chain noise + * @plcp_delta_threshold: plcp error rate threshold used to trigger + * radio tuning when there is a high receiving plcp error rate + * @wd_timeout: TX queues watchdog timeout + * @temperature_kelvin: temperature report by uCode in kelvin + * @max_event_log_size: size of event log buffer size for ucode event logging + * @ucode_tracing: support ucode continuous tracing + * @sensitivity_calib_by_driver: driver has the capability to perform + * sensitivity calibration operation + * @chain_noise_calib_by_driver: driver has the capability to perform + * chain noise calibration operation + */ +struct iwl_base_params { + int eeprom_size; + int num_of_queues; /* def: HW dependent */ + int num_of_ampdu_queues;/* def: HW dependent */ + /* for iwl_legacy_apm_init() */ + u32 pll_cfg_val; + bool set_l0s; + bool use_bsm; + + u16 led_compensation; + int chain_noise_num_beacons; + u8 plcp_delta_threshold; + unsigned int wd_timeout; + bool temperature_kelvin; + u32 max_event_log_size; + const bool ucode_tracing; + const bool sensitivity_calib_by_driver; + const bool chain_noise_calib_by_driver; +}; + +/** + * struct iwl_cfg + * @fw_name_pre: Firmware filename prefix. The api version and extension + * (.ucode) will be added to filename before loading from disk. The + * filename is constructed as fw_name_pre<api>.ucode. + * @ucode_api_max: Highest version of uCode API supported by driver. + * @ucode_api_min: Lowest version of uCode API supported by driver. + * @scan_antennas: available antenna for scan operation + * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) + * + * We enable the driver to be backward compatible wrt API version. The + * driver specifies which APIs it supports (with @ucode_api_max being the + * highest and @ucode_api_min the lowest). Firmware will only be loaded if + * it has a supported API version. The firmware's API version will be + * stored in @iwl_priv, enabling the driver to make runtime changes based + * on firmware version used. + * + * For example, + * if (IWL_UCODE_API(priv->ucode_ver) >= 2) { + * Driver interacts with Firmware API version >= 2. + * } else { + * Driver interacts with Firmware API version 1. + * } + * + * The ideal usage of this infrastructure is to treat a new ucode API + * release as a new hardware revision. That is, through utilizing the + * iwl_hcmd_utils_ops etc. we accommodate different command structures + * and flows between hardware versions as well as their API + * versions. + * + */ +struct iwl_cfg { + /* params specific to an individual device within a device family */ + const char *name; + const char *fw_name_pre; + const unsigned int ucode_api_max; + const unsigned int ucode_api_min; + u8 valid_tx_ant; + u8 valid_rx_ant; + unsigned int sku; + u16 eeprom_ver; + u16 eeprom_calib_ver; + const struct iwl_ops *ops; + /* module based parameters which can be set from modprobe cmd */ + const struct iwl_mod_params *mod_params; + /* params not likely to change within a device family */ + struct iwl_base_params *base_params; + /* params likely to change within a device family */ + u8 scan_rx_antennas[IEEE80211_NUM_BANDS]; + u8 scan_tx_antennas[IEEE80211_NUM_BANDS]; + enum iwl_led_mode led_mode; +}; + +/*************************** + * L i b * + ***************************/ + +struct ieee80211_hw *iwl_legacy_alloc_all(struct iwl_cfg *cfg); +int iwl_legacy_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, + const struct ieee80211_tx_queue_params *params); +int iwl_legacy_mac_tx_last_beacon(struct ieee80211_hw *hw); +void iwl_legacy_set_rxon_hwcrypto(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + int hw_decrypt); +int iwl_legacy_check_rxon_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_legacy_full_rxon_required(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +int iwl_legacy_set_rxon_channel(struct iwl_priv *priv, + struct ieee80211_channel *ch, + struct iwl_rxon_context *ctx); +void iwl_legacy_set_flags_for_band(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + enum ieee80211_band band, + struct ieee80211_vif *vif); +u8 iwl_legacy_get_single_channel_number(struct iwl_priv *priv, + enum ieee80211_band band); +void iwl_legacy_set_rxon_ht(struct iwl_priv *priv, + struct iwl_ht_config *ht_conf); +bool iwl_legacy_is_ht40_tx_allowed(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct ieee80211_sta_ht_cap *ht_cap); +void iwl_legacy_connection_init_rx_config(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +void iwl_legacy_set_rate(struct iwl_priv *priv); +int iwl_legacy_set_decrypted_flag(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u32 decrypt_res, + struct ieee80211_rx_status *stats); +void iwl_legacy_irq_handle_error(struct iwl_priv *priv); +int iwl_legacy_mac_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void iwl_legacy_mac_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +int iwl_legacy_mac_change_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum nl80211_iftype newtype, bool newp2p); +int iwl_legacy_alloc_txq_mem(struct iwl_priv *priv); +void iwl_legacy_txq_mem(struct iwl_priv *priv); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS +int iwl_legacy_alloc_traffic_mem(struct iwl_priv *priv); +void iwl_legacy_free_traffic_mem(struct iwl_priv *priv); +void iwl_legacy_reset_traffic_log(struct iwl_priv *priv); +void iwl_legacy_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +void iwl_legacy_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header); +const char *iwl_legacy_get_mgmt_string(int cmd); +const char *iwl_legacy_get_ctrl_string(int cmd); +void iwl_legacy_clear_traffic_stats(struct iwl_priv *priv); +void iwl_legacy_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, + u16 len); +#else +static inline int iwl_legacy_alloc_traffic_mem(struct iwl_priv *priv) +{ + return 0; +} +static inline void iwl_legacy_free_traffic_mem(struct iwl_priv *priv) +{ +} +static inline void iwl_legacy_reset_traffic_log(struct iwl_priv *priv) +{ +} +static inline void iwl_legacy_dbg_log_tx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_legacy_dbg_log_rx_data_frame(struct iwl_priv *priv, + u16 length, struct ieee80211_hdr *header) +{ +} +static inline void iwl_legacy_update_stats(struct iwl_priv *priv, bool is_tx, + __le16 fc, u16 len) +{ +} +#endif +/***************************************************** + * RX handlers. + * **************************************************/ +void iwl_legacy_rx_pm_sleep_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl_legacy_rx_pm_debug_statistics_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl_legacy_rx_reply_error(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); + +/***************************************************** +* RX +******************************************************/ +void iwl_legacy_cmd_queue_unmap(struct iwl_priv *priv); +void iwl_legacy_cmd_queue_free(struct iwl_priv *priv); +int iwl_legacy_rx_queue_alloc(struct iwl_priv *priv); +void iwl_legacy_rx_queue_update_write_ptr(struct iwl_priv *priv, + struct iwl_rx_queue *q); +int iwl_legacy_rx_queue_space(const struct iwl_rx_queue *q); +void iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +/* Handlers */ +void iwl_legacy_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); +void iwl_legacy_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt); +void iwl_legacy_chswitch_done(struct iwl_priv *priv, bool is_success); +void iwl_legacy_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); + +/* TX helpers */ + +/***************************************************** +* TX +******************************************************/ +void iwl_legacy_txq_update_write_ptr(struct iwl_priv *priv, + struct iwl_tx_queue *txq); +int iwl_legacy_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, + int slots_num, u32 txq_id); +void iwl_legacy_tx_queue_reset(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + int slots_num, u32 txq_id); +void iwl_legacy_tx_queue_unmap(struct iwl_priv *priv, int txq_id); +void iwl_legacy_tx_queue_free(struct iwl_priv *priv, int txq_id); +void iwl_legacy_setup_watchdog(struct iwl_priv *priv); +/***************************************************** + * TX power + ****************************************************/ +int iwl_legacy_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force); + +/******************************************************************************* + * Rate + ******************************************************************************/ + +u8 iwl_legacy_get_lowest_plcp(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); + +/******************************************************************************* + * Scanning + ******************************************************************************/ +void iwl_legacy_init_scan_params(struct iwl_priv *priv); +int iwl_legacy_scan_cancel(struct iwl_priv *priv); +int iwl_legacy_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms); +void iwl_legacy_force_scan_end(struct iwl_priv *priv); +int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req); +void iwl_legacy_internal_short_hw_scan(struct iwl_priv *priv); +int iwl_legacy_force_reset(struct iwl_priv *priv, int mode, bool external); +u16 iwl_legacy_fill_probe_req(struct iwl_priv *priv, + struct ieee80211_mgmt *frame, + const u8 *ta, const u8 *ie, int ie_len, int left); +void iwl_legacy_setup_rx_scan_handlers(struct iwl_priv *priv); +u16 iwl_legacy_get_active_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, + u8 n_probes); +u16 iwl_legacy_get_passive_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, + struct ieee80211_vif *vif); +void iwl_legacy_setup_scan_deferred_work(struct iwl_priv *priv); +void iwl_legacy_cancel_scan_deferred_work(struct iwl_priv *priv); + +/* For faster active scanning, scan will move to the next channel if fewer than + * PLCP_QUIET_THRESH packets are heard on this channel within + * ACTIVE_QUIET_TIME after sending probe request. This shortens the dwell + * time if it's a quiet channel (nothing responded to our probe, and there's + * no other traffic). + * Disable "quiet" feature by setting PLCP_QUIET_THRESH to 0. */ +#define IWL_ACTIVE_QUIET_TIME cpu_to_le16(10) /* msec */ +#define IWL_PLCP_QUIET_THRESH cpu_to_le16(1) /* packets */ + +#define IWL_SCAN_CHECK_WATCHDOG (HZ * 7) + +/***************************************************** + * S e n d i n g H o s t C o m m a n d s * + *****************************************************/ + +const char *iwl_legacy_get_cmd_string(u8 cmd); +int __must_check iwl_legacy_send_cmd_sync(struct iwl_priv *priv, + struct iwl_host_cmd *cmd); +int iwl_legacy_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); +int __must_check iwl_legacy_send_cmd_pdu(struct iwl_priv *priv, u8 id, + u16 len, const void *data); +int iwl_legacy_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, + const void *data, + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt)); + +int iwl_legacy_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd); + + +/***************************************************** + * PCI * + *****************************************************/ + +static inline u16 iwl_legacy_pcie_link_ctl(struct iwl_priv *priv) +{ + int pos; + u16 pci_lnk_ctl; + pos = pci_find_capability(priv->pci_dev, PCI_CAP_ID_EXP); + pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl); + return pci_lnk_ctl; +} + +void iwl_legacy_bg_watchdog(unsigned long data); +u32 iwl_legacy_usecs_to_beacons(struct iwl_priv *priv, + u32 usec, u32 beacon_interval); +__le32 iwl_legacy_add_beacon_time(struct iwl_priv *priv, u32 base, + u32 addon, u32 beacon_interval); + +#ifdef CONFIG_PM +int iwl_legacy_pci_suspend(struct device *device); +int iwl_legacy_pci_resume(struct device *device); +extern const struct dev_pm_ops iwl_legacy_pm_ops; + +#define IWL_LEGACY_PM_OPS (&iwl_legacy_pm_ops) + +#else /* !CONFIG_PM */ + +#define IWL_LEGACY_PM_OPS NULL + +#endif /* !CONFIG_PM */ + +/***************************************************** +* Error Handling Debugging +******************************************************/ +void iwl4965_dump_nic_error_log(struct iwl_priv *priv); +int iwl4965_dump_nic_event_log(struct iwl_priv *priv, + bool full_log, char **buf, bool display); +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +void iwl_legacy_print_rx_config_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +#else +static inline void iwl_legacy_print_rx_config_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ +} +#endif + +void iwl_legacy_clear_isr_stats(struct iwl_priv *priv); + +/***************************************************** +* GEOS +******************************************************/ +int iwl_legacy_init_geos(struct iwl_priv *priv); +void iwl_legacy_free_geos(struct iwl_priv *priv); + +/*************** DRIVER STATUS FUNCTIONS *****/ + +#define STATUS_HCMD_ACTIVE 0 /* host command in progress */ +/* 1 is unused (used to be STATUS_HCMD_SYNC_ACTIVE) */ +#define STATUS_INT_ENABLED 2 +#define STATUS_RF_KILL_HW 3 +#define STATUS_CT_KILL 4 +#define STATUS_INIT 5 +#define STATUS_ALIVE 6 +#define STATUS_READY 7 +#define STATUS_TEMPERATURE 8 +#define STATUS_GEO_CONFIGURED 9 +#define STATUS_EXIT_PENDING 10 +#define STATUS_STATISTICS 12 +#define STATUS_SCANNING 13 +#define STATUS_SCAN_ABORTING 14 +#define STATUS_SCAN_HW 15 +#define STATUS_POWER_PMI 16 +#define STATUS_FW_ERROR 17 + + +static inline int iwl_legacy_is_ready(struct iwl_priv *priv) +{ + /* The adapter is 'ready' if READY and GEO_CONFIGURED bits are + * set but EXIT_PENDING is not */ + return test_bit(STATUS_READY, &priv->status) && + test_bit(STATUS_GEO_CONFIGURED, &priv->status) && + !test_bit(STATUS_EXIT_PENDING, &priv->status); +} + +static inline int iwl_legacy_is_alive(struct iwl_priv *priv) +{ + return test_bit(STATUS_ALIVE, &priv->status); +} + +static inline int iwl_legacy_is_init(struct iwl_priv *priv) +{ + return test_bit(STATUS_INIT, &priv->status); +} + +static inline int iwl_legacy_is_rfkill_hw(struct iwl_priv *priv) +{ + return test_bit(STATUS_RF_KILL_HW, &priv->status); +} + +static inline int iwl_legacy_is_rfkill(struct iwl_priv *priv) +{ + return iwl_legacy_is_rfkill_hw(priv); +} + +static inline int iwl_legacy_is_ctkill(struct iwl_priv *priv) +{ + return test_bit(STATUS_CT_KILL, &priv->status); +} + +static inline int iwl_legacy_is_ready_rf(struct iwl_priv *priv) +{ + + if (iwl_legacy_is_rfkill(priv)) + return 0; + + return iwl_legacy_is_ready(priv); +} + +extern void iwl_legacy_send_bt_config(struct iwl_priv *priv); +extern int iwl_legacy_send_statistics_request(struct iwl_priv *priv, + u8 flags, bool clear); +void iwl_legacy_apm_stop(struct iwl_priv *priv); +int iwl_legacy_apm_init(struct iwl_priv *priv); + +int iwl_legacy_send_rxon_timing(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +static inline int iwl_legacy_send_rxon_assoc(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + return priv->cfg->ops->hcmd->rxon_assoc(priv, ctx); +} +static inline int iwl_legacy_commit_rxon(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + return priv->cfg->ops->hcmd->commit_rxon(priv, ctx); +} +static inline const struct ieee80211_supported_band *iwl_get_hw_mode( + struct iwl_priv *priv, enum ieee80211_band band) +{ + return priv->hw->wiphy->bands[band]; +} + +/* mac80211 handlers */ +int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed); +void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw); +void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf, + u32 changes); +void iwl_legacy_tx_cmd_protection(struct iwl_priv *priv, + struct ieee80211_tx_info *info, + __le16 fc, __le32 *tx_flags); + +irqreturn_t iwl_legacy_isr(int irq, void *data); + +#endif /* __iwl_legacy_core_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-csr.h b/drivers/net/wireless/iwlegacy/iwl-csr.h new file mode 100644 index 00000000000..668a9616c26 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-csr.h @@ -0,0 +1,422 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_legacy_csr_h__ +#define __iwl_legacy_csr_h__ +/* + * CSR (control and status registers) + * + * CSR registers are mapped directly into PCI bus space, and are accessible + * whenever platform supplies power to device, even when device is in + * low power states due to driver-invoked device resets + * (e.g. CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. + * + * Use iwl_write32() and iwl_read32() family to access these registers; + * these provide simple PCI bus access, without waking up the MAC. + * Do not use iwl_legacy_write_direct32() family for these registers; + * no need to "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. + * The MAC (uCode processor, etc.) does not need to be powered up for accessing + * the CSR registers. + * + * NOTE: Device does need to be awake in order to read this memory + * via CSR_EEPROM register + */ +#define CSR_BASE (0x000) + +#define CSR_HW_IF_CONFIG_REG (CSR_BASE+0x000) /* hardware interface config */ +#define CSR_INT_COALESCING (CSR_BASE+0x004) /* accum ints, 32-usec units */ +#define CSR_INT (CSR_BASE+0x008) /* host interrupt status/ack */ +#define CSR_INT_MASK (CSR_BASE+0x00c) /* host interrupt enable */ +#define CSR_FH_INT_STATUS (CSR_BASE+0x010) /* busmaster int status/ack*/ +#define CSR_GPIO_IN (CSR_BASE+0x018) /* read external chip pins */ +#define CSR_RESET (CSR_BASE+0x020) /* busmaster enable, NMI, etc*/ +#define CSR_GP_CNTRL (CSR_BASE+0x024) + +/* 2nd byte of CSR_INT_COALESCING, not accessible via iwl_write32()! */ +#define CSR_INT_PERIODIC_REG (CSR_BASE+0x005) + +/* + * Hardware revision info + * Bit fields: + * 31-8: Reserved + * 7-4: Type of device: see CSR_HW_REV_TYPE_xxx definitions + * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D + * 1-0: "Dash" (-) value, as in A-1, etc. + * + * NOTE: Revision step affects calculation of CCK txpower for 4965. + * NOTE: See also CSR_HW_REV_WA_REG (work-around for bug in 4965). + */ +#define CSR_HW_REV (CSR_BASE+0x028) + +/* + * EEPROM memory reads + * + * NOTE: Device must be awake, initialized via apm_ops.init(), + * in order to read. + */ +#define CSR_EEPROM_REG (CSR_BASE+0x02c) +#define CSR_EEPROM_GP (CSR_BASE+0x030) + +#define CSR_GIO_REG (CSR_BASE+0x03C) +#define CSR_GP_UCODE_REG (CSR_BASE+0x048) +#define CSR_GP_DRIVER_REG (CSR_BASE+0x050) + +/* + * UCODE-DRIVER GP (general purpose) mailbox registers. + * SET/CLR registers set/clear bit(s) if "1" is written. + */ +#define CSR_UCODE_DRV_GP1 (CSR_BASE+0x054) +#define CSR_UCODE_DRV_GP1_SET (CSR_BASE+0x058) +#define CSR_UCODE_DRV_GP1_CLR (CSR_BASE+0x05c) +#define CSR_UCODE_DRV_GP2 (CSR_BASE+0x060) + +#define CSR_LED_REG (CSR_BASE+0x094) +#define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) + +/* Analog phase-lock-loop configuration */ +#define CSR_ANA_PLL_CFG (CSR_BASE+0x20c) + +/* + * CSR Hardware Revision Workaround Register. Indicates hardware rev; + * "step" determines CCK backoff for txpower calculation. Used for 4965 only. + * See also CSR_HW_REV register. + * Bit fields: + * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step + * 1-0: "Dash" (-) value, as in C-1, etc. + */ +#define CSR_HW_REV_WA_REG (CSR_BASE+0x22C) + +#define CSR_DBG_HPET_MEM_REG (CSR_BASE+0x240) +#define CSR_DBG_LINK_PWR_MGMT_REG (CSR_BASE+0x250) + +/* Bits for CSR_HW_IF_CONFIG_REG */ +#define CSR49_HW_IF_CONFIG_REG_BIT_4965_R (0x00000010) +#define CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x00000C00) +#define CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) +#define CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) + +#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MB (0x00000100) +#define CSR39_HW_IF_CONFIG_REG_BIT_3945_MM (0x00000200) +#define CSR39_HW_IF_CONFIG_REG_BIT_SKU_MRC (0x00000400) +#define CSR39_HW_IF_CONFIG_REG_BIT_BOARD_TYPE (0x00000800) +#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_A (0x00000000) +#define CSR39_HW_IF_CONFIG_REG_BITS_SILICON_TYPE_B (0x00001000) + +#define CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) +#define CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ +#define CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ +#define CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ + +#define CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ +#define CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ + +/* interrupt flags in INTA, set by uCode or hardware (e.g. dma), + * acknowledged (reset) by host writing "1" to flagged bits. */ +#define CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ +#define CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ +#define CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ +#define CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ +#define CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ +#define CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ +#define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ +#define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ +#define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses, 3945 */ +#define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ +#define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ + +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_ALIVE) + +/* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ +#define CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ +#define CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ +#define CSR39_FH_INT_BIT_RX_CHNL2 (1 << 18) /* Rx channel 2 (3945 only) */ +#define CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ +#define CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ +#define CSR39_FH_INT_BIT_TX_CHNL6 (1 << 6) /* Tx channel 6 (3945 only) */ +#define CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ +#define CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ + +#define CSR39_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR39_FH_INT_BIT_RX_CHNL2 | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + + +#define CSR39_FH_INT_TX_MASK (CSR39_FH_INT_BIT_TX_CHNL6 | \ + CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0) + +#define CSR49_FH_INT_RX_MASK (CSR_FH_INT_BIT_HI_PRIOR | \ + CSR_FH_INT_BIT_RX_CHNL1 | \ + CSR_FH_INT_BIT_RX_CHNL0) + +#define CSR49_FH_INT_TX_MASK (CSR_FH_INT_BIT_TX_CHNL1 | \ + CSR_FH_INT_BIT_TX_CHNL0) + +/* GPIO */ +#define CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) +#define CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) +#define CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) + +/* RESET */ +#define CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) +#define CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) +#define CSR_RESET_REG_FLAG_SW_RESET (0x00000080) +#define CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) +#define CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) +#define CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) + +/* + * GP (general purpose) CONTROL REGISTER + * Bit fields: + * 27: HW_RF_KILL_SW + * Indicates state of (platform's) hardware RF-Kill switch + * 26-24: POWER_SAVE_TYPE + * Indicates current power-saving mode: + * 000 -- No power saving + * 001 -- MAC power-down + * 010 -- PHY (radio) power-down + * 011 -- Error + * 9-6: SYS_CONFIG + * Indicates current system configuration, reflecting pins on chip + * as forced high/low by device circuit board. + * 4: GOING_TO_SLEEP + * Indicates MAC is entering a power-saving sleep power-down. + * Not a good time to access device-internal resources. + * 3: MAC_ACCESS_REQ + * Host sets this to request and maintain MAC wakeup, to allow host + * access to device-internal resources. Host must wait for + * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR + * device registers. + * 2: INIT_DONE + * Host sets this to put device into fully operational D0 power mode. + * Host resets this after SW_RESET to put device into low power mode. + * 0: MAC_CLOCK_READY + * Indicates MAC (ucode processor, etc.) is powered up and can run. + * Internal resources are accessible. + * NOTE: This does not indicate that the processor is actually running. + * NOTE: This does not indicate that 4965 or 3945 has completed + * init or post-power-down restore of internal SRAM memory. + * Use CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that + * SRAM is restored and uCode is in normal operation mode. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + * NOTE: After device reset, this bit remains "0" until host sets + * INIT_DONE + */ +#define CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) +#define CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) +#define CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) +#define CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) + +#define CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) + +#define CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) +#define CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) +#define CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) + + +/* EEPROM REG */ +#define CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) +#define CSR_EEPROM_REG_BIT_CMD (0x00000002) +#define CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) +#define CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) + +/* EEPROM GP */ +#define CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ +#define CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) +#define CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) + +/* GP REG */ +#define CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ +#define CSR_GP_REG_NO_POWER_SAVE (0x00000000) +#define CSR_GP_REG_MAC_POWER_SAVE (0x01000000) +#define CSR_GP_REG_PHY_POWER_SAVE (0x02000000) +#define CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) + + +/* CSR GIO */ +#define CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) + +/* + * UCODE-DRIVER GP (general purpose) mailbox register 1 + * Host driver and uCode write and/or read this register to communicate with + * each other. + * Bit fields: + * 4: UCODE_DISABLE + * Host sets this to request permanent halt of uCode, same as + * sending CARD_STATE command with "halt" bit set. + * 3: CT_KILL_EXIT + * Host sets this to request exit from CT_KILL state, i.e. host thinks + * device temperature is low enough to continue normal operation. + * 2: CMD_BLOCKED + * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) + * to release uCode to clear all Tx and command queues, enter + * unassociated mode, and power down. + * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. + * 1: SW_BIT_RFKILL + * Host sets this when issuing CARD_STATE command to request + * device sleep. + * 0: MAC_SLEEP + * uCode sets this when preparing a power-saving power-down. + * uCode resets this when power-up is complete and SRAM is sane. + * NOTE: 3945/4965 saves internal SRAM data to host when powering down, + * and must restore this data after powering back up. + * MAC_SLEEP is the best indication that restore is complete. + * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and + * do not need to save/restore it. + */ +#define CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) +#define CSR_UCODE_SW_BIT_RFKILL (0x00000002) +#define CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) +#define CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) + +/* GIO Chicken Bits (PCI Express bus link power management) */ +#define CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) +#define CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) + +/* LED */ +#define CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) +#define CSR_LED_REG_TRUN_ON (0x78) +#define CSR_LED_REG_TRUN_OFF (0x38) + +/* ANA_PLL */ +#define CSR39_ANA_PLL_CFG_VAL (0x01000000) + +/* HPET MEM debug */ +#define CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) + +/* DRAM INT TABLE */ +#define CSR_DRAM_INT_TBL_ENABLE (1 << 31) +#define CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) + +/* + * HBUS (Host-side Bus) + * + * HBUS registers are mapped directly into PCI bus space, but are used + * to indirectly access device's internal memory or registers that + * may be powered-down. + * + * Use iwl_legacy_write_direct32()/iwl_legacy_read_direct32() family + * for these registers; + * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ + * to make sure the MAC (uCode processor, etc.) is powered up for accessing + * internal resources. + * + * Do not use iwl_write32()/iwl_read32() family to access these registers; + * these provide only simple PCI bus access, without waking up the MAC. + */ +#define HBUS_BASE (0x400) + +/* + * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM + * structures, error log, event log, verifying uCode load). + * First write to address register, then read from or write to data register + * to complete the job. Once the address register is set up, accesses to + * data registers auto-increment the address by one dword. + * Bit usage for address registers (read or write): + * 0-31: memory address within device + */ +#define HBUS_TARG_MEM_RADDR (HBUS_BASE+0x00c) +#define HBUS_TARG_MEM_WADDR (HBUS_BASE+0x010) +#define HBUS_TARG_MEM_WDAT (HBUS_BASE+0x018) +#define HBUS_TARG_MEM_RDAT (HBUS_BASE+0x01c) + +/* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ +#define HBUS_TARG_MBX_C (HBUS_BASE+0x030) +#define HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) + +/* + * Registers for accessing device's internal peripheral registers + * (e.g. SCD, BSM, etc.). First write to address register, + * then read from or write to data register to complete the job. + * Bit usage for address registers (read or write): + * 0-15: register address (offset) within device + * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) + */ +#define HBUS_TARG_PRPH_WADDR (HBUS_BASE+0x044) +#define HBUS_TARG_PRPH_RADDR (HBUS_BASE+0x048) +#define HBUS_TARG_PRPH_WDAT (HBUS_BASE+0x04c) +#define HBUS_TARG_PRPH_RDAT (HBUS_BASE+0x050) + +/* + * Per-Tx-queue write pointer (index, really!) + * Indicates index to next TFD that driver will fill (1 past latest filled). + * Bit usage: + * 0-7: queue write index + * 11-8: queue selector + */ +#define HBUS_TARG_WRPTR (HBUS_BASE+0x060) + +#endif /* !__iwl_legacy_csr_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-debug.h b/drivers/net/wireless/iwlegacy/iwl-debug.h new file mode 100644 index 00000000000..ae13112701b --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-debug.h @@ -0,0 +1,198 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_debug_h__ +#define __iwl_legacy_debug_h__ + +struct iwl_priv; +extern u32 iwlegacy_debug_level; + +#define IWL_ERR(p, f, a...) dev_err(&((p)->pci_dev->dev), f, ## a) +#define IWL_WARN(p, f, a...) dev_warn(&((p)->pci_dev->dev), f, ## a) +#define IWL_INFO(p, f, a...) dev_info(&((p)->pci_dev->dev), f, ## a) +#define IWL_CRIT(p, f, a...) dev_crit(&((p)->pci_dev->dev), f, ## a) + +#define iwl_print_hex_error(priv, p, len) \ +do { \ + print_hex_dump(KERN_ERR, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +#define IWL_DEBUG(__priv, level, fmt, args...) \ +do { \ + if (iwl_legacy_get_debug_level(__priv) & (level)) \ + dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ + "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ + __func__ , ## args); \ +} while (0) + +#define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) \ +do { \ + if ((iwl_legacy_get_debug_level(__priv) & (level)) && net_ratelimit()) \ + dev_printk(KERN_ERR, &(__priv->hw->wiphy->dev), \ + "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ + __func__ , ## args); \ +} while (0) + +#define iwl_print_hex_dump(priv, level, p, len) \ +do { \ + if (iwl_legacy_get_debug_level(priv) & level) \ + print_hex_dump(KERN_DEBUG, "iwl data: ", \ + DUMP_PREFIX_OFFSET, 16, 1, p, len, 1); \ +} while (0) + +#else +#define IWL_DEBUG(__priv, level, fmt, args...) +#define IWL_DEBUG_LIMIT(__priv, level, fmt, args...) +static inline void iwl_print_hex_dump(struct iwl_priv *priv, int level, + const void *p, u32 len) +{} +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUG */ + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS +int iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name); +void iwl_legacy_dbgfs_unregister(struct iwl_priv *priv); +#else +static inline int +iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name) +{ + return 0; +} +static inline void iwl_legacy_dbgfs_unregister(struct iwl_priv *priv) +{ +} +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUGFS */ + +/* + * To use the debug system: + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of + * + * #define IWL_DL_xxxx VALUE + * + * where xxxx should be the name of the classification (for example, WEP). + * + * You then need to either add a IWL_xxxx_DEBUG() macro definition for your + * classification, or use IWL_DEBUG(IWL_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * The active debug levels can be accessed via files + * + * /sys/module/iwl4965/parameters/debug{50} + * /sys/module/iwl3945/parameters/debug + * /sys/class/net/wlan0/device/debug_level + * + * when CONFIG_IWLWIFI_LEGACY_DEBUG=y. + */ + +/* 0x0000000F - 0x00000001 */ +#define IWL_DL_INFO (1 << 0) +#define IWL_DL_MAC80211 (1 << 1) +#define IWL_DL_HCMD (1 << 2) +#define IWL_DL_STATE (1 << 3) +/* 0x000000F0 - 0x00000010 */ +#define IWL_DL_MACDUMP (1 << 4) +#define IWL_DL_HCMD_DUMP (1 << 5) +#define IWL_DL_EEPROM (1 << 6) +#define IWL_DL_RADIO (1 << 7) +/* 0x00000F00 - 0x00000100 */ +#define IWL_DL_POWER (1 << 8) +#define IWL_DL_TEMP (1 << 9) +#define IWL_DL_NOTIF (1 << 10) +#define IWL_DL_SCAN (1 << 11) +/* 0x0000F000 - 0x00001000 */ +#define IWL_DL_ASSOC (1 << 12) +#define IWL_DL_DROP (1 << 13) +#define IWL_DL_TXPOWER (1 << 14) +#define IWL_DL_AP (1 << 15) +/* 0x000F0000 - 0x00010000 */ +#define IWL_DL_FW (1 << 16) +#define IWL_DL_RF_KILL (1 << 17) +#define IWL_DL_FW_ERRORS (1 << 18) +#define IWL_DL_LED (1 << 19) +/* 0x00F00000 - 0x00100000 */ +#define IWL_DL_RATE (1 << 20) +#define IWL_DL_CALIB (1 << 21) +#define IWL_DL_WEP (1 << 22) +#define IWL_DL_TX (1 << 23) +/* 0x0F000000 - 0x01000000 */ +#define IWL_DL_RX (1 << 24) +#define IWL_DL_ISR (1 << 25) +#define IWL_DL_HT (1 << 26) +#define IWL_DL_IO (1 << 27) +/* 0xF0000000 - 0x10000000 */ +#define IWL_DL_11H (1 << 28) +#define IWL_DL_STATS (1 << 29) +#define IWL_DL_TX_REPLY (1 << 30) +#define IWL_DL_QOS (1 << 31) + +#define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) +#define IWL_DEBUG_MACDUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_MACDUMP, f, ## a) +#define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) +#define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) +#define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) +#define IWL_DEBUG_TX(p, f, a...) IWL_DEBUG(p, IWL_DL_TX, f, ## a) +#define IWL_DEBUG_ISR(p, f, a...) IWL_DEBUG(p, IWL_DL_ISR, f, ## a) +#define IWL_DEBUG_LED(p, f, a...) IWL_DEBUG(p, IWL_DL_LED, f, ## a) +#define IWL_DEBUG_WEP(p, f, a...) IWL_DEBUG(p, IWL_DL_WEP, f, ## a) +#define IWL_DEBUG_HC(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD, f, ## a) +#define IWL_DEBUG_HC_DUMP(p, f, a...) IWL_DEBUG(p, IWL_DL_HCMD_DUMP, f, ## a) +#define IWL_DEBUG_EEPROM(p, f, a...) IWL_DEBUG(p, IWL_DL_EEPROM, f, ## a) +#define IWL_DEBUG_CALIB(p, f, a...) IWL_DEBUG(p, IWL_DL_CALIB, f, ## a) +#define IWL_DEBUG_FW(p, f, a...) IWL_DEBUG(p, IWL_DL_FW, f, ## a) +#define IWL_DEBUG_RF_KILL(p, f, a...) IWL_DEBUG(p, IWL_DL_RF_KILL, f, ## a) +#define IWL_DEBUG_DROP(p, f, a...) IWL_DEBUG(p, IWL_DL_DROP, f, ## a) +#define IWL_DEBUG_DROP_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_DROP, f, ## a) +#define IWL_DEBUG_AP(p, f, a...) IWL_DEBUG(p, IWL_DL_AP, f, ## a) +#define IWL_DEBUG_TXPOWER(p, f, a...) IWL_DEBUG(p, IWL_DL_TXPOWER, f, ## a) +#define IWL_DEBUG_IO(p, f, a...) IWL_DEBUG(p, IWL_DL_IO, f, ## a) +#define IWL_DEBUG_RATE(p, f, a...) IWL_DEBUG(p, IWL_DL_RATE, f, ## a) +#define IWL_DEBUG_RATE_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_RATE, f, ## a) +#define IWL_DEBUG_NOTIF(p, f, a...) IWL_DEBUG(p, IWL_DL_NOTIF, f, ## a) +#define IWL_DEBUG_ASSOC(p, f, a...) \ + IWL_DEBUG(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_ASSOC_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_ASSOC | IWL_DL_INFO, f, ## a) +#define IWL_DEBUG_HT(p, f, a...) IWL_DEBUG(p, IWL_DL_HT, f, ## a) +#define IWL_DEBUG_STATS(p, f, a...) IWL_DEBUG(p, IWL_DL_STATS, f, ## a) +#define IWL_DEBUG_STATS_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_STATS, f, ## a) +#define IWL_DEBUG_TX_REPLY(p, f, a...) IWL_DEBUG(p, IWL_DL_TX_REPLY, f, ## a) +#define IWL_DEBUG_TX_REPLY_LIMIT(p, f, a...) \ + IWL_DEBUG_LIMIT(p, IWL_DL_TX_REPLY, f, ## a) +#define IWL_DEBUG_QOS(p, f, a...) IWL_DEBUG(p, IWL_DL_QOS, f, ## a) +#define IWL_DEBUG_RADIO(p, f, a...) IWL_DEBUG(p, IWL_DL_RADIO, f, ## a) +#define IWL_DEBUG_POWER(p, f, a...) IWL_DEBUG(p, IWL_DL_POWER, f, ## a) +#define IWL_DEBUG_11H(p, f, a...) IWL_DEBUG(p, IWL_DL_11H, f, ## a) + +#endif diff --git a/drivers/net/wireless/iwlegacy/iwl-debugfs.c b/drivers/net/wireless/iwlegacy/iwl-debugfs.c new file mode 100644 index 00000000000..2d32438b4cb --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-debugfs.c @@ -0,0 +1,1467 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#include <linux/ieee80211.h> +#include <net/mac80211.h> + + +#include "iwl-dev.h" +#include "iwl-debug.h" +#include "iwl-core.h" +#include "iwl-io.h" + +/* create and remove of files */ +#define DEBUGFS_ADD_FILE(name, parent, mode) do { \ + if (!debugfs_create_file(#name, mode, parent, priv, \ + &iwl_legacy_dbgfs_##name##_ops)) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_BOOL(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_bool(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +#define DEBUGFS_ADD_X32(name, parent, ptr) do { \ + struct dentry *__tmp; \ + __tmp = debugfs_create_x32(#name, S_IWUSR | S_IRUSR, \ + parent, ptr); \ + if (IS_ERR(__tmp) || !__tmp) \ + goto err; \ +} while (0) + +/* file operation */ +#define DEBUGFS_READ_FUNC(name) \ +static ssize_t iwl_legacy_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos); + +#define DEBUGFS_WRITE_FUNC(name) \ +static ssize_t iwl_legacy_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos); + + +static int +iwl_legacy_dbgfs_open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +#define DEBUGFS_READ_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ +static const struct file_operations iwl_legacy_dbgfs_##name##_ops = { \ + .read = iwl_legacy_dbgfs_##name##_read, \ + .open = iwl_legacy_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_WRITE_FILE_OPS(name) \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_legacy_dbgfs_##name##_ops = { \ + .write = iwl_legacy_dbgfs_##name##_write, \ + .open = iwl_legacy_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +#define DEBUGFS_READ_WRITE_FILE_OPS(name) \ + DEBUGFS_READ_FUNC(name); \ + DEBUGFS_WRITE_FUNC(name); \ +static const struct file_operations iwl_legacy_dbgfs_##name##_ops = { \ + .write = iwl_legacy_dbgfs_##name##_write, \ + .read = iwl_legacy_dbgfs_##name##_read, \ + .open = iwl_legacy_dbgfs_open_file_generic, \ + .llseek = generic_file_llseek, \ +}; + +static ssize_t iwl_legacy_dbgfs_tx_statistics_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + + int cnt; + ssize_t ret; + const size_t bufsz = 100 + + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%25s\t\t: %u\n", + iwl_legacy_get_mgmt_string(cnt), + priv->tx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%25s\t\t: %u\n", + iwl_legacy_get_ctrl_string(cnt), + priv->tx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->tx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->tx_stats.data_bytes); + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t +iwl_legacy_dbgfs_clear_traffic_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 clear_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &clear_flag) != 1) + return -EFAULT; + iwl_legacy_clear_traffic_stats(priv); + + return count; +} + +static ssize_t iwl_legacy_dbgfs_rx_statistics_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + int cnt; + ssize_t ret; + const size_t bufsz = 100 + + sizeof(char) * 50 * (MANAGEMENT_MAX + CONTROL_MAX); + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "Management:\n"); + for (cnt = 0; cnt < MANAGEMENT_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%25s\t\t: %u\n", + iwl_legacy_get_mgmt_string(cnt), + priv->rx_stats.mgmt[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Control:\n"); + for (cnt = 0; cnt < CONTROL_MAX; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\t%25s\t\t: %u\n", + iwl_legacy_get_ctrl_string(cnt), + priv->rx_stats.ctrl[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "Data:\n"); + pos += scnprintf(buf + pos, bufsz - pos, "\tcnt: %u\n", + priv->rx_stats.data_cnt); + pos += scnprintf(buf + pos, bufsz - pos, "\tbytes: %llu\n", + priv->rx_stats.data_bytes); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +#define BYTE1_MASK 0x000000ff; +#define BYTE2_MASK 0x0000ffff; +#define BYTE3_MASK 0x00ffffff; +static ssize_t iwl_legacy_dbgfs_sram_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + u32 val; + char *buf; + ssize_t ret; + int i; + int pos = 0; + struct iwl_priv *priv = file->private_data; + size_t bufsz; + + /* default is to dump the entire data segment */ + if (!priv->dbgfs_sram_offset && !priv->dbgfs_sram_len) { + priv->dbgfs_sram_offset = 0x800000; + if (priv->ucode_type == UCODE_INIT) + priv->dbgfs_sram_len = priv->ucode_init_data.len; + else + priv->dbgfs_sram_len = priv->ucode_data.len; + } + bufsz = 30 + priv->dbgfs_sram_len * sizeof(char) * 10; + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + pos += scnprintf(buf + pos, bufsz - pos, "sram_len: 0x%x\n", + priv->dbgfs_sram_len); + pos += scnprintf(buf + pos, bufsz - pos, "sram_offset: 0x%x\n", + priv->dbgfs_sram_offset); + for (i = priv->dbgfs_sram_len; i > 0; i -= 4) { + val = iwl_legacy_read_targ_mem(priv, priv->dbgfs_sram_offset + \ + priv->dbgfs_sram_len - i); + if (i < 4) { + switch (i) { + case 1: + val &= BYTE1_MASK; + break; + case 2: + val &= BYTE2_MASK; + break; + case 3: + val &= BYTE3_MASK; + break; + } + } + if (!(i % 16)) + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "0x%08x ", val); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_sram_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[64]; + int buf_size; + u32 offset, len; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + + if (sscanf(buf, "%x,%x", &offset, &len) == 2) { + priv->dbgfs_sram_offset = offset; + priv->dbgfs_sram_len = len; + } else { + priv->dbgfs_sram_offset = 0; + priv->dbgfs_sram_len = 0; + } + + return count; +} + +static ssize_t +iwl_legacy_dbgfs_stations_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_station_entry *station; + int max_sta = priv->hw_params.max_stations; + char *buf; + int i, j, pos = 0; + ssize_t ret; + /* Add 30 for initial string */ + const size_t bufsz = 30 + sizeof(char) * 500 * (priv->num_stations); + + buf = kmalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + pos += scnprintf(buf + pos, bufsz - pos, "num of stations: %d\n\n", + priv->num_stations); + + for (i = 0; i < max_sta; i++) { + station = &priv->stations[i]; + if (!station->used) + continue; + pos += scnprintf(buf + pos, bufsz - pos, + "station %d - addr: %pM, flags: %#x\n", + i, station->sta.sta.addr, + station->sta.station_flags_msk); + pos += scnprintf(buf + pos, bufsz - pos, + "TID\tseq_num\ttxq_id\tframes\ttfds\t"); + pos += scnprintf(buf + pos, bufsz - pos, + "start_idx\tbitmap\t\t\trate_n_flags\n"); + + for (j = 0; j < MAX_TID_COUNT; j++) { + pos += scnprintf(buf + pos, bufsz - pos, + "%d:\t%#x\t%#x\t%u\t%u\t%u\t\t%#.16llx\t%#x", + j, station->tid[j].seq_number, + station->tid[j].agg.txq_id, + station->tid[j].agg.frame_count, + station->tid[j].tfds_in_queue, + station->tid[j].agg.start_idx, + station->tid[j].agg.bitmap, + station->tid[j].agg.rate_n_flags); + + if (station->tid[j].agg.wait_for_ba) + pos += scnprintf(buf + pos, bufsz - pos, + " - waitforba"); + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_nvm_read(struct file *file, + char __user *user_buf, + size_t count, + loff_t *ppos) +{ + ssize_t ret; + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0, buf_size = 0; + const u8 *ptr; + char *buf; + u16 eeprom_ver; + size_t eeprom_len = priv->cfg->base_params->eeprom_size; + buf_size = 4 * eeprom_len + 256; + + if (eeprom_len % 16) { + IWL_ERR(priv, "NVM size is not multiple of 16.\n"); + return -ENODATA; + } + + ptr = priv->eeprom; + if (!ptr) { + IWL_ERR(priv, "Invalid EEPROM memory\n"); + return -ENOMEM; + } + + /* 4 characters for byte 0xYY */ + buf = kzalloc(buf_size, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + eeprom_ver = iwl_legacy_eeprom_query16(priv, EEPROM_VERSION); + pos += scnprintf(buf + pos, buf_size - pos, "EEPROM " + "version: 0x%x\n", eeprom_ver); + for (ofs = 0 ; ofs < eeprom_len ; ofs += 16) { + pos += scnprintf(buf + pos, buf_size - pos, "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16 , 16, 2, buf + pos, + buf_size - pos, 0); + pos += strlen(buf + pos); + if (buf_size - pos > 0) + buf[pos++] = '\n'; + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_log_event_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -ENOMEM; + + ret = pos = priv->cfg->ops->lib->dump_nic_event_log( + priv, true, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + } + return ret; +} + +static ssize_t iwl_legacy_dbgfs_log_event_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + u32 event_log_flag; + char buf[8]; + int buf_size; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &event_log_flag) != 1) + return -EFAULT; + if (event_log_flag == 1) + priv->cfg->ops->lib->dump_nic_event_log(priv, true, + NULL, false); + + return count; +} + + + +static ssize_t +iwl_legacy_dbgfs_channels_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct ieee80211_channel *channels = NULL; + const struct ieee80211_supported_band *supp_band = NULL; + int pos = 0, i, bufsz = PAGE_SIZE; + char *buf; + ssize_t ret; + + if (!test_bit(STATUS_GEO_CONFIGURED, &priv->status)) + return -EAGAIN; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_2GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 2.4GHz band 802.11bg):\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + } + supp_band = iwl_get_hw_mode(priv, IEEE80211_BAND_5GHZ); + if (supp_band) { + channels = supp_band->channels; + + pos += scnprintf(buf + pos, bufsz - pos, + "Displaying %d channels in 5.2GHz band (802.11a)\n", + supp_band->n_channels); + + for (i = 0; i < supp_band->n_channels; i++) + pos += scnprintf(buf + pos, bufsz - pos, + "%d: %ddBm: BSS%s%s, %s.\n", + channels[i].hw_value, + channels[i].max_power, + channels[i].flags & IEEE80211_CHAN_RADAR ? + " (IEEE 802.11h required)" : "", + ((channels[i].flags & IEEE80211_CHAN_NO_IBSS) + || (channels[i].flags & + IEEE80211_CHAN_RADAR)) ? "" : + ", IBSS", + channels[i].flags & + IEEE80211_CHAN_PASSIVE_SCAN ? + "passive only" : "active/passive"); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[512]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_HCMD_ACTIVE:\t %d\n", + test_bit(STATUS_HCMD_ACTIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INT_ENABLED:\t %d\n", + test_bit(STATUS_INT_ENABLED, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_RF_KILL_HW:\t %d\n", + test_bit(STATUS_RF_KILL_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_CT_KILL:\t\t %d\n", + test_bit(STATUS_CT_KILL, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_INIT:\t\t %d\n", + test_bit(STATUS_INIT, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_ALIVE:\t\t %d\n", + test_bit(STATUS_ALIVE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_READY:\t\t %d\n", + test_bit(STATUS_READY, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_TEMPERATURE:\t %d\n", + test_bit(STATUS_TEMPERATURE, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_GEO_CONFIGURED:\t %d\n", + test_bit(STATUS_GEO_CONFIGURED, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_EXIT_PENDING:\t %d\n", + test_bit(STATUS_EXIT_PENDING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_STATISTICS:\t %d\n", + test_bit(STATUS_STATISTICS, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCANNING:\t %d\n", + test_bit(STATUS_SCANNING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_ABORTING:\t %d\n", + test_bit(STATUS_SCAN_ABORTING, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_SCAN_HW:\t\t %d\n", + test_bit(STATUS_SCAN_HW, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_POWER_PMI:\t %d\n", + test_bit(STATUS_POWER_PMI, &priv->status)); + pos += scnprintf(buf + pos, bufsz - pos, "STATUS_FW_ERROR:\t %d\n", + test_bit(STATUS_FW_ERROR, &priv->status)); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_interrupt_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = 24 * 64; /* 24 items * 64 char per item */ + ssize_t ret; + + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, + "Interrupt Statistics Report:\n"); + + pos += scnprintf(buf + pos, bufsz - pos, "HW Error:\t\t\t %u\n", + priv->isr_stats.hw); + pos += scnprintf(buf + pos, bufsz - pos, "SW Error:\t\t\t %u\n", + priv->isr_stats.sw); + if (priv->isr_stats.sw || priv->isr_stats.hw) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tLast Restarting Code: 0x%X\n", + priv->isr_stats.err_code); + } +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + pos += scnprintf(buf + pos, bufsz - pos, "Frame transmitted:\t\t %u\n", + priv->isr_stats.sch); + pos += scnprintf(buf + pos, bufsz - pos, "Alive interrupt:\t\t %u\n", + priv->isr_stats.alive); +#endif + pos += scnprintf(buf + pos, bufsz - pos, + "HW RF KILL switch toggled:\t %u\n", + priv->isr_stats.rfkill); + + pos += scnprintf(buf + pos, bufsz - pos, "CT KILL:\t\t\t %u\n", + priv->isr_stats.ctkill); + + pos += scnprintf(buf + pos, bufsz - pos, "Wakeup Interrupt:\t\t %u\n", + priv->isr_stats.wakeup); + + pos += scnprintf(buf + pos, bufsz - pos, + "Rx command responses:\t\t %u\n", + priv->isr_stats.rx); + for (cnt = 0; cnt < REPLY_MAX; cnt++) { + if (priv->isr_stats.rx_handlers[cnt] > 0) + pos += scnprintf(buf + pos, bufsz - pos, + "\tRx handler[%36s]:\t\t %u\n", + iwl_legacy_get_cmd_string(cnt), + priv->isr_stats.rx_handlers[cnt]); + } + + pos += scnprintf(buf + pos, bufsz - pos, "Tx/FH interrupt:\t\t %u\n", + priv->isr_stats.tx); + + pos += scnprintf(buf + pos, bufsz - pos, "Unexpected INTA:\t\t %u\n", + priv->isr_stats.unhandled); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_interrupt_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + u32 reset_flag; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%x", &reset_flag) != 1) + return -EFAULT; + if (reset_flag == 0) + iwl_legacy_clear_isr_stats(priv); + + return count; +} + +static ssize_t +iwl_legacy_dbgfs_qos_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + struct iwl_rxon_context *ctx; + int pos = 0, i; + char buf[256 * NUM_IWL_RXON_CTX]; + const size_t bufsz = sizeof(buf); + + for_each_context(priv, ctx) { + pos += scnprintf(buf + pos, bufsz - pos, "context %d:\n", + ctx->ctxid); + for (i = 0; i < AC_NUM; i++) { + pos += scnprintf(buf + pos, bufsz - pos, + "\tcw_min\tcw_max\taifsn\ttxop\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "AC[%d]\t%u\t%u\t%u\t%u\n", i, + ctx->qos_data.def_qos_parm.ac[i].cw_min, + ctx->qos_data.def_qos_parm.ac[i].cw_max, + ctx->qos_data.def_qos_parm.ac[i].aifsn, + ctx->qos_data.def_qos_parm.ac[i].edca_txop); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_disable_ht40_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int ht40; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &ht40) != 1) + return -EFAULT; + if (!iwl_legacy_is_any_associated(priv)) + priv->disable_ht40 = ht40 ? true : false; + else { + IWL_ERR(priv, "Sta associated with AP - " + "Change to 40MHz channel support is not allowed\n"); + return -EINVAL; + } + + return count; +} + +static ssize_t iwl_legacy_dbgfs_disable_ht40_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[100]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, + "11n 40MHz Mode: %s\n", + priv->disable_ht40 ? "Disabled" : "Enabled"); + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +DEBUGFS_READ_WRITE_FILE_OPS(sram); +DEBUGFS_READ_WRITE_FILE_OPS(log_event); +DEBUGFS_READ_FILE_OPS(nvm); +DEBUGFS_READ_FILE_OPS(stations); +DEBUGFS_READ_FILE_OPS(channels); +DEBUGFS_READ_FILE_OPS(status); +DEBUGFS_READ_WRITE_FILE_OPS(interrupt); +DEBUGFS_READ_FILE_OPS(qos); +DEBUGFS_READ_WRITE_FILE_OPS(disable_ht40); + +static ssize_t iwl_legacy_dbgfs_traffic_log_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + int pos = 0, ofs = 0; + int cnt = 0, entry; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + struct iwl_rx_queue *rxq = &priv->rxq; + char *buf; + int bufsz = ((IWL_TRAFFIC_ENTRIES * IWL_TRAFFIC_ENTRY_SIZE * 64) * 2) + + (priv->cfg->base_params->num_of_queues * 32 * 8) + 400; + const u8 *ptr; + ssize_t ret; + + if (!priv->txq) { + IWL_ERR(priv, "txq not ready\n"); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate buffer\n"); + return -ENOMEM; + } + pos += scnprintf(buf + pos, bufsz - pos, "Tx Queue\n"); + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "q[%d]: read_ptr: %u, write_ptr: %u\n", + cnt, q->read_ptr, q->write_ptr); + } + if (priv->tx_traffic && (iwlegacy_debug_level & IWL_DL_TX)) { + ptr = priv->tx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Tx Traffic idx: %u\n", priv->tx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf + pos); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + pos += scnprintf(buf + pos, bufsz - pos, "Rx Queue\n"); + pos += scnprintf(buf + pos, bufsz - pos, + "read: %u, write: %u\n", + rxq->read, rxq->write); + + if (priv->rx_traffic && (iwlegacy_debug_level & IWL_DL_RX)) { + ptr = priv->rx_traffic; + pos += scnprintf(buf + pos, bufsz - pos, + "Rx Traffic idx: %u\n", priv->rx_traffic_idx); + for (cnt = 0, ofs = 0; cnt < IWL_TRAFFIC_ENTRIES; cnt++) { + for (entry = 0; entry < IWL_TRAFFIC_ENTRY_SIZE / 16; + entry++, ofs += 16) { + pos += scnprintf(buf + pos, bufsz - pos, + "0x%.4x ", ofs); + hex_dump_to_buffer(ptr + ofs, 16, 16, 2, + buf + pos, bufsz - pos, 0); + pos += strlen(buf + pos); + if (bufsz - pos > 0) + buf[pos++] = '\n'; + } + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_traffic_log_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int traffic_log; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &traffic_log) != 1) + return -EFAULT; + if (traffic_log == 0) + iwl_legacy_reset_traffic_log(priv); + + return count; +} + +static ssize_t iwl_legacy_dbgfs_tx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + struct iwl_tx_queue *txq; + struct iwl_queue *q; + char *buf; + int pos = 0; + int cnt; + int ret; + const size_t bufsz = sizeof(char) * 64 * + priv->cfg->base_params->num_of_queues; + + if (!priv->txq) { + IWL_ERR(priv, "txq not ready\n"); + return -EAGAIN; + } + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) { + txq = &priv->txq[cnt]; + q = &txq->q; + pos += scnprintf(buf + pos, bufsz - pos, + "hwq %.2d: read=%u write=%u stop=%d" + " swq_id=%#.2x (ac %d/hwq %d)\n", + cnt, q->read_ptr, q->write_ptr, + !!test_bit(cnt, priv->queue_stopped), + txq->swq_id, txq->swq_id & 3, + (txq->swq_id >> 2) & 0x1f); + if (cnt >= 4) + continue; + /* for the ACs, display the stop count too */ + pos += scnprintf(buf + pos, bufsz - pos, + " stop-count: %d\n", + atomic_read(&priv->queue_stop_count[cnt])); + } + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_rx_queue_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + struct iwl_rx_queue *rxq = &priv->rxq; + char buf[256]; + int pos = 0; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "read: %u\n", + rxq->read); + pos += scnprintf(buf + pos, bufsz - pos, "write: %u\n", + rxq->write); + pos += scnprintf(buf + pos, bufsz - pos, "free_count: %u\n", + rxq->free_count); + if (rxq->rb_stts) { + pos += scnprintf(buf + pos, bufsz - pos, "closed_rb_num: %u\n", + le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF); + } else { + pos += scnprintf(buf + pos, bufsz - pos, + "closed_rb_num: Not Allocated\n"); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_ucode_rx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + return priv->cfg->ops->lib->debugfs_ops.rx_stats_read(file, + user_buf, count, ppos); +} + +static ssize_t iwl_legacy_dbgfs_ucode_tx_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + return priv->cfg->ops->lib->debugfs_ops.tx_stats_read(file, + user_buf, count, ppos); +} + +static ssize_t iwl_legacy_dbgfs_ucode_general_stats_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + return priv->cfg->ops->lib->debugfs_ops.general_stats_read(file, + user_buf, count, ppos); +} + +static ssize_t iwl_legacy_dbgfs_sensitivity_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_sensitivity_data) * 4 + 100; + ssize_t ret; + struct iwl_sensitivity_data *data; + + data = &priv->sensitivity_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm:\t\t\t %u\n", + data->auto_corr_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc:\t\t %u\n", + data->auto_corr_ofdm_mrc); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_ofdm_x1:\t\t %u\n", + data->auto_corr_ofdm_x1); + pos += scnprintf(buf + pos, bufsz - pos, + "auto_corr_ofdm_mrc_x1:\t\t %u\n", + data->auto_corr_ofdm_mrc_x1); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck:\t\t\t %u\n", + data->auto_corr_cck); + pos += scnprintf(buf + pos, bufsz - pos, "auto_corr_cck_mrc:\t\t %u\n", + data->auto_corr_cck_mrc); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_ofdm:\t\t %u\n", + data->last_bad_plcp_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_ofdm:\t\t %u\n", + data->last_fa_cnt_ofdm); + pos += scnprintf(buf + pos, bufsz - pos, + "last_bad_plcp_cnt_cck:\t\t %u\n", + data->last_bad_plcp_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "last_fa_cnt_cck:\t\t %u\n", + data->last_fa_cnt_cck); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_curr_state:\t\t\t %u\n", + data->nrg_curr_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_prev_state:\t\t\t %u\n", + data->nrg_prev_state); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_value:\t\t\t"); + for (cnt = 0; cnt < 10; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_value[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_rssi:\t\t"); + for (cnt = 0; cnt < NRG_NUM_PREV_STAT_L; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->nrg_silence_rssi[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_ref:\t\t %u\n", + data->nrg_silence_ref); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_energy_idx:\t\t\t %u\n", + data->nrg_energy_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_silence_idx:\t\t %u\n", + data->nrg_silence_idx); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_cck:\t\t\t %u\n", + data->nrg_th_cck); + pos += scnprintf(buf + pos, bufsz - pos, + "nrg_auto_corr_silence_diff:\t %u\n", + data->nrg_auto_corr_silence_diff); + pos += scnprintf(buf + pos, bufsz - pos, "num_in_cck_no_fa:\t\t %u\n", + data->num_in_cck_no_fa); + pos += scnprintf(buf + pos, bufsz - pos, "nrg_th_ofdm:\t\t\t %u\n", + data->nrg_th_ofdm); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + + +static ssize_t iwl_legacy_dbgfs_chain_noise_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + int cnt = 0; + char *buf; + int bufsz = sizeof(struct iwl_chain_noise_data) * 4 + 100; + ssize_t ret; + struct iwl_chain_noise_data *data; + + data = &priv->chain_noise_data; + buf = kzalloc(bufsz, GFP_KERNEL); + if (!buf) { + IWL_ERR(priv, "Can not allocate Buffer\n"); + return -ENOMEM; + } + + pos += scnprintf(buf + pos, bufsz - pos, "active_chains:\t\t\t %u\n", + data->active_chains); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_a:\t\t\t %u\n", + data->chain_noise_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_b:\t\t\t %u\n", + data->chain_noise_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_noise_c:\t\t\t %u\n", + data->chain_noise_c); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_a:\t\t\t %u\n", + data->chain_signal_a); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_b:\t\t\t %u\n", + data->chain_signal_b); + pos += scnprintf(buf + pos, bufsz - pos, "chain_signal_c:\t\t\t %u\n", + data->chain_signal_c); + pos += scnprintf(buf + pos, bufsz - pos, "beacon_count:\t\t\t %u\n", + data->beacon_count); + + pos += scnprintf(buf + pos, bufsz - pos, "disconn_array:\t\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->disconn_array[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "delta_gain_code:\t\t"); + for (cnt = 0; cnt < NUM_RX_CHAINS; cnt++) { + pos += scnprintf(buf + pos, bufsz - pos, " %u", + data->delta_gain_code[cnt]); + } + pos += scnprintf(buf + pos, bufsz - pos, "\n"); + pos += scnprintf(buf + pos, bufsz - pos, "radio_write:\t\t\t %u\n", + data->radio_write); + pos += scnprintf(buf + pos, bufsz - pos, "state:\t\t\t\t %u\n", + data->state); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); + kfree(buf); + return ret; +} + +static ssize_t iwl_legacy_dbgfs_power_save_status_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[60]; + int pos = 0; + const size_t bufsz = sizeof(buf); + u32 pwrsave_status; + + pwrsave_status = iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_REG_POWER_SAVE_STATUS_MSK; + + pos += scnprintf(buf + pos, bufsz - pos, "Power Save Status: "); + pos += scnprintf(buf + pos, bufsz - pos, "%s\n", + (pwrsave_status == CSR_GP_REG_NO_POWER_SAVE) ? "none" : + (pwrsave_status == CSR_GP_REG_MAC_POWER_SAVE) ? "MAC" : + (pwrsave_status == CSR_GP_REG_PHY_POWER_SAVE) ? "PHY" : + "error"); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_clear_ucode_statistics_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int clear; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &clear) != 1) + return -EFAULT; + + /* make request to uCode to retrieve statistics information */ + mutex_lock(&priv->mutex); + iwl_legacy_send_statistics_request(priv, CMD_SYNC, true); + mutex_unlock(&priv->mutex); + + return count; +} + +static ssize_t iwl_legacy_dbgfs_ucode_tracing_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[128]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "ucode trace timer is %s\n", + priv->event_log.ucode_trace ? "On" : "Off"); + pos += scnprintf(buf + pos, bufsz - pos, "non_wraps_count:\t\t %u\n", + priv->event_log.non_wraps_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_once_count:\t\t %u\n", + priv->event_log.wraps_once_count); + pos += scnprintf(buf + pos, bufsz - pos, "wraps_more_count:\t\t %u\n", + priv->event_log.wraps_more_count); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_ucode_tracing_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int trace; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &trace) != 1) + return -EFAULT; + + if (trace) { + priv->event_log.ucode_trace = true; + /* schedule the ucode timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } else { + priv->event_log.ucode_trace = false; + del_timer_sync(&priv->ucode_trace); + } + + return count; +} + +static ssize_t iwl_legacy_dbgfs_rxon_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_legacy_dbgfs_rxon_filter_flags_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int len = 0; + char buf[20]; + + len = sprintf(buf, "0x%04X\n", + le32_to_cpu(priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t iwl_legacy_dbgfs_fh_reg_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char *buf; + int pos = 0; + ssize_t ret = -EFAULT; + + if (priv->cfg->ops->lib->dump_fh) { + ret = pos = priv->cfg->ops->lib->dump_fh(priv, &buf, true); + if (buf) { + ret = simple_read_from_buffer(user_buf, + count, ppos, buf, pos); + kfree(buf); + } + } + + return ret; +} + +static ssize_t iwl_legacy_dbgfs_missed_beacon_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%d\n", + priv->missed_beacon_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_missed_beacon_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int missed; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &missed) != 1) + return -EINVAL; + + if (missed < IWL_MISSED_BEACON_THRESHOLD_MIN || + missed > IWL_MISSED_BEACON_THRESHOLD_MAX) + priv->missed_beacon_threshold = + IWL_MISSED_BEACON_THRESHOLD_DEF; + else + priv->missed_beacon_threshold = missed; + + return count; +} + +static ssize_t iwl_legacy_dbgfs_plcp_delta_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int pos = 0; + char buf[12]; + const size_t bufsz = sizeof(buf); + + pos += scnprintf(buf + pos, bufsz - pos, "%u\n", + priv->cfg->base_params->plcp_delta_threshold); + + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_plcp_delta_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int plcp; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &plcp) != 1) + return -EINVAL; + if ((plcp < IWL_MAX_PLCP_ERR_THRESHOLD_MIN) || + (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX)) + priv->cfg->base_params->plcp_delta_threshold = + IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE; + else + priv->cfg->base_params->plcp_delta_threshold = plcp; + return count; +} + +static ssize_t iwl_legacy_dbgfs_force_reset_read(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + int i, pos = 0; + char buf[300]; + const size_t bufsz = sizeof(buf); + struct iwl_force_reset *force_reset; + + for (i = 0; i < IWL_MAX_FORCE_RESET; i++) { + force_reset = &priv->force_reset[i]; + pos += scnprintf(buf + pos, bufsz - pos, + "Force reset method %d\n", i); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request: %d\n", + force_reset->reset_request_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request success: %d\n", + force_reset->reset_success_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\tnumber of reset request reject: %d\n", + force_reset->reset_reject_count); + pos += scnprintf(buf + pos, bufsz - pos, + "\treset duration: %lu\n", + force_reset->reset_duration); + } + return simple_read_from_buffer(user_buf, count, ppos, buf, pos); +} + +static ssize_t iwl_legacy_dbgfs_force_reset_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int reset, ret; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &reset) != 1) + return -EINVAL; + switch (reset) { + case IWL_RF_RESET: + case IWL_FW_RESET: + ret = iwl_legacy_force_reset(priv, reset, true); + break; + default: + return -EINVAL; + } + return ret ? ret : count; +} + +static ssize_t iwl_legacy_dbgfs_wd_timeout_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) { + + struct iwl_priv *priv = file->private_data; + char buf[8]; + int buf_size; + int timeout; + + memset(buf, 0, sizeof(buf)); + buf_size = min(count, sizeof(buf) - 1); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + if (sscanf(buf, "%d", &timeout) != 1) + return -EINVAL; + if (timeout < 0 || timeout > IWL_MAX_WD_TIMEOUT) + timeout = IWL_DEF_WD_TIMEOUT; + + priv->cfg->base_params->wd_timeout = timeout; + iwl_legacy_setup_watchdog(priv); + return count; +} + +DEBUGFS_READ_FILE_OPS(rx_statistics); +DEBUGFS_READ_FILE_OPS(tx_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(traffic_log); +DEBUGFS_READ_FILE_OPS(rx_queue); +DEBUGFS_READ_FILE_OPS(tx_queue); +DEBUGFS_READ_FILE_OPS(ucode_rx_stats); +DEBUGFS_READ_FILE_OPS(ucode_tx_stats); +DEBUGFS_READ_FILE_OPS(ucode_general_stats); +DEBUGFS_READ_FILE_OPS(sensitivity); +DEBUGFS_READ_FILE_OPS(chain_noise); +DEBUGFS_READ_FILE_OPS(power_save_status); +DEBUGFS_WRITE_FILE_OPS(clear_ucode_statistics); +DEBUGFS_WRITE_FILE_OPS(clear_traffic_statistics); +DEBUGFS_READ_WRITE_FILE_OPS(ucode_tracing); +DEBUGFS_READ_FILE_OPS(fh_reg); +DEBUGFS_READ_WRITE_FILE_OPS(missed_beacon); +DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta); +DEBUGFS_READ_WRITE_FILE_OPS(force_reset); +DEBUGFS_READ_FILE_OPS(rxon_flags); +DEBUGFS_READ_FILE_OPS(rxon_filter_flags); +DEBUGFS_WRITE_FILE_OPS(wd_timeout); + +/* + * Create the debugfs files and directories + * + */ +int iwl_legacy_dbgfs_register(struct iwl_priv *priv, const char *name) +{ + struct dentry *phyd = priv->hw->wiphy->debugfsdir; + struct dentry *dir_drv, *dir_data, *dir_rf, *dir_debug; + + dir_drv = debugfs_create_dir(name, phyd); + if (!dir_drv) + return -ENOMEM; + + priv->debugfs_dir = dir_drv; + + dir_data = debugfs_create_dir("data", dir_drv); + if (!dir_data) + goto err; + dir_rf = debugfs_create_dir("rf", dir_drv); + if (!dir_rf) + goto err; + dir_debug = debugfs_create_dir("debug", dir_drv); + if (!dir_debug) + goto err; + + DEBUGFS_ADD_FILE(nvm, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(sram, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(log_event, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(stations, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(channels, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(status, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(interrupt, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(qos, dir_data, S_IRUSR); + DEBUGFS_ADD_FILE(disable_ht40, dir_data, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_statistics, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_statistics, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(traffic_log, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(tx_queue, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(power_save_status, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(clear_ucode_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(clear_traffic_statistics, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(fh_reg, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(missed_beacon, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(plcp_delta, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(force_reset, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(ucode_rx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_tx_stats, dir_debug, S_IRUSR); + DEBUGFS_ADD_FILE(ucode_general_stats, dir_debug, S_IRUSR); + + if (priv->cfg->base_params->sensitivity_calib_by_driver) + DEBUGFS_ADD_FILE(sensitivity, dir_debug, S_IRUSR); + if (priv->cfg->base_params->chain_noise_calib_by_driver) + DEBUGFS_ADD_FILE(chain_noise, dir_debug, S_IRUSR); + if (priv->cfg->base_params->ucode_tracing) + DEBUGFS_ADD_FILE(ucode_tracing, dir_debug, S_IWUSR | S_IRUSR); + DEBUGFS_ADD_FILE(rxon_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(rxon_filter_flags, dir_debug, S_IWUSR); + DEBUGFS_ADD_FILE(wd_timeout, dir_debug, S_IWUSR); + if (priv->cfg->base_params->sensitivity_calib_by_driver) + DEBUGFS_ADD_BOOL(disable_sensitivity, dir_rf, + &priv->disable_sens_cal); + if (priv->cfg->base_params->chain_noise_calib_by_driver) + DEBUGFS_ADD_BOOL(disable_chain_noise, dir_rf, + &priv->disable_chain_noise_cal); + DEBUGFS_ADD_BOOL(disable_tx_power, dir_rf, + &priv->disable_tx_power_cal); + return 0; + +err: + IWL_ERR(priv, "Can't create the debugfs directory\n"); + iwl_legacy_dbgfs_unregister(priv); + return -ENOMEM; +} +EXPORT_SYMBOL(iwl_legacy_dbgfs_register); + +/** + * Remove the debugfs files and directories + * + */ +void iwl_legacy_dbgfs_unregister(struct iwl_priv *priv) +{ + if (!priv->debugfs_dir) + return; + + debugfs_remove_recursive(priv->debugfs_dir); + priv->debugfs_dir = NULL; +} +EXPORT_SYMBOL(iwl_legacy_dbgfs_unregister); diff --git a/drivers/net/wireless/iwlegacy/iwl-dev.h b/drivers/net/wireless/iwlegacy/iwl-dev.h new file mode 100644 index 00000000000..9ee849d669f --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-dev.h @@ -0,0 +1,1426 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +/* + * Please use this file (iwl-dev.h) for driver implementation definitions. + * Please use iwl-commands.h for uCode API definitions. + * Please use iwl-4965-hw.h for hardware-related definitions. + */ + +#ifndef __iwl_legacy_dev_h__ +#define __iwl_legacy_dev_h__ + +#include <linux/pci.h> /* for struct pci_device_id */ +#include <linux/kernel.h> +#include <linux/leds.h> +#include <linux/wait.h> +#include <net/ieee80211_radiotap.h> + +#include "iwl-eeprom.h" +#include "iwl-csr.h" +#include "iwl-prph.h" +#include "iwl-fh.h" +#include "iwl-debug.h" +#include "iwl-4965-hw.h" +#include "iwl-3945-hw.h" +#include "iwl-led.h" +#include "iwl-power.h" +#include "iwl-legacy-rs.h" + +struct iwl_tx_queue; + +/* CT-KILL constants */ +#define CT_KILL_THRESHOLD_LEGACY 110 /* in Celsius */ + +/* Default noise level to report when noise measurement is not available. + * This may be because we're: + * 1) Not associated (4965, no beacon statistics being sent to driver) + * 2) Scanning (noise measurement does not apply to associated channel) + * 3) Receiving CCK (3945 delivers noise info only for OFDM frames) + * Use default noise value of -127 ... this is below the range of measurable + * Rx dBm for either 3945 or 4965, so it can indicate "unmeasurable" to user. + * Also, -127 works better than 0 when averaging frames with/without + * noise info (e.g. averaging might be done in app); measured dBm values are + * always negative ... using a negative value as the default keeps all + * averages within an s8's (used in some apps) range of negative values. */ +#define IWL_NOISE_MEAS_NOT_AVAILABLE (-127) + +/* + * RTS threshold here is total size [2347] minus 4 FCS bytes + * Per spec: + * a value of 0 means RTS on all data/management packets + * a value > max MSDU size means no RTS + * else RTS for data/management frames where MPDU is larger + * than RTS value. + */ +#define DEFAULT_RTS_THRESHOLD 2347U +#define MIN_RTS_THRESHOLD 0U +#define MAX_RTS_THRESHOLD 2347U +#define MAX_MSDU_SIZE 2304U +#define MAX_MPDU_SIZE 2346U +#define DEFAULT_BEACON_INTERVAL 100U +#define DEFAULT_SHORT_RETRY_LIMIT 7U +#define DEFAULT_LONG_RETRY_LIMIT 4U + +struct iwl_rx_mem_buffer { + dma_addr_t page_dma; + struct page *page; + struct list_head list; +}; + +#define rxb_addr(r) page_address(r->page) + +/* defined below */ +struct iwl_device_cmd; + +struct iwl_cmd_meta { + /* only for SYNC commands, iff the reply skb is wanted */ + struct iwl_host_cmd *source; + /* + * only for ASYNC commands + * (which is somewhat stupid -- look at iwl-sta.c for instance + * which duplicates a bunch of code because the callback isn't + * invoked for SYNC commands, if it were and its result passed + * through it would be simpler...) + */ + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt); + + /* The CMD_SIZE_HUGE flag bit indicates that the command + * structure is stored at the end of the shared queue memory. */ + u32 flags; + + DEFINE_DMA_UNMAP_ADDR(mapping); + DEFINE_DMA_UNMAP_LEN(len); +}; + +/* + * Generic queue structure + * + * Contains common data for Rx and Tx queues + */ +struct iwl_queue { + int n_bd; /* number of BDs in this queue */ + int write_ptr; /* 1-st empty entry (index) host_w*/ + int read_ptr; /* last used entry (index) host_r*/ + /* use for monitoring and recovering the stuck queue */ + dma_addr_t dma_addr; /* physical addr for BD's */ + int n_window; /* safe queue window */ + u32 id; + int low_mark; /* low watermark, resume queue if free + * space more than this */ + int high_mark; /* high watermark, stop queue if free + * space less than this */ +} __packed; + +/* One for each TFD */ +struct iwl_tx_info { + struct sk_buff *skb; + struct iwl_rxon_context *ctx; +}; + +/** + * struct iwl_tx_queue - Tx Queue for DMA + * @q: generic Rx/Tx queue descriptor + * @bd: base of circular buffer of TFDs + * @cmd: array of command/TX buffer pointers + * @meta: array of meta data for each command/tx buffer + * @dma_addr_cmd: physical address of cmd/tx buffer array + * @txb: array of per-TFD driver data + * @time_stamp: time (in jiffies) of last read_ptr change + * @need_update: indicates need to update read/write index + * @sched_retry: indicates queue is high-throughput aggregation (HT AGG) enabled + * + * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame + * descriptors) and required locking structures. + */ +#define TFD_TX_CMD_SLOTS 256 +#define TFD_CMD_SLOTS 32 + +struct iwl_tx_queue { + struct iwl_queue q; + void *tfds; + struct iwl_device_cmd **cmd; + struct iwl_cmd_meta *meta; + struct iwl_tx_info *txb; + unsigned long time_stamp; + u8 need_update; + u8 sched_retry; + u8 active; + u8 swq_id; +}; + +#define IWL_NUM_SCAN_RATES (2) + +struct iwl4965_channel_tgd_info { + u8 type; + s8 max_power; +}; + +struct iwl4965_channel_tgh_info { + s64 last_radar_time; +}; + +#define IWL4965_MAX_RATE (33) + +struct iwl3945_clip_group { + /* maximum power level to prevent clipping for each rate, derived by + * us from this band's saturation power in EEPROM */ + const s8 clip_powers[IWL_MAX_RATES]; +}; + +/* current Tx power values to use, one for each rate for each channel. + * requested power is limited by: + * -- regulatory EEPROM limits for this channel + * -- hardware capabilities (clip-powers) + * -- spectrum management + * -- user preference (e.g. iwconfig) + * when requested power is set, base power index must also be set. */ +struct iwl3945_channel_power_info { + struct iwl3945_tx_power tpc; /* actual radio and DSP gain settings */ + s8 power_table_index; /* actual (compenst'd) index into gain table */ + s8 base_power_index; /* gain index for power at factory temp. */ + s8 requested_power; /* power (dBm) requested for this chnl/rate */ +}; + +/* current scan Tx power values to use, one for each scan rate for each + * channel. */ +struct iwl3945_scan_power_info { + struct iwl3945_tx_power tpc; /* actual radio and DSP gain settings */ + s8 power_table_index; /* actual (compenst'd) index into gain table */ + s8 requested_power; /* scan pwr (dBm) requested for chnl/rate */ +}; + +/* + * One for each channel, holds all channel setup data + * Some of the fields (e.g. eeprom and flags/max_power_avg) are redundant + * with one another! + */ +struct iwl_channel_info { + struct iwl4965_channel_tgd_info tgd; + struct iwl4965_channel_tgh_info tgh; + struct iwl_eeprom_channel eeprom; /* EEPROM regulatory limit */ + struct iwl_eeprom_channel ht40_eeprom; /* EEPROM regulatory limit for + * HT40 channel */ + + u8 channel; /* channel number */ + u8 flags; /* flags copied from EEPROM */ + s8 max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + s8 curr_txpow; /* (dBm) regulatory/spectrum/user (not h/w) limit */ + s8 min_power; /* always 0 */ + s8 scan_power; /* (dBm) regul. eeprom, direct scans, any rate */ + + u8 group_index; /* 0-4, maps channel to group1/2/3/4/5 */ + u8 band_index; /* 0-4, maps channel to band1/2/3/4/5 */ + enum ieee80211_band band; + + /* HT40 channel info */ + s8 ht40_max_power_avg; /* (dBm) regul. eeprom, normal Tx, any rate */ + u8 ht40_flags; /* flags copied from EEPROM */ + u8 ht40_extension_channel; /* HT_IE_EXT_CHANNEL_* */ + + /* Radio/DSP gain settings for each "normal" data Tx rate. + * These include, in addition to RF and DSP gain, a few fields for + * remembering/modifying gain settings (indexes). */ + struct iwl3945_channel_power_info power_info[IWL4965_MAX_RATE]; + + /* Radio/DSP gain settings for each scan rate, for directed scans. */ + struct iwl3945_scan_power_info scan_pwr_info[IWL_NUM_SCAN_RATES]; +}; + +#define IWL_TX_FIFO_BK 0 /* shared */ +#define IWL_TX_FIFO_BE 1 +#define IWL_TX_FIFO_VI 2 /* shared */ +#define IWL_TX_FIFO_VO 3 +#define IWL_TX_FIFO_UNUSED -1 + +/* Minimum number of queues. MAX_NUM is defined in hw specific files. + * Set the minimum to accommodate the 4 standard TX queues, 1 command + * queue, 2 (unused) HCCA queues, and 4 HT queues (one for each AC) */ +#define IWL_MIN_NUM_QUEUES 10 + +#define IWL_DEFAULT_CMD_QUEUE_NUM 4 + +#define IEEE80211_DATA_LEN 2304 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_HLEN (IEEE80211_4ADDR_LEN) +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +struct iwl_frame { + union { + struct ieee80211_hdr frame; + struct iwl_tx_beacon_cmd beacon; + u8 raw[IEEE80211_FRAME_LEN]; + u8 cmd[360]; + } u; + struct list_head list; +}; + +#define SEQ_TO_SN(seq) (((seq) & IEEE80211_SCTL_SEQ) >> 4) +#define SN_TO_SEQ(ssn) (((ssn) << 4) & IEEE80211_SCTL_SEQ) +#define MAX_SN ((IEEE80211_SCTL_SEQ) >> 4) + +enum { + CMD_SYNC = 0, + CMD_SIZE_NORMAL = 0, + CMD_NO_SKB = 0, + CMD_SIZE_HUGE = (1 << 0), + CMD_ASYNC = (1 << 1), + CMD_WANT_SKB = (1 << 2), +}; + +#define DEF_CMD_PAYLOAD_SIZE 320 + +/** + * struct iwl_device_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for a scan command + * (which is relatively huge; space is allocated separately). + */ +struct iwl_device_cmd { + struct iwl_cmd_header hdr; /* uCode API */ + union { + u32 flags; + u8 val8; + u16 val16; + u32 val32; + struct iwl_tx_cmd tx; + u8 payload[DEF_CMD_PAYLOAD_SIZE]; + } __packed cmd; +} __packed; + +#define TFD_MAX_PAYLOAD_SIZE (sizeof(struct iwl_device_cmd)) + + +struct iwl_host_cmd { + const void *data; + unsigned long reply_page; + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt); + u32 flags; + u16 len; + u8 id; +}; + +#define SUP_RATE_11A_MAX_NUM_CHANNELS 8 +#define SUP_RATE_11B_MAX_NUM_CHANNELS 4 +#define SUP_RATE_11G_MAX_NUM_CHANNELS 12 + +/** + * struct iwl_rx_queue - Rx queue + * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) + * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) + * @read: Shared index to newest available Rx buffer + * @write: Shared index to oldest written Rx packet + * @free_count: Number of pre-allocated buffers in rx_free + * @rx_free: list of free SKBs for use + * @rx_used: List of Rx buffers with no SKB + * @need_update: flag to indicate we need to update read/write index + * @rb_stts: driver's pointer to receive buffer status + * @rb_stts_dma: bus address of receive buffer status + * + * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers + */ +struct iwl_rx_queue { + __le32 *bd; + dma_addr_t bd_dma; + struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; + struct iwl_rx_mem_buffer *queue[RX_QUEUE_SIZE]; + u32 read; + u32 write; + u32 free_count; + u32 write_actual; + struct list_head rx_free; + struct list_head rx_used; + int need_update; + struct iwl_rb_status *rb_stts; + dma_addr_t rb_stts_dma; + spinlock_t lock; +}; + +#define IWL_SUPPORTED_RATES_IE_LEN 8 + +#define MAX_TID_COUNT 9 + +#define IWL_INVALID_RATE 0xFF +#define IWL_INVALID_VALUE -1 + +/** + * struct iwl_ht_agg -- aggregation status while waiting for block-ack + * @txq_id: Tx queue used for Tx attempt + * @frame_count: # frames attempted by Tx command + * @wait_for_ba: Expect block-ack before next Tx reply + * @start_idx: Index of 1st Transmit Frame Descriptor (TFD) in Tx window + * @bitmap0: Low order bitmap, one bit for each frame pending ACK in Tx window + * @bitmap1: High order, one bit for each frame pending ACK in Tx window + * @rate_n_flags: Rate at which Tx was attempted + * + * If REPLY_TX indicates that aggregation was attempted, driver must wait + * for block ack (REPLY_COMPRESSED_BA). This struct stores tx reply info + * until block ack arrives. + */ +struct iwl_ht_agg { + u16 txq_id; + u16 frame_count; + u16 wait_for_ba; + u16 start_idx; + u64 bitmap; + u32 rate_n_flags; +#define IWL_AGG_OFF 0 +#define IWL_AGG_ON 1 +#define IWL_EMPTYING_HW_QUEUE_ADDBA 2 +#define IWL_EMPTYING_HW_QUEUE_DELBA 3 + u8 state; +}; + + +struct iwl_tid_data { + u16 seq_number; /* 4965 only */ + u16 tfds_in_queue; + struct iwl_ht_agg agg; +}; + +struct iwl_hw_key { + u32 cipher; + int keylen; + u8 keyidx; + u8 key[32]; +}; + +union iwl_ht_rate_supp { + u16 rates; + struct { + u8 siso_rate; + u8 mimo_rate; + }; +}; + +#define CFG_HT_RX_AMPDU_FACTOR_8K (0x0) +#define CFG_HT_RX_AMPDU_FACTOR_16K (0x1) +#define CFG_HT_RX_AMPDU_FACTOR_32K (0x2) +#define CFG_HT_RX_AMPDU_FACTOR_64K (0x3) +#define CFG_HT_RX_AMPDU_FACTOR_DEF CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MAX CFG_HT_RX_AMPDU_FACTOR_64K +#define CFG_HT_RX_AMPDU_FACTOR_MIN CFG_HT_RX_AMPDU_FACTOR_8K + +/* + * Maximal MPDU density for TX aggregation + * 4 - 2us density + * 5 - 4us density + * 6 - 8us density + * 7 - 16us density + */ +#define CFG_HT_MPDU_DENSITY_2USEC (0x4) +#define CFG_HT_MPDU_DENSITY_4USEC (0x5) +#define CFG_HT_MPDU_DENSITY_8USEC (0x6) +#define CFG_HT_MPDU_DENSITY_16USEC (0x7) +#define CFG_HT_MPDU_DENSITY_DEF CFG_HT_MPDU_DENSITY_4USEC +#define CFG_HT_MPDU_DENSITY_MAX CFG_HT_MPDU_DENSITY_16USEC +#define CFG_HT_MPDU_DENSITY_MIN (0x1) + +struct iwl_ht_config { + bool single_chain_sufficient; + enum ieee80211_smps_mode smps; /* current smps mode */ +}; + +/* QoS structures */ +struct iwl_qos_info { + int qos_active; + struct iwl_qosparam_cmd def_qos_parm; +}; + +/* + * Structure should be accessed with sta_lock held. When station addition + * is in progress (IWL_STA_UCODE_INPROGRESS) it is possible to access only + * the commands (iwl_legacy_addsta_cmd and iwl_link_quality_cmd) without + * sta_lock held. + */ +struct iwl_station_entry { + struct iwl_legacy_addsta_cmd sta; + struct iwl_tid_data tid[MAX_TID_COUNT]; + u8 used, ctxid; + struct iwl_hw_key keyinfo; + struct iwl_link_quality_cmd *lq; +}; + +struct iwl_station_priv_common { + struct iwl_rxon_context *ctx; + u8 sta_id; +}; + +/* + * iwl_station_priv: Driver's private station information + * + * When mac80211 creates a station it reserves some space (hw->sta_data_size) + * in the structure for use by driver. This structure is places in that + * space. + * + * The common struct MUST be first because it is shared between + * 3945 and 4965! + */ +struct iwl_station_priv { + struct iwl_station_priv_common common; + struct iwl_lq_sta lq_sta; + atomic_t pending_frames; + bool client; + bool asleep; +}; + +/** + * struct iwl_vif_priv - driver's private per-interface information + * + * When mac80211 allocates a virtual interface, it can allocate + * space for us to put data into. + */ +struct iwl_vif_priv { + struct iwl_rxon_context *ctx; + u8 ibss_bssid_sta_id; +}; + +/* one for each uCode image (inst/data, boot/init/runtime) */ +struct fw_desc { + void *v_addr; /* access by driver */ + dma_addr_t p_addr; /* access by card's busmaster DMA */ + u32 len; /* bytes */ +}; + +/* uCode file layout */ +struct iwl_ucode_header { + __le32 ver; /* major/minor/API/serial */ + struct { + __le32 inst_size; /* bytes of runtime code */ + __le32 data_size; /* bytes of runtime data */ + __le32 init_size; /* bytes of init code */ + __le32 init_data_size; /* bytes of init data */ + __le32 boot_size; /* bytes of bootstrap code */ + u8 data[0]; /* in same order as sizes */ + } v1; +}; + +struct iwl4965_ibss_seq { + u8 mac[ETH_ALEN]; + u16 seq_num; + u16 frag_num; + unsigned long packet_time; + struct list_head list; +}; + +struct iwl_sensitivity_ranges { + u16 min_nrg_cck; + u16 max_nrg_cck; + + u16 nrg_th_cck; + u16 nrg_th_ofdm; + + u16 auto_corr_min_ofdm; + u16 auto_corr_min_ofdm_mrc; + u16 auto_corr_min_ofdm_x1; + u16 auto_corr_min_ofdm_mrc_x1; + + u16 auto_corr_max_ofdm; + u16 auto_corr_max_ofdm_mrc; + u16 auto_corr_max_ofdm_x1; + u16 auto_corr_max_ofdm_mrc_x1; + + u16 auto_corr_max_cck; + u16 auto_corr_max_cck_mrc; + u16 auto_corr_min_cck; + u16 auto_corr_min_cck_mrc; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + + +#define KELVIN_TO_CELSIUS(x) ((x)-273) +#define CELSIUS_TO_KELVIN(x) ((x)+273) + + +/** + * struct iwl_hw_params + * @max_txq_num: Max # Tx queues supported + * @dma_chnl_num: Number of Tx DMA/FIFO channels + * @scd_bc_tbls_size: size of scheduler byte count tables + * @tfd_size: TFD size + * @tx/rx_chains_num: Number of TX/RX chains + * @valid_tx/rx_ant: usable antennas + * @max_rxq_size: Max # Rx frames in Rx queue (must be power-of-2) + * @max_rxq_log: Log-base-2 of max_rxq_size + * @rx_page_order: Rx buffer page order + * @rx_wrt_ptr_reg: FH{39}_RSCSR_CHNL0_WPTR + * @max_stations: + * @ht40_channel: is 40MHz width possible in band 2.4 + * BIT(IEEE80211_BAND_5GHZ) BIT(IEEE80211_BAND_5GHZ) + * @sw_crypto: 0 for hw, 1 for sw + * @max_xxx_size: for ucode uses + * @ct_kill_threshold: temperature threshold + * @beacon_time_tsf_bits: number of valid tsf bits for beacon time + * @struct iwl_sensitivity_ranges: range of sensitivity values + */ +struct iwl_hw_params { + u8 max_txq_num; + u8 dma_chnl_num; + u16 scd_bc_tbls_size; + u32 tfd_size; + u8 tx_chains_num; + u8 rx_chains_num; + u8 valid_tx_ant; + u8 valid_rx_ant; + u16 max_rxq_size; + u16 max_rxq_log; + u32 rx_page_order; + u32 rx_wrt_ptr_reg; + u8 max_stations; + u8 ht40_channel; + u8 max_beacon_itrvl; /* in 1024 ms */ + u32 max_inst_size; + u32 max_data_size; + u32 max_bsm_size; + u32 ct_kill_threshold; /* value in hw-dependent units */ + u16 beacon_time_tsf_bits; + const struct iwl_sensitivity_ranges *sens; +}; + + +/****************************************************************************** + * + * Functions implemented in core module which are forward declared here + * for use by iwl-[4-5].c + * + * NOTE: The implementation of these functions are not hardware specific + * which is why they are in the core module files. + * + * Naming convention -- + * iwl_ <-- Is part of iwlwifi + * iwlXXXX_ <-- Hardware specific (implemented in iwl-XXXX.c for XXXX) + * iwl4965_bg_ <-- Called from work queue context + * iwl4965_mac_ <-- mac80211 callback + * + ****************************************************************************/ +extern void iwl4965_update_chain_flags(struct iwl_priv *priv); +extern const u8 iwlegacy_bcast_addr[ETH_ALEN]; +extern int iwl_legacy_queue_space(const struct iwl_queue *q); +static inline int iwl_legacy_queue_used(const struct iwl_queue *q, int i) +{ + return q->write_ptr >= q->read_ptr ? + (i >= q->read_ptr && i < q->write_ptr) : + !(i < q->read_ptr && i >= q->write_ptr); +} + + +static inline u8 iwl_legacy_get_cmd_index(struct iwl_queue *q, u32 index, + int is_huge) +{ + /* + * This is for init calibration result and scan command which + * required buffer > TFD_MAX_PAYLOAD_SIZE, + * the big buffer at end of command array + */ + if (is_huge) + return q->n_window; /* must be power of 2 */ + + /* Otherwise, use normal size buffers */ + return index & (q->n_window - 1); +} + + +struct iwl_dma_ptr { + dma_addr_t dma; + void *addr; + size_t size; +}; + +#define IWL_OPERATION_MODE_AUTO 0 +#define IWL_OPERATION_MODE_HT_ONLY 1 +#define IWL_OPERATION_MODE_MIXED 2 +#define IWL_OPERATION_MODE_20MHZ 3 + +#define IWL_TX_CRC_SIZE 4 +#define IWL_TX_DELIMITER_SIZE 4 + +#define TX_POWER_IWL_ILLEGAL_VOLTAGE -10000 + +/* Sensitivity and chain noise calibration */ +#define INITIALIZATION_VALUE 0xFFFF +#define IWL4965_CAL_NUM_BEACONS 20 +#define IWL_CAL_NUM_BEACONS 16 +#define MAXIMUM_ALLOWED_PATHLOSS 15 + +#define CHAIN_NOISE_MAX_DELTA_GAIN_CODE 3 + +#define MAX_FA_OFDM 50 +#define MIN_FA_OFDM 5 +#define MAX_FA_CCK 50 +#define MIN_FA_CCK 5 + +#define AUTO_CORR_STEP_OFDM 1 + +#define AUTO_CORR_STEP_CCK 3 +#define AUTO_CORR_MAX_TH_CCK 160 + +#define NRG_DIFF 2 +#define NRG_STEP_CCK 2 +#define NRG_MARGIN 8 +#define MAX_NUMBER_CCK_NO_FA 100 + +#define AUTO_CORR_CCK_MIN_VAL_DEF (125) + +#define CHAIN_A 0 +#define CHAIN_B 1 +#define CHAIN_C 2 +#define CHAIN_NOISE_DELTA_GAIN_INIT_VAL 4 +#define ALL_BAND_FILTER 0xFF00 +#define IN_BAND_FILTER 0xFF +#define MIN_AVERAGE_NOISE_MAX_VALUE 0xFFFFFFFF + +#define NRG_NUM_PREV_STAT_L 20 +#define NUM_RX_CHAINS 3 + +enum iwl4965_false_alarm_state { + IWL_FA_TOO_MANY = 0, + IWL_FA_TOO_FEW = 1, + IWL_FA_GOOD_RANGE = 2, +}; + +enum iwl4965_chain_noise_state { + IWL_CHAIN_NOISE_ALIVE = 0, /* must be 0 */ + IWL_CHAIN_NOISE_ACCUMULATE, + IWL_CHAIN_NOISE_CALIBRATED, + IWL_CHAIN_NOISE_DONE, +}; + +enum iwl4965_calib_enabled_state { + IWL_CALIB_DISABLED = 0, /* must be 0 */ + IWL_CALIB_ENABLED = 1, +}; + +/* + * enum iwl_calib + * defines the order in which results of initial calibrations + * should be sent to the runtime uCode + */ +enum iwl_calib { + IWL_CALIB_MAX, +}; + +/* Opaque calibration results */ +struct iwl_calib_result { + void *buf; + size_t buf_len; +}; + +enum ucode_type { + UCODE_NONE = 0, + UCODE_INIT, + UCODE_RT +}; + +/* Sensitivity calib data */ +struct iwl_sensitivity_data { + u32 auto_corr_ofdm; + u32 auto_corr_ofdm_mrc; + u32 auto_corr_ofdm_x1; + u32 auto_corr_ofdm_mrc_x1; + u32 auto_corr_cck; + u32 auto_corr_cck_mrc; + + u32 last_bad_plcp_cnt_ofdm; + u32 last_fa_cnt_ofdm; + u32 last_bad_plcp_cnt_cck; + u32 last_fa_cnt_cck; + + u32 nrg_curr_state; + u32 nrg_prev_state; + u32 nrg_value[10]; + u8 nrg_silence_rssi[NRG_NUM_PREV_STAT_L]; + u32 nrg_silence_ref; + u32 nrg_energy_idx; + u32 nrg_silence_idx; + u32 nrg_th_cck; + s32 nrg_auto_corr_silence_diff; + u32 num_in_cck_no_fa; + u32 nrg_th_ofdm; + + u16 barker_corr_th_min; + u16 barker_corr_th_min_mrc; + u16 nrg_th_cca; +}; + +/* Chain noise (differential Rx gain) calib data */ +struct iwl_chain_noise_data { + u32 active_chains; + u32 chain_noise_a; + u32 chain_noise_b; + u32 chain_noise_c; + u32 chain_signal_a; + u32 chain_signal_b; + u32 chain_signal_c; + u16 beacon_count; + u8 disconn_array[NUM_RX_CHAINS]; + u8 delta_gain_code[NUM_RX_CHAINS]; + u8 radio_write; + u8 state; +}; + +#define EEPROM_SEM_TIMEOUT 10 /* milliseconds */ +#define EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + +#define IWL_TRAFFIC_ENTRIES (256) +#define IWL_TRAFFIC_ENTRY_SIZE (64) + +enum { + MEASUREMENT_READY = (1 << 0), + MEASUREMENT_ACTIVE = (1 << 1), +}; + +/* interrupt statistics */ +struct isr_statistics { + u32 hw; + u32 sw; + u32 err_code; + u32 sch; + u32 alive; + u32 rfkill; + u32 ctkill; + u32 wakeup; + u32 rx; + u32 rx_handlers[REPLY_MAX]; + u32 tx; + u32 unhandled; +}; + +/* management statistics */ +enum iwl_mgmt_stats { + MANAGEMENT_ASSOC_REQ = 0, + MANAGEMENT_ASSOC_RESP, + MANAGEMENT_REASSOC_REQ, + MANAGEMENT_REASSOC_RESP, + MANAGEMENT_PROBE_REQ, + MANAGEMENT_PROBE_RESP, + MANAGEMENT_BEACON, + MANAGEMENT_ATIM, + MANAGEMENT_DISASSOC, + MANAGEMENT_AUTH, + MANAGEMENT_DEAUTH, + MANAGEMENT_ACTION, + MANAGEMENT_MAX, +}; +/* control statistics */ +enum iwl_ctrl_stats { + CONTROL_BACK_REQ = 0, + CONTROL_BACK, + CONTROL_PSPOLL, + CONTROL_RTS, + CONTROL_CTS, + CONTROL_ACK, + CONTROL_CFEND, + CONTROL_CFENDACK, + CONTROL_MAX, +}; + +struct traffic_stats { +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + u32 mgmt[MANAGEMENT_MAX]; + u32 ctrl[CONTROL_MAX]; + u32 data_cnt; + u64 data_bytes; +#endif +}; + +/* + * iwl_switch_rxon: "channel switch" structure + * + * @ switch_in_progress: channel switch in progress + * @ channel: new channel + */ +struct iwl_switch_rxon { + bool switch_in_progress; + __le16 channel; +}; + +/* + * schedule the timer to wake up every UCODE_TRACE_PERIOD milliseconds + * to perform continuous uCode event logging operation if enabled + */ +#define UCODE_TRACE_PERIOD (100) + +/* + * iwl_event_log: current uCode event log position + * + * @ucode_trace: enable/disable ucode continuous trace timer + * @num_wraps: how many times the event buffer wraps + * @next_entry: the entry just before the next one that uCode would fill + * @non_wraps_count: counter for no wrap detected when dump ucode events + * @wraps_once_count: counter for wrap once detected when dump ucode events + * @wraps_more_count: counter for wrap more than once detected + * when dump ucode events + */ +struct iwl_event_log { + bool ucode_trace; + u32 num_wraps; + u32 next_entry; + int non_wraps_count; + int wraps_once_count; + int wraps_more_count; +}; + +/* + * host interrupt timeout value + * used with setting interrupt coalescing timer + * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit + * + * default interrupt coalescing timer is 64 x 32 = 2048 usecs + * default interrupt coalescing calibration timer is 16 x 32 = 512 usecs + */ +#define IWL_HOST_INT_TIMEOUT_MAX (0xFF) +#define IWL_HOST_INT_TIMEOUT_DEF (0x40) +#define IWL_HOST_INT_TIMEOUT_MIN (0x0) +#define IWL_HOST_INT_CALIB_TIMEOUT_MAX (0xFF) +#define IWL_HOST_INT_CALIB_TIMEOUT_DEF (0x10) +#define IWL_HOST_INT_CALIB_TIMEOUT_MIN (0x0) + +/* + * This is the threshold value of plcp error rate per 100mSecs. It is + * used to set and check for the validity of plcp_delta. + */ +#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN (1) +#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF (50) +#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100) +#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF (200) +#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255) +#define IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE (0) + +#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3) +#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5) + +/* TX queue watchdog timeouts in mSecs */ +#define IWL_DEF_WD_TIMEOUT (2000) +#define IWL_LONG_WD_TIMEOUT (10000) +#define IWL_MAX_WD_TIMEOUT (120000) + +enum iwl_reset { + IWL_RF_RESET = 0, + IWL_FW_RESET, + IWL_MAX_FORCE_RESET, +}; + +struct iwl_force_reset { + int reset_request_count; + int reset_success_count; + int reset_reject_count; + unsigned long reset_duration; + unsigned long last_force_reset_jiffies; +}; + +/* extend beacon time format bit shifting */ +/* + * for _3945 devices + * bits 31:24 - extended + * bits 23:0 - interval + */ +#define IWL3945_EXT_BEACON_TIME_POS 24 +/* + * for _4965 devices + * bits 31:22 - extended + * bits 21:0 - interval + */ +#define IWL4965_EXT_BEACON_TIME_POS 22 + +enum iwl_rxon_context_id { + IWL_RXON_CTX_BSS, + + NUM_IWL_RXON_CTX +}; + +struct iwl_rxon_context { + struct ieee80211_vif *vif; + + const u8 *ac_to_fifo; + const u8 *ac_to_queue; + u8 mcast_queue; + + /* + * We could use the vif to indicate active, but we + * also need it to be active during disabling when + * we already removed the vif for type setting. + */ + bool always_active, is_active; + + bool ht_need_multiple_chains; + + enum iwl_rxon_context_id ctxid; + + u32 interface_modes, exclusive_interface_modes; + u8 unused_devtype, ap_devtype, ibss_devtype, station_devtype; + + /* + * We declare this const so it can only be + * changed via explicit cast within the + * routines that actually update the physical + * hardware. + */ + const struct iwl_legacy_rxon_cmd active; + struct iwl_legacy_rxon_cmd staging; + + struct iwl_rxon_time_cmd timing; + + struct iwl_qos_info qos_data; + + u8 bcast_sta_id, ap_sta_id; + + u8 rxon_cmd, rxon_assoc_cmd, rxon_timing_cmd; + u8 qos_cmd; + u8 wep_key_cmd; + + struct iwl_wep_key wep_keys[WEP_KEYS_MAX]; + u8 key_mapping_keys; + + __le32 station_flags; + + struct { + bool non_gf_sta_present; + u8 protection; + bool enabled, is_40mhz; + u8 extension_chan_offset; + } ht; +}; + +struct iwl_priv { + + /* ieee device used by generic ieee processing code */ + struct ieee80211_hw *hw; + struct ieee80211_channel *ieee_channels; + struct ieee80211_rate *ieee_rates; + struct iwl_cfg *cfg; + + /* temporary frame storage list */ + struct list_head free_frames; + int frames_count; + + enum ieee80211_band band; + int alloc_rxb_page; + + void (*rx_handlers[REPLY_MAX])(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb); + + struct ieee80211_supported_band bands[IEEE80211_NUM_BANDS]; + + /* spectrum measurement report caching */ + struct iwl_spectrum_notification measure_report; + u8 measurement_status; + + /* ucode beacon time */ + u32 ucode_beacon_time; + int missed_beacon_threshold; + + /* track IBSS manager (last beacon) status */ + u32 ibss_manager; + + /* storing the jiffies when the plcp error rate is received */ + unsigned long plcp_jiffies; + + /* force reset */ + struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET]; + + /* we allocate array of iwl_channel_info for NIC's valid channels. + * Access via channel # using indirect index array */ + struct iwl_channel_info *channel_info; /* channel info array */ + u8 channel_count; /* # of channels */ + + /* thermal calibration */ + s32 temperature; /* degrees Kelvin */ + s32 last_temperature; + + /* init calibration results */ + struct iwl_calib_result calib_results[IWL_CALIB_MAX]; + + /* Scan related variables */ + unsigned long scan_start; + unsigned long scan_start_tsf; + void *scan_cmd; + enum ieee80211_band scan_band; + struct cfg80211_scan_request *scan_request; + struct ieee80211_vif *scan_vif; + bool is_internal_short_scan; + u8 scan_tx_ant[IEEE80211_NUM_BANDS]; + u8 mgmt_tx_ant; + + /* spinlock */ + spinlock_t lock; /* protect general shared data */ + spinlock_t hcmd_lock; /* protect hcmd */ + spinlock_t reg_lock; /* protect hw register access */ + struct mutex mutex; + struct mutex sync_cmd_mutex; /* enable serialization of sync commands */ + + /* basic pci-network driver stuff */ + struct pci_dev *pci_dev; + + /* pci hardware address support */ + void __iomem *hw_base; + u32 hw_rev; + u32 hw_wa_rev; + u8 rev_id; + + /* microcode/device supports multiple contexts */ + u8 valid_contexts; + + /* command queue number */ + u8 cmd_queue; + + /* max number of station keys */ + u8 sta_key_max_num; + + /* EEPROM MAC addresses */ + struct mac_address addresses[1]; + + /* uCode images, save to reload in case of failure */ + int fw_index; /* firmware we're trying to load */ + u32 ucode_ver; /* version of ucode, copy of + iwl_ucode.ver */ + struct fw_desc ucode_code; /* runtime inst */ + struct fw_desc ucode_data; /* runtime data original */ + struct fw_desc ucode_data_backup; /* runtime data save/restore */ + struct fw_desc ucode_init; /* initialization inst */ + struct fw_desc ucode_init_data; /* initialization data */ + struct fw_desc ucode_boot; /* bootstrap inst */ + enum ucode_type ucode_type; + u8 ucode_write_complete; /* the image write is complete */ + char firmware_name[25]; + + struct iwl_rxon_context contexts[NUM_IWL_RXON_CTX]; + + struct iwl_switch_rxon switch_rxon; + + /* 1st responses from initialize and runtime uCode images. + * _4965's initialize alive response contains some calibration data. */ + struct iwl_init_alive_resp card_alive_init; + struct iwl_alive_resp card_alive; + + u16 active_rate; + + u8 start_calib; + struct iwl_sensitivity_data sensitivity_data; + struct iwl_chain_noise_data chain_noise_data; + __le16 sensitivity_tbl[HD_TABLE_SIZE]; + + struct iwl_ht_config current_ht_config; + + /* Rate scaling data */ + u8 retry_rate; + + wait_queue_head_t wait_command_queue; + + int activity_timer_active; + + /* Rx and Tx DMA processing queues */ + struct iwl_rx_queue rxq; + struct iwl_tx_queue *txq; + unsigned long txq_ctx_active_msk; + struct iwl_dma_ptr kw; /* keep warm address */ + struct iwl_dma_ptr scd_bc_tbls; + + u32 scd_base_addr; /* scheduler sram base address */ + + unsigned long status; + + /* counts mgmt, ctl, and data packets */ + struct traffic_stats tx_stats; + struct traffic_stats rx_stats; + + /* counts interrupts */ + struct isr_statistics isr_stats; + + struct iwl_power_mgr power_data; + + /* context information */ + u8 bssid[ETH_ALEN]; /* used only on 3945 but filled by core */ + + /* station table variables */ + + /* Note: if lock and sta_lock are needed, lock must be acquired first */ + spinlock_t sta_lock; + int num_stations; + struct iwl_station_entry stations[IWL_STATION_COUNT]; + unsigned long ucode_key_table; + + /* queue refcounts */ +#define IWL_MAX_HW_QUEUES 32 + unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; + /* for each AC */ + atomic_t queue_stop_count[4]; + + /* Indication if ieee80211_ops->open has been called */ + u8 is_open; + + u8 mac80211_registered; + + /* eeprom -- this is in the card's little endian byte order */ + u8 *eeprom; + struct iwl_eeprom_calib_info *calib_info; + + enum nl80211_iftype iw_mode; + + /* Last Rx'd beacon timestamp */ + u64 timestamp; + + union { +#if defined(CONFIG_IWL3945) || defined(CONFIG_IWL3945_MODULE) + struct { + void *shared_virt; + dma_addr_t shared_phys; + + struct delayed_work thermal_periodic; + struct delayed_work rfkill_poll; + + struct iwl3945_notif_statistics statistics; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + struct iwl3945_notif_statistics accum_statistics; + struct iwl3945_notif_statistics delta_statistics; + struct iwl3945_notif_statistics max_delta; +#endif + + u32 sta_supp_rates; + int last_rx_rssi; /* From Rx packet statistics */ + + /* Rx'd packet timing information */ + u32 last_beacon_time; + u64 last_tsf; + + /* + * each calibration channel group in the + * EEPROM has a derived clip setting for + * each rate. + */ + const struct iwl3945_clip_group clip_groups[5]; + + } _3945; +#endif +#if defined(CONFIG_IWL4965) || defined(CONFIG_IWL4965_MODULE) + struct { + /* + * reporting the number of tids has AGG on. 0 means + * no AGGREGATION + */ + u8 agg_tids_count; + + struct iwl_rx_phy_res last_phy_res; + bool last_phy_res_valid; + + struct completion firmware_loading_complete; + + /* + * chain noise reset and gain commands are the + * two extra calibration commands follows the standard + * phy calibration commands + */ + u8 phy_calib_chain_noise_reset_cmd; + u8 phy_calib_chain_noise_gain_cmd; + + struct iwl_notif_statistics statistics; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + struct iwl_notif_statistics accum_statistics; + struct iwl_notif_statistics delta_statistics; + struct iwl_notif_statistics max_delta; +#endif + + } _4965; +#endif + }; + + struct iwl_hw_params hw_params; + + u32 inta_mask; + + struct workqueue_struct *workqueue; + + struct work_struct restart; + struct work_struct scan_completed; + struct work_struct rx_replenish; + struct work_struct abort_scan; + + struct iwl_rxon_context *beacon_ctx; + struct sk_buff *beacon_skb; + + struct work_struct start_internal_scan; + struct work_struct tx_flush; + + struct tasklet_struct irq_tasklet; + + struct delayed_work init_alive_start; + struct delayed_work alive_start; + struct delayed_work scan_check; + + /* TX Power */ + s8 tx_power_user_lmt; + s8 tx_power_device_lmt; + s8 tx_power_next; + + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + /* debugging info */ + u32 debug_level; /* per device debugging will override global + iwlegacy_debug_level if set */ +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUG */ +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUGFS + /* debugfs */ + u16 tx_traffic_idx; + u16 rx_traffic_idx; + u8 *tx_traffic; + u8 *rx_traffic; + struct dentry *debugfs_dir; + u32 dbgfs_sram_offset, dbgfs_sram_len; + bool disable_ht40; +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUGFS */ + + struct work_struct txpower_work; + u32 disable_sens_cal; + u32 disable_chain_noise_cal; + u32 disable_tx_power_cal; + struct work_struct run_time_calib_work; + struct timer_list statistics_periodic; + struct timer_list ucode_trace; + struct timer_list watchdog; + bool hw_ready; + + struct iwl_event_log event_log; + + struct led_classdev led; + unsigned long blink_on, blink_off; + bool led_registered; +}; /*iwl_priv */ + +static inline void iwl_txq_ctx_activate(struct iwl_priv *priv, int txq_id) +{ + set_bit(txq_id, &priv->txq_ctx_active_msk); +} + +static inline void iwl_txq_ctx_deactivate(struct iwl_priv *priv, int txq_id) +{ + clear_bit(txq_id, &priv->txq_ctx_active_msk); +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +/* + * iwl_legacy_get_debug_level: Return active debug level for device + * + * Using sysfs it is possible to set per device debug level. This debug + * level will be used if set, otherwise the global debug level which can be + * set via module parameter is used. + */ +static inline u32 iwl_legacy_get_debug_level(struct iwl_priv *priv) +{ + if (priv->debug_level) + return priv->debug_level; + else + return iwlegacy_debug_level; +} +#else +static inline u32 iwl_legacy_get_debug_level(struct iwl_priv *priv) +{ + return iwlegacy_debug_level; +} +#endif + + +static inline struct ieee80211_hdr * +iwl_legacy_tx_queue_get_hdr(struct iwl_priv *priv, + int txq_id, int idx) +{ + if (priv->txq[txq_id].txb[idx].skb) + return (struct ieee80211_hdr *)priv->txq[txq_id]. + txb[idx].skb->data; + return NULL; +} + +static inline struct iwl_rxon_context * +iwl_legacy_rxon_ctx_from_vif(struct ieee80211_vif *vif) +{ + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + return vif_priv->ctx; +} + +#define for_each_context(priv, ctx) \ + for (ctx = &priv->contexts[IWL_RXON_CTX_BSS]; \ + ctx < &priv->contexts[NUM_IWL_RXON_CTX]; ctx++) \ + if (priv->valid_contexts & BIT(ctx->ctxid)) + +static inline int iwl_legacy_is_associated(struct iwl_priv *priv, + enum iwl_rxon_context_id ctxid) +{ + return (priv->contexts[ctxid].active.filter_flags & + RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int iwl_legacy_is_any_associated(struct iwl_priv *priv) +{ + return iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS); +} + +static inline int iwl_legacy_is_associated_ctx(struct iwl_rxon_context *ctx) +{ + return (ctx->active.filter_flags & RXON_FILTER_ASSOC_MSK) ? 1 : 0; +} + +static inline int iwl_legacy_is_channel_valid(const struct iwl_channel_info *ch_info) +{ + if (ch_info == NULL) + return 0; + return (ch_info->flags & EEPROM_CHANNEL_VALID) ? 1 : 0; +} + +static inline int iwl_legacy_is_channel_radar(const struct iwl_channel_info *ch_info) +{ + return (ch_info->flags & EEPROM_CHANNEL_RADAR) ? 1 : 0; +} + +static inline u8 iwl_legacy_is_channel_a_band(const struct iwl_channel_info *ch_info) +{ + return ch_info->band == IEEE80211_BAND_5GHZ; +} + +static inline int +iwl_legacy_is_channel_passive(const struct iwl_channel_info *ch) +{ + return (!(ch->flags & EEPROM_CHANNEL_ACTIVE)) ? 1 : 0; +} + +static inline void +__iwl_legacy_free_pages(struct iwl_priv *priv, struct page *page) +{ + __free_pages(page, priv->hw_params.rx_page_order); + priv->alloc_rxb_page--; +} + +static inline void iwl_legacy_free_pages(struct iwl_priv *priv, unsigned long page) +{ + free_pages(page, priv->hw_params.rx_page_order); + priv->alloc_rxb_page--; +} +#endif /* __iwl_legacy_dev_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-devtrace.c b/drivers/net/wireless/iwlegacy/iwl-devtrace.c new file mode 100644 index 00000000000..080b852b33b --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-devtrace.c @@ -0,0 +1,45 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/module.h> + +/* sparse doesn't like tracepoint macros */ +#ifndef __CHECKER__ +#include "iwl-dev.h" + +#define CREATE_TRACE_POINTS +#include "iwl-devtrace.h" + +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_iowrite8); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ioread32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_iowrite32); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_rx); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_tx); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_error); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_cont_event); +EXPORT_TRACEPOINT_SYMBOL(iwlwifi_legacy_dev_ucode_wrap_event); +#endif diff --git a/drivers/net/wireless/iwlegacy/iwl-devtrace.h b/drivers/net/wireless/iwlegacy/iwl-devtrace.h new file mode 100644 index 00000000000..9612aa0f6ec --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-devtrace.h @@ -0,0 +1,270 @@ +/****************************************************************************** + * + * Copyright(c) 2009 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#if !defined(__IWLWIFI_LEGACY_DEVICE_TRACE) || defined(TRACE_HEADER_MULTI_READ) +#define __IWLWIFI_LEGACY_DEVICE_TRACE + +#include <linux/tracepoint.h> + +#if !defined(CONFIG_IWLWIFI_LEGACY_DEVICE_TRACING) || defined(__CHECKER__) +#undef TRACE_EVENT +#define TRACE_EVENT(name, proto, ...) \ +static inline void trace_ ## name(proto) {} +#endif + + +#define PRIV_ENTRY __field(struct iwl_priv *, priv) +#define PRIV_ASSIGN (__entry->priv = priv) + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_legacy_io + +TRACE_EVENT(iwlwifi_legacy_dev_ioread32, + TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] read io[%#x] = %#x", __entry->priv, + __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_legacy_dev_iowrite8, + TP_PROTO(struct iwl_priv *priv, u32 offs, u8 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u8, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, + __entry->offs, __entry->val) +); + +TRACE_EVENT(iwlwifi_legacy_dev_iowrite32, + TP_PROTO(struct iwl_priv *priv, u32 offs, u32 val), + TP_ARGS(priv, offs, val), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, offs) + __field(u32, val) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->offs = offs; + __entry->val = val; + ), + TP_printk("[%p] write io[%#x] = %#x)", __entry->priv, + __entry->offs, __entry->val) +); + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi_legacy_ucode + +TRACE_EVENT(iwlwifi_legacy_dev_ucode_cont_event, + TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_ARGS(priv, time, data, ev), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u", + __entry->priv, __entry->time, __entry->data, __entry->ev) +); + +TRACE_EVENT(iwlwifi_legacy_dev_ucode_wrap_event, + TP_PROTO(struct iwl_priv *priv, u32 wraps, u32 n_entry, u32 p_entry), + TP_ARGS(priv, wraps, n_entry, p_entry), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, wraps) + __field(u32, n_entry) + __field(u32, p_entry) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->wraps = wraps; + __entry->n_entry = n_entry; + __entry->p_entry = p_entry; + ), + TP_printk("[%p] wraps=#%02d n=0x%X p=0x%X", + __entry->priv, __entry->wraps, __entry->n_entry, + __entry->p_entry) +); + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM iwlwifi + +TRACE_EVENT(iwlwifi_legacy_dev_hcmd, + TP_PROTO(struct iwl_priv *priv, void *hcmd, size_t len, u32 flags), + TP_ARGS(priv, hcmd, len, flags), + TP_STRUCT__entry( + PRIV_ENTRY + __dynamic_array(u8, hcmd, len) + __field(u32, flags) + ), + TP_fast_assign( + PRIV_ASSIGN; + memcpy(__get_dynamic_array(hcmd), hcmd, len); + __entry->flags = flags; + ), + TP_printk("[%p] hcmd %#.2x (%ssync)", + __entry->priv, ((u8 *)__get_dynamic_array(hcmd))[0], + __entry->flags & CMD_ASYNC ? "a" : "") +); + +TRACE_EVENT(iwlwifi_legacy_dev_rx, + TP_PROTO(struct iwl_priv *priv, void *rxbuf, size_t len), + TP_ARGS(priv, rxbuf, len), + TP_STRUCT__entry( + PRIV_ENTRY + __dynamic_array(u8, rxbuf, len) + ), + TP_fast_assign( + PRIV_ASSIGN; + memcpy(__get_dynamic_array(rxbuf), rxbuf, len); + ), + TP_printk("[%p] RX cmd %#.2x", + __entry->priv, ((u8 *)__get_dynamic_array(rxbuf))[4]) +); + +TRACE_EVENT(iwlwifi_legacy_dev_tx, + TP_PROTO(struct iwl_priv *priv, void *tfd, size_t tfdlen, + void *buf0, size_t buf0_len, + void *buf1, size_t buf1_len), + TP_ARGS(priv, tfd, tfdlen, buf0, buf0_len, buf1, buf1_len), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(size_t, framelen) + __dynamic_array(u8, tfd, tfdlen) + + /* + * Do not insert between or below these items, + * we want to keep the frame together (except + * for the possible padding). + */ + __dynamic_array(u8, buf0, buf0_len) + __dynamic_array(u8, buf1, buf1_len) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->framelen = buf0_len + buf1_len; + memcpy(__get_dynamic_array(tfd), tfd, tfdlen); + memcpy(__get_dynamic_array(buf0), buf0, buf0_len); + memcpy(__get_dynamic_array(buf1), buf1, buf1_len); + ), + TP_printk("[%p] TX %.2x (%zu bytes)", + __entry->priv, + ((u8 *)__get_dynamic_array(buf0))[0], + __entry->framelen) +); + +TRACE_EVENT(iwlwifi_legacy_dev_ucode_error, + TP_PROTO(struct iwl_priv *priv, u32 desc, u32 time, + u32 data1, u32 data2, u32 line, u32 blink1, + u32 blink2, u32 ilink1, u32 ilink2), + TP_ARGS(priv, desc, time, data1, data2, line, + blink1, blink2, ilink1, ilink2), + TP_STRUCT__entry( + PRIV_ENTRY + __field(u32, desc) + __field(u32, time) + __field(u32, data1) + __field(u32, data2) + __field(u32, line) + __field(u32, blink1) + __field(u32, blink2) + __field(u32, ilink1) + __field(u32, ilink2) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->desc = desc; + __entry->time = time; + __entry->data1 = data1; + __entry->data2 = data2; + __entry->line = line; + __entry->blink1 = blink1; + __entry->blink2 = blink2; + __entry->ilink1 = ilink1; + __entry->ilink2 = ilink2; + ), + TP_printk("[%p] #%02d %010u data 0x%08X 0x%08X line %u, " + "blink 0x%05X 0x%05X ilink 0x%05X 0x%05X", + __entry->priv, __entry->desc, __entry->time, __entry->data1, + __entry->data2, __entry->line, __entry->blink1, + __entry->blink2, __entry->ilink1, __entry->ilink2) +); + +TRACE_EVENT(iwlwifi_legacy_dev_ucode_event, + TP_PROTO(struct iwl_priv *priv, u32 time, u32 data, u32 ev), + TP_ARGS(priv, time, data, ev), + TP_STRUCT__entry( + PRIV_ENTRY + + __field(u32, time) + __field(u32, data) + __field(u32, ev) + ), + TP_fast_assign( + PRIV_ASSIGN; + __entry->time = time; + __entry->data = data; + __entry->ev = ev; + ), + TP_printk("[%p] EVT_LOGT:%010u:0x%08x:%04u", + __entry->priv, __entry->time, __entry->data, __entry->ev) +); +#endif /* __IWLWIFI_DEVICE_TRACE */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE iwl-devtrace +#include <trace/define_trace.h> diff --git a/drivers/net/wireless/iwlegacy/iwl-eeprom.c b/drivers/net/wireless/iwlegacy/iwl-eeprom.c new file mode 100644 index 00000000000..04c5648027d --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-eeprom.c @@ -0,0 +1,561 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <net/mac80211.h> + +#include "iwl-commands.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-debug.h" +#include "iwl-eeprom.h" +#include "iwl-io.h" + +/************************** EEPROM BANDS **************************** + * + * The iwlegacy_eeprom_band definitions below provide the mapping from the + * EEPROM contents to the specific channel number supported for each + * band. + * + * For example, iwl_priv->eeprom.band_3_channels[4] from the band_3 + * definition below maps to physical channel 42 in the 5.2GHz spectrum. + * The specific geography and calibration information for that channel + * is contained in the eeprom map itself. + * + * During init, we copy the eeprom information and channel map + * information into priv->channel_info_24/52 and priv->channel_map_24/52 + * + * channel_map_24/52 provides the index in the channel_info array for a + * given channel. We have to have two separate maps as there is channel + * overlap with the 2.4GHz and 5.2GHz spectrum as seen in band_1 and + * band_2 + * + * A value of 0xff stored in the channel_map indicates that the channel + * is not supported by the hardware at all. + * + * A value of 0xfe in the channel_map indicates that the channel is not + * valid for Tx with the current hardware. This means that + * while the system can tune and receive on a given channel, it may not + * be able to associate or transmit any frames on that + * channel. There is no corresponding channel information for that + * entry. + * + *********************************************************************/ + +/* 2.4 GHz */ +const u8 iwlegacy_eeprom_band_1[14] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 +}; + +/* 5.2 GHz bands */ +static const u8 iwlegacy_eeprom_band_2[] = { /* 4915-5080MHz */ + 183, 184, 185, 187, 188, 189, 192, 196, 7, 8, 11, 12, 16 +}; + +static const u8 iwlegacy_eeprom_band_3[] = { /* 5170-5320MHz */ + 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 +}; + +static const u8 iwlegacy_eeprom_band_4[] = { /* 5500-5700MHz */ + 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 +}; + +static const u8 iwlegacy_eeprom_band_5[] = { /* 5725-5825MHz */ + 145, 149, 153, 157, 161, 165 +}; + +static const u8 iwlegacy_eeprom_band_6[] = { /* 2.4 ht40 channel */ + 1, 2, 3, 4, 5, 6, 7 +}; + +static const u8 iwlegacy_eeprom_band_7[] = { /* 5.2 ht40 channel */ + 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157 +}; + +/****************************************************************************** + * + * EEPROM related functions + * +******************************************************************************/ + +static int iwl_legacy_eeprom_verify_signature(struct iwl_priv *priv) +{ + u32 gp = iwl_read32(priv, CSR_EEPROM_GP) & CSR_EEPROM_GP_VALID_MSK; + int ret = 0; + + IWL_DEBUG_EEPROM(priv, "EEPROM signature=0x%08x\n", gp); + switch (gp) { + case CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K: + case CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K: + break; + default: + IWL_ERR(priv, "bad EEPROM signature," + "EEPROM_GP=0x%08x\n", gp); + ret = -ENOENT; + break; + } + return ret; +} + +const u8 +*iwl_legacy_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) +{ + BUG_ON(offset >= priv->cfg->base_params->eeprom_size); + return &priv->eeprom[offset]; +} +EXPORT_SYMBOL(iwl_legacy_eeprom_query_addr); + +u16 iwl_legacy_eeprom_query16(const struct iwl_priv *priv, size_t offset) +{ + if (!priv->eeprom) + return 0; + return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); +} +EXPORT_SYMBOL(iwl_legacy_eeprom_query16); + +/** + * iwl_legacy_eeprom_init - read EEPROM contents + * + * Load the EEPROM contents from adapter into priv->eeprom + * + * NOTE: This routine uses the non-debug IO access functions. + */ +int iwl_legacy_eeprom_init(struct iwl_priv *priv) +{ + __le16 *e; + u32 gp = iwl_read32(priv, CSR_EEPROM_GP); + int sz; + int ret; + u16 addr; + + /* allocate eeprom */ + sz = priv->cfg->base_params->eeprom_size; + IWL_DEBUG_EEPROM(priv, "NVM size = %d\n", sz); + priv->eeprom = kzalloc(sz, GFP_KERNEL); + if (!priv->eeprom) { + ret = -ENOMEM; + goto alloc_err; + } + e = (__le16 *)priv->eeprom; + + priv->cfg->ops->lib->apm_ops.init(priv); + + ret = iwl_legacy_eeprom_verify_signature(priv); + if (ret < 0) { + IWL_ERR(priv, "EEPROM not found, EEPROM_GP=0x%08x\n", gp); + ret = -ENOENT; + goto err; + } + + /* Make sure driver (instead of uCode) is allowed to read EEPROM */ + ret = priv->cfg->ops->lib->eeprom_ops.acquire_semaphore(priv); + if (ret < 0) { + IWL_ERR(priv, "Failed to acquire EEPROM semaphore.\n"); + ret = -ENOENT; + goto err; + } + + /* eeprom is an array of 16bit values */ + for (addr = 0; addr < sz; addr += sizeof(u16)) { + u32 r; + + _iwl_legacy_write32(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_MSK_ADDR & (addr << 1)); + + ret = iwl_poll_bit(priv, CSR_EEPROM_REG, + CSR_EEPROM_REG_READ_VALID_MSK, + CSR_EEPROM_REG_READ_VALID_MSK, + IWL_EEPROM_ACCESS_TIMEOUT); + if (ret < 0) { + IWL_ERR(priv, "Time out reading EEPROM[%d]\n", + addr); + goto done; + } + r = _iwl_legacy_read_direct32(priv, CSR_EEPROM_REG); + e[addr / 2] = cpu_to_le16(r >> 16); + } + + IWL_DEBUG_EEPROM(priv, "NVM Type: %s, version: 0x%x\n", + "EEPROM", + iwl_legacy_eeprom_query16(priv, EEPROM_VERSION)); + + ret = 0; +done: + priv->cfg->ops->lib->eeprom_ops.release_semaphore(priv); + +err: + if (ret) + iwl_legacy_eeprom_free(priv); + /* Reset chip to save power until we load uCode during "up". */ + iwl_legacy_apm_stop(priv); +alloc_err: + return ret; +} +EXPORT_SYMBOL(iwl_legacy_eeprom_init); + +void iwl_legacy_eeprom_free(struct iwl_priv *priv) +{ + kfree(priv->eeprom); + priv->eeprom = NULL; +} +EXPORT_SYMBOL(iwl_legacy_eeprom_free); + +static void iwl_legacy_init_band_reference(const struct iwl_priv *priv, + int eep_band, int *eeprom_ch_count, + const struct iwl_eeprom_channel **eeprom_ch_info, + const u8 **eeprom_ch_index) +{ + u32 offset = priv->cfg->ops->lib-> + eeprom_ops.regulatory_bands[eep_band - 1]; + switch (eep_band) { + case 1: /* 2.4GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_1); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_1; + break; + case 2: /* 4.9GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_2); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_2; + break; + case 3: /* 5.2GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_3); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_3; + break; + case 4: /* 5.5GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_4); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_4; + break; + case 5: /* 5.7GHz band */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_5); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_5; + break; + case 6: /* 2.4GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_6); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_6; + break; + case 7: /* 5 GHz ht40 channels */ + *eeprom_ch_count = ARRAY_SIZE(iwlegacy_eeprom_band_7); + *eeprom_ch_info = (struct iwl_eeprom_channel *) + iwl_legacy_eeprom_query_addr(priv, offset); + *eeprom_ch_index = iwlegacy_eeprom_band_7; + break; + default: + BUG(); + return; + } +} + +#define CHECK_AND_PRINT(x) ((eeprom_ch->flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") +/** + * iwl_legacy_mod_ht40_chan_info - Copy ht40 channel info into driver's priv. + * + * Does not set up a command, or touch hardware. + */ +static int iwl_legacy_mod_ht40_chan_info(struct iwl_priv *priv, + enum ieee80211_band band, u16 channel, + const struct iwl_eeprom_channel *eeprom_ch, + u8 clear_ht40_extension_channel) +{ + struct iwl_channel_info *ch_info; + + ch_info = (struct iwl_channel_info *) + iwl_legacy_get_channel_info(priv, band, channel); + + if (!iwl_legacy_is_channel_valid(ch_info)) + return -1; + + IWL_DEBUG_EEPROM(priv, "HT40 Ch. %d [%sGHz] %s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", + ch_info->channel, + iwl_legacy_is_channel_a_band(ch_info) ? + "5.2" : "2.4", + CHECK_AND_PRINT(IBSS), + CHECK_AND_PRINT(ACTIVE), + CHECK_AND_PRINT(RADAR), + CHECK_AND_PRINT(WIDE), + CHECK_AND_PRINT(DFS), + eeprom_ch->flags, + eeprom_ch->max_power_avg, + ((eeprom_ch->flags & EEPROM_CHANNEL_IBSS) + && !(eeprom_ch->flags & EEPROM_CHANNEL_RADAR)) ? + "" : "not "); + + ch_info->ht40_eeprom = *eeprom_ch; + ch_info->ht40_max_power_avg = eeprom_ch->max_power_avg; + ch_info->ht40_flags = eeprom_ch->flags; + if (eeprom_ch->flags & EEPROM_CHANNEL_VALID) + ch_info->ht40_extension_channel &= + ~clear_ht40_extension_channel; + + return 0; +} + +#define CHECK_AND_PRINT_I(x) ((eeprom_ch_info[ch].flags & EEPROM_CHANNEL_##x) \ + ? # x " " : "") + +/** + * iwl_legacy_init_channel_map - Set up driver's info for all possible channels + */ +int iwl_legacy_init_channel_map(struct iwl_priv *priv) +{ + int eeprom_ch_count = 0; + const u8 *eeprom_ch_index = NULL; + const struct iwl_eeprom_channel *eeprom_ch_info = NULL; + int band, ch; + struct iwl_channel_info *ch_info; + + if (priv->channel_count) { + IWL_DEBUG_EEPROM(priv, "Channel map already initialized.\n"); + return 0; + } + + IWL_DEBUG_EEPROM(priv, "Initializing regulatory info from EEPROM\n"); + + priv->channel_count = + ARRAY_SIZE(iwlegacy_eeprom_band_1) + + ARRAY_SIZE(iwlegacy_eeprom_band_2) + + ARRAY_SIZE(iwlegacy_eeprom_band_3) + + ARRAY_SIZE(iwlegacy_eeprom_band_4) + + ARRAY_SIZE(iwlegacy_eeprom_band_5); + + IWL_DEBUG_EEPROM(priv, "Parsing data for %d channels.\n", + priv->channel_count); + + priv->channel_info = kzalloc(sizeof(struct iwl_channel_info) * + priv->channel_count, GFP_KERNEL); + if (!priv->channel_info) { + IWL_ERR(priv, "Could not allocate channel_info\n"); + priv->channel_count = 0; + return -ENOMEM; + } + + ch_info = priv->channel_info; + + /* Loop through the 5 EEPROM bands adding them in order to the + * channel map we maintain (that contains additional information than + * what just in the EEPROM) */ + for (band = 1; band <= 5; band++) { + + iwl_legacy_init_band_reference(priv, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_index); + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + ch_info->channel = eeprom_ch_index[ch]; + ch_info->band = (band == 1) ? IEEE80211_BAND_2GHZ : + IEEE80211_BAND_5GHZ; + + /* permanently store EEPROM's channel regulatory flags + * and max power in channel info database. */ + ch_info->eeprom = eeprom_ch_info[ch]; + + /* Copy the run-time flags so they are there even on + * invalid channels */ + ch_info->flags = eeprom_ch_info[ch].flags; + /* First write that ht40 is not enabled, and then enable + * one by one */ + ch_info->ht40_extension_channel = + IEEE80211_CHAN_NO_HT40; + + if (!(iwl_legacy_is_channel_valid(ch_info))) { + IWL_DEBUG_EEPROM(priv, + "Ch. %d Flags %x [%sGHz] - " + "No traffic\n", + ch_info->channel, + ch_info->flags, + iwl_legacy_is_channel_a_band(ch_info) ? + "5.2" : "2.4"); + ch_info++; + continue; + } + + /* Initialize regulatory-based run-time data */ + ch_info->max_power_avg = ch_info->curr_txpow = + eeprom_ch_info[ch].max_power_avg; + ch_info->scan_power = eeprom_ch_info[ch].max_power_avg; + ch_info->min_power = 0; + + IWL_DEBUG_EEPROM(priv, "Ch. %d [%sGHz] " + "%s%s%s%s%s%s(0x%02x %ddBm):" + " Ad-Hoc %ssupported\n", + ch_info->channel, + iwl_legacy_is_channel_a_band(ch_info) ? + "5.2" : "2.4", + CHECK_AND_PRINT_I(VALID), + CHECK_AND_PRINT_I(IBSS), + CHECK_AND_PRINT_I(ACTIVE), + CHECK_AND_PRINT_I(RADAR), + CHECK_AND_PRINT_I(WIDE), + CHECK_AND_PRINT_I(DFS), + eeprom_ch_info[ch].flags, + eeprom_ch_info[ch].max_power_avg, + ((eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_IBSS) + && !(eeprom_ch_info[ch]. + flags & EEPROM_CHANNEL_RADAR)) + ? "" : "not "); + + /* Set the tx_power_user_lmt to the highest power + * supported by any channel */ + if (eeprom_ch_info[ch].max_power_avg > + priv->tx_power_user_lmt) + priv->tx_power_user_lmt = + eeprom_ch_info[ch].max_power_avg; + + ch_info++; + } + } + + /* Check if we do have HT40 channels */ + if (priv->cfg->ops->lib->eeprom_ops.regulatory_bands[5] == + EEPROM_REGULATORY_BAND_NO_HT40 && + priv->cfg->ops->lib->eeprom_ops.regulatory_bands[6] == + EEPROM_REGULATORY_BAND_NO_HT40) + return 0; + + /* Two additional EEPROM bands for 2.4 and 5 GHz HT40 channels */ + for (band = 6; band <= 7; band++) { + enum ieee80211_band ieeeband; + + iwl_legacy_init_band_reference(priv, band, &eeprom_ch_count, + &eeprom_ch_info, &eeprom_ch_index); + + /* EEPROM band 6 is 2.4, band 7 is 5 GHz */ + ieeeband = + (band == 6) ? IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ; + + /* Loop through each band adding each of the channels */ + for (ch = 0; ch < eeprom_ch_count; ch++) { + /* Set up driver's info for lower half */ + iwl_legacy_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch], + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40PLUS); + + /* Set up driver's info for upper half */ + iwl_legacy_mod_ht40_chan_info(priv, ieeeband, + eeprom_ch_index[ch] + 4, + &eeprom_ch_info[ch], + IEEE80211_CHAN_NO_HT40MINUS); + } + } + + return 0; +} +EXPORT_SYMBOL(iwl_legacy_init_channel_map); + +/* + * iwl_legacy_free_channel_map - undo allocations in iwl_legacy_init_channel_map + */ +void iwl_legacy_free_channel_map(struct iwl_priv *priv) +{ + kfree(priv->channel_info); + priv->channel_count = 0; +} +EXPORT_SYMBOL(iwl_legacy_free_channel_map); + +/** + * iwl_legacy_get_channel_info - Find driver's private channel info + * + * Based on band and channel number. + */ +const struct +iwl_channel_info *iwl_legacy_get_channel_info(const struct iwl_priv *priv, + enum ieee80211_band band, u16 channel) +{ + int i; + + switch (band) { + case IEEE80211_BAND_5GHZ: + for (i = 14; i < priv->channel_count; i++) { + if (priv->channel_info[i].channel == channel) + return &priv->channel_info[i]; + } + break; + case IEEE80211_BAND_2GHZ: + if (channel >= 1 && channel <= 14) + return &priv->channel_info[channel - 1]; + break; + default: + BUG(); + } + + return NULL; +} +EXPORT_SYMBOL(iwl_legacy_get_channel_info); diff --git a/drivers/net/wireless/iwlegacy/iwl-eeprom.h b/drivers/net/wireless/iwlegacy/iwl-eeprom.h new file mode 100644 index 00000000000..c59c8100202 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-eeprom.h @@ -0,0 +1,344 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_legacy_eeprom_h__ +#define __iwl_legacy_eeprom_h__ + +#include <net/mac80211.h> + +struct iwl_priv; + +/* + * EEPROM access time values: + * + * Driver initiates EEPROM read by writing byte address << 1 to CSR_EEPROM_REG. + * Driver then polls CSR_EEPROM_REG for CSR_EEPROM_REG_READ_VALID_MSK (0x1). + * When polling, wait 10 uSec between polling loops, up to a maximum 5000 uSec. + * Driver reads 16-bit value from bits 31-16 of CSR_EEPROM_REG. + */ +#define IWL_EEPROM_ACCESS_TIMEOUT 5000 /* uSec */ + +#define IWL_EEPROM_SEM_TIMEOUT 10 /* microseconds */ +#define IWL_EEPROM_SEM_RETRY_LIMIT 1000 /* number of attempts (not time) */ + + +/* + * Regulatory channel usage flags in EEPROM struct iwl4965_eeprom_channel.flags. + * + * IBSS and/or AP operation is allowed *only* on those channels with + * (VALID && IBSS && ACTIVE && !RADAR). This restriction is in place because + * RADAR detection is not supported by the 4965 driver, but is a + * requirement for establishing a new network for legal operation on channels + * requiring RADAR detection or restricting ACTIVE scanning. + * + * NOTE: "WIDE" flag does not indicate anything about "HT40" 40 MHz channels. + * It only indicates that 20 MHz channel use is supported; HT40 channel + * usage is indicated by a separate set of regulatory flags for each + * HT40 channel pair. + * + * NOTE: Using a channel inappropriately will result in a uCode error! + */ +#define IWL_NUM_TX_CALIB_GROUPS 5 +enum { + EEPROM_CHANNEL_VALID = (1 << 0), /* usable for this SKU/geo */ + EEPROM_CHANNEL_IBSS = (1 << 1), /* usable as an IBSS channel */ + /* Bit 2 Reserved */ + EEPROM_CHANNEL_ACTIVE = (1 << 3), /* active scanning allowed */ + EEPROM_CHANNEL_RADAR = (1 << 4), /* radar detection required */ + EEPROM_CHANNEL_WIDE = (1 << 5), /* 20 MHz channel okay */ + /* Bit 6 Reserved (was Narrow Channel) */ + EEPROM_CHANNEL_DFS = (1 << 7), /* dynamic freq selection candidate */ +}; + +/* SKU Capabilities */ +/* 3945 only */ +#define EEPROM_SKU_CAP_SW_RF_KILL_ENABLE (1 << 0) +#define EEPROM_SKU_CAP_HW_RF_KILL_ENABLE (1 << 1) + +/* *regulatory* channel data format in eeprom, one for each channel. + * There are separate entries for HT40 (40 MHz) vs. normal (20 MHz) channels. */ +struct iwl_eeprom_channel { + u8 flags; /* EEPROM_CHANNEL_* flags copied from EEPROM */ + s8 max_power_avg; /* max power (dBm) on this chnl, limit 31 */ +} __packed; + +/* 3945 Specific */ +#define EEPROM_3945_EEPROM_VERSION (0x2f) + +/* 4965 has two radio transmitters (and 3 radio receivers) */ +#define EEPROM_TX_POWER_TX_CHAINS (2) + +/* 4965 has room for up to 8 sets of txpower calibration data */ +#define EEPROM_TX_POWER_BANDS (8) + +/* 4965 factory calibration measures txpower gain settings for + * each of 3 target output levels */ +#define EEPROM_TX_POWER_MEASUREMENTS (3) + +/* 4965 Specific */ +/* 4965 driver does not work with txpower calibration version < 5 */ +#define EEPROM_4965_TX_POWER_VERSION (5) +#define EEPROM_4965_EEPROM_VERSION (0x2f) +#define EEPROM_4965_CALIB_VERSION_OFFSET (2*0xB6) /* 2 bytes */ +#define EEPROM_4965_CALIB_TXPOWER_OFFSET (2*0xE8) /* 48 bytes */ +#define EEPROM_4965_BOARD_REVISION (2*0x4F) /* 2 bytes */ +#define EEPROM_4965_BOARD_PBA (2*0x56+1) /* 9 bytes */ + +/* 2.4 GHz */ +extern const u8 iwlegacy_eeprom_band_1[14]; + +/* + * factory calibration data for one txpower level, on one channel, + * measured on one of the 2 tx chains (radio transmitter and associated + * antenna). EEPROM contains: + * + * 1) Temperature (degrees Celsius) of device when measurement was made. + * + * 2) Gain table index used to achieve the target measurement power. + * This refers to the "well-known" gain tables (see iwl-4965-hw.h). + * + * 3) Actual measured output power, in half-dBm ("34" = 17 dBm). + * + * 4) RF power amplifier detector level measurement (not used). + */ +struct iwl_eeprom_calib_measure { + u8 temperature; /* Device temperature (Celsius) */ + u8 gain_idx; /* Index into gain table */ + u8 actual_pow; /* Measured RF output power, half-dBm */ + s8 pa_det; /* Power amp detector level (not used) */ +} __packed; + + +/* + * measurement set for one channel. EEPROM contains: + * + * 1) Channel number measured + * + * 2) Measurements for each of 3 power levels for each of 2 radio transmitters + * (a.k.a. "tx chains") (6 measurements altogether) + */ +struct iwl_eeprom_calib_ch_info { + u8 ch_num; + struct iwl_eeprom_calib_measure + measurements[EEPROM_TX_POWER_TX_CHAINS] + [EEPROM_TX_POWER_MEASUREMENTS]; +} __packed; + +/* + * txpower subband info. + * + * For each frequency subband, EEPROM contains the following: + * + * 1) First and last channels within range of the subband. "0" values + * indicate that this sample set is not being used. + * + * 2) Sample measurement sets for 2 channels close to the range endpoints. + */ +struct iwl_eeprom_calib_subband_info { + u8 ch_from; /* channel number of lowest channel in subband */ + u8 ch_to; /* channel number of highest channel in subband */ + struct iwl_eeprom_calib_ch_info ch1; + struct iwl_eeprom_calib_ch_info ch2; +} __packed; + + +/* + * txpower calibration info. EEPROM contains: + * + * 1) Factory-measured saturation power levels (maximum levels at which + * tx power amplifier can output a signal without too much distortion). + * There is one level for 2.4 GHz band and one for 5 GHz band. These + * values apply to all channels within each of the bands. + * + * 2) Factory-measured power supply voltage level. This is assumed to be + * constant (i.e. same value applies to all channels/bands) while the + * factory measurements are being made. + * + * 3) Up to 8 sets of factory-measured txpower calibration values. + * These are for different frequency ranges, since txpower gain + * characteristics of the analog radio circuitry vary with frequency. + * + * Not all sets need to be filled with data; + * struct iwl_eeprom_calib_subband_info contains range of channels + * (0 if unused) for each set of data. + */ +struct iwl_eeprom_calib_info { + u8 saturation_power24; /* half-dBm (e.g. "34" = 17 dBm) */ + u8 saturation_power52; /* half-dBm */ + __le16 voltage; /* signed */ + struct iwl_eeprom_calib_subband_info + band_info[EEPROM_TX_POWER_BANDS]; +} __packed; + + +/* General */ +#define EEPROM_DEVICE_ID (2*0x08) /* 2 bytes */ +#define EEPROM_MAC_ADDRESS (2*0x15) /* 6 bytes */ +#define EEPROM_BOARD_REVISION (2*0x35) /* 2 bytes */ +#define EEPROM_BOARD_PBA_NUMBER (2*0x3B+1) /* 9 bytes */ +#define EEPROM_VERSION (2*0x44) /* 2 bytes */ +#define EEPROM_SKU_CAP (2*0x45) /* 2 bytes */ +#define EEPROM_OEM_MODE (2*0x46) /* 2 bytes */ +#define EEPROM_WOWLAN_MODE (2*0x47) /* 2 bytes */ +#define EEPROM_RADIO_CONFIG (2*0x48) /* 2 bytes */ +#define EEPROM_NUM_MAC_ADDRESS (2*0x4C) /* 2 bytes */ + +/* The following masks are to be applied on EEPROM_RADIO_CONFIG */ +#define EEPROM_RF_CFG_TYPE_MSK(x) (x & 0x3) /* bits 0-1 */ +#define EEPROM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ +#define EEPROM_RF_CFG_DASH_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ +#define EEPROM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ +#define EEPROM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ +#define EEPROM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ + +#define EEPROM_3945_RF_CFG_TYPE_MAX 0x0 +#define EEPROM_4965_RF_CFG_TYPE_MAX 0x1 + +/* + * Per-channel regulatory data. + * + * Each channel that *might* be supported by iwl has a fixed location + * in EEPROM containing EEPROM_CHANNEL_* usage flags (LSB) and max regulatory + * txpower (MSB). + * + * Entries immediately below are for 20 MHz channel width. HT40 (40 MHz) + * channels (only for 4965, not supported by 3945) appear later in the EEPROM. + * + * 2.4 GHz channels 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + */ +#define EEPROM_REGULATORY_SKU_ID (2*0x60) /* 4 bytes */ +#define EEPROM_REGULATORY_BAND_1 (2*0x62) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_1_CHANNELS (2*0x63) /* 28 bytes */ + +/* + * 4.9 GHz channels 183, 184, 185, 187, 188, 189, 192, 196, + * 5.0 GHz channels 7, 8, 11, 12, 16 + * (4915-5080MHz) (none of these is ever supported) + */ +#define EEPROM_REGULATORY_BAND_2 (2*0x71) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_2_CHANNELS (2*0x72) /* 26 bytes */ + +/* + * 5.2 GHz channels 34, 36, 38, 40, 42, 44, 46, 48, 52, 56, 60, 64 + * (5170-5320MHz) + */ +#define EEPROM_REGULATORY_BAND_3 (2*0x7F) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_3_CHANNELS (2*0x80) /* 24 bytes */ + +/* + * 5.5 GHz channels 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140 + * (5500-5700MHz) + */ +#define EEPROM_REGULATORY_BAND_4 (2*0x8C) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_4_CHANNELS (2*0x8D) /* 22 bytes */ + +/* + * 5.7 GHz channels 145, 149, 153, 157, 161, 165 + * (5725-5825MHz) + */ +#define EEPROM_REGULATORY_BAND_5 (2*0x98) /* 2 bytes */ +#define EEPROM_REGULATORY_BAND_5_CHANNELS (2*0x99) /* 12 bytes */ + +/* + * 2.4 GHz HT40 channels 1 (5), 2 (6), 3 (7), 4 (8), 5 (9), 6 (10), 7 (11) + * + * The channel listed is the center of the lower 20 MHz half of the channel. + * The overall center frequency is actually 2 channels (10 MHz) above that, + * and the upper half of each HT40 channel is centered 4 channels (20 MHz) away + * from the lower half; e.g. the upper half of HT40 channel 1 is channel 5, + * and the overall HT40 channel width centers on channel 3. + * + * NOTE: The RXON command uses 20 MHz channel numbers to specify the + * control channel to which to tune. RXON also specifies whether the + * control channel is the upper or lower half of a HT40 channel. + * + * NOTE: 4965 does not support HT40 channels on 2.4 GHz. + */ +#define EEPROM_4965_REGULATORY_BAND_24_HT40_CHANNELS (2*0xA0) /* 14 bytes */ + +/* + * 5.2 GHz HT40 channels 36 (40), 44 (48), 52 (56), 60 (64), + * 100 (104), 108 (112), 116 (120), 124 (128), 132 (136), 149 (153), 157 (161) + */ +#define EEPROM_4965_REGULATORY_BAND_52_HT40_CHANNELS (2*0xA8) /* 22 bytes */ + +#define EEPROM_REGULATORY_BAND_NO_HT40 (0) + +struct iwl_eeprom_ops { + const u32 regulatory_bands[7]; + int (*acquire_semaphore) (struct iwl_priv *priv); + void (*release_semaphore) (struct iwl_priv *priv); +}; + + +int iwl_legacy_eeprom_init(struct iwl_priv *priv); +void iwl_legacy_eeprom_free(struct iwl_priv *priv); +const u8 *iwl_legacy_eeprom_query_addr(const struct iwl_priv *priv, + size_t offset); +u16 iwl_legacy_eeprom_query16(const struct iwl_priv *priv, size_t offset); +int iwl_legacy_init_channel_map(struct iwl_priv *priv); +void iwl_legacy_free_channel_map(struct iwl_priv *priv); +const struct iwl_channel_info *iwl_legacy_get_channel_info( + const struct iwl_priv *priv, + enum ieee80211_band band, u16 channel); + +#endif /* __iwl_legacy_eeprom_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-fh.h b/drivers/net/wireless/iwlegacy/iwl-fh.h new file mode 100644 index 00000000000..4e20c7e5c88 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-fh.h @@ -0,0 +1,513 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + *****************************************************************************/ +#ifndef __iwl_legacy_fh_h__ +#define __iwl_legacy_fh_h__ + +/****************************/ +/* Flow Handler Definitions */ +/****************************/ + +/** + * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) + * Addresses are offsets from device's PCI hardware base address. + */ +#define FH_MEM_LOWER_BOUND (0x1000) +#define FH_MEM_UPPER_BOUND (0x2000) + +/** + * Keep-Warm (KW) buffer base address. + * + * Driver must allocate a 4KByte buffer that is used by 4965 for keeping the + * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency + * DRAM access when 4965 is Txing or Rxing. The dummy accesses prevent host + * from going into a power-savings mode that would cause higher DRAM latency, + * and possible data over/under-runs, before all Tx/Rx is complete. + * + * Driver loads FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) + * of the buffer, which must be 4K aligned. Once this is set up, the 4965 + * automatically invokes keep-warm accesses when normal accesses might not + * be sufficient to maintain fast DRAM response. + * + * Bit fields: + * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned + */ +#define FH_KW_MEM_ADDR_REG (FH_MEM_LOWER_BOUND + 0x97C) + + +/** + * TFD Circular Buffers Base (CBBC) addresses + * + * 4965 has 16 base pointer registers, one for each of 16 host-DRAM-resident + * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) + * (see struct iwl_tfd_frame). These 16 pointer registers are offset by 0x04 + * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte + * aligned (address bits 0-7 must be 0). + * + * Bit fields in each pointer register: + * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned + */ +#define FH_MEM_CBBC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) +#define FH_MEM_CBBC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xA10) + +/* Find TFD CB base pointer for given queue (range 0-15). */ +#define FH_MEM_CBBC_QUEUE(x) (FH_MEM_CBBC_LOWER_BOUND + (x) * 0x4) + + +/** + * Rx SRAM Control and Status Registers (RSCSR) + * + * These registers provide handshake between driver and 4965 for the Rx queue + * (this queue handles *all* command responses, notifications, Rx data, etc. + * sent from 4965 uCode to host driver). Unlike Tx, there is only one Rx + * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can + * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer + * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 + * mapping between RBDs and RBs. + * + * Driver must allocate host DRAM memory for the following, and set the + * physical address of each into 4965 registers: + * + * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 + * entries (although any power of 2, up to 4096, is selectable by driver). + * Each entry (1 dword) points to a receive buffer (RB) of consistent size + * (typically 4K, although 8K or 16K are also selectable by driver). + * Driver sets up RB size and number of RBDs in the CB via Rx config + * register FH_MEM_RCSR_CHNL0_CONFIG_REG. + * + * Bit fields within one RBD: + * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned + * + * Driver sets physical address [35:8] of base of RBD circular buffer + * into FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. + * + * 2) Rx status buffer, 8 bytes, in which 4965 indicates which Rx Buffers + * (RBs) have been filled, via a "write pointer", actually the index of + * the RB's corresponding RBD within the circular buffer. Driver sets + * physical address [35:4] into FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. + * + * Bit fields in lower dword of Rx status buffer (upper dword not used + * by driver; see struct iwl4965_shared, val0): + * 31-12: Not used by driver + * 11- 0: Index of last filled Rx buffer descriptor + * (4965 writes, driver reads this value) + * + * As the driver prepares Receive Buffers (RBs) for 4965 to fill, driver must + * enter pointers to these RBs into contiguous RBD circular buffer entries, + * and update the 4965's "write" index register, + * FH_RSCSR_CHNL0_RBDCB_WPTR_REG. + * + * This "write" index corresponds to the *next* RBD that the driver will make + * available, i.e. one RBD past the tail of the ready-to-fill RBDs within + * the circular buffer. This value should initially be 0 (before preparing any + * RBs), should be 8 after preparing the first 8 RBs (for example), and must + * wrap back to 0 at the end of the circular buffer (but don't wrap before + * "read" index has advanced past 1! See below). + * NOTE: 4965 EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. + * + * As the 4965 fills RBs (referenced from contiguous RBDs within the circular + * buffer), it updates the Rx status buffer in host DRAM, 2) described above, + * to tell the driver the index of the latest filled RBD. The driver must + * read this "read" index from DRAM after receiving an Rx interrupt from 4965. + * + * The driver must also internally keep track of a third index, which is the + * next RBD to process. When receiving an Rx interrupt, driver should process + * all filled but unprocessed RBs up to, but not including, the RB + * corresponding to the "read" index. For example, if "read" index becomes "1", + * driver may process the RB pointed to by RBD 0. Depending on volume of + * traffic, there may be many RBs to process. + * + * If read index == write index, 4965 thinks there is no room to put new data. + * Due to this, the maximum number of filled RBs is 255, instead of 256. To + * be safe, make sure that there is a gap of at least 2 RBDs between "write" + * and "read" indexes; that is, make sure that there are no more than 254 + * buffers waiting to be filled. + */ +#define FH_MEM_RSCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xBC0) +#define FH_MEM_RSCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_RSCSR_CHNL0 (FH_MEM_RSCSR_LOWER_BOUND) + +/** + * Physical base address of 8-byte Rx Status buffer. + * Bit fields: + * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. + */ +#define FH_RSCSR_CHNL0_STTS_WPTR_REG (FH_MEM_RSCSR_CHNL0) + +/** + * Physical base address of Rx Buffer Descriptor Circular Buffer. + * Bit fields: + * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. + */ +#define FH_RSCSR_CHNL0_RBDCB_BASE_REG (FH_MEM_RSCSR_CHNL0 + 0x004) + +/** + * Rx write pointer (index, really!). + * Bit fields: + * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. + * NOTE: For 256-entry circular buffer, use only bits [7:0]. + */ +#define FH_RSCSR_CHNL0_RBDCB_WPTR_REG (FH_MEM_RSCSR_CHNL0 + 0x008) +#define FH_RSCSR_CHNL0_WPTR (FH_RSCSR_CHNL0_RBDCB_WPTR_REG) + + +/** + * Rx Config/Status Registers (RCSR) + * Rx Config Reg for channel 0 (only channel used) + * + * Driver must initialize FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for + * normal operation (see bit fields). + * + * Clearing FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. + * Driver should poll FH_MEM_RSSR_RX_STATUS_REG for + * FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. + * + * Bit fields: + * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29-24: reserved + * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), + * min "5" for 32 RBDs, max "12" for 4096 RBDs. + * 19-18: reserved + * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, + * '10' 12K, '11' 16K. + * 15-14: reserved + * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) + * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) + * typical value 0x10 (about 1/2 msec) + * 3- 0: reserved + */ +#define FH_MEM_RCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC00) +#define FH_MEM_RCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xCC0) +#define FH_MEM_RCSR_CHNL0 (FH_MEM_RCSR_LOWER_BOUND) + +#define FH_MEM_RCSR_CHNL0_CONFIG_REG (FH_MEM_RCSR_CHNL0) + +#define FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ +#define FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ +#define FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ +#define FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/ + +#define FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) +#define FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) +#define RX_RB_TIMEOUT (0x10) + +#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) +#define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) +#define FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) + +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) +#define FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) + +#define FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) +#define FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) + +#define FH_RSCSR_FRAME_SIZE_MSK (0x00003FFF) /* bits 0-13 */ + +/** + * Rx Shared Status Registers (RSSR) + * + * After stopping Rx DMA channel (writing 0 to + * FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll + * FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. + * + * Bit fields: + * 24: 1 = Channel 0 is idle + * + * FH_MEM_RSSR_SHARED_CTRL_REG and FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV + * contain default values that should not be altered by the driver. + */ +#define FH_MEM_RSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xC40) +#define FH_MEM_RSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) + +#define FH_MEM_RSSR_SHARED_CTRL_REG (FH_MEM_RSSR_LOWER_BOUND) +#define FH_MEM_RSSR_RX_STATUS_REG (FH_MEM_RSSR_LOWER_BOUND + 0x004) +#define FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ + (FH_MEM_RSSR_LOWER_BOUND + 0x008) + +#define FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) + +#define FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 + +/* TFDB Area - TFDs buffer table */ +#define FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) +#define FH_TFDIB_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x900) +#define FH_TFDIB_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x958) +#define FH_TFDIB_CTRL0_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) +#define FH_TFDIB_CTRL1_REG(_chnl) (FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) + +/** + * Transmit DMA Channel Control/Status Registers (TCSR) + * + * 4965 has one configuration register for each of 8 Tx DMA/FIFO channels + * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, + * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. + * + * To use a Tx DMA channel, driver must initialize its + * FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: + * + * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + * FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL + * + * All other bits should be 0. + * + * Bit fields: + * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, + * '10' operate normally + * 29- 4: Reserved, set to "0" + * 3: Enable internal DMA requests (1, normal operation), disable (0) + * 2- 0: Reserved, set to "0" + */ +#define FH_TCSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xD00) +#define FH_TCSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xE60) + +/* Find Control/Status reg for given Tx DMA/FIFO channel */ +#define FH49_TCSR_CHNL_NUM (7) +#define FH50_TCSR_CHNL_NUM (8) + +/* TCSR: tx_config register values */ +#define FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl)) +#define FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) +#define FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ + (FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) + +#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) + +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) + +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) + +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) +#define FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) + +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) +#define FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) + +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) + +#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) +#define FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) + +/** + * Tx Shared Status Registers (TSSR) + * + * After stopping Tx DMA channel (writing 0 to + * FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll + * FH_TSSR_TX_STATUS_REG until selected Tx channel is idle + * (channel's buffers empty | no pending requests). + * + * Bit fields: + * 31-24: 1 = Channel buffers empty (channel 7:0) + * 23-16: 1 = No pending requests (channel 7:0) + */ +#define FH_TSSR_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0xEA0) +#define FH_TSSR_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0xEC0) + +#define FH_TSSR_TX_STATUS_REG (FH_TSSR_LOWER_BOUND + 0x010) + +/** + * Bit fields for TSSR(Tx Shared Status & Control) error status register: + * 31: Indicates an address error when accessed to internal memory + * uCode/driver must write "1" in order to clear this flag + * 30: Indicates that Host did not send the expected number of dwords to FH + * uCode/driver must write "1" in order to clear this flag + * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA + * command was received from the scheduler while the TRB was already full + * with previous command + * uCode/driver must write "1" in order to clear this flag + * 7-0: Each status bit indicates a channel's TxCredit error. When an error + * bit is set, it indicates that the FH has received a full indication + * from the RTC TxFIFO and the current value of the TxCredit counter was + * not equal to zero. This mean that the credit mechanism was not + * synchronized to the TxFIFO status + * uCode/driver must write "1" in order to clear this flag + */ +#define FH_TSSR_TX_ERROR_REG (FH_TSSR_LOWER_BOUND + 0x018) + +#define FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) + +/* Tx service channels */ +#define FH_SRVC_CHNL (9) +#define FH_SRVC_LOWER_BOUND (FH_MEM_LOWER_BOUND + 0x9C8) +#define FH_SRVC_UPPER_BOUND (FH_MEM_LOWER_BOUND + 0x9D0) +#define FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ + (FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) + +#define FH_TX_CHICKEN_BITS_REG (FH_MEM_LOWER_BOUND + 0xE98) +/* Instruct FH to increment the retry count of a packet when + * it is brought from the memory to TX-FIFO + */ +#define FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) + +#define RX_QUEUE_SIZE 256 +#define RX_QUEUE_MASK 255 +#define RX_QUEUE_SIZE_LOG 8 + +/* + * RX related structures and functions + */ +#define RX_FREE_BUFFERS 64 +#define RX_LOW_WATERMARK 8 + +/* Size of one Rx buffer in host DRAM */ +#define IWL_RX_BUF_SIZE_3K (3 * 1000) /* 3945 only */ +#define IWL_RX_BUF_SIZE_4K (4 * 1024) +#define IWL_RX_BUF_SIZE_8K (8 * 1024) + +/** + * struct iwl_rb_status - reseve buffer status + * host memory mapped FH registers + * @closed_rb_num [0:11] - Indicates the index of the RB which was closed + * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed + * @finished_rb_num [0:11] - Indicates the index of the current RB + * in which the last frame was written to + * @finished_fr_num [0:11] - Indicates the index of the RX Frame + * which was transfered + */ +struct iwl_rb_status { + __le16 closed_rb_num; + __le16 closed_fr_num; + __le16 finished_rb_num; + __le16 finished_fr_nam; + __le32 __unused; /* 3945 only */ +} __packed; + + +#define TFD_QUEUE_SIZE_MAX (256) +#define TFD_QUEUE_SIZE_BC_DUP (64) +#define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) +#define IWL_TX_DMA_MASK DMA_BIT_MASK(36) +#define IWL_NUM_OF_TBS 20 + +static inline u8 iwl_legacy_get_dma_hi_addr(dma_addr_t addr) +{ + return (sizeof(addr) > sizeof(u32) ? (addr >> 16) >> 16 : 0) & 0xF; +} +/** + * struct iwl_tfd_tb transmit buffer descriptor within transmit frame descriptor + * + * This structure contains dma address and length of transmission address + * + * @lo: low [31:0] portion of the dma address of TX buffer + * every even is unaligned on 16 bit boundary + * @hi_n_len 0-3 [35:32] portion of dma + * 4-15 length of the tx buffer + */ +struct iwl_tfd_tb { + __le32 lo; + __le16 hi_n_len; +} __packed; + +/** + * struct iwl_tfd + * + * Transmit Frame Descriptor (TFD) + * + * @ __reserved1[3] reserved + * @ num_tbs 0-4 number of active tbs + * 5 reserved + * 6-7 padding (not used) + * @ tbs[20] transmit frame buffer descriptors + * @ __pad padding + * + * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. + * Both driver and device share these circular buffers, each of which must be + * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes + * + * Driver must indicate the physical address of the base of each + * circular buffer via the FH_MEM_CBBC_QUEUE registers. + * + * Each TFD contains pointer/size information for up to 20 data buffers + * in host DRAM. These buffers collectively contain the (one) frame described + * by the TFD. Each buffer must be a single contiguous block of memory within + * itself, but buffers may be scattered in host DRAM. Each buffer has max size + * of (4K - 4). The concatenates all of a TFD's buffers into a single + * Tx frame, up to 8 KBytes in size. + * + * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. + */ +struct iwl_tfd { + u8 __reserved1[3]; + u8 num_tbs; + struct iwl_tfd_tb tbs[IWL_NUM_OF_TBS]; + __le32 __pad; +} __packed; + +/* Keep Warm Size */ +#define IWL_KW_SIZE 0x1000 /* 4k */ + +#endif /* !__iwl_legacy_fh_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-hcmd.c b/drivers/net/wireless/iwlegacy/iwl-hcmd.c new file mode 100644 index 00000000000..9d721cbda5b --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-hcmd.c @@ -0,0 +1,271 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <net/mac80211.h> + +#include "iwl-dev.h" +#include "iwl-debug.h" +#include "iwl-eeprom.h" +#include "iwl-core.h" + + +const char *iwl_legacy_get_cmd_string(u8 cmd) +{ + switch (cmd) { + IWL_CMD(REPLY_ALIVE); + IWL_CMD(REPLY_ERROR); + IWL_CMD(REPLY_RXON); + IWL_CMD(REPLY_RXON_ASSOC); + IWL_CMD(REPLY_QOS_PARAM); + IWL_CMD(REPLY_RXON_TIMING); + IWL_CMD(REPLY_ADD_STA); + IWL_CMD(REPLY_REMOVE_STA); + IWL_CMD(REPLY_WEPKEY); + IWL_CMD(REPLY_3945_RX); + IWL_CMD(REPLY_TX); + IWL_CMD(REPLY_RATE_SCALE); + IWL_CMD(REPLY_LEDS_CMD); + IWL_CMD(REPLY_TX_LINK_QUALITY_CMD); + IWL_CMD(REPLY_CHANNEL_SWITCH); + IWL_CMD(CHANNEL_SWITCH_NOTIFICATION); + IWL_CMD(REPLY_SPECTRUM_MEASUREMENT_CMD); + IWL_CMD(SPECTRUM_MEASURE_NOTIFICATION); + IWL_CMD(POWER_TABLE_CMD); + IWL_CMD(PM_SLEEP_NOTIFICATION); + IWL_CMD(PM_DEBUG_STATISTIC_NOTIFIC); + IWL_CMD(REPLY_SCAN_CMD); + IWL_CMD(REPLY_SCAN_ABORT_CMD); + IWL_CMD(SCAN_START_NOTIFICATION); + IWL_CMD(SCAN_RESULTS_NOTIFICATION); + IWL_CMD(SCAN_COMPLETE_NOTIFICATION); + IWL_CMD(BEACON_NOTIFICATION); + IWL_CMD(REPLY_TX_BEACON); + IWL_CMD(REPLY_TX_PWR_TABLE_CMD); + IWL_CMD(REPLY_BT_CONFIG); + IWL_CMD(REPLY_STATISTICS_CMD); + IWL_CMD(STATISTICS_NOTIFICATION); + IWL_CMD(CARD_STATE_NOTIFICATION); + IWL_CMD(MISSED_BEACONS_NOTIFICATION); + IWL_CMD(REPLY_CT_KILL_CONFIG_CMD); + IWL_CMD(SENSITIVITY_CMD); + IWL_CMD(REPLY_PHY_CALIBRATION_CMD); + IWL_CMD(REPLY_RX_PHY_CMD); + IWL_CMD(REPLY_RX_MPDU_CMD); + IWL_CMD(REPLY_RX); + IWL_CMD(REPLY_COMPRESSED_BA); + default: + return "UNKNOWN"; + + } +} +EXPORT_SYMBOL(iwl_legacy_get_cmd_string); + +#define HOST_COMPLETE_TIMEOUT (HZ / 2) + +static void iwl_legacy_generic_cmd_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt) +{ + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(priv, "Bad return from %s (0x%08X)\n", + iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + return; + } + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + switch (cmd->hdr.cmd) { + case REPLY_TX_LINK_QUALITY_CMD: + case SENSITIVITY_CMD: + IWL_DEBUG_HC_DUMP(priv, "back from %s (0x%08X)\n", + iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + break; + default: + IWL_DEBUG_HC(priv, "back from %s (0x%08X)\n", + iwl_legacy_get_cmd_string(cmd->hdr.cmd), pkt->hdr.flags); + } +#endif +} + +static int +iwl_legacy_send_cmd_async(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + int ret; + + BUG_ON(!(cmd->flags & CMD_ASYNC)); + + /* An asynchronous command can not expect an SKB to be set. */ + BUG_ON(cmd->flags & CMD_WANT_SKB); + + /* Assign a generic callback if one is not provided */ + if (!cmd->callback) + cmd->callback = iwl_legacy_generic_cmd_callback; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EBUSY; + + ret = iwl_legacy_enqueue_hcmd(priv, cmd); + if (ret < 0) { + IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n", + iwl_legacy_get_cmd_string(cmd->id), ret); + return ret; + } + return 0; +} + +int iwl_legacy_send_cmd_sync(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + int cmd_idx; + int ret; + + BUG_ON(cmd->flags & CMD_ASYNC); + + /* A synchronous command can not have a callback set. */ + BUG_ON(cmd->callback); + + IWL_DEBUG_INFO(priv, "Attempting to send sync command %s\n", + iwl_legacy_get_cmd_string(cmd->id)); + mutex_lock(&priv->sync_cmd_mutex); + + set_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Setting HCMD_ACTIVE for command %s\n", + iwl_legacy_get_cmd_string(cmd->id)); + + cmd_idx = iwl_legacy_enqueue_hcmd(priv, cmd); + if (cmd_idx < 0) { + ret = cmd_idx; + IWL_ERR(priv, "Error sending %s: enqueue_hcmd failed: %d\n", + iwl_legacy_get_cmd_string(cmd->id), ret); + goto out; + } + + ret = wait_event_interruptible_timeout(priv->wait_command_queue, + !test_bit(STATUS_HCMD_ACTIVE, &priv->status), + HOST_COMPLETE_TIMEOUT); + if (!ret) { + if (test_bit(STATUS_HCMD_ACTIVE, &priv->status)) { + IWL_ERR(priv, + "Error sending %s: time out after %dms.\n", + iwl_legacy_get_cmd_string(cmd->id), + jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); + + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, + "Clearing HCMD_ACTIVE for command %s\n", + iwl_legacy_get_cmd_string(cmd->id)); + ret = -ETIMEDOUT; + goto cancel; + } + } + + if (test_bit(STATUS_RF_KILL_HW, &priv->status)) { + IWL_ERR(priv, "Command %s aborted: RF KILL Switch\n", + iwl_legacy_get_cmd_string(cmd->id)); + ret = -ECANCELED; + goto fail; + } + if (test_bit(STATUS_FW_ERROR, &priv->status)) { + IWL_ERR(priv, "Command %s failed: FW Error\n", + iwl_legacy_get_cmd_string(cmd->id)); + ret = -EIO; + goto fail; + } + if ((cmd->flags & CMD_WANT_SKB) && !cmd->reply_page) { + IWL_ERR(priv, "Error: Response NULL in '%s'\n", + iwl_legacy_get_cmd_string(cmd->id)); + ret = -EIO; + goto cancel; + } + + ret = 0; + goto out; + +cancel: + if (cmd->flags & CMD_WANT_SKB) { + /* + * Cancel the CMD_WANT_SKB flag for the cmd in the + * TX cmd queue. Otherwise in case the cmd comes + * in later, it will possibly set an invalid + * address (cmd->meta.source). + */ + priv->txq[priv->cmd_queue].meta[cmd_idx].flags &= + ~CMD_WANT_SKB; + } +fail: + if (cmd->reply_page) { + iwl_legacy_free_pages(priv, cmd->reply_page); + cmd->reply_page = 0; + } +out: + mutex_unlock(&priv->sync_cmd_mutex); + return ret; +} +EXPORT_SYMBOL(iwl_legacy_send_cmd_sync); + +int iwl_legacy_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + if (cmd->flags & CMD_ASYNC) + return iwl_legacy_send_cmd_async(priv, cmd); + + return iwl_legacy_send_cmd_sync(priv, cmd); +} +EXPORT_SYMBOL(iwl_legacy_send_cmd); + +int +iwl_legacy_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + return iwl_legacy_send_cmd_sync(priv, &cmd); +} +EXPORT_SYMBOL(iwl_legacy_send_cmd_pdu); + +int iwl_legacy_send_cmd_pdu_async(struct iwl_priv *priv, + u8 id, u16 len, const void *data, + void (*callback)(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt)) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = len, + .data = data, + }; + + cmd.flags |= CMD_ASYNC; + cmd.callback = callback; + + return iwl_legacy_send_cmd_async(priv, &cmd); +} +EXPORT_SYMBOL(iwl_legacy_send_cmd_pdu_async); diff --git a/drivers/net/wireless/iwlegacy/iwl-helpers.h b/drivers/net/wireless/iwlegacy/iwl-helpers.h new file mode 100644 index 00000000000..02132e75583 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-helpers.h @@ -0,0 +1,181 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_helpers_h__ +#define __iwl_legacy_helpers_h__ + +#include <linux/ctype.h> +#include <net/mac80211.h> + +#include "iwl-io.h" + +#define IWL_MASK(lo, hi) ((1 << (hi)) | ((1 << (hi)) - (1 << (lo)))) + + +static inline struct ieee80211_conf *iwl_legacy_ieee80211_get_hw_conf( + struct ieee80211_hw *hw) +{ + return &hw->conf; +} + +/** + * iwl_legacy_queue_inc_wrap - increment queue index, wrap back to beginning + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl_legacy_queue_inc_wrap(int index, int n_bd) +{ + return ++index & (n_bd - 1); +} + +/** + * iwl_legacy_queue_dec_wrap - decrement queue index, wrap back to end + * @index -- current index + * @n_bd -- total number of entries in queue (must be power of 2) + */ +static inline int iwl_legacy_queue_dec_wrap(int index, int n_bd) +{ + return --index & (n_bd - 1); +} + +/* TODO: Move fw_desc functions to iwl-pci.ko */ +static inline void iwl_legacy_free_fw_desc(struct pci_dev *pci_dev, + struct fw_desc *desc) +{ + if (desc->v_addr) + dma_free_coherent(&pci_dev->dev, desc->len, + desc->v_addr, desc->p_addr); + desc->v_addr = NULL; + desc->len = 0; +} + +static inline int iwl_legacy_alloc_fw_desc(struct pci_dev *pci_dev, + struct fw_desc *desc) +{ + if (!desc->len) { + desc->v_addr = NULL; + return -EINVAL; + } + + desc->v_addr = dma_alloc_coherent(&pci_dev->dev, desc->len, + &desc->p_addr, GFP_KERNEL); + return (desc->v_addr != NULL) ? 0 : -ENOMEM; +} + +/* + * we have 8 bits used like this: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | +-+-------- AC queue (0-3) + * | | | | | | + * | +-+-+-+-+------------ HW queue ID + * | + * +---------------------- unused + */ +static inline void +iwl_legacy_set_swq_id(struct iwl_tx_queue *txq, u8 ac, u8 hwq) +{ + BUG_ON(ac > 3); /* only have 2 bits */ + BUG_ON(hwq > 31); /* only use 5 bits */ + + txq->swq_id = (hwq << 2) | ac; +} + +static inline void iwl_legacy_wake_queue(struct iwl_priv *priv, + struct iwl_tx_queue *txq) +{ + u8 queue = txq->swq_id; + u8 ac = queue & 3; + u8 hwq = (queue >> 2) & 0x1f; + + if (test_and_clear_bit(hwq, priv->queue_stopped)) + if (atomic_dec_return(&priv->queue_stop_count[ac]) <= 0) + ieee80211_wake_queue(priv->hw, ac); +} + +static inline void iwl_legacy_stop_queue(struct iwl_priv *priv, + struct iwl_tx_queue *txq) +{ + u8 queue = txq->swq_id; + u8 ac = queue & 3; + u8 hwq = (queue >> 2) & 0x1f; + + if (!test_and_set_bit(hwq, priv->queue_stopped)) + if (atomic_inc_return(&priv->queue_stop_count[ac]) > 0) + ieee80211_stop_queue(priv->hw, ac); +} + +#define ieee80211_stop_queue DO_NOT_USE_ieee80211_stop_queue +#define ieee80211_wake_queue DO_NOT_USE_ieee80211_wake_queue + +static inline void iwl_legacy_disable_interrupts(struct iwl_priv *priv) +{ + clear_bit(STATUS_INT_ENABLED, &priv->status); + + /* disable interrupts from uCode/NIC to host */ + iwl_write32(priv, CSR_INT_MASK, 0x00000000); + + /* acknowledge/clear/reset any interrupts still pending + * from uCode or flow handler (Rx/Tx DMA) */ + iwl_write32(priv, CSR_INT, 0xffffffff); + iwl_write32(priv, CSR_FH_INT_STATUS, 0xffffffff); + IWL_DEBUG_ISR(priv, "Disabled interrupts\n"); +} + +static inline void iwl_legacy_enable_interrupts(struct iwl_priv *priv) +{ + IWL_DEBUG_ISR(priv, "Enabling interrupts\n"); + set_bit(STATUS_INT_ENABLED, &priv->status); + iwl_write32(priv, CSR_INT_MASK, priv->inta_mask); +} + +/** + * iwl_legacy_beacon_time_mask_low - mask of lower 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_legacy_beacon_time_mask_low(struct iwl_priv *priv, + u16 tsf_bits) +{ + return (1 << tsf_bits) - 1; +} + +/** + * iwl_legacy_beacon_time_mask_high - mask of higher 32 bit of beacon time + * @priv -- pointer to iwl_priv data structure + * @tsf_bits -- number of bits need to shift for masking) + */ +static inline u32 iwl_legacy_beacon_time_mask_high(struct iwl_priv *priv, + u16 tsf_bits) +{ + return ((1 << (32 - tsf_bits)) - 1) << tsf_bits; +} + +#endif /* __iwl_legacy_helpers_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-io.h b/drivers/net/wireless/iwlegacy/iwl-io.h new file mode 100644 index 00000000000..5cc5d342914 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-io.h @@ -0,0 +1,545 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_io_h__ +#define __iwl_legacy_io_h__ + +#include <linux/io.h> + +#include "iwl-dev.h" +#include "iwl-debug.h" +#include "iwl-devtrace.h" + +/* + * IO, register, and NIC memory access functions + * + * NOTE on naming convention and macro usage for these + * + * A single _ prefix before a an access function means that no state + * check or debug information is printed when that function is called. + * + * A double __ prefix before an access function means that state is checked + * and the current line number and caller function name are printed in addition + * to any other debug output. + * + * The non-prefixed name is the #define that maps the caller into a + * #define that provides the caller's name and __LINE__ to the double + * prefix version. + * + * If you wish to call the function without any debug or state checking, + * you should use the single _ prefix version (as is used by dependent IO + * routines, for example _iwl_legacy_read_direct32 calls the non-check version of + * _iwl_legacy_read32.) + * + * These declarations are *extremely* useful in quickly isolating code deltas + * which result in misconfiguration of the hardware I/O. In combination with + * git-bisect and the IO debug level you can quickly determine the specific + * commit which breaks the IO sequence to the hardware. + * + */ + +static inline void _iwl_legacy_write8(struct iwl_priv *priv, u32 ofs, u8 val) +{ + trace_iwlwifi_legacy_dev_iowrite8(priv, ofs, val); + iowrite8(val, priv->hw_base + ofs); +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline void +__iwl_legacy_write8(const char *f, u32 l, struct iwl_priv *priv, + u32 ofs, u8 val) +{ + IWL_DEBUG_IO(priv, "write8(0x%08X, 0x%02X) - %s %d\n", ofs, val, f, l); + _iwl_legacy_write8(priv, ofs, val); +} +#define iwl_write8(priv, ofs, val) \ + __iwl_legacy_write8(__FILE__, __LINE__, priv, ofs, val) +#else +#define iwl_write8(priv, ofs, val) _iwl_legacy_write8(priv, ofs, val) +#endif + + +static inline void _iwl_legacy_write32(struct iwl_priv *priv, u32 ofs, u32 val) +{ + trace_iwlwifi_legacy_dev_iowrite32(priv, ofs, val); + iowrite32(val, priv->hw_base + ofs); +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline void +__iwl_legacy_write32(const char *f, u32 l, struct iwl_priv *priv, + u32 ofs, u32 val) +{ + IWL_DEBUG_IO(priv, "write32(0x%08X, 0x%08X) - %s %d\n", ofs, val, f, l); + _iwl_legacy_write32(priv, ofs, val); +} +#define iwl_write32(priv, ofs, val) \ + __iwl_legacy_write32(__FILE__, __LINE__, priv, ofs, val) +#else +#define iwl_write32(priv, ofs, val) _iwl_legacy_write32(priv, ofs, val) +#endif + +static inline u32 _iwl_legacy_read32(struct iwl_priv *priv, u32 ofs) +{ + u32 val = ioread32(priv->hw_base + ofs); + trace_iwlwifi_legacy_dev_ioread32(priv, ofs, val); + return val; +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline u32 +__iwl_legacy_read32(char *f, u32 l, struct iwl_priv *priv, u32 ofs) +{ + IWL_DEBUG_IO(priv, "read_direct32(0x%08X) - %s %d\n", ofs, f, l); + return _iwl_legacy_read32(priv, ofs); +} +#define iwl_read32(priv, ofs) __iwl_legacy_read32(__FILE__, __LINE__, priv, ofs) +#else +#define iwl_read32(p, o) _iwl_legacy_read32(p, o) +#endif + +#define IWL_POLL_INTERVAL 10 /* microseconds */ +static inline int +_iwl_legacy_poll_bit(struct iwl_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int t = 0; + + do { + if ((_iwl_legacy_read32(priv, addr) & mask) == (bits & mask)) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline int __iwl_legacy_poll_bit(const char *f, u32 l, + struct iwl_priv *priv, u32 addr, + u32 bits, u32 mask, int timeout) +{ + int ret = _iwl_legacy_poll_bit(priv, addr, bits, mask, timeout); + IWL_DEBUG_IO(priv, "poll_bit(0x%08X, 0x%08X, 0x%08X) - %s- %s %d\n", + addr, bits, mask, + unlikely(ret == -ETIMEDOUT) ? "timeout" : "", f, l); + return ret; +} +#define iwl_poll_bit(priv, addr, bits, mask, timeout) \ + __iwl_legacy_poll_bit(__FILE__, __LINE__, priv, addr, \ + bits, mask, timeout) +#else +#define iwl_poll_bit(p, a, b, m, t) _iwl_legacy_poll_bit(p, a, b, m, t) +#endif + +static inline void _iwl_legacy_set_bit(struct iwl_priv *priv, u32 reg, u32 mask) +{ + _iwl_legacy_write32(priv, reg, _iwl_legacy_read32(priv, reg) | mask); +} +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline void __iwl_legacy_set_bit(const char *f, u32 l, + struct iwl_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl_legacy_read32(priv, reg) | mask; + IWL_DEBUG_IO(priv, "set_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, + mask, val); + _iwl_legacy_write32(priv, reg, val); +} +static inline void iwl_legacy_set_bit(struct iwl_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + __iwl_legacy_set_bit(__FILE__, __LINE__, p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +#else +static inline void iwl_legacy_set_bit(struct iwl_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + _iwl_legacy_set_bit(p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +#endif + +static inline void +_iwl_legacy_clear_bit(struct iwl_priv *priv, u32 reg, u32 mask) +{ + _iwl_legacy_write32(priv, reg, _iwl_legacy_read32(priv, reg) & ~mask); +} +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline void +__iwl_legacy_clear_bit(const char *f, u32 l, + struct iwl_priv *priv, u32 reg, u32 mask) +{ + u32 val = _iwl_legacy_read32(priv, reg) & ~mask; + IWL_DEBUG_IO(priv, "clear_bit(0x%08X, 0x%08X) = 0x%08X\n", reg, mask, val); + _iwl_legacy_write32(priv, reg, val); +} +static inline void iwl_legacy_clear_bit(struct iwl_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + __iwl_legacy_clear_bit(__FILE__, __LINE__, p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +#else +static inline void iwl_legacy_clear_bit(struct iwl_priv *p, u32 r, u32 m) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&p->reg_lock, reg_flags); + _iwl_legacy_clear_bit(p, r, m); + spin_unlock_irqrestore(&p->reg_lock, reg_flags); +} +#endif + +static inline int _iwl_legacy_grab_nic_access(struct iwl_priv *priv) +{ + int ret; + u32 val; + + /* this bit wakes up the NIC */ + _iwl_legacy_set_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* + * These bits say the device is running, and should keep running for + * at least a short while (at least as long as MAC_ACCESS_REQ stays 1), + * but they do not indicate that embedded SRAM is restored yet; + * 3945 and 4965 have volatile SRAM, and must save/restore contents + * to/from host DRAM when sleeping/waking for power-saving. + * Each direction takes approximately 1/4 millisecond; with this + * overhead, it's a good idea to grab and hold MAC_ACCESS_REQUEST if a + * series of register accesses are expected (e.g. reading Event Log), + * to keep device from sleeping. + * + * CSR_UCODE_DRV_GP1 register bit MAC_SLEEP == 0 indicates that + * SRAM is okay/restored. We don't check that here because this call + * is just for hardware register access; but GP1 MAC_SLEEP check is a + * good idea before accessing 3945/4965 SRAM (e.g. reading Event Log). + * + */ + ret = _iwl_legacy_poll_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, + (CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | + CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000); + if (ret < 0) { + val = _iwl_legacy_read32(priv, CSR_GP_CNTRL); + IWL_ERR(priv, + "MAC is in deep sleep!. CSR_GP_CNTRL = 0x%08X\n", val); + _iwl_legacy_write32(priv, CSR_RESET, + CSR_RESET_REG_FLAG_FORCE_NMI); + return -EIO; + } + + return 0; +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline int __iwl_legacy_grab_nic_access(const char *f, u32 l, + struct iwl_priv *priv) +{ + IWL_DEBUG_IO(priv, "grabbing nic access - %s %d\n", f, l); + return _iwl_legacy_grab_nic_access(priv); +} +#define iwl_grab_nic_access(priv) \ + __iwl_legacy_grab_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl_grab_nic_access(priv) \ + _iwl_legacy_grab_nic_access(priv) +#endif + +static inline void _iwl_legacy_release_nic_access(struct iwl_priv *priv) +{ + _iwl_legacy_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); +} +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline void __iwl_legacy_release_nic_access(const char *f, u32 l, + struct iwl_priv *priv) +{ + + IWL_DEBUG_IO(priv, "releasing nic access - %s %d\n", f, l); + _iwl_legacy_release_nic_access(priv); +} +#define iwl_release_nic_access(priv) \ + __iwl_legacy_release_nic_access(__FILE__, __LINE__, priv) +#else +#define iwl_release_nic_access(priv) \ + _iwl_legacy_release_nic_access(priv) +#endif + +static inline u32 _iwl_legacy_read_direct32(struct iwl_priv *priv, u32 reg) +{ + return _iwl_legacy_read32(priv, reg); +} +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline u32 __iwl_legacy_read_direct32(const char *f, u32 l, + struct iwl_priv *priv, u32 reg) +{ + u32 value = _iwl_legacy_read_direct32(priv, reg); + IWL_DEBUG_IO(priv, + "read_direct32(0x%4X) = 0x%08x - %s %d\n", reg, value, + f, l); + return value; +} +static inline u32 iwl_legacy_read_direct32(struct iwl_priv *priv, u32 reg) +{ + u32 value; + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + value = __iwl_legacy_read_direct32(__FILE__, __LINE__, priv, reg); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return value; +} + +#else +static inline u32 iwl_legacy_read_direct32(struct iwl_priv *priv, u32 reg) +{ + u32 value; + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + value = _iwl_legacy_read_direct32(priv, reg); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return value; + +} +#endif + +static inline void _iwl_legacy_write_direct32(struct iwl_priv *priv, + u32 reg, u32 value) +{ + _iwl_legacy_write32(priv, reg, value); +} +static inline void +iwl_legacy_write_direct32(struct iwl_priv *priv, u32 reg, u32 value) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (!iwl_grab_nic_access(priv)) { + _iwl_legacy_write_direct32(priv, reg, value); + iwl_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static inline void iwl_legacy_write_reg_buf(struct iwl_priv *priv, + u32 reg, u32 len, u32 *values) +{ + u32 count = sizeof(u32); + + if ((priv != NULL) && (values != NULL)) { + for (; 0 < len; len -= count, reg += count, values++) + iwl_legacy_write_direct32(priv, reg, *values); + } +} + +static inline int _iwl_legacy_poll_direct_bit(struct iwl_priv *priv, u32 addr, + u32 mask, int timeout) +{ + int t = 0; + + do { + if ((iwl_legacy_read_direct32(priv, addr) & mask) == mask) + return t; + udelay(IWL_POLL_INTERVAL); + t += IWL_POLL_INTERVAL; + } while (t < timeout); + + return -ETIMEDOUT; +} + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static inline int __iwl_legacy_poll_direct_bit(const char *f, u32 l, + struct iwl_priv *priv, + u32 addr, u32 mask, int timeout) +{ + int ret = _iwl_legacy_poll_direct_bit(priv, addr, mask, timeout); + + if (unlikely(ret == -ETIMEDOUT)) + IWL_DEBUG_IO(priv, "poll_direct_bit(0x%08X, 0x%08X) - " + "timedout - %s %d\n", addr, mask, f, l); + else + IWL_DEBUG_IO(priv, "poll_direct_bit(0x%08X, 0x%08X) = 0x%08X " + "- %s %d\n", addr, mask, ret, f, l); + return ret; +} +#define iwl_poll_direct_bit(priv, addr, mask, timeout) \ +__iwl_legacy_poll_direct_bit(__FILE__, __LINE__, priv, addr, mask, timeout) +#else +#define iwl_poll_direct_bit _iwl_legacy_poll_direct_bit +#endif + +static inline u32 _iwl_legacy_read_prph(struct iwl_priv *priv, u32 reg) +{ + _iwl_legacy_write_direct32(priv, HBUS_TARG_PRPH_RADDR, reg | (3 << 24)); + rmb(); + return _iwl_legacy_read_direct32(priv, HBUS_TARG_PRPH_RDAT); +} +static inline u32 iwl_legacy_read_prph(struct iwl_priv *priv, u32 reg) +{ + unsigned long reg_flags; + u32 val; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + val = _iwl_legacy_read_prph(priv, reg); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return val; +} + +static inline void _iwl_legacy_write_prph(struct iwl_priv *priv, + u32 addr, u32 val) +{ + _iwl_legacy_write_direct32(priv, HBUS_TARG_PRPH_WADDR, + ((addr & 0x0000FFFF) | (3 << 24))); + wmb(); + _iwl_legacy_write_direct32(priv, HBUS_TARG_PRPH_WDAT, val); +} + +static inline void +iwl_legacy_write_prph(struct iwl_priv *priv, u32 addr, u32 val) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (!iwl_grab_nic_access(priv)) { + _iwl_legacy_write_prph(priv, addr, val); + iwl_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +#define _iwl_legacy_set_bits_prph(priv, reg, mask) \ +_iwl_legacy_write_prph(priv, reg, (_iwl_legacy_read_prph(priv, reg) | mask)) + +static inline void +iwl_legacy_set_bits_prph(struct iwl_priv *priv, u32 reg, u32 mask) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + _iwl_legacy_set_bits_prph(priv, reg, mask); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +#define _iwl_legacy_set_bits_mask_prph(priv, reg, bits, mask) \ +_iwl_legacy_write_prph(priv, reg, \ + ((_iwl_legacy_read_prph(priv, reg) & mask) | bits)) + +static inline void iwl_legacy_set_bits_mask_prph(struct iwl_priv *priv, u32 reg, + u32 bits, u32 mask) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + _iwl_legacy_set_bits_mask_prph(priv, reg, bits, mask); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static inline void iwl_legacy_clear_bits_prph(struct iwl_priv + *priv, u32 reg, u32 mask) +{ + unsigned long reg_flags; + u32 val; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + val = _iwl_legacy_read_prph(priv, reg); + _iwl_legacy_write_prph(priv, reg, (val & ~mask)); + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static inline u32 iwl_legacy_read_targ_mem(struct iwl_priv *priv, u32 addr) +{ + unsigned long reg_flags; + u32 value; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, addr); + rmb(); + value = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return value; +} + +static inline void +iwl_legacy_write_targ_mem(struct iwl_priv *priv, u32 addr, u32 val) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (!iwl_grab_nic_access(priv)) { + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + wmb(); + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_WDAT, val); + iwl_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static inline void +iwl_legacy_write_targ_mem_buf(struct iwl_priv *priv, u32 addr, + u32 len, u32 *values) +{ + unsigned long reg_flags; + + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (!iwl_grab_nic_access(priv)) { + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_WADDR, addr); + wmb(); + for (; 0 < len; len -= sizeof(u32), values++) + _iwl_legacy_write_direct32(priv, + HBUS_TARG_MEM_WDAT, *values); + + iwl_release_nic_access(priv); + } + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} +#endif diff --git a/drivers/net/wireless/iwlegacy/iwl-led.c b/drivers/net/wireless/iwlegacy/iwl-led.c new file mode 100644 index 00000000000..15eb8b70715 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-led.c @@ -0,0 +1,188 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include <asm/unaligned.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" + +/* default: IWL_LED_BLINK(0) using blinking index table */ +static int led_mode; +module_param(led_mode, int, S_IRUGO); +MODULE_PARM_DESC(led_mode, "0=system default, " + "1=On(RF On)/Off(RF Off), 2=blinking"); + +static const struct ieee80211_tpt_blink iwl_blink[] = { + { .throughput = 0 * 1024 - 1, .blink_time = 334 }, + { .throughput = 1 * 1024 - 1, .blink_time = 260 }, + { .throughput = 5 * 1024 - 1, .blink_time = 220 }, + { .throughput = 10 * 1024 - 1, .blink_time = 190 }, + { .throughput = 20 * 1024 - 1, .blink_time = 170 }, + { .throughput = 50 * 1024 - 1, .blink_time = 150 }, + { .throughput = 70 * 1024 - 1, .blink_time = 130 }, + { .throughput = 100 * 1024 - 1, .blink_time = 110 }, + { .throughput = 200 * 1024 - 1, .blink_time = 80 }, + { .throughput = 300 * 1024 - 1, .blink_time = 50 }, +}; + +/* + * Adjust led blink rate to compensate on a MAC Clock difference on every HW + * Led blink rate analysis showed an average deviation of 0% on 3945, + * 5% on 4965 HW. + * Need to compensate on the led on/off time per HW according to the deviation + * to achieve the desired led frequency + * The calculation is: (100-averageDeviation)/100 * blinkTime + * For code efficiency the calculation will be: + * compensation = (100 - averageDeviation) * 64 / 100 + * NewBlinkTime = (compensation * BlinkTime) / 64 + */ +static inline u8 iwl_legacy_blink_compensation(struct iwl_priv *priv, + u8 time, u16 compensation) +{ + if (!compensation) { + IWL_ERR(priv, "undefined blink compensation: " + "use pre-defined blinking time\n"); + return time; + } + + return (u8)((time * compensation) >> 6); +} + +/* Set led pattern command */ +static int iwl_legacy_led_cmd(struct iwl_priv *priv, + unsigned long on, + unsigned long off) +{ + struct iwl_led_cmd led_cmd = { + .id = IWL_LED_LINK, + .interval = IWL_DEF_LED_INTRVL + }; + int ret; + + if (!test_bit(STATUS_READY, &priv->status)) + return -EBUSY; + + if (priv->blink_on == on && priv->blink_off == off) + return 0; + + IWL_DEBUG_LED(priv, "Led blink time compensation=%u\n", + priv->cfg->base_params->led_compensation); + led_cmd.on = iwl_legacy_blink_compensation(priv, on, + priv->cfg->base_params->led_compensation); + led_cmd.off = iwl_legacy_blink_compensation(priv, off, + priv->cfg->base_params->led_compensation); + + ret = priv->cfg->ops->led->cmd(priv, &led_cmd); + if (!ret) { + priv->blink_on = on; + priv->blink_off = off; + } + return ret; +} + +static void iwl_legacy_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + unsigned long on = 0; + + if (brightness > 0) + on = IWL_LED_SOLID; + + iwl_legacy_led_cmd(priv, on, 0); +} + +static int iwl_legacy_led_blink_set(struct led_classdev *led_cdev, + unsigned long *delay_on, + unsigned long *delay_off) +{ + struct iwl_priv *priv = container_of(led_cdev, struct iwl_priv, led); + + return iwl_legacy_led_cmd(priv, *delay_on, *delay_off); +} + +void iwl_legacy_leds_init(struct iwl_priv *priv) +{ + int mode = led_mode; + int ret; + + if (mode == IWL_LED_DEFAULT) + mode = priv->cfg->led_mode; + + priv->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(priv->hw->wiphy)); + priv->led.brightness_set = iwl_legacy_led_brightness_set; + priv->led.blink_set = iwl_legacy_led_blink_set; + priv->led.max_brightness = 1; + + switch (mode) { + case IWL_LED_DEFAULT: + WARN_ON(1); + break; + case IWL_LED_BLINK: + priv->led.default_trigger = + ieee80211_create_tpt_led_trigger(priv->hw, + IEEE80211_TPT_LEDTRIG_FL_CONNECTED, + iwl_blink, ARRAY_SIZE(iwl_blink)); + break; + case IWL_LED_RF_STATE: + priv->led.default_trigger = + ieee80211_get_radio_led_name(priv->hw); + break; + } + + ret = led_classdev_register(&priv->pci_dev->dev, &priv->led); + if (ret) { + kfree(priv->led.name); + return; + } + + priv->led_registered = true; +} +EXPORT_SYMBOL(iwl_legacy_leds_init); + +void iwl_legacy_leds_exit(struct iwl_priv *priv) +{ + if (!priv->led_registered) + return; + + led_classdev_unregister(&priv->led); + kfree(priv->led.name); +} +EXPORT_SYMBOL(iwl_legacy_leds_exit); diff --git a/drivers/net/wireless/iwlegacy/iwl-led.h b/drivers/net/wireless/iwlegacy/iwl-led.h new file mode 100644 index 00000000000..f0791f70f79 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-led.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_leds_h__ +#define __iwl_legacy_leds_h__ + + +struct iwl_priv; + +#define IWL_LED_SOLID 11 +#define IWL_DEF_LED_INTRVL cpu_to_le32(1000) + +#define IWL_LED_ACTIVITY (0<<1) +#define IWL_LED_LINK (1<<1) + +/* + * LED mode + * IWL_LED_DEFAULT: use device default + * IWL_LED_RF_STATE: turn LED on/off based on RF state + * LED ON = RF ON + * LED OFF = RF OFF + * IWL_LED_BLINK: adjust led blink rate based on blink table + */ +enum iwl_led_mode { + IWL_LED_DEFAULT, + IWL_LED_RF_STATE, + IWL_LED_BLINK, +}; + +void iwl_legacy_leds_init(struct iwl_priv *priv); +void iwl_legacy_leds_exit(struct iwl_priv *priv); + +#endif /* __iwl_legacy_leds_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-legacy-rs.h b/drivers/net/wireless/iwlegacy/iwl-legacy-rs.h new file mode 100644 index 00000000000..38647e481eb --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-legacy-rs.h @@ -0,0 +1,456 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_rs_h__ +#define __iwl_legacy_rs_h__ + +struct iwl_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 plcp_siso; /* uCode API: IWL_RATE_SISO_6M_PLCP, etc. */ + u8 plcp_mimo2; /* uCode API: IWL_RATE_MIMO2_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ +}; + +struct iwl3945_rate_info { + u8 plcp; /* uCode API: IWL_RATE_6M_PLCP, etc. */ + u8 ieee; /* MAC header: IWL_RATE_6M_IEEE, etc. */ + u8 prev_ieee; /* previous rate in IEEE speeds */ + u8 next_ieee; /* next rate in IEEE speeds */ + u8 prev_rs; /* previous rate used in rs algo */ + u8 next_rs; /* next rate used in rs algo */ + u8 prev_rs_tgg; /* previous rate used in TGG rs algo */ + u8 next_rs_tgg; /* next rate used in TGG rs algo */ + u8 table_rs_index; /* index in rate scale table cmd */ + u8 prev_table_rs; /* prev in rate table cmd */ +}; + + +/* + * These serve as indexes into + * struct iwl_rate_info iwlegacy_rates[IWL_RATE_COUNT]; + */ +enum { + IWL_RATE_1M_INDEX = 0, + IWL_RATE_2M_INDEX, + IWL_RATE_5M_INDEX, + IWL_RATE_11M_INDEX, + IWL_RATE_6M_INDEX, + IWL_RATE_9M_INDEX, + IWL_RATE_12M_INDEX, + IWL_RATE_18M_INDEX, + IWL_RATE_24M_INDEX, + IWL_RATE_36M_INDEX, + IWL_RATE_48M_INDEX, + IWL_RATE_54M_INDEX, + IWL_RATE_60M_INDEX, + IWL_RATE_COUNT, + IWL_RATE_COUNT_LEGACY = IWL_RATE_COUNT - 1, /* Excluding 60M */ + IWL_RATE_COUNT_3945 = IWL_RATE_COUNT - 1, + IWL_RATE_INVM_INDEX = IWL_RATE_COUNT, + IWL_RATE_INVALID = IWL_RATE_COUNT, +}; + +enum { + IWL_RATE_6M_INDEX_TABLE = 0, + IWL_RATE_9M_INDEX_TABLE, + IWL_RATE_12M_INDEX_TABLE, + IWL_RATE_18M_INDEX_TABLE, + IWL_RATE_24M_INDEX_TABLE, + IWL_RATE_36M_INDEX_TABLE, + IWL_RATE_48M_INDEX_TABLE, + IWL_RATE_54M_INDEX_TABLE, + IWL_RATE_1M_INDEX_TABLE, + IWL_RATE_2M_INDEX_TABLE, + IWL_RATE_5M_INDEX_TABLE, + IWL_RATE_11M_INDEX_TABLE, + IWL_RATE_INVM_INDEX_TABLE = IWL_RATE_INVM_INDEX - 1, +}; + +enum { + IWL_FIRST_OFDM_RATE = IWL_RATE_6M_INDEX, + IWL39_LAST_OFDM_RATE = IWL_RATE_54M_INDEX, + IWL_LAST_OFDM_RATE = IWL_RATE_60M_INDEX, + IWL_FIRST_CCK_RATE = IWL_RATE_1M_INDEX, + IWL_LAST_CCK_RATE = IWL_RATE_11M_INDEX, +}; + +/* #define vs. enum to keep from defaulting to 'large integer' */ +#define IWL_RATE_6M_MASK (1 << IWL_RATE_6M_INDEX) +#define IWL_RATE_9M_MASK (1 << IWL_RATE_9M_INDEX) +#define IWL_RATE_12M_MASK (1 << IWL_RATE_12M_INDEX) +#define IWL_RATE_18M_MASK (1 << IWL_RATE_18M_INDEX) +#define IWL_RATE_24M_MASK (1 << IWL_RATE_24M_INDEX) +#define IWL_RATE_36M_MASK (1 << IWL_RATE_36M_INDEX) +#define IWL_RATE_48M_MASK (1 << IWL_RATE_48M_INDEX) +#define IWL_RATE_54M_MASK (1 << IWL_RATE_54M_INDEX) +#define IWL_RATE_60M_MASK (1 << IWL_RATE_60M_INDEX) +#define IWL_RATE_1M_MASK (1 << IWL_RATE_1M_INDEX) +#define IWL_RATE_2M_MASK (1 << IWL_RATE_2M_INDEX) +#define IWL_RATE_5M_MASK (1 << IWL_RATE_5M_INDEX) +#define IWL_RATE_11M_MASK (1 << IWL_RATE_11M_INDEX) + +/* uCode API values for legacy bit rates, both OFDM and CCK */ +enum { + IWL_RATE_6M_PLCP = 13, + IWL_RATE_9M_PLCP = 15, + IWL_RATE_12M_PLCP = 5, + IWL_RATE_18M_PLCP = 7, + IWL_RATE_24M_PLCP = 9, + IWL_RATE_36M_PLCP = 11, + IWL_RATE_48M_PLCP = 1, + IWL_RATE_54M_PLCP = 3, + IWL_RATE_60M_PLCP = 3,/*FIXME:RS:should be removed*/ + IWL_RATE_1M_PLCP = 10, + IWL_RATE_2M_PLCP = 20, + IWL_RATE_5M_PLCP = 55, + IWL_RATE_11M_PLCP = 110, + /*FIXME:RS:add IWL_RATE_LEGACY_INVM_PLCP = 0,*/ +}; + +/* uCode API values for OFDM high-throughput (HT) bit rates */ +enum { + IWL_RATE_SISO_6M_PLCP = 0, + IWL_RATE_SISO_12M_PLCP = 1, + IWL_RATE_SISO_18M_PLCP = 2, + IWL_RATE_SISO_24M_PLCP = 3, + IWL_RATE_SISO_36M_PLCP = 4, + IWL_RATE_SISO_48M_PLCP = 5, + IWL_RATE_SISO_54M_PLCP = 6, + IWL_RATE_SISO_60M_PLCP = 7, + IWL_RATE_MIMO2_6M_PLCP = 0x8, + IWL_RATE_MIMO2_12M_PLCP = 0x9, + IWL_RATE_MIMO2_18M_PLCP = 0xa, + IWL_RATE_MIMO2_24M_PLCP = 0xb, + IWL_RATE_MIMO2_36M_PLCP = 0xc, + IWL_RATE_MIMO2_48M_PLCP = 0xd, + IWL_RATE_MIMO2_54M_PLCP = 0xe, + IWL_RATE_MIMO2_60M_PLCP = 0xf, + IWL_RATE_SISO_INVM_PLCP, + IWL_RATE_MIMO2_INVM_PLCP = IWL_RATE_SISO_INVM_PLCP, +}; + +/* MAC header values for bit rates */ +enum { + IWL_RATE_6M_IEEE = 12, + IWL_RATE_9M_IEEE = 18, + IWL_RATE_12M_IEEE = 24, + IWL_RATE_18M_IEEE = 36, + IWL_RATE_24M_IEEE = 48, + IWL_RATE_36M_IEEE = 72, + IWL_RATE_48M_IEEE = 96, + IWL_RATE_54M_IEEE = 108, + IWL_RATE_60M_IEEE = 120, + IWL_RATE_1M_IEEE = 2, + IWL_RATE_2M_IEEE = 4, + IWL_RATE_5M_IEEE = 11, + IWL_RATE_11M_IEEE = 22, +}; + +#define IWL_CCK_BASIC_RATES_MASK \ + (IWL_RATE_1M_MASK | \ + IWL_RATE_2M_MASK) + +#define IWL_CCK_RATES_MASK \ + (IWL_CCK_BASIC_RATES_MASK | \ + IWL_RATE_5M_MASK | \ + IWL_RATE_11M_MASK) + +#define IWL_OFDM_BASIC_RATES_MASK \ + (IWL_RATE_6M_MASK | \ + IWL_RATE_12M_MASK | \ + IWL_RATE_24M_MASK) + +#define IWL_OFDM_RATES_MASK \ + (IWL_OFDM_BASIC_RATES_MASK | \ + IWL_RATE_9M_MASK | \ + IWL_RATE_18M_MASK | \ + IWL_RATE_36M_MASK | \ + IWL_RATE_48M_MASK | \ + IWL_RATE_54M_MASK) + +#define IWL_BASIC_RATES_MASK \ + (IWL_OFDM_BASIC_RATES_MASK | \ + IWL_CCK_BASIC_RATES_MASK) + +#define IWL_RATES_MASK ((1 << IWL_RATE_COUNT) - 1) +#define IWL_RATES_MASK_3945 ((1 << IWL_RATE_COUNT_3945) - 1) + +#define IWL_INVALID_VALUE -1 + +#define IWL_MIN_RSSI_VAL -100 +#define IWL_MAX_RSSI_VAL 0 + +/* These values specify how many Tx frame attempts before + * searching for a new modulation mode */ +#define IWL_LEGACY_FAILURE_LIMIT 160 +#define IWL_LEGACY_SUCCESS_LIMIT 480 +#define IWL_LEGACY_TABLE_COUNT 160 + +#define IWL_NONE_LEGACY_FAILURE_LIMIT 400 +#define IWL_NONE_LEGACY_SUCCESS_LIMIT 4500 +#define IWL_NONE_LEGACY_TABLE_COUNT 1500 + +/* Success ratio (ACKed / attempted tx frames) values (perfect is 128 * 100) */ +#define IWL_RS_GOOD_RATIO 12800 /* 100% */ +#define IWL_RATE_SCALE_SWITCH 10880 /* 85% */ +#define IWL_RATE_HIGH_TH 10880 /* 85% */ +#define IWL_RATE_INCREASE_TH 6400 /* 50% */ +#define IWL_RATE_DECREASE_TH 1920 /* 15% */ + +/* possible actions when in legacy mode */ +#define IWL_LEGACY_SWITCH_ANTENNA1 0 +#define IWL_LEGACY_SWITCH_ANTENNA2 1 +#define IWL_LEGACY_SWITCH_SISO 2 +#define IWL_LEGACY_SWITCH_MIMO2_AB 3 +#define IWL_LEGACY_SWITCH_MIMO2_AC 4 +#define IWL_LEGACY_SWITCH_MIMO2_BC 5 + +/* possible actions when in siso mode */ +#define IWL_SISO_SWITCH_ANTENNA1 0 +#define IWL_SISO_SWITCH_ANTENNA2 1 +#define IWL_SISO_SWITCH_MIMO2_AB 2 +#define IWL_SISO_SWITCH_MIMO2_AC 3 +#define IWL_SISO_SWITCH_MIMO2_BC 4 +#define IWL_SISO_SWITCH_GI 5 + +/* possible actions when in mimo mode */ +#define IWL_MIMO2_SWITCH_ANTENNA1 0 +#define IWL_MIMO2_SWITCH_ANTENNA2 1 +#define IWL_MIMO2_SWITCH_SISO_A 2 +#define IWL_MIMO2_SWITCH_SISO_B 3 +#define IWL_MIMO2_SWITCH_SISO_C 4 +#define IWL_MIMO2_SWITCH_GI 5 + +#define IWL_MAX_SEARCH IWL_MIMO2_SWITCH_GI + +#define IWL_ACTION_LIMIT 3 /* # possible actions */ + +#define LQ_SIZE 2 /* 2 mode tables: "Active" and "Search" */ + +/* load per tid defines for A-MPDU activation */ +#define IWL_AGG_TPT_THREHOLD 0 +#define IWL_AGG_LOAD_THRESHOLD 10 +#define IWL_AGG_ALL_TID 0xff +#define TID_QUEUE_CELL_SPACING 50 /*mS */ +#define TID_QUEUE_MAX_SIZE 20 +#define TID_ROUND_VALUE 5 /* mS */ +#define TID_MAX_LOAD_COUNT 8 + +#define TID_MAX_TIME_DIFF ((TID_QUEUE_MAX_SIZE - 1) * TID_QUEUE_CELL_SPACING) +#define TIME_WRAP_AROUND(x, y) (((y) > (x)) ? (y) - (x) : (0-(x)) + (y)) + +extern const struct iwl_rate_info iwlegacy_rates[IWL_RATE_COUNT]; + +enum iwl_table_type { + LQ_NONE, + LQ_G, /* legacy types */ + LQ_A, + LQ_SISO, /* high-throughput types */ + LQ_MIMO2, + LQ_MAX, +}; + +#define is_legacy(tbl) (((tbl) == LQ_G) || ((tbl) == LQ_A)) +#define is_siso(tbl) ((tbl) == LQ_SISO) +#define is_mimo2(tbl) ((tbl) == LQ_MIMO2) +#define is_mimo(tbl) (is_mimo2(tbl)) +#define is_Ht(tbl) (is_siso(tbl) || is_mimo(tbl)) +#define is_a_band(tbl) ((tbl) == LQ_A) +#define is_g_and(tbl) ((tbl) == LQ_G) + +#define ANT_NONE 0x0 +#define ANT_A BIT(0) +#define ANT_B BIT(1) +#define ANT_AB (ANT_A | ANT_B) +#define ANT_C BIT(2) +#define ANT_AC (ANT_A | ANT_C) +#define ANT_BC (ANT_B | ANT_C) +#define ANT_ABC (ANT_AB | ANT_C) + +#define IWL_MAX_MCS_DISPLAY_SIZE 12 + +struct iwl_rate_mcs_info { + char mbps[IWL_MAX_MCS_DISPLAY_SIZE]; + char mcs[IWL_MAX_MCS_DISPLAY_SIZE]; +}; + +/** + * struct iwl_rate_scale_data -- tx success history for one rate + */ +struct iwl_rate_scale_data { + u64 data; /* bitmap of successful frames */ + s32 success_counter; /* number of frames successful */ + s32 success_ratio; /* per-cent * 128 */ + s32 counter; /* number of frames attempted */ + s32 average_tpt; /* success ratio * expected throughput */ + unsigned long stamp; +}; + +/** + * struct iwl_scale_tbl_info -- tx params and success history for all rates + * + * There are two of these in struct iwl_lq_sta, + * one for "active", and one for "search". + */ +struct iwl_scale_tbl_info { + enum iwl_table_type lq_type; + u8 ant_type; + u8 is_SGI; /* 1 = short guard interval */ + u8 is_ht40; /* 1 = 40 MHz channel width */ + u8 is_dup; /* 1 = duplicated data streams */ + u8 action; /* change modulation; IWL_[LEGACY/SISO/MIMO]_SWITCH_* */ + u8 max_search; /* maximun number of tables we can search */ + s32 *expected_tpt; /* throughput metrics; expected_tpt_G, etc. */ + u32 current_rate; /* rate_n_flags, uCode API format */ + struct iwl_rate_scale_data win[IWL_RATE_COUNT]; /* rate histories */ +}; + +struct iwl_traffic_load { + unsigned long time_stamp; /* age of the oldest statistics */ + u32 packet_count[TID_QUEUE_MAX_SIZE]; /* packet count in this time + * slice */ + u32 total; /* total num of packets during the + * last TID_MAX_TIME_DIFF */ + u8 queue_count; /* number of queues that has + * been used since the last cleanup */ + u8 head; /* start of the circular buffer */ +}; + +/** + * struct iwl_lq_sta -- driver's rate scaling private structure + * + * Pointer to this gets passed back and forth between driver and mac80211. + */ +struct iwl_lq_sta { + u8 active_tbl; /* index of active table, range 0-1 */ + u8 enable_counter; /* indicates HT mode */ + u8 stay_in_tbl; /* 1: disallow, 0: allow search for new mode */ + u8 search_better_tbl; /* 1: currently trying alternate mode */ + s32 last_tpt; + + /* The following determine when to search for a new mode */ + u32 table_count_limit; + u32 max_failure_limit; /* # failed frames before new search */ + u32 max_success_limit; /* # successful frames before new search */ + u32 table_count; + u32 total_failed; /* total failed frames, any/all rates */ + u32 total_success; /* total successful frames, any/all rates */ + u64 flush_timer; /* time staying in mode before new search */ + + u8 action_counter; /* # mode-switch actions tried */ + u8 is_green; + u8 is_dup; + enum ieee80211_band band; + + /* The following are bitmaps of rates; IWL_RATE_6M_MASK, etc. */ + u32 supp_rates; + u16 active_legacy_rate; + u16 active_siso_rate; + u16 active_mimo2_rate; + s8 max_rate_idx; /* Max rate set by user */ + u8 missed_rate_counter; + + struct iwl_link_quality_cmd lq; + struct iwl_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ + struct iwl_traffic_load load[TID_MAX_LOAD_COUNT]; + u8 tx_agg_tid_en; +#ifdef CONFIG_MAC80211_DEBUGFS + struct dentry *rs_sta_dbgfs_scale_table_file; + struct dentry *rs_sta_dbgfs_stats_table_file; + struct dentry *rs_sta_dbgfs_rate_scale_data_file; + struct dentry *rs_sta_dbgfs_tx_agg_tid_en_file; + u32 dbg_fixed_rate; +#endif + struct iwl_priv *drv; + + /* used to be in sta_info */ + int last_txrate_idx; + /* last tx rate_n_flags */ + u32 last_rate_n_flags; + /* packets destined for this STA are aggregated */ + u8 is_agg; +}; + +static inline u8 iwl4965_num_of_ant(u8 mask) +{ + return !!((mask) & ANT_A) + + !!((mask) & ANT_B) + + !!((mask) & ANT_C); +} + +static inline u8 iwl4965_first_antenna(u8 mask) +{ + if (mask & ANT_A) + return ANT_A; + if (mask & ANT_B) + return ANT_B; + return ANT_C; +} + + +/** + * iwl3945_rate_scale_init - Initialize the rate scale table based on assoc info + * + * The specific throughput table used is based on the type of network + * the associated with, including A, B, G, and G w/ TGG protection + */ +extern void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id); + +/* Initialize station's rate scaling information after adding station */ +extern void iwl4965_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); +extern void iwl3945_rs_rate_init(struct iwl_priv *priv, + struct ieee80211_sta *sta, u8 sta_id); + +/** + * iwl_rate_control_register - Register the rate control algorithm callbacks + * + * Since the rate control algorithm is hardware specific, there is no need + * or reason to place it as a stand alone module. The driver can call + * iwl_rate_control_register in order to register the rate control callbacks + * with the mac80211 subsystem. This should be performed prior to calling + * ieee80211_register_hw + * + */ +extern int iwl4965_rate_control_register(void); +extern int iwl3945_rate_control_register(void); + +/** + * iwl_rate_control_unregister - Unregister the rate control callbacks + * + * This should be called after calling ieee80211_unregister_hw, but before + * the driver is unloaded. + */ +extern void iwl4965_rate_control_unregister(void); +extern void iwl3945_rate_control_unregister(void); + +#endif /* __iwl_legacy_rs__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-power.c b/drivers/net/wireless/iwlegacy/iwl-power.c new file mode 100644 index 00000000000..903ef0d6d6c --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-power.c @@ -0,0 +1,165 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/init.h> + +#include <net/mac80211.h> + +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-commands.h" +#include "iwl-debug.h" +#include "iwl-power.h" + +/* + * Setting power level allows the card to go to sleep when not busy. + * + * We calculate a sleep command based on the required latency, which + * we get from mac80211. In order to handle thermal throttling, we can + * also use pre-defined power levels. + */ + +/* + * This defines the old power levels. They are still used by default + * (level 1) and for thermal throttle (levels 3 through 5) + */ + +struct iwl_power_vec_entry { + struct iwl_powertable_cmd cmd; + u8 no_dtim; /* number of skip dtim */ +}; + +static void iwl_legacy_power_sleep_cam_cmd(struct iwl_priv *priv, + struct iwl_powertable_cmd *cmd) +{ + memset(cmd, 0, sizeof(*cmd)); + + if (priv->power_data.pci_pm) + cmd->flags |= IWL_POWER_PCI_PM_MSK; + + IWL_DEBUG_POWER(priv, "Sleep command for CAM\n"); +} + +static int +iwl_legacy_set_power(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd) +{ + IWL_DEBUG_POWER(priv, "Sending power/sleep command\n"); + IWL_DEBUG_POWER(priv, "Flags value = 0x%08X\n", cmd->flags); + IWL_DEBUG_POWER(priv, "Tx timeout = %u\n", + le32_to_cpu(cmd->tx_data_timeout)); + IWL_DEBUG_POWER(priv, "Rx timeout = %u\n", + le32_to_cpu(cmd->rx_data_timeout)); + IWL_DEBUG_POWER(priv, + "Sleep interval vector = { %d , %d , %d , %d , %d }\n", + le32_to_cpu(cmd->sleep_interval[0]), + le32_to_cpu(cmd->sleep_interval[1]), + le32_to_cpu(cmd->sleep_interval[2]), + le32_to_cpu(cmd->sleep_interval[3]), + le32_to_cpu(cmd->sleep_interval[4])); + + return iwl_legacy_send_cmd_pdu(priv, POWER_TABLE_CMD, + sizeof(struct iwl_powertable_cmd), cmd); +} + +int +iwl_legacy_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force) +{ + int ret; + bool update_chains; + + lockdep_assert_held(&priv->mutex); + + /* Don't update the RX chain when chain noise calibration is running */ + update_chains = priv->chain_noise_data.state == IWL_CHAIN_NOISE_DONE || + priv->chain_noise_data.state == IWL_CHAIN_NOISE_ALIVE; + + if (!memcmp(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)) && !force) + return 0; + + if (!iwl_legacy_is_ready_rf(priv)) + return -EIO; + + /* scan complete use sleep_power_next, need to be updated */ + memcpy(&priv->power_data.sleep_cmd_next, cmd, sizeof(*cmd)); + if (test_bit(STATUS_SCANNING, &priv->status) && !force) { + IWL_DEBUG_INFO(priv, "Defer power set mode while scanning\n"); + return 0; + } + + if (cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK) + set_bit(STATUS_POWER_PMI, &priv->status); + + ret = iwl_legacy_set_power(priv, cmd); + if (!ret) { + if (!(cmd->flags & IWL_POWER_DRIVER_ALLOW_SLEEP_MSK)) + clear_bit(STATUS_POWER_PMI, &priv->status); + + if (priv->cfg->ops->lib->update_chain_flags && update_chains) + priv->cfg->ops->lib->update_chain_flags(priv); + else if (priv->cfg->ops->lib->update_chain_flags) + IWL_DEBUG_POWER(priv, + "Cannot update the power, chain noise " + "calibration running: %d\n", + priv->chain_noise_data.state); + + memcpy(&priv->power_data.sleep_cmd, cmd, sizeof(*cmd)); + } else + IWL_ERR(priv, "set power fail, ret = %d", ret); + + return ret; +} + +int iwl_legacy_power_update_mode(struct iwl_priv *priv, bool force) +{ + struct iwl_powertable_cmd cmd; + + iwl_legacy_power_sleep_cam_cmd(priv, &cmd); + return iwl_legacy_power_set_mode(priv, &cmd, force); +} +EXPORT_SYMBOL(iwl_legacy_power_update_mode); + +/* initialize to default */ +void iwl_legacy_power_initialize(struct iwl_priv *priv) +{ + u16 lctl = iwl_legacy_pcie_link_ctl(priv); + + priv->power_data.pci_pm = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); + + priv->power_data.debug_sleep_level_override = -1; + + memset(&priv->power_data.sleep_cmd, 0, + sizeof(priv->power_data.sleep_cmd)); +} +EXPORT_SYMBOL(iwl_legacy_power_initialize); diff --git a/drivers/net/wireless/iwlegacy/iwl-power.h b/drivers/net/wireless/iwlegacy/iwl-power.h new file mode 100644 index 00000000000..d30b36acdc4 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-power.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * + * Copyright(c) 2007 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#ifndef __iwl_legacy_power_setting_h__ +#define __iwl_legacy_power_setting_h__ + +#include "iwl-commands.h" + +enum iwl_power_level { + IWL_POWER_INDEX_1, + IWL_POWER_INDEX_2, + IWL_POWER_INDEX_3, + IWL_POWER_INDEX_4, + IWL_POWER_INDEX_5, + IWL_POWER_NUM +}; + +struct iwl_power_mgr { + struct iwl_powertable_cmd sleep_cmd; + struct iwl_powertable_cmd sleep_cmd_next; + int debug_sleep_level_override; + bool pci_pm; +}; + +int +iwl_legacy_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, + bool force); +int iwl_legacy_power_update_mode(struct iwl_priv *priv, bool force); +void iwl_legacy_power_initialize(struct iwl_priv *priv); + +#endif /* __iwl_legacy_power_setting_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-prph.h b/drivers/net/wireless/iwlegacy/iwl-prph.h new file mode 100644 index 00000000000..30a493003ab --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-prph.h @@ -0,0 +1,523 @@ +/****************************************************************************** + * + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + * BSD LICENSE + * + * Copyright(c) 2005 - 2011 Intel Corporation. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name Intel Corporation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +#ifndef __iwl_legacy_prph_h__ +#define __iwl_legacy_prph_h__ + +/* + * Registers in this file are internal, not PCI bus memory mapped. + * Driver accesses these via HBUS_TARG_PRPH_* registers. + */ +#define PRPH_BASE (0x00000) +#define PRPH_END (0xFFFFF) + +/* APMG (power management) constants */ +#define APMG_BASE (PRPH_BASE + 0x3000) +#define APMG_CLK_CTRL_REG (APMG_BASE + 0x0000) +#define APMG_CLK_EN_REG (APMG_BASE + 0x0004) +#define APMG_CLK_DIS_REG (APMG_BASE + 0x0008) +#define APMG_PS_CTRL_REG (APMG_BASE + 0x000c) +#define APMG_PCIDEV_STT_REG (APMG_BASE + 0x0010) +#define APMG_RFKILL_REG (APMG_BASE + 0x0014) +#define APMG_RTC_INT_STT_REG (APMG_BASE + 0x001c) +#define APMG_RTC_INT_MSK_REG (APMG_BASE + 0x0020) +#define APMG_DIGITAL_SVR_REG (APMG_BASE + 0x0058) +#define APMG_ANALOG_SVR_REG (APMG_BASE + 0x006C) + +#define APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) +#define APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) +#define APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) + +#define APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) +#define APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) +#define APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) +#define APMG_PS_CTRL_VAL_PWR_SRC_MAX (0x01000000) /* 3945 only */ +#define APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) +#define APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ +#define APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) + +#define APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) + +/** + * BSM (Bootstrap State Machine) + * + * The Bootstrap State Machine (BSM) stores a short bootstrap uCode program + * in special SRAM that does not power down when the embedded control + * processor is sleeping (e.g. for periodic power-saving shutdowns of radio). + * + * When powering back up after sleeps (or during initial uCode load), the BSM + * internally loads the short bootstrap program from the special SRAM into the + * embedded processor's instruction SRAM, and starts the processor so it runs + * the bootstrap program. + * + * This bootstrap program loads (via PCI busmaster DMA) instructions and data + * images for a uCode program from host DRAM locations. The host driver + * indicates DRAM locations and sizes for instruction and data images via the + * four BSM_DRAM_* registers. Once the bootstrap program loads the new program, + * the new program starts automatically. + * + * The uCode used for open-source drivers includes two programs: + * + * 1) Initialization -- performs hardware calibration and sets up some + * internal data, then notifies host via "initialize alive" notification + * (struct iwl_init_alive_resp) that it has completed all of its work. + * After signal from host, it then loads and starts the runtime program. + * The initialization program must be used when initially setting up the + * NIC after loading the driver. + * + * 2) Runtime/Protocol -- performs all normal runtime operations. This + * notifies host via "alive" notification (struct iwl_alive_resp) that it + * is ready to be used. + * + * When initializing the NIC, the host driver does the following procedure: + * + * 1) Load bootstrap program (instructions only, no data image for bootstrap) + * into bootstrap memory. Use dword writes starting at BSM_SRAM_LOWER_BOUND + * + * 2) Point (via BSM_DRAM_*) to the "initialize" uCode data and instruction + * images in host DRAM. + * + * 3) Set up BSM to copy from BSM SRAM into uCode instruction SRAM when asked: + * BSM_WR_MEM_SRC_REG = 0 + * BSM_WR_MEM_DST_REG = RTC_INST_LOWER_BOUND + * BSM_WR_MEM_DWCOUNT_REG = # dwords in bootstrap instruction image + * + * 4) Load bootstrap into instruction SRAM: + * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START + * + * 5) Wait for load completion: + * Poll BSM_WR_CTRL_REG for BSM_WR_CTRL_REG_BIT_START = 0 + * + * 6) Enable future boot loads whenever NIC's power management triggers it: + * BSM_WR_CTRL_REG = BSM_WR_CTRL_REG_BIT_START_EN + * + * 7) Start the NIC by removing all reset bits: + * CSR_RESET = 0 + * + * The bootstrap uCode (already in instruction SRAM) loads initialization + * uCode. Initialization uCode performs data initialization, sends + * "initialize alive" notification to host, and waits for a signal from + * host to load runtime code. + * + * 4) Point (via BSM_DRAM_*) to the "runtime" uCode data and instruction + * images in host DRAM. The last register loaded must be the instruction + * byte count register ("1" in MSbit tells initialization uCode to load + * the runtime uCode): + * BSM_DRAM_INST_BYTECOUNT_REG = byte count | BSM_DRAM_INST_LOAD + * + * 5) Wait for "alive" notification, then issue normal runtime commands. + * + * Data caching during power-downs: + * + * Just before the embedded controller powers down (e.g for automatic + * power-saving modes, or for RFKILL), uCode stores (via PCI busmaster DMA) + * a current snapshot of the embedded processor's data SRAM into host DRAM. + * This caches the data while the embedded processor's memory is powered down. + * Location and size are controlled by BSM_DRAM_DATA_* registers. + * + * NOTE: Instruction SRAM does not need to be saved, since that doesn't + * change during operation; the original image (from uCode distribution + * file) can be used for reload. + * + * When powering back up, the BSM loads the bootstrap program. Bootstrap looks + * at the BSM_DRAM_* registers, which now point to the runtime instruction + * image and the cached (modified) runtime data (*not* the initialization + * uCode). Bootstrap reloads these runtime images into SRAM, and restarts the + * uCode from where it left off before the power-down. + * + * NOTE: Initialization uCode does *not* run as part of the save/restore + * procedure. + * + * This save/restore method is mostly for autonomous power management during + * normal operation (result of POWER_TABLE_CMD). Platform suspend/resume and + * RFKILL should use complete restarts (with total re-initialization) of uCode, + * allowing total shutdown (including BSM memory). + * + * Note that, during normal operation, the host DRAM that held the initial + * startup data for the runtime code is now being used as a backup data cache + * for modified data! If you need to completely re-initialize the NIC, make + * sure that you use the runtime data image from the uCode distribution file, + * not the modified/saved runtime data. You may want to store a separate + * "clean" runtime data image in DRAM to avoid disk reads of distribution file. + */ + +/* BSM bit fields */ +#define BSM_WR_CTRL_REG_BIT_START (0x80000000) /* start boot load now */ +#define BSM_WR_CTRL_REG_BIT_START_EN (0x40000000) /* enable boot after pwrup*/ +#define BSM_DRAM_INST_LOAD (0x80000000) /* start program load now */ + +/* BSM addresses */ +#define BSM_BASE (PRPH_BASE + 0x3400) +#define BSM_END (PRPH_BASE + 0x3800) + +#define BSM_WR_CTRL_REG (BSM_BASE + 0x000) /* ctl and status */ +#define BSM_WR_MEM_SRC_REG (BSM_BASE + 0x004) /* source in BSM mem */ +#define BSM_WR_MEM_DST_REG (BSM_BASE + 0x008) /* dest in SRAM mem */ +#define BSM_WR_DWCOUNT_REG (BSM_BASE + 0x00C) /* bytes */ +#define BSM_WR_STATUS_REG (BSM_BASE + 0x010) /* bit 0: 1 == done */ + +/* + * Pointers and size regs for bootstrap load and data SRAM save/restore. + * NOTE: 3945 pointers use bits 31:0 of DRAM address. + * 4965 pointers use bits 35:4 of DRAM address. + */ +#define BSM_DRAM_INST_PTR_REG (BSM_BASE + 0x090) +#define BSM_DRAM_INST_BYTECOUNT_REG (BSM_BASE + 0x094) +#define BSM_DRAM_DATA_PTR_REG (BSM_BASE + 0x098) +#define BSM_DRAM_DATA_BYTECOUNT_REG (BSM_BASE + 0x09C) + +/* + * BSM special memory, stays powered on during power-save sleeps. + * Read/write, address range from LOWER_BOUND to (LOWER_BOUND + SIZE -1) + */ +#define BSM_SRAM_LOWER_BOUND (PRPH_BASE + 0x3800) +#define BSM_SRAM_SIZE (1024) /* bytes */ + + +/* 3945 Tx scheduler registers */ +#define ALM_SCD_BASE (PRPH_BASE + 0x2E00) +#define ALM_SCD_MODE_REG (ALM_SCD_BASE + 0x000) +#define ALM_SCD_ARASTAT_REG (ALM_SCD_BASE + 0x004) +#define ALM_SCD_TXFACT_REG (ALM_SCD_BASE + 0x010) +#define ALM_SCD_TXF4MF_REG (ALM_SCD_BASE + 0x014) +#define ALM_SCD_TXF5MF_REG (ALM_SCD_BASE + 0x020) +#define ALM_SCD_SBYP_MODE_1_REG (ALM_SCD_BASE + 0x02C) +#define ALM_SCD_SBYP_MODE_2_REG (ALM_SCD_BASE + 0x030) + +/** + * Tx Scheduler + * + * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs + * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in + * host DRAM. It steers each frame's Tx command (which contains the frame + * data) into one of up to 7 prioritized Tx DMA FIFO channels within the + * device. A queue maps to only one (selectable by driver) Tx DMA channel, + * but one DMA channel may take input from several queues. + * + * Tx DMA FIFOs have dedicated purposes. For 4965, they are used as follows + * (cf. default_queue_to_tx_fifo in iwl-4965.c): + * + * 0 -- EDCA BK (background) frames, lowest priority + * 1 -- EDCA BE (best effort) frames, normal priority + * 2 -- EDCA VI (video) frames, higher priority + * 3 -- EDCA VO (voice) and management frames, highest priority + * 4 -- Commands (e.g. RXON, etc.) + * 5 -- unused (HCCA) + * 6 -- unused (HCCA) + * 7 -- not used by driver (device-internal only) + * + * + * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. + * In addition, driver can map the remaining queues to Tx DMA/FIFO + * channels 0-3 to support 11n aggregation via EDCA DMA channels. + * + * The driver sets up each queue to work in one of two modes: + * + * 1) Scheduler-Ack, in which the scheduler automatically supports a + * block-ack (BA) window of up to 64 TFDs. In this mode, each queue + * contains TFDs for a unique combination of Recipient Address (RA) + * and Traffic Identifier (TID), that is, traffic of a given + * Quality-Of-Service (QOS) priority, destined for a single station. + * + * In scheduler-ack mode, the scheduler keeps track of the Tx status of + * each frame within the BA window, including whether it's been transmitted, + * and whether it's been acknowledged by the receiving station. The device + * automatically processes block-acks received from the receiving STA, + * and reschedules un-acked frames to be retransmitted (successful + * Tx completion may end up being out-of-order). + * + * The driver must maintain the queue's Byte Count table in host DRAM + * (struct iwl4965_sched_queue_byte_cnt_tbl) for this mode. + * This mode does not support fragmentation. + * + * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. + * The device may automatically retry Tx, but will retry only one frame + * at a time, until receiving ACK from receiving station, or reaching + * retry limit and giving up. + * + * The command queue (#4/#9) must use this mode! + * This mode does not require use of the Byte Count table in host DRAM. + * + * Driver controls scheduler operation via 3 means: + * 1) Scheduler registers + * 2) Shared scheduler data base in internal 4956 SRAM + * 3) Shared data in host DRAM + * + * Initialization: + * + * When loading, driver should allocate memory for: + * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. + * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory + * (1024 bytes for each queue). + * + * After receiving "Alive" response from uCode, driver must initialize + * the scheduler (especially for queue #4/#9, the command queue, otherwise + * the driver can't issue commands!): + */ + +/** + * Max Tx window size is the max number of contiguous TFDs that the scheduler + * can keep track of at one time when creating block-ack chains of frames. + * Note that "64" matches the number of ack bits in a block-ack packet. + * Driver should use SCD_WIN_SIZE and SCD_FRAME_LIMIT values to initialize + * IWL49_SCD_CONTEXT_QUEUE_OFFSET(x) values. + */ +#define SCD_WIN_SIZE 64 +#define SCD_FRAME_LIMIT 64 + +/* SCD registers are internal, must be accessed via HBUS_TARG_PRPH regs */ +#define IWL49_SCD_START_OFFSET 0xa02c00 + +/* + * 4965 tells driver SRAM address for internal scheduler structs via this reg. + * Value is valid only after "Alive" response from uCode. + */ +#define IWL49_SCD_SRAM_BASE_ADDR (IWL49_SCD_START_OFFSET + 0x0) + +/* + * Driver may need to update queue-empty bits after changing queue's + * write and read pointers (indexes) during (re-)initialization (i.e. when + * scheduler is not tracking what's happening). + * Bit fields: + * 31-16: Write mask -- 1: update empty bit, 0: don't change empty bit + * 15-00: Empty state, one for each queue -- 1: empty, 0: non-empty + * NOTE: This register is not used by Linux driver. + */ +#define IWL49_SCD_EMPTY_BITS (IWL49_SCD_START_OFFSET + 0x4) + +/* + * Physical base address of array of byte count (BC) circular buffers (CBs). + * Each Tx queue has a BC CB in host DRAM to support Scheduler-ACK mode. + * This register points to BC CB for queue 0, must be on 1024-byte boundary. + * Others are spaced by 1024 bytes. + * Each BC CB is 2 bytes * (256 + 64) = 740 bytes, followed by 384 bytes pad. + * (Index into a queue's BC CB) = (index into queue's TFD CB) = (SSN & 0xff). + * Bit fields: + * 25-00: Byte Count CB physical address [35:10], must be 1024-byte aligned. + */ +#define IWL49_SCD_DRAM_BASE_ADDR (IWL49_SCD_START_OFFSET + 0x10) + +/* + * Enables any/all Tx DMA/FIFO channels. + * Scheduler generates requests for only the active channels. + * Set this to 0xff to enable all 8 channels (normal usage). + * Bit fields: + * 7- 0: Enable (1), disable (0), one bit for each channel 0-7 + */ +#define IWL49_SCD_TXFACT (IWL49_SCD_START_OFFSET + 0x1c) +/* + * Queue (x) Write Pointers (indexes, really!), one for each Tx queue. + * Initialized and updated by driver as new TFDs are added to queue. + * NOTE: If using Block Ack, index must correspond to frame's + * Start Sequence Number; index = (SSN & 0xff) + * NOTE: Alternative to HBUS_TARG_WRPTR, which is what Linux driver uses? + */ +#define IWL49_SCD_QUEUE_WRPTR(x) (IWL49_SCD_START_OFFSET + 0x24 + (x) * 4) + +/* + * Queue (x) Read Pointers (indexes, really!), one for each Tx queue. + * For FIFO mode, index indicates next frame to transmit. + * For Scheduler-ACK mode, index indicates first frame in Tx window. + * Initialized by driver, updated by scheduler. + */ +#define IWL49_SCD_QUEUE_RDPTR(x) (IWL49_SCD_START_OFFSET + 0x64 + (x) * 4) + +/* + * Select which queues work in chain mode (1) vs. not (0). + * Use chain mode to build chains of aggregated frames. + * Bit fields: + * 31-16: Reserved + * 15-00: Mode, one bit for each queue -- 1: Chain mode, 0: one-at-a-time + * NOTE: If driver sets up queue for chain mode, it should be also set up + * Scheduler-ACK mode as well, via SCD_QUEUE_STATUS_BITS(x). + */ +#define IWL49_SCD_QUEUECHAIN_SEL (IWL49_SCD_START_OFFSET + 0xd0) + +/* + * Select which queues interrupt driver when scheduler increments + * a queue's read pointer (index). + * Bit fields: + * 31-16: Reserved + * 15-00: Interrupt enable, one bit for each queue -- 1: enabled, 0: disabled + * NOTE: This functionality is apparently a no-op; driver relies on interrupts + * from Rx queue to read Tx command responses and update Tx queues. + */ +#define IWL49_SCD_INTERRUPT_MASK (IWL49_SCD_START_OFFSET + 0xe4) + +/* + * Queue search status registers. One for each queue. + * Sets up queue mode and assigns queue to Tx DMA channel. + * Bit fields: + * 19-10: Write mask/enable bits for bits 0-9 + * 9: Driver should init to "0" + * 8: Scheduler-ACK mode (1), non-Scheduler-ACK (i.e. FIFO) mode (0). + * Driver should init to "1" for aggregation mode, or "0" otherwise. + * 7-6: Driver should init to "0" + * 5: Window Size Left; indicates whether scheduler can request + * another TFD, based on window size, etc. Driver should init + * this bit to "1" for aggregation mode, or "0" for non-agg. + * 4-1: Tx FIFO to use (range 0-7). + * 0: Queue is active (1), not active (0). + * Other bits should be written as "0" + * + * NOTE: If enabling Scheduler-ACK mode, chain mode should also be enabled + * via SCD_QUEUECHAIN_SEL. + */ +#define IWL49_SCD_QUEUE_STATUS_BITS(x)\ + (IWL49_SCD_START_OFFSET + 0x104 + (x) * 4) + +/* Bit field positions */ +#define IWL49_SCD_QUEUE_STTS_REG_POS_ACTIVE (0) +#define IWL49_SCD_QUEUE_STTS_REG_POS_TXF (1) +#define IWL49_SCD_QUEUE_STTS_REG_POS_WSL (5) +#define IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK (8) + +/* Write masks */ +#define IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (10) +#define IWL49_SCD_QUEUE_STTS_REG_MSK (0x0007FC00) + +/** + * 4965 internal SRAM structures for scheduler, shared with driver ... + * + * Driver should clear and initialize the following areas after receiving + * "Alive" response from 4965 uCode, i.e. after initial + * uCode load, or after a uCode load done for error recovery: + * + * SCD_CONTEXT_DATA_OFFSET (size 128 bytes) + * SCD_TX_STTS_BITMAP_OFFSET (size 256 bytes) + * SCD_TRANSLATE_TBL_OFFSET (size 32 bytes) + * + * Driver accesses SRAM via HBUS_TARG_MEM_* registers. + * Driver reads base address of this scheduler area from SCD_SRAM_BASE_ADDR. + * All OFFSET values must be added to this base address. + */ + +/* + * Queue context. One 8-byte entry for each of 16 queues. + * + * Driver should clear this entire area (size 0x80) to 0 after receiving + * "Alive" notification from uCode. Additionally, driver should init + * each queue's entry as follows: + * + * LS Dword bit fields: + * 0-06: Max Tx window size for Scheduler-ACK. Driver should init to 64. + * + * MS Dword bit fields: + * 16-22: Frame limit. Driver should init to 10 (0xa). + * + * Driver should init all other bits to 0. + * + * Init must be done after driver receives "Alive" response from 4965 uCode, + * and when setting up queue for aggregation. + */ +#define IWL49_SCD_CONTEXT_DATA_OFFSET 0x380 +#define IWL49_SCD_CONTEXT_QUEUE_OFFSET(x) \ + (IWL49_SCD_CONTEXT_DATA_OFFSET + ((x) * 8)) + +#define IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS (0) +#define IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK (0x0000007F) +#define IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) +#define IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) + +/* + * Tx Status Bitmap + * + * Driver should clear this entire area (size 0x100) to 0 after receiving + * "Alive" notification from uCode. Area is used only by device itself; + * no other support (besides clearing) is required from driver. + */ +#define IWL49_SCD_TX_STTS_BITMAP_OFFSET 0x400 + +/* + * RAxTID to queue translation mapping. + * + * When queue is in Scheduler-ACK mode, frames placed in a that queue must be + * for only one combination of receiver address (RA) and traffic ID (TID), i.e. + * one QOS priority level destined for one station (for this wireless link, + * not final destination). The SCD_TRANSLATE_TABLE area provides 16 16-bit + * mappings, one for each of the 16 queues. If queue is not in Scheduler-ACK + * mode, the device ignores the mapping value. + * + * Bit fields, for each 16-bit map: + * 15-9: Reserved, set to 0 + * 8-4: Index into device's station table for recipient station + * 3-0: Traffic ID (tid), range 0-15 + * + * Driver should clear this entire area (size 32 bytes) to 0 after receiving + * "Alive" notification from uCode. To update a 16-bit map value, driver + * must read a dword-aligned value from device SRAM, replace the 16-bit map + * value of interest, and write the dword value back into device SRAM. + */ +#define IWL49_SCD_TRANSLATE_TBL_OFFSET 0x500 + +/* Find translation table dword to read/write for given queue */ +#define IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(x) \ + ((IWL49_SCD_TRANSLATE_TBL_OFFSET + ((x) * 2)) & 0xfffffffc) + +#define IWL_SCD_TXFIFO_POS_TID (0) +#define IWL_SCD_TXFIFO_POS_RA (4) +#define IWL_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) + +/*********************** END TX SCHEDULER *************************************/ + +#endif /* __iwl_legacy_prph_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-rx.c b/drivers/net/wireless/iwlegacy/iwl-rx.c new file mode 100644 index 00000000000..654cf233a38 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-rx.c @@ -0,0 +1,302 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/etherdevice.h> +#include <linux/slab.h> +#include <net/mac80211.h> +#include <asm/unaligned.h> +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +/************************** RX-FUNCTIONS ****************************/ +/* + * Rx theory of operation + * + * Driver allocates a circular buffer of Receive Buffer Descriptors (RBDs), + * each of which point to Receive Buffers to be filled by the NIC. These get + * used not only for Rx frames, but for any command response or notification + * from the NIC. The driver and NIC manage the Rx buffers by means + * of indexes into the circular buffer. + * + * Rx Queue Indexes + * The host/firmware share two index registers for managing the Rx buffers. + * + * The READ index maps to the first position that the firmware may be writing + * to -- the driver can read up to (but not including) this position and get + * good data. + * The READ index is managed by the firmware once the card is enabled. + * + * The WRITE index maps to the last position the driver has read from -- the + * position preceding WRITE is the last slot the firmware can place a packet. + * + * The queue is empty (no good data) if WRITE = READ - 1, and is full if + * WRITE = READ. + * + * During initialization, the host sets up the READ queue position to the first + * INDEX position, and WRITE to the last (READ - 1 wrapped) + * + * When the firmware places a packet in a buffer, it will advance the READ index + * and fire the RX interrupt. The driver can then query the READ index and + * process as many packets as possible, moving the WRITE index forward as it + * resets the Rx queue buffers with new memory. + * + * The management in the driver is as follows: + * + A list of pre-allocated SKBs is stored in iwl->rxq->rx_free. When + * iwl->rxq->free_count drops to or below RX_LOW_WATERMARK, work is scheduled + * to replenish the iwl->rxq->rx_free. + * + In iwl_rx_replenish (scheduled) if 'processed' != 'read' then the + * iwl->rxq is replenished and the READ INDEX is updated (updating the + * 'processed' and 'read' driver indexes as well) + * + A received packet is processed and handed to the kernel network stack, + * detached from the iwl->rxq. The driver 'processed' index is updated. + * + The Host/Firmware iwl->rxq is replenished at tasklet time from the rx_free + * list. If there are no allocated buffers in iwl->rxq->rx_free, the READ + * INDEX is not incremented and iwl->status(RX_STALLED) is set. If there + * were enough free buffers and RX_STALLED is set it is cleared. + * + * + * Driver sequence: + * + * iwl_legacy_rx_queue_alloc() Allocates rx_free + * iwl_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl_rx_queue_restock + * iwl_rx_queue_restock() Moves available buffers from rx_free into Rx + * queue, updates firmware pointers, and updates + * the WRITE index. If insufficient rx_free buffers + * are available, schedules iwl_rx_replenish + * + * -- enable interrupts -- + * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the + * READ INDEX, detaching the SKB from the pool. + * Moves the packet buffer from queue to rx_used. + * Calls iwl_rx_queue_restock to refill any empty + * slots. + * ... + * + */ + +/** + * iwl_legacy_rx_queue_space - Return number of free slots available in queue. + */ +int iwl_legacy_rx_queue_space(const struct iwl_rx_queue *q) +{ + int s = q->read - q->write; + if (s <= 0) + s += RX_QUEUE_SIZE; + /* keep some buffer to not confuse full and empty queue */ + s -= 2; + if (s < 0) + s = 0; + return s; +} +EXPORT_SYMBOL(iwl_legacy_rx_queue_space); + +/** + * iwl_legacy_rx_queue_update_write_ptr - Update the write pointer for the RX queue + */ +void +iwl_legacy_rx_queue_update_write_ptr(struct iwl_priv *priv, + struct iwl_rx_queue *q) +{ + unsigned long flags; + u32 rx_wrt_ptr_reg = priv->hw_params.rx_wrt_ptr_reg; + u32 reg; + + spin_lock_irqsave(&q->lock, flags); + + if (q->need_update == 0) + goto exit_unlock; + + /* If power-saving is in use, make sure device is awake */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(priv, + "Rx queue requesting wakeup," + " GP1 = 0x%x\n", reg); + iwl_legacy_set_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + goto exit_unlock; + } + + q->write_actual = (q->write & ~0x7); + iwl_legacy_write_direct32(priv, rx_wrt_ptr_reg, + q->write_actual); + + /* Else device is assumed to be awake */ + } else { + /* Device expects a multiple of 8 */ + q->write_actual = (q->write & ~0x7); + iwl_legacy_write_direct32(priv, rx_wrt_ptr_reg, + q->write_actual); + } + + q->need_update = 0; + + exit_unlock: + spin_unlock_irqrestore(&q->lock, flags); +} +EXPORT_SYMBOL(iwl_legacy_rx_queue_update_write_ptr); + +int iwl_legacy_rx_queue_alloc(struct iwl_priv *priv) +{ + struct iwl_rx_queue *rxq = &priv->rxq; + struct device *dev = &priv->pci_dev->dev; + int i; + + spin_lock_init(&rxq->lock); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + /* Alloc the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_alloc_coherent(dev, 4 * RX_QUEUE_SIZE, &rxq->bd_dma, + GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + + rxq->rb_stts = dma_alloc_coherent(dev, sizeof(struct iwl_rb_status), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb; + + /* Fill the rx_used queue with _all_ of the Rx buffers */ + for (i = 0; i < RX_FREE_BUFFERS + RX_QUEUE_SIZE; i++) + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + + /* Set us so that we have processed and used all buffers, but have + * not restocked the Rx queue with fresh buffers */ + rxq->read = rxq->write = 0; + rxq->write_actual = 0; + rxq->free_count = 0; + rxq->need_update = 0; + return 0; + +err_rb: + dma_free_coherent(&priv->pci_dev->dev, 4 * RX_QUEUE_SIZE, rxq->bd, + rxq->bd_dma); +err_bd: + return -ENOMEM; +} +EXPORT_SYMBOL(iwl_legacy_rx_queue_alloc); + + +void iwl_legacy_rx_spectrum_measure_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_spectrum_notification *report = &(pkt->u.spectrum_notif); + + if (!report->state) { + IWL_DEBUG_11H(priv, + "Spectrum Measure Notification: Start\n"); + return; + } + + memcpy(&priv->measure_report, report, sizeof(*report)); + priv->measurement_status |= MEASUREMENT_READY; +} +EXPORT_SYMBOL(iwl_legacy_rx_spectrum_measure_notif); + +void iwl_legacy_recover_from_statistics(struct iwl_priv *priv, + struct iwl_rx_packet *pkt) +{ + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + if (iwl_legacy_is_any_associated(priv)) { + if (priv->cfg->ops->lib->check_plcp_health) { + if (!priv->cfg->ops->lib->check_plcp_health( + priv, pkt)) { + /* + * high plcp error detected + * reset Radio + */ + iwl_legacy_force_reset(priv, + IWL_RF_RESET, false); + } + } + } +} +EXPORT_SYMBOL(iwl_legacy_recover_from_statistics); + +/* + * returns non-zero if packet should be dropped + */ +int iwl_legacy_set_decrypted_flag(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + u32 decrypt_res, + struct ieee80211_rx_status *stats) +{ + u16 fc = le16_to_cpu(hdr->frame_control); + + /* + * All contexts have the same setting here due to it being + * a module parameter, so OK to check any context. + */ + if (priv->contexts[IWL_RXON_CTX_BSS].active.filter_flags & + RXON_FILTER_DIS_DECRYPT_MSK) + return 0; + + if (!(fc & IEEE80211_FCTL_PROTECTED)) + return 0; + + IWL_DEBUG_RX(priv, "decrypt_res:0x%x\n", decrypt_res); + switch (decrypt_res & RX_RES_STATUS_SEC_TYPE_MSK) { + case RX_RES_STATUS_SEC_TYPE_TKIP: + /* The uCode has got a bad phase 1 Key, pushes the packet. + * Decryption will be done in SW. */ + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_KEY_TTAK) + break; + + case RX_RES_STATUS_SEC_TYPE_WEP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_BAD_ICV_MIC) { + /* bad ICV, the packet is destroyed since the + * decryption is inplace, drop it */ + IWL_DEBUG_RX(priv, "Packet destroyed\n"); + return -1; + } + case RX_RES_STATUS_SEC_TYPE_CCMP: + if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == + RX_RES_STATUS_DECRYPT_OK) { + IWL_DEBUG_RX(priv, "hw decrypt successfully!!!\n"); + stats->flag |= RX_FLAG_DECRYPTED; + } + break; + + default: + break; + } + return 0; +} +EXPORT_SYMBOL(iwl_legacy_set_decrypted_flag); diff --git a/drivers/net/wireless/iwlegacy/iwl-scan.c b/drivers/net/wireless/iwlegacy/iwl-scan.c new file mode 100644 index 00000000000..60f597f796c --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-scan.c @@ -0,0 +1,625 @@ +/****************************************************************************** + * + * GPL LICENSE SUMMARY + * + * Copyright(c) 2008 - 2011 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, + * USA + * + * The full GNU General Public License is included in this distribution + * in the file called LICENSE.GPL. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + *****************************************************************************/ +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/etherdevice.h> +#include <net/mac80211.h> + +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" +#include "iwl-io.h" +#include "iwl-helpers.h" + +/* For active scan, listen ACTIVE_DWELL_TIME (msec) on each channel after + * sending probe req. This should be set long enough to hear probe responses + * from more than one AP. */ +#define IWL_ACTIVE_DWELL_TIME_24 (30) /* all times in msec */ +#define IWL_ACTIVE_DWELL_TIME_52 (20) + +#define IWL_ACTIVE_DWELL_FACTOR_24GHZ (3) +#define IWL_ACTIVE_DWELL_FACTOR_52GHZ (2) + +/* For passive scan, listen PASSIVE_DWELL_TIME (msec) on each channel. + * Must be set longer than active dwell time. + * For the most reliable scan, set > AP beacon interval (typically 100msec). */ +#define IWL_PASSIVE_DWELL_TIME_24 (20) /* all times in msec */ +#define IWL_PASSIVE_DWELL_TIME_52 (10) +#define IWL_PASSIVE_DWELL_BASE (100) +#define IWL_CHANNEL_TUNE_TIME 5 + +static int iwl_legacy_send_scan_abort(struct iwl_priv *priv) +{ + int ret; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = REPLY_SCAN_ABORT_CMD, + .flags = CMD_WANT_SKB, + }; + + /* Exit instantly with error when device is not ready + * to receive scan abort command or it does not perform + * hardware scan currently */ + if (!test_bit(STATUS_READY, &priv->status) || + !test_bit(STATUS_GEO_CONFIGURED, &priv->status) || + !test_bit(STATUS_SCAN_HW, &priv->status) || + test_bit(STATUS_FW_ERROR, &priv->status) || + test_bit(STATUS_EXIT_PENDING, &priv->status)) + return -EIO; + + ret = iwl_legacy_send_cmd_sync(priv, &cmd); + if (ret) + return ret; + + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->u.status != CAN_ABORT_STATUS) { + /* The scan abort will return 1 for success or + * 2 for "failure". A failure condition can be + * due to simply not being in an active scan which + * can occur if we send the scan abort before we + * the microcode has notified us that a scan is + * completed. */ + IWL_DEBUG_SCAN(priv, "SCAN_ABORT ret %d.\n", pkt->u.status); + ret = -EIO; + } + + iwl_legacy_free_pages(priv, cmd.reply_page); + return ret; +} + +static void iwl_legacy_complete_scan(struct iwl_priv *priv, bool aborted) +{ + /* check if scan was requested from mac80211 */ + if (priv->scan_request) { + IWL_DEBUG_SCAN(priv, "Complete scan in mac80211\n"); + ieee80211_scan_completed(priv->hw, aborted); + } + + priv->is_internal_short_scan = false; + priv->scan_vif = NULL; + priv->scan_request = NULL; +} + +void iwl_legacy_force_scan_end(struct iwl_priv *priv) +{ + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Forcing scan end while not scanning\n"); + return; + } + + IWL_DEBUG_SCAN(priv, "Forcing scan end\n"); + clear_bit(STATUS_SCANNING, &priv->status); + clear_bit(STATUS_SCAN_HW, &priv->status); + clear_bit(STATUS_SCAN_ABORTING, &priv->status); + iwl_legacy_complete_scan(priv, true); +} + +static void iwl_legacy_do_scan_abort(struct iwl_priv *priv) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (!test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Not performing scan to abort\n"); + return; + } + + if (test_and_set_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan abort in progress\n"); + return; + } + + ret = iwl_legacy_send_scan_abort(priv); + if (ret) { + IWL_DEBUG_SCAN(priv, "Send scan abort failed %d\n", ret); + iwl_legacy_force_scan_end(priv); + } else + IWL_DEBUG_SCAN(priv, "Sucessfully send scan abort\n"); +} + +/** + * iwl_scan_cancel - Cancel any currently executing HW scan + */ +int iwl_legacy_scan_cancel(struct iwl_priv *priv) +{ + IWL_DEBUG_SCAN(priv, "Queuing abort scan\n"); + queue_work(priv->workqueue, &priv->abort_scan); + return 0; +} +EXPORT_SYMBOL(iwl_legacy_scan_cancel); + +/** + * iwl_legacy_scan_cancel_timeout - Cancel any currently executing HW scan + * @ms: amount of time to wait (in milliseconds) for scan to abort + * + */ +int iwl_legacy_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(ms); + + lockdep_assert_held(&priv->mutex); + + IWL_DEBUG_SCAN(priv, "Scan cancel timeout\n"); + + iwl_legacy_do_scan_abort(priv); + + while (time_before_eq(jiffies, timeout)) { + if (!test_bit(STATUS_SCAN_HW, &priv->status)) + break; + msleep(20); + } + + return test_bit(STATUS_SCAN_HW, &priv->status); +} +EXPORT_SYMBOL(iwl_legacy_scan_cancel_timeout); + +/* Service response to REPLY_SCAN_CMD (0x80) */ +static void iwl_legacy_rx_reply_scan(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanreq_notification *notif = + (struct iwl_scanreq_notification *)pkt->u.raw; + + IWL_DEBUG_SCAN(priv, "Scan request status = 0x%x\n", notif->status); +#endif +} + +/* Service SCAN_START_NOTIFICATION (0x82) */ +static void iwl_legacy_rx_scan_start_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanstart_notification *notif = + (struct iwl_scanstart_notification *)pkt->u.raw; + priv->scan_start_tsf = le32_to_cpu(notif->tsf_low); + IWL_DEBUG_SCAN(priv, "Scan start: " + "%d [802.11%s] " + "(TSF: 0x%08X:%08X) - %d (beacon timer %u)\n", + notif->channel, + notif->band ? "bg" : "a", + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + notif->status, notif->beacon_timer); +} + +/* Service SCAN_RESULTS_NOTIFICATION (0x83) */ +static void iwl_legacy_rx_scan_results_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scanresults_notification *notif = + (struct iwl_scanresults_notification *)pkt->u.raw; + + IWL_DEBUG_SCAN(priv, "Scan ch.res: " + "%d [802.11%s] " + "(TSF: 0x%08X:%08X) - %d " + "elapsed=%lu usec\n", + notif->channel, + notif->band ? "bg" : "a", + le32_to_cpu(notif->tsf_high), + le32_to_cpu(notif->tsf_low), + le32_to_cpu(notif->statistics[0]), + le32_to_cpu(notif->tsf_low) - priv->scan_start_tsf); +#endif +} + +/* Service SCAN_COMPLETE_NOTIFICATION (0x84) */ +static void iwl_legacy_rx_scan_complete_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_scancomplete_notification *scan_notif = (void *)pkt->u.raw; +#endif + + IWL_DEBUG_SCAN(priv, + "Scan complete: %d channels (TSF 0x%08X:%08X) - %d\n", + scan_notif->scanned_channels, + scan_notif->tsf_low, + scan_notif->tsf_high, scan_notif->status); + + /* The HW is no longer scanning */ + clear_bit(STATUS_SCAN_HW, &priv->status); + + IWL_DEBUG_SCAN(priv, "Scan on %sGHz took %dms\n", + (priv->scan_band == IEEE80211_BAND_2GHZ) ? "2.4" : "5.2", + jiffies_to_msecs(jiffies - priv->scan_start)); + + queue_work(priv->workqueue, &priv->scan_completed); +} + +void iwl_legacy_setup_rx_scan_handlers(struct iwl_priv *priv) +{ + /* scan handlers */ + priv->rx_handlers[REPLY_SCAN_CMD] = iwl_legacy_rx_reply_scan; + priv->rx_handlers[SCAN_START_NOTIFICATION] = + iwl_legacy_rx_scan_start_notif; + priv->rx_handlers[SCAN_RESULTS_NOTIFICATION] = + iwl_legacy_rx_scan_results_notif; + priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = + iwl_legacy_rx_scan_complete_notif; +} +EXPORT_SYMBOL(iwl_legacy_setup_rx_scan_handlers); + +inline u16 iwl_legacy_get_active_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, + u8 n_probes) +{ + if (band == IEEE80211_BAND_5GHZ) + return IWL_ACTIVE_DWELL_TIME_52 + + IWL_ACTIVE_DWELL_FACTOR_52GHZ * (n_probes + 1); + else + return IWL_ACTIVE_DWELL_TIME_24 + + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); +} +EXPORT_SYMBOL(iwl_legacy_get_active_dwell_time); + +u16 iwl_legacy_get_passive_dwell_time(struct iwl_priv *priv, + enum ieee80211_band band, + struct ieee80211_vif *vif) +{ + struct iwl_rxon_context *ctx; + u16 passive = (band == IEEE80211_BAND_2GHZ) ? + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_24 : + IWL_PASSIVE_DWELL_BASE + IWL_PASSIVE_DWELL_TIME_52; + + if (iwl_legacy_is_any_associated(priv)) { + /* + * If we're associated, we clamp the maximum passive + * dwell time to be 98% of the smallest beacon interval + * (minus 2 * channel tune time) + */ + for_each_context(priv, ctx) { + u16 value; + + if (!iwl_legacy_is_associated_ctx(ctx)) + continue; + value = ctx->vif ? ctx->vif->bss_conf.beacon_int : 0; + if ((value > IWL_PASSIVE_DWELL_BASE) || !value) + value = IWL_PASSIVE_DWELL_BASE; + value = (value * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; + passive = min(value, passive); + } + } + + return passive; +} +EXPORT_SYMBOL(iwl_legacy_get_passive_dwell_time); + +void iwl_legacy_init_scan_params(struct iwl_priv *priv) +{ + u8 ant_idx = fls(priv->hw_params.valid_tx_ant) - 1; + if (!priv->scan_tx_ant[IEEE80211_BAND_5GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_5GHZ] = ant_idx; + if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) + priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; +} +EXPORT_SYMBOL(iwl_legacy_init_scan_params); + +static int __must_check iwl_legacy_scan_initiate(struct iwl_priv *priv, + struct ieee80211_vif *vif, + bool internal, + enum ieee80211_band band) +{ + int ret; + + lockdep_assert_held(&priv->mutex); + + if (WARN_ON(!priv->cfg->ops->utils->request_scan)) + return -EOPNOTSUPP; + + cancel_delayed_work(&priv->scan_check); + + if (!iwl_legacy_is_ready_rf(priv)) { + IWL_WARN(priv, "Request scan called when driver not ready.\n"); + return -EIO; + } + + if (test_bit(STATUS_SCAN_HW, &priv->status)) { + IWL_DEBUG_SCAN(priv, + "Multiple concurrent scan requests in parallel.\n"); + return -EBUSY; + } + + if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan request while abort pending.\n"); + return -EBUSY; + } + + IWL_DEBUG_SCAN(priv, "Starting %sscan...\n", + internal ? "internal short " : ""); + + set_bit(STATUS_SCANNING, &priv->status); + priv->is_internal_short_scan = internal; + priv->scan_start = jiffies; + priv->scan_band = band; + + ret = priv->cfg->ops->utils->request_scan(priv, vif); + if (ret) { + clear_bit(STATUS_SCANNING, &priv->status); + priv->is_internal_short_scan = false; + return ret; + } + + queue_delayed_work(priv->workqueue, &priv->scan_check, + IWL_SCAN_CHECK_WATCHDOG); + + return 0; +} + +int iwl_legacy_mac_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_scan_request *req) +{ + struct iwl_priv *priv = hw->priv; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (req->n_channels == 0) + return -EINVAL; + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_SCANNING, &priv->status) && + !priv->is_internal_short_scan) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + ret = -EAGAIN; + goto out_unlock; + } + + /* mac80211 will only ask for one band at a time */ + priv->scan_request = req; + priv->scan_vif = vif; + + /* + * If an internal scan is in progress, just set + * up the scan_request as per above. + */ + if (priv->is_internal_short_scan) { + IWL_DEBUG_SCAN(priv, "SCAN request during internal scan\n"); + ret = 0; + } else + ret = iwl_legacy_scan_initiate(priv, vif, false, + req->channels[0]->band); + + IWL_DEBUG_MAC80211(priv, "leave\n"); + +out_unlock: + mutex_unlock(&priv->mutex); + + return ret; +} +EXPORT_SYMBOL(iwl_legacy_mac_hw_scan); + +/* + * internal short scan, this function should only been called while associated. + * It will reset and tune the radio to prevent possible RF related problem + */ +void iwl_legacy_internal_short_hw_scan(struct iwl_priv *priv) +{ + queue_work(priv->workqueue, &priv->start_internal_scan); +} + +static void iwl_legacy_bg_start_internal_scan(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, start_internal_scan); + + IWL_DEBUG_SCAN(priv, "Start internal scan\n"); + + mutex_lock(&priv->mutex); + + if (priv->is_internal_short_scan == true) { + IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n"); + goto unlock; + } + + if (test_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already in progress.\n"); + goto unlock; + } + + if (iwl_legacy_scan_initiate(priv, NULL, true, priv->band)) + IWL_DEBUG_SCAN(priv, "failed to start internal short scan\n"); + unlock: + mutex_unlock(&priv->mutex); +} + +static void iwl_legacy_bg_scan_check(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, scan_check.work); + + IWL_DEBUG_SCAN(priv, "Scan check work\n"); + + /* Since we are here firmware does not finish scan and + * most likely is in bad shape, so we don't bother to + * send abort command, just force scan complete to mac80211 */ + mutex_lock(&priv->mutex); + iwl_legacy_force_scan_end(priv); + mutex_unlock(&priv->mutex); +} + +/** + * iwl_legacy_fill_probe_req - fill in all required fields and IE for probe request + */ + +u16 +iwl_legacy_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, + const u8 *ta, const u8 *ies, int ie_len, int left) +{ + int len = 0; + u8 *pos = NULL; + + /* Make sure there is enough space for the probe request, + * two mandatory IEs and the data */ + left -= 24; + if (left < 0) + return 0; + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + memcpy(frame->da, iwlegacy_bcast_addr, ETH_ALEN); + memcpy(frame->sa, ta, ETH_ALEN); + memcpy(frame->bssid, iwlegacy_bcast_addr, ETH_ALEN); + frame->seq_ctrl = 0; + + len += 24; + + /* ...next IE... */ + pos = &frame->u.probe_req.variable[0]; + + /* fill in our indirect SSID IE */ + left -= 2; + if (left < 0) + return 0; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + len += 2; + + if (WARN_ON(left < ie_len)) + return len; + + if (ies && ie_len) { + memcpy(pos, ies, ie_len); + len += ie_len; + } + + return (u16)len; +} +EXPORT_SYMBOL(iwl_legacy_fill_probe_req); + +static void iwl_legacy_bg_abort_scan(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, abort_scan); + + IWL_DEBUG_SCAN(priv, "Abort scan work\n"); + + /* We keep scan_check work queued in case when firmware will not + * report back scan completed notification */ + mutex_lock(&priv->mutex); + iwl_legacy_scan_cancel_timeout(priv, 200); + mutex_unlock(&priv->mutex); +} + +static void iwl_legacy_bg_scan_completed(struct work_struct *work) +{ + struct iwl_priv *priv = + container_of(work, struct iwl_priv, scan_completed); + bool aborted; + + IWL_DEBUG_SCAN(priv, "Completed %sscan.\n", + priv->is_internal_short_scan ? "internal short " : ""); + + cancel_delayed_work(&priv->scan_check); + + mutex_lock(&priv->mutex); + + aborted = test_and_clear_bit(STATUS_SCAN_ABORTING, &priv->status); + if (aborted) + IWL_DEBUG_SCAN(priv, "Aborted scan completed.\n"); + + if (!test_and_clear_bit(STATUS_SCANNING, &priv->status)) { + IWL_DEBUG_SCAN(priv, "Scan already completed.\n"); + goto out_settings; + } + + if (priv->is_internal_short_scan && !aborted) { + int err; + + /* Check if mac80211 requested scan during our internal scan */ + if (priv->scan_request == NULL) + goto out_complete; + + /* If so request a new scan */ + err = iwl_legacy_scan_initiate(priv, priv->scan_vif, false, + priv->scan_request->channels[0]->band); + if (err) { + IWL_DEBUG_SCAN(priv, + "failed to initiate pending scan: %d\n", err); + aborted = true; + goto out_complete; + } + + goto out; + } + +out_complete: + iwl_legacy_complete_scan(priv, aborted); + +out_settings: + /* Can we still talk to firmware ? */ + if (!iwl_legacy_is_ready_rf(priv)) + goto out; + + /* + * We do not commit power settings while scan is pending, + * do it now if the settings changed. + */ + iwl_legacy_power_set_mode(priv, &priv->power_data.sleep_cmd_next, + false); + iwl_legacy_set_tx_power(priv, priv->tx_power_next, false); + + priv->cfg->ops->utils->post_scan(priv); + +out: + mutex_unlock(&priv->mutex); +} + +void iwl_legacy_setup_scan_deferred_work(struct iwl_priv *priv) +{ + INIT_WORK(&priv->scan_completed, iwl_legacy_bg_scan_completed); + INIT_WORK(&priv->abort_scan, iwl_legacy_bg_abort_scan); + INIT_WORK(&priv->start_internal_scan, + iwl_legacy_bg_start_internal_scan); + INIT_DELAYED_WORK(&priv->scan_check, iwl_legacy_bg_scan_check); +} +EXPORT_SYMBOL(iwl_legacy_setup_scan_deferred_work); + +void iwl_legacy_cancel_scan_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->start_internal_scan); + cancel_work_sync(&priv->abort_scan); + cancel_work_sync(&priv->scan_completed); + + if (cancel_delayed_work_sync(&priv->scan_check)) { + mutex_lock(&priv->mutex); + iwl_legacy_force_scan_end(priv); + mutex_unlock(&priv->mutex); + } +} +EXPORT_SYMBOL(iwl_legacy_cancel_scan_deferred_work); diff --git a/drivers/net/wireless/iwlegacy/iwl-spectrum.h b/drivers/net/wireless/iwlegacy/iwl-spectrum.h new file mode 100644 index 00000000000..9f70a472310 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-spectrum.h @@ -0,0 +1,92 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#ifndef __iwl_legacy_spectrum_h__ +#define __iwl_legacy_spectrum_h__ +enum { /* ieee80211_basic_report.map */ + IEEE80211_BASIC_MAP_BSS = (1 << 0), + IEEE80211_BASIC_MAP_OFDM = (1 << 1), + IEEE80211_BASIC_MAP_UNIDENTIFIED = (1 << 2), + IEEE80211_BASIC_MAP_RADAR = (1 << 3), + IEEE80211_BASIC_MAP_UNMEASURED = (1 << 4), + /* Bits 5-7 are reserved */ + +}; +struct ieee80211_basic_report { + u8 channel; + __le64 start_time; + __le16 duration; + u8 map; +} __packed; + +enum { /* ieee80211_measurement_request.mode */ + /* Bit 0 is reserved */ + IEEE80211_MEASUREMENT_ENABLE = (1 << 1), + IEEE80211_MEASUREMENT_REQUEST = (1 << 2), + IEEE80211_MEASUREMENT_REPORT = (1 << 3), + /* Bits 4-7 are reserved */ +}; + +enum { + IEEE80211_REPORT_BASIC = 0, /* required */ + IEEE80211_REPORT_CCA = 1, /* optional */ + IEEE80211_REPORT_RPI = 2, /* optional */ + /* 3-255 reserved */ +}; + +struct ieee80211_measurement_params { + u8 channel; + __le64 start_time; + __le16 duration; +} __packed; + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __packed; + +struct ieee80211_measurement_request { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + struct ieee80211_measurement_params params[0]; +} __packed; + +struct ieee80211_measurement_report { + struct ieee80211_info_element ie; + u8 token; + u8 mode; + u8 type; + union { + struct ieee80211_basic_report basic[0]; + } u; +} __packed; + +#endif diff --git a/drivers/net/wireless/iwlegacy/iwl-sta.c b/drivers/net/wireless/iwlegacy/iwl-sta.c new file mode 100644 index 00000000000..47c9da3834e --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-sta.c @@ -0,0 +1,816 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <net/mac80211.h> +#include <linux/etherdevice.h> +#include <linux/sched.h> +#include <linux/lockdep.h> + +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" + +/* priv->sta_lock must be held */ +static void iwl_legacy_sta_ucode_activate(struct iwl_priv *priv, u8 sta_id) +{ + + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) + IWL_ERR(priv, + "ACTIVATE a non DRIVER active station id %u addr %pM\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + + if (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_ASSOC(priv, + "STA id %u addr %pM already present" + " in uCode (according to driver)\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } else { + priv->stations[sta_id].used |= IWL_STA_UCODE_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Added STA id %u addr %pM to uCode\n", + sta_id, priv->stations[sta_id].sta.sta.addr); + } +} + +static int iwl_legacy_process_add_sta_resp(struct iwl_priv *priv, + struct iwl_legacy_addsta_cmd *addsta, + struct iwl_rx_packet *pkt, + bool sync) +{ + u8 sta_id = addsta->sta.sta_id; + unsigned long flags; + int ret = -EIO; + + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(priv, "Bad return from REPLY_ADD_STA (0x%08X)\n", + pkt->hdr.flags); + return ret; + } + + IWL_DEBUG_INFO(priv, "Processing response for adding station %u\n", + sta_id); + + spin_lock_irqsave(&priv->sta_lock, flags); + + switch (pkt->u.add_sta.status) { + case ADD_STA_SUCCESS_MSK: + IWL_DEBUG_INFO(priv, "REPLY_ADD_STA PASSED\n"); + iwl_legacy_sta_ucode_activate(priv, sta_id); + ret = 0; + break; + case ADD_STA_NO_ROOM_IN_TABLE: + IWL_ERR(priv, "Adding station %d failed, no room in table.\n", + sta_id); + break; + case ADD_STA_NO_BLOCK_ACK_RESOURCE: + IWL_ERR(priv, + "Adding station %d failed, no block ack resource.\n", + sta_id); + break; + case ADD_STA_MODIFY_NON_EXIST_STA: + IWL_ERR(priv, "Attempting to modify non-existing station %d\n", + sta_id); + break; + default: + IWL_DEBUG_ASSOC(priv, "Received REPLY_ADD_STA:(0x%08X)\n", + pkt->u.add_sta.status); + break; + } + + IWL_DEBUG_INFO(priv, "%s station id %u addr %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + sta_id, priv->stations[sta_id].sta.sta.addr); + + /* + * XXX: The MAC address in the command buffer is often changed from + * the original sent to the device. That is, the MAC address + * written to the command buffer often is not the same MAC adress + * read from the command buffer when the command returns. This + * issue has not yet been resolved and this debugging is left to + * observe the problem. + */ + IWL_DEBUG_INFO(priv, "%s station according to cmd buffer %pM\n", + priv->stations[sta_id].sta.mode == + STA_CONTROL_MODIFY_MSK ? "Modified" : "Added", + addsta->sta.addr); + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return ret; +} + +static void iwl_legacy_add_sta_callback(struct iwl_priv *priv, + struct iwl_device_cmd *cmd, + struct iwl_rx_packet *pkt) +{ + struct iwl_legacy_addsta_cmd *addsta = + (struct iwl_legacy_addsta_cmd *)cmd->cmd.payload; + + iwl_legacy_process_add_sta_resp(priv, addsta, pkt, false); + +} + +int iwl_legacy_send_add_sta(struct iwl_priv *priv, + struct iwl_legacy_addsta_cmd *sta, u8 flags) +{ + struct iwl_rx_packet *pkt = NULL; + int ret = 0; + u8 data[sizeof(*sta)]; + struct iwl_host_cmd cmd = { + .id = REPLY_ADD_STA, + .flags = flags, + .data = data, + }; + u8 sta_id __maybe_unused = sta->sta.sta_id; + + IWL_DEBUG_INFO(priv, "Adding sta %u (%pM) %ssynchronously\n", + sta_id, sta->sta.addr, flags & CMD_ASYNC ? "a" : ""); + + if (flags & CMD_ASYNC) + cmd.callback = iwl_legacy_add_sta_callback; + else { + cmd.flags |= CMD_WANT_SKB; + might_sleep(); + } + + cmd.len = priv->cfg->ops->utils->build_addsta_hcmd(sta, data); + ret = iwl_legacy_send_cmd(priv, &cmd); + + if (ret || (flags & CMD_ASYNC)) + return ret; + + if (ret == 0) { + pkt = (struct iwl_rx_packet *)cmd.reply_page; + ret = iwl_legacy_process_add_sta_resp(priv, sta, pkt, true); + } + iwl_legacy_free_pages(priv, cmd.reply_page); + + return ret; +} +EXPORT_SYMBOL(iwl_legacy_send_add_sta); + +static void iwl_legacy_set_ht_add_station(struct iwl_priv *priv, u8 index, + struct ieee80211_sta *sta, + struct iwl_rxon_context *ctx) +{ + struct ieee80211_sta_ht_cap *sta_ht_inf = &sta->ht_cap; + __le32 sta_flags; + u8 mimo_ps_mode; + + if (!sta || !sta_ht_inf->ht_supported) + goto done; + + mimo_ps_mode = (sta_ht_inf->cap & IEEE80211_HT_CAP_SM_PS) >> 2; + IWL_DEBUG_ASSOC(priv, "spatial multiplexing power save mode: %s\n", + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_STATIC) ? + "static" : + (mimo_ps_mode == WLAN_HT_CAP_SM_PS_DYNAMIC) ? + "dynamic" : "disabled"); + + sta_flags = priv->stations[index].sta.station_flags; + + sta_flags &= ~(STA_FLG_RTS_MIMO_PROT_MSK | STA_FLG_MIMO_DIS_MSK); + + switch (mimo_ps_mode) { + case WLAN_HT_CAP_SM_PS_STATIC: + sta_flags |= STA_FLG_MIMO_DIS_MSK; + break; + case WLAN_HT_CAP_SM_PS_DYNAMIC: + sta_flags |= STA_FLG_RTS_MIMO_PROT_MSK; + break; + case WLAN_HT_CAP_SM_PS_DISABLED: + break; + default: + IWL_WARN(priv, "Invalid MIMO PS mode %d\n", mimo_ps_mode); + break; + } + + sta_flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_factor << STA_FLG_MAX_AGG_SIZE_POS); + + sta_flags |= cpu_to_le32( + (u32)sta_ht_inf->ampdu_density << STA_FLG_AGG_MPDU_DENSITY_POS); + + if (iwl_legacy_is_ht40_tx_allowed(priv, ctx, &sta->ht_cap)) + sta_flags |= STA_FLG_HT40_EN_MSK; + else + sta_flags &= ~STA_FLG_HT40_EN_MSK; + + priv->stations[index].sta.station_flags = sta_flags; + done: + return; +} + +/** + * iwl_legacy_prep_station - Prepare station information for addition + * + * should be called with sta_lock held + */ +u8 iwl_legacy_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, struct ieee80211_sta *sta) +{ + struct iwl_station_entry *station; + int i; + u8 sta_id = IWL_INVALID_STATION; + u16 rate; + + if (is_ap) + sta_id = ctx->ap_sta_id; + else if (is_broadcast_ether_addr(addr)) + sta_id = ctx->bcast_sta_id; + else + for (i = IWL_STA_ID; i < priv->hw_params.max_stations; i++) { + if (!compare_ether_addr(priv->stations[i].sta.sta.addr, + addr)) { + sta_id = i; + break; + } + + if (!priv->stations[i].used && + sta_id == IWL_INVALID_STATION) + sta_id = i; + } + + /* + * These two conditions have the same outcome, but keep them + * separate + */ + if (unlikely(sta_id == IWL_INVALID_STATION)) + return sta_id; + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, + "STA %d already in process of being added.\n", + sta_id); + return sta_id; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE) && + !compare_ether_addr(priv->stations[sta_id].sta.sta.addr, addr)) { + IWL_DEBUG_ASSOC(priv, + "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + return sta_id; + } + + station = &priv->stations[sta_id]; + station->used = IWL_STA_DRIVER_ACTIVE; + IWL_DEBUG_ASSOC(priv, "Add STA to driver ID %d: %pM\n", + sta_id, addr); + priv->num_stations++; + + /* Set up the REPLY_ADD_STA command to send to device */ + memset(&station->sta, 0, sizeof(struct iwl_legacy_addsta_cmd)); + memcpy(station->sta.sta.addr, addr, ETH_ALEN); + station->sta.mode = 0; + station->sta.sta.sta_id = sta_id; + station->sta.station_flags = ctx->station_flags; + station->ctxid = ctx->ctxid; + + if (sta) { + struct iwl_station_priv_common *sta_priv; + + sta_priv = (void *)sta->drv_priv; + sta_priv->ctx = ctx; + } + + /* + * OK to call unconditionally, since local stations (IBSS BSSID + * STA and broadcast STA) pass in a NULL sta, and mac80211 + * doesn't allow HT IBSS. + */ + iwl_legacy_set_ht_add_station(priv, sta_id, sta, ctx); + + /* 3945 only */ + rate = (priv->band == IEEE80211_BAND_5GHZ) ? + IWL_RATE_6M_PLCP : IWL_RATE_1M_PLCP; + /* Turn on both antennas for the station... */ + station->sta.rate_n_flags = cpu_to_le16(rate | RATE_MCS_ANT_AB_MSK); + + return sta_id; + +} +EXPORT_SYMBOL_GPL(iwl_legacy_prep_station); + +#define STA_WAIT_TIMEOUT (HZ/2) + +/** + * iwl_legacy_add_station_common - + */ +int +iwl_legacy_add_station_common(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r) +{ + unsigned long flags_spin; + int ret = 0; + u8 sta_id; + struct iwl_legacy_addsta_cmd sta_cmd; + + *sta_id_r = 0; + spin_lock_irqsave(&priv->sta_lock, flags_spin); + sta_id = iwl_legacy_prep_station(priv, ctx, addr, is_ap, sta); + if (sta_id == IWL_INVALID_STATION) { + IWL_ERR(priv, "Unable to prepare station %pM for addition\n", + addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EINVAL; + } + + /* + * uCode is not able to deal with multiple requests to add a + * station. Keep track if one is in progress so that we do not send + * another. + */ + if (priv->stations[sta_id].used & IWL_STA_UCODE_INPROGRESS) { + IWL_DEBUG_INFO(priv, + "STA %d already in process of being added.\n", + sta_id); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } + + if ((priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE) && + (priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, + "STA %d (%pM) already added, not adding again.\n", + sta_id, addr); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EEXIST; + } + + priv->stations[sta_id].used |= IWL_STA_UCODE_INPROGRESS; + memcpy(&sta_cmd, &priv->stations[sta_id].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + /* Add station to device's station table */ + ret = iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_irqsave(&priv->sta_lock, flags_spin); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[sta_id].sta.sta.addr); + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + priv->stations[sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + *sta_id_r = sta_id; + return ret; +} +EXPORT_SYMBOL(iwl_legacy_add_station_common); + +/** + * iwl_legacy_sta_ucode_deactivate - deactivate ucode status for a station + * + * priv->sta_lock must be held + */ +static void iwl_legacy_sta_ucode_deactivate(struct iwl_priv *priv, u8 sta_id) +{ + /* Ucode must be active and driver must be non active */ + if ((priv->stations[sta_id].used & + (IWL_STA_UCODE_ACTIVE | IWL_STA_DRIVER_ACTIVE)) != + IWL_STA_UCODE_ACTIVE) + IWL_ERR(priv, "removed non active STA %u\n", sta_id); + + priv->stations[sta_id].used &= ~IWL_STA_UCODE_ACTIVE; + + memset(&priv->stations[sta_id], 0, sizeof(struct iwl_station_entry)); + IWL_DEBUG_ASSOC(priv, "Removed STA %u\n", sta_id); +} + +static int iwl_legacy_send_remove_station(struct iwl_priv *priv, + const u8 *addr, int sta_id, + bool temporary) +{ + struct iwl_rx_packet *pkt; + int ret; + + unsigned long flags_spin; + struct iwl_rem_sta_cmd rm_sta_cmd; + + struct iwl_host_cmd cmd = { + .id = REPLY_REMOVE_STA, + .len = sizeof(struct iwl_rem_sta_cmd), + .flags = CMD_SYNC, + .data = &rm_sta_cmd, + }; + + memset(&rm_sta_cmd, 0, sizeof(rm_sta_cmd)); + rm_sta_cmd.num_sta = 1; + memcpy(&rm_sta_cmd.addr, addr, ETH_ALEN); + + cmd.flags |= CMD_WANT_SKB; + + ret = iwl_legacy_send_cmd(priv, &cmd); + + if (ret) + return ret; + + pkt = (struct iwl_rx_packet *)cmd.reply_page; + if (pkt->hdr.flags & IWL_CMD_FAILED_MSK) { + IWL_ERR(priv, "Bad return from REPLY_REMOVE_STA (0x%08X)\n", + pkt->hdr.flags); + ret = -EIO; + } + + if (!ret) { + switch (pkt->u.rem_sta.status) { + case REM_STA_SUCCESS_MSK: + if (!temporary) { + spin_lock_irqsave(&priv->sta_lock, flags_spin); + iwl_legacy_sta_ucode_deactivate(priv, sta_id); + spin_unlock_irqrestore(&priv->sta_lock, + flags_spin); + } + IWL_DEBUG_ASSOC(priv, "REPLY_REMOVE_STA PASSED\n"); + break; + default: + ret = -EIO; + IWL_ERR(priv, "REPLY_REMOVE_STA failed\n"); + break; + } + } + iwl_legacy_free_pages(priv, cmd.reply_page); + + return ret; +} + +/** + * iwl_legacy_remove_station - Remove driver's knowledge of station. + */ +int iwl_legacy_remove_station(struct iwl_priv *priv, const u8 sta_id, + const u8 *addr) +{ + unsigned long flags; + + if (!iwl_legacy_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Unable to remove station %pM, device not ready.\n", + addr); + /* + * It is typical for stations to be removed when we are + * going down. Return success since device will be down + * soon anyway + */ + return 0; + } + + IWL_DEBUG_ASSOC(priv, "Removing STA from driver:%d %pM\n", + sta_id, addr); + + if (WARN_ON(sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + spin_lock_irqsave(&priv->sta_lock, flags); + + if (!(priv->stations[sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non DRIVER active\n", + addr); + goto out_err; + } + + if (!(priv->stations[sta_id].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_INFO(priv, "Removing %pM but non UCODE active\n", + addr); + goto out_err; + } + + if (priv->stations[sta_id].used & IWL_STA_LOCAL) { + kfree(priv->stations[sta_id].lq); + priv->stations[sta_id].lq = NULL; + } + + priv->stations[sta_id].used &= ~IWL_STA_DRIVER_ACTIVE; + + priv->num_stations--; + + BUG_ON(priv->num_stations < 0); + + spin_unlock_irqrestore(&priv->sta_lock, flags); + + return iwl_legacy_send_remove_station(priv, addr, sta_id, false); +out_err: + spin_unlock_irqrestore(&priv->sta_lock, flags); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(iwl_legacy_remove_station); + +/** + * iwl_legacy_clear_ucode_stations - clear ucode station table bits + * + * This function clears all the bits in the driver indicating + * which stations are active in the ucode. Call when something + * other than explicit station management would cause this in + * the ucode, e.g. unassociated RXON. + */ +void iwl_legacy_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx) +{ + int i; + unsigned long flags_spin; + bool cleared = false; + + IWL_DEBUG_INFO(priv, "Clearing ucode stations in driver\n"); + + spin_lock_irqsave(&priv->sta_lock, flags_spin); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if (ctx && ctx->ctxid != priv->stations[i].ctxid) + continue; + + if (priv->stations[i].used & IWL_STA_UCODE_ACTIVE) { + IWL_DEBUG_INFO(priv, + "Clearing ucode active for station %d\n", i); + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + cleared = true; + } + } + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + if (!cleared) + IWL_DEBUG_INFO(priv, + "No active stations found to be cleared\n"); +} +EXPORT_SYMBOL(iwl_legacy_clear_ucode_stations); + +/** + * iwl_legacy_restore_stations() - Restore driver known stations to device + * + * All stations considered active by driver, but not present in ucode, is + * restored. + * + * Function sleeps. + */ +void +iwl_legacy_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) +{ + struct iwl_legacy_addsta_cmd sta_cmd; + struct iwl_link_quality_cmd lq; + unsigned long flags_spin; + int i; + bool found = false; + int ret; + bool send_lq; + + if (!iwl_legacy_is_ready(priv)) { + IWL_DEBUG_INFO(priv, + "Not ready yet, not restoring any stations.\n"); + return; + } + + IWL_DEBUG_ASSOC(priv, "Restoring all known stations ... start.\n"); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if (ctx->ctxid != priv->stations[i].ctxid) + continue; + if ((priv->stations[i].used & IWL_STA_DRIVER_ACTIVE) && + !(priv->stations[i].used & IWL_STA_UCODE_ACTIVE)) { + IWL_DEBUG_ASSOC(priv, "Restoring sta %pM\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].sta.mode = 0; + priv->stations[i].used |= IWL_STA_UCODE_INPROGRESS; + found = true; + } + } + + for (i = 0; i < priv->hw_params.max_stations; i++) { + if ((priv->stations[i].used & IWL_STA_UCODE_INPROGRESS)) { + memcpy(&sta_cmd, &priv->stations[i].sta, + sizeof(struct iwl_legacy_addsta_cmd)); + send_lq = false; + if (priv->stations[i].lq) { + memcpy(&lq, priv->stations[i].lq, + sizeof(struct iwl_link_quality_cmd)); + send_lq = true; + } + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + ret = iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); + if (ret) { + spin_lock_irqsave(&priv->sta_lock, flags_spin); + IWL_ERR(priv, "Adding station %pM failed.\n", + priv->stations[i].sta.sta.addr); + priv->stations[i].used &= + ~IWL_STA_DRIVER_ACTIVE; + priv->stations[i].used &= + ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, + flags_spin); + } + /* + * Rate scaling has already been initialized, send + * current LQ command + */ + if (send_lq) + iwl_legacy_send_lq_cmd(priv, ctx, &lq, + CMD_SYNC, true); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[i].used &= ~IWL_STA_UCODE_INPROGRESS; + } + } + + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + if (!found) + IWL_DEBUG_INFO(priv, "Restoring all known stations" + " .... no stations to be restored.\n"); + else + IWL_DEBUG_INFO(priv, "Restoring all known stations" + " .... complete.\n"); +} +EXPORT_SYMBOL(iwl_legacy_restore_stations); + +int iwl_legacy_get_free_ucode_key_index(struct iwl_priv *priv) +{ + int i; + + for (i = 0; i < priv->sta_key_max_num; i++) + if (!test_and_set_bit(i, &priv->ucode_key_table)) + return i; + + return WEP_INVALID_OFFSET; +} +EXPORT_SYMBOL(iwl_legacy_get_free_ucode_key_index); + +void iwl_legacy_dealloc_bcast_stations(struct iwl_priv *priv) +{ + unsigned long flags; + int i; + + spin_lock_irqsave(&priv->sta_lock, flags); + for (i = 0; i < priv->hw_params.max_stations; i++) { + if (!(priv->stations[i].used & IWL_STA_BCAST)) + continue; + + priv->stations[i].used &= ~IWL_STA_UCODE_ACTIVE; + priv->num_stations--; + BUG_ON(priv->num_stations < 0); + kfree(priv->stations[i].lq); + priv->stations[i].lq = NULL; + } + spin_unlock_irqrestore(&priv->sta_lock, flags); +} +EXPORT_SYMBOL_GPL(iwl_legacy_dealloc_bcast_stations); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +static void iwl_legacy_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ + int i; + IWL_DEBUG_RATE(priv, "lq station id 0x%x\n", lq->sta_id); + IWL_DEBUG_RATE(priv, "lq ant 0x%X 0x%X\n", + lq->general_params.single_stream_ant_msk, + lq->general_params.dual_stream_ant_msk); + + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) + IWL_DEBUG_RATE(priv, "lq index %d 0x%X\n", + i, lq->rs_table[i].rate_n_flags); +} +#else +static inline void iwl_legacy_dump_lq_cmd(struct iwl_priv *priv, + struct iwl_link_quality_cmd *lq) +{ +} +#endif + +/** + * iwl_legacy_is_lq_table_valid() - Test one aspect of LQ cmd for validity + * + * It sometimes happens when a HT rate has been in use and we + * loose connectivity with AP then mac80211 will first tell us that the + * current channel is not HT anymore before removing the station. In such a + * scenario the RXON flags will be updated to indicate we are not + * communicating HT anymore, but the LQ command may still contain HT rates. + * Test for this to prevent driver from sending LQ command between the time + * RXON flags are updated and when LQ command is updated. + */ +static bool iwl_legacy_is_lq_table_valid(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq) +{ + int i; + + if (ctx->ht.enabled) + return true; + + IWL_DEBUG_INFO(priv, "Channel %u is not an HT channel\n", + ctx->active.channel); + for (i = 0; i < LINK_QUAL_MAX_RETRY_NUM; i++) { + if (le32_to_cpu(lq->rs_table[i].rate_n_flags) & + RATE_MCS_HT_MSK) { + IWL_DEBUG_INFO(priv, + "index %d of LQ expects HT channel\n", + i); + return false; + } + } + return true; +} + +/** + * iwl_legacy_send_lq_cmd() - Send link quality command + * @init: This command is sent as part of station initialization right + * after station has been added. + * + * The link quality command is sent as the last step of station creation. + * This is the special case in which init is set and we call a callback in + * this case to clear the state indicating that station creation is in + * progress. + */ +int iwl_legacy_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, u8 flags, bool init) +{ + int ret = 0; + unsigned long flags_spin; + + struct iwl_host_cmd cmd = { + .id = REPLY_TX_LINK_QUALITY_CMD, + .len = sizeof(struct iwl_link_quality_cmd), + .flags = flags, + .data = lq, + }; + + if (WARN_ON(lq->sta_id == IWL_INVALID_STATION)) + return -EINVAL; + + + spin_lock_irqsave(&priv->sta_lock, flags_spin); + if (!(priv->stations[lq->sta_id].used & IWL_STA_DRIVER_ACTIVE)) { + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + return -EINVAL; + } + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + + iwl_legacy_dump_lq_cmd(priv, lq); + BUG_ON(init && (cmd.flags & CMD_ASYNC)); + + if (iwl_legacy_is_lq_table_valid(priv, ctx, lq)) + ret = iwl_legacy_send_cmd(priv, &cmd); + else + ret = -EINVAL; + + if (cmd.flags & CMD_ASYNC) + return ret; + + if (init) { + IWL_DEBUG_INFO(priv, "init LQ command complete," + " clearing sta addition status for sta %d\n", + lq->sta_id); + spin_lock_irqsave(&priv->sta_lock, flags_spin); + priv->stations[lq->sta_id].used &= ~IWL_STA_UCODE_INPROGRESS; + spin_unlock_irqrestore(&priv->sta_lock, flags_spin); + } + return ret; +} +EXPORT_SYMBOL(iwl_legacy_send_lq_cmd); + +int iwl_legacy_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv_common *sta_common = (void *)sta->drv_priv; + int ret; + + IWL_DEBUG_INFO(priv, "received request to remove station %pM\n", + sta->addr); + mutex_lock(&priv->mutex); + IWL_DEBUG_INFO(priv, "proceeding to remove station %pM\n", + sta->addr); + ret = iwl_legacy_remove_station(priv, sta_common->sta_id, sta->addr); + if (ret) + IWL_ERR(priv, "Error removing station %pM\n", + sta->addr); + mutex_unlock(&priv->mutex); + return ret; +} +EXPORT_SYMBOL(iwl_legacy_mac_sta_remove); diff --git a/drivers/net/wireless/iwlegacy/iwl-sta.h b/drivers/net/wireless/iwlegacy/iwl-sta.h new file mode 100644 index 00000000000..67bd75fe01a --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-sta.h @@ -0,0 +1,148 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ +#ifndef __iwl_legacy_sta_h__ +#define __iwl_legacy_sta_h__ + +#include "iwl-dev.h" + +#define HW_KEY_DYNAMIC 0 +#define HW_KEY_DEFAULT 1 + +#define IWL_STA_DRIVER_ACTIVE BIT(0) /* driver entry is active */ +#define IWL_STA_UCODE_ACTIVE BIT(1) /* ucode entry is active */ +#define IWL_STA_UCODE_INPROGRESS BIT(2) /* ucode entry is in process of + being activated */ +#define IWL_STA_LOCAL BIT(3) /* station state not directed by mac80211; + (this is for the IBSS BSSID stations) */ +#define IWL_STA_BCAST BIT(4) /* this station is the special bcast station */ + + +void iwl_legacy_restore_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +void iwl_legacy_clear_ucode_stations(struct iwl_priv *priv, + struct iwl_rxon_context *ctx); +void iwl_legacy_dealloc_bcast_stations(struct iwl_priv *priv); +int iwl_legacy_get_free_ucode_key_index(struct iwl_priv *priv); +int iwl_legacy_send_add_sta(struct iwl_priv *priv, + struct iwl_legacy_addsta_cmd *sta, u8 flags); +int iwl_legacy_add_station_common(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta, u8 *sta_id_r); +int iwl_legacy_remove_station(struct iwl_priv *priv, + const u8 sta_id, + const u8 *addr); +int iwl_legacy_mac_sta_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +u8 iwl_legacy_prep_station(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + const u8 *addr, bool is_ap, + struct ieee80211_sta *sta); + +int iwl_legacy_send_lq_cmd(struct iwl_priv *priv, + struct iwl_rxon_context *ctx, + struct iwl_link_quality_cmd *lq, + u8 flags, bool init); + +/** + * iwl_legacy_clear_driver_stations - clear knowledge of all stations from driver + * @priv: iwl priv struct + * + * This is called during iwl_down() to make sure that in the case + * we're coming there from a hardware restart mac80211 will be + * able to reconfigure stations -- if we're getting there in the + * normal down flow then the stations will already be cleared. + */ +static inline void iwl_legacy_clear_driver_stations(struct iwl_priv *priv) +{ + unsigned long flags; + struct iwl_rxon_context *ctx; + + spin_lock_irqsave(&priv->sta_lock, flags); + memset(priv->stations, 0, sizeof(priv->stations)); + priv->num_stations = 0; + + priv->ucode_key_table = 0; + + for_each_context(priv, ctx) { + /* + * Remove all key information that is not stored as part + * of station information since mac80211 may not have had + * a chance to remove all the keys. When device is + * reconfigured by mac80211 after an error all keys will + * be reconfigured. + */ + memset(ctx->wep_keys, 0, sizeof(ctx->wep_keys)); + ctx->key_mapping_keys = 0; + } + + spin_unlock_irqrestore(&priv->sta_lock, flags); +} + +static inline int iwl_legacy_sta_id(struct ieee80211_sta *sta) +{ + if (WARN_ON(!sta)) + return IWL_INVALID_STATION; + + return ((struct iwl_station_priv_common *)sta->drv_priv)->sta_id; +} + +/** + * iwl_legacy_sta_id_or_broadcast - return sta_id or broadcast sta + * @priv: iwl priv + * @context: the current context + * @sta: mac80211 station + * + * In certain circumstances mac80211 passes a station pointer + * that may be %NULL, for example during TX or key setup. In + * that case, we need to use the broadcast station, so this + * inline wraps that pattern. + */ +static inline int iwl_legacy_sta_id_or_broadcast(struct iwl_priv *priv, + struct iwl_rxon_context *context, + struct ieee80211_sta *sta) +{ + int sta_id; + + if (!sta) + return context->bcast_sta_id; + + sta_id = iwl_legacy_sta_id(sta); + + /* + * mac80211 should not be passing a partially + * initialised station! + */ + WARN_ON(sta_id == IWL_INVALID_STATION); + + return sta_id; +} +#endif /* __iwl_legacy_sta_h__ */ diff --git a/drivers/net/wireless/iwlegacy/iwl-tx.c b/drivers/net/wireless/iwlegacy/iwl-tx.c new file mode 100644 index 00000000000..a227773cb38 --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl-tx.c @@ -0,0 +1,660 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#include <linux/etherdevice.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <net/mac80211.h> +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-sta.h" +#include "iwl-io.h" +#include "iwl-helpers.h" + +/** + * iwl_legacy_txq_update_write_ptr - Send new write index to hardware + */ +void +iwl_legacy_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) +{ + u32 reg = 0; + int txq_id = txq->q.id; + + if (txq->need_update == 0) + return; + + /* if we're trying to save power */ + if (test_bit(STATUS_POWER_PMI, &priv->status)) { + /* wake up nic if it's powered down ... + * uCode will wake up, and interrupt us again, so next + * time we'll skip this part. */ + reg = iwl_read32(priv, CSR_UCODE_DRV_GP1); + + if (reg & CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP) { + IWL_DEBUG_INFO(priv, + "Tx queue %d requesting wakeup," + " GP1 = 0x%x\n", txq_id, reg); + iwl_legacy_set_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + return; + } + + iwl_legacy_write_direct32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + + /* + * else not in power-save mode, + * uCode will never sleep when we're + * trying to tx (during RFKILL, we're not trying to tx). + */ + } else + iwl_write32(priv, HBUS_TARG_WRPTR, + txq->q.write_ptr | (txq_id << 8)); + txq->need_update = 0; +} +EXPORT_SYMBOL(iwl_legacy_txq_update_write_ptr); + +/** + * iwl_legacy_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's + */ +void iwl_legacy_tx_queue_unmap(struct iwl_priv *priv, int txq_id) +{ + struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct iwl_queue *q = &txq->q; + + if (q->n_bd == 0) + return; + + while (q->write_ptr != q->read_ptr) { + priv->cfg->ops->lib->txq_free_tfd(priv, txq); + q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd); + } +} +EXPORT_SYMBOL(iwl_legacy_tx_queue_unmap); + +/** + * iwl_legacy_tx_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void iwl_legacy_tx_queue_free(struct iwl_priv *priv, int txq_id) +{ + struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct device *dev = &priv->pci_dev->dev; + int i; + + iwl_legacy_tx_queue_unmap(priv, txq_id); + + /* De-alloc array of command/tx buffers */ + for (i = 0; i < TFD_TX_CMD_SLOTS; i++) + kfree(txq->cmd[i]); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + dma_free_coherent(dev, priv->hw_params.tfd_size * + txq->q.n_bd, txq->tfds, txq->q.dma_addr); + + /* De-alloc array of per-TFD driver data */ + kfree(txq->txb); + txq->txb = NULL; + + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} +EXPORT_SYMBOL(iwl_legacy_tx_queue_free); + +/** + * iwl_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue + */ +void iwl_legacy_cmd_queue_unmap(struct iwl_priv *priv) +{ + struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; + struct iwl_queue *q = &txq->q; + bool huge = false; + int i; + + if (q->n_bd == 0) + return; + + while (q->read_ptr != q->write_ptr) { + /* we have no way to tell if it is a huge cmd ATM */ + i = iwl_legacy_get_cmd_index(q, q->read_ptr, 0); + + if (txq->meta[i].flags & CMD_SIZE_HUGE) + huge = true; + else + pci_unmap_single(priv->pci_dev, + dma_unmap_addr(&txq->meta[i], mapping), + dma_unmap_len(&txq->meta[i], len), + PCI_DMA_BIDIRECTIONAL); + + q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd); + } + + if (huge) { + i = q->n_window; + pci_unmap_single(priv->pci_dev, + dma_unmap_addr(&txq->meta[i], mapping), + dma_unmap_len(&txq->meta[i], len), + PCI_DMA_BIDIRECTIONAL); + } +} +EXPORT_SYMBOL(iwl_legacy_cmd_queue_unmap); + +/** + * iwl_legacy_cmd_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void iwl_legacy_cmd_queue_free(struct iwl_priv *priv) +{ + struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; + struct device *dev = &priv->pci_dev->dev; + int i; + + iwl_legacy_cmd_queue_unmap(priv); + + /* De-alloc array of command/tx buffers */ + for (i = 0; i <= TFD_CMD_SLOTS; i++) + kfree(txq->cmd[i]); + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) + dma_free_coherent(dev, priv->hw_params.tfd_size * txq->q.n_bd, + txq->tfds, txq->q.dma_addr); + + /* deallocate arrays */ + kfree(txq->cmd); + kfree(txq->meta); + txq->cmd = NULL; + txq->meta = NULL; + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} +EXPORT_SYMBOL(iwl_legacy_cmd_queue_free); + +/*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** + * DMA services + * + * Theory of operation + * + * A Tx or Rx queue resides in host DRAM, and is comprised of a circular buffer + * of buffer descriptors, each of which points to one or more data buffers for + * the device to read from or fill. Driver and device exchange status of each + * queue via "read" and "write" pointers. Driver keeps minimum of 2 empty + * entries in each circular buffer, to protect against confusing empty and full + * queue states. + * + * The device reads or writes the data in the queues via the device's several + * DMA/FIFO channels. Each queue is mapped to a single DMA channel. + * + * For Tx queue, there are low mark and high mark limits. If, after queuing + * the packet for Tx, free space become < low mark, Tx queue stopped. When + * reclaiming packets (on 'tx done IRQ), if free space become > high mark, + * Tx queue resumed. + * + * See more detailed info in iwl-4965-hw.h. + ***************************************************/ + +int iwl_legacy_queue_space(const struct iwl_queue *q) +{ + int s = q->read_ptr - q->write_ptr; + + if (q->read_ptr > q->write_ptr) + s -= q->n_bd; + + if (s <= 0) + s += q->n_window; + /* keep some reserve to not confuse empty and full situations */ + s -= 2; + if (s < 0) + s = 0; + return s; +} +EXPORT_SYMBOL(iwl_legacy_queue_space); + + +/** + * iwl_legacy_queue_init - Initialize queue's high/low-water and read/write indexes + */ +static int iwl_legacy_queue_init(struct iwl_priv *priv, struct iwl_queue *q, + int count, int slots_num, u32 id) +{ + q->n_bd = count; + q->n_window = slots_num; + q->id = id; + + /* count must be power-of-two size, otherwise iwl_legacy_queue_inc_wrap + * and iwl_legacy_queue_dec_wrap are broken. */ + BUG_ON(!is_power_of_2(count)); + + /* slots_num must be power-of-two size, otherwise + * iwl_legacy_get_cmd_index is broken. */ + BUG_ON(!is_power_of_2(slots_num)); + + q->low_mark = q->n_window / 4; + if (q->low_mark < 4) + q->low_mark = 4; + + q->high_mark = q->n_window / 8; + if (q->high_mark < 2) + q->high_mark = 2; + + q->write_ptr = q->read_ptr = 0; + + return 0; +} + +/** + * iwl_legacy_tx_queue_alloc - Alloc driver data and TFD CB for one Tx/cmd queue + */ +static int iwl_legacy_tx_queue_alloc(struct iwl_priv *priv, + struct iwl_tx_queue *txq, u32 id) +{ + struct device *dev = &priv->pci_dev->dev; + size_t tfd_sz = priv->hw_params.tfd_size * TFD_QUEUE_SIZE_MAX; + + /* Driver private data, only for Tx (not command) queues, + * not shared with device. */ + if (id != priv->cmd_queue) { + txq->txb = kzalloc(sizeof(txq->txb[0]) * + TFD_QUEUE_SIZE_MAX, GFP_KERNEL); + if (!txq->txb) { + IWL_ERR(priv, "kmalloc for auxiliary BD " + "structures failed\n"); + goto error; + } + } else { + txq->txb = NULL; + } + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = dma_alloc_coherent(dev, tfd_sz, &txq->q.dma_addr, + GFP_KERNEL); + if (!txq->tfds) { + IWL_ERR(priv, "pci_alloc_consistent(%zd) failed\n", tfd_sz); + goto error; + } + txq->q.id = id; + + return 0; + + error: + kfree(txq->txb); + txq->txb = NULL; + + return -ENOMEM; +} + +/** + * iwl_legacy_tx_queue_init - Allocate and initialize one tx/cmd queue + */ +int iwl_legacy_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, + int slots_num, u32 txq_id) +{ + int i, len; + int ret; + int actual_slots = slots_num; + + /* + * Alloc buffer array for commands (Tx or other types of commands). + * For the command queue (#4/#9), allocate command space + one big + * command for scan, since scan command is very huge; the system will + * not have two scans at the same time, so only one is needed. + * For normal Tx queues (all other queues), no super-size command + * space is needed. + */ + if (txq_id == priv->cmd_queue) + actual_slots++; + + txq->meta = kzalloc(sizeof(struct iwl_cmd_meta) * actual_slots, + GFP_KERNEL); + txq->cmd = kzalloc(sizeof(struct iwl_device_cmd *) * actual_slots, + GFP_KERNEL); + + if (!txq->meta || !txq->cmd) + goto out_free_arrays; + + len = sizeof(struct iwl_device_cmd); + for (i = 0; i < actual_slots; i++) { + /* only happens for cmd queue */ + if (i == slots_num) + len = IWL_MAX_CMD_SIZE; + + txq->cmd[i] = kmalloc(len, GFP_KERNEL); + if (!txq->cmd[i]) + goto err; + } + + /* Alloc driver data array and TFD circular buffer */ + ret = iwl_legacy_tx_queue_alloc(priv, txq, txq_id); + if (ret) + goto err; + + txq->need_update = 0; + + /* + * For the default queues 0-3, set up the swq_id + * already -- all others need to get one later + * (if they need one at all). + */ + if (txq_id < 4) + iwl_legacy_set_swq_id(txq, txq_id, txq_id); + + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * iwl_legacy_queue_inc_wrap and iwl_legacy_queue_dec_wrap are broken. */ + BUILD_BUG_ON(TFD_QUEUE_SIZE_MAX & (TFD_QUEUE_SIZE_MAX - 1)); + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + iwl_legacy_queue_init(priv, &txq->q, + TFD_QUEUE_SIZE_MAX, slots_num, txq_id); + + /* Tell device where to find queue */ + priv->cfg->ops->lib->txq_init(priv, txq); + + return 0; +err: + for (i = 0; i < actual_slots; i++) + kfree(txq->cmd[i]); +out_free_arrays: + kfree(txq->meta); + kfree(txq->cmd); + + return -ENOMEM; +} +EXPORT_SYMBOL(iwl_legacy_tx_queue_init); + +void iwl_legacy_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, + int slots_num, u32 txq_id) +{ + int actual_slots = slots_num; + + if (txq_id == priv->cmd_queue) + actual_slots++; + + memset(txq->meta, 0, sizeof(struct iwl_cmd_meta) * actual_slots); + + txq->need_update = 0; + + /* Initialize queue's high/low-water marks, and head/tail indexes */ + iwl_legacy_queue_init(priv, &txq->q, + TFD_QUEUE_SIZE_MAX, slots_num, txq_id); + + /* Tell device where to find queue */ + priv->cfg->ops->lib->txq_init(priv, txq); +} +EXPORT_SYMBOL(iwl_legacy_tx_queue_reset); + +/*************** HOST COMMAND QUEUE FUNCTIONS *****/ + +/** + * iwl_legacy_enqueue_hcmd - enqueue a uCode command + * @priv: device private data point + * @cmd: a point to the ucode command structure + * + * The function returns < 0 values to indicate the operation is + * failed. On success, it turns the index (> 0) of command in the + * command queue. + */ +int iwl_legacy_enqueue_hcmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) +{ + struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; + struct iwl_queue *q = &txq->q; + struct iwl_device_cmd *out_cmd; + struct iwl_cmd_meta *out_meta; + dma_addr_t phys_addr; + unsigned long flags; + int len; + u32 idx; + u16 fix_size; + + cmd->len = priv->cfg->ops->utils->get_hcmd_size(cmd->id, cmd->len); + fix_size = (u16)(cmd->len + sizeof(out_cmd->hdr)); + + /* If any of the command structures end up being larger than + * the TFD_MAX_PAYLOAD_SIZE, and it sent as a 'small' command then + * we will need to increase the size of the TFD entries + * Also, check to see if command buffer should not exceed the size + * of device_cmd and max_cmd_size. */ + BUG_ON((fix_size > TFD_MAX_PAYLOAD_SIZE) && + !(cmd->flags & CMD_SIZE_HUGE)); + BUG_ON(fix_size > IWL_MAX_CMD_SIZE); + + if (iwl_legacy_is_rfkill(priv) || iwl_legacy_is_ctkill(priv)) { + IWL_WARN(priv, "Not sending command - %s KILL\n", + iwl_legacy_is_rfkill(priv) ? "RF" : "CT"); + return -EIO; + } + + if (iwl_legacy_queue_space(q) < ((cmd->flags & CMD_ASYNC) ? 2 : 1)) { + IWL_ERR(priv, "No space in command queue\n"); + IWL_ERR(priv, "Restarting adapter due to queue full\n"); + queue_work(priv->workqueue, &priv->restart); + return -ENOSPC; + } + + spin_lock_irqsave(&priv->hcmd_lock, flags); + + /* If this is a huge cmd, mark the huge flag also on the meta.flags + * of the _original_ cmd. This is used for DMA mapping clean up. + */ + if (cmd->flags & CMD_SIZE_HUGE) { + idx = iwl_legacy_get_cmd_index(q, q->write_ptr, 0); + txq->meta[idx].flags = CMD_SIZE_HUGE; + } + + idx = iwl_legacy_get_cmd_index(q, q->write_ptr, cmd->flags & CMD_SIZE_HUGE); + out_cmd = txq->cmd[idx]; + out_meta = &txq->meta[idx]; + + memset(out_meta, 0, sizeof(*out_meta)); /* re-initialize to NULL */ + out_meta->flags = cmd->flags; + if (cmd->flags & CMD_WANT_SKB) + out_meta->source = cmd; + if (cmd->flags & CMD_ASYNC) + out_meta->callback = cmd->callback; + + out_cmd->hdr.cmd = cmd->id; + memcpy(&out_cmd->cmd.payload, cmd->data, cmd->len); + + /* At this point, the out_cmd now has all of the incoming cmd + * information */ + + out_cmd->hdr.flags = 0; + out_cmd->hdr.sequence = cpu_to_le16(QUEUE_TO_SEQ(priv->cmd_queue) | + INDEX_TO_SEQ(q->write_ptr)); + if (cmd->flags & CMD_SIZE_HUGE) + out_cmd->hdr.sequence |= SEQ_HUGE_FRAME; + len = sizeof(struct iwl_device_cmd); + if (idx == TFD_CMD_SLOTS) + len = IWL_MAX_CMD_SIZE; + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + switch (out_cmd->hdr.cmd) { + case REPLY_TX_LINK_QUALITY_CMD: + case SENSITIVITY_CMD: + IWL_DEBUG_HC_DUMP(priv, + "Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + iwl_legacy_get_cmd_string(out_cmd->hdr.cmd), + out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, + q->write_ptr, idx, priv->cmd_queue); + break; + default: + IWL_DEBUG_HC(priv, "Sending command %s (#%x), seq: 0x%04X, " + "%d bytes at %d[%d]:%d\n", + iwl_legacy_get_cmd_string(out_cmd->hdr.cmd), + out_cmd->hdr.cmd, + le16_to_cpu(out_cmd->hdr.sequence), fix_size, + q->write_ptr, idx, priv->cmd_queue); + } +#endif + txq->need_update = 1; + + if (priv->cfg->ops->lib->txq_update_byte_cnt_tbl) + /* Set up entry in queue's byte count circular buffer */ + priv->cfg->ops->lib->txq_update_byte_cnt_tbl(priv, txq, 0); + + phys_addr = pci_map_single(priv->pci_dev, &out_cmd->hdr, + fix_size, PCI_DMA_BIDIRECTIONAL); + dma_unmap_addr_set(out_meta, mapping, phys_addr); + dma_unmap_len_set(out_meta, len, fix_size); + + trace_iwlwifi_legacy_dev_hcmd(priv, &out_cmd->hdr, + fix_size, cmd->flags); + + priv->cfg->ops->lib->txq_attach_buf_to_tfd(priv, txq, + phys_addr, fix_size, 1, + U32_PAD(cmd->len)); + + /* Increment and update queue's write index */ + q->write_ptr = iwl_legacy_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_legacy_txq_update_write_ptr(priv, txq); + + spin_unlock_irqrestore(&priv->hcmd_lock, flags); + return idx; +} + +/** + * iwl_legacy_hcmd_queue_reclaim - Reclaim TX command queue entries already Tx'd + * + * When FW advances 'R' index, all entries between old and new 'R' index + * need to be reclaimed. As result, some free space forms. If there is + * enough free space (> low mark), wake the stack that feeds us. + */ +static void iwl_legacy_hcmd_queue_reclaim(struct iwl_priv *priv, int txq_id, + int idx, int cmd_idx) +{ + struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct iwl_queue *q = &txq->q; + int nfreed = 0; + + if ((idx >= q->n_bd) || (iwl_legacy_queue_used(q, idx) == 0)) { + IWL_ERR(priv, "Read index for DMA queue txq id (%d), index %d, " + "is out of range [0-%d] %d %d.\n", txq_id, + idx, q->n_bd, q->write_ptr, q->read_ptr); + return; + } + + for (idx = iwl_legacy_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = iwl_legacy_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (nfreed++ > 0) { + IWL_ERR(priv, "HCMD skipped: index (%d) %d %d\n", idx, + q->write_ptr, q->read_ptr); + queue_work(priv->workqueue, &priv->restart); + } + + } +} + +/** + * iwl_legacy_tx_cmd_complete - Pull unused buffers off the queue and reclaim them + * @rxb: Rx buffer to reclaim + * + * If an Rx buffer has an async callback associated with it the callback + * will be executed. The attached skb (if present) will only be freed + * if the callback returns 1 + */ +void +iwl_legacy_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u16 sequence = le16_to_cpu(pkt->hdr.sequence); + int txq_id = SEQ_TO_QUEUE(sequence); + int index = SEQ_TO_INDEX(sequence); + int cmd_index; + bool huge = !!(pkt->hdr.sequence & SEQ_HUGE_FRAME); + struct iwl_device_cmd *cmd; + struct iwl_cmd_meta *meta; + struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; + + /* If a Tx command is being handled and it isn't in the actual + * command queue then there a command routing bug has been introduced + * in the queue management code. */ + if (WARN(txq_id != priv->cmd_queue, + "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", + txq_id, priv->cmd_queue, sequence, + priv->txq[priv->cmd_queue].q.read_ptr, + priv->txq[priv->cmd_queue].q.write_ptr)) { + iwl_print_hex_error(priv, pkt, 32); + return; + } + + /* If this is a huge cmd, clear the huge flag on the meta.flags + * of the _original_ cmd. So that iwl_legacy_cmd_queue_free won't unmap + * the DMA buffer for the scan (huge) command. + */ + if (huge) { + cmd_index = iwl_legacy_get_cmd_index(&txq->q, index, 0); + txq->meta[cmd_index].flags = 0; + } + cmd_index = iwl_legacy_get_cmd_index(&txq->q, index, huge); + cmd = txq->cmd[cmd_index]; + meta = &txq->meta[cmd_index]; + + pci_unmap_single(priv->pci_dev, + dma_unmap_addr(meta, mapping), + dma_unmap_len(meta, len), + PCI_DMA_BIDIRECTIONAL); + + /* Input error checking is done when commands are added to queue. */ + if (meta->flags & CMD_WANT_SKB) { + meta->source->reply_page = (unsigned long)rxb_addr(rxb); + rxb->page = NULL; + } else if (meta->callback) + meta->callback(priv, cmd, pkt); + + iwl_legacy_hcmd_queue_reclaim(priv, txq_id, index, cmd_index); + + if (!(meta->flags & CMD_ASYNC)) { + clear_bit(STATUS_HCMD_ACTIVE, &priv->status); + IWL_DEBUG_INFO(priv, "Clearing HCMD_ACTIVE for command %s\n", + iwl_legacy_get_cmd_string(cmd->hdr.cmd)); + wake_up_interruptible(&priv->wait_command_queue); + } + meta->flags = 0; +} +EXPORT_SYMBOL(iwl_legacy_tx_cmd_complete); diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlegacy/iwl3945-base.c index adcef735180..ab87e1b7352 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlegacy/iwl3945-base.c @@ -1,6 +1,6 @@ /****************************************************************************** * - * Copyright(c) 2003 - 2010 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -61,7 +61,6 @@ #include "iwl-helpers.h" #include "iwl-dev.h" #include "iwl-spectrum.h" -#include "iwl-legacy.h" /* * module name, copyright, version, etc. @@ -70,7 +69,7 @@ #define DRV_DESCRIPTION \ "Intel(R) PRO/Wireless 3945ABG/BG Network Connection driver for Linux" -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG #define VD "d" #else #define VD @@ -82,7 +81,7 @@ * this was configurable. */ #define DRV_VERSION IWLWIFI_VERSION VD "s" -#define DRV_COPYRIGHT "Copyright(c) 2003-2010 Intel Corporation" +#define DRV_COPYRIGHT "Copyright(c) 2003-2011 Intel Corporation" #define DRV_AUTHOR "<ilw@linux.intel.com>" MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -164,7 +163,7 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv, if ((priv->stations[sta_id].sta.key.key_flags & STA_KEY_FLG_ENCRYPT_MSK) == STA_KEY_FLG_NO_ENC) priv->stations[sta_id].sta.key.key_offset = - iwl_get_free_ucode_key_index(priv); + iwl_legacy_get_free_ucode_key_index(priv); /* else, we are overriding an existing key => no need to allocated room * in uCode. */ @@ -177,7 +176,8 @@ static int iwl3945_set_ccmp_dynamic_key_info(struct iwl_priv *priv, IWL_DEBUG_INFO(priv, "hwcrypto: modify ucode station key info\n"); - ret = iwl_send_add_sta(priv, &priv->stations[sta_id].sta, CMD_ASYNC); + ret = iwl_legacy_send_add_sta(priv, + &priv->stations[sta_id].sta, CMD_ASYNC); spin_unlock_irqrestore(&priv->sta_lock, flags); @@ -201,7 +201,7 @@ static int iwl3945_set_wep_dynamic_key_info(struct iwl_priv *priv, static int iwl3945_clear_sta_key_info(struct iwl_priv *priv, u8 sta_id) { unsigned long flags; - struct iwl_addsta_cmd sta_cmd; + struct iwl_legacy_addsta_cmd sta_cmd; spin_lock_irqsave(&priv->sta_lock, flags); memset(&priv->stations[sta_id].keyinfo, 0, sizeof(struct iwl_hw_key)); @@ -210,11 +210,11 @@ static int iwl3945_clear_sta_key_info(struct iwl_priv *priv, u8 sta_id) priv->stations[sta_id].sta.key.key_flags = STA_KEY_FLG_NO_ENC; priv->stations[sta_id].sta.sta.modify_mask = STA_MODIFY_KEY_MASK; priv->stations[sta_id].sta.mode = STA_CONTROL_MODIFY_MSK; - memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_addsta_cmd)); + memcpy(&sta_cmd, &priv->stations[sta_id].sta, sizeof(struct iwl_legacy_addsta_cmd)); spin_unlock_irqrestore(&priv->sta_lock, flags); IWL_DEBUG_INFO(priv, "hwcrypto: clear ucode station key info\n"); - return iwl_send_add_sta(priv, &sta_cmd, CMD_SYNC); + return iwl_legacy_send_add_sta(priv, &sta_cmd, CMD_SYNC); } static int iwl3945_set_dynamic_key(struct iwl_priv *priv, @@ -318,7 +318,7 @@ unsigned int iwl3945_fill_beacon_frame(struct iwl_priv *priv, int left) { - if (!iwl_is_associated(priv, IWL_RXON_CTX_BSS) || !priv->beacon_skb) + if (!iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS) || !priv->beacon_skb) return 0; if (priv->beacon_skb->len > left) @@ -344,12 +344,12 @@ static int iwl3945_send_beacon_cmd(struct iwl_priv *priv) return -ENOMEM; } - rate = iwl_rate_get_lowest_plcp(priv, + rate = iwl_legacy_get_lowest_plcp(priv, &priv->contexts[IWL_RXON_CTX_BSS]); frame_size = iwl3945_hw_get_beacon_cmd(priv, frame, rate); - rc = iwl_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, + rc = iwl_legacy_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, &frame->u.cmd[0]); iwl3945_free_frame(priv, frame); @@ -443,7 +443,7 @@ static void iwl3945_build_tx_cmd_basic(struct iwl_priv *priv, tx_flags |= TX_CMD_FLG_SEQ_CTL_MSK; } - priv->cfg->ops->utils->tx_cmd_protection(priv, info, fc, &tx_flags); + iwl_legacy_tx_cmd_protection(priv, info, fc, &tx_flags); tx_flags &= ~(TX_CMD_FLG_ANT_SEL_MSK); if (ieee80211_is_mgmt(fc)) { @@ -485,7 +485,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) unsigned long flags; spin_lock_irqsave(&priv->lock, flags); - if (iwl_is_rfkill(priv)) { + if (iwl_legacy_is_rfkill(priv)) { IWL_DEBUG_DROP(priv, "Dropping - RF KILL\n"); goto drop_unlock; } @@ -500,7 +500,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) fc = hdr->frame_control; -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG if (ieee80211_is_auth(fc)) IWL_DEBUG_TX(priv, "Sending AUTH frame\n"); else if (ieee80211_is_assoc_req(fc)) @@ -514,7 +514,7 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) hdr_len = ieee80211_hdrlen(fc); /* Find index into station table for destination station */ - sta_id = iwl_sta_id_or_broadcast( + sta_id = iwl_legacy_sta_id_or_broadcast( priv, &priv->contexts[IWL_RXON_CTX_BSS], info->control.sta); if (sta_id == IWL_INVALID_STATION) { @@ -536,12 +536,12 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) txq = &priv->txq[txq_id]; q = &txq->q; - if ((iwl_queue_space(q) < q->high_mark)) + if ((iwl_legacy_queue_space(q) < q->high_mark)) goto drop; spin_lock_irqsave(&priv->lock, flags); - idx = get_cmd_index(q, q->write_ptr, 0); + idx = iwl_legacy_get_cmd_index(q, q->write_ptr, 0); /* Set up driver data for this TFD */ memset(&(txq->txb[q->write_ptr]), 0, sizeof(struct iwl_tx_info)); @@ -582,8 +582,8 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) len = (u16)skb->len; tx_cmd->len = cpu_to_le16(len); - iwl_dbg_log_tx_data_frame(priv, len, hdr); - iwl_update_stats(priv, true, fc, len); + iwl_legacy_dbg_log_tx_data_frame(priv, len, hdr); + iwl_legacy_update_stats(priv, true, fc, len); tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_A_MSK; tx_cmd->tx_flags &= ~TX_CMD_FLG_ANT_B_MSK; @@ -642,20 +642,20 @@ static int iwl3945_tx_skb(struct iwl_priv *priv, struct sk_buff *skb) /* Tell device the write index *just past* this latest filled TFD */ - q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_txq_update_write_ptr(priv, txq); + q->write_ptr = iwl_legacy_queue_inc_wrap(q->write_ptr, q->n_bd); + iwl_legacy_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); - if ((iwl_queue_space(q) < q->high_mark) + if ((iwl_legacy_queue_space(q) < q->high_mark) && priv->mac80211_registered) { if (wait_write_ptr) { spin_lock_irqsave(&priv->lock, flags); txq->need_update = 1; - iwl_txq_update_write_ptr(priv, txq); + iwl_legacy_txq_update_write_ptr(priv, txq); spin_unlock_irqrestore(&priv->lock, flags); } - iwl_stop_queue(priv, txq); + iwl_legacy_stop_queue(priv, txq); } return 0; @@ -683,8 +683,8 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, int duration = le16_to_cpu(params->duration); struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS)) - add_time = iwl_usecs_to_beacons(priv, + if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) + add_time = iwl_legacy_usecs_to_beacons(priv, le64_to_cpu(params->start_time) - priv->_3945.last_tsf, le16_to_cpu(ctx->timing.beacon_interval)); @@ -697,9 +697,9 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, cmd.len = sizeof(spectrum); spectrum.len = cpu_to_le16(cmd.len - sizeof(spectrum.len)); - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS)) + if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) spectrum.start_time = - iwl_add_beacon_time(priv, + iwl_legacy_add_beacon_time(priv, priv->_3945.last_beacon_time, add_time, le16_to_cpu(ctx->timing.beacon_interval)); else @@ -712,7 +712,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, spectrum.flags |= RXON_FLG_BAND_24G_MSK | RXON_FLG_AUTO_DETECT_MSK | RXON_FLG_TGG_PROTECT_MSK; - rc = iwl_send_cmd_sync(priv, &cmd); + rc = iwl_legacy_send_cmd_sync(priv, &cmd); if (rc) return rc; @@ -739,7 +739,7 @@ static int iwl3945_get_measurement(struct iwl_priv *priv, break; } - iwl_free_pages(priv, cmd.reply_page); + iwl_legacy_free_pages(priv, cmd.reply_page); return rc; } @@ -783,45 +783,19 @@ static void iwl3945_rx_reply_alive(struct iwl_priv *priv, static void iwl3945_rx_reply_add_sta(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG struct iwl_rx_packet *pkt = rxb_addr(rxb); #endif IWL_DEBUG_RX(priv, "Received REPLY_ADD_STA: 0x%02X\n", pkt->u.status); } -static void iwl3945_bg_beacon_update(struct work_struct *work) -{ - struct iwl_priv *priv = - container_of(work, struct iwl_priv, beacon_update); - struct sk_buff *beacon; - - /* Pull updated AP beacon from mac80211. will fail if not in AP mode */ - beacon = ieee80211_beacon_get(priv->hw, - priv->contexts[IWL_RXON_CTX_BSS].vif); - - if (!beacon) { - IWL_ERR(priv, "update beacon failed\n"); - return; - } - - mutex_lock(&priv->mutex); - /* new beacon skb is allocated every time; dispose previous.*/ - if (priv->beacon_skb) - dev_kfree_skb(priv->beacon_skb); - - priv->beacon_skb = beacon; - mutex_unlock(&priv->mutex); - - iwl3945_send_beacon_cmd(priv); -} - static void iwl3945_rx_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl3945_beacon_notif *beacon = &(pkt->u.beacon_status); -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG u8 rate = beacon->beacon_notify_hdr.rate; IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d " @@ -835,9 +809,6 @@ static void iwl3945_rx_beacon_notif(struct iwl_priv *priv, priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); - if ((priv->iw_mode == NL80211_IFTYPE_AP) && - (!test_bit(STATUS_EXIT_PENDING, &priv->status))) - queue_work(priv->workqueue, &priv->beacon_update); } /* Handle notification from uCode that card's power state is changing @@ -862,7 +833,7 @@ static void iwl3945_rx_card_state_notif(struct iwl_priv *priv, clear_bit(STATUS_RF_KILL_HW, &priv->status); - iwl_scan_cancel(priv); + iwl_legacy_scan_cancel(priv); if ((test_bit(STATUS_RF_KILL_HW, &status) != test_bit(STATUS_RF_KILL_HW, &priv->status))) @@ -885,13 +856,13 @@ static void iwl3945_setup_rx_handlers(struct iwl_priv *priv) { priv->rx_handlers[REPLY_ALIVE] = iwl3945_rx_reply_alive; priv->rx_handlers[REPLY_ADD_STA] = iwl3945_rx_reply_add_sta; - priv->rx_handlers[REPLY_ERROR] = iwl_rx_reply_error; - priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_rx_csa; + priv->rx_handlers[REPLY_ERROR] = iwl_legacy_rx_reply_error; + priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_legacy_rx_csa; priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = - iwl_rx_spectrum_measure_notif; - priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_rx_pm_sleep_notif; + iwl_legacy_rx_spectrum_measure_notif; + priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_legacy_rx_pm_sleep_notif; priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = - iwl_rx_pm_debug_statistics_notif; + iwl_legacy_rx_pm_debug_statistics_notif; priv->rx_handlers[BEACON_NOTIFICATION] = iwl3945_rx_beacon_notif; /* @@ -902,7 +873,7 @@ static void iwl3945_setup_rx_handlers(struct iwl_priv *priv) priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl3945_reply_statistics; priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl3945_hw_rx_statistics; - iwl_setup_rx_scan_handlers(priv); + iwl_legacy_setup_rx_scan_handlers(priv); priv->rx_handlers[CARD_STATE_NOTIFICATION] = iwl3945_rx_card_state_notif; /* Set up hardware specific Rx handlers */ @@ -1003,7 +974,7 @@ static void iwl3945_rx_queue_restock(struct iwl_priv *priv) spin_lock_irqsave(&rxq->lock, flags); write = rxq->write & ~0x7; - while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + while ((iwl_legacy_rx_queue_space(rxq) > 0) && (rxq->free_count)) { /* Get next free Rx buffer, remove from free list */ element = rxq->rx_free.next; rxb = list_entry(element, struct iwl_rx_mem_buffer, list); @@ -1029,7 +1000,7 @@ static void iwl3945_rx_queue_restock(struct iwl_priv *priv) spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); - iwl_rx_queue_update_write_ptr(priv, rxq); + iwl_legacy_rx_queue_update_write_ptr(priv, rxq); } } @@ -1123,7 +1094,7 @@ void iwl3945_rx_queue_reset(struct iwl_priv *priv, struct iwl_rx_queue *rxq) pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << priv->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); - __iwl_free_pages(priv, rxq->pool[i].page); + __iwl_legacy_free_pages(priv, rxq->pool[i].page); rxq->pool[i].page = NULL; } list_add_tail(&rxq->pool[i].list, &rxq->rx_used); @@ -1170,7 +1141,7 @@ static void iwl3945_rx_queue_free(struct iwl_priv *priv, struct iwl_rx_queue *rx pci_unmap_page(priv->pci_dev, rxq->pool[i].page_dma, PAGE_SIZE << priv->hw_params.rx_page_order, PCI_DMA_FROMDEVICE); - __iwl_free_pages(priv, rxq->pool[i].page); + __iwl_legacy_free_pages(priv, rxq->pool[i].page); rxq->pool[i].page = NULL; } } @@ -1275,7 +1246,7 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; len += sizeof(u32); /* account for status word */ - trace_iwlwifi_dev_rx(priv, pkt, len); + trace_iwlwifi_legacy_dev_rx(priv, pkt, len); /* Reclaim a command buffer only if this packet is a response * to a (driver-originated) command. @@ -1292,14 +1263,14 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) * rx_handlers table. See iwl3945_setup_rx_handlers() */ if (priv->rx_handlers[pkt->hdr.cmd]) { IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i, - get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); + iwl_legacy_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); } else { /* No handling needed */ IWL_DEBUG_RX(priv, "r %d i %d No handler needed for %s, 0x%02x\n", - r, i, get_cmd_string(pkt->hdr.cmd), + r, i, iwl_legacy_get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); } @@ -1312,10 +1283,10 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) if (reclaim) { /* Invoke any callbacks, transfer the buffer to caller, - * and fire off the (possibly) blocking iwl_send_cmd() + * and fire off the (possibly) blocking iwl_legacy_send_cmd() * as we reclaim the driver command queue */ if (rxb->page) - iwl_tx_cmd_complete(priv, rxb); + iwl_legacy_tx_cmd_complete(priv, rxb); else IWL_WARN(priv, "Claim null rxb?\n"); } @@ -1357,14 +1328,14 @@ static void iwl3945_rx_handle(struct iwl_priv *priv) } /* call this function to flush any scheduled tasklet */ -static inline void iwl_synchronize_irq(struct iwl_priv *priv) +static inline void iwl3945_synchronize_irq(struct iwl_priv *priv) { /* wait to make sure we flush pending tasklet*/ synchronize_irq(priv->pci_dev->irq); tasklet_kill(&priv->irq_tasklet); } -static const char *desc_lookup(int i) +static const char *iwl3945_desc_lookup(int i) { switch (i) { case 1: @@ -1401,7 +1372,7 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv) } - count = iwl_read_targ_mem(priv, base); + count = iwl_legacy_read_targ_mem(priv, base); if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { IWL_ERR(priv, "Start IWL Error Log Dump:\n"); @@ -1414,25 +1385,25 @@ void iwl3945_dump_nic_error_log(struct iwl_priv *priv) for (i = ERROR_START_OFFSET; i < (count * ERROR_ELEM_SIZE) + ERROR_START_OFFSET; i += ERROR_ELEM_SIZE) { - desc = iwl_read_targ_mem(priv, base + i); + desc = iwl_legacy_read_targ_mem(priv, base + i); time = - iwl_read_targ_mem(priv, base + i + 1 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 1 * sizeof(u32)); blink1 = - iwl_read_targ_mem(priv, base + i + 2 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 2 * sizeof(u32)); blink2 = - iwl_read_targ_mem(priv, base + i + 3 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 3 * sizeof(u32)); ilink1 = - iwl_read_targ_mem(priv, base + i + 4 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 4 * sizeof(u32)); ilink2 = - iwl_read_targ_mem(priv, base + i + 5 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 5 * sizeof(u32)); data1 = - iwl_read_targ_mem(priv, base + i + 6 * sizeof(u32)); + iwl_legacy_read_targ_mem(priv, base + i + 6 * sizeof(u32)); IWL_ERR(priv, "%-13s (0x%X) %010u 0x%05X 0x%05X 0x%05X 0x%05X %u\n\n", - desc_lookup(desc), desc, time, blink1, blink2, + iwl3945_desc_lookup(desc), desc, time, blink1, blink2, ilink1, ilink2, data1); - trace_iwlwifi_dev_ucode_error(priv, desc, time, data1, 0, + trace_iwlwifi_legacy_dev_ucode_error(priv, desc, time, data1, 0, 0, blink1, blink2, ilink1, ilink2); } } @@ -1471,14 +1442,14 @@ static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, iwl_grab_nic_access(priv); /* Set starting address; reads will auto-increment */ - _iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); rmb(); /* "time" is actually "data" for mode 0 (no timestamp). * place event id # at far right for easier visual parsing. */ for (i = 0; i < num_events; i++) { - ev = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); - time = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (mode == 0) { /* data, ev */ if (bufsz) { @@ -1487,11 +1458,12 @@ static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, time, ev); } else { IWL_ERR(priv, "0x%08x\t%04u\n", time, ev); - trace_iwlwifi_dev_ucode_event(priv, 0, + trace_iwlwifi_legacy_dev_ucode_event(priv, 0, time, ev); } } else { - data = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + data = _iwl_legacy_read_direct32(priv, + HBUS_TARG_MEM_RDAT); if (bufsz) { pos += scnprintf(*buf + pos, bufsz - pos, "%010u:0x%08x:%04u\n", @@ -1499,7 +1471,7 @@ static int iwl3945_print_event_log(struct iwl_priv *priv, u32 start_idx, } else { IWL_ERR(priv, "%010u\t0x%08x\t%04u\n", time, data, ev); - trace_iwlwifi_dev_ucode_event(priv, time, + trace_iwlwifi_legacy_dev_ucode_event(priv, time, data, ev); } } @@ -1570,10 +1542,10 @@ int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, } /* event log header */ - capacity = iwl_read_targ_mem(priv, base); - mode = iwl_read_targ_mem(priv, base + (1 * sizeof(u32))); - num_wraps = iwl_read_targ_mem(priv, base + (2 * sizeof(u32))); - next_entry = iwl_read_targ_mem(priv, base + (3 * sizeof(u32))); + capacity = iwl_legacy_read_targ_mem(priv, base); + mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl_legacy_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl_legacy_read_targ_mem(priv, base + (3 * sizeof(u32))); if (capacity > priv->cfg->base_params->max_event_log_size) { IWL_ERR(priv, "Log capacity %d is bogus, limit to %d entries\n", @@ -1595,8 +1567,8 @@ int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, return pos; } -#ifdef CONFIG_IWLWIFI_DEBUG - if (!(iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log) +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (!(iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log) size = (size > DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES) ? DEFAULT_IWL3945_DUMP_EVENT_LOG_ENTRIES : size; #else @@ -1607,7 +1579,7 @@ int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, IWL_ERR(priv, "Start IWL Event Log Dump: display last %d count\n", size); -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG if (display) { if (full_log) bufsz = capacity * 48; @@ -1617,7 +1589,7 @@ int iwl3945_dump_nic_event_log(struct iwl_priv *priv, bool full_log, if (!*buf) return -ENOMEM; } - if ((iwl_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { + if ((iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { /* if uCode has wrapped back to top of log, * start at the oldest entry, * i.e the next one that uCode would fill. @@ -1647,7 +1619,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) u32 inta, handled = 0; u32 inta_fh; unsigned long flags; -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG u32 inta_mask; #endif @@ -1665,8 +1637,8 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_get_debug_level(priv) & IWL_DL_ISR) { +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & IWL_DL_ISR) { /* just for debug */ inta_mask = iwl_read32(priv, CSR_INT_MASK); IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", @@ -1690,18 +1662,18 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) IWL_ERR(priv, "Hardware error detected. Restarting.\n"); /* Tell the device to stop sending interrupts */ - iwl_disable_interrupts(priv); + iwl_legacy_disable_interrupts(priv); priv->isr_stats.hw++; - iwl_irq_handle_error(priv); + iwl_legacy_irq_handle_error(priv); handled |= CSR_INT_BIT_HW_ERR; return; } -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & (IWL_DL_ISR)) { /* NIC fires this, but we don't use it, redundant with WAKEUP */ if (inta & CSR_INT_BIT_SCD) { IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " @@ -1724,20 +1696,20 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) IWL_ERR(priv, "Microcode SW error detected. " "Restarting 0x%X.\n", inta); priv->isr_stats.sw++; - iwl_irq_handle_error(priv); + iwl_legacy_irq_handle_error(priv); handled |= CSR_INT_BIT_SW_ERR; } /* uCode wakes up after power-down sleep */ if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(priv, "Wakeup interrupt\n"); - iwl_rx_queue_update_write_ptr(priv, &priv->rxq); - iwl_txq_update_write_ptr(priv, &priv->txq[0]); - iwl_txq_update_write_ptr(priv, &priv->txq[1]); - iwl_txq_update_write_ptr(priv, &priv->txq[2]); - iwl_txq_update_write_ptr(priv, &priv->txq[3]); - iwl_txq_update_write_ptr(priv, &priv->txq[4]); - iwl_txq_update_write_ptr(priv, &priv->txq[5]); + iwl_legacy_rx_queue_update_write_ptr(priv, &priv->rxq); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[0]); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[1]); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[2]); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[3]); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[4]); + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[5]); priv->isr_stats.wakeup++; handled |= CSR_INT_BIT_WAKEUP; @@ -1757,7 +1729,7 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) priv->isr_stats.tx++; iwl_write32(priv, CSR_FH_INT_STATUS, (1 << 6)); - iwl_write_direct32(priv, FH39_TCSR_CREDIT + iwl_legacy_write_direct32(priv, FH39_TCSR_CREDIT (FH39_SRVC_CHNL), 0x0); handled |= CSR_INT_BIT_FH_TX; } @@ -1776,10 +1748,10 @@ static void iwl3945_irq_tasklet(struct iwl_priv *priv) /* Re-enable all interrupts */ /* only Re-enable if disabled by irq */ if (test_bit(STATUS_INT_ENABLED, &priv->status)) - iwl_enable_interrupts(priv); + iwl_legacy_enable_interrupts(priv); -#ifdef CONFIG_IWLWIFI_DEBUG - if (iwl_get_debug_level(priv) & (IWL_DL_ISR)) { +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & (IWL_DL_ISR)) { inta = iwl_read32(priv, CSR_INT); inta_mask = iwl_read32(priv, CSR_INT_MASK); inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); @@ -1806,14 +1778,14 @@ static int iwl3945_get_single_channel_for_scan(struct iwl_priv *priv, return added; } - active_dwell = iwl_get_active_dwell_time(priv, band, 0); - passive_dwell = iwl_get_passive_dwell_time(priv, band, vif); + active_dwell = iwl_legacy_get_active_dwell_time(priv, band, 0); + passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; - channel = iwl_get_single_channel_number(priv, band); + channel = iwl_legacy_get_single_channel_number(priv, band); if (channel) { scan_ch->channel = channel; @@ -1849,8 +1821,8 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, if (!sband) return 0; - active_dwell = iwl_get_active_dwell_time(priv, band, n_probes); - passive_dwell = iwl_get_passive_dwell_time(priv, band, vif); + active_dwell = iwl_legacy_get_active_dwell_time(priv, band, n_probes); + passive_dwell = iwl_legacy_get_passive_dwell_time(priv, band, vif); if (passive_dwell <= active_dwell) passive_dwell = active_dwell + 1; @@ -1863,10 +1835,12 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, scan_ch->channel = chan->hw_value; - ch_info = iwl_get_channel_info(priv, band, scan_ch->channel); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_SCAN(priv, "Channel %d is INVALID for this band.\n", - scan_ch->channel); + ch_info = iwl_legacy_get_channel_info(priv, band, + scan_ch->channel); + if (!iwl_legacy_is_channel_valid(ch_info)) { + IWL_DEBUG_SCAN(priv, + "Channel %d is INVALID for this band.\n", + scan_ch->channel); continue; } @@ -1875,7 +1849,7 @@ static int iwl3945_get_channels_for_scan(struct iwl_priv *priv, /* If passive , set up for auto-switch * and use long active_dwell time. */ - if (!is_active || is_channel_passive(ch_info) || + if (!is_active || iwl_legacy_is_channel_passive(ch_info) || (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)) { scan_ch->type = 0; /* passive */ if (IWL_UCODE_API(priv->ucode_ver) == 1) @@ -1955,12 +1929,12 @@ static void iwl3945_init_hw_rates(struct iwl_priv *priv, static void iwl3945_dealloc_ucode_pci(struct iwl_priv *priv) { - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_code); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_data_backup); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_init_data); - iwl_free_fw_desc(priv->pci_dev, &priv->ucode_boot); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_code); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_data); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_init); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_init_data); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_boot); } /** @@ -1976,7 +1950,7 @@ static int iwl3945_verify_inst_full(struct iwl_priv *priv, __le32 *image, u32 le IWL_DEBUG_INFO(priv, "ucode inst image size is %u\n", len); - iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, + iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, IWL39_RTC_INST_LOWER_BOUND); errcnt = 0; @@ -1984,7 +1958,7 @@ static int iwl3945_verify_inst_full(struct iwl_priv *priv, __le32 *image, u32 le /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IWL_DL_IO is set */ - val = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + val = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { IWL_ERR(priv, "uCode INST section is invalid at " "offset 0x%x, is 0x%x, s/b 0x%x\n", @@ -2023,9 +1997,9 @@ static int iwl3945_verify_inst_sparse(struct iwl_priv *priv, __le32 *image, u32 /* read data comes through single port, auto-incr addr */ /* NOTE: Use the debugless read so we don't flood kernel log * if IWL_DL_IO is set */ - iwl_write_direct32(priv, HBUS_TARG_MEM_RADDR, + iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, i + IWL39_RTC_INST_LOWER_BOUND); - val = _iwl_read_direct32(priv, HBUS_TARG_MEM_RDAT); + val = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); if (val != le32_to_cpu(*image)) { #if 0 /* Enable this if you want to see details */ IWL_ERR(priv, "uCode INST section is invalid at " @@ -2101,7 +2075,7 @@ static void iwl3945_nic_start(struct iwl_priv *priv) #define IWL3945_UCODE_GET(item) \ static u32 iwl3945_ucode_get_##item(const struct iwl_ucode_header *ucode)\ { \ - return le32_to_cpu(ucode->u.v1.item); \ + return le32_to_cpu(ucode->v1.item); \ } static u32 iwl3945_ucode_get_header_size(u32 api_ver) @@ -2111,7 +2085,7 @@ static u32 iwl3945_ucode_get_header_size(u32 api_ver) static u8 *iwl3945_ucode_get_data(const struct iwl_ucode_header *ucode) { - return (u8 *) ucode->u.v1.data; + return (u8 *) ucode->v1.data; } IWL3945_UCODE_GET(inst_size); @@ -2286,13 +2260,13 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) * 1) unmodified from disk * 2) backup cache for save/restore during power-downs */ priv->ucode_code.len = inst_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); priv->ucode_data.len = data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); priv->ucode_data_backup.len = data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr || !priv->ucode_data_backup.v_addr) @@ -2301,10 +2275,10 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* Initialization instructions and data */ if (init_size && init_data_size) { priv->ucode_init.len = init_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); priv->ucode_init_data.len = init_data_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) goto err_pci_alloc; @@ -2313,7 +2287,7 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* Bootstrap (instructions only, no data) */ if (boot_size) { priv->ucode_boot.len = boot_size; - iwl_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); if (!priv->ucode_boot.v_addr) goto err_pci_alloc; @@ -2400,14 +2374,14 @@ static int iwl3945_set_ucode_ptrs(struct iwl_priv *priv) pdata = priv->ucode_data_backup.p_addr; /* Tell bootstrap uCode where to find image to load */ - iwl_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); - iwl_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); - iwl_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, + iwl_legacy_write_prph(priv, BSM_DRAM_INST_PTR_REG, pinst); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_PTR_REG, pdata); + iwl_legacy_write_prph(priv, BSM_DRAM_DATA_BYTECOUNT_REG, priv->ucode_data.len); /* Inst byte count must be last to set up, bit 31 signals uCode * that all new ptr/size info is in place */ - iwl_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, + iwl_legacy_write_prph(priv, BSM_DRAM_INST_BYTECOUNT_REG, priv->ucode_code.len | BSM_DRAM_INST_LOAD); IWL_DEBUG_INFO(priv, "Runtime uCode pointers are set.\n"); @@ -2488,7 +2462,7 @@ static void iwl3945_alive_start(struct iwl_priv *priv) goto restart; } - rfkill = iwl_read_prph(priv, APMG_RFKILL_REG); + rfkill = iwl_legacy_read_prph(priv, APMG_RFKILL_REG); IWL_DEBUG_INFO(priv, "RFKILL status: 0x%x\n", rfkill); if (rfkill & 0x1) { @@ -2510,18 +2484,18 @@ static void iwl3945_alive_start(struct iwl_priv *priv) set_bit(STATUS_ALIVE, &priv->status); /* Enable watchdog to monitor the driver tx queues */ - iwl_setup_watchdog(priv); + iwl_legacy_setup_watchdog(priv); - if (iwl_is_rfkill(priv)) + if (iwl_legacy_is_rfkill(priv)) return; ieee80211_wake_queues(priv->hw); priv->active_rate = IWL_RATES_MASK_3945; - iwl_power_update_mode(priv, true); + iwl_legacy_power_update_mode(priv, true); - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS)) { + if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) { struct iwl3945_rxon_cmd *active_rxon = (struct iwl3945_rxon_cmd *)(&ctx->active); @@ -2529,11 +2503,11 @@ static void iwl3945_alive_start(struct iwl_priv *priv) active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { /* Initialize our rx_config data */ - iwl_connection_init_rx_config(priv, ctx); + iwl_legacy_connection_init_rx_config(priv, ctx); } /* Configure Bluetooth device coexistence support */ - priv->cfg->ops->hcmd->send_bt_config(priv); + iwl_legacy_send_bt_config(priv); set_bit(STATUS_READY, &priv->status); @@ -2560,7 +2534,7 @@ static void __iwl3945_down(struct iwl_priv *priv) IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); - iwl_scan_cancel_timeout(priv, 200); + iwl_legacy_scan_cancel_timeout(priv, 200); exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); @@ -2569,9 +2543,9 @@ static void __iwl3945_down(struct iwl_priv *priv) del_timer_sync(&priv->watchdog); /* Station information will now be cleared in device */ - iwl_clear_ucode_stations(priv, NULL); - iwl_dealloc_bcast_stations(priv); - iwl_clear_driver_stations(priv); + iwl_legacy_clear_ucode_stations(priv, NULL); + iwl_legacy_dealloc_bcast_stations(priv); + iwl_legacy_clear_driver_stations(priv); /* Unblock any waiting calls */ wake_up_interruptible_all(&priv->wait_command_queue); @@ -2586,16 +2560,16 @@ static void __iwl3945_down(struct iwl_priv *priv) /* tell the device to stop sending interrupts */ spin_lock_irqsave(&priv->lock, flags); - iwl_disable_interrupts(priv); + iwl_legacy_disable_interrupts(priv); spin_unlock_irqrestore(&priv->lock, flags); - iwl_synchronize_irq(priv); + iwl3945_synchronize_irq(priv); if (priv->mac80211_registered) ieee80211_stop_queues(priv->hw); /* If we have not previously called iwl3945_init() then * clear all bits but the RF Kill bits and return */ - if (!iwl_is_init(priv)) { + if (!iwl_legacy_is_init(priv)) { priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) << STATUS_RF_KILL_HW | test_bit(STATUS_GEO_CONFIGURED, &priv->status) << @@ -2620,11 +2594,11 @@ static void __iwl3945_down(struct iwl_priv *priv) iwl3945_hw_rxq_stop(priv); /* Power-down device's busmaster DMA clocks */ - iwl_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); + iwl_legacy_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); /* Stop the device, and put it in low power state */ - iwl_apm_stop(priv); + iwl_legacy_apm_stop(priv); exit: memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); @@ -2655,7 +2629,8 @@ static int iwl3945_alloc_bcast_station(struct iwl_priv *priv) u8 sta_id; spin_lock_irqsave(&priv->sta_lock, flags); - sta_id = iwl_prep_station(priv, ctx, iwl_bcast_addr, false, NULL); + sta_id = iwl_legacy_prep_station(priv, ctx, + iwlegacy_bcast_addr, false, NULL); if (sta_id == IWL_INVALID_STATION) { IWL_ERR(priv, "Unable to prepare broadcast station\n"); spin_unlock_irqrestore(&priv->sta_lock, flags); @@ -2713,7 +2688,7 @@ static int __iwl3945_up(struct iwl_priv *priv) /* clear (again), then enable host interrupts */ iwl_write32(priv, CSR_INT, 0xFFFFFFFF); - iwl_enable_interrupts(priv); + iwl_legacy_enable_interrupts(priv); /* really make sure rfkill handshake bits are cleared */ iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); @@ -2855,7 +2830,7 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) scan->quiet_plcp_th = IWL_PLCP_QUIET_THRESH; scan->quiet_time = IWL_ACTIVE_QUIET_TIME; - if (iwl_is_associated(priv, IWL_RXON_CTX_BSS)) { + if (iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS)) { u16 interval = 0; u32 extra; u32 suspend_time = 100; @@ -2943,7 +2918,7 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) if (!priv->is_internal_short_scan) { scan->tx_cmd.len = cpu_to_le16( - iwl_fill_probe_req(priv, + iwl_legacy_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data, vif->addr, priv->scan_request->ie, @@ -2952,9 +2927,9 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) } else { /* use bcast addr, will not be transmitted but must be valid */ scan->tx_cmd.len = cpu_to_le16( - iwl_fill_probe_req(priv, + iwl_legacy_fill_probe_req(priv, (struct ieee80211_mgmt *)scan->data, - iwl_bcast_addr, NULL, 0, + iwlegacy_bcast_addr, NULL, 0, IWL_MAX_SCAN_SIZE - sizeof(*scan))); } /* select Rx antennas */ @@ -2982,7 +2957,7 @@ int iwl3945_request_scan(struct iwl_priv *priv, struct ieee80211_vif *vif) scan->len = cpu_to_le16(cmd.len); set_bit(STATUS_SCAN_HW, &priv->status); - ret = iwl_send_cmd_sync(priv, &cmd); + ret = iwl_legacy_send_cmd_sync(priv, &cmd); if (ret) clear_bit(STATUS_SCAN_HW, &priv->status); return ret; @@ -3050,25 +3025,20 @@ void iwl3945_post_associate(struct iwl_priv *priv) if (!ctx->vif || !priv->is_open) return; - if (ctx->vif->type == NL80211_IFTYPE_AP) { - IWL_ERR(priv, "%s Should not be called in AP mode\n", __func__); - return; - } - IWL_DEBUG_ASSOC(priv, "Associated as %d to: %pM\n", ctx->vif->bss_conf.aid, ctx->active.bssid_addr); if (test_bit(STATUS_EXIT_PENDING, &priv->status)) return; - iwl_scan_cancel_timeout(priv, 200); + iwl_legacy_scan_cancel_timeout(priv, 200); - conf = ieee80211_get_hw_conf(priv->hw); + conf = iwl_legacy_ieee80211_get_hw_conf(priv->hw); ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; iwl3945_commit_rxon(priv, ctx); - rc = iwl_send_rxon_timing(priv, ctx); + rc = iwl_legacy_send_rxon_timing(priv, ctx); if (rc) IWL_WARN(priv, "REPLY_RXON_TIMING failed - " "Attempting to continue.\n"); @@ -3200,7 +3170,7 @@ static void iwl3945_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; @@ -3213,7 +3183,6 @@ static int iwl3945_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MAC80211(priv, "leave\n"); - return NETDEV_TX_OK; } void iwl3945_config_ap(struct iwl_priv *priv) @@ -3226,14 +3195,14 @@ void iwl3945_config_ap(struct iwl_priv *priv) return; /* The following should be done only at AP bring up */ - if (!(iwl_is_associated(priv, IWL_RXON_CTX_BSS))) { + if (!(iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS))) { /* RXON - unassoc (to set timing command) */ ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; iwl3945_commit_rxon(priv, ctx); /* RXON Timing */ - rc = iwl_send_rxon_timing(priv, ctx); + rc = iwl_legacy_send_rxon_timing(priv, ctx); if (rc) IWL_WARN(priv, "REPLY_RXON_TIMING failed - " "Attempting to continue.\n"); @@ -3260,10 +3229,6 @@ void iwl3945_config_ap(struct iwl_priv *priv) iwl3945_commit_rxon(priv, ctx); } iwl3945_send_beacon_cmd(priv); - - /* FIXME - we need to add code here to detect a totally new - * configuration, reset the AP, unassoc, rxon timing, assoc, - * clear sta table, add BCAST sta... */ } static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -3291,17 +3256,17 @@ static int iwl3945_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) return -EOPNOTSUPP; - static_key = !iwl_is_associated(priv, IWL_RXON_CTX_BSS); + static_key = !iwl_legacy_is_associated(priv, IWL_RXON_CTX_BSS); if (!static_key) { - sta_id = iwl_sta_id_or_broadcast( + sta_id = iwl_legacy_sta_id_or_broadcast( priv, &priv->contexts[IWL_RXON_CTX_BSS], sta); if (sta_id == IWL_INVALID_STATION) return -EINVAL; } mutex_lock(&priv->mutex); - iwl_scan_cancel_timeout(priv, 100); + iwl_legacy_scan_cancel_timeout(priv, 100); switch (cmd) { case SET_KEY: @@ -3346,7 +3311,8 @@ static int iwl3945_mac_sta_add(struct ieee80211_hw *hw, sta_priv->common.sta_id = IWL_INVALID_STATION; - ret = iwl_add_station_common(priv, &priv->contexts[IWL_RXON_CTX_BSS], + ret = iwl_legacy_add_station_common(priv, + &priv->contexts[IWL_RXON_CTX_BSS], sta->addr, is_ap, sta, &sta_id); if (ret) { IWL_ERR(priv, "Unable to add station %pM (%d)\n", @@ -3407,7 +3373,7 @@ static void iwl3945_configure_filter(struct ieee80211_hw *hw, /* * Receiving all multicast frames is always enabled by the - * default flags setup in iwl_connection_init_rx_config() + * default flags setup in iwl_legacy_connection_init_rx_config() * since we currently do not support programming multicast * filters into the device. */ @@ -3422,7 +3388,7 @@ static void iwl3945_configure_filter(struct ieee80211_hw *hw, * *****************************************************************************/ -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG /* * The following adds a new attribute to the sysfs representation @@ -3435,13 +3401,13 @@ static void iwl3945_configure_filter(struct ieee80211_hw *hw, * level that is used instead of the global debug level if it (the per * device debug level) is set. */ -static ssize_t show_debug_level(struct device *d, +static ssize_t iwl3945_show_debug_level(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - return sprintf(buf, "0x%08X\n", iwl_get_debug_level(priv)); + return sprintf(buf, "0x%08X\n", iwl_legacy_get_debug_level(priv)); } -static ssize_t store_debug_level(struct device *d, +static ssize_t iwl3945_store_debug_level(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3454,7 +3420,7 @@ static ssize_t store_debug_level(struct device *d, IWL_INFO(priv, "%s is not in hex or decimal form.\n", buf); else { priv->debug_level = val; - if (iwl_alloc_traffic_mem(priv)) + if (iwl_legacy_alloc_traffic_mem(priv)) IWL_ERR(priv, "Not enough memory to generate traffic log\n"); } @@ -3462,31 +3428,31 @@ static ssize_t store_debug_level(struct device *d, } static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, - show_debug_level, store_debug_level); + iwl3945_show_debug_level, iwl3945_store_debug_level); -#endif /* CONFIG_IWLWIFI_DEBUG */ +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUG */ -static ssize_t show_temperature(struct device *d, +static ssize_t iwl3945_show_temperature(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; return sprintf(buf, "%d\n", iwl3945_hw_get_temperature(priv)); } -static DEVICE_ATTR(temperature, S_IRUGO, show_temperature, NULL); +static DEVICE_ATTR(temperature, S_IRUGO, iwl3945_show_temperature, NULL); -static ssize_t show_tx_power(struct device *d, +static ssize_t iwl3945_show_tx_power(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%d\n", priv->tx_power_user_lmt); } -static ssize_t store_tx_power(struct device *d, +static ssize_t iwl3945_store_tx_power(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3503,9 +3469,9 @@ static ssize_t store_tx_power(struct device *d, return count; } -static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, show_tx_power, store_tx_power); +static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, iwl3945_show_tx_power, iwl3945_store_tx_power); -static ssize_t show_flags(struct device *d, +static ssize_t iwl3945_show_flags(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); @@ -3514,7 +3480,7 @@ static ssize_t show_flags(struct device *d, return sprintf(buf, "0x%04X\n", ctx->active.flags); } -static ssize_t store_flags(struct device *d, +static ssize_t iwl3945_store_flags(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3525,7 +3491,7 @@ static ssize_t store_flags(struct device *d, mutex_lock(&priv->mutex); if (le32_to_cpu(ctx->staging.flags) != flags) { /* Cancel any currently running scans... */ - if (iwl_scan_cancel_timeout(priv, 100)) + if (iwl_legacy_scan_cancel_timeout(priv, 100)) IWL_WARN(priv, "Could not cancel scan.\n"); else { IWL_DEBUG_INFO(priv, "Committing rxon.flags = 0x%04X\n", @@ -3539,9 +3505,9 @@ static ssize_t store_flags(struct device *d, return count; } -static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, show_flags, store_flags); +static DEVICE_ATTR(flags, S_IWUSR | S_IRUGO, iwl3945_show_flags, iwl3945_store_flags); -static ssize_t show_filter_flags(struct device *d, +static ssize_t iwl3945_show_filter_flags(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); @@ -3551,7 +3517,7 @@ static ssize_t show_filter_flags(struct device *d, le32_to_cpu(ctx->active.filter_flags)); } -static ssize_t store_filter_flags(struct device *d, +static ssize_t iwl3945_store_filter_flags(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3562,7 +3528,7 @@ static ssize_t store_filter_flags(struct device *d, mutex_lock(&priv->mutex); if (le32_to_cpu(ctx->staging.filter_flags) != filter_flags) { /* Cancel any currently running scans... */ - if (iwl_scan_cancel_timeout(priv, 100)) + if (iwl_legacy_scan_cancel_timeout(priv, 100)) IWL_WARN(priv, "Could not cancel scan.\n"); else { IWL_DEBUG_INFO(priv, "Committing rxon.filter_flags = " @@ -3577,10 +3543,10 @@ static ssize_t store_filter_flags(struct device *d, return count; } -static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, show_filter_flags, - store_filter_flags); +static DEVICE_ATTR(filter_flags, S_IWUSR | S_IRUGO, iwl3945_show_filter_flags, + iwl3945_store_filter_flags); -static ssize_t show_measurement(struct device *d, +static ssize_t iwl3945_show_measurement(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); @@ -3612,7 +3578,7 @@ static ssize_t show_measurement(struct device *d, return len; } -static ssize_t store_measurement(struct device *d, +static ssize_t iwl3945_store_measurement(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3649,9 +3615,9 @@ static ssize_t store_measurement(struct device *d, } static DEVICE_ATTR(measurement, S_IRUSR | S_IWUSR, - show_measurement, store_measurement); + iwl3945_show_measurement, iwl3945_store_measurement); -static ssize_t store_retry_rate(struct device *d, +static ssize_t iwl3945_store_retry_rate(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3664,38 +3630,38 @@ static ssize_t store_retry_rate(struct device *d, return count; } -static ssize_t show_retry_rate(struct device *d, +static ssize_t iwl3945_show_retry_rate(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); return sprintf(buf, "%d", priv->retry_rate); } -static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, show_retry_rate, - store_retry_rate); +static DEVICE_ATTR(retry_rate, S_IWUSR | S_IRUSR, iwl3945_show_retry_rate, + iwl3945_store_retry_rate); -static ssize_t show_channels(struct device *d, +static ssize_t iwl3945_show_channels(struct device *d, struct device_attribute *attr, char *buf) { /* all this shit doesn't belong into sysfs anyway */ return 0; } -static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); +static DEVICE_ATTR(channels, S_IRUSR, iwl3945_show_channels, NULL); -static ssize_t show_antenna(struct device *d, +static ssize_t iwl3945_show_antenna(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; return sprintf(buf, "%d\n", iwl3945_mod_params.antenna); } -static ssize_t store_antenna(struct device *d, +static ssize_t iwl3945_store_antenna(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3720,20 +3686,20 @@ static ssize_t store_antenna(struct device *d, return count; } -static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, show_antenna, store_antenna); +static DEVICE_ATTR(antenna, S_IWUSR | S_IRUGO, iwl3945_show_antenna, iwl3945_store_antenna); -static ssize_t show_status(struct device *d, +static ssize_t iwl3945_show_status(struct device *d, struct device_attribute *attr, char *buf) { struct iwl_priv *priv = dev_get_drvdata(d); - if (!iwl_is_alive(priv)) + if (!iwl_legacy_is_alive(priv)) return -EAGAIN; return sprintf(buf, "0x%08x\n", (int)priv->status); } -static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); +static DEVICE_ATTR(status, S_IRUGO, iwl3945_show_status, NULL); -static ssize_t dump_error_log(struct device *d, +static ssize_t iwl3945_dump_error_log(struct device *d, struct device_attribute *attr, const char *buf, size_t count) { @@ -3746,7 +3712,7 @@ static ssize_t dump_error_log(struct device *d, return strnlen(buf, count); } -static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, dump_error_log); +static DEVICE_ATTR(dump_errors, S_IWUSR, NULL, iwl3945_dump_error_log); /***************************************************************************** * @@ -3762,18 +3728,17 @@ static void iwl3945_setup_deferred_work(struct iwl_priv *priv) INIT_WORK(&priv->restart, iwl3945_bg_restart); INIT_WORK(&priv->rx_replenish, iwl3945_bg_rx_replenish); - INIT_WORK(&priv->beacon_update, iwl3945_bg_beacon_update); INIT_DELAYED_WORK(&priv->init_alive_start, iwl3945_bg_init_alive_start); INIT_DELAYED_WORK(&priv->alive_start, iwl3945_bg_alive_start); INIT_DELAYED_WORK(&priv->_3945.rfkill_poll, iwl3945_rfkill_poll); - iwl_setup_scan_deferred_work(priv); + iwl_legacy_setup_scan_deferred_work(priv); iwl3945_hw_setup_deferred_work(priv); init_timer(&priv->watchdog); priv->watchdog.data = (unsigned long)priv; - priv->watchdog.function = iwl_bg_watchdog; + priv->watchdog.function = iwl_legacy_bg_watchdog; tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) iwl3945_irq_tasklet, (unsigned long)priv); @@ -3785,9 +3750,8 @@ static void iwl3945_cancel_deferred_work(struct iwl_priv *priv) cancel_delayed_work_sync(&priv->init_alive_start); cancel_delayed_work(&priv->alive_start); - cancel_work_sync(&priv->beacon_update); - iwl_cancel_scan_deferred_work(priv); + iwl_legacy_cancel_scan_deferred_work(priv); } static struct attribute *iwl3945_sysfs_entries[] = { @@ -3801,7 +3765,7 @@ static struct attribute *iwl3945_sysfs_entries[] = { &dev_attr_status.attr, &dev_attr_temperature.attr, &dev_attr_tx_power.attr, -#ifdef CONFIG_IWLWIFI_DEBUG +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG &dev_attr_debug_level.attr, #endif NULL @@ -3816,19 +3780,19 @@ struct ieee80211_ops iwl3945_hw_ops = { .tx = iwl3945_mac_tx, .start = iwl3945_mac_start, .stop = iwl3945_mac_stop, - .add_interface = iwl_mac_add_interface, - .remove_interface = iwl_mac_remove_interface, - .change_interface = iwl_mac_change_interface, + .add_interface = iwl_legacy_mac_add_interface, + .remove_interface = iwl_legacy_mac_remove_interface, + .change_interface = iwl_legacy_mac_change_interface, .config = iwl_legacy_mac_config, .configure_filter = iwl3945_configure_filter, .set_key = iwl3945_mac_set_key, - .conf_tx = iwl_mac_conf_tx, + .conf_tx = iwl_legacy_mac_conf_tx, .reset_tsf = iwl_legacy_mac_reset_tsf, .bss_info_changed = iwl_legacy_mac_bss_info_changed, - .hw_scan = iwl_mac_hw_scan, + .hw_scan = iwl_legacy_mac_hw_scan, .sta_add = iwl3945_mac_sta_add, - .sta_remove = iwl_mac_sta_remove, - .tx_last_beacon = iwl_mac_tx_last_beacon, + .sta_remove = iwl_legacy_mac_sta_remove, + .tx_last_beacon = iwl_legacy_mac_tx_last_beacon, }; static int iwl3945_init_drv(struct iwl_priv *priv) @@ -3870,7 +3834,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv) ret = -EINVAL; goto err; } - ret = iwl_init_channel_map(priv); + ret = iwl_legacy_init_channel_map(priv); if (ret) { IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); goto err; @@ -3882,7 +3846,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv) goto err_free_channel_map; } - ret = iwlcore_init_geos(priv); + ret = iwl_legacy_init_geos(priv); if (ret) { IWL_ERR(priv, "initializing geos failed: %d\n", ret); goto err_free_channel_map; @@ -3892,7 +3856,7 @@ static int iwl3945_init_drv(struct iwl_priv *priv) return 0; err_free_channel_map: - iwl_free_channel_map(priv); + iwl_legacy_free_channel_map(priv); err: return ret; } @@ -3912,10 +3876,6 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) hw->flags = IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_SPECTRUM_MGMT; - if (!priv->cfg->base_params->broken_powersave) - hw->flags |= IEEE80211_HW_SUPPORTS_PS | - IEEE80211_HW_SUPPORTS_DYNAMIC_PS; - hw->wiphy->interface_modes = priv->contexts[IWL_RXON_CTX_BSS].interface_modes; @@ -3938,7 +3898,7 @@ static int iwl3945_setup_mac(struct iwl_priv *priv) priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &priv->bands[IEEE80211_BAND_5GHZ]; - iwl_leds_init(priv); + iwl_legacy_leds_init(priv); ret = ieee80211_register_hw(priv->hw); if (ret) { @@ -3965,7 +3925,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e /* mac80211 allocates memory for this device instance, including * space for this driver's private structure */ - hw = iwl_alloc_all(cfg); + hw = iwl_legacy_alloc_all(cfg); if (hw == NULL) { pr_err("Can not allocate network device\n"); err = -ENOMEM; @@ -4005,13 +3965,12 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e iwl3945_hw_ops.hw_scan = NULL; } - IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); priv->cfg = cfg; priv->pci_dev = pdev; priv->inta_mask = CSR_INI_SET_MASK; - if (iwl_alloc_traffic_mem(priv)) + if (iwl_legacy_alloc_traffic_mem(priv)) IWL_ERR(priv, "Not enough memory to generate traffic log\n"); /*************************** @@ -4075,7 +4034,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e * ********************/ /* Read the EEPROM */ - err = iwl_eeprom_init(priv); + err = iwl_legacy_eeprom_init(priv); if (err) { IWL_ERR(priv, "Unable to init EEPROM\n"); goto out_iounmap; @@ -4112,12 +4071,12 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e * ********************/ spin_lock_irqsave(&priv->lock, flags); - iwl_disable_interrupts(priv); + iwl_legacy_disable_interrupts(priv); spin_unlock_irqrestore(&priv->lock, flags); pci_enable_msi(priv->pci_dev); - err = request_irq(priv->pci_dev->irq, priv->cfg->ops->lib->isr_ops.isr, + err = request_irq(priv->pci_dev->irq, iwl_legacy_isr, IRQF_SHARED, DRV_NAME, priv); if (err) { IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq); @@ -4130,24 +4089,24 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e goto out_release_irq; } - iwl_set_rxon_channel(priv, + iwl_legacy_set_rxon_channel(priv, &priv->bands[IEEE80211_BAND_2GHZ].channels[5], &priv->contexts[IWL_RXON_CTX_BSS]); iwl3945_setup_deferred_work(priv); iwl3945_setup_rx_handlers(priv); - iwl_power_initialize(priv); + iwl_legacy_power_initialize(priv); /********************************* * 8. Setup and Register mac80211 * *******************************/ - iwl_enable_interrupts(priv); + iwl_legacy_enable_interrupts(priv); err = iwl3945_setup_mac(priv); if (err) goto out_remove_sysfs; - err = iwl_dbgfs_register(priv, DRV_NAME); + err = iwl_legacy_dbgfs_register(priv, DRV_NAME); if (err) IWL_ERR(priv, "failed to create debugfs files. Ignoring error: %d\n", err); @@ -4165,12 +4124,12 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e free_irq(priv->pci_dev->irq, priv); out_disable_msi: pci_disable_msi(priv->pci_dev); - iwlcore_free_geos(priv); - iwl_free_channel_map(priv); + iwl_legacy_free_geos(priv); + iwl_legacy_free_channel_map(priv); out_unset_hw_params: iwl3945_unset_hw_params(priv); out_eeprom_free: - iwl_eeprom_free(priv); + iwl_legacy_eeprom_free(priv); out_iounmap: pci_iounmap(pdev, priv->hw_base); out_pci_release_regions: @@ -4179,7 +4138,7 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e pci_set_drvdata(pdev, NULL); pci_disable_device(pdev); out_ieee80211_free_hw: - iwl_free_traffic_mem(priv); + iwl_legacy_free_traffic_mem(priv); ieee80211_free_hw(priv->hw); out: return err; @@ -4195,11 +4154,11 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); - iwl_dbgfs_unregister(priv); + iwl_legacy_dbgfs_unregister(priv); set_bit(STATUS_EXIT_PENDING, &priv->status); - iwl_leds_exit(priv); + iwl_legacy_leds_exit(priv); if (priv->mac80211_registered) { ieee80211_unregister_hw(priv->hw); @@ -4215,16 +4174,16 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) * paths to avoid running iwl_down() at all before leaving driver. * This (inexpensive) call *makes sure* device is reset. */ - iwl_apm_stop(priv); + iwl_legacy_apm_stop(priv); /* make sure we flush any pending irq or * tasklet for the driver */ spin_lock_irqsave(&priv->lock, flags); - iwl_disable_interrupts(priv); + iwl_legacy_disable_interrupts(priv); spin_unlock_irqrestore(&priv->lock, flags); - iwl_synchronize_irq(priv); + iwl3945_synchronize_irq(priv); sysfs_remove_group(&pdev->dev.kobj, &iwl3945_attribute_group); @@ -4246,7 +4205,7 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) * until now... */ destroy_workqueue(priv->workqueue); priv->workqueue = NULL; - iwl_free_traffic_mem(priv); + iwl_legacy_free_traffic_mem(priv); free_irq(pdev->irq, priv); pci_disable_msi(pdev); @@ -4256,8 +4215,8 @@ static void __devexit iwl3945_pci_remove(struct pci_dev *pdev) pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); - iwl_free_channel_map(priv); - iwlcore_free_geos(priv); + iwl_legacy_free_channel_map(priv); + iwl_legacy_free_geos(priv); kfree(priv->scan_cmd); if (priv->beacon_skb) dev_kfree_skb(priv->beacon_skb); @@ -4277,7 +4236,7 @@ static struct pci_driver iwl3945_driver = { .id_table = iwl3945_hw_card_ids, .probe = iwl3945_pci_probe, .remove = __devexit_p(iwl3945_pci_remove), - .driver.pm = IWL_PM_OPS, + .driver.pm = IWL_LEGACY_PM_OPS, }; static int __init iwl3945_init(void) @@ -4318,17 +4277,17 @@ module_param_named(antenna, iwl3945_mod_params.antenna, int, S_IRUGO); MODULE_PARM_DESC(antenna, "select antenna (1=Main, 2=Aux, default 0 [both])"); module_param_named(swcrypto, iwl3945_mod_params.sw_crypto, int, S_IRUGO); MODULE_PARM_DESC(swcrypto, - "using software crypto (default 1 [software])\n"); -#ifdef CONFIG_IWLWIFI_DEBUG -module_param_named(debug, iwl_debug_level, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "debug output mask"); -#endif + "using software crypto (default 1 [software])"); module_param_named(disable_hw_scan, iwl3945_mod_params.disable_hw_scan, - int, S_IRUGO); + int, S_IRUGO); MODULE_PARM_DESC(disable_hw_scan, - "disable hardware scanning (default 0) (deprecated)"); -module_param_named(fw_restart3945, iwl3945_mod_params.restart_fw, int, S_IRUGO); -MODULE_PARM_DESC(fw_restart3945, "restart firmware in case of error"); + "disable hardware scanning (default 0) (deprecated)"); +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +module_param_named(debug, iwlegacy_debug_level, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif +module_param_named(fw_restart, iwl3945_mod_params.restart_fw, int, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); module_exit(iwl3945_exit); module_init(iwl3945_init); diff --git a/drivers/net/wireless/iwlegacy/iwl4965-base.c b/drivers/net/wireless/iwlegacy/iwl4965-base.c new file mode 100644 index 00000000000..91b3d8b9d7a --- /dev/null +++ b/drivers/net/wireless/iwlegacy/iwl4965-base.c @@ -0,0 +1,3632 @@ +/****************************************************************************** + * + * Copyright(c) 2003 - 2011 Intel Corporation. All rights reserved. + * + * Portions of this file are derived from the ipw3945 project, as well + * as portions of the ieee80211 subsystem header files. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * Intel Linux Wireless <ilw@linux.intel.com> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + * + *****************************************************************************/ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/pci-aspm.h> +#include <linux/slab.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/firmware.h> +#include <linux/etherdevice.h> +#include <linux/if_arp.h> + +#include <net/mac80211.h> + +#include <asm/div64.h> + +#define DRV_NAME "iwl4965" + +#include "iwl-eeprom.h" +#include "iwl-dev.h" +#include "iwl-core.h" +#include "iwl-io.h" +#include "iwl-helpers.h" +#include "iwl-sta.h" +#include "iwl-4965-calib.h" +#include "iwl-4965.h" +#include "iwl-4965-led.h" + + +/****************************************************************************** + * + * module boiler plate + * + ******************************************************************************/ + +/* + * module name, copyright, version, etc. + */ +#define DRV_DESCRIPTION "Intel(R) Wireless WiFi 4965 driver for Linux" + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +#define VD "d" +#else +#define VD +#endif + +#define DRV_VERSION IWLWIFI_VERSION VD + + +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_VERSION(DRV_VERSION); +MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("iwl4965"); + +void iwl4965_update_chain_flags(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + + if (priv->cfg->ops->hcmd->set_rxon_chain) { + for_each_context(priv, ctx) { + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + if (ctx->active.rx_chain != ctx->staging.rx_chain) + iwl_legacy_commit_rxon(priv, ctx); + } + } +} + +static void iwl4965_clear_free_frames(struct iwl_priv *priv) +{ + struct list_head *element; + + IWL_DEBUG_INFO(priv, "%d frames on pre-allocated heap on clear.\n", + priv->frames_count); + + while (!list_empty(&priv->free_frames)) { + element = priv->free_frames.next; + list_del(element); + kfree(list_entry(element, struct iwl_frame, list)); + priv->frames_count--; + } + + if (priv->frames_count) { + IWL_WARN(priv, "%d frames still in use. Did we lose one?\n", + priv->frames_count); + priv->frames_count = 0; + } +} + +static struct iwl_frame *iwl4965_get_free_frame(struct iwl_priv *priv) +{ + struct iwl_frame *frame; + struct list_head *element; + if (list_empty(&priv->free_frames)) { + frame = kzalloc(sizeof(*frame), GFP_KERNEL); + if (!frame) { + IWL_ERR(priv, "Could not allocate frame!\n"); + return NULL; + } + + priv->frames_count++; + return frame; + } + + element = priv->free_frames.next; + list_del(element); + return list_entry(element, struct iwl_frame, list); +} + +static void iwl4965_free_frame(struct iwl_priv *priv, struct iwl_frame *frame) +{ + memset(frame, 0, sizeof(*frame)); + list_add(&frame->list, &priv->free_frames); +} + +static u32 iwl4965_fill_beacon_frame(struct iwl_priv *priv, + struct ieee80211_hdr *hdr, + int left) +{ + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_skb) + return 0; + + if (priv->beacon_skb->len > left) + return 0; + + memcpy(hdr, priv->beacon_skb->data, priv->beacon_skb->len); + + return priv->beacon_skb->len; +} + +/* Parse the beacon frame to find the TIM element and set tim_idx & tim_size */ +static void iwl4965_set_beacon_tim(struct iwl_priv *priv, + struct iwl_tx_beacon_cmd *tx_beacon_cmd, + u8 *beacon, u32 frame_size) +{ + u16 tim_idx; + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)beacon; + + /* + * The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx+1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && (beacon[tim_idx] == WLAN_EID_TIM)) { + tx_beacon_cmd->tim_idx = cpu_to_le16(tim_idx); + tx_beacon_cmd->tim_size = beacon[tim_idx+1]; + } else + IWL_WARN(priv, "Unable to find TIM Element in beacon\n"); +} + +static unsigned int iwl4965_hw_get_beacon_cmd(struct iwl_priv *priv, + struct iwl_frame *frame) +{ + struct iwl_tx_beacon_cmd *tx_beacon_cmd; + u32 frame_size; + u32 rate_flags; + u32 rate; + /* + * We have to set up the TX command, the TX Beacon command, and the + * beacon contents. + */ + + lockdep_assert_held(&priv->mutex); + + if (!priv->beacon_ctx) { + IWL_ERR(priv, "trying to build beacon w/o beacon context!\n"); + return 0; + } + + /* Initialize memory */ + tx_beacon_cmd = &frame->u.beacon; + memset(tx_beacon_cmd, 0, sizeof(*tx_beacon_cmd)); + + /* Set up TX beacon contents */ + frame_size = iwl4965_fill_beacon_frame(priv, tx_beacon_cmd->frame, + sizeof(frame->u) - sizeof(*tx_beacon_cmd)); + if (WARN_ON_ONCE(frame_size > MAX_MPDU_SIZE)) + return 0; + if (!frame_size) + return 0; + + /* Set up TX command fields */ + tx_beacon_cmd->tx.len = cpu_to_le16((u16)frame_size); + tx_beacon_cmd->tx.sta_id = priv->beacon_ctx->bcast_sta_id; + tx_beacon_cmd->tx.stop_time.life_time = TX_CMD_LIFE_TIME_INFINITE; + tx_beacon_cmd->tx.tx_flags = TX_CMD_FLG_SEQ_CTL_MSK | + TX_CMD_FLG_TSF_MSK | TX_CMD_FLG_STA_RATE_MSK; + + /* Set up TX beacon command fields */ + iwl4965_set_beacon_tim(priv, tx_beacon_cmd, (u8 *)tx_beacon_cmd->frame, + frame_size); + + /* Set up packet rate and flags */ + rate = iwl_legacy_get_lowest_plcp(priv, priv->beacon_ctx); + priv->mgmt_tx_ant = iwl4965_toggle_tx_ant(priv, priv->mgmt_tx_ant, + priv->hw_params.valid_tx_ant); + rate_flags = iwl4965_ant_idx_to_flags(priv->mgmt_tx_ant); + if ((rate >= IWL_FIRST_CCK_RATE) && (rate <= IWL_LAST_CCK_RATE)) + rate_flags |= RATE_MCS_CCK_MSK; + tx_beacon_cmd->tx.rate_n_flags = iwl4965_hw_set_rate_n_flags(rate, + rate_flags); + + return sizeof(*tx_beacon_cmd) + frame_size; +} + +int iwl4965_send_beacon_cmd(struct iwl_priv *priv) +{ + struct iwl_frame *frame; + unsigned int frame_size; + int rc; + + frame = iwl4965_get_free_frame(priv); + if (!frame) { + IWL_ERR(priv, "Could not obtain free frame buffer for beacon " + "command.\n"); + return -ENOMEM; + } + + frame_size = iwl4965_hw_get_beacon_cmd(priv, frame); + if (!frame_size) { + IWL_ERR(priv, "Error configuring the beacon command\n"); + iwl4965_free_frame(priv, frame); + return -EINVAL; + } + + rc = iwl_legacy_send_cmd_pdu(priv, REPLY_TX_BEACON, frame_size, + &frame->u.cmd[0]); + + iwl4965_free_frame(priv, frame); + + return rc; +} + +static inline dma_addr_t iwl4965_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + dma_addr_t addr = get_unaligned_le32(&tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + addr |= + ((dma_addr_t)(le16_to_cpu(tb->hi_n_len) & 0xF) << 16) << 16; + + return addr; +} + +static inline u16 iwl4965_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + + return le16_to_cpu(tb->hi_n_len) >> 4; +} + +static inline void iwl4965_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, + dma_addr_t addr, u16 len) +{ + struct iwl_tfd_tb *tb = &tfd->tbs[idx]; + u16 hi_n_len = len << 4; + + put_unaligned_le32(addr, &tb->lo); + if (sizeof(dma_addr_t) > sizeof(u32)) + hi_n_len |= ((addr >> 16) >> 16) & 0xF; + + tb->hi_n_len = cpu_to_le16(hi_n_len); + + tfd->num_tbs = idx + 1; +} + +static inline u8 iwl4965_tfd_get_num_tbs(struct iwl_tfd *tfd) +{ + return tfd->num_tbs & 0x1f; +} + +/** + * iwl4965_hw_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] + * @priv - driver private data + * @txq - tx queue + * + * Does NOT advance any TFD circular buffer read/write indexes + * Does NOT free the TFD itself (which is within circular buffer) + */ +void iwl4965_hw_txq_free_tfd(struct iwl_priv *priv, struct iwl_tx_queue *txq) +{ + struct iwl_tfd *tfd_tmp = (struct iwl_tfd *)txq->tfds; + struct iwl_tfd *tfd; + struct pci_dev *dev = priv->pci_dev; + int index = txq->q.read_ptr; + int i; + int num_tbs; + + tfd = &tfd_tmp[index]; + + /* Sanity check on number of chunks */ + num_tbs = iwl4965_tfd_get_num_tbs(tfd); + + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(priv, "Too many chunks: %i\n", num_tbs); + /* @todo issue fatal error, it is quite serious situation */ + return; + } + + /* Unmap tx_cmd */ + if (num_tbs) + pci_unmap_single(dev, + dma_unmap_addr(&txq->meta[index], mapping), + dma_unmap_len(&txq->meta[index], len), + PCI_DMA_BIDIRECTIONAL); + + /* Unmap chunks, if any. */ + for (i = 1; i < num_tbs; i++) + pci_unmap_single(dev, iwl4965_tfd_tb_get_addr(tfd, i), + iwl4965_tfd_tb_get_len(tfd, i), + PCI_DMA_TODEVICE); + + /* free SKB */ + if (txq->txb) { + struct sk_buff *skb; + + skb = txq->txb[txq->q.read_ptr].skb; + + /* can be called from irqs-disabled context */ + if (skb) { + dev_kfree_skb_any(skb); + txq->txb[txq->q.read_ptr].skb = NULL; + } + } +} + +int iwl4965_hw_txq_attach_buf_to_tfd(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + dma_addr_t addr, u16 len, + u8 reset, u8 pad) +{ + struct iwl_queue *q; + struct iwl_tfd *tfd, *tfd_tmp; + u32 num_tbs; + + q = &txq->q; + tfd_tmp = (struct iwl_tfd *)txq->tfds; + tfd = &tfd_tmp[q->write_ptr]; + + if (reset) + memset(tfd, 0, sizeof(*tfd)); + + num_tbs = iwl4965_tfd_get_num_tbs(tfd); + + /* Each TFD can point to a maximum 20 Tx buffers */ + if (num_tbs >= IWL_NUM_OF_TBS) { + IWL_ERR(priv, "Error can not send more than %d chunks\n", + IWL_NUM_OF_TBS); + return -EINVAL; + } + + BUG_ON(addr & ~DMA_BIT_MASK(36)); + if (unlikely(addr & ~IWL_TX_DMA_MASK)) + IWL_ERR(priv, "Unaligned address = %llx\n", + (unsigned long long)addr); + + iwl4965_tfd_set_tb(tfd, num_tbs, addr, len); + + return 0; +} + +/* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * + * 4965 supports up to 16 Tx queues in DRAM, mapped to up to 8 Tx DMA + * channels supported in hardware. + */ +int iwl4965_hw_tx_queue_init(struct iwl_priv *priv, + struct iwl_tx_queue *txq) +{ + int txq_id = txq->q.id; + + /* Circular buffer (TFD queue in DRAM) physical base address */ + iwl_legacy_write_direct32(priv, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + + return 0; +} + +/****************************************************************************** + * + * Generic RX handler implementations + * + ******************************************************************************/ +static void iwl4965_rx_reply_alive(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_alive_resp *palive; + struct delayed_work *pwork; + + palive = &pkt->u.alive_frame; + + IWL_DEBUG_INFO(priv, "Alive ucode status 0x%08X revision " + "0x%01X 0x%01X\n", + palive->is_valid, palive->ver_type, + palive->ver_subtype); + + if (palive->ver_subtype == INITIALIZE_SUBTYPE) { + IWL_DEBUG_INFO(priv, "Initialization Alive received.\n"); + memcpy(&priv->card_alive_init, + &pkt->u.alive_frame, + sizeof(struct iwl_init_alive_resp)); + pwork = &priv->init_alive_start; + } else { + IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); + memcpy(&priv->card_alive, &pkt->u.alive_frame, + sizeof(struct iwl_alive_resp)); + pwork = &priv->alive_start; + } + + /* We delay the ALIVE response by 5ms to + * give the HW RF Kill time to activate... */ + if (palive->is_valid == UCODE_VALID_OK) + queue_delayed_work(priv->workqueue, pwork, + msecs_to_jiffies(5)); + else + IWL_WARN(priv, "uCode did not respond OK.\n"); +} + +/** + * iwl4965_bg_statistics_periodic - Timer callback to queue statistics + * + * This callback is provided in order to send a statistics request. + * + * This timer function is continually reset to execute within + * REG_RECALIB_PERIOD seconds since the last STATISTICS_NOTIFICATION + * was received. We need to ensure we receive the statistics in order + * to update the temperature used for calibrating the TXPOWER. + */ +static void iwl4965_bg_statistics_periodic(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + /* dont send host command if rf-kill is on */ + if (!iwl_legacy_is_ready_rf(priv)) + return; + + iwl_legacy_send_statistics_request(priv, CMD_ASYNC, false); +} + + +static void iwl4965_print_cont_event_trace(struct iwl_priv *priv, u32 base, + u32 start_idx, u32 num_events, + u32 mode) +{ + u32 i; + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + if (mode == 0) + ptr = base + (4 * sizeof(u32)) + (start_idx * 2 * sizeof(u32)); + else + ptr = base + (4 * sizeof(u32)) + (start_idx * 3 * sizeof(u32)); + + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->reg_lock, reg_flags); + if (iwl_grab_nic_access(priv)) { + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return; + } + + /* Set starting address; reads will auto-increment */ + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + rmb(); + + /* + * "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. + */ + for (i = 0; i < num_events; i++) { + ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + trace_iwlwifi_legacy_dev_ucode_cont_event(priv, + 0, time, ev); + } else { + data = _iwl_legacy_read_direct32(priv, + HBUS_TARG_MEM_RDAT); + trace_iwlwifi_legacy_dev_ucode_cont_event(priv, + time, data, ev); + } + } + /* Allow device to power down */ + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); +} + +static void iwl4965_continuous_event_trace(struct iwl_priv *priv) +{ + u32 capacity; /* event log capacity in # entries */ + u32 base; /* SRAM byte address of event log header */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + + if (priv->ucode_type == UCODE_INIT) + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + else + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + if (priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + capacity = iwl_legacy_read_targ_mem(priv, base); + num_wraps = iwl_legacy_read_targ_mem(priv, + base + (2 * sizeof(u32))); + mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32))); + next_entry = iwl_legacy_read_targ_mem(priv, + base + (3 * sizeof(u32))); + } else + return; + + if (num_wraps == priv->event_log.num_wraps) { + iwl4965_print_cont_event_trace(priv, + base, priv->event_log.next_entry, + next_entry - priv->event_log.next_entry, + mode); + priv->event_log.non_wraps_count++; + } else { + if ((num_wraps - priv->event_log.num_wraps) > 1) + priv->event_log.wraps_more_count++; + else + priv->event_log.wraps_once_count++; + trace_iwlwifi_legacy_dev_ucode_wrap_event(priv, + num_wraps - priv->event_log.num_wraps, + next_entry, priv->event_log.next_entry); + if (next_entry < priv->event_log.next_entry) { + iwl4965_print_cont_event_trace(priv, base, + priv->event_log.next_entry, + capacity - priv->event_log.next_entry, + mode); + + iwl4965_print_cont_event_trace(priv, base, 0, + next_entry, mode); + } else { + iwl4965_print_cont_event_trace(priv, base, + next_entry, capacity - next_entry, + mode); + + iwl4965_print_cont_event_trace(priv, base, 0, + next_entry, mode); + } + } + priv->event_log.num_wraps = num_wraps; + priv->event_log.next_entry = next_entry; +} + +/** + * iwl4965_bg_ucode_trace - Timer callback to log ucode event + * + * The timer is continually set to execute every + * UCODE_TRACE_PERIOD milliseconds after the last timer expired + * this function is to perform continuous uCode event logging operation + * if enabled + */ +static void iwl4965_bg_ucode_trace(unsigned long data) +{ + struct iwl_priv *priv = (struct iwl_priv *)data; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (priv->event_log.ucode_trace) { + iwl4965_continuous_event_trace(priv); + /* Reschedule the timer to occur in UCODE_TRACE_PERIOD */ + mod_timer(&priv->ucode_trace, + jiffies + msecs_to_jiffies(UCODE_TRACE_PERIOD)); + } +} + +static void iwl4965_rx_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl4965_beacon_notif *beacon = + (struct iwl4965_beacon_notif *)pkt->u.raw; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + u8 rate = iwl4965_hw_get_rate(beacon->beacon_notify_hdr.rate_n_flags); + + IWL_DEBUG_RX(priv, "beacon status %x retries %d iss %d " + "tsf %d %d rate %d\n", + le32_to_cpu(beacon->beacon_notify_hdr.u.status) & TX_STATUS_MSK, + beacon->beacon_notify_hdr.failure_frame, + le32_to_cpu(beacon->ibss_mgr_status), + le32_to_cpu(beacon->high_tsf), + le32_to_cpu(beacon->low_tsf), rate); +#endif + + priv->ibss_manager = le32_to_cpu(beacon->ibss_mgr_status); +} + +static void iwl4965_perform_ct_kill_task(struct iwl_priv *priv) +{ + unsigned long flags; + + IWL_DEBUG_POWER(priv, "Stop all queues\n"); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + iwl_read32(priv, CSR_UCODE_DRV_GP1); + + spin_lock_irqsave(&priv->reg_lock, flags); + if (!iwl_grab_nic_access(priv)) + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, flags); +} + +/* Handle notification from uCode that card's power state is changing + * due to software, hardware, or critical temperature RFKILL */ +static void iwl4965_rx_card_state_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + u32 flags = le32_to_cpu(pkt->u.card_state_notif.flags); + unsigned long status = priv->status; + + IWL_DEBUG_RF_KILL(priv, "Card state received: HW:%s SW:%s CT:%s\n", + (flags & HW_CARD_DISABLED) ? "Kill" : "On", + (flags & SW_CARD_DISABLED) ? "Kill" : "On", + (flags & CT_CARD_DISABLED) ? + "Reached" : "Not reached"); + + if (flags & (SW_CARD_DISABLED | HW_CARD_DISABLED | + CT_CARD_DISABLED)) { + + iwl_write32(priv, CSR_UCODE_DRV_GP1_SET, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + iwl_legacy_write_direct32(priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + + if (!(flags & RXON_CARD_DISABLED)) { + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + iwl_legacy_write_direct32(priv, HBUS_TARG_MBX_C, + HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED); + } + } + + if (flags & CT_CARD_DISABLED) + iwl4965_perform_ct_kill_task(priv); + + if (flags & HW_CARD_DISABLED) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + + if (!(flags & RXON_CARD_DISABLED)) + iwl_legacy_scan_cancel(priv); + + if ((test_bit(STATUS_RF_KILL_HW, &status) != + test_bit(STATUS_RF_KILL_HW, &priv->status))) + wiphy_rfkill_set_hw_state(priv->hw->wiphy, + test_bit(STATUS_RF_KILL_HW, &priv->status)); + else + wake_up_interruptible(&priv->wait_command_queue); +} + +/** + * iwl4965_setup_rx_handlers - Initialize Rx handler callbacks + * + * Setup the RX handlers for each of the reply types sent from the uCode + * to the host. + * + * This function chains into the hardware specific files for them to setup + * any hardware specific handlers as well. + */ +static void iwl4965_setup_rx_handlers(struct iwl_priv *priv) +{ + priv->rx_handlers[REPLY_ALIVE] = iwl4965_rx_reply_alive; + priv->rx_handlers[REPLY_ERROR] = iwl_legacy_rx_reply_error; + priv->rx_handlers[CHANNEL_SWITCH_NOTIFICATION] = iwl_legacy_rx_csa; + priv->rx_handlers[SPECTRUM_MEASURE_NOTIFICATION] = + iwl_legacy_rx_spectrum_measure_notif; + priv->rx_handlers[PM_SLEEP_NOTIFICATION] = iwl_legacy_rx_pm_sleep_notif; + priv->rx_handlers[PM_DEBUG_STATISTIC_NOTIFIC] = + iwl_legacy_rx_pm_debug_statistics_notif; + priv->rx_handlers[BEACON_NOTIFICATION] = iwl4965_rx_beacon_notif; + + /* + * The same handler is used for both the REPLY to a discrete + * statistics request from the host as well as for the periodic + * statistics notifications (after received beacons) from the uCode. + */ + priv->rx_handlers[REPLY_STATISTICS_CMD] = iwl4965_reply_statistics; + priv->rx_handlers[STATISTICS_NOTIFICATION] = iwl4965_rx_statistics; + + iwl_legacy_setup_rx_scan_handlers(priv); + + /* status change handler */ + priv->rx_handlers[CARD_STATE_NOTIFICATION] = + iwl4965_rx_card_state_notif; + + priv->rx_handlers[MISSED_BEACONS_NOTIFICATION] = + iwl4965_rx_missed_beacon_notif; + /* Rx handlers */ + priv->rx_handlers[REPLY_RX_PHY_CMD] = iwl4965_rx_reply_rx_phy; + priv->rx_handlers[REPLY_RX_MPDU_CMD] = iwl4965_rx_reply_rx; + /* block ack */ + priv->rx_handlers[REPLY_COMPRESSED_BA] = iwl4965_rx_reply_compressed_ba; + /* Set up hardware specific Rx handlers */ + priv->cfg->ops->lib->rx_handler_setup(priv); +} + +/** + * iwl4965_rx_handle - Main entry function for receiving responses from uCode + * + * Uses the priv->rx_handlers callback function array to invoke + * the appropriate handlers, including command responses, + * frame-received notifications, and other notifications. + */ +void iwl4965_rx_handle(struct iwl_priv *priv) +{ + struct iwl_rx_mem_buffer *rxb; + struct iwl_rx_packet *pkt; + struct iwl_rx_queue *rxq = &priv->rxq; + u32 r, i; + int reclaim; + unsigned long flags; + u8 fill_rx = 0; + u32 count = 8; + int total_empty; + + /* uCode's read index (stored in shared DRAM) indicates the last Rx + * buffer that the driver may process (last buffer filled by ucode). */ + r = le16_to_cpu(rxq->rb_stts->closed_rb_num) & 0x0FFF; + i = rxq->read; + + /* Rx interrupt, but nothing sent from uCode */ + if (i == r) + IWL_DEBUG_RX(priv, "r = %d, i = %d\n", r, i); + + /* calculate total frames need to be restock after handling RX */ + total_empty = r - rxq->write_actual; + if (total_empty < 0) + total_empty += RX_QUEUE_SIZE; + + if (total_empty > (RX_QUEUE_SIZE / 2)) + fill_rx = 1; + + while (i != r) { + int len; + + rxb = rxq->queue[i]; + + /* If an RXB doesn't have a Rx queue slot associated with it, + * then a bug has been introduced in the queue refilling + * routines -- catch it here */ + BUG_ON(rxb == NULL); + + rxq->queue[i] = NULL; + + pci_unmap_page(priv->pci_dev, rxb->page_dma, + PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + pkt = rxb_addr(rxb); + + len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; + len += sizeof(u32); /* account for status word */ + trace_iwlwifi_legacy_dev_rx(priv, pkt, len); + + /* Reclaim a command buffer only if this packet is a response + * to a (driver-originated) command. + * If the packet (e.g. Rx frame) originated from uCode, + * there is no command buffer to reclaim. + * Ucode should set SEQ_RX_FRAME bit if ucode-originated, + * but apparently a few don't get set; catch them here. */ + reclaim = !(pkt->hdr.sequence & SEQ_RX_FRAME) && + (pkt->hdr.cmd != REPLY_RX_PHY_CMD) && + (pkt->hdr.cmd != REPLY_RX) && + (pkt->hdr.cmd != REPLY_RX_MPDU_CMD) && + (pkt->hdr.cmd != REPLY_COMPRESSED_BA) && + (pkt->hdr.cmd != STATISTICS_NOTIFICATION) && + (pkt->hdr.cmd != REPLY_TX); + + /* Based on type of command response or notification, + * handle those that need handling via function in + * rx_handlers table. See iwl4965_setup_rx_handlers() */ + if (priv->rx_handlers[pkt->hdr.cmd]) { + IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, + i, iwl_legacy_get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); + priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; + priv->rx_handlers[pkt->hdr.cmd] (priv, rxb); + } else { + /* No handling needed */ + IWL_DEBUG_RX(priv, + "r %d i %d No handler needed for %s, 0x%02x\n", + r, i, iwl_legacy_get_cmd_string(pkt->hdr.cmd), + pkt->hdr.cmd); + } + + /* + * XXX: After here, we should always check rxb->page + * against NULL before touching it or its virtual + * memory (pkt). Because some rx_handler might have + * already taken or freed the pages. + */ + + if (reclaim) { + /* Invoke any callbacks, transfer the buffer to caller, + * and fire off the (possibly) blocking iwl_legacy_send_cmd() + * as we reclaim the driver command queue */ + if (rxb->page) + iwl_legacy_tx_cmd_complete(priv, rxb); + else + IWL_WARN(priv, "Claim null rxb?\n"); + } + + /* Reuse the page if possible. For notification packets and + * SKBs that fail to Rx correctly, add them back into the + * rx_free list for reuse later. */ + spin_lock_irqsave(&rxq->lock, flags); + if (rxb->page != NULL) { + rxb->page_dma = pci_map_page(priv->pci_dev, rxb->page, + 0, PAGE_SIZE << priv->hw_params.rx_page_order, + PCI_DMA_FROMDEVICE); + list_add_tail(&rxb->list, &rxq->rx_free); + rxq->free_count++; + } else + list_add_tail(&rxb->list, &rxq->rx_used); + + spin_unlock_irqrestore(&rxq->lock, flags); + + i = (i + 1) & RX_QUEUE_MASK; + /* If there are a lot of unused frames, + * restock the Rx queue so ucode wont assert. */ + if (fill_rx) { + count++; + if (count >= 8) { + rxq->read = i; + iwl4965_rx_replenish_now(priv); + count = 0; + } + } + } + + /* Backtrack one entry */ + rxq->read = i; + if (fill_rx) + iwl4965_rx_replenish_now(priv); + else + iwl4965_rx_queue_restock(priv); +} + +/* call this function to flush any scheduled tasklet */ +static inline void iwl4965_synchronize_irq(struct iwl_priv *priv) +{ + /* wait to make sure we flush pending tasklet*/ + synchronize_irq(priv->pci_dev->irq); + tasklet_kill(&priv->irq_tasklet); +} + +static void iwl4965_irq_tasklet(struct iwl_priv *priv) +{ + u32 inta, handled = 0; + u32 inta_fh; + unsigned long flags; + u32 i; +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + u32 inta_mask; +#endif + + spin_lock_irqsave(&priv->lock, flags); + + /* Ack/clear/reset pending uCode interrupts. + * Note: Some bits in CSR_INT are "OR" of bits in CSR_FH_INT_STATUS, + * and will clear only when CSR_FH_INT_STATUS gets cleared. */ + inta = iwl_read32(priv, CSR_INT); + iwl_write32(priv, CSR_INT, inta); + + /* Ack/clear/reset pending flow-handler (DMA) interrupts. + * Any new interrupts that happen after this, either while we're + * in this tasklet, or later, will show up in next ISR/tasklet. */ + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + iwl_write32(priv, CSR_FH_INT_STATUS, inta_fh); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & IWL_DL_ISR) { + /* just for debug */ + inta_mask = iwl_read32(priv, CSR_INT_MASK); + IWL_DEBUG_ISR(priv, "inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", + inta, inta_mask, inta_fh); + } +#endif + + spin_unlock_irqrestore(&priv->lock, flags); + + /* Since CSR_INT and CSR_FH_INT_STATUS reads and clears are not + * atomic, make sure that inta covers all the interrupts that + * we've discovered, even if FH interrupt came in just after + * reading CSR_INT. */ + if (inta_fh & CSR49_FH_INT_RX_MASK) + inta |= CSR_INT_BIT_FH_RX; + if (inta_fh & CSR49_FH_INT_TX_MASK) + inta |= CSR_INT_BIT_FH_TX; + + /* Now service all interrupt bits discovered above. */ + if (inta & CSR_INT_BIT_HW_ERR) { + IWL_ERR(priv, "Hardware error detected. Restarting.\n"); + + /* Tell the device to stop sending interrupts */ + iwl_legacy_disable_interrupts(priv); + + priv->isr_stats.hw++; + iwl_legacy_irq_handle_error(priv); + + handled |= CSR_INT_BIT_HW_ERR; + + return; + } + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & (IWL_DL_ISR)) { + /* NIC fires this, but we don't use it, redundant with WAKEUP */ + if (inta & CSR_INT_BIT_SCD) { + IWL_DEBUG_ISR(priv, "Scheduler finished to transmit " + "the frame/frames.\n"); + priv->isr_stats.sch++; + } + + /* Alive notification via Rx interrupt will do the real work */ + if (inta & CSR_INT_BIT_ALIVE) { + IWL_DEBUG_ISR(priv, "Alive interrupt\n"); + priv->isr_stats.alive++; + } + } +#endif + /* Safely ignore these bits for debug checks below */ + inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); + + /* HW RF KILL switch toggled */ + if (inta & CSR_INT_BIT_RF_KILL) { + int hw_rf_kill = 0; + if (!(iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW)) + hw_rf_kill = 1; + + IWL_WARN(priv, "RF_KILL bit toggled to %s.\n", + hw_rf_kill ? "disable radio" : "enable radio"); + + priv->isr_stats.rfkill++; + + /* driver only loads ucode once setting the interface up. + * the driver allows loading the ucode even if the radio + * is killed. Hence update the killswitch state here. The + * rfkill handler will care about restarting if needed. + */ + if (!test_bit(STATUS_ALIVE, &priv->status)) { + if (hw_rf_kill) + set_bit(STATUS_RF_KILL_HW, &priv->status); + else + clear_bit(STATUS_RF_KILL_HW, &priv->status); + wiphy_rfkill_set_hw_state(priv->hw->wiphy, hw_rf_kill); + } + + handled |= CSR_INT_BIT_RF_KILL; + } + + /* Chip got too hot and stopped itself */ + if (inta & CSR_INT_BIT_CT_KILL) { + IWL_ERR(priv, "Microcode CT kill error detected.\n"); + priv->isr_stats.ctkill++; + handled |= CSR_INT_BIT_CT_KILL; + } + + /* Error detected by uCode */ + if (inta & CSR_INT_BIT_SW_ERR) { + IWL_ERR(priv, "Microcode SW error detected. " + " Restarting 0x%X.\n", inta); + priv->isr_stats.sw++; + iwl_legacy_irq_handle_error(priv); + handled |= CSR_INT_BIT_SW_ERR; + } + + /* + * uCode wakes up after power-down sleep. + * Tell device about any new tx or host commands enqueued, + * and about any Rx buffers made available while asleep. + */ + if (inta & CSR_INT_BIT_WAKEUP) { + IWL_DEBUG_ISR(priv, "Wakeup interrupt\n"); + iwl_legacy_rx_queue_update_write_ptr(priv, &priv->rxq); + for (i = 0; i < priv->hw_params.max_txq_num; i++) + iwl_legacy_txq_update_write_ptr(priv, &priv->txq[i]); + priv->isr_stats.wakeup++; + handled |= CSR_INT_BIT_WAKEUP; + } + + /* All uCode command responses, including Tx command responses, + * Rx "responses" (frame-received notification), and other + * notifications from uCode come through here*/ + if (inta & (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX)) { + iwl4965_rx_handle(priv); + priv->isr_stats.rx++; + handled |= (CSR_INT_BIT_FH_RX | CSR_INT_BIT_SW_RX); + } + + /* This "Tx" DMA channel is used only for loading uCode */ + if (inta & CSR_INT_BIT_FH_TX) { + IWL_DEBUG_ISR(priv, "uCode load interrupt\n"); + priv->isr_stats.tx++; + handled |= CSR_INT_BIT_FH_TX; + /* Wake up uCode load routine, now that load is complete */ + priv->ucode_write_complete = 1; + wake_up_interruptible(&priv->wait_command_queue); + } + + if (inta & ~handled) { + IWL_ERR(priv, "Unhandled INTA bits 0x%08x\n", inta & ~handled); + priv->isr_stats.unhandled++; + } + + if (inta & ~(priv->inta_mask)) { + IWL_WARN(priv, "Disabled INTA bits 0x%08x were pending\n", + inta & ~priv->inta_mask); + IWL_WARN(priv, " with FH_INT = 0x%08x\n", inta_fh); + } + + /* Re-enable all interrupts */ + /* only Re-enable if diabled by irq */ + if (test_bit(STATUS_INT_ENABLED, &priv->status)) + iwl_legacy_enable_interrupts(priv); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (iwl_legacy_get_debug_level(priv) & (IWL_DL_ISR)) { + inta = iwl_read32(priv, CSR_INT); + inta_mask = iwl_read32(priv, CSR_INT_MASK); + inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); + IWL_DEBUG_ISR(priv, + "End inta 0x%08x, enabled 0x%08x, fh 0x%08x, " + "flags 0x%08lx\n", inta, inta_mask, inta_fh, flags); + } +#endif +} + +/***************************************************************************** + * + * sysfs attributes + * + *****************************************************************************/ + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + +/* + * The following adds a new attribute to the sysfs representation + * of this device driver (i.e. a new file in /sys/class/net/wlan0/device/) + * used for controlling the debug level. + * + * See the level definitions in iwl for details. + * + * The debug_level being managed using sysfs below is a per device debug + * level that is used instead of the global debug level if it (the per + * device debug level) is set. + */ +static ssize_t iwl4965_show_debug_level(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + return sprintf(buf, "0x%08X\n", iwl_legacy_get_debug_level(priv)); +} +static ssize_t iwl4965_store_debug_level(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 0, &val); + if (ret) + IWL_ERR(priv, "%s is not in hex or decimal form.\n", buf); + else { + priv->debug_level = val; + if (iwl_legacy_alloc_traffic_mem(priv)) + IWL_ERR(priv, + "Not enough memory to generate traffic log\n"); + } + return strnlen(buf, count); +} + +static DEVICE_ATTR(debug_level, S_IWUSR | S_IRUGO, + iwl4965_show_debug_level, iwl4965_store_debug_level); + + +#endif /* CONFIG_IWLWIFI_LEGACY_DEBUG */ + + +static ssize_t iwl4965_show_temperature(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + + if (!iwl_legacy_is_alive(priv)) + return -EAGAIN; + + return sprintf(buf, "%d\n", priv->temperature); +} + +static DEVICE_ATTR(temperature, S_IRUGO, iwl4965_show_temperature, NULL); + +static ssize_t iwl4965_show_tx_power(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + + if (!iwl_legacy_is_ready_rf(priv)) + return sprintf(buf, "off\n"); + else + return sprintf(buf, "%d\n", priv->tx_power_user_lmt); +} + +static ssize_t iwl4965_store_tx_power(struct device *d, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct iwl_priv *priv = dev_get_drvdata(d); + unsigned long val; + int ret; + + ret = strict_strtoul(buf, 10, &val); + if (ret) + IWL_INFO(priv, "%s is not in decimal form.\n", buf); + else { + ret = iwl_legacy_set_tx_power(priv, val, false); + if (ret) + IWL_ERR(priv, "failed setting tx power (0x%d).\n", + ret); + else + ret = count; + } + return ret; +} + +static DEVICE_ATTR(tx_power, S_IWUSR | S_IRUGO, + iwl4965_show_tx_power, iwl4965_store_tx_power); + +static struct attribute *iwl_sysfs_entries[] = { + &dev_attr_temperature.attr, + &dev_attr_tx_power.attr, +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + &dev_attr_debug_level.attr, +#endif + NULL +}; + +static struct attribute_group iwl_attribute_group = { + .name = NULL, /* put in device directory */ + .attrs = iwl_sysfs_entries, +}; + +/****************************************************************************** + * + * uCode download functions + * + ******************************************************************************/ + +static void iwl4965_dealloc_ucode_pci(struct iwl_priv *priv) +{ + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_code); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_data); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_init); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_init_data); + iwl_legacy_free_fw_desc(priv->pci_dev, &priv->ucode_boot); +} + +static void iwl4965_nic_start(struct iwl_priv *priv) +{ + /* Remove all resets to allow NIC to operate */ + iwl_write32(priv, CSR_RESET, 0); +} + +static void iwl4965_ucode_callback(const struct firmware *ucode_raw, + void *context); +static int iwl4965_mac_setup_register(struct iwl_priv *priv, + u32 max_probe_length); + +static int __must_check iwl4965_request_firmware(struct iwl_priv *priv, bool first) +{ + const char *name_pre = priv->cfg->fw_name_pre; + char tag[8]; + + if (first) { + priv->fw_index = priv->cfg->ucode_api_max; + sprintf(tag, "%d", priv->fw_index); + } else { + priv->fw_index--; + sprintf(tag, "%d", priv->fw_index); + } + + if (priv->fw_index < priv->cfg->ucode_api_min) { + IWL_ERR(priv, "no suitable firmware found!\n"); + return -ENOENT; + } + + sprintf(priv->firmware_name, "%s%s%s", name_pre, tag, ".ucode"); + + IWL_DEBUG_INFO(priv, "attempting to load firmware '%s'\n", + priv->firmware_name); + + return request_firmware_nowait(THIS_MODULE, 1, priv->firmware_name, + &priv->pci_dev->dev, GFP_KERNEL, priv, + iwl4965_ucode_callback); +} + +struct iwl4965_firmware_pieces { + const void *inst, *data, *init, *init_data, *boot; + size_t inst_size, data_size, init_size, init_data_size, boot_size; +}; + +static int iwl4965_load_firmware(struct iwl_priv *priv, + const struct firmware *ucode_raw, + struct iwl4965_firmware_pieces *pieces) +{ + struct iwl_ucode_header *ucode = (void *)ucode_raw->data; + u32 api_ver, hdr_size; + const u8 *src; + + priv->ucode_ver = le32_to_cpu(ucode->ver); + api_ver = IWL_UCODE_API(priv->ucode_ver); + + switch (api_ver) { + default: + case 0: + case 1: + case 2: + hdr_size = 24; + if (ucode_raw->size < hdr_size) { + IWL_ERR(priv, "File size too small!\n"); + return -EINVAL; + } + pieces->inst_size = le32_to_cpu(ucode->v1.inst_size); + pieces->data_size = le32_to_cpu(ucode->v1.data_size); + pieces->init_size = le32_to_cpu(ucode->v1.init_size); + pieces->init_data_size = + le32_to_cpu(ucode->v1.init_data_size); + pieces->boot_size = le32_to_cpu(ucode->v1.boot_size); + src = ucode->v1.data; + break; + } + + /* Verify size of file vs. image size info in file's header */ + if (ucode_raw->size != hdr_size + pieces->inst_size + + pieces->data_size + pieces->init_size + + pieces->init_data_size + pieces->boot_size) { + + IWL_ERR(priv, + "uCode file size %d does not match expected size\n", + (int)ucode_raw->size); + return -EINVAL; + } + + pieces->inst = src; + src += pieces->inst_size; + pieces->data = src; + src += pieces->data_size; + pieces->init = src; + src += pieces->init_size; + pieces->init_data = src; + src += pieces->init_data_size; + pieces->boot = src; + src += pieces->boot_size; + + return 0; +} + +/** + * iwl4965_ucode_callback - callback when firmware was loaded + * + * If loaded successfully, copies the firmware into buffers + * for the card to fetch (via DMA). + */ +static void +iwl4965_ucode_callback(const struct firmware *ucode_raw, void *context) +{ + struct iwl_priv *priv = context; + struct iwl_ucode_header *ucode; + int err; + struct iwl4965_firmware_pieces pieces; + const unsigned int api_max = priv->cfg->ucode_api_max; + const unsigned int api_min = priv->cfg->ucode_api_min; + u32 api_ver; + + u32 max_probe_length = 200; + u32 standard_phy_calibration_size = + IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; + + memset(&pieces, 0, sizeof(pieces)); + + if (!ucode_raw) { + if (priv->fw_index <= priv->cfg->ucode_api_max) + IWL_ERR(priv, + "request for firmware file '%s' failed.\n", + priv->firmware_name); + goto try_again; + } + + IWL_DEBUG_INFO(priv, "Loaded firmware file '%s' (%zd bytes).\n", + priv->firmware_name, ucode_raw->size); + + /* Make sure that we got at least the API version number */ + if (ucode_raw->size < 4) { + IWL_ERR(priv, "File size way too small!\n"); + goto try_again; + } + + /* Data from ucode file: header followed by uCode images */ + ucode = (struct iwl_ucode_header *)ucode_raw->data; + + err = iwl4965_load_firmware(priv, ucode_raw, &pieces); + + if (err) + goto try_again; + + api_ver = IWL_UCODE_API(priv->ucode_ver); + + /* + * api_ver should match the api version forming part of the + * firmware filename ... but we don't check for that and only rely + * on the API version read from firmware header from here on forward + */ + if (api_ver < api_min || api_ver > api_max) { + IWL_ERR(priv, + "Driver unable to support your firmware API. " + "Driver supports v%u, firmware is v%u.\n", + api_max, api_ver); + goto try_again; + } + + if (api_ver != api_max) + IWL_ERR(priv, + "Firmware has old API version. Expected v%u, " + "got v%u. New firmware can be obtained " + "from http://www.intellinuxwireless.org.\n", + api_max, api_ver); + + IWL_INFO(priv, "loaded firmware version %u.%u.%u.%u\n", + IWL_UCODE_MAJOR(priv->ucode_ver), + IWL_UCODE_MINOR(priv->ucode_ver), + IWL_UCODE_API(priv->ucode_ver), + IWL_UCODE_SERIAL(priv->ucode_ver)); + + snprintf(priv->hw->wiphy->fw_version, + sizeof(priv->hw->wiphy->fw_version), + "%u.%u.%u.%u", + IWL_UCODE_MAJOR(priv->ucode_ver), + IWL_UCODE_MINOR(priv->ucode_ver), + IWL_UCODE_API(priv->ucode_ver), + IWL_UCODE_SERIAL(priv->ucode_ver)); + + /* + * For any of the failures below (before allocating pci memory) + * we will try to load a version with a smaller API -- maybe the + * user just got a corrupted version of the latest API. + */ + + IWL_DEBUG_INFO(priv, "f/w package hdr ucode version raw = 0x%x\n", + priv->ucode_ver); + IWL_DEBUG_INFO(priv, "f/w package hdr runtime inst size = %Zd\n", + pieces.inst_size); + IWL_DEBUG_INFO(priv, "f/w package hdr runtime data size = %Zd\n", + pieces.data_size); + IWL_DEBUG_INFO(priv, "f/w package hdr init inst size = %Zd\n", + pieces.init_size); + IWL_DEBUG_INFO(priv, "f/w package hdr init data size = %Zd\n", + pieces.init_data_size); + IWL_DEBUG_INFO(priv, "f/w package hdr boot inst size = %Zd\n", + pieces.boot_size); + + /* Verify that uCode images will fit in card's SRAM */ + if (pieces.inst_size > priv->hw_params.max_inst_size) { + IWL_ERR(priv, "uCode instr len %Zd too large to fit in\n", + pieces.inst_size); + goto try_again; + } + + if (pieces.data_size > priv->hw_params.max_data_size) { + IWL_ERR(priv, "uCode data len %Zd too large to fit in\n", + pieces.data_size); + goto try_again; + } + + if (pieces.init_size > priv->hw_params.max_inst_size) { + IWL_ERR(priv, "uCode init instr len %Zd too large to fit in\n", + pieces.init_size); + goto try_again; + } + + if (pieces.init_data_size > priv->hw_params.max_data_size) { + IWL_ERR(priv, "uCode init data len %Zd too large to fit in\n", + pieces.init_data_size); + goto try_again; + } + + if (pieces.boot_size > priv->hw_params.max_bsm_size) { + IWL_ERR(priv, "uCode boot instr len %Zd too large to fit in\n", + pieces.boot_size); + goto try_again; + } + + /* Allocate ucode buffers for card's bus-master loading ... */ + + /* Runtime instructions and 2 copies of data: + * 1) unmodified from disk + * 2) backup cache for save/restore during power-downs */ + priv->ucode_code.len = pieces.inst_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_code); + + priv->ucode_data.len = pieces.data_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_data); + + priv->ucode_data_backup.len = pieces.data_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_data_backup); + + if (!priv->ucode_code.v_addr || !priv->ucode_data.v_addr || + !priv->ucode_data_backup.v_addr) + goto err_pci_alloc; + + /* Initialization instructions and data */ + if (pieces.init_size && pieces.init_data_size) { + priv->ucode_init.len = pieces.init_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_init); + + priv->ucode_init_data.len = pieces.init_data_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_init_data); + + if (!priv->ucode_init.v_addr || !priv->ucode_init_data.v_addr) + goto err_pci_alloc; + } + + /* Bootstrap (instructions only, no data) */ + if (pieces.boot_size) { + priv->ucode_boot.len = pieces.boot_size; + iwl_legacy_alloc_fw_desc(priv->pci_dev, &priv->ucode_boot); + + if (!priv->ucode_boot.v_addr) + goto err_pci_alloc; + } + + /* Now that we can no longer fail, copy information */ + + priv->sta_key_max_num = STA_KEY_MAX_NUM; + + /* Copy images into buffers for card's bus-master reads ... */ + + /* Runtime instructions (first block of data in file) */ + IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode instr len %Zd\n", + pieces.inst_size); + memcpy(priv->ucode_code.v_addr, pieces.inst, pieces.inst_size); + + IWL_DEBUG_INFO(priv, "uCode instr buf vaddr = 0x%p, paddr = 0x%08x\n", + priv->ucode_code.v_addr, (u32)priv->ucode_code.p_addr); + + /* + * Runtime data + * NOTE: Copy into backup buffer will be done in iwl_up() + */ + IWL_DEBUG_INFO(priv, "Copying (but not loading) uCode data len %Zd\n", + pieces.data_size); + memcpy(priv->ucode_data.v_addr, pieces.data, pieces.data_size); + memcpy(priv->ucode_data_backup.v_addr, pieces.data, pieces.data_size); + + /* Initialization instructions */ + if (pieces.init_size) { + IWL_DEBUG_INFO(priv, + "Copying (but not loading) init instr len %Zd\n", + pieces.init_size); + memcpy(priv->ucode_init.v_addr, pieces.init, pieces.init_size); + } + + /* Initialization data */ + if (pieces.init_data_size) { + IWL_DEBUG_INFO(priv, + "Copying (but not loading) init data len %Zd\n", + pieces.init_data_size); + memcpy(priv->ucode_init_data.v_addr, pieces.init_data, + pieces.init_data_size); + } + + /* Bootstrap instructions */ + IWL_DEBUG_INFO(priv, "Copying (but not loading) boot instr len %Zd\n", + pieces.boot_size); + memcpy(priv->ucode_boot.v_addr, pieces.boot, pieces.boot_size); + + /* + * figure out the offset of chain noise reset and gain commands + * base on the size of standard phy calibration commands table size + */ + priv->_4965.phy_calib_chain_noise_reset_cmd = + standard_phy_calibration_size; + priv->_4965.phy_calib_chain_noise_gain_cmd = + standard_phy_calibration_size + 1; + + /************************************************** + * This is still part of probe() in a sense... + * + * 9. Setup and register with mac80211 and debugfs + **************************************************/ + err = iwl4965_mac_setup_register(priv, max_probe_length); + if (err) + goto out_unbind; + + err = iwl_legacy_dbgfs_register(priv, DRV_NAME); + if (err) + IWL_ERR(priv, + "failed to create debugfs files. Ignoring error: %d\n", err); + + err = sysfs_create_group(&priv->pci_dev->dev.kobj, + &iwl_attribute_group); + if (err) { + IWL_ERR(priv, "failed to create sysfs device attributes\n"); + goto out_unbind; + } + + /* We have our copies now, allow OS release its copies */ + release_firmware(ucode_raw); + complete(&priv->_4965.firmware_loading_complete); + return; + + try_again: + /* try next, if any */ + if (iwl4965_request_firmware(priv, false)) + goto out_unbind; + release_firmware(ucode_raw); + return; + + err_pci_alloc: + IWL_ERR(priv, "failed to allocate pci memory\n"); + iwl4965_dealloc_ucode_pci(priv); + out_unbind: + complete(&priv->_4965.firmware_loading_complete); + device_release_driver(&priv->pci_dev->dev); + release_firmware(ucode_raw); +} + +static const char * const desc_lookup_text[] = { + "OK", + "FAIL", + "BAD_PARAM", + "BAD_CHECKSUM", + "NMI_INTERRUPT_WDG", + "SYSASSERT", + "FATAL_ERROR", + "BAD_COMMAND", + "HW_ERROR_TUNE_LOCK", + "HW_ERROR_TEMPERATURE", + "ILLEGAL_CHAN_FREQ", + "VCC_NOT_STABLE", + "FH_ERROR", + "NMI_INTERRUPT_HOST", + "NMI_INTERRUPT_ACTION_PT", + "NMI_INTERRUPT_UNKNOWN", + "UCODE_VERSION_MISMATCH", + "HW_ERROR_ABS_LOCK", + "HW_ERROR_CAL_LOCK_FAIL", + "NMI_INTERRUPT_INST_ACTION_PT", + "NMI_INTERRUPT_DATA_ACTION_PT", + "NMI_TRM_HW_ER", + "NMI_INTERRUPT_TRM", + "NMI_INTERRUPT_BREAK_POINT" + "DEBUG_0", + "DEBUG_1", + "DEBUG_2", + "DEBUG_3", +}; + +static struct { char *name; u8 num; } advanced_lookup[] = { + { "NMI_INTERRUPT_WDG", 0x34 }, + { "SYSASSERT", 0x35 }, + { "UCODE_VERSION_MISMATCH", 0x37 }, + { "BAD_COMMAND", 0x38 }, + { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, + { "FATAL_ERROR", 0x3D }, + { "NMI_TRM_HW_ERR", 0x46 }, + { "NMI_INTERRUPT_TRM", 0x4C }, + { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, + { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, + { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, + { "NMI_INTERRUPT_HOST", 0x66 }, + { "NMI_INTERRUPT_ACTION_PT", 0x7C }, + { "NMI_INTERRUPT_UNKNOWN", 0x84 }, + { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, + { "ADVANCED_SYSASSERT", 0 }, +}; + +static const char *iwl4965_desc_lookup(u32 num) +{ + int i; + int max = ARRAY_SIZE(desc_lookup_text); + + if (num < max) + return desc_lookup_text[num]; + + max = ARRAY_SIZE(advanced_lookup) - 1; + for (i = 0; i < max; i++) { + if (advanced_lookup[i].num == num) + break; + } + return advanced_lookup[i].name; +} + +#define ERROR_START_OFFSET (1 * sizeof(u32)) +#define ERROR_ELEM_SIZE (7 * sizeof(u32)) + +void iwl4965_dump_nic_error_log(struct iwl_priv *priv) +{ + u32 data2, line; + u32 desc, time, count, base, data1; + u32 blink1, blink2, ilink1, ilink2; + u32 pc, hcmd; + + if (priv->ucode_type == UCODE_INIT) { + base = le32_to_cpu(priv->card_alive_init.error_event_table_ptr); + } else { + base = le32_to_cpu(priv->card_alive.error_event_table_ptr); + } + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Not valid error log pointer 0x%08X for %s uCode\n", + base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT"); + return; + } + + count = iwl_legacy_read_targ_mem(priv, base); + + if (ERROR_START_OFFSET <= count * ERROR_ELEM_SIZE) { + IWL_ERR(priv, "Start IWL Error Log Dump:\n"); + IWL_ERR(priv, "Status: 0x%08lX, count: %d\n", + priv->status, count); + } + + desc = iwl_legacy_read_targ_mem(priv, base + 1 * sizeof(u32)); + priv->isr_stats.err_code = desc; + pc = iwl_legacy_read_targ_mem(priv, base + 2 * sizeof(u32)); + blink1 = iwl_legacy_read_targ_mem(priv, base + 3 * sizeof(u32)); + blink2 = iwl_legacy_read_targ_mem(priv, base + 4 * sizeof(u32)); + ilink1 = iwl_legacy_read_targ_mem(priv, base + 5 * sizeof(u32)); + ilink2 = iwl_legacy_read_targ_mem(priv, base + 6 * sizeof(u32)); + data1 = iwl_legacy_read_targ_mem(priv, base + 7 * sizeof(u32)); + data2 = iwl_legacy_read_targ_mem(priv, base + 8 * sizeof(u32)); + line = iwl_legacy_read_targ_mem(priv, base + 9 * sizeof(u32)); + time = iwl_legacy_read_targ_mem(priv, base + 11 * sizeof(u32)); + hcmd = iwl_legacy_read_targ_mem(priv, base + 22 * sizeof(u32)); + + trace_iwlwifi_legacy_dev_ucode_error(priv, desc, + time, data1, data2, line, + blink1, blink2, ilink1, ilink2); + + IWL_ERR(priv, "Desc Time " + "data1 data2 line\n"); + IWL_ERR(priv, "%-28s (0x%04X) %010u 0x%08X 0x%08X %u\n", + iwl4965_desc_lookup(desc), desc, time, data1, data2, line); + IWL_ERR(priv, "pc blink1 blink2 ilink1 ilink2 hcmd\n"); + IWL_ERR(priv, "0x%05X 0x%05X 0x%05X 0x%05X 0x%05X 0x%05X\n", + pc, blink1, blink2, ilink1, ilink2, hcmd); +} + +#define EVENT_START_OFFSET (4 * sizeof(u32)) + +/** + * iwl4965_print_event_log - Dump error event log to syslog + * + */ +static int iwl4965_print_event_log(struct iwl_priv *priv, u32 start_idx, + u32 num_events, u32 mode, + int pos, char **buf, size_t bufsz) +{ + u32 i; + u32 base; /* SRAM byte address of event log header */ + u32 event_size; /* 2 u32s, or 3 u32s if timestamp recorded */ + u32 ptr; /* SRAM byte address of log data */ + u32 ev, time, data; /* event log data */ + unsigned long reg_flags; + + if (num_events == 0) + return pos; + + if (priv->ucode_type == UCODE_INIT) { + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + } else { + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + } + + if (mode == 0) + event_size = 2 * sizeof(u32); + else + event_size = 3 * sizeof(u32); + + ptr = base + EVENT_START_OFFSET + (start_idx * event_size); + + /* Make sure device is powered up for SRAM reads */ + spin_lock_irqsave(&priv->reg_lock, reg_flags); + iwl_grab_nic_access(priv); + + /* Set starting address; reads will auto-increment */ + _iwl_legacy_write_direct32(priv, HBUS_TARG_MEM_RADDR, ptr); + rmb(); + + /* "time" is actually "data" for mode 0 (no timestamp). + * place event id # at far right for easier visual parsing. */ + for (i = 0; i < num_events; i++) { + ev = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + time = _iwl_legacy_read_direct32(priv, HBUS_TARG_MEM_RDAT); + if (mode == 0) { + /* data, ev */ + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOG:0x%08x:%04u\n", + time, ev); + } else { + trace_iwlwifi_legacy_dev_ucode_event(priv, 0, + time, ev); + IWL_ERR(priv, "EVT_LOG:0x%08x:%04u\n", + time, ev); + } + } else { + data = _iwl_legacy_read_direct32(priv, + HBUS_TARG_MEM_RDAT); + if (bufsz) { + pos += scnprintf(*buf + pos, bufsz - pos, + "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + } else { + IWL_ERR(priv, "EVT_LOGT:%010u:0x%08x:%04u\n", + time, data, ev); + trace_iwlwifi_legacy_dev_ucode_event(priv, time, + data, ev); + } + } + } + + /* Allow device to power down */ + iwl_release_nic_access(priv); + spin_unlock_irqrestore(&priv->reg_lock, reg_flags); + return pos; +} + +/** + * iwl4965_print_last_event_logs - Dump the newest # of event log to syslog + */ +static int iwl4965_print_last_event_logs(struct iwl_priv *priv, u32 capacity, + u32 num_wraps, u32 next_entry, + u32 size, u32 mode, + int pos, char **buf, size_t bufsz) +{ + /* + * display the newest DEFAULT_LOG_ENTRIES entries + * i.e the entries just before the next ont that uCode would fill. + */ + if (num_wraps) { + if (next_entry < size) { + pos = iwl4965_print_event_log(priv, + capacity - (size - next_entry), + size - next_entry, mode, + pos, buf, bufsz); + pos = iwl4965_print_event_log(priv, 0, + next_entry, mode, + pos, buf, bufsz); + } else + pos = iwl4965_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } else { + if (next_entry < size) { + pos = iwl4965_print_event_log(priv, 0, next_entry, + mode, pos, buf, bufsz); + } else { + pos = iwl4965_print_event_log(priv, next_entry - size, + size, mode, pos, buf, bufsz); + } + } + return pos; +} + +#define DEFAULT_DUMP_EVENT_LOG_ENTRIES (20) + +int iwl4965_dump_nic_event_log(struct iwl_priv *priv, bool full_log, + char **buf, bool display) +{ + u32 base; /* SRAM byte address of event log header */ + u32 capacity; /* event log capacity in # entries */ + u32 mode; /* 0 - no timestamp, 1 - timestamp recorded */ + u32 num_wraps; /* # times uCode wrapped to top of log */ + u32 next_entry; /* index of next entry to be written by uCode */ + u32 size; /* # entries that we'll print */ + int pos = 0; + size_t bufsz = 0; + + if (priv->ucode_type == UCODE_INIT) { + base = le32_to_cpu(priv->card_alive_init.log_event_table_ptr); + } else { + base = le32_to_cpu(priv->card_alive.log_event_table_ptr); + } + + if (!priv->cfg->ops->lib->is_valid_rtc_data_addr(base)) { + IWL_ERR(priv, + "Invalid event log pointer 0x%08X for %s uCode\n", + base, (priv->ucode_type == UCODE_INIT) ? "Init" : "RT"); + return -EINVAL; + } + + /* event log header */ + capacity = iwl_legacy_read_targ_mem(priv, base); + mode = iwl_legacy_read_targ_mem(priv, base + (1 * sizeof(u32))); + num_wraps = iwl_legacy_read_targ_mem(priv, base + (2 * sizeof(u32))); + next_entry = iwl_legacy_read_targ_mem(priv, base + (3 * sizeof(u32))); + + size = num_wraps ? capacity : next_entry; + + /* bail out if nothing in log */ + if (size == 0) { + IWL_ERR(priv, "Start IWL Event Log Dump: nothing in log\n"); + return pos; + } + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (!(iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) && !full_log) + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#else + size = (size > DEFAULT_DUMP_EVENT_LOG_ENTRIES) + ? DEFAULT_DUMP_EVENT_LOG_ENTRIES : size; +#endif + IWL_ERR(priv, "Start IWL Event Log Dump: display last %u entries\n", + size); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG + if (display) { + if (full_log) + bufsz = capacity * 48; + else + bufsz = size * 48; + *buf = kmalloc(bufsz, GFP_KERNEL); + if (!*buf) + return -ENOMEM; + } + if ((iwl_legacy_get_debug_level(priv) & IWL_DL_FW_ERRORS) || full_log) { + /* + * if uCode has wrapped back to top of log, + * start at the oldest entry, + * i.e the next one that uCode would fill. + */ + if (num_wraps) + pos = iwl4965_print_event_log(priv, next_entry, + capacity - next_entry, mode, + pos, buf, bufsz); + /* (then/else) start at top of log */ + pos = iwl4965_print_event_log(priv, 0, + next_entry, mode, pos, buf, bufsz); + } else + pos = iwl4965_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#else + pos = iwl4965_print_last_event_logs(priv, capacity, num_wraps, + next_entry, size, mode, + pos, buf, bufsz); +#endif + return pos; +} + +static void iwl4965_rf_kill_ct_config(struct iwl_priv *priv) +{ + struct iwl_ct_kill_config cmd; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&priv->lock, flags); + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT); + spin_unlock_irqrestore(&priv->lock, flags); + + cmd.critical_temperature_R = + cpu_to_le32(priv->hw_params.ct_kill_threshold); + + ret = iwl_legacy_send_cmd_pdu(priv, REPLY_CT_KILL_CONFIG_CMD, + sizeof(cmd), &cmd); + if (ret) + IWL_ERR(priv, "REPLY_CT_KILL_CONFIG_CMD failed\n"); + else + IWL_DEBUG_INFO(priv, "REPLY_CT_KILL_CONFIG_CMD " + "succeeded, " + "critical temperature is %d\n", + priv->hw_params.ct_kill_threshold); +} + +static const s8 default_queue_to_tx_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, + IWL49_CMD_FIFO_NUM, + IWL_TX_FIFO_UNUSED, + IWL_TX_FIFO_UNUSED, +}; + +static int iwl4965_alive_notify(struct iwl_priv *priv) +{ + u32 a; + unsigned long flags; + int i, chan; + u32 reg_val; + + spin_lock_irqsave(&priv->lock, flags); + + /* Clear 4965's internal Tx Scheduler data base */ + priv->scd_base_addr = iwl_legacy_read_prph(priv, + IWL49_SCD_SRAM_BASE_ADDR); + a = priv->scd_base_addr + IWL49_SCD_CONTEXT_DATA_OFFSET; + for (; a < priv->scd_base_addr + IWL49_SCD_TX_STTS_BITMAP_OFFSET; a += 4) + iwl_legacy_write_targ_mem(priv, a, 0); + for (; a < priv->scd_base_addr + IWL49_SCD_TRANSLATE_TBL_OFFSET; a += 4) + iwl_legacy_write_targ_mem(priv, a, 0); + for (; a < priv->scd_base_addr + + IWL49_SCD_TRANSLATE_TBL_OFFSET_QUEUE(priv->hw_params.max_txq_num); a += 4) + iwl_legacy_write_targ_mem(priv, a, 0); + + /* Tel 4965 where to find Tx byte count tables */ + iwl_legacy_write_prph(priv, IWL49_SCD_DRAM_BASE_ADDR, + priv->scd_bc_tbls.dma >> 10); + + /* Enable DMA channel */ + for (chan = 0; chan < FH49_TCSR_CHNL_NUM ; chan++) + iwl_legacy_write_direct32(priv, + FH_TCSR_CHNL_TX_CONFIG_REG(chan), + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | + FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); + + /* Update FH chicken bits */ + reg_val = iwl_legacy_read_direct32(priv, FH_TX_CHICKEN_BITS_REG); + iwl_legacy_write_direct32(priv, FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + /* Disable chain mode for all queues */ + iwl_legacy_write_prph(priv, IWL49_SCD_QUEUECHAIN_SEL, 0); + + /* Initialize each Tx queue (including the command queue) */ + for (i = 0; i < priv->hw_params.max_txq_num; i++) { + + /* TFD circular buffer read/write indexes */ + iwl_legacy_write_prph(priv, IWL49_SCD_QUEUE_RDPTR(i), 0); + iwl_legacy_write_direct32(priv, HBUS_TARG_WRPTR, 0 | (i << 8)); + + /* Max Tx Window size for Scheduler-ACK mode */ + iwl_legacy_write_targ_mem(priv, priv->scd_base_addr + + IWL49_SCD_CONTEXT_QUEUE_OFFSET(i), + (SCD_WIN_SIZE << + IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_POS) & + IWL49_SCD_QUEUE_CTX_REG1_WIN_SIZE_MSK); + + /* Frame limit */ + iwl_legacy_write_targ_mem(priv, priv->scd_base_addr + + IWL49_SCD_CONTEXT_QUEUE_OFFSET(i) + + sizeof(u32), + (SCD_FRAME_LIMIT << + IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IWL49_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK); + + } + iwl_legacy_write_prph(priv, IWL49_SCD_INTERRUPT_MASK, + (1 << priv->hw_params.max_txq_num) - 1); + + /* Activate all Tx DMA/FIFO channels */ + iwl4965_txq_set_sched(priv, IWL_MASK(0, 6)); + + iwl4965_set_wr_ptrs(priv, IWL_DEFAULT_CMD_QUEUE_NUM, 0); + + /* make sure all queue are not stopped */ + memset(&priv->queue_stopped[0], 0, sizeof(priv->queue_stopped)); + for (i = 0; i < 4; i++) + atomic_set(&priv->queue_stop_count[i], 0); + + /* reset to 0 to enable all the queue first */ + priv->txq_ctx_active_msk = 0; + /* Map each Tx/cmd queue to its corresponding fifo */ + BUILD_BUG_ON(ARRAY_SIZE(default_queue_to_tx_fifo) != 7); + + for (i = 0; i < ARRAY_SIZE(default_queue_to_tx_fifo); i++) { + int ac = default_queue_to_tx_fifo[i]; + + iwl_txq_ctx_activate(priv, i); + + if (ac == IWL_TX_FIFO_UNUSED) + continue; + + iwl4965_tx_queue_set_status(priv, &priv->txq[i], ac, 0); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return 0; +} + +/** + * iwl4965_alive_start - called after REPLY_ALIVE notification received + * from protocol/runtime uCode (initialization uCode's + * Alive gets handled by iwl_init_alive_start()). + */ +static void iwl4965_alive_start(struct iwl_priv *priv) +{ + int ret = 0; + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + + IWL_DEBUG_INFO(priv, "Runtime Alive received.\n"); + + if (priv->card_alive.is_valid != UCODE_VALID_OK) { + /* We had an error bringing up the hardware, so take it + * all the way back down so we can try again */ + IWL_DEBUG_INFO(priv, "Alive failed.\n"); + goto restart; + } + + /* Initialize uCode has loaded Runtime uCode ... verify inst image. + * This is a paranoid check, because we would not have gotten the + * "runtime" alive if code weren't properly loaded. */ + if (iwl4965_verify_ucode(priv)) { + /* Runtime instruction load was bad; + * take it all the way back down so we can try again */ + IWL_DEBUG_INFO(priv, "Bad runtime uCode load.\n"); + goto restart; + } + + ret = iwl4965_alive_notify(priv); + if (ret) { + IWL_WARN(priv, + "Could not complete ALIVE transition [ntf]: %d\n", ret); + goto restart; + } + + + /* After the ALIVE response, we can send host commands to the uCode */ + set_bit(STATUS_ALIVE, &priv->status); + + /* Enable watchdog to monitor the driver tx queues */ + iwl_legacy_setup_watchdog(priv); + + if (iwl_legacy_is_rfkill(priv)) + return; + + ieee80211_wake_queues(priv->hw); + + priv->active_rate = IWL_RATES_MASK; + + if (iwl_legacy_is_associated_ctx(ctx)) { + struct iwl_legacy_rxon_cmd *active_rxon = + (struct iwl_legacy_rxon_cmd *)&ctx->active; + /* apply any changes in staging */ + ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; + active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + } else { + struct iwl_rxon_context *tmp; + /* Initialize our rx_config data */ + for_each_context(priv, tmp) + iwl_legacy_connection_init_rx_config(priv, tmp); + + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); + } + + /* Configure bluetooth coexistence if enabled */ + iwl_legacy_send_bt_config(priv); + + iwl4965_reset_run_time_calib(priv); + + set_bit(STATUS_READY, &priv->status); + + /* Configure the adapter for unassociated operation */ + iwl_legacy_commit_rxon(priv, ctx); + + /* At this point, the NIC is initialized and operational */ + iwl4965_rf_kill_ct_config(priv); + + IWL_DEBUG_INFO(priv, "ALIVE processing complete.\n"); + wake_up_interruptible(&priv->wait_command_queue); + + iwl_legacy_power_update_mode(priv, true); + IWL_DEBUG_INFO(priv, "Updated power mode\n"); + + return; + + restart: + queue_work(priv->workqueue, &priv->restart); +} + +static void iwl4965_cancel_deferred_work(struct iwl_priv *priv); + +static void __iwl4965_down(struct iwl_priv *priv) +{ + unsigned long flags; + int exit_pending = test_bit(STATUS_EXIT_PENDING, &priv->status); + + IWL_DEBUG_INFO(priv, DRV_NAME " is going down\n"); + + iwl_legacy_scan_cancel_timeout(priv, 200); + + exit_pending = test_and_set_bit(STATUS_EXIT_PENDING, &priv->status); + + /* Stop TX queues watchdog. We need to have STATUS_EXIT_PENDING bit set + * to prevent rearm timer */ + del_timer_sync(&priv->watchdog); + + iwl_legacy_clear_ucode_stations(priv, NULL); + iwl_legacy_dealloc_bcast_stations(priv); + iwl_legacy_clear_driver_stations(priv); + + /* Unblock any waiting calls */ + wake_up_interruptible_all(&priv->wait_command_queue); + + /* Wipe out the EXIT_PENDING status bit if we are not actually + * exiting the module */ + if (!exit_pending) + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + /* stop and reset the on-board processor */ + iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + /* tell the device to stop sending interrupts */ + spin_lock_irqsave(&priv->lock, flags); + iwl_legacy_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + iwl4965_synchronize_irq(priv); + + if (priv->mac80211_registered) + ieee80211_stop_queues(priv->hw); + + /* If we have not previously called iwl_init() then + * clear all bits but the RF Kill bit and return */ + if (!iwl_legacy_is_init(priv)) { + priv->status = test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_GEO_CONFIGURED, &priv->status) << + STATUS_GEO_CONFIGURED | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + goto exit; + } + + /* ...otherwise clear out all the status bits but the RF Kill + * bit and continue taking the NIC down. */ + priv->status &= test_bit(STATUS_RF_KILL_HW, &priv->status) << + STATUS_RF_KILL_HW | + test_bit(STATUS_GEO_CONFIGURED, &priv->status) << + STATUS_GEO_CONFIGURED | + test_bit(STATUS_FW_ERROR, &priv->status) << + STATUS_FW_ERROR | + test_bit(STATUS_EXIT_PENDING, &priv->status) << + STATUS_EXIT_PENDING; + + iwl4965_txq_ctx_stop(priv); + iwl4965_rxq_stop(priv); + + /* Power-down device's busmaster DMA clocks */ + iwl_legacy_write_prph(priv, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); + udelay(5); + + /* Make sure (redundant) we've released our request to stay awake */ + iwl_legacy_clear_bit(priv, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + + /* Stop the device, and put it in low power state */ + iwl_legacy_apm_stop(priv); + + exit: + memset(&priv->card_alive, 0, sizeof(struct iwl_alive_resp)); + + dev_kfree_skb(priv->beacon_skb); + priv->beacon_skb = NULL; + + /* clear out any free frames */ + iwl4965_clear_free_frames(priv); +} + +static void iwl4965_down(struct iwl_priv *priv) +{ + mutex_lock(&priv->mutex); + __iwl4965_down(priv); + mutex_unlock(&priv->mutex); + + iwl4965_cancel_deferred_work(priv); +} + +#define HW_READY_TIMEOUT (50) + +static int iwl4965_set_hw_ready(struct iwl_priv *priv) +{ + int ret = 0; + + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); + + /* See if we got it */ + ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, + HW_READY_TIMEOUT); + if (ret != -ETIMEDOUT) + priv->hw_ready = true; + else + priv->hw_ready = false; + + IWL_DEBUG_INFO(priv, "hardware %s\n", + (priv->hw_ready == 1) ? "ready" : "not ready"); + return ret; +} + +static int iwl4965_prepare_card_hw(struct iwl_priv *priv) +{ + int ret = 0; + + IWL_DEBUG_INFO(priv, "iwl4965_prepare_card_hw enter\n"); + + ret = iwl4965_set_hw_ready(priv); + if (priv->hw_ready) + return ret; + + /* If HW is not ready, prepare the conditions to check again */ + iwl_legacy_set_bit(priv, CSR_HW_IF_CONFIG_REG, + CSR_HW_IF_CONFIG_REG_PREPARE); + + ret = iwl_poll_bit(priv, CSR_HW_IF_CONFIG_REG, + ~CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, + CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE, 150000); + + /* HW should be ready by now, check again. */ + if (ret != -ETIMEDOUT) + iwl4965_set_hw_ready(priv); + + return ret; +} + +#define MAX_HW_RESTARTS 5 + +static int __iwl4965_up(struct iwl_priv *priv) +{ + struct iwl_rxon_context *ctx; + int i; + int ret; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) { + IWL_WARN(priv, "Exit pending; will not bring the NIC up\n"); + return -EIO; + } + + if (!priv->ucode_data_backup.v_addr || !priv->ucode_data.v_addr) { + IWL_ERR(priv, "ucode not available for device bringup\n"); + return -EIO; + } + + for_each_context(priv, ctx) { + ret = iwl4965_alloc_bcast_station(priv, ctx); + if (ret) { + iwl_legacy_dealloc_bcast_stations(priv); + return ret; + } + } + + iwl4965_prepare_card_hw(priv); + + if (!priv->hw_ready) { + IWL_WARN(priv, "Exit HW not ready\n"); + return -EIO; + } + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (iwl_read32(priv, + CSR_GP_CNTRL) & CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(STATUS_RF_KILL_HW, &priv->status); + else + set_bit(STATUS_RF_KILL_HW, &priv->status); + + if (iwl_legacy_is_rfkill(priv)) { + wiphy_rfkill_set_hw_state(priv->hw->wiphy, true); + + iwl_legacy_enable_interrupts(priv); + IWL_WARN(priv, "Radio disabled by HW RF Kill switch\n"); + return 0; + } + + iwl_write32(priv, CSR_INT, 0xFFFFFFFF); + + /* must be initialised before iwl_hw_nic_init */ + priv->cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + + ret = iwl4965_hw_nic_init(priv); + if (ret) { + IWL_ERR(priv, "Unable to init nic\n"); + return ret; + } + + /* make sure rfkill handshake bits are cleared */ + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, + CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); + + /* clear (again), then enable host interrupts */ + iwl_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl_legacy_enable_interrupts(priv); + + /* really make sure rfkill handshake bits are cleared */ + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + iwl_write32(priv, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); + + /* Copy original ucode data image from disk into backup cache. + * This will be used to initialize the on-board processor's + * data SRAM for a clean start when the runtime program first loads. */ + memcpy(priv->ucode_data_backup.v_addr, priv->ucode_data.v_addr, + priv->ucode_data.len); + + for (i = 0; i < MAX_HW_RESTARTS; i++) { + + /* load bootstrap state machine, + * load bootstrap program into processor's memory, + * prepare to load the "initialize" uCode */ + ret = priv->cfg->ops->lib->load_ucode(priv); + + if (ret) { + IWL_ERR(priv, "Unable to set up bootstrap uCode: %d\n", + ret); + continue; + } + + /* start card; "initialize" will load runtime ucode */ + iwl4965_nic_start(priv); + + IWL_DEBUG_INFO(priv, DRV_NAME " is coming up\n"); + + return 0; + } + + set_bit(STATUS_EXIT_PENDING, &priv->status); + __iwl4965_down(priv); + clear_bit(STATUS_EXIT_PENDING, &priv->status); + + /* tried to restart and config the device for as long as our + * patience could withstand */ + IWL_ERR(priv, "Unable to initialize device after %d attempts.\n", i); + return -EIO; +} + + +/***************************************************************************** + * + * Workqueue callbacks + * + *****************************************************************************/ + +static void iwl4965_bg_init_alive_start(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, init_alive_start.work); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + priv->cfg->ops->lib->init_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_alive_start(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, alive_start.work); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl4965_alive_start(priv); + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_run_time_calib_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + run_time_calib_work); + + mutex_lock(&priv->mutex); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) { + mutex_unlock(&priv->mutex); + return; + } + + if (priv->start_calib) { + iwl4965_chain_noise_calibration(priv, + (void *)&priv->_4965.statistics); + iwl4965_sensitivity_calibration(priv, + (void *)&priv->_4965.statistics); + } + + mutex_unlock(&priv->mutex); +} + +static void iwl4965_bg_restart(struct work_struct *data) +{ + struct iwl_priv *priv = container_of(data, struct iwl_priv, restart); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + if (test_and_clear_bit(STATUS_FW_ERROR, &priv->status)) { + struct iwl_rxon_context *ctx; + + mutex_lock(&priv->mutex); + for_each_context(priv, ctx) + ctx->vif = NULL; + priv->is_open = 0; + + __iwl4965_down(priv); + + mutex_unlock(&priv->mutex); + iwl4965_cancel_deferred_work(priv); + ieee80211_restart_hw(priv->hw); + } else { + iwl4965_down(priv); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + __iwl4965_up(priv); + mutex_unlock(&priv->mutex); + } +} + +static void iwl4965_bg_rx_replenish(struct work_struct *data) +{ + struct iwl_priv *priv = + container_of(data, struct iwl_priv, rx_replenish); + + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + iwl4965_rx_replenish(priv); + mutex_unlock(&priv->mutex); +} + +/***************************************************************************** + * + * mac80211 entry point functions + * + *****************************************************************************/ + +#define UCODE_READY_TIMEOUT (4 * HZ) + +/* + * Not a mac80211 entry point function, but it fits in with all the + * other mac80211 functions grouped here. + */ +static int iwl4965_mac_setup_register(struct iwl_priv *priv, + u32 max_probe_length) +{ + int ret; + struct ieee80211_hw *hw = priv->hw; + struct iwl_rxon_context *ctx; + + hw->rate_control_algorithm = "iwl-4965-rs"; + + /* Tell mac80211 our characteristics */ + hw->flags = IEEE80211_HW_SIGNAL_DBM | + IEEE80211_HW_AMPDU_AGGREGATION | + IEEE80211_HW_NEED_DTIM_PERIOD | + IEEE80211_HW_SPECTRUM_MGMT | + IEEE80211_HW_REPORTS_TX_ACK_STATUS; + + if (priv->cfg->sku & IWL_SKU_N) + hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | + IEEE80211_HW_SUPPORTS_STATIC_SMPS; + + hw->sta_data_size = sizeof(struct iwl_station_priv); + hw->vif_data_size = sizeof(struct iwl_vif_priv); + + for_each_context(priv, ctx) { + hw->wiphy->interface_modes |= ctx->interface_modes; + hw->wiphy->interface_modes |= ctx->exclusive_interface_modes; + } + + hw->wiphy->flags |= WIPHY_FLAG_CUSTOM_REGULATORY | + WIPHY_FLAG_DISABLE_BEACON_HINTS; + + /* + * For now, disable PS by default because it affects + * RX performance significantly. + */ + hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; + + hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX; + /* we create the 802.11 header and a zero-length SSID element */ + hw->wiphy->max_scan_ie_len = max_probe_length - 24 - 2; + + /* Default value; 4 EDCA QOS priorities */ + hw->queues = 4; + + hw->max_listen_interval = IWL_CONN_MAX_LISTEN_INTERVAL; + + if (priv->bands[IEEE80211_BAND_2GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_2GHZ] = + &priv->bands[IEEE80211_BAND_2GHZ]; + if (priv->bands[IEEE80211_BAND_5GHZ].n_channels) + priv->hw->wiphy->bands[IEEE80211_BAND_5GHZ] = + &priv->bands[IEEE80211_BAND_5GHZ]; + + iwl_legacy_leds_init(priv); + + ret = ieee80211_register_hw(priv->hw); + if (ret) { + IWL_ERR(priv, "Failed to register hw (error %d)\n", ret); + return ret; + } + priv->mac80211_registered = 1; + + return 0; +} + + +int iwl4965_mac_start(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + int ret; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + /* we should be verifying the device is ready to be opened */ + mutex_lock(&priv->mutex); + ret = __iwl4965_up(priv); + mutex_unlock(&priv->mutex); + + if (ret) + return ret; + + if (iwl_legacy_is_rfkill(priv)) + goto out; + + IWL_DEBUG_INFO(priv, "Start UP work done.\n"); + + /* Wait for START_ALIVE from Run Time ucode. Otherwise callbacks from + * mac80211 will not be run successfully. */ + ret = wait_event_interruptible_timeout(priv->wait_command_queue, + test_bit(STATUS_READY, &priv->status), + UCODE_READY_TIMEOUT); + if (!ret) { + if (!test_bit(STATUS_READY, &priv->status)) { + IWL_ERR(priv, "START_ALIVE timeout after %dms.\n", + jiffies_to_msecs(UCODE_READY_TIMEOUT)); + return -ETIMEDOUT; + } + } + + iwl4965_led_enable(priv); + +out: + priv->is_open = 1; + IWL_DEBUG_MAC80211(priv, "leave\n"); + return 0; +} + +void iwl4965_mac_stop(struct ieee80211_hw *hw) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (!priv->is_open) + return; + + priv->is_open = 0; + + iwl4965_down(priv); + + flush_workqueue(priv->workqueue); + + /* enable interrupts again in order to receive rfkill changes */ + iwl_write32(priv, CSR_INT, 0xFFFFFFFF); + iwl_legacy_enable_interrupts(priv); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +void iwl4965_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct iwl_priv *priv = hw->priv; + + IWL_DEBUG_MACDUMP(priv, "enter\n"); + + IWL_DEBUG_TX(priv, "dev->xmit(%d bytes) at rate 0x%02x\n", skb->len, + ieee80211_get_tx_rate(hw, IEEE80211_SKB_CB(skb))->bitrate); + + if (iwl4965_tx_skb(priv, skb)) + dev_kfree_skb_any(skb); + + IWL_DEBUG_MACDUMP(priv, "leave\n"); +} + +void iwl4965_mac_update_tkip_key(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_key_conf *keyconf, + struct ieee80211_sta *sta, + u32 iv32, u16 *phase1key) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + iwl4965_update_tkip_key(priv, vif_priv->ctx, keyconf, sta, + iv32, phase1key); + + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +int iwl4965_mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, + struct ieee80211_vif *vif, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + struct iwl_rxon_context *ctx = vif_priv->ctx; + int ret; + u8 sta_id; + bool is_default_wep_key = false; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (priv->cfg->mod_params->sw_crypto) { + IWL_DEBUG_MAC80211(priv, "leave - hwcrypto disabled\n"); + return -EOPNOTSUPP; + } + + sta_id = iwl_legacy_sta_id_or_broadcast(priv, vif_priv->ctx, sta); + if (sta_id == IWL_INVALID_STATION) + return -EINVAL; + + mutex_lock(&priv->mutex); + iwl_legacy_scan_cancel_timeout(priv, 100); + + /* + * If we are getting WEP group key and we didn't receive any key mapping + * so far, we are in legacy wep mode (group key only), otherwise we are + * in 1X mode. + * In legacy wep mode, we use another host command to the uCode. + */ + if ((key->cipher == WLAN_CIPHER_SUITE_WEP40 || + key->cipher == WLAN_CIPHER_SUITE_WEP104) && + !sta) { + if (cmd == SET_KEY) + is_default_wep_key = !ctx->key_mapping_keys; + else + is_default_wep_key = + (key->hw_key_idx == HW_KEY_DEFAULT); + } + + switch (cmd) { + case SET_KEY: + if (is_default_wep_key) + ret = iwl4965_set_default_wep_key(priv, + vif_priv->ctx, key); + else + ret = iwl4965_set_dynamic_key(priv, vif_priv->ctx, + key, sta_id); + + IWL_DEBUG_MAC80211(priv, "enable hwcrypto key\n"); + break; + case DISABLE_KEY: + if (is_default_wep_key) + ret = iwl4965_remove_default_wep_key(priv, ctx, key); + else + ret = iwl4965_remove_dynamic_key(priv, ctx, + key, sta_id); + + IWL_DEBUG_MAC80211(priv, "disable hwcrypto key\n"); + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&priv->mutex); + IWL_DEBUG_MAC80211(priv, "leave\n"); + + return ret; +} + +int iwl4965_mac_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + enum ieee80211_ampdu_mlme_action action, + struct ieee80211_sta *sta, u16 tid, u16 *ssn, + u8 buf_size) +{ + struct iwl_priv *priv = hw->priv; + int ret = -EINVAL; + + IWL_DEBUG_HT(priv, "A-MPDU action on addr %pM tid %d\n", + sta->addr, tid); + + if (!(priv->cfg->sku & IWL_SKU_N)) + return -EACCES; + + mutex_lock(&priv->mutex); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + IWL_DEBUG_HT(priv, "start Rx\n"); + ret = iwl4965_sta_rx_agg_start(priv, sta, tid, *ssn); + break; + case IEEE80211_AMPDU_RX_STOP: + IWL_DEBUG_HT(priv, "stop Rx\n"); + ret = iwl4965_sta_rx_agg_stop(priv, sta, tid); + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + ret = 0; + break; + case IEEE80211_AMPDU_TX_START: + IWL_DEBUG_HT(priv, "start Tx\n"); + ret = iwl4965_tx_agg_start(priv, vif, sta, tid, ssn); + if (ret == 0) { + priv->_4965.agg_tids_count++; + IWL_DEBUG_HT(priv, "priv->_4965.agg_tids_count = %u\n", + priv->_4965.agg_tids_count); + } + break; + case IEEE80211_AMPDU_TX_STOP: + IWL_DEBUG_HT(priv, "stop Tx\n"); + ret = iwl4965_tx_agg_stop(priv, vif, sta, tid); + if ((ret == 0) && (priv->_4965.agg_tids_count > 0)) { + priv->_4965.agg_tids_count--; + IWL_DEBUG_HT(priv, "priv->_4965.agg_tids_count = %u\n", + priv->_4965.agg_tids_count); + } + if (test_bit(STATUS_EXIT_PENDING, &priv->status)) + ret = 0; + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + ret = 0; + break; + } + mutex_unlock(&priv->mutex); + + return ret; +} + +int iwl4965_mac_sta_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_priv *priv = hw->priv; + struct iwl_station_priv *sta_priv = (void *)sta->drv_priv; + struct iwl_vif_priv *vif_priv = (void *)vif->drv_priv; + bool is_ap = vif->type == NL80211_IFTYPE_STATION; + int ret; + u8 sta_id; + + IWL_DEBUG_INFO(priv, "received request to add station %pM\n", + sta->addr); + mutex_lock(&priv->mutex); + IWL_DEBUG_INFO(priv, "proceeding to add station %pM\n", + sta->addr); + sta_priv->common.sta_id = IWL_INVALID_STATION; + + atomic_set(&sta_priv->pending_frames, 0); + + ret = iwl_legacy_add_station_common(priv, vif_priv->ctx, sta->addr, + is_ap, sta, &sta_id); + if (ret) { + IWL_ERR(priv, "Unable to add station %pM (%d)\n", + sta->addr, ret); + /* Should we return success if return code is EEXIST ? */ + mutex_unlock(&priv->mutex); + return ret; + } + + sta_priv->common.sta_id = sta_id; + + /* Initialize rate scaling */ + IWL_DEBUG_INFO(priv, "Initializing rate scaling for station %pM\n", + sta->addr); + iwl4965_rs_rate_init(priv, sta, sta_id); + mutex_unlock(&priv->mutex); + + return 0; +} + +void iwl4965_mac_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_channel_switch *ch_switch) +{ + struct iwl_priv *priv = hw->priv; + const struct iwl_channel_info *ch_info; + struct ieee80211_conf *conf = &hw->conf; + struct ieee80211_channel *channel = ch_switch->channel; + struct iwl_ht_config *ht_conf = &priv->current_ht_config; + + struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; + u16 ch; + unsigned long flags = 0; + + IWL_DEBUG_MAC80211(priv, "enter\n"); + + if (iwl_legacy_is_rfkill(priv)) + goto out_exit; + + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) + goto out_exit; + + if (!iwl_legacy_is_associated_ctx(ctx)) + goto out_exit; + + /* channel switch in progress */ + if (priv->switch_rxon.switch_in_progress == true) + goto out_exit; + + mutex_lock(&priv->mutex); + if (priv->cfg->ops->lib->set_channel_switch) { + + ch = channel->hw_value; + if (le16_to_cpu(ctx->active.channel) != ch) { + ch_info = iwl_legacy_get_channel_info(priv, + channel->band, + ch); + if (!iwl_legacy_is_channel_valid(ch_info)) { + IWL_DEBUG_MAC80211(priv, "invalid channel\n"); + goto out; + } + spin_lock_irqsave(&priv->lock, flags); + + priv->current_ht_config.smps = conf->smps_mode; + + /* Configure HT40 channels */ + ctx->ht.enabled = conf_is_ht(conf); + if (ctx->ht.enabled) { + if (conf_is_ht40_minus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_BELOW; + ctx->ht.is_40mhz = true; + } else if (conf_is_ht40_plus(conf)) { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ctx->ht.is_40mhz = true; + } else { + ctx->ht.extension_chan_offset = + IEEE80211_HT_PARAM_CHA_SEC_NONE; + ctx->ht.is_40mhz = false; + } + } else + ctx->ht.is_40mhz = false; + + if ((le16_to_cpu(ctx->staging.channel) != ch)) + ctx->staging.flags = 0; + + iwl_legacy_set_rxon_channel(priv, channel, ctx); + iwl_legacy_set_rxon_ht(priv, ht_conf); + iwl_legacy_set_flags_for_band(priv, ctx, channel->band, + ctx->vif); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl_legacy_set_rate(priv); + /* + * at this point, staging_rxon has the + * configuration for channel switch + */ + if (priv->cfg->ops->lib->set_channel_switch(priv, + ch_switch)) + priv->switch_rxon.switch_in_progress = false; + } + } +out: + mutex_unlock(&priv->mutex); +out_exit: + if (!priv->switch_rxon.switch_in_progress) + ieee80211_chswitch_done(ctx->vif, false); + IWL_DEBUG_MAC80211(priv, "leave\n"); +} + +void iwl4965_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_priv *priv = hw->priv; + __le32 filter_or = 0, filter_nand = 0; + struct iwl_rxon_context *ctx; + +#define CHK(test, flag) do { \ + if (*total_flags & (test)) \ + filter_or |= (flag); \ + else \ + filter_nand |= (flag); \ + } while (0) + + IWL_DEBUG_MAC80211(priv, "Enter: changed: 0x%x, total: 0x%x\n", + changed_flags, *total_flags); + + CHK(FIF_OTHER_BSS | FIF_PROMISC_IN_BSS, RXON_FILTER_PROMISC_MSK); + /* Setting _just_ RXON_FILTER_CTL2HOST_MSK causes FH errors */ + CHK(FIF_CONTROL, RXON_FILTER_CTL2HOST_MSK | RXON_FILTER_PROMISC_MSK); + CHK(FIF_BCN_PRBRESP_PROMISC, RXON_FILTER_BCON_AWARE_MSK); + +#undef CHK + + mutex_lock(&priv->mutex); + + for_each_context(priv, ctx) { + ctx->staging.filter_flags &= ~filter_nand; + ctx->staging.filter_flags |= filter_or; + + /* + * Not committing directly because hardware can perform a scan, + * but we'll eventually commit the filter flags change anyway. + */ + } + + mutex_unlock(&priv->mutex); + + /* + * Receiving all multicast frames is always enabled by the + * default flags setup in iwl_legacy_connection_init_rx_config() + * since we currently do not support programming multicast + * filters into the device. + */ + *total_flags &= FIF_OTHER_BSS | FIF_ALLMULTI | FIF_PROMISC_IN_BSS | + FIF_BCN_PRBRESP_PROMISC | FIF_CONTROL; +} + +/***************************************************************************** + * + * driver setup and teardown + * + *****************************************************************************/ + +static void iwl4965_bg_txpower_work(struct work_struct *work) +{ + struct iwl_priv *priv = container_of(work, struct iwl_priv, + txpower_work); + + /* If a scan happened to start before we got here + * then just return; the statistics notification will + * kick off another scheduled work to compensate for + * any temperature delta we missed here. */ + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || + test_bit(STATUS_SCANNING, &priv->status)) + return; + + mutex_lock(&priv->mutex); + + /* Regardless of if we are associated, we must reconfigure the + * TX power since frames can be sent on non-radar channels while + * not associated */ + priv->cfg->ops->lib->send_tx_power(priv); + + /* Update last_temperature to keep is_calib_needed from running + * when it isn't needed... */ + priv->last_temperature = priv->temperature; + + mutex_unlock(&priv->mutex); +} + +static void iwl4965_setup_deferred_work(struct iwl_priv *priv) +{ + priv->workqueue = create_singlethread_workqueue(DRV_NAME); + + init_waitqueue_head(&priv->wait_command_queue); + + INIT_WORK(&priv->restart, iwl4965_bg_restart); + INIT_WORK(&priv->rx_replenish, iwl4965_bg_rx_replenish); + INIT_WORK(&priv->run_time_calib_work, iwl4965_bg_run_time_calib_work); + INIT_DELAYED_WORK(&priv->init_alive_start, iwl4965_bg_init_alive_start); + INIT_DELAYED_WORK(&priv->alive_start, iwl4965_bg_alive_start); + + iwl_legacy_setup_scan_deferred_work(priv); + + INIT_WORK(&priv->txpower_work, iwl4965_bg_txpower_work); + + init_timer(&priv->statistics_periodic); + priv->statistics_periodic.data = (unsigned long)priv; + priv->statistics_periodic.function = iwl4965_bg_statistics_periodic; + + init_timer(&priv->ucode_trace); + priv->ucode_trace.data = (unsigned long)priv; + priv->ucode_trace.function = iwl4965_bg_ucode_trace; + + init_timer(&priv->watchdog); + priv->watchdog.data = (unsigned long)priv; + priv->watchdog.function = iwl_legacy_bg_watchdog; + + tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long)) + iwl4965_irq_tasklet, (unsigned long)priv); +} + +static void iwl4965_cancel_deferred_work(struct iwl_priv *priv) +{ + cancel_work_sync(&priv->txpower_work); + cancel_delayed_work_sync(&priv->init_alive_start); + cancel_delayed_work(&priv->alive_start); + cancel_work_sync(&priv->run_time_calib_work); + + iwl_legacy_cancel_scan_deferred_work(priv); + + del_timer_sync(&priv->statistics_periodic); + del_timer_sync(&priv->ucode_trace); +} + +static void iwl4965_init_hw_rates(struct iwl_priv *priv, + struct ieee80211_rate *rates) +{ + int i; + + for (i = 0; i < IWL_RATE_COUNT_LEGACY; i++) { + rates[i].bitrate = iwlegacy_rates[i].ieee * 5; + rates[i].hw_value = i; /* Rate scaling will work on indexes */ + rates[i].hw_value_short = i; + rates[i].flags = 0; + if ((i >= IWL_FIRST_CCK_RATE) && (i <= IWL_LAST_CCK_RATE)) { + /* + * If CCK != 1M then set short preamble rate flag. + */ + rates[i].flags |= + (iwlegacy_rates[i].plcp == IWL_RATE_1M_PLCP) ? + 0 : IEEE80211_RATE_SHORT_PREAMBLE; + } + } +} +/* + * Acquire priv->lock before calling this function ! + */ +void iwl4965_set_wr_ptrs(struct iwl_priv *priv, int txq_id, u32 index) +{ + iwl_legacy_write_direct32(priv, HBUS_TARG_WRPTR, + (index & 0xff) | (txq_id << 8)); + iwl_legacy_write_prph(priv, IWL49_SCD_QUEUE_RDPTR(txq_id), index); +} + +void iwl4965_tx_queue_set_status(struct iwl_priv *priv, + struct iwl_tx_queue *txq, + int tx_fifo_id, int scd_retry) +{ + int txq_id = txq->q.id; + + /* Find out whether to activate Tx queue */ + int active = test_bit(txq_id, &priv->txq_ctx_active_msk) ? 1 : 0; + + /* Set up and activate */ + iwl_legacy_write_prph(priv, IWL49_SCD_QUEUE_STATUS_BITS(txq_id), + (active << IWL49_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (tx_fifo_id << IWL49_SCD_QUEUE_STTS_REG_POS_TXF) | + (scd_retry << IWL49_SCD_QUEUE_STTS_REG_POS_WSL) | + (scd_retry << IWL49_SCD_QUEUE_STTS_REG_POS_SCD_ACK) | + IWL49_SCD_QUEUE_STTS_REG_MSK); + + txq->sched_retry = scd_retry; + + IWL_DEBUG_INFO(priv, "%s %s Queue %d on AC %d\n", + active ? "Activate" : "Deactivate", + scd_retry ? "BA" : "AC", txq_id, tx_fifo_id); +} + + +static int iwl4965_init_drv(struct iwl_priv *priv) +{ + int ret; + + spin_lock_init(&priv->sta_lock); + spin_lock_init(&priv->hcmd_lock); + + INIT_LIST_HEAD(&priv->free_frames); + + mutex_init(&priv->mutex); + mutex_init(&priv->sync_cmd_mutex); + + priv->ieee_channels = NULL; + priv->ieee_rates = NULL; + priv->band = IEEE80211_BAND_2GHZ; + + priv->iw_mode = NL80211_IFTYPE_STATION; + priv->current_ht_config.smps = IEEE80211_SMPS_STATIC; + priv->missed_beacon_threshold = IWL_MISSED_BEACON_THRESHOLD_DEF; + priv->_4965.agg_tids_count = 0; + + /* initialize force reset */ + priv->force_reset[IWL_RF_RESET].reset_duration = + IWL_DELAY_NEXT_FORCE_RF_RESET; + priv->force_reset[IWL_FW_RESET].reset_duration = + IWL_DELAY_NEXT_FORCE_FW_RELOAD; + + /* Choose which receivers/antennas to use */ + if (priv->cfg->ops->hcmd->set_rxon_chain) + priv->cfg->ops->hcmd->set_rxon_chain(priv, + &priv->contexts[IWL_RXON_CTX_BSS]); + + iwl_legacy_init_scan_params(priv); + + /* Set the tx_power_user_lmt to the lowest power level + * this value will get overwritten by channel max power avg + * from eeprom */ + priv->tx_power_user_lmt = IWL4965_TX_POWER_TARGET_POWER_MIN; + priv->tx_power_next = IWL4965_TX_POWER_TARGET_POWER_MIN; + + ret = iwl_legacy_init_channel_map(priv); + if (ret) { + IWL_ERR(priv, "initializing regulatory failed: %d\n", ret); + goto err; + } + + ret = iwl_legacy_init_geos(priv); + if (ret) { + IWL_ERR(priv, "initializing geos failed: %d\n", ret); + goto err_free_channel_map; + } + iwl4965_init_hw_rates(priv, priv->ieee_rates); + + return 0; + +err_free_channel_map: + iwl_legacy_free_channel_map(priv); +err: + return ret; +} + +static void iwl4965_uninit_drv(struct iwl_priv *priv) +{ + iwl4965_calib_free_results(priv); + iwl_legacy_free_geos(priv); + iwl_legacy_free_channel_map(priv); + kfree(priv->scan_cmd); +} + +static void iwl4965_hw_detect(struct iwl_priv *priv) +{ + priv->hw_rev = _iwl_legacy_read32(priv, CSR_HW_REV); + priv->hw_wa_rev = _iwl_legacy_read32(priv, CSR_HW_REV_WA_REG); + pci_read_config_byte(priv->pci_dev, PCI_REVISION_ID, &priv->rev_id); + IWL_DEBUG_INFO(priv, "HW Revision ID = 0x%X\n", priv->rev_id); +} + +static int iwl4965_set_hw_params(struct iwl_priv *priv) +{ + priv->hw_params.max_rxq_size = RX_QUEUE_SIZE; + priv->hw_params.max_rxq_log = RX_QUEUE_SIZE_LOG; + if (priv->cfg->mod_params->amsdu_size_8K) + priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_8K); + else + priv->hw_params.rx_page_order = get_order(IWL_RX_BUF_SIZE_4K); + + priv->hw_params.max_beacon_itrvl = IWL_MAX_UCODE_BEACON_INTERVAL; + + if (priv->cfg->mod_params->disable_11n) + priv->cfg->sku &= ~IWL_SKU_N; + + /* Device-specific setup */ + return priv->cfg->ops->lib->set_hw_params(priv); +} + +static const u8 iwl4965_bss_ac_to_fifo[] = { + IWL_TX_FIFO_VO, + IWL_TX_FIFO_VI, + IWL_TX_FIFO_BE, + IWL_TX_FIFO_BK, +}; + +static const u8 iwl4965_bss_ac_to_queue[] = { + 0, 1, 2, 3, +}; + +static int +iwl4965_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = 0, i; + struct iwl_priv *priv; + struct ieee80211_hw *hw; + struct iwl_cfg *cfg = (struct iwl_cfg *)(ent->driver_data); + unsigned long flags; + u16 pci_cmd; + + /************************ + * 1. Allocating HW data + ************************/ + + hw = iwl_legacy_alloc_all(cfg); + if (!hw) { + err = -ENOMEM; + goto out; + } + priv = hw->priv; + /* At this point both hw and priv are allocated. */ + + /* + * The default context is always valid, + * more may be discovered when firmware + * is loaded. + */ + priv->valid_contexts = BIT(IWL_RXON_CTX_BSS); + + for (i = 0; i < NUM_IWL_RXON_CTX; i++) + priv->contexts[i].ctxid = i; + + priv->contexts[IWL_RXON_CTX_BSS].always_active = true; + priv->contexts[IWL_RXON_CTX_BSS].is_active = true; + priv->contexts[IWL_RXON_CTX_BSS].rxon_cmd = REPLY_RXON; + priv->contexts[IWL_RXON_CTX_BSS].rxon_timing_cmd = REPLY_RXON_TIMING; + priv->contexts[IWL_RXON_CTX_BSS].rxon_assoc_cmd = REPLY_RXON_ASSOC; + priv->contexts[IWL_RXON_CTX_BSS].qos_cmd = REPLY_QOS_PARAM; + priv->contexts[IWL_RXON_CTX_BSS].ap_sta_id = IWL_AP_ID; + priv->contexts[IWL_RXON_CTX_BSS].wep_key_cmd = REPLY_WEPKEY; + priv->contexts[IWL_RXON_CTX_BSS].ac_to_fifo = iwl4965_bss_ac_to_fifo; + priv->contexts[IWL_RXON_CTX_BSS].ac_to_queue = iwl4965_bss_ac_to_queue; + priv->contexts[IWL_RXON_CTX_BSS].exclusive_interface_modes = + BIT(NL80211_IFTYPE_ADHOC); + priv->contexts[IWL_RXON_CTX_BSS].interface_modes = + BIT(NL80211_IFTYPE_STATION); + priv->contexts[IWL_RXON_CTX_BSS].ap_devtype = RXON_DEV_TYPE_AP; + priv->contexts[IWL_RXON_CTX_BSS].ibss_devtype = RXON_DEV_TYPE_IBSS; + priv->contexts[IWL_RXON_CTX_BSS].station_devtype = RXON_DEV_TYPE_ESS; + priv->contexts[IWL_RXON_CTX_BSS].unused_devtype = RXON_DEV_TYPE_ESS; + + BUILD_BUG_ON(NUM_IWL_RXON_CTX != 1); + + SET_IEEE80211_DEV(hw, &pdev->dev); + + IWL_DEBUG_INFO(priv, "*** LOAD DRIVER ***\n"); + priv->cfg = cfg; + priv->pci_dev = pdev; + priv->inta_mask = CSR_INI_SET_MASK; + + if (iwl_legacy_alloc_traffic_mem(priv)) + IWL_ERR(priv, "Not enough memory to generate traffic log\n"); + + /************************** + * 2. Initializing PCI bus + **************************/ + pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM); + + if (pci_enable_device(pdev)) { + err = -ENODEV; + goto out_ieee80211_free_hw; + } + + pci_set_master(pdev); + + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(36)); + if (err) { + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); + if (!err) + err = pci_set_consistent_dma_mask(pdev, + DMA_BIT_MASK(32)); + /* both attempts failed: */ + if (err) { + IWL_WARN(priv, "No suitable DMA available.\n"); + goto out_pci_disable_device; + } + } + + err = pci_request_regions(pdev, DRV_NAME); + if (err) + goto out_pci_disable_device; + + pci_set_drvdata(pdev, priv); + + + /*********************** + * 3. Read REV register + ***********************/ + priv->hw_base = pci_iomap(pdev, 0, 0); + if (!priv->hw_base) { + err = -ENODEV; + goto out_pci_release_regions; + } + + IWL_DEBUG_INFO(priv, "pci_resource_len = 0x%08llx\n", + (unsigned long long) pci_resource_len(pdev, 0)); + IWL_DEBUG_INFO(priv, "pci_resource_base = %p\n", priv->hw_base); + + /* these spin locks will be used in apm_ops.init and EEPROM access + * we should init now + */ + spin_lock_init(&priv->reg_lock); + spin_lock_init(&priv->lock); + + /* + * stop and reset the on-board processor just in case it is in a + * strange state ... like being left stranded by a primary kernel + * and this is now the kdump kernel trying to start up + */ + iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET); + + iwl4965_hw_detect(priv); + IWL_INFO(priv, "Detected %s, REV=0x%X\n", + priv->cfg->name, priv->hw_rev); + + /* We disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state */ + pci_write_config_byte(pdev, PCI_CFG_RETRY_TIMEOUT, 0x00); + + iwl4965_prepare_card_hw(priv); + if (!priv->hw_ready) { + IWL_WARN(priv, "Failed, HW not ready\n"); + goto out_iounmap; + } + + /***************** + * 4. Read EEPROM + *****************/ + /* Read the EEPROM */ + err = iwl_legacy_eeprom_init(priv); + if (err) { + IWL_ERR(priv, "Unable to init EEPROM\n"); + goto out_iounmap; + } + err = iwl4965_eeprom_check_version(priv); + if (err) + goto out_free_eeprom; + + if (err) + goto out_free_eeprom; + + /* extract MAC Address */ + iwl4965_eeprom_get_mac(priv, priv->addresses[0].addr); + IWL_DEBUG_INFO(priv, "MAC address: %pM\n", priv->addresses[0].addr); + priv->hw->wiphy->addresses = priv->addresses; + priv->hw->wiphy->n_addresses = 1; + + /************************ + * 5. Setup HW constants + ************************/ + if (iwl4965_set_hw_params(priv)) { + IWL_ERR(priv, "failed to set hw parameters\n"); + goto out_free_eeprom; + } + + /******************* + * 6. Setup priv + *******************/ + + err = iwl4965_init_drv(priv); + if (err) + goto out_free_eeprom; + /* At this point both hw and priv are initialized. */ + + /******************** + * 7. Setup services + ********************/ + spin_lock_irqsave(&priv->lock, flags); + iwl_legacy_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + pci_enable_msi(priv->pci_dev); + + err = request_irq(priv->pci_dev->irq, iwl_legacy_isr, + IRQF_SHARED, DRV_NAME, priv); + if (err) { + IWL_ERR(priv, "Error allocating IRQ %d\n", priv->pci_dev->irq); + goto out_disable_msi; + } + + iwl4965_setup_deferred_work(priv); + iwl4965_setup_rx_handlers(priv); + + /********************************************* + * 8. Enable interrupts and read RFKILL state + *********************************************/ + + /* enable interrupts if needed: hw bug w/a */ + pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(priv->pci_dev, PCI_COMMAND, pci_cmd); + } + + iwl_legacy_enable_interrupts(priv); + + /* If platform's RF_KILL switch is NOT set to KILL */ + if (iwl_read32(priv, CSR_GP_CNTRL) & + CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) + clear_bit(STATUS_RF_KILL_HW, &priv->status); + else + set_bit(STATUS_RF_KILL_HW, &priv->status); + + wiphy_rfkill_set_hw_state(priv->hw->wiphy, + test_bit(STATUS_RF_KILL_HW, &priv->status)); + + iwl_legacy_power_initialize(priv); + + init_completion(&priv->_4965.firmware_loading_complete); + + err = iwl4965_request_firmware(priv, true); + if (err) + goto out_destroy_workqueue; + + return 0; + + out_destroy_workqueue: + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + free_irq(priv->pci_dev->irq, priv); + out_disable_msi: + pci_disable_msi(priv->pci_dev); + iwl4965_uninit_drv(priv); + out_free_eeprom: + iwl_legacy_eeprom_free(priv); + out_iounmap: + pci_iounmap(pdev, priv->hw_base); + out_pci_release_regions: + pci_set_drvdata(pdev, NULL); + pci_release_regions(pdev); + out_pci_disable_device: + pci_disable_device(pdev); + out_ieee80211_free_hw: + iwl_legacy_free_traffic_mem(priv); + ieee80211_free_hw(priv->hw); + out: + return err; +} + +static void __devexit iwl4965_pci_remove(struct pci_dev *pdev) +{ + struct iwl_priv *priv = pci_get_drvdata(pdev); + unsigned long flags; + + if (!priv) + return; + + wait_for_completion(&priv->_4965.firmware_loading_complete); + + IWL_DEBUG_INFO(priv, "*** UNLOAD DRIVER ***\n"); + + iwl_legacy_dbgfs_unregister(priv); + sysfs_remove_group(&pdev->dev.kobj, &iwl_attribute_group); + + /* ieee80211_unregister_hw call wil cause iwl_mac_stop to + * to be called and iwl4965_down since we are removing the device + * we need to set STATUS_EXIT_PENDING bit. + */ + set_bit(STATUS_EXIT_PENDING, &priv->status); + + iwl_legacy_leds_exit(priv); + + if (priv->mac80211_registered) { + ieee80211_unregister_hw(priv->hw); + priv->mac80211_registered = 0; + } else { + iwl4965_down(priv); + } + + /* + * Make sure device is reset to low power before unloading driver. + * This may be redundant with iwl4965_down(), but there are paths to + * run iwl4965_down() without calling apm_ops.stop(), and there are + * paths to avoid running iwl4965_down() at all before leaving driver. + * This (inexpensive) call *makes sure* device is reset. + */ + iwl_legacy_apm_stop(priv); + + /* make sure we flush any pending irq or + * tasklet for the driver + */ + spin_lock_irqsave(&priv->lock, flags); + iwl_legacy_disable_interrupts(priv); + spin_unlock_irqrestore(&priv->lock, flags); + + iwl4965_synchronize_irq(priv); + + iwl4965_dealloc_ucode_pci(priv); + + if (priv->rxq.bd) + iwl4965_rx_queue_free(priv, &priv->rxq); + iwl4965_hw_txq_ctx_free(priv); + + iwl_legacy_eeprom_free(priv); + + + /*netif_stop_queue(dev); */ + flush_workqueue(priv->workqueue); + + /* ieee80211_unregister_hw calls iwl_mac_stop, which flushes + * priv->workqueue... so we can't take down the workqueue + * until now... */ + destroy_workqueue(priv->workqueue); + priv->workqueue = NULL; + iwl_legacy_free_traffic_mem(priv); + + free_irq(priv->pci_dev->irq, priv); + pci_disable_msi(priv->pci_dev); + pci_iounmap(pdev, priv->hw_base); + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + iwl4965_uninit_drv(priv); + + dev_kfree_skb(priv->beacon_skb); + + ieee80211_free_hw(priv->hw); +} + +/* + * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask + * must be called under priv->lock and mac access + */ +void iwl4965_txq_set_sched(struct iwl_priv *priv, u32 mask) +{ + iwl_legacy_write_prph(priv, IWL49_SCD_TXFACT, mask); +} + +/***************************************************************************** + * + * driver and module entry point + * + *****************************************************************************/ + +/* Hardware specific file defines the PCI IDs table for that hardware module */ +static DEFINE_PCI_DEVICE_TABLE(iwl4965_hw_card_ids) = { +#if defined(CONFIG_IWL4965_MODULE) || defined(CONFIG_IWL4965) + {IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_cfg)}, + {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_cfg)}, +#endif /* CONFIG_IWL4965 */ + + {0} +}; +MODULE_DEVICE_TABLE(pci, iwl4965_hw_card_ids); + +static struct pci_driver iwl4965_driver = { + .name = DRV_NAME, + .id_table = iwl4965_hw_card_ids, + .probe = iwl4965_pci_probe, + .remove = __devexit_p(iwl4965_pci_remove), + .driver.pm = IWL_LEGACY_PM_OPS, +}; + +static int __init iwl4965_init(void) +{ + + int ret; + pr_info(DRV_DESCRIPTION ", " DRV_VERSION "\n"); + pr_info(DRV_COPYRIGHT "\n"); + + ret = iwl4965_rate_control_register(); + if (ret) { + pr_err("Unable to register rate control algorithm: %d\n", ret); + return ret; + } + + ret = pci_register_driver(&iwl4965_driver); + if (ret) { + pr_err("Unable to initialize PCI module\n"); + goto error_register; + } + + return ret; + +error_register: + iwl4965_rate_control_unregister(); + return ret; +} + +static void __exit iwl4965_exit(void) +{ + pci_unregister_driver(&iwl4965_driver); + iwl4965_rate_control_unregister(); +} + +module_exit(iwl4965_exit); +module_init(iwl4965_init); + +#ifdef CONFIG_IWLWIFI_LEGACY_DEBUG +module_param_named(debug, iwlegacy_debug_level, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "debug output mask"); +#endif + +module_param_named(swcrypto, iwl4965_mod_params.sw_crypto, int, S_IRUGO); +MODULE_PARM_DESC(swcrypto, "using crypto in software (default 0 [hardware])"); +module_param_named(queues_num, iwl4965_mod_params.num_of_queues, int, S_IRUGO); +MODULE_PARM_DESC(queues_num, "number of hw queues."); +module_param_named(11n_disable, iwl4965_mod_params.disable_11n, int, S_IRUGO); +MODULE_PARM_DESC(11n_disable, "disable 11n functionality"); +module_param_named(amsdu_size_8K, iwl4965_mod_params.amsdu_size_8K, + int, S_IRUGO); +MODULE_PARM_DESC(amsdu_size_8K, "enable 8K amsdu size"); +module_param_named(fw_restart, iwl4965_mod_params.restart_fw, int, S_IRUGO); +MODULE_PARM_DESC(fw_restart, "restart firmware in case of error"); diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index e1e3b1cf3cf..17d555f2215 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -1,18 +1,52 @@ -config IWLWIFI - tristate "Intel Wireless Wifi" +config IWLAGN + tristate "Intel Wireless WiFi Next Gen AGN - Wireless-N/Advanced-N/Ultimate-N (iwlagn) " depends on PCI && MAC80211 select FW_LOADER select NEW_LEDS select LEDS_CLASS select LEDS_TRIGGERS select MAC80211_LEDS + ---help--- + Select to build the driver supporting the: + + Intel Wireless WiFi Link Next-Gen AGN + + This option enables support for use with the following hardware: + Intel Wireless WiFi Link 6250AGN Adapter + Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN) + Intel WiFi Link 1000BGN + Intel Wireless WiFi 5150AGN + Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN + Intel 6005 Series Wi-Fi Adapters + Intel 6030 Series Wi-Fi Adapters + Intel Wireless WiFi Link 6150BGN 2 Adapter + Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) + Intel 2000 Series Wi-Fi Adapters + + + This driver uses the kernel's mac80211 subsystem. + + In order to use this driver, you will need a microcode (uCode) + image for it. You can obtain the microcode from: + + <http://intellinuxwireless.org/>. + + The microcode is typically installed in /lib/firmware. You can + look in the hotplug script /etc/hotplug/firmware.agent to + determine which directory FIRMWARE_DIR is set to when the script + runs. + + If you want to compile the driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read <file:Documentation/kbuild/modules.txt>. The + module will be called iwlagn. menu "Debugging Options" - depends on IWLWIFI + depends on IWLAGN config IWLWIFI_DEBUG - bool "Enable full debugging output in iwlagn and iwl3945 drivers" - depends on IWLWIFI + bool "Enable full debugging output in the iwlagn driver" + depends on IWLAGN ---help--- This option will enable debug tracing output for the iwlwifi drivers @@ -37,7 +71,7 @@ config IWLWIFI_DEBUG config IWLWIFI_DEBUGFS bool "iwlagn debugfs support" - depends on IWLWIFI && MAC80211_DEBUGFS + depends on IWLAGN && MAC80211_DEBUGFS ---help--- Enable creation of debugfs files for the iwlwifi drivers. This is a low-impact option that allows getting insight into the @@ -45,13 +79,13 @@ config IWLWIFI_DEBUGFS config IWLWIFI_DEBUG_EXPERIMENTAL_UCODE bool "Experimental uCode support" - depends on IWLWIFI && IWLWIFI_DEBUG + depends on IWLAGN && IWLWIFI_DEBUG ---help--- Enable use of experimental ucode for testing and debugging. config IWLWIFI_DEVICE_TRACING bool "iwlwifi device access tracing" - depends on IWLWIFI + depends on IWLAGN depends on EVENT_TRACING help Say Y here to trace all commands, including TX frames and IO @@ -68,57 +102,9 @@ config IWLWIFI_DEVICE_TRACING occur. endmenu -config IWLAGN - tristate "Intel Wireless WiFi Next Gen AGN (iwlagn)" - depends on IWLWIFI - ---help--- - Select to build the driver supporting the: - - Intel Wireless WiFi Link Next-Gen AGN - - This driver uses the kernel's mac80211 subsystem. - - In order to use this driver, you will need a microcode (uCode) - image for it. You can obtain the microcode from: - - <http://intellinuxwireless.org/>. - - The microcode is typically installed in /lib/firmware. You can - look in the hotplug script /etc/hotplug/firmware.agent to - determine which directory FIRMWARE_DIR is set to when the script - runs. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/kbuild/modules.txt>. The - module will be called iwlagn. - - -config IWL4965 - bool "Intel Wireless WiFi 4965AGN" - depends on IWLAGN - ---help--- - This option enables support for Intel Wireless WiFi Link 4965AGN - -config IWL5000 - bool "Intel Wireless-N/Advanced-N/Ultimate-N WiFi Link" - depends on IWLAGN - ---help--- - This option enables support for use with the following hardware: - Intel Wireless WiFi Link 6250AGN Adapter - Intel 6000 Series Wi-Fi Adapters (6200AGN and 6300AGN) - Intel WiFi Link 1000BGN - Intel Wireless WiFi 5150AGN - Intel Wireless WiFi 5100AGN, 5300AGN, and 5350AGN - Intel 6005 Series Wi-Fi Adapters - Intel 6030 Series Wi-Fi Adapters - Intel Wireless WiFi Link 6150BGN 2 Adapter - Intel 100 Series Wi-Fi Adapters (100BGN and 130BGN) - Intel 2000 Series Wi-Fi Adapters - config IWL_P2P bool "iwlwifi experimental P2P support" - depends on IWL5000 + depends on IWLAGN help This option enables experimental P2P support for some devices based on microcode support. Since P2P support is still under @@ -132,27 +118,3 @@ config IWL_P2P Say Y only if you want to experiment with P2P. -config IWL3945 - tristate "Intel PRO/Wireless 3945ABG/BG Network Connection (iwl3945)" - depends on IWLWIFI - ---help--- - Select to build the driver supporting the: - - Intel PRO/Wireless 3945ABG/BG Network Connection - - This driver uses the kernel's mac80211 subsystem. - - In order to use this driver, you will need a microcode (uCode) - image for it. You can obtain the microcode from: - - <http://intellinuxwireless.org/>. - - The microcode is typically installed in /lib/firmware. You can - look in the hotplug script /etc/hotplug/firmware.agent to - determine which directory FIRMWARE_DIR is set to when the script - runs. - - If you want to compile the driver as a module ( = code which can be - inserted in and removed from the running kernel whenever you want), - say M here and read <file:Documentation/kbuild/modules.txt>. The - module will be called iwl3945. diff --git a/drivers/net/wireless/iwlwifi/Makefile b/drivers/net/wireless/iwlwifi/Makefile index 25be742c69c..9d6ee836426 100644 --- a/drivers/net/wireless/iwlwifi/Makefile +++ b/drivers/net/wireless/iwlwifi/Makefile @@ -1,36 +1,23 @@ -obj-$(CONFIG_IWLWIFI) += iwlcore.o -iwlcore-objs := iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o -iwlcore-objs += iwl-rx.o iwl-tx.o iwl-sta.o -iwlcore-objs += iwl-scan.o iwl-led.o -iwlcore-$(CONFIG_IWL3945) += iwl-legacy.o -iwlcore-$(CONFIG_IWL4965) += iwl-legacy.o -iwlcore-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o -iwlcore-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o - -# If 3945 is selected only, iwl-legacy.o will be added -# to iwlcore-m above, but it needs to be built in. -iwlcore-objs += $(iwlcore-m) - -CFLAGS_iwl-devtrace.o := -I$(src) - # AGN obj-$(CONFIG_IWLAGN) += iwlagn.o iwlagn-objs := iwl-agn.o iwl-agn-rs.o iwl-agn-led.o iwlagn-objs += iwl-agn-ucode.o iwl-agn-tx.o -iwlagn-objs += iwl-agn-lib.o iwl-agn-rx.o iwl-agn-calib.o +iwlagn-objs += iwl-agn-lib.o iwl-agn-calib.o iwlagn-objs += iwl-agn-tt.o iwl-agn-sta.o iwl-agn-eeprom.o -iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-agn-debugfs.o -iwlagn-$(CONFIG_IWL4965) += iwl-4965.o -iwlagn-$(CONFIG_IWL5000) += iwl-agn-rxon.o iwl-agn-hcmd.o iwl-agn-ict.o -iwlagn-$(CONFIG_IWL5000) += iwl-5000.o -iwlagn-$(CONFIG_IWL5000) += iwl-6000.o -iwlagn-$(CONFIG_IWL5000) += iwl-1000.o -iwlagn-$(CONFIG_IWL5000) += iwl-2000.o +iwlagn-objs += iwl-core.o iwl-eeprom.o iwl-hcmd.o iwl-power.o +iwlagn-objs += iwl-rx.o iwl-tx.o iwl-sta.o +iwlagn-objs += iwl-scan.o iwl-led.o +iwlagn-objs += iwl-agn-rxon.o iwl-agn-hcmd.o iwl-agn-ict.o +iwlagn-objs += iwl-5000.o +iwlagn-objs += iwl-6000.o +iwlagn-objs += iwl-1000.o +iwlagn-objs += iwl-2000.o + +iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-agn-debugfs.o +iwlagn-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-debugfs.o +iwlagn-$(CONFIG_IWLWIFI_DEVICE_TRACING) += iwl-devtrace.o -# 3945 -obj-$(CONFIG_IWL3945) += iwl3945.o -iwl3945-objs := iwl3945-base.o iwl-3945.o iwl-3945-rs.o iwl-3945-led.o -iwl3945-$(CONFIG_IWLWIFI_DEBUGFS) += iwl-3945-debugfs.o +CFLAGS_iwl-devtrace.o := -I$(src) ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/iwlwifi/iwl-1000.c b/drivers/net/wireless/iwlwifi/iwl-1000.c index ba78bc8a259..e8e1c2dc865 100644 --- a/drivers/net/wireless/iwlwifi/iwl-1000.c +++ b/drivers/net/wireless/iwlwifi/iwl-1000.c @@ -232,8 +232,6 @@ static struct iwl_lib_ops iwl1000_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-2000.c b/drivers/net/wireless/iwlwifi/iwl-2000.c index 30483e27ce5..d7b6126408c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-2000.c +++ b/drivers/net/wireless/iwlwifi/iwl-2000.c @@ -315,8 +315,6 @@ static struct iwl_lib_ops iwl2000_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { @@ -418,6 +416,7 @@ static struct iwl_bt_params iwl2030_bt_params = { .bt_init_traffic_load = IWL_BT_COEX_TRAFFIC_LOAD_NONE, .bt_prio_boost = IWLAGN_BT_PRIO_BOOST_DEFAULT, .bt_sco_disable = true, + .bt_session_2 = true, }; #define IWL_DEVICE_2000 \ diff --git a/drivers/net/wireless/iwlwifi/iwl-5000.c b/drivers/net/wireless/iwlwifi/iwl-5000.c index 79ab0a6b138..3ea31b659d1 100644 --- a/drivers/net/wireless/iwlwifi/iwl-5000.c +++ b/drivers/net/wireless/iwlwifi/iwl-5000.c @@ -51,7 +51,7 @@ #include "iwl-agn-debugfs.h" /* Highest firmware API version supported */ -#define IWL5000_UCODE_API_MAX 2 +#define IWL5000_UCODE_API_MAX 5 #define IWL5150_UCODE_API_MAX 2 /* Lowest firmware API version supported */ @@ -402,8 +402,6 @@ static struct iwl_lib_ops iwl5000_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { @@ -471,8 +469,6 @@ static struct iwl_lib_ops iwl5150_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-6000.c b/drivers/net/wireless/iwlwifi/iwl-6000.c index f6493f77610..a745b01c0ec 100644 --- a/drivers/net/wireless/iwlwifi/iwl-6000.c +++ b/drivers/net/wireless/iwlwifi/iwl-6000.c @@ -343,8 +343,6 @@ static struct iwl_lib_ops iwl6000_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { @@ -415,8 +413,6 @@ static struct iwl_lib_ops iwl6030_lib = { .bt_stats_read = iwl_ucode_bt_stats_read, .reply_tx_error = iwl_reply_tx_error_read, }, - .check_plcp_health = iwl_good_plcp_health, - .check_ack_health = iwl_good_ack_health, .txfifo_flush = iwlagn_txfifo_flush, .dev_txfifo_flush = iwlagn_dev_txfifo_flush, .tt_ops = { diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c index 325ff5c89ee..fd142bee918 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-lib.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-lib.c @@ -609,6 +609,7 @@ const u8 *iwlagn_eeprom_query_addr(const struct iwl_priv *priv, struct iwl_mod_params iwlagn_mod_params = { .amsdu_size_8K = 1, .restart_fw = 1, + .plcp_check = true, /* the rest are 0 by default */ }; @@ -1173,7 +1174,7 @@ void iwlagn_rx_reply_rx(struct iwl_priv *priv, /* TSF isn't reliable. In order to allow smooth user experience, * this W/A doesn't propagate it to the mac80211 */ - /*rx_status.flag |= RX_FLAG_TSFT;*/ + /*rx_status.flag |= RX_FLAG_MACTIME_MPDU;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); @@ -1804,26 +1805,39 @@ static const __le32 iwlagn_concurrent_lookup[12] = { void iwlagn_send_advance_bt_config(struct iwl_priv *priv) { - struct iwlagn_bt_cmd bt_cmd = { + struct iwl_basic_bt_cmd basic = { .max_kill = IWLAGN_BT_MAX_KILL_DEFAULT, .bt3_timer_t7_value = IWLAGN_BT3_T7_DEFAULT, .bt3_prio_sample_time = IWLAGN_BT3_PRIO_SAMPLE_DEFAULT, .bt3_timer_t2_value = IWLAGN_BT3_T2_DEFAULT, }; + struct iwl6000_bt_cmd bt_cmd_6000; + struct iwl2000_bt_cmd bt_cmd_2000; + int ret; BUILD_BUG_ON(sizeof(iwlagn_def_3w_lookup) != - sizeof(bt_cmd.bt3_lookup_table)); - - if (priv->cfg->bt_params) - bt_cmd.prio_boost = priv->cfg->bt_params->bt_prio_boost; - else - bt_cmd.prio_boost = 0; - bt_cmd.kill_ack_mask = priv->kill_ack_mask; - bt_cmd.kill_cts_mask = priv->kill_cts_mask; + sizeof(basic.bt3_lookup_table)); + + if (priv->cfg->bt_params) { + if (priv->cfg->bt_params->bt_session_2) { + bt_cmd_2000.prio_boost = cpu_to_le32( + priv->cfg->bt_params->bt_prio_boost); + bt_cmd_2000.tx_prio_boost = 0; + bt_cmd_2000.rx_prio_boost = 0; + } else { + bt_cmd_6000.prio_boost = + priv->cfg->bt_params->bt_prio_boost; + bt_cmd_6000.tx_prio_boost = 0; + bt_cmd_6000.rx_prio_boost = 0; + } + } else { + IWL_ERR(priv, "failed to construct BT Coex Config\n"); + return; + } - bt_cmd.valid = priv->bt_valid; - bt_cmd.tx_prio_boost = 0; - bt_cmd.rx_prio_boost = 0; + basic.kill_ack_mask = priv->kill_ack_mask; + basic.kill_cts_mask = priv->kill_cts_mask; + basic.valid = priv->bt_valid; /* * Configure BT coex mode to "no coexistence" when the @@ -1832,32 +1846,43 @@ void iwlagn_send_advance_bt_config(struct iwl_priv *priv) * IBSS mode (no proper uCode support for coex then). */ if (!bt_coex_active || priv->iw_mode == NL80211_IFTYPE_ADHOC) { - bt_cmd.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_DISABLED; } else { - bt_cmd.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << + basic.flags = IWLAGN_BT_FLAG_COEX_MODE_3W << IWLAGN_BT_FLAG_COEX_MODE_SHIFT; if (priv->cfg->bt_params && priv->cfg->bt_params->bt_sco_disable) - bt_cmd.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; + basic.flags |= IWLAGN_BT_FLAG_SYNC_2_BT_DISABLE; if (priv->bt_ch_announce) - bt_cmd.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; - IWL_DEBUG_INFO(priv, "BT coex flag: 0X%x\n", bt_cmd.flags); + basic.flags |= IWLAGN_BT_FLAG_CHANNEL_INHIBITION; + IWL_DEBUG_INFO(priv, "BT coex flag: 0X%x\n", basic.flags); } - priv->bt_enable_flag = bt_cmd.flags; + priv->bt_enable_flag = basic.flags; if (priv->bt_full_concurrent) - memcpy(bt_cmd.bt3_lookup_table, iwlagn_concurrent_lookup, + memcpy(basic.bt3_lookup_table, iwlagn_concurrent_lookup, sizeof(iwlagn_concurrent_lookup)); else - memcpy(bt_cmd.bt3_lookup_table, iwlagn_def_3w_lookup, + memcpy(basic.bt3_lookup_table, iwlagn_def_3w_lookup, sizeof(iwlagn_def_3w_lookup)); IWL_DEBUG_INFO(priv, "BT coex %s in %s mode\n", - bt_cmd.flags ? "active" : "disabled", + basic.flags ? "active" : "disabled", priv->bt_full_concurrent ? "full concurrency" : "3-wire"); - if (iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, sizeof(bt_cmd), &bt_cmd)) + if (priv->cfg->bt_params->bt_session_2) { + memcpy(&bt_cmd_2000.basic, &basic, + sizeof(basic)); + ret = iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(bt_cmd_2000), &bt_cmd_2000); + } else { + memcpy(&bt_cmd_6000.basic, &basic, + sizeof(basic)); + ret = iwl_send_cmd_pdu(priv, REPLY_BT_CONFIG, + sizeof(bt_cmd_6000), &bt_cmd_6000); + } + if (ret) IWL_ERR(priv, "failed to send BT Coex Config\n"); } @@ -1984,12 +2009,14 @@ static void iwlagn_print_uartmsg(struct iwl_priv *priv, (BT_UART_MSG_FRAME6DISCOVERABLE_MSK & uart_msg->frame6) >> BT_UART_MSG_FRAME6DISCOVERABLE_POS); - IWL_DEBUG_NOTIF(priv, "Sniff Activity = 0x%X, Inquiry/Page SR Mode = " - "0x%X, Connectable = 0x%X", + IWL_DEBUG_NOTIF(priv, "Sniff Activity = 0x%X, Page = " + "0x%X, Inquiry = 0x%X, Connectable = 0x%X", (BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7SNIFFACTIVITY_POS, - (BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_MSK & uart_msg->frame7) >> - BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_POS, + (BT_UART_MSG_FRAME7PAGE_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7PAGE_POS, + (BT_UART_MSG_FRAME7INQUIRY_MSK & uart_msg->frame7) >> + BT_UART_MSG_FRAME7INQUIRY_POS, (BT_UART_MSG_FRAME7CONNECTABLE_MSK & uart_msg->frame7) >> BT_UART_MSG_FRAME7CONNECTABLE_POS); } diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c index 6c2adc58d65..dfdbea6e8f9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-rxon.c @@ -471,6 +471,7 @@ static void iwlagn_check_needed_chains(struct iwl_priv *priv, struct iwl_rxon_context *tmp; struct ieee80211_sta *sta; struct iwl_ht_config *ht_conf = &priv->current_ht_config; + struct ieee80211_sta_ht_cap *ht_cap; bool need_multiple; lockdep_assert_held(&priv->mutex); @@ -479,23 +480,7 @@ static void iwlagn_check_needed_chains(struct iwl_priv *priv, case NL80211_IFTYPE_STATION: rcu_read_lock(); sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (sta) { - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - int maxstreams; - - maxstreams = (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) - >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - maxstreams += 1; - - need_multiple = true; - - if ((ht_cap->mcs.rx_mask[1] == 0) && - (ht_cap->mcs.rx_mask[2] == 0)) - need_multiple = false; - if (maxstreams <= 1) - need_multiple = false; - } else { + if (!sta) { /* * If at all, this can only happen through a race * when the AP disconnects us while we're still @@ -503,7 +488,46 @@ static void iwlagn_check_needed_chains(struct iwl_priv *priv, * will soon tell us about that. */ need_multiple = false; + rcu_read_unlock(); + break; + } + + ht_cap = &sta->ht_cap; + + need_multiple = true; + + /* + * If the peer advertises no support for receiving 2 and 3 + * stream MCS rates, it can't be transmitting them either. + */ + if (ht_cap->mcs.rx_mask[1] == 0 && + ht_cap->mcs.rx_mask[2] == 0) { + need_multiple = false; + } else if (!(ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_DEFINED)) { + /* If it can't TX MCS at all ... */ + need_multiple = false; + } else if (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_RX_DIFF) { + int maxstreams; + + /* + * But if it can receive them, it might still not + * be able to transmit them, which is what we need + * to check here -- so check the number of streams + * it advertises for TX (if different from RX). + */ + + maxstreams = (ht_cap->mcs.tx_params & + IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK); + maxstreams >>= + IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; + maxstreams += 1; + + if (maxstreams <= 1) + need_multiple = false; } + rcu_read_unlock(); break; case NL80211_IFTYPE_ADHOC: diff --git a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c index 266490d8a39..a709d05c586 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn-tx.c @@ -947,7 +947,7 @@ void iwlagn_txq_ctx_reset(struct iwl_priv *priv) */ void iwlagn_txq_ctx_stop(struct iwl_priv *priv) { - int ch; + int ch, txq_id; unsigned long flags; /* Turn off all Tx DMA fifos */ @@ -966,6 +966,16 @@ void iwlagn_txq_ctx_stop(struct iwl_priv *priv) iwl_read_direct32(priv, FH_TSSR_TX_STATUS_REG)); } spin_unlock_irqrestore(&priv->lock, flags); + + if (!priv->txq) + return; + + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < priv->hw_params.max_txq_num; txq_id++) + if (txq_id == priv->cmd_queue) + iwl_cmd_queue_unmap(priv); + else + iwl_tx_queue_unmap(priv, txq_id); } /* diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 8025c62d4d0..f189bbe78fa 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -86,7 +86,6 @@ MODULE_DESCRIPTION(DRV_DESCRIPTION); MODULE_VERSION(DRV_VERSION); MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); MODULE_LICENSE("GPL"); -MODULE_ALIAS("iwl4965"); static int iwlagn_ant_coupling; static bool iwlagn_bt_ch_announce = 1; @@ -466,6 +465,15 @@ static void iwl_rx_reply_alive(struct iwl_priv *priv, IWL_WARN(priv, "%s uCode did not respond OK.\n", (palive->ver_subtype == INITIALIZE_SUBTYPE) ? "init" : "runtime"); + /* + * If fail to load init uCode, + * let's try to load the init uCode again. + * We should not get into this situation, but if it + * does happen, we should not move on and loading "runtime" + * without proper calibrate the device. + */ + if (palive->ver_subtype == INITIALIZE_SUBTYPE) + priv->ucode_type = UCODE_NONE; queue_work(priv->workqueue, &priv->restart); } } @@ -1405,72 +1413,6 @@ static void iwl_irq_tasklet(struct iwl_priv *priv) iwl_enable_rfkill_int(priv); } -/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ -#define ACK_CNT_RATIO (50) -#define BA_TIMEOUT_CNT (5) -#define BA_TIMEOUT_MAX (16) - -/** - * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. - * - * When the ACK count ratio is low and aggregated BA timeout retries exceeding - * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal - * operation state. - */ -bool iwl_good_ack_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) -{ - int actual_delta, expected_delta, ba_timeout_delta; - struct statistics_tx *cur, *old; - - if (priv->_agn.agg_tids_count) - return true; - - if (iwl_bt_statistics(priv)) { - cur = &pkt->u.stats_bt.tx; - old = &priv->_agn.statistics_bt.tx; - } else { - cur = &pkt->u.stats.tx; - old = &priv->_agn.statistics.tx; - } - - actual_delta = le32_to_cpu(cur->actual_ack_cnt) - - le32_to_cpu(old->actual_ack_cnt); - expected_delta = le32_to_cpu(cur->expected_ack_cnt) - - le32_to_cpu(old->expected_ack_cnt); - - /* Values should not be negative, but we do not trust the firmware */ - if (actual_delta <= 0 || expected_delta <= 0) - return true; - - ba_timeout_delta = le32_to_cpu(cur->agg.ba_timeout) - - le32_to_cpu(old->agg.ba_timeout); - - if ((actual_delta * 100 / expected_delta) < ACK_CNT_RATIO && - ba_timeout_delta > BA_TIMEOUT_CNT) { - IWL_DEBUG_RADIO(priv, "deltas: actual %d expected %d ba_timeout %d\n", - actual_delta, expected_delta, ba_timeout_delta); - -#ifdef CONFIG_IWLWIFI_DEBUGFS - /* - * This is ifdef'ed on DEBUGFS because otherwise the - * statistics aren't available. If DEBUGFS is set but - * DEBUG is not, these will just compile out. - */ - IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta %d\n", - priv->_agn.delta_statistics.tx.rx_detected_cnt); - IWL_DEBUG_RADIO(priv, - "ack_or_ba_timeout_collision delta %d\n", - priv->_agn.delta_statistics.tx.ack_or_ba_timeout_collision); -#endif - - if (ba_timeout_delta >= BA_TIMEOUT_MAX) - return false; - } - - return true; -} - - /***************************************************************************** * * sysfs attributes @@ -2735,9 +2677,11 @@ static void iwl_alive_start(struct iwl_priv *priv) priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); } - if (priv->cfg->bt_params && - !priv->cfg->bt_params->advanced_bt_coexist) { - /* Configure Bluetooth device coexistence support */ + if (!priv->cfg->bt_params || (priv->cfg->bt_params && + !priv->cfg->bt_params->advanced_bt_coexist)) { + /* + * default is 2-wire BT coexexistence support + */ priv->cfg->ops->hcmd->send_bt_config(priv); } @@ -3320,7 +3264,7 @@ void iwlagn_mac_stop(struct ieee80211_hw *hw) IWL_DEBUG_MAC80211(priv, "leave\n"); } -int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct iwl_priv *priv = hw->priv; @@ -3333,7 +3277,6 @@ int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) dev_kfree_skb_any(skb); IWL_DEBUG_MACDUMP(priv, "leave\n"); - return NETDEV_TX_OK; } void iwlagn_mac_update_tkip_key(struct ieee80211_hw *hw, @@ -3799,7 +3742,6 @@ static void iwlagn_bg_roc_done(struct work_struct *work) mutex_unlock(&priv->mutex); } -#ifdef CONFIG_IWL5000 static int iwl_mac_remain_on_channel(struct ieee80211_hw *hw, struct ieee80211_channel *channel, enum nl80211_channel_type channel_type, @@ -3855,7 +3797,6 @@ static int iwl_mac_cancel_remain_on_channel(struct ieee80211_hw *hw) return 0; } -#endif /***************************************************************************** * @@ -4025,7 +3966,6 @@ static void iwl_uninit_drv(struct iwl_priv *priv) kfree(priv->scan_cmd); } -#ifdef CONFIG_IWL5000 struct ieee80211_ops iwlagn_hw_ops = { .tx = iwlagn_mac_tx, .start = iwlagn_mac_start, @@ -4050,7 +3990,6 @@ struct ieee80211_ops iwlagn_hw_ops = { .remain_on_channel = iwl_mac_remain_on_channel, .cancel_remain_on_channel = iwl_mac_cancel_remain_on_channel, }; -#endif static void iwl_hw_detect(struct iwl_priv *priv) { @@ -4118,12 +4057,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (cfg->mod_params->disable_hw_scan) { dev_printk(KERN_DEBUG, &(pdev->dev), "sw scan support is deprecated\n"); -#ifdef CONFIG_IWL5000 iwlagn_hw_ops.hw_scan = NULL; -#endif -#ifdef CONFIG_IWL4965 - iwl4965_hw_ops.hw_scan = NULL; -#endif } hw = iwl_alloc_all(cfg); @@ -4502,12 +4436,6 @@ static void __devexit iwl_pci_remove(struct pci_dev *pdev) /* Hardware specific file defines the PCI IDs table for that hardware module */ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { -#ifdef CONFIG_IWL4965 - {IWL_PCI_DEVICE(0x4229, PCI_ANY_ID, iwl4965_agn_cfg)}, - {IWL_PCI_DEVICE(0x4230, PCI_ANY_ID, iwl4965_agn_cfg)}, -#endif /* CONFIG_IWL4965 */ -#ifdef CONFIG_IWL5000 -/* 5100 Series WiFi */ {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ @@ -4693,8 +4621,6 @@ static DEFINE_PCI_DEVICE_TABLE(iwl_hw_card_ids) = { {IWL_PCI_DEVICE(0x0893, 0x0266, iwl230_bg_cfg)}, {IWL_PCI_DEVICE(0x0892, 0x0466, iwl230_bg_cfg)}, -#endif /* CONFIG_IWL5000 */ - {0} }; MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); @@ -4793,3 +4719,9 @@ MODULE_PARM_DESC(antenna_coupling, module_param_named(bt_ch_inhibition, iwlagn_bt_ch_announce, bool, S_IRUGO); MODULE_PARM_DESC(bt_ch_inhibition, "Disable BT channel inhibition (default: enable)"); + +module_param_named(plcp_check, iwlagn_mod_params.plcp_check, bool, S_IRUGO); +MODULE_PARM_DESC(plcp_check, "Check plcp health (default: 1 [enabled])"); + +module_param_named(ack_check, iwlagn_mod_params.ack_check, bool, S_IRUGO); +MODULE_PARM_DESC(ack_check, "Check ack health (default: 0 [disabled])"); diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.h b/drivers/net/wireless/iwlwifi/iwl-agn.h index d00e1ea50a8..b5a169be48e 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.h +++ b/drivers/net/wireless/iwlwifi/iwl-agn.h @@ -121,8 +121,6 @@ void iwl_disable_ict(struct iwl_priv *priv); int iwl_alloc_isr_ict(struct iwl_priv *priv); void iwl_free_isr_ict(struct iwl_priv *priv); irqreturn_t iwl_isr_ict(int irq, void *data); -bool iwl_good_ack_health(struct iwl_priv *priv, - struct iwl_rx_packet *pkt); /* tx queue */ void iwlagn_set_wr_ptrs(struct iwl_priv *priv, @@ -248,8 +246,6 @@ u8 iwl_toggle_tx_ant(struct iwl_priv *priv, u8 ant_idx, u8 valid); /* rx */ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); -bool iwl_good_plcp_health(struct iwl_priv *priv, - struct iwl_rx_packet *pkt); void iwl_rx_statistics(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); void iwl_reply_statistics(struct iwl_priv *priv, @@ -356,7 +352,7 @@ iwlagn_remove_notification(struct iwl_priv *priv, struct iwl_notification_wait *wait_entry); /* mac80211 handlers (for 4965) */ -int iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void iwlagn_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int iwlagn_mac_start(struct ieee80211_hw *hw); void iwlagn_mac_stop(struct ieee80211_hw *hw); void iwlagn_configure_filter(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/iwlwifi/iwl-commands.h b/drivers/net/wireless/iwlwifi/iwl-commands.h index 0a1d4aeb36a..03cfb74da2b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-commands.h +++ b/drivers/net/wireless/iwlwifi/iwl-commands.h @@ -2477,7 +2477,7 @@ struct iwl_bt_cmd { IWLAGN_BT_VALID_BT4_TIMES | \ IWLAGN_BT_VALID_3W_LUT) -struct iwlagn_bt_cmd { +struct iwl_basic_bt_cmd { u8 flags; u8 ledtime; /* unused */ u8 max_kill; @@ -2490,6 +2490,10 @@ struct iwlagn_bt_cmd { __le32 bt3_lookup_table[12]; __le16 bt4_decision_time; /* unused */ __le16 valid; +}; + +struct iwl6000_bt_cmd { + struct iwl_basic_bt_cmd basic; u8 prio_boost; /* * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask @@ -2499,6 +2503,18 @@ struct iwlagn_bt_cmd { __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ }; +struct iwl2000_bt_cmd { + struct iwl_basic_bt_cmd basic; + __le32 prio_boost; + /* + * set IWLAGN_BT_VALID_BOOST to "1" in "valid" bitmask + * if configure the following patterns + */ + u8 reserved; + u8 tx_prio_boost; /* SW boost of WiFi tx priority */ + __le16 rx_prio_boost; /* SW boost of WiFi rx priority */ +}; + #define IWLAGN_BT_SCO_ACTIVE cpu_to_le32(BIT(0)) struct iwlagn_bt_sco_cmd { @@ -4150,6 +4166,10 @@ enum iwl_bt_coex_profile_traffic_load { */ }; +#define BT_SESSION_ACTIVITY_1_UART_MSG 0x1 +#define BT_SESSION_ACTIVITY_2_UART_MSG 0x2 + +/* BT UART message - Share Part (BT -> WiFi) */ #define BT_UART_MSG_FRAME1MSGTYPE_POS (0) #define BT_UART_MSG_FRAME1MSGTYPE_MSK \ (0x7 << BT_UART_MSG_FRAME1MSGTYPE_POS) @@ -4234,9 +4254,12 @@ enum iwl_bt_coex_profile_traffic_load { #define BT_UART_MSG_FRAME7SNIFFACTIVITY_POS (0) #define BT_UART_MSG_FRAME7SNIFFACTIVITY_MSK \ (0x7 << BT_UART_MSG_FRAME7SNIFFACTIVITY_POS) -#define BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_POS (3) -#define BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_MSK \ - (0x3 << BT_UART_MSG_FRAME7INQUIRYPAGESRMODE_POS) +#define BT_UART_MSG_FRAME7PAGE_POS (3) +#define BT_UART_MSG_FRAME7PAGE_MSK \ + (0x1 << BT_UART_MSG_FRAME7PAGE_POS) +#define BT_UART_MSG_FRAME7INQUIRY_POS (4) +#define BT_UART_MSG_FRAME7INQUIRY_MSK \ + (0x1 << BT_UART_MSG_FRAME7INQUIRY_POS) #define BT_UART_MSG_FRAME7CONNECTABLE_POS (5) #define BT_UART_MSG_FRAME7CONNECTABLE_MSK \ (0x1 << BT_UART_MSG_FRAME7CONNECTABLE_POS) @@ -4244,6 +4267,83 @@ enum iwl_bt_coex_profile_traffic_load { #define BT_UART_MSG_FRAME7RESERVED_MSK \ (0x3 << BT_UART_MSG_FRAME7RESERVED_POS) +/* BT Session Activity 2 UART message (BT -> WiFi) */ +#define BT_UART_MSG_2_FRAME1RESERVED1_POS (5) +#define BT_UART_MSG_2_FRAME1RESERVED1_MSK \ + (0x1<<BT_UART_MSG_2_FRAME1RESERVED1_POS) +#define BT_UART_MSG_2_FRAME1RESERVED2_POS (6) +#define BT_UART_MSG_2_FRAME1RESERVED2_MSK \ + (0x3<<BT_UART_MSG_2_FRAME1RESERVED2_POS) + +#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS (0) +#define BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_MSK \ + (0x3F<<BT_UART_MSG_2_FRAME2AGGTRAFFICLOAD_POS) +#define BT_UART_MSG_2_FRAME2RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME2RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME2RESERVED_POS) + +#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS (0) +#define BT_UART_MSG_2_FRAME3BRLASTTXPOWER_MSK \ + (0xF<<BT_UART_MSG_2_FRAME3BRLASTTXPOWER_POS) +#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS (4) +#define BT_UART_MSG_2_FRAME3INQPAGESRMODE_MSK \ + (0x1<<BT_UART_MSG_2_FRAME3INQPAGESRMODE_POS) +#define BT_UART_MSG_2_FRAME3LEMASTER_POS (5) +#define BT_UART_MSG_2_FRAME3LEMASTER_MSK \ + (0x1<<BT_UART_MSG_2_FRAME3LEMASTER_POS) +#define BT_UART_MSG_2_FRAME3RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME3RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME3RESERVED_POS) + +#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS (0) +#define BT_UART_MSG_2_FRAME4LELASTTXPOWER_MSK \ + (0xF<<BT_UART_MSG_2_FRAME4LELASTTXPOWER_POS) +#define BT_UART_MSG_2_FRAME4NUMLECONN_POS (4) +#define BT_UART_MSG_2_FRAME4NUMLECONN_MSK \ + (0x3<<BT_UART_MSG_2_FRAME4NUMLECONN_POS) +#define BT_UART_MSG_2_FRAME4RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME4RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME4RESERVED_POS) + +#define BT_UART_MSG_2_FRAME5BTMINRSSI_POS (0) +#define BT_UART_MSG_2_FRAME5BTMINRSSI_MSK \ + (0xF<<BT_UART_MSG_2_FRAME5BTMINRSSI_POS) +#define BT_UART_MSG_2_FRAME5LESCANINITMODE_POS (4) +#define BT_UART_MSG_2_FRAME5LESCANINITMODE_MSK \ + (0x1<<BT_UART_MSG_2_FRAME5LESCANINITMODE_POS) +#define BT_UART_MSG_2_FRAME5LEADVERMODE_POS (5) +#define BT_UART_MSG_2_FRAME5LEADVERMODE_MSK \ + (0x1<<BT_UART_MSG_2_FRAME5LEADVERMODE_POS) +#define BT_UART_MSG_2_FRAME5RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME5RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME5RESERVED_POS) + +#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS (0) +#define BT_UART_MSG_2_FRAME6LECONNINTERVAL_MSK \ + (0x1F<<BT_UART_MSG_2_FRAME6LECONNINTERVAL_POS) +#define BT_UART_MSG_2_FRAME6RFU_POS (5) +#define BT_UART_MSG_2_FRAME6RFU_MSK \ + (0x1<<BT_UART_MSG_2_FRAME6RFU_POS) +#define BT_UART_MSG_2_FRAME6RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME6RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME6RESERVED_POS) + +#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS (0) +#define BT_UART_MSG_2_FRAME7LECONNSLAVELAT_MSK \ + (0x7<<BT_UART_MSG_2_FRAME7LECONNSLAVELAT_POS) +#define BT_UART_MSG_2_FRAME7LEPROFILE1_POS (3) +#define BT_UART_MSG_2_FRAME7LEPROFILE1_MSK \ + (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE1_POS) +#define BT_UART_MSG_2_FRAME7LEPROFILE2_POS (4) +#define BT_UART_MSG_2_FRAME7LEPROFILE2_MSK \ + (0x1<<BT_UART_MSG_2_FRAME7LEPROFILE2_POS) +#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS (5) +#define BT_UART_MSG_2_FRAME7LEPROFILEOTHER_MSK \ + (0x1<<BT_UART_MSG_2_FRAME7LEPROFILEOTHER_POS) +#define BT_UART_MSG_2_FRAME7RESERVED_POS (6) +#define BT_UART_MSG_2_FRAME7RESERVED_MSK \ + (0x3<<BT_UART_MSG_2_FRAME7RESERVED_POS) + struct iwl_bt_uart_msg { u8 header; diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c index 4ad89389a0a..4bd34206025 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.c +++ b/drivers/net/wireless/iwlwifi/iwl-core.c @@ -43,11 +43,6 @@ #include "iwl-helpers.h" -MODULE_DESCRIPTION("iwl core"); -MODULE_VERSION(IWLWIFI_VERSION); -MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR); -MODULE_LICENSE("GPL"); - /* * set bt_coex_active to true, uCode will do kill/defer * every time the priority line is asserted (BT is sending signals on the @@ -65,15 +60,12 @@ MODULE_LICENSE("GPL"); * default: bt_coex_active = true (BT_COEX_ENABLE) */ bool bt_coex_active = true; -EXPORT_SYMBOL_GPL(bt_coex_active); module_param(bt_coex_active, bool, S_IRUGO); MODULE_PARM_DESC(bt_coex_active, "enable wifi/bluetooth co-exist"); u32 iwl_debug_level; -EXPORT_SYMBOL(iwl_debug_level); const u8 iwl_bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; -EXPORT_SYMBOL(iwl_bcast_addr); /* This function both allocates and initializes hw and priv. */ @@ -98,7 +90,6 @@ struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg) out: return hw; } -EXPORT_SYMBOL(iwl_alloc_all); #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */ #define MAX_BIT_RATE_20_MHZ 72 /* Mbps */ @@ -272,7 +263,6 @@ int iwlcore_init_geos(struct iwl_priv *priv) return 0; } -EXPORT_SYMBOL(iwlcore_init_geos); /* * iwlcore_free_geos - undo allocations in iwlcore_init_geos @@ -283,7 +273,6 @@ void iwlcore_free_geos(struct iwl_priv *priv) kfree(priv->ieee_rates); clear_bit(STATUS_GEO_CONFIGURED, &priv->status); } -EXPORT_SYMBOL(iwlcore_free_geos); static bool iwl_is_channel_extension(struct iwl_priv *priv, enum ieee80211_band band, @@ -328,7 +317,6 @@ bool iwl_is_ht40_tx_allowed(struct iwl_priv *priv, le16_to_cpu(ctx->staging.channel), ctx->ht.extension_chan_offset); } -EXPORT_SYMBOL(iwl_is_ht40_tx_allowed); static u16 iwl_adjust_beacon_interval(u16 beacon_val, u16 max_beacon_val) { @@ -429,7 +417,6 @@ int iwl_send_rxon_timing(struct iwl_priv *priv, struct iwl_rxon_context *ctx) return iwl_send_cmd_pdu(priv, ctx->rxon_timing_cmd, sizeof(ctx->timing), &ctx->timing); } -EXPORT_SYMBOL(iwl_send_rxon_timing); void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, int hw_decrypt) @@ -442,7 +429,6 @@ void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, struct iwl_rxon_context *ctx, rxon->filter_flags |= RXON_FILTER_DIS_DECRYPT_MSK; } -EXPORT_SYMBOL(iwl_set_rxon_hwcrypto); /* validate RXON structure is valid */ int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx) @@ -515,7 +501,6 @@ int iwl_check_rxon_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx) } return 0; } -EXPORT_SYMBOL(iwl_check_rxon_cmd); /** * iwl_full_rxon_required - check if full RXON (vs RXON_ASSOC) cmd is needed @@ -579,7 +564,6 @@ int iwl_full_rxon_required(struct iwl_priv *priv, return 0; } -EXPORT_SYMBOL(iwl_full_rxon_required); u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv, struct iwl_rxon_context *ctx) @@ -593,7 +577,6 @@ u8 iwl_rate_get_lowest_plcp(struct iwl_priv *priv, else return IWL_RATE_6M_PLCP; } -EXPORT_SYMBOL(iwl_rate_get_lowest_plcp); static void _iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf, @@ -670,7 +653,6 @@ void iwl_set_rxon_ht(struct iwl_priv *priv, struct iwl_ht_config *ht_conf) for_each_context(priv, ctx) _iwl_set_rxon_ht(priv, ht_conf, ctx); } -EXPORT_SYMBOL(iwl_set_rxon_ht); /* Return valid, unused, channel for a passive scan to reset the RF */ u8 iwl_get_single_channel_number(struct iwl_priv *priv, @@ -711,7 +693,6 @@ u8 iwl_get_single_channel_number(struct iwl_priv *priv, return channel; } -EXPORT_SYMBOL(iwl_get_single_channel_number); /** * iwl_set_rxon_channel - Set the band and channel values in staging RXON @@ -742,7 +723,6 @@ int iwl_set_rxon_channel(struct iwl_priv *priv, struct ieee80211_channel *ch, return 0; } -EXPORT_SYMBOL(iwl_set_rxon_channel); void iwl_set_flags_for_band(struct iwl_priv *priv, struct iwl_rxon_context *ctx, @@ -766,7 +746,6 @@ void iwl_set_flags_for_band(struct iwl_priv *priv, ctx->staging.flags &= ~RXON_FLG_CCK_MSK; } } -EXPORT_SYMBOL(iwl_set_flags_for_band); /* * initialize rxon structure with default values from eeprom @@ -838,7 +817,6 @@ void iwl_connection_init_rx_config(struct iwl_priv *priv, ctx->staging.ofdm_ht_dual_stream_basic_rates = 0xff; ctx->staging.ofdm_ht_triple_stream_basic_rates = 0xff; } -EXPORT_SYMBOL(iwl_connection_init_rx_config); void iwl_set_rate(struct iwl_priv *priv) { @@ -871,7 +849,6 @@ void iwl_set_rate(struct iwl_priv *priv) (IWL_OFDM_BASIC_RATES_MASK >> IWL_FIRST_OFDM_RATE) & 0xFF; } } -EXPORT_SYMBOL(iwl_set_rate); void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) { @@ -891,7 +868,6 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success) mutex_unlock(&priv->mutex); } } -EXPORT_SYMBOL(iwl_chswitch_done); void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) { @@ -919,7 +895,6 @@ void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) } } } -EXPORT_SYMBOL(iwl_rx_csa); #ifdef CONFIG_IWLWIFI_DEBUG void iwl_print_rx_config_cmd(struct iwl_priv *priv, @@ -941,13 +916,15 @@ void iwl_print_rx_config_cmd(struct iwl_priv *priv, IWL_DEBUG_RADIO(priv, "u8[6] bssid_addr: %pM\n", rxon->bssid_addr); IWL_DEBUG_RADIO(priv, "u16 assoc_id: 0x%x\n", le16_to_cpu(rxon->assoc_id)); } -EXPORT_SYMBOL(iwl_print_rx_config_cmd); #endif /** * iwl_irq_handle_error - called for HW or SW error interrupt from card */ void iwl_irq_handle_error(struct iwl_priv *priv) { + unsigned int reload_msec; + unsigned long reload_jiffies; + /* Set the FW error flag -- cleared on iwl_down */ set_bit(STATUS_FW_ERROR, &priv->status); @@ -991,6 +968,25 @@ void iwl_irq_handle_error(struct iwl_priv *priv) * commands by clearing the INIT status bit */ clear_bit(STATUS_READY, &priv->status); + /* + * If firmware keep reloading, then it indicate something + * serious wrong and firmware having problem to recover + * from it. Instead of keep trying which will fill the syslog + * and hang the system, let's just stop it + */ + reload_jiffies = jiffies; + reload_msec = jiffies_to_msecs((long) reload_jiffies - + (long) priv->reload_jiffies); + priv->reload_jiffies = reload_jiffies; + if (reload_msec <= IWL_MIN_RELOAD_DURATION) { + priv->reload_count++; + if (priv->reload_count >= IWL_MAX_CONTINUE_RELOAD_CNT) { + IWL_ERR(priv, "BUG_ON, Stop restarting\n"); + return; + } + } else + priv->reload_count = 0; + if (!test_bit(STATUS_EXIT_PENDING, &priv->status)) { IWL_DEBUG(priv, IWL_DL_FW_ERRORS, "Restarting adapter due to uCode error.\n"); @@ -999,7 +995,6 @@ void iwl_irq_handle_error(struct iwl_priv *priv) queue_work(priv->workqueue, &priv->restart); } } -EXPORT_SYMBOL(iwl_irq_handle_error); static int iwl_apm_stop_master(struct iwl_priv *priv) { @@ -1036,7 +1031,6 @@ void iwl_apm_stop(struct iwl_priv *priv) */ iwl_clear_bit(priv, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } -EXPORT_SYMBOL(iwl_apm_stop); /* @@ -1151,7 +1145,6 @@ int iwl_apm_init(struct iwl_priv *priv) out: return ret; } -EXPORT_SYMBOL(iwl_apm_init); int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) @@ -1211,7 +1204,6 @@ int iwl_set_tx_power(struct iwl_priv *priv, s8 tx_power, bool force) } return ret; } -EXPORT_SYMBOL(iwl_set_tx_power); void iwl_send_bt_config(struct iwl_priv *priv) { @@ -1235,7 +1227,6 @@ void iwl_send_bt_config(struct iwl_priv *priv) sizeof(struct iwl_bt_cmd), &bt_cmd)) IWL_ERR(priv, "failed to send BT Coex Config\n"); } -EXPORT_SYMBOL(iwl_send_bt_config); int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) { @@ -1253,7 +1244,6 @@ int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear) sizeof(struct iwl_statistics_cmd), &statistics_cmd); } -EXPORT_SYMBOL(iwl_send_statistics_request); void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -1265,7 +1255,6 @@ void iwl_rx_pm_sleep_notif(struct iwl_priv *priv, sleep->pm_sleep_mode, sleep->pm_wakeup_src); #endif } -EXPORT_SYMBOL(iwl_rx_pm_sleep_notif); void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -1277,7 +1266,6 @@ void iwl_rx_pm_debug_statistics_notif(struct iwl_priv *priv, get_cmd_string(pkt->hdr.cmd)); iwl_print_hex_dump(priv, IWL_DL_RADIO, pkt->u.raw, len); } -EXPORT_SYMBOL(iwl_rx_pm_debug_statistics_notif); void iwl_rx_reply_error(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -1292,7 +1280,6 @@ void iwl_rx_reply_error(struct iwl_priv *priv, le16_to_cpu(pkt->u.err_resp.bad_cmd_seq_num), le32_to_cpu(pkt->u.err_resp.error_info)); } -EXPORT_SYMBOL(iwl_rx_reply_error); void iwl_clear_isr_stats(struct iwl_priv *priv) { @@ -1344,7 +1331,6 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue, IWL_DEBUG_MAC80211(priv, "leave\n"); return 0; } -EXPORT_SYMBOL(iwl_mac_conf_tx); int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw) { @@ -1352,7 +1338,6 @@ int iwl_mac_tx_last_beacon(struct ieee80211_hw *hw) return priv->ibss_manager == IWL_IBSS_MANAGER; } -EXPORT_SYMBOL_GPL(iwl_mac_tx_last_beacon); static int iwl_set_mode(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { @@ -1462,7 +1447,6 @@ int iwl_mac_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) IWL_DEBUG_MAC80211(priv, "leave\n"); return err; } -EXPORT_SYMBOL(iwl_mac_add_interface); static void iwl_teardown_interface(struct iwl_priv *priv, struct ieee80211_vif *vif, @@ -1515,7 +1499,6 @@ void iwl_mac_remove_interface(struct ieee80211_hw *hw, IWL_DEBUG_MAC80211(priv, "leave\n"); } -EXPORT_SYMBOL(iwl_mac_remove_interface); int iwl_alloc_txq_mem(struct iwl_priv *priv) { @@ -1530,14 +1513,12 @@ int iwl_alloc_txq_mem(struct iwl_priv *priv) } return 0; } -EXPORT_SYMBOL(iwl_alloc_txq_mem); void iwl_free_txq_mem(struct iwl_priv *priv) { kfree(priv->txq); priv->txq = NULL; } -EXPORT_SYMBOL(iwl_free_txq_mem); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -1576,7 +1557,6 @@ int iwl_alloc_traffic_mem(struct iwl_priv *priv) iwl_reset_traffic_log(priv); return 0; } -EXPORT_SYMBOL(iwl_alloc_traffic_mem); void iwl_free_traffic_mem(struct iwl_priv *priv) { @@ -1586,7 +1566,6 @@ void iwl_free_traffic_mem(struct iwl_priv *priv) kfree(priv->rx_traffic); priv->rx_traffic = NULL; } -EXPORT_SYMBOL(iwl_free_traffic_mem); void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, u16 length, struct ieee80211_hdr *header) @@ -1611,7 +1590,6 @@ void iwl_dbg_log_tx_data_frame(struct iwl_priv *priv, (priv->tx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; } } -EXPORT_SYMBOL(iwl_dbg_log_tx_data_frame); void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, u16 length, struct ieee80211_hdr *header) @@ -1636,7 +1614,6 @@ void iwl_dbg_log_rx_data_frame(struct iwl_priv *priv, (priv->rx_traffic_idx + 1) % IWL_TRAFFIC_ENTRIES; } } -EXPORT_SYMBOL(iwl_dbg_log_rx_data_frame); const char *get_mgmt_string(int cmd) { @@ -1773,7 +1750,6 @@ void iwl_update_stats(struct iwl_priv *priv, bool is_tx, __le16 fc, u16 len) stats->data_bytes += len; } } -EXPORT_SYMBOL(iwl_update_stats); #endif static void iwl_force_rf_reset(struct iwl_priv *priv) @@ -1912,7 +1888,6 @@ int iwl_mac_change_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mutex_unlock(&priv->mutex); return err; } -EXPORT_SYMBOL(iwl_mac_change_interface); /* * On every watchdog tick we check (latest) time stamp. If it does not @@ -1984,7 +1959,6 @@ void iwl_bg_watchdog(unsigned long data) mod_timer(&priv->watchdog, jiffies + msecs_to_jiffies(IWL_WD_TICK(timeout))); } -EXPORT_SYMBOL(iwl_bg_watchdog); void iwl_setup_watchdog(struct iwl_priv *priv) { @@ -1996,7 +1970,6 @@ void iwl_setup_watchdog(struct iwl_priv *priv) else del_timer(&priv->watchdog); } -EXPORT_SYMBOL(iwl_setup_watchdog); /* * extended beacon time format @@ -2022,7 +1995,6 @@ u32 iwl_usecs_to_beacons(struct iwl_priv *priv, u32 usec, u32 beacon_interval) return (quot << priv->hw_params.beacon_time_tsf_bits) + rem; } -EXPORT_SYMBOL(iwl_usecs_to_beacons); /* base is usually what we get from ucode with each received frame, * the same as HW timer counter counting down @@ -2050,7 +2022,6 @@ __le32 iwl_add_beacon_time(struct iwl_priv *priv, u32 base, return cpu_to_le32(res); } -EXPORT_SYMBOL(iwl_add_beacon_time); #ifdef CONFIG_PM @@ -2070,7 +2041,6 @@ int iwl_pci_suspend(struct device *device) return 0; } -EXPORT_SYMBOL(iwl_pci_suspend); int iwl_pci_resume(struct device *device) { @@ -2099,7 +2069,6 @@ int iwl_pci_resume(struct device *device) return 0; } -EXPORT_SYMBOL(iwl_pci_resume); const struct dev_pm_ops iwl_pm_ops = { .suspend = iwl_pci_suspend, @@ -2109,6 +2078,5 @@ const struct dev_pm_ops iwl_pm_ops = { .poweroff = iwl_pci_suspend, .restore = iwl_pci_resume, }; -EXPORT_SYMBOL(iwl_pm_ops); #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h index e0ec17079dc..d47f3a87fce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-core.h +++ b/drivers/net/wireless/iwlwifi/iwl-core.h @@ -210,12 +210,7 @@ struct iwl_lib_ops { /* temperature */ struct iwl_temp_ops temp_ops; - /* check for plcp health */ - bool (*check_plcp_health)(struct iwl_priv *priv, - struct iwl_rx_packet *pkt); - /* check for ack health */ - bool (*check_ack_health)(struct iwl_priv *priv, - struct iwl_rx_packet *pkt); + int (*txfifo_flush)(struct iwl_priv *priv, u16 flush_control); void (*dev_txfifo_flush)(struct iwl_priv *priv, u16 flush_control); @@ -261,6 +256,8 @@ struct iwl_mod_params { int amsdu_size_8K; /* def: 1 = enable 8K amsdu size */ int antenna; /* def: 0 = both antennas (use diversity) */ int restart_fw; /* def: 1 = restart firmware */ + bool plcp_check; /* def: true = enable plcp health check */ + bool ack_check; /* def: false = disable ack health check */ }; /* @@ -339,6 +336,7 @@ struct iwl_bt_params { u8 ampdu_factor; u8 ampdu_density; bool bt_sco_disable; + bool bt_session_2; }; /* * @use_rts_for_aggregation: use rts/cts protection for HT traffic @@ -509,6 +507,7 @@ void iwl_rx_reply_error(struct iwl_priv *priv, * RX ******************************************************/ void iwl_cmd_queue_free(struct iwl_priv *priv); +void iwl_cmd_queue_unmap(struct iwl_priv *priv); int iwl_rx_queue_alloc(struct iwl_priv *priv); void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q); @@ -517,8 +516,6 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); /* Handlers */ void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); -void iwl_recover_from_statistics(struct iwl_priv *priv, - struct iwl_rx_packet *pkt); void iwl_chswitch_done(struct iwl_priv *priv, bool is_success); void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb); @@ -533,6 +530,7 @@ int iwl_tx_queue_init(struct iwl_priv *priv, struct iwl_tx_queue *txq, void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id); void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id); +void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id); void iwl_setup_watchdog(struct iwl_priv *priv); /***************************************************** * TX power diff --git a/drivers/net/wireless/iwlwifi/iwl-debugfs.c b/drivers/net/wireless/iwlwifi/iwl-debugfs.c index bc7a965c18f..8842411f1cf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-debugfs.c +++ b/drivers/net/wireless/iwlwifi/iwl-debugfs.c @@ -1788,7 +1788,6 @@ err: iwl_dbgfs_unregister(priv); return -ENOMEM; } -EXPORT_SYMBOL(iwl_dbgfs_register); /** * Remove the debugfs files and directories @@ -1802,7 +1801,6 @@ void iwl_dbgfs_unregister(struct iwl_priv *priv) debugfs_remove_recursive(priv->debugfs_dir); priv->debugfs_dir = NULL; } -EXPORT_SYMBOL(iwl_dbgfs_unregister); diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h index ecfbef40278..58165c769cf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-dev.h +++ b/drivers/net/wireless/iwlwifi/iwl-dev.h @@ -43,14 +43,14 @@ #include "iwl-prph.h" #include "iwl-fh.h" #include "iwl-debug.h" -#include "iwl-4965-hw.h" -#include "iwl-3945-hw.h" #include "iwl-agn-hw.h" #include "iwl-led.h" #include "iwl-power.h" #include "iwl-agn-rs.h" #include "iwl-agn-tt.h" +#define U32_PAD(n) ((4-(n))&0x3) + struct iwl_tx_queue; /* CT-KILL constants */ @@ -1110,6 +1110,11 @@ struct iwl_event_log { /* BT Antenna Coupling Threshold (dB) */ #define IWL_BT_ANTENNA_COUPLING_THRESHOLD (35) +/* Firmware reload counter and Timestamp */ +#define IWL_MIN_RELOAD_DURATION 1000 /* 1000 ms */ +#define IWL_MAX_CONTINUE_RELOAD_CNT 4 + + enum iwl_reset { IWL_RF_RESET = 0, IWL_FW_RESET, @@ -1262,6 +1267,10 @@ struct iwl_priv { /* force reset */ struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET]; + /* firmware reload counter and timestamp */ + unsigned long reload_jiffies; + int reload_count; + /* we allocate array of iwl_channel_info for NIC's valid channels. * Access via channel # using indirect index array */ struct iwl_channel_info *channel_info; /* channel info array */ diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom.c b/drivers/net/wireless/iwlwifi/iwl-eeprom.c index 358cfd7e5af..833194a2c63 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom.c @@ -222,7 +222,6 @@ const u8 *iwlcore_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) BUG_ON(offset >= priv->cfg->base_params->eeprom_size); return &priv->eeprom[offset]; } -EXPORT_SYMBOL(iwlcore_eeprom_query_addr); static int iwl_init_otp_access(struct iwl_priv *priv) { @@ -382,7 +381,6 @@ const u8 *iwl_eeprom_query_addr(const struct iwl_priv *priv, size_t offset) { return priv->cfg->ops->lib->eeprom_ops.query_addr(priv, offset); } -EXPORT_SYMBOL(iwl_eeprom_query_addr); u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) { @@ -390,7 +388,6 @@ u16 iwl_eeprom_query16(const struct iwl_priv *priv, size_t offset) return 0; return (u16)priv->eeprom[offset] | ((u16)priv->eeprom[offset + 1] << 8); } -EXPORT_SYMBOL(iwl_eeprom_query16); /** * iwl_eeprom_init - read EEPROM contents @@ -509,14 +506,12 @@ err: alloc_err: return ret; } -EXPORT_SYMBOL(iwl_eeprom_init); void iwl_eeprom_free(struct iwl_priv *priv) { kfree(priv->eeprom); priv->eeprom = NULL; } -EXPORT_SYMBOL(iwl_eeprom_free); static void iwl_init_band_reference(const struct iwl_priv *priv, int eep_band, int *eeprom_ch_count, @@ -779,7 +774,6 @@ int iwl_init_channel_map(struct iwl_priv *priv) return 0; } -EXPORT_SYMBOL(iwl_init_channel_map); /* * iwl_free_channel_map - undo allocations in iwl_init_channel_map @@ -789,7 +783,6 @@ void iwl_free_channel_map(struct iwl_priv *priv) kfree(priv->channel_info); priv->channel_count = 0; } -EXPORT_SYMBOL(iwl_free_channel_map); /** * iwl_get_channel_info - Find driver's private channel info @@ -818,4 +811,3 @@ const struct iwl_channel_info *iwl_get_channel_info(const struct iwl_priv *priv, return NULL; } -EXPORT_SYMBOL(iwl_get_channel_info); diff --git a/drivers/net/wireless/iwlwifi/iwl-hcmd.c b/drivers/net/wireless/iwlwifi/iwl-hcmd.c index e4b953d7b7b..02499f68468 100644 --- a/drivers/net/wireless/iwlwifi/iwl-hcmd.c +++ b/drivers/net/wireless/iwlwifi/iwl-hcmd.c @@ -114,7 +114,6 @@ const char *get_cmd_string(u8 cmd) } } -EXPORT_SYMBOL(get_cmd_string); #define HOST_COMPLETE_TIMEOUT (HZ / 2) @@ -253,7 +252,6 @@ out: mutex_unlock(&priv->sync_cmd_mutex); return ret; } -EXPORT_SYMBOL(iwl_send_cmd_sync); int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) { @@ -262,7 +260,6 @@ int iwl_send_cmd(struct iwl_priv *priv, struct iwl_host_cmd *cmd) return iwl_send_cmd_sync(priv, cmd); } -EXPORT_SYMBOL(iwl_send_cmd); int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data) { @@ -274,7 +271,6 @@ int iwl_send_cmd_pdu(struct iwl_priv *priv, u8 id, u16 len, const void *data) return iwl_send_cmd_sync(priv, &cmd); } -EXPORT_SYMBOL(iwl_send_cmd_pdu); int iwl_send_cmd_pdu_async(struct iwl_priv *priv, u8 id, u16 len, const void *data, @@ -293,4 +289,3 @@ int iwl_send_cmd_pdu_async(struct iwl_priv *priv, return iwl_send_cmd_async(priv, &cmd); } -EXPORT_SYMBOL(iwl_send_cmd_pdu_async); diff --git a/drivers/net/wireless/iwlwifi/iwl-led.c b/drivers/net/wireless/iwlwifi/iwl-led.c index 074ad227522..d7f2a0bb32c 100644 --- a/drivers/net/wireless/iwlwifi/iwl-led.c +++ b/drivers/net/wireless/iwlwifi/iwl-led.c @@ -175,7 +175,6 @@ void iwl_leds_init(struct iwl_priv *priv) priv->led_registered = true; } -EXPORT_SYMBOL(iwl_leds_init); void iwl_leds_exit(struct iwl_priv *priv) { @@ -185,4 +184,3 @@ void iwl_leds_exit(struct iwl_priv *priv) led_classdev_unregister(&priv->led); kfree(priv->led.name); } -EXPORT_SYMBOL(iwl_leds_exit); diff --git a/drivers/net/wireless/iwlwifi/iwl-legacy.c b/drivers/net/wireless/iwlwifi/iwl-legacy.c deleted file mode 100644 index e1ace3ce30b..00000000000 --- a/drivers/net/wireless/iwlwifi/iwl-legacy.c +++ /dev/null @@ -1,657 +0,0 @@ -/****************************************************************************** - * - * GPL LICENSE SUMMARY - * - * Copyright(c) 2008 - 2010 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of version 2 of the GNU General Public License as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, - * USA - * - * The full GNU General Public License is included in this distribution - * in the file called LICENSE.GPL. - * - * Contact Information: - * Intel Linux Wireless <ilw@linux.intel.com> - * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 - *****************************************************************************/ - -#include <linux/kernel.h> -#include <net/mac80211.h> - -#include "iwl-dev.h" -#include "iwl-core.h" -#include "iwl-helpers.h" -#include "iwl-legacy.h" - -static void iwl_update_qos(struct iwl_priv *priv, struct iwl_rxon_context *ctx) -{ - if (test_bit(STATUS_EXIT_PENDING, &priv->status)) - return; - - if (!ctx->is_active) - return; - - ctx->qos_data.def_qos_parm.qos_flags = 0; - - if (ctx->qos_data.qos_active) - ctx->qos_data.def_qos_parm.qos_flags |= - QOS_PARAM_FLG_UPDATE_EDCA_MSK; - - if (ctx->ht.enabled) - ctx->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK; - - IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n", - ctx->qos_data.qos_active, - ctx->qos_data.def_qos_parm.qos_flags); - - iwl_send_cmd_pdu_async(priv, ctx->qos_cmd, - sizeof(struct iwl_qosparam_cmd), - &ctx->qos_data.def_qos_parm, NULL); -} - -/** - * iwl_legacy_mac_config - mac80211 config callback - */ -int iwl_legacy_mac_config(struct ieee80211_hw *hw, u32 changed) -{ - struct iwl_priv *priv = hw->priv; - const struct iwl_channel_info *ch_info; - struct ieee80211_conf *conf = &hw->conf; - struct ieee80211_channel *channel = conf->channel; - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - struct iwl_rxon_context *ctx; - unsigned long flags = 0; - int ret = 0; - u16 ch; - int scan_active = 0; - bool ht_changed[NUM_IWL_RXON_CTX] = {}; - - if (WARN_ON(!priv->cfg->ops->legacy)) - return -EOPNOTSUPP; - - mutex_lock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "enter to channel %d changed 0x%X\n", - channel->hw_value, changed); - - if (unlikely(test_bit(STATUS_SCANNING, &priv->status))) { - scan_active = 1; - IWL_DEBUG_MAC80211(priv, "scan active\n"); - } - - if (changed & (IEEE80211_CONF_CHANGE_SMPS | - IEEE80211_CONF_CHANGE_CHANNEL)) { - /* mac80211 uses static for non-HT which is what we want */ - priv->current_ht_config.smps = conf->smps_mode; - - /* - * Recalculate chain counts. - * - * If monitor mode is enabled then mac80211 will - * set up the SM PS mode to OFF if an HT channel is - * configured. - */ - if (priv->cfg->ops->hcmd->set_rxon_chain) - for_each_context(priv, ctx) - priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); - } - - /* during scanning mac80211 will delay channel setting until - * scan finish with changed = 0 - */ - if (!changed || (changed & IEEE80211_CONF_CHANGE_CHANNEL)) { - if (scan_active) - goto set_ch_out; - - ch = channel->hw_value; - ch_info = iwl_get_channel_info(priv, channel->band, ch); - if (!is_channel_valid(ch_info)) { - IWL_DEBUG_MAC80211(priv, "leave - invalid channel\n"); - ret = -EINVAL; - goto set_ch_out; - } - - spin_lock_irqsave(&priv->lock, flags); - - for_each_context(priv, ctx) { - /* Configure HT40 channels */ - if (ctx->ht.enabled != conf_is_ht(conf)) { - ctx->ht.enabled = conf_is_ht(conf); - ht_changed[ctx->ctxid] = true; - } - if (ctx->ht.enabled) { - if (conf_is_ht40_minus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_BELOW; - ctx->ht.is_40mhz = true; - } else if (conf_is_ht40_plus(conf)) { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_ABOVE; - ctx->ht.is_40mhz = true; - } else { - ctx->ht.extension_chan_offset = - IEEE80211_HT_PARAM_CHA_SEC_NONE; - ctx->ht.is_40mhz = false; - } - } else - ctx->ht.is_40mhz = false; - - /* - * Default to no protection. Protection mode will - * later be set from BSS config in iwl_ht_conf - */ - ctx->ht.protection = IEEE80211_HT_OP_MODE_PROTECTION_NONE; - - /* if we are switching from ht to 2.4 clear flags - * from any ht related info since 2.4 does not - * support ht */ - if ((le16_to_cpu(ctx->staging.channel) != ch)) - ctx->staging.flags = 0; - - iwl_set_rxon_channel(priv, channel, ctx); - iwl_set_rxon_ht(priv, ht_conf); - - iwl_set_flags_for_band(priv, ctx, channel->band, - ctx->vif); - } - - spin_unlock_irqrestore(&priv->lock, flags); - - if (priv->cfg->ops->legacy->update_bcast_stations) - ret = priv->cfg->ops->legacy->update_bcast_stations(priv); - - set_ch_out: - /* The list of supported rates and rate mask can be different - * for each band; since the band may have changed, reset - * the rate mask to what mac80211 lists */ - iwl_set_rate(priv); - } - - if (changed & (IEEE80211_CONF_CHANGE_PS | - IEEE80211_CONF_CHANGE_IDLE)) { - ret = iwl_power_update_mode(priv, false); - if (ret) - IWL_DEBUG_MAC80211(priv, "Error setting sleep level\n"); - } - - if (changed & IEEE80211_CONF_CHANGE_POWER) { - IWL_DEBUG_MAC80211(priv, "TX Power old=%d new=%d\n", - priv->tx_power_user_lmt, conf->power_level); - - iwl_set_tx_power(priv, conf->power_level, false); - } - - if (!iwl_is_ready(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - goto out; - } - - if (scan_active) - goto out; - - for_each_context(priv, ctx) { - if (memcmp(&ctx->active, &ctx->staging, sizeof(ctx->staging))) - iwlcore_commit_rxon(priv, ctx); - else - IWL_DEBUG_INFO(priv, - "Not re-sending same RXON configuration.\n"); - if (ht_changed[ctx->ctxid]) - iwl_update_qos(priv, ctx); - } - -out: - IWL_DEBUG_MAC80211(priv, "leave\n"); - mutex_unlock(&priv->mutex); - return ret; -} -EXPORT_SYMBOL(iwl_legacy_mac_config); - -void iwl_legacy_mac_reset_tsf(struct ieee80211_hw *hw) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - /* IBSS can only be the IWL_RXON_CTX_BSS context */ - struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - - if (WARN_ON(!priv->cfg->ops->legacy)) - return; - - mutex_lock(&priv->mutex); - IWL_DEBUG_MAC80211(priv, "enter\n"); - - spin_lock_irqsave(&priv->lock, flags); - memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_config)); - spin_unlock_irqrestore(&priv->lock, flags); - - spin_lock_irqsave(&priv->lock, flags); - - /* new association get rid of ibss beacon skb */ - if (priv->beacon_skb) - dev_kfree_skb(priv->beacon_skb); - - priv->beacon_skb = NULL; - - priv->timestamp = 0; - - spin_unlock_irqrestore(&priv->lock, flags); - - iwl_scan_cancel_timeout(priv, 100); - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - not ready\n"); - mutex_unlock(&priv->mutex); - return; - } - - /* we are restarting association process - * clear RXON_FILTER_ASSOC_MSK bit - */ - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - iwlcore_commit_rxon(priv, ctx); - - iwl_set_rate(priv); - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} -EXPORT_SYMBOL(iwl_legacy_mac_reset_tsf); - -static void iwl_ht_conf(struct iwl_priv *priv, - struct ieee80211_vif *vif) -{ - struct iwl_ht_config *ht_conf = &priv->current_ht_config; - struct ieee80211_sta *sta; - struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - IWL_DEBUG_ASSOC(priv, "enter:\n"); - - if (!ctx->ht.enabled) - return; - - ctx->ht.protection = - bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_PROTECTION; - ctx->ht.non_gf_sta_present = - !!(bss_conf->ht_operation_mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT); - - ht_conf->single_chain_sufficient = false; - - switch (vif->type) { - case NL80211_IFTYPE_STATION: - rcu_read_lock(); - sta = ieee80211_find_sta(vif, bss_conf->bssid); - if (sta) { - struct ieee80211_sta_ht_cap *ht_cap = &sta->ht_cap; - int maxstreams; - - maxstreams = (ht_cap->mcs.tx_params & - IEEE80211_HT_MCS_TX_MAX_STREAMS_MASK) - >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT; - maxstreams += 1; - - if ((ht_cap->mcs.rx_mask[1] == 0) && - (ht_cap->mcs.rx_mask[2] == 0)) - ht_conf->single_chain_sufficient = true; - if (maxstreams <= 1) - ht_conf->single_chain_sufficient = true; - } else { - /* - * If at all, this can only happen through a race - * when the AP disconnects us while we're still - * setting up the connection, in that case mac80211 - * will soon tell us about that. - */ - ht_conf->single_chain_sufficient = true; - } - rcu_read_unlock(); - break; - case NL80211_IFTYPE_ADHOC: - ht_conf->single_chain_sufficient = true; - break; - default: - break; - } - - IWL_DEBUG_ASSOC(priv, "leave\n"); -} - -static inline void iwl_set_no_assoc(struct iwl_priv *priv, - struct ieee80211_vif *vif) -{ - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - - /* - * inform the ucode that there is no longer an - * association and that no more packets should be - * sent - */ - ctx->staging.filter_flags &= ~RXON_FILTER_ASSOC_MSK; - ctx->staging.assoc_id = 0; - iwlcore_commit_rxon(priv, ctx); -} - -static void iwlcore_beacon_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif) -{ - struct iwl_priv *priv = hw->priv; - unsigned long flags; - __le64 timestamp; - struct sk_buff *skb = ieee80211_beacon_get(hw, vif); - - if (!skb) - return; - - IWL_DEBUG_MAC80211(priv, "enter\n"); - - lockdep_assert_held(&priv->mutex); - - if (!priv->beacon_ctx) { - IWL_ERR(priv, "update beacon but no beacon context!\n"); - dev_kfree_skb(skb); - return; - } - - spin_lock_irqsave(&priv->lock, flags); - - if (priv->beacon_skb) - dev_kfree_skb(priv->beacon_skb); - - priv->beacon_skb = skb; - - timestamp = ((struct ieee80211_mgmt *)skb->data)->u.beacon.timestamp; - priv->timestamp = le64_to_cpu(timestamp); - - IWL_DEBUG_MAC80211(priv, "leave\n"); - spin_unlock_irqrestore(&priv->lock, flags); - - if (!iwl_is_ready_rf(priv)) { - IWL_DEBUG_MAC80211(priv, "leave - RF not ready\n"); - return; - } - - priv->cfg->ops->legacy->post_associate(priv); -} - -void iwl_legacy_mac_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *bss_conf, - u32 changes) -{ - struct iwl_priv *priv = hw->priv; - struct iwl_rxon_context *ctx = iwl_rxon_ctx_from_vif(vif); - int ret; - - if (WARN_ON(!priv->cfg->ops->legacy)) - return; - - IWL_DEBUG_MAC80211(priv, "changes = 0x%X\n", changes); - - if (!iwl_is_alive(priv)) - return; - - mutex_lock(&priv->mutex); - - if (changes & BSS_CHANGED_QOS) { - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - ctx->qos_data.qos_active = bss_conf->qos; - iwl_update_qos(priv, ctx); - spin_unlock_irqrestore(&priv->lock, flags); - } - - if (changes & BSS_CHANGED_BEACON_ENABLED) { - /* - * the add_interface code must make sure we only ever - * have a single interface that could be beaconing at - * any time. - */ - if (vif->bss_conf.enable_beacon) - priv->beacon_ctx = ctx; - else - priv->beacon_ctx = NULL; - } - - if (changes & BSS_CHANGED_BEACON && vif->type == NL80211_IFTYPE_AP) { - dev_kfree_skb(priv->beacon_skb); - priv->beacon_skb = ieee80211_beacon_get(hw, vif); - } - - if (changes & BSS_CHANGED_BEACON_INT && vif->type == NL80211_IFTYPE_AP) - iwl_send_rxon_timing(priv, ctx); - - if (changes & BSS_CHANGED_BSSID) { - IWL_DEBUG_MAC80211(priv, "BSSID %pM\n", bss_conf->bssid); - - /* - * If there is currently a HW scan going on in the - * background then we need to cancel it else the RXON - * below/in post_associate will fail. - */ - if (iwl_scan_cancel_timeout(priv, 100)) { - IWL_WARN(priv, "Aborted scan still in progress after 100ms\n"); - IWL_DEBUG_MAC80211(priv, "leaving - scan abort failed.\n"); - mutex_unlock(&priv->mutex); - return; - } - - /* mac80211 only sets assoc when in STATION mode */ - if (vif->type == NL80211_IFTYPE_ADHOC || bss_conf->assoc) { - memcpy(ctx->staging.bssid_addr, - bss_conf->bssid, ETH_ALEN); - - /* currently needed in a few places */ - memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); - } else { - ctx->staging.filter_flags &= - ~RXON_FILTER_ASSOC_MSK; - } - - } - - /* - * This needs to be after setting the BSSID in case - * mac80211 decides to do both changes at once because - * it will invoke post_associate. - */ - if (vif->type == NL80211_IFTYPE_ADHOC && changes & BSS_CHANGED_BEACON) - iwlcore_beacon_update(hw, vif); - - if (changes & BSS_CHANGED_ERP_PREAMBLE) { - IWL_DEBUG_MAC80211(priv, "ERP_PREAMBLE %d\n", - bss_conf->use_short_preamble); - if (bss_conf->use_short_preamble) - ctx->staging.flags |= RXON_FLG_SHORT_PREAMBLE_MSK; - else - ctx->staging.flags &= ~RXON_FLG_SHORT_PREAMBLE_MSK; - } - - if (changes & BSS_CHANGED_ERP_CTS_PROT) { - IWL_DEBUG_MAC80211(priv, "ERP_CTS %d\n", bss_conf->use_cts_prot); - if (bss_conf->use_cts_prot && (priv->band != IEEE80211_BAND_5GHZ)) - ctx->staging.flags |= RXON_FLG_TGG_PROTECT_MSK; - else - ctx->staging.flags &= ~RXON_FLG_TGG_PROTECT_MSK; - if (bss_conf->use_cts_prot) - ctx->staging.flags |= RXON_FLG_SELF_CTS_EN; - else - ctx->staging.flags &= ~RXON_FLG_SELF_CTS_EN; - } - - if (changes & BSS_CHANGED_BASIC_RATES) { - /* XXX use this information - * - * To do that, remove code from iwl_set_rate() and put something - * like this here: - * - if (A-band) - ctx->staging.ofdm_basic_rates = - bss_conf->basic_rates; - else - ctx->staging.ofdm_basic_rates = - bss_conf->basic_rates >> 4; - ctx->staging.cck_basic_rates = - bss_conf->basic_rates & 0xF; - */ - } - - if (changes & BSS_CHANGED_HT) { - iwl_ht_conf(priv, vif); - - if (priv->cfg->ops->hcmd->set_rxon_chain) - priv->cfg->ops->hcmd->set_rxon_chain(priv, ctx); - } - - if (changes & BSS_CHANGED_ASSOC) { - IWL_DEBUG_MAC80211(priv, "ASSOC %d\n", bss_conf->assoc); - if (bss_conf->assoc) { - priv->timestamp = bss_conf->timestamp; - - if (!iwl_is_rfkill(priv)) - priv->cfg->ops->legacy->post_associate(priv); - } else - iwl_set_no_assoc(priv, vif); - } - - if (changes && iwl_is_associated_ctx(ctx) && bss_conf->aid) { - IWL_DEBUG_MAC80211(priv, "Changes (%#x) while associated\n", - changes); - ret = iwl_send_rxon_assoc(priv, ctx); - if (!ret) { - /* Sync active_rxon with latest change. */ - memcpy((void *)&ctx->active, - &ctx->staging, - sizeof(struct iwl_rxon_cmd)); - } - } - - if (changes & BSS_CHANGED_BEACON_ENABLED) { - if (vif->bss_conf.enable_beacon) { - memcpy(ctx->staging.bssid_addr, - bss_conf->bssid, ETH_ALEN); - memcpy(priv->bssid, bss_conf->bssid, ETH_ALEN); - priv->cfg->ops->legacy->config_ap(priv); - } else - iwl_set_no_assoc(priv, vif); - } - - if (changes & BSS_CHANGED_IBSS) { - ret = priv->cfg->ops->legacy->manage_ibss_station(priv, vif, - bss_conf->ibss_joined); - if (ret) - IWL_ERR(priv, "failed to %s IBSS station %pM\n", - bss_conf->ibss_joined ? "add" : "remove", - bss_conf->bssid); - } - - mutex_unlock(&priv->mutex); - - IWL_DEBUG_MAC80211(priv, "leave\n"); -} -EXPORT_SYMBOL(iwl_legacy_mac_bss_info_changed); - -irqreturn_t iwl_isr_legacy(int irq, void *data) -{ - struct iwl_priv *priv = data; - u32 inta, inta_mask; - u32 inta_fh; - unsigned long flags; - if (!priv) - return IRQ_NONE; - - spin_lock_irqsave(&priv->lock, flags); - - /* Disable (but don't clear!) interrupts here to avoid - * back-to-back ISRs and sporadic interrupts from our NIC. - * If we have something to service, the tasklet will re-enable ints. - * If we *don't* have something, we'll re-enable before leaving here. */ - inta_mask = iwl_read32(priv, CSR_INT_MASK); /* just for debug */ - iwl_write32(priv, CSR_INT_MASK, 0x00000000); - - /* Discover which interrupts are active/pending */ - inta = iwl_read32(priv, CSR_INT); - inta_fh = iwl_read32(priv, CSR_FH_INT_STATUS); - - /* Ignore interrupt if there's nothing in NIC to service. - * This may be due to IRQ shared with another device, - * or due to sporadic interrupts thrown from our NIC. */ - if (!inta && !inta_fh) { - IWL_DEBUG_ISR(priv, - "Ignore interrupt, inta == 0, inta_fh == 0\n"); - goto none; - } - - if ((inta == 0xFFFFFFFF) || ((inta & 0xFFFFFFF0) == 0xa5a5a5a0)) { - /* Hardware disappeared. It might have already raised - * an interrupt */ - IWL_WARN(priv, "HARDWARE GONE?? INTA == 0x%08x\n", inta); - goto unplugged; - } - - IWL_DEBUG_ISR(priv, "ISR inta 0x%08x, enabled 0x%08x, fh 0x%08x\n", - inta, inta_mask, inta_fh); - - inta &= ~CSR_INT_BIT_SCD; - - /* iwl_irq_tasklet() will service interrupts and re-enable them */ - if (likely(inta || inta_fh)) - tasklet_schedule(&priv->irq_tasklet); - -unplugged: - spin_unlock_irqrestore(&priv->lock, flags); - return IRQ_HANDLED; - -none: - /* re-enable interrupts here since we don't have anything to service. */ - /* only Re-enable if disabled by irq */ - if (test_bit(STATUS_INT_ENABLED, &priv->status)) - iwl_enable_interrupts(priv); - spin_unlock_irqrestore(&priv->lock, flags); - return IRQ_NONE; -} -EXPORT_SYMBOL(iwl_isr_legacy); - -/* - * iwl_legacy_tx_cmd_protection: Set rts/cts. 3945 and 4965 only share this - * function. - */ -void iwl_legacy_tx_cmd_protection(struct iwl_priv *priv, - struct ieee80211_tx_info *info, - __le16 fc, __le32 *tx_flags) -{ - if (info->control.rates[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) { - *tx_flags |= TX_CMD_FLG_RTS_MSK; - *tx_flags &= ~TX_CMD_FLG_CTS_MSK; - *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; - - if (!ieee80211_is_mgmt(fc)) - return; - - switch (fc & cpu_to_le16(IEEE80211_FCTL_STYPE)) { - case cpu_to_le16(IEEE80211_STYPE_AUTH): - case cpu_to_le16(IEEE80211_STYPE_DEAUTH): - case cpu_to_le16(IEEE80211_STYPE_ASSOC_REQ): - case cpu_to_le16(IEEE80211_STYPE_REASSOC_REQ): - *tx_flags &= ~TX_CMD_FLG_RTS_MSK; - *tx_flags |= TX_CMD_FLG_CTS_MSK; - break; - } - } else if (info->control.rates[0].flags & - IEEE80211_TX_RC_USE_CTS_PROTECT) { - *tx_flags &= ~TX_CMD_FLG_RTS_MSK; - *tx_flags |= TX_CMD_FLG_CTS_MSK; - *tx_flags |= TX_CMD_FLG_FULL_TXOP_PROT_MSK; - } -} -EXPORT_SYMBOL(iwl_legacy_tx_cmd_protection); diff --git a/drivers/net/wireless/iwlwifi/iwl-power.c b/drivers/net/wireless/iwlwifi/iwl-power.c index 1d1bf3234d8..576795e2c75 100644 --- a/drivers/net/wireless/iwlwifi/iwl-power.c +++ b/drivers/net/wireless/iwlwifi/iwl-power.c @@ -425,7 +425,6 @@ int iwl_power_set_mode(struct iwl_priv *priv, struct iwl_powertable_cmd *cmd, return ret; } -EXPORT_SYMBOL(iwl_power_set_mode); int iwl_power_update_mode(struct iwl_priv *priv, bool force) { @@ -434,7 +433,6 @@ int iwl_power_update_mode(struct iwl_priv *priv, bool force) iwl_power_build_cmd(priv, &cmd); return iwl_power_set_mode(priv, &cmd, force); } -EXPORT_SYMBOL(iwl_power_update_mode); /* initialize to default */ void iwl_power_initialize(struct iwl_priv *priv) @@ -448,4 +446,3 @@ void iwl_power_initialize(struct iwl_priv *priv) memset(&priv->power_data.sleep_cmd, 0, sizeof(priv->power_data.sleep_cmd)); } -EXPORT_SYMBOL(iwl_power_initialize); diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c index bc89393fb69..566e2d979ce 100644 --- a/drivers/net/wireless/iwlwifi/iwl-rx.c +++ b/drivers/net/wireless/iwlwifi/iwl-rx.c @@ -37,6 +37,7 @@ #include "iwl-sta.h" #include "iwl-io.h" #include "iwl-helpers.h" +#include "iwl-agn-calib.h" /************************** RX-FUNCTIONS ****************************/ /* * Rx theory of operation @@ -118,7 +119,6 @@ int iwl_rx_queue_space(const struct iwl_rx_queue *q) s = 0; return s; } -EXPORT_SYMBOL(iwl_rx_queue_space); /** * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue @@ -170,7 +170,6 @@ void iwl_rx_queue_update_write_ptr(struct iwl_priv *priv, struct iwl_rx_queue *q exit_unlock: spin_unlock_irqrestore(&q->lock, flags); } -EXPORT_SYMBOL(iwl_rx_queue_update_write_ptr); int iwl_rx_queue_alloc(struct iwl_priv *priv) { @@ -211,8 +210,6 @@ err_rb: err_bd: return -ENOMEM; } -EXPORT_SYMBOL(iwl_rx_queue_alloc); - void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) @@ -229,27 +226,397 @@ void iwl_rx_spectrum_measure_notif(struct iwl_priv *priv, memcpy(&priv->measure_report, report, sizeof(*report)); priv->measurement_status |= MEASUREMENT_READY; } -EXPORT_SYMBOL(iwl_rx_spectrum_measure_notif); -void iwl_recover_from_statistics(struct iwl_priv *priv, - struct iwl_rx_packet *pkt) +/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */ +#define ACK_CNT_RATIO (50) +#define BA_TIMEOUT_CNT (5) +#define BA_TIMEOUT_MAX (16) + +/** + * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries. + * + * When the ACK count ratio is low and aggregated BA timeout retries exceeding + * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal + * operation state. + */ +static bool iwl_good_ack_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) +{ + int actual_delta, expected_delta, ba_timeout_delta; + struct statistics_tx *cur, *old; + + if (priv->_agn.agg_tids_count) + return true; + + if (iwl_bt_statistics(priv)) { + cur = &pkt->u.stats_bt.tx; + old = &priv->_agn.statistics_bt.tx; + } else { + cur = &pkt->u.stats.tx; + old = &priv->_agn.statistics.tx; + } + + actual_delta = le32_to_cpu(cur->actual_ack_cnt) - + le32_to_cpu(old->actual_ack_cnt); + expected_delta = le32_to_cpu(cur->expected_ack_cnt) - + le32_to_cpu(old->expected_ack_cnt); + + /* Values should not be negative, but we do not trust the firmware */ + if (actual_delta <= 0 || expected_delta <= 0) + return true; + + ba_timeout_delta = le32_to_cpu(cur->agg.ba_timeout) - + le32_to_cpu(old->agg.ba_timeout); + + if ((actual_delta * 100 / expected_delta) < ACK_CNT_RATIO && + ba_timeout_delta > BA_TIMEOUT_CNT) { + IWL_DEBUG_RADIO(priv, "deltas: actual %d expected %d ba_timeout %d\n", + actual_delta, expected_delta, ba_timeout_delta); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + /* + * This is ifdef'ed on DEBUGFS because otherwise the + * statistics aren't available. If DEBUGFS is set but + * DEBUG is not, these will just compile out. + */ + IWL_DEBUG_RADIO(priv, "rx_detected_cnt delta %d\n", + priv->_agn.delta_statistics.tx.rx_detected_cnt); + IWL_DEBUG_RADIO(priv, + "ack_or_ba_timeout_collision delta %d\n", + priv->_agn.delta_statistics.tx.ack_or_ba_timeout_collision); +#endif + + if (ba_timeout_delta >= BA_TIMEOUT_MAX) + return false; + } + + return true; +} + +/** + * iwl_good_plcp_health - checks for plcp error. + * + * When the plcp error is exceeding the thresholds, reset the radio + * to improve the throughput. + */ +static bool iwl_good_plcp_health(struct iwl_priv *priv, struct iwl_rx_packet *pkt) { + bool rc = true; + int combined_plcp_delta; + unsigned int plcp_msec; + unsigned long plcp_received_jiffies; + + if (priv->cfg->base_params->plcp_delta_threshold == + IWL_MAX_PLCP_ERR_THRESHOLD_DISABLE) { + IWL_DEBUG_RADIO(priv, "plcp_err check disabled\n"); + return rc; + } + + /* + * check for plcp_err and trigger radio reset if it exceeds + * the plcp error threshold plcp_delta. + */ + plcp_received_jiffies = jiffies; + plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies - + (long) priv->plcp_jiffies); + priv->plcp_jiffies = plcp_received_jiffies; + /* + * check to make sure plcp_msec is not 0 to prevent division + * by zero. + */ + if (plcp_msec) { + struct statistics_rx_phy *ofdm; + struct statistics_rx_ht_phy *ofdm_ht; + + if (iwl_bt_statistics(priv)) { + ofdm = &pkt->u.stats_bt.rx.ofdm; + ofdm_ht = &pkt->u.stats_bt.rx.ofdm_ht; + combined_plcp_delta = + (le32_to_cpu(ofdm->plcp_err) - + le32_to_cpu(priv->_agn.statistics_bt. + rx.ofdm.plcp_err)) + + (le32_to_cpu(ofdm_ht->plcp_err) - + le32_to_cpu(priv->_agn.statistics_bt. + rx.ofdm_ht.plcp_err)); + } else { + ofdm = &pkt->u.stats.rx.ofdm; + ofdm_ht = &pkt->u.stats.rx.ofdm_ht; + combined_plcp_delta = + (le32_to_cpu(ofdm->plcp_err) - + le32_to_cpu(priv->_agn.statistics. + rx.ofdm.plcp_err)) + + (le32_to_cpu(ofdm_ht->plcp_err) - + le32_to_cpu(priv->_agn.statistics. + rx.ofdm_ht.plcp_err)); + } + + if ((combined_plcp_delta > 0) && + ((combined_plcp_delta * 100) / plcp_msec) > + priv->cfg->base_params->plcp_delta_threshold) { + /* + * if plcp_err exceed the threshold, + * the following data is printed in csv format: + * Text: plcp_err exceeded %d, + * Received ofdm.plcp_err, + * Current ofdm.plcp_err, + * Received ofdm_ht.plcp_err, + * Current ofdm_ht.plcp_err, + * combined_plcp_delta, + * plcp_msec + */ + IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, " + "%u, %u, %u, %u, %d, %u mSecs\n", + priv->cfg->base_params->plcp_delta_threshold, + le32_to_cpu(ofdm->plcp_err), + le32_to_cpu(ofdm->plcp_err), + le32_to_cpu(ofdm_ht->plcp_err), + le32_to_cpu(ofdm_ht->plcp_err), + combined_plcp_delta, plcp_msec); + + rc = false; + } + } + return rc; +} + +static void iwl_recover_from_statistics(struct iwl_priv *priv, struct iwl_rx_packet *pkt) +{ + const struct iwl_mod_params *mod_params = priv->cfg->mod_params; + if (test_bit(STATUS_EXIT_PENDING, &priv->status) || !iwl_is_any_associated(priv)) return; - if (priv->cfg->ops->lib->check_ack_health && - !priv->cfg->ops->lib->check_ack_health(priv, pkt)) { + if (mod_params->ack_check && !iwl_good_ack_health(priv, pkt)) { IWL_ERR(priv, "low ack count detected, restart firmware\n"); if (!iwl_force_reset(priv, IWL_FW_RESET, false)) return; } - if (priv->cfg->ops->lib->check_plcp_health && - !priv->cfg->ops->lib->check_plcp_health(priv, pkt)) + if (mod_params->plcp_check && !iwl_good_plcp_health(priv, pkt)) iwl_force_reset(priv, IWL_RF_RESET, false); } -EXPORT_SYMBOL(iwl_recover_from_statistics); + +/* Calculate noise level, based on measurements during network silence just + * before arriving beacon. This measurement can be done only if we know + * exactly when to expect beacons, therefore only when we're associated. */ +static void iwl_rx_calc_noise(struct iwl_priv *priv) +{ + struct statistics_rx_non_phy *rx_info; + int num_active_rx = 0; + int total_silence = 0; + int bcn_silence_a, bcn_silence_b, bcn_silence_c; + int last_rx_noise; + + if (iwl_bt_statistics(priv)) + rx_info = &(priv->_agn.statistics_bt.rx.general.common); + else + rx_info = &(priv->_agn.statistics.rx.general); + bcn_silence_a = + le32_to_cpu(rx_info->beacon_silence_rssi_a) & IN_BAND_FILTER; + bcn_silence_b = + le32_to_cpu(rx_info->beacon_silence_rssi_b) & IN_BAND_FILTER; + bcn_silence_c = + le32_to_cpu(rx_info->beacon_silence_rssi_c) & IN_BAND_FILTER; + + if (bcn_silence_a) { + total_silence += bcn_silence_a; + num_active_rx++; + } + if (bcn_silence_b) { + total_silence += bcn_silence_b; + num_active_rx++; + } + if (bcn_silence_c) { + total_silence += bcn_silence_c; + num_active_rx++; + } + + /* Average among active antennas */ + if (num_active_rx) + last_rx_noise = (total_silence / num_active_rx) - 107; + else + last_rx_noise = IWL_NOISE_MEAS_NOT_AVAILABLE; + + IWL_DEBUG_CALIB(priv, "inband silence a %u, b %u, c %u, dBm %d\n", + bcn_silence_a, bcn_silence_b, bcn_silence_c, + last_rx_noise); +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +/* + * based on the assumption of all statistics counter are in DWORD + * FIXME: This function is for debugging, do not deal with + * the case of counters roll-over. + */ +static void iwl_accumulative_statistics(struct iwl_priv *priv, + __le32 *stats) +{ + int i, size; + __le32 *prev_stats; + u32 *accum_stats; + u32 *delta, *max_delta; + struct statistics_general_common *general, *accum_general; + struct statistics_tx *tx, *accum_tx; + + if (iwl_bt_statistics(priv)) { + prev_stats = (__le32 *)&priv->_agn.statistics_bt; + accum_stats = (u32 *)&priv->_agn.accum_statistics_bt; + size = sizeof(struct iwl_bt_notif_statistics); + general = &priv->_agn.statistics_bt.general.common; + accum_general = &priv->_agn.accum_statistics_bt.general.common; + tx = &priv->_agn.statistics_bt.tx; + accum_tx = &priv->_agn.accum_statistics_bt.tx; + delta = (u32 *)&priv->_agn.delta_statistics_bt; + max_delta = (u32 *)&priv->_agn.max_delta_bt; + } else { + prev_stats = (__le32 *)&priv->_agn.statistics; + accum_stats = (u32 *)&priv->_agn.accum_statistics; + size = sizeof(struct iwl_notif_statistics); + general = &priv->_agn.statistics.general.common; + accum_general = &priv->_agn.accum_statistics.general.common; + tx = &priv->_agn.statistics.tx; + accum_tx = &priv->_agn.accum_statistics.tx; + delta = (u32 *)&priv->_agn.delta_statistics; + max_delta = (u32 *)&priv->_agn.max_delta; + } + for (i = sizeof(__le32); i < size; + i += sizeof(__le32), stats++, prev_stats++, delta++, + max_delta++, accum_stats++) { + if (le32_to_cpu(*stats) > le32_to_cpu(*prev_stats)) { + *delta = (le32_to_cpu(*stats) - + le32_to_cpu(*prev_stats)); + *accum_stats += *delta; + if (*delta > *max_delta) + *max_delta = *delta; + } + } + + /* reset accumulative statistics for "no-counter" type statistics */ + accum_general->temperature = general->temperature; + accum_general->temperature_m = general->temperature_m; + accum_general->ttl_timestamp = general->ttl_timestamp; + accum_tx->tx_power.ant_a = tx->tx_power.ant_a; + accum_tx->tx_power.ant_b = tx->tx_power.ant_b; + accum_tx->tx_power.ant_c = tx->tx_power.ant_c; +} +#endif + +#define REG_RECALIB_PERIOD (60) + +void iwl_rx_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + int change; + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + if (iwl_bt_statistics(priv)) { + IWL_DEBUG_RX(priv, + "Statistics notification received (%d vs %d).\n", + (int)sizeof(struct iwl_bt_notif_statistics), + le32_to_cpu(pkt->len_n_flags) & + FH_RSCSR_FRAME_SIZE_MSK); + + change = ((priv->_agn.statistics_bt.general.common.temperature != + pkt->u.stats_bt.general.common.temperature) || + ((priv->_agn.statistics_bt.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats_bt.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK))); +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats_bt); +#endif + + } else { + IWL_DEBUG_RX(priv, + "Statistics notification received (%d vs %d).\n", + (int)sizeof(struct iwl_notif_statistics), + le32_to_cpu(pkt->len_n_flags) & + FH_RSCSR_FRAME_SIZE_MSK); + + change = ((priv->_agn.statistics.general.common.temperature != + pkt->u.stats.general.common.temperature) || + ((priv->_agn.statistics.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK) != + (pkt->u.stats.flag & + STATISTICS_REPLY_FLG_HT40_MODE_MSK))); +#ifdef CONFIG_IWLWIFI_DEBUGFS + iwl_accumulative_statistics(priv, (__le32 *)&pkt->u.stats); +#endif + + } + + iwl_recover_from_statistics(priv, pkt); + + if (iwl_bt_statistics(priv)) + memcpy(&priv->_agn.statistics_bt, &pkt->u.stats_bt, + sizeof(priv->_agn.statistics_bt)); + else + memcpy(&priv->_agn.statistics, &pkt->u.stats, + sizeof(priv->_agn.statistics)); + + set_bit(STATUS_STATISTICS, &priv->status); + + /* Reschedule the statistics timer to occur in + * REG_RECALIB_PERIOD seconds to ensure we get a + * thermal update even if the uCode doesn't give + * us one */ + mod_timer(&priv->statistics_periodic, jiffies + + msecs_to_jiffies(REG_RECALIB_PERIOD * 1000)); + + if (unlikely(!test_bit(STATUS_SCANNING, &priv->status)) && + (pkt->hdr.cmd == STATISTICS_NOTIFICATION)) { + iwl_rx_calc_noise(priv); + queue_work(priv->workqueue, &priv->run_time_calib_work); + } + if (priv->cfg->ops->lib->temp_ops.temperature && change) + priv->cfg->ops->lib->temp_ops.temperature(priv); +} + +void iwl_reply_statistics(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + if (le32_to_cpu(pkt->u.stats.flag) & UCODE_STATISTICS_CLEAR_MSK) { +#ifdef CONFIG_IWLWIFI_DEBUGFS + memset(&priv->_agn.accum_statistics, 0, + sizeof(struct iwl_notif_statistics)); + memset(&priv->_agn.delta_statistics, 0, + sizeof(struct iwl_notif_statistics)); + memset(&priv->_agn.max_delta, 0, + sizeof(struct iwl_notif_statistics)); + memset(&priv->_agn.accum_statistics_bt, 0, + sizeof(struct iwl_bt_notif_statistics)); + memset(&priv->_agn.delta_statistics_bt, 0, + sizeof(struct iwl_bt_notif_statistics)); + memset(&priv->_agn.max_delta_bt, 0, + sizeof(struct iwl_bt_notif_statistics)); +#endif + IWL_DEBUG_RX(priv, "Statistics have been cleared\n"); + } + iwl_rx_statistics(priv, rxb); +} + +void iwl_rx_missed_beacon_notif(struct iwl_priv *priv, + struct iwl_rx_mem_buffer *rxb) + +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_missed_beacon_notif *missed_beacon; + + missed_beacon = &pkt->u.missed_beacon; + if (le32_to_cpu(missed_beacon->consecutive_missed_beacons) > + priv->missed_beacon_threshold) { + IWL_DEBUG_CALIB(priv, + "missed bcn cnsq %d totl %d rcd %d expctd %d\n", + le32_to_cpu(missed_beacon->consecutive_missed_beacons), + le32_to_cpu(missed_beacon->total_missed_becons), + le32_to_cpu(missed_beacon->num_recvd_beacons), + le32_to_cpu(missed_beacon->num_expected_beacons)); + if (!test_bit(STATUS_SCANNING, &priv->status)) + iwl_init_sensitivity(priv); + } +} /* * returns non-zero if packet should be dropped @@ -302,4 +669,3 @@ int iwl_set_decrypted_flag(struct iwl_priv *priv, } return 0; } -EXPORT_SYMBOL(iwl_set_decrypted_flag); diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c index 08f1bea8b65..faa6d34cb65 100644 --- a/drivers/net/wireless/iwlwifi/iwl-scan.c +++ b/drivers/net/wireless/iwlwifi/iwl-scan.c @@ -155,7 +155,6 @@ int iwl_scan_cancel(struct iwl_priv *priv) queue_work(priv->workqueue, &priv->abort_scan); return 0; } -EXPORT_SYMBOL(iwl_scan_cancel); /** * iwl_scan_cancel_timeout - Cancel any currently executing HW scan @@ -180,7 +179,6 @@ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms) return test_bit(STATUS_SCAN_HW, &priv->status); } -EXPORT_SYMBOL(iwl_scan_cancel_timeout); /* Service response to REPLY_SCAN_CMD (0x80) */ static void iwl_rx_reply_scan(struct iwl_priv *priv, @@ -288,7 +286,6 @@ void iwl_setup_rx_scan_handlers(struct iwl_priv *priv) priv->rx_handlers[SCAN_COMPLETE_NOTIFICATION] = iwl_rx_scan_complete_notif; } -EXPORT_SYMBOL(iwl_setup_rx_scan_handlers); inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv, enum ieee80211_band band, @@ -301,7 +298,6 @@ inline u16 iwl_get_active_dwell_time(struct iwl_priv *priv, return IWL_ACTIVE_DWELL_TIME_24 + IWL_ACTIVE_DWELL_FACTOR_24GHZ * (n_probes + 1); } -EXPORT_SYMBOL(iwl_get_active_dwell_time); u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, enum ieee80211_band band, @@ -333,7 +329,6 @@ u16 iwl_get_passive_dwell_time(struct iwl_priv *priv, return passive; } -EXPORT_SYMBOL(iwl_get_passive_dwell_time); void iwl_init_scan_params(struct iwl_priv *priv) { @@ -343,7 +338,6 @@ void iwl_init_scan_params(struct iwl_priv *priv) if (!priv->scan_tx_ant[IEEE80211_BAND_2GHZ]) priv->scan_tx_ant[IEEE80211_BAND_2GHZ] = ant_idx; } -EXPORT_SYMBOL(iwl_init_scan_params); static int __must_check iwl_scan_initiate(struct iwl_priv *priv, struct ieee80211_vif *vif, @@ -439,7 +433,6 @@ out_unlock: return ret; } -EXPORT_SYMBOL(iwl_mac_hw_scan); /* * internal short scan, this function should only been called while associated. @@ -536,7 +529,6 @@ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame, return (u16)len; } -EXPORT_SYMBOL(iwl_fill_probe_req); static void iwl_bg_abort_scan(struct work_struct *work) { @@ -621,7 +613,6 @@ void iwl_setup_scan_deferred_work(struct iwl_priv *priv) INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan); INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check); } -EXPORT_SYMBOL(iwl_setup_scan_deferred_work); void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) { @@ -635,4 +626,3 @@ void iwl_cancel_scan_deferred_work(struct iwl_priv *priv) mutex_unlock(&priv->mutex); } } -EXPORT_SYMBOL(iwl_cancel_scan_deferred_work); diff --git a/drivers/net/wireless/iwlwifi/iwl-sta.c b/drivers/net/wireless/iwlwifi/iwl-sta.c index 49493d17651..bc90a12408a 100644 --- a/drivers/net/wireless/iwlwifi/iwl-sta.c +++ b/drivers/net/wireless/iwlwifi/iwl-sta.c @@ -169,7 +169,6 @@ int iwl_send_add_sta(struct iwl_priv *priv, return ret; } -EXPORT_SYMBOL(iwl_send_add_sta); static void iwl_set_ht_add_station(struct iwl_priv *priv, u8 index, struct ieee80211_sta *sta, @@ -316,7 +315,6 @@ u8 iwl_prep_station(struct iwl_priv *priv, struct iwl_rxon_context *ctx, return sta_id; } -EXPORT_SYMBOL_GPL(iwl_prep_station); #define STA_WAIT_TIMEOUT (HZ/2) @@ -379,7 +377,6 @@ int iwl_add_station_common(struct iwl_priv *priv, struct iwl_rxon_context *ctx, *sta_id_r = sta_id; return ret; } -EXPORT_SYMBOL(iwl_add_station_common); /** * iwl_sta_ucode_deactivate - deactivate ucode status for a station @@ -513,7 +510,6 @@ out_err: spin_unlock_irqrestore(&priv->sta_lock, flags); return -EINVAL; } -EXPORT_SYMBOL_GPL(iwl_remove_station); /** * iwl_clear_ucode_stations - clear ucode station table bits @@ -548,7 +544,6 @@ void iwl_clear_ucode_stations(struct iwl_priv *priv, if (!cleared) IWL_DEBUG_INFO(priv, "No active stations found to be cleared\n"); } -EXPORT_SYMBOL(iwl_clear_ucode_stations); /** * iwl_restore_stations() - Restore driver known stations to device @@ -625,7 +620,6 @@ void iwl_restore_stations(struct iwl_priv *priv, struct iwl_rxon_context *ctx) else IWL_DEBUG_INFO(priv, "Restoring all known stations .... complete.\n"); } -EXPORT_SYMBOL(iwl_restore_stations); void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { @@ -668,7 +662,6 @@ void iwl_reprogram_ap_sta(struct iwl_priv *priv, struct iwl_rxon_context *ctx) priv->stations[sta_id].sta.sta.addr, ret); iwl_send_lq_cmd(priv, ctx, &lq, CMD_SYNC, true); } -EXPORT_SYMBOL(iwl_reprogram_ap_sta); int iwl_get_free_ucode_key_index(struct iwl_priv *priv) { @@ -680,7 +673,6 @@ int iwl_get_free_ucode_key_index(struct iwl_priv *priv) return WEP_INVALID_OFFSET; } -EXPORT_SYMBOL(iwl_get_free_ucode_key_index); void iwl_dealloc_bcast_stations(struct iwl_priv *priv) { @@ -700,7 +692,6 @@ void iwl_dealloc_bcast_stations(struct iwl_priv *priv) } spin_unlock_irqrestore(&priv->sta_lock, flags); } -EXPORT_SYMBOL_GPL(iwl_dealloc_bcast_stations); #ifdef CONFIG_IWLWIFI_DEBUG static void iwl_dump_lq_cmd(struct iwl_priv *priv, @@ -810,7 +801,6 @@ int iwl_send_lq_cmd(struct iwl_priv *priv, struct iwl_rxon_context *ctx, } return ret; } -EXPORT_SYMBOL(iwl_send_lq_cmd); int iwl_mac_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -832,4 +822,3 @@ int iwl_mac_sta_remove(struct ieee80211_hw *hw, mutex_unlock(&priv->mutex); return ret; } -EXPORT_SYMBOL(iwl_mac_sta_remove); diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c index 073b6ce6141..277c9175dcf 100644 --- a/drivers/net/wireless/iwlwifi/iwl-tx.c +++ b/drivers/net/wireless/iwlwifi/iwl-tx.c @@ -84,7 +84,23 @@ void iwl_txq_update_write_ptr(struct iwl_priv *priv, struct iwl_tx_queue *txq) } txq->need_update = 0; } -EXPORT_SYMBOL(iwl_txq_update_write_ptr); + +/** + * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's + */ +void iwl_tx_queue_unmap(struct iwl_priv *priv, int txq_id) +{ + struct iwl_tx_queue *txq = &priv->txq[txq_id]; + struct iwl_queue *q = &txq->q; + + if (q->n_bd == 0) + return; + + while (q->write_ptr != q->read_ptr) { + priv->cfg->ops->lib->txq_free_tfd(priv, txq); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + } +} /** * iwl_tx_queue_free - Deallocate DMA queue. @@ -97,17 +113,10 @@ EXPORT_SYMBOL(iwl_txq_update_write_ptr); void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) { struct iwl_tx_queue *txq = &priv->txq[txq_id]; - struct iwl_queue *q = &txq->q; struct device *dev = &priv->pci_dev->dev; int i; - if (q->n_bd == 0) - return; - - /* first, empty all BD's */ - for (; q->write_ptr != q->read_ptr; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) - priv->cfg->ops->lib->txq_free_tfd(priv, txq); + iwl_tx_queue_unmap(priv, txq_id); /* De-alloc array of command/tx buffers */ for (i = 0; i < TFD_TX_CMD_SLOTS; i++) @@ -131,42 +140,35 @@ void iwl_tx_queue_free(struct iwl_priv *priv, int txq_id) /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } -EXPORT_SYMBOL(iwl_tx_queue_free); /** - * iwl_cmd_queue_free - Deallocate DMA queue. - * @txq: Transmit queue to deallocate. - * - * Empty queue by removing and destroying all BD's. - * Free all buffers. - * 0-fill, but do not free "txq" descriptor structure. + * iwl_cmd_queue_unmap - Unmap any remaining DMA mappings from command queue */ -void iwl_cmd_queue_free(struct iwl_priv *priv) +void iwl_cmd_queue_unmap(struct iwl_priv *priv) { struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; struct iwl_queue *q = &txq->q; - struct device *dev = &priv->pci_dev->dev; int i; bool huge = false; if (q->n_bd == 0) return; - for (; q->read_ptr != q->write_ptr; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + while (q->read_ptr != q->write_ptr) { /* we have no way to tell if it is a huge cmd ATM */ i = get_cmd_index(q, q->read_ptr, 0); - if (txq->meta[i].flags & CMD_SIZE_HUGE) { + if (txq->meta[i].flags & CMD_SIZE_HUGE) huge = true; - continue; - } + else + pci_unmap_single(priv->pci_dev, + dma_unmap_addr(&txq->meta[i], mapping), + dma_unmap_len(&txq->meta[i], len), + PCI_DMA_BIDIRECTIONAL); - pci_unmap_single(priv->pci_dev, - dma_unmap_addr(&txq->meta[i], mapping), - dma_unmap_len(&txq->meta[i], len), - PCI_DMA_BIDIRECTIONAL); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); } + if (huge) { i = q->n_window; pci_unmap_single(priv->pci_dev, @@ -174,6 +176,23 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) dma_unmap_len(&txq->meta[i], len), PCI_DMA_BIDIRECTIONAL); } +} + +/** + * iwl_cmd_queue_free - Deallocate DMA queue. + * @txq: Transmit queue to deallocate. + * + * Empty queue by removing and destroying all BD's. + * Free all buffers. + * 0-fill, but do not free "txq" descriptor structure. + */ +void iwl_cmd_queue_free(struct iwl_priv *priv) +{ + struct iwl_tx_queue *txq = &priv->txq[priv->cmd_queue]; + struct device *dev = &priv->pci_dev->dev; + int i; + + iwl_cmd_queue_unmap(priv); /* De-alloc array of command/tx buffers */ for (i = 0; i <= TFD_CMD_SLOTS; i++) @@ -193,7 +212,6 @@ void iwl_cmd_queue_free(struct iwl_priv *priv) /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } -EXPORT_SYMBOL(iwl_cmd_queue_free); /*************** DMA-QUEUE-GENERAL-FUNCTIONS ***** * DMA services @@ -233,7 +251,6 @@ int iwl_queue_space(const struct iwl_queue *q) s = 0; return s; } -EXPORT_SYMBOL(iwl_queue_space); /** @@ -384,7 +401,6 @@ out_free_arrays: return -ENOMEM; } -EXPORT_SYMBOL(iwl_tx_queue_init); void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, int slots_num, u32 txq_id) @@ -404,7 +420,6 @@ void iwl_tx_queue_reset(struct iwl_priv *priv, struct iwl_tx_queue *txq, /* Tell device where to find queue */ priv->cfg->ops->lib->txq_init(priv, txq); } -EXPORT_SYMBOL(iwl_tx_queue_reset); /*************** HOST COMMAND QUEUE FUNCTIONS *****/ @@ -641,4 +656,3 @@ void iwl_tx_cmd_complete(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb) } meta->flags = 0; } -EXPORT_SYMBOL(iwl_tx_cmd_complete); diff --git a/drivers/net/wireless/libertas_tf/main.c b/drivers/net/wireless/libertas_tf/main.c index 9278b3c8ee3..d4005081f1d 100644 --- a/drivers/net/wireless/libertas_tf/main.c +++ b/drivers/net/wireless/libertas_tf/main.c @@ -225,7 +225,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) lbtf_deb_leave(LBTF_DEB_MAIN); } -static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct lbtf_private *priv = hw->priv; @@ -236,7 +236,6 @@ static int lbtf_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) * there are no buffered multicast frames to send */ ieee80211_stop_queues(priv->hw); - return NETDEV_TX_OK; } static void lbtf_tx_work(struct work_struct *work) diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 5d39b284058..56f439d5801 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -541,7 +541,7 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, } -static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { bool ack; struct ieee80211_tx_info *txi; @@ -551,7 +551,7 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (skb->len < 10) { /* Should not happen; just a sanity check for addr1 use */ dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } ack = mac80211_hwsim_tx_frame(hw, skb); @@ -571,7 +571,6 @@ static int mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack) txi->flags |= IEEE80211_TX_STAT_ACK; ieee80211_tx_status_irqsafe(hw, skb); - return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index af4f2c64f24..df5959f36d0 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1535,6 +1535,13 @@ mwl8k_txq_reclaim(struct ieee80211_hw *hw, int index, int limit, int force) info = IEEE80211_SKB_CB(skb); ieee80211_tx_info_clear_status(info); + + /* Rate control is happening in the firmware. + * Ensure no tx rate is being reported. + */ + info->status.rates[0].idx = -1; + info->status.rates[0].count = 1; + if (MWL8K_TXD_SUCCESS(status)) info->flags |= IEEE80211_TX_STAT_ACK; @@ -1566,7 +1573,7 @@ static void mwl8k_txq_deinit(struct ieee80211_hw *hw, int index) txq->txd = NULL; } -static int +static void mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; @@ -1628,7 +1635,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) wiphy_debug(hw->wiphy, "failed to dma map skb, dropping TX frame.\n"); dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } spin_lock_bh(&priv->tx_lock); @@ -1665,8 +1672,6 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, int index, struct sk_buff *skb) mwl8k_tx_start(priv); spin_unlock_bh(&priv->tx_lock); - - return NETDEV_TX_OK; } @@ -2121,8 +2126,18 @@ static int mwl8k_cmd_set_hw_spec(struct ieee80211_hw *hw) cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rxd_dma); cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); - for (i = 0; i < MWL8K_TX_QUEUES; i++) - cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].txd_dma); + + /* + * Mac80211 stack has Q0 as highest priority and Q3 as lowest in + * that order. Firmware has Q3 as highest priority and Q0 as lowest + * in that order. Map Q3 of mac80211 to Q0 of firmware so that the + * priority is interpreted the right way in firmware. + */ + for (i = 0; i < MWL8K_TX_QUEUES; i++) { + int j = MWL8K_TX_QUEUES - 1 - i; + cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[j].txd_dma); + } + cmd->flags = cpu_to_le32(MWL8K_SET_HW_SPEC_FLAG_HOST_DECR_MGMT | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_PROBERESP | MWL8K_SET_HW_SPEC_FLAG_HOSTFORM_BEACON); @@ -3725,22 +3740,19 @@ static void mwl8k_rx_poll(unsigned long data) /* * Core driver operations. */ -static int mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void mwl8k_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct mwl8k_priv *priv = hw->priv; int index = skb_get_queue_mapping(skb); - int rc; if (!priv->radio_on) { wiphy_debug(hw->wiphy, "dropped TX frame since radio disabled\n"); dev_kfree_skb(skb); - return NETDEV_TX_OK; + return; } - rc = mwl8k_txq_xmit(hw, index, skb); - - return rc; + mwl8k_txq_xmit(hw, index, skb); } static int mwl8k_start(struct ieee80211_hw *hw) @@ -3945,9 +3957,13 @@ static int mwl8k_config(struct ieee80211_hw *hw, u32 changed) if (rc) goto out; - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x7); - if (!rc) - rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_RX, 0x3); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of RX antennas"); + rc = mwl8k_cmd_rf_antenna(hw, MWL8K_RF_ANTENNA_TX, 0x7); + if (rc) + wiphy_warn(hw->wiphy, "failed to set # of TX antennas"); + } else { rc = mwl8k_cmd_rf_tx_power(hw, conf->power_level); if (rc) @@ -4320,12 +4336,14 @@ static int mwl8k_conf_tx(struct ieee80211_hw *hw, u16 queue, if (!priv->wmm_enabled) rc = mwl8k_cmd_set_wmm_mode(hw, 1); - if (!rc) - rc = mwl8k_cmd_set_edca_params(hw, queue, + if (!rc) { + int q = MWL8K_TX_QUEUES - 1 - queue; + rc = mwl8k_cmd_set_edca_params(hw, q, params->cw_min, params->cw_max, params->aifs, params->txop); + } mwl8k_fw_unlock(hw); } @@ -4760,7 +4778,7 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) hw->queues = MWL8K_TX_QUEUES; /* Set rssi values to dBm */ - hw->flags |= IEEE80211_HW_SIGNAL_DBM; + hw->flags |= IEEE80211_HW_SIGNAL_DBM | IEEE80211_HW_HAS_RATE_CONTROL; hw->vif_data_size = sizeof(struct mwl8k_vif); hw->sta_data_size = sizeof(struct mwl8k_sta); diff --git a/drivers/net/wireless/orinoco/scan.c b/drivers/net/wireless/orinoco/scan.c index 86cb54c842e..e99ca1c1e0d 100644 --- a/drivers/net/wireless/orinoco/scan.c +++ b/drivers/net/wireless/orinoco/scan.c @@ -111,6 +111,11 @@ static void orinoco_add_hostscan_result(struct orinoco_private *priv, freq = ieee80211_dsss_chan_to_freq(le16_to_cpu(bss->a.channel)); channel = ieee80211_get_channel(wiphy, freq); + if (!channel) { + printk(KERN_DEBUG "Invalid channel designation %04X(%04X)", + bss->a.channel, freq); + return; /* Then ignore it for now */ + } timestamp = 0; capability = le16_to_cpu(bss->a.capabilities); beacon_interval = le16_to_cpu(bss->a.beacon_interv); diff --git a/drivers/net/wireless/p54/eeprom.c b/drivers/net/wireless/p54/eeprom.c index f54e15fcd62..13d750da930 100644 --- a/drivers/net/wireless/p54/eeprom.c +++ b/drivers/net/wireless/p54/eeprom.c @@ -524,10 +524,13 @@ err_data: struct p54_rssi_db_entry *p54_rssi_find(struct p54_common *priv, const u16 freq) { - struct p54_rssi_db_entry *entry = (void *)(priv->rssi_db->data + - priv->rssi_db->offset); + struct p54_rssi_db_entry *entry; int i, found = -1; + if (!priv->rssi_db) + return &p54_rssi_default; + + entry = (void *)(priv->rssi_db->data + priv->rssi_db->offset); for (i = 0; i < priv->rssi_db->entries; i++) { if (!same_band(freq, entry[i].freq)) continue; diff --git a/drivers/net/wireless/p54/fwio.c b/drivers/net/wireless/p54/fwio.c index 0d3d108f6fe..2fab7d20ffc 100644 --- a/drivers/net/wireless/p54/fwio.c +++ b/drivers/net/wireless/p54/fwio.c @@ -559,6 +559,7 @@ int p54_set_edcf(struct p54_common *priv) { struct sk_buff *skb; struct p54_edcf *edcf; + u8 rtd; skb = p54_alloc_skb(priv, P54_HDR_FLAG_CONTROL_OPSET, sizeof(*edcf), P54_CONTROL_TYPE_DCFINIT, GFP_ATOMIC); @@ -575,9 +576,15 @@ int p54_set_edcf(struct p54_common *priv) edcf->sifs = 0x0a; edcf->eofpad = 0x06; } + /* + * calculate the extra round trip delay according to the + * formula from 802.11-2007 17.3.8.6. + */ + rtd = 3 * priv->coverage_class; + edcf->slottime += rtd; + edcf->round_trip_delay = cpu_to_le16(rtd); /* (see prism54/isl_oid.h for further details) */ edcf->frameburst = cpu_to_le16(0); - edcf->round_trip_delay = cpu_to_le16(0); edcf->flags = 0; memset(edcf->mapping, 0, sizeof(edcf->mapping)); memcpy(edcf->queue, priv->qos_params, sizeof(edcf->queue)); diff --git a/drivers/net/wireless/p54/lmac.h b/drivers/net/wireless/p54/lmac.h index 5ca117e6f95..eb581abc107 100644 --- a/drivers/net/wireless/p54/lmac.h +++ b/drivers/net/wireless/p54/lmac.h @@ -526,7 +526,7 @@ int p54_init_leds(struct p54_common *priv); void p54_unregister_leds(struct p54_common *priv); /* xmit functions */ -int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); +void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb); int p54_tx_cancel(struct p54_common *priv, __le32 req_id); void p54_tx(struct p54_common *priv, struct sk_buff *skb); diff --git a/drivers/net/wireless/p54/main.c b/drivers/net/wireless/p54/main.c index e14a05bbc48..356e6bb443a 100644 --- a/drivers/net/wireless/p54/main.c +++ b/drivers/net/wireless/p54/main.c @@ -157,7 +157,7 @@ static int p54_beacon_update(struct p54_common *priv, * to cancel the old beacon template by hand, instead the firmware * will release the previous one through the feedback mechanism. */ - WARN_ON(p54_tx_80211(priv->hw, beacon)); + p54_tx_80211(priv->hw, beacon); priv->tsf_high32 = 0; priv->tsf_low32 = 0; @@ -566,6 +566,17 @@ static void p54_flush(struct ieee80211_hw *dev, bool drop) WARN(total, "tx flush timeout, unresponsive firmware"); } +static void p54_set_coverage_class(struct ieee80211_hw *dev, u8 coverage_class) +{ + struct p54_common *priv = dev->priv; + + mutex_lock(&priv->conf_mutex); + /* support all coverage class values as in 802.11-2007 Table 7-27 */ + priv->coverage_class = clamp_t(u8, coverage_class, 0, 31); + p54_set_edcf(priv); + mutex_unlock(&priv->conf_mutex); +} + static const struct ieee80211_ops p54_ops = { .tx = p54_tx_80211, .start = p54_start, @@ -584,6 +595,7 @@ static const struct ieee80211_ops p54_ops = { .conf_tx = p54_conf_tx, .get_stats = p54_get_stats, .get_survey = p54_get_survey, + .set_coverage_class = p54_set_coverage_class, }; struct ieee80211_hw *p54_init_common(size_t priv_data_len) diff --git a/drivers/net/wireless/p54/p54.h b/drivers/net/wireless/p54/p54.h index f951c8f3186..50730fc23fe 100644 --- a/drivers/net/wireless/p54/p54.h +++ b/drivers/net/wireless/p54/p54.h @@ -217,6 +217,7 @@ struct p54_common { u32 tsf_low32, tsf_high32; u32 basic_rate_mask; u16 aid; + u8 coverage_class; bool powersave_override; __le32 beacon_req_id; struct completion beacon_comp; diff --git a/drivers/net/wireless/p54/p54pci.c b/drivers/net/wireless/p54/p54pci.c index 1eacba4daa5..0494d7b102d 100644 --- a/drivers/net/wireless/p54/p54pci.c +++ b/drivers/net/wireless/p54/p54pci.c @@ -199,6 +199,7 @@ static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, while (i != idx) { u16 len; struct sk_buff *skb; + dma_addr_t dma_addr; desc = &ring[i]; len = le16_to_cpu(desc->len); skb = rx_buf[i]; @@ -216,17 +217,20 @@ static void p54p_check_rx_ring(struct ieee80211_hw *dev, u32 *index, len = priv->common.rx_mtu; } + dma_addr = le32_to_cpu(desc->host_addr); + pci_dma_sync_single_for_cpu(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); skb_put(skb, len); if (p54_rx(dev, skb)) { - pci_unmap_single(priv->pdev, - le32_to_cpu(desc->host_addr), - priv->common.rx_mtu + 32, - PCI_DMA_FROMDEVICE); + pci_unmap_single(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); rx_buf[i] = NULL; - desc->host_addr = 0; + desc->host_addr = cpu_to_le32(0); } else { skb_trim(skb, 0); + pci_dma_sync_single_for_device(priv->pdev, dma_addr, + priv->common.rx_mtu + 32, PCI_DMA_FROMDEVICE); desc->len = cpu_to_le16(priv->common.rx_mtu + 32); } diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index 21713a7638c..9b344a921e7 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -98,6 +98,7 @@ static struct usb_device_id p54u_table[] __devinitdata = { {USB_DEVICE(0x1413, 0x5400)}, /* Telsey 802.11g USB2.0 Adapter */ {USB_DEVICE(0x1435, 0x0427)}, /* Inventel UR054G */ {USB_DEVICE(0x1668, 0x1050)}, /* Actiontec 802UIG-1 */ + {USB_DEVICE(0x1740, 0x1000)}, /* Senao NUB-350 */ {USB_DEVICE(0x2001, 0x3704)}, /* DLink DWL-G122 rev A2 */ {USB_DEVICE(0x2001, 0x3705)}, /* D-Link DWL-G120 rev C1 */ {USB_DEVICE(0x413c, 0x5513)}, /* Dell WLA3310 USB Wireless Adapter */ diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 917d5d948e3..7834c26c295 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -367,7 +367,7 @@ static int p54_rx_data(struct p54_common *priv, struct sk_buff *skb) rx_status->mactime = ((u64)priv->tsf_high32) << 32 | tsf32; priv->tsf_low32 = tsf32; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) header_len += hdr->align[0]; @@ -696,7 +696,7 @@ static u8 p54_convert_algo(u32 cipher) } } -int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) +void p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) { struct p54_common *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -717,12 +717,8 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) &hdr_flags, &aid, &burst_allowed); if (p54_tx_qos_accounting_alloc(priv, skb, queue)) { - if (!IS_QOS_QUEUE(queue)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } else { - return NETDEV_TX_BUSY; - } + dev_kfree_skb_any(skb); + return; } padding = (unsigned long)(skb->data - (sizeof(*hdr) + sizeof(*txhdr))) & 3; @@ -865,5 +861,4 @@ int p54_tx_80211(struct ieee80211_hw *dev, struct sk_buff *skb) p54info->extra_len = extra_len; p54_tx(priv, skb); - return NETDEV_TX_OK; } diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index 848cc2cce24..518542b4bf9 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -2597,6 +2597,9 @@ static int rndis_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev, __le32 mode; int ret; + if (priv->device_type != RNDIS_BCM4320B) + return -ENOTSUPP; + netdev_dbg(usbdev->net, "%s(): %s, %d\n", __func__, enabled ? "enabled" : "disabled", timeout); diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 1df432c1f2c..19453d23e90 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -1185,7 +1185,7 @@ void rt2x00lib_rxdone(struct queue_entry *entry); /* * mac80211 handlers. */ -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); +void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb); int rt2x00mac_start(struct ieee80211_hw *hw); void rt2x00mac_stop(struct ieee80211_hw *hw); int rt2x00mac_add_interface(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 1b3edef9e3d..c2c35838c2f 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -99,7 +99,7 @@ static int rt2x00mac_tx_rts_cts(struct rt2x00_dev *rt2x00dev, return retval; } -int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +void rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rt2x00_dev *rt2x00dev = hw->priv; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); @@ -155,12 +155,11 @@ int rt2x00mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (rt2x00queue_threshold(queue)) rt2x00queue_pause_queue(queue); - return NETDEV_TX_OK; + return; exit_fail: ieee80211_stop_queue(rt2x00dev->hw, qid); dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } EXPORT_SYMBOL_GPL(rt2x00mac_tx); diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 5851cbc1e95..80db5cabc9b 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -146,7 +146,7 @@ static void rtl8180_handle_rx(struct ieee80211_hw *dev) rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; rx_status.mactime = le64_to_cpu(entry->tsft); - rx_status.flag |= RX_FLAG_TSFT; + rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; @@ -240,7 +240,7 @@ static irqreturn_t rtl8180_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; @@ -321,8 +321,6 @@ static int rtl8180_tx(struct ieee80211_hw *dev, struct sk_buff *skb) spin_unlock_irqrestore(&priv->lock, flags); rtl818x_iowrite8(priv, &priv->map->TX_DMA_POLLING, (1 << (prio + 4))); - - return 0; } void rtl8180_set_anaparam(struct rtl8180_priv *priv, u32 anaparam) @@ -687,7 +685,6 @@ static void rtl8180_beacon_work(struct work_struct *work) struct ieee80211_hw *dev = vif_priv->dev; struct ieee80211_mgmt *mgmt; struct sk_buff *skb; - int err = 0; /* don't overflow the tx ring */ if (ieee80211_queue_stopped(dev, 0)) @@ -708,8 +705,7 @@ static void rtl8180_beacon_work(struct work_struct *work) /* TODO: use actual beacon queue */ skb_set_queue_mapping(skb, 0); - err = rtl8180_tx(dev, skb); - WARN_ON(err); + rtl8180_tx(dev, skb); resched: /* diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 6b82cac37ee..c5a5e788f25 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -227,7 +227,7 @@ static void rtl8187_tx_cb(struct urb *urb) } } -static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct rtl8187_priv *priv = dev->priv; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -241,7 +241,7 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { kfree_skb(skb); - return NETDEV_TX_OK; + return; } flags = skb->len; @@ -309,8 +309,6 @@ static int rtl8187_tx(struct ieee80211_hw *dev, struct sk_buff *skb) kfree_skb(skb); } usb_free_urb(urb); - - return NETDEV_TX_OK; } static void rtl8187_rx_cb(struct urb *urb) @@ -373,7 +371,7 @@ static void rtl8187_rx_cb(struct urb *urb) rx_status.rate_idx = rate; rx_status.freq = dev->conf.channel->center_freq; rx_status.band = dev->conf.channel->band; - rx_status.flag |= RX_FLAG_TSFT; + rx_status.flag |= RX_FLAG_MACTIME_MPDU; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status)); diff --git a/drivers/net/wireless/rtlwifi/Kconfig b/drivers/net/wireless/rtlwifi/Kconfig index 86f8d4d6403..ce49e0ce7ca 100644 --- a/drivers/net/wireless/rtlwifi/Kconfig +++ b/drivers/net/wireless/rtlwifi/Kconfig @@ -3,6 +3,7 @@ config RTL8192CE depends on MAC80211 && PCI && EXPERIMENTAL select FW_LOADER select RTLWIFI + select RTL8192C_COMMON ---help--- This is the driver for Realtek RTL8192CE/RTL8188CE 802.11n PCIe wireless network adapters. @@ -14,6 +15,7 @@ config RTL8192CU depends on MAC80211 && USB && EXPERIMENTAL select FW_LOADER select RTLWIFI + select RTL8192C_COMMON ---help--- This is the driver for Realtek RTL8192CU/RTL8188CU 802.11n USB wireless network adapters. @@ -24,3 +26,8 @@ config RTLWIFI tristate depends on RTL8192CE || RTL8192CU default m + +config RTL8192C_COMMON + tristate + depends on RTL8192CE || RTL8192CU + default m diff --git a/drivers/net/wireless/rtlwifi/Makefile b/drivers/net/wireless/rtlwifi/Makefile index c3e83a1da33..9192fd58341 100644 --- a/drivers/net/wireless/rtlwifi/Makefile +++ b/drivers/net/wireless/rtlwifi/Makefile @@ -5,12 +5,18 @@ rtlwifi-objs := \ core.o \ debug.o \ efuse.o \ - pci.o \ ps.o \ rc.o \ regd.o \ usb.o +rtl8192c_common-objs += \ + +ifeq ($(CONFIG_PCI),y) +rtlwifi-objs += pci.o +endif + +obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c/ obj-$(CONFIG_RTL8192CE) += rtl8192ce/ obj-$(CONFIG_RTL8192CU) += rtl8192cu/ diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 3f40dc2b129..bb0c781f4a1 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -283,13 +283,7 @@ int rtl_init_core(struct ieee80211_hw *hw) rtlmac->hw = hw; /* <2> rate control register */ - if (rtl_rate_control_register()) { - RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, - ("rtl: Unable to register rtl_rc," - "use default RC !!\n")); - } else { - hw->rate_control_algorithm = "rtl_rc"; - } + hw->rate_control_algorithm = "rtl_rc"; /* * <3> init CRDA must come after init @@ -325,8 +319,6 @@ int rtl_init_core(struct ieee80211_hw *hw) void rtl_deinit_core(struct ieee80211_hw *hw) { - /*RC*/ - rtl_rate_control_unregister(); } void rtl_init_rx_config(struct ieee80211_hw *hw) @@ -945,11 +937,16 @@ MODULE_DESCRIPTION("Realtek 802.11n PCI wireless core"); static int __init rtl_core_module_init(void) { + if (rtl_rate_control_register()) + printk(KERN_ERR "rtlwifi: Unable to register rtl_rc," + "use default RC !!\n"); return 0; } static void __exit rtl_core_module_exit(void) { + /*RC*/ + rtl_rate_control_unregister(); } module_init(rtl_core_module_init); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index b0996bf8a21..e4f4aee8f29 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -82,7 +82,7 @@ static void rtl_op_stop(struct ieee80211_hw *hw) mutex_unlock(&rtlpriv->locks.conf_mutex); } -static int rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -97,11 +97,10 @@ static int rtl_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) rtlpriv->intf_ops->adapter_tx(hw, skb); - return NETDEV_TX_OK; + return; err_free: dev_kfree_skb_any(skb); - return NETDEV_TX_OK; } static int rtl_op_add_interface(struct ieee80211_hw *hw, @@ -552,6 +551,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_MAC80211, DBG_TRACE, ("BSS_CHANGED_HT\n")); + rcu_read_lock(); sta = ieee80211_find_sta(mac->vif, mac->bssid); if (sta) { @@ -564,6 +564,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, mac->current_ampdu_factor = sta->ht_cap.ampdu_factor; } + rcu_read_unlock(); rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SHORTGI_DENSITY, (u8 *) (&mac->max_mss_density)); @@ -615,6 +616,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, else mac->mode = WIRELESS_MODE_G; + rcu_read_lock(); sta = ieee80211_find_sta(mac->vif, mac->bssid); if (sta) { @@ -649,6 +651,7 @@ static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, */ } } + rcu_read_unlock(); /*mac80211 just give us CCK rates any time *So we add G rate in basic rates when diff --git a/drivers/net/wireless/rtlwifi/efuse.c b/drivers/net/wireless/rtlwifi/efuse.c index 62876cd5c41..4f92cba6810 100644 --- a/drivers/net/wireless/rtlwifi/efuse.c +++ b/drivers/net/wireless/rtlwifi/efuse.c @@ -1169,21 +1169,3 @@ static u8 efuse_calculate_word_cnts(u8 word_en) return word_cnts; } -void efuse_reset_loader(struct ieee80211_hw *hw) -{ - struct rtl_priv *rtlpriv = rtl_priv(hw); - u16 tmp_u2b; - - tmp_u2b = rtl_read_word(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN]); - rtl_write_word(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN], - (tmp_u2b & ~(BIT(12)))); - udelay(10000); - rtl_write_word(rtlpriv, rtlpriv->cfg->maps[SYS_FUNC_EN], - (tmp_u2b | BIT(12))); - udelay(10000); -} - -bool efuse_program_map(struct ieee80211_hw *hw, char *p_filename, u8 tabletype) -{ - return true; -} diff --git a/drivers/net/wireless/rtlwifi/efuse.h b/drivers/net/wireless/rtlwifi/efuse.h index 2d39a4df181..47774dd4c2a 100644 --- a/drivers/net/wireless/rtlwifi/efuse.h +++ b/drivers/net/wireless/rtlwifi/efuse.h @@ -117,8 +117,5 @@ extern bool efuse_shadow_update_chk(struct ieee80211_hw *hw); extern void rtl_efuse_shadow_map_update(struct ieee80211_hw *hw); extern void efuse_force_write_vendor_Id(struct ieee80211_hw *hw); extern void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx); -extern bool efuse_program_map(struct ieee80211_hw *hw, - char *p_filename, u8 tabletype); -extern void efuse_reset_loader(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/Makefile b/drivers/net/wireless/rtlwifi/rtl8192c/Makefile new file mode 100644 index 00000000000..aee42d7ae8a --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8192c/Makefile @@ -0,0 +1,9 @@ +rtl8192c-common-objs := \ + main.o \ + dm_common.o \ + fw_common.o \ + phy_common.o + +obj-$(CONFIG_RTL8192C_COMMON) += rtl8192c-common.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c index b4f1e4e6b73..bb023274414 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.c @@ -27,6 +27,8 @@ * *****************************************************************************/ +#include "dm_common.h" + struct dig_t dm_digtable; static struct ps_t dm_pstable; @@ -517,6 +519,7 @@ void rtl92c_dm_write_dig(struct ieee80211_hw *hw) dm_digtable.pre_igvalue = dm_digtable.cur_igvalue; } } +EXPORT_SYMBOL(rtl92c_dm_write_dig); static void rtl92c_dm_pwdb_monitor(struct ieee80211_hw *hw) { @@ -554,6 +557,7 @@ void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw) rtlpriv->dm.is_any_nonbepkts = false; rtlpriv->dm.is_cur_rdlstate = false; } +EXPORT_SYMBOL(rtl92c_dm_init_edca_turbo); static void rtl92c_dm_check_edca_turbo(struct ieee80211_hw *hw) { @@ -1103,6 +1107,7 @@ void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw) { rtl92c_dm_check_txpower_tracking_thermal_meter(hw); } +EXPORT_SYMBOL(rtl92c_dm_check_txpower_tracking); void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) { @@ -1118,6 +1123,7 @@ void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) rtlpriv->dm.useramask = false; } +EXPORT_SYMBOL(rtl92c_dm_init_rate_adaptive_mask); static void rtl92c_dm_refresh_rate_adaptive_mask(struct ieee80211_hw *hw) { @@ -1307,6 +1313,7 @@ void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal) dm_pstable.pre_rfstate = dm_pstable.cur_rfstate; } } +EXPORT_SYMBOL(rtl92c_dm_rf_saving); static void rtl92c_dm_dynamic_bb_powersaving(struct ieee80211_hw *hw) { @@ -1360,6 +1367,7 @@ void rtl92c_dm_init(struct ieee80211_hw *hw) rtl92c_dm_initialize_txpower_tracking(hw); rtl92c_dm_init_dynamic_bb_powersaving(hw); } +EXPORT_SYMBOL(rtl92c_dm_init); void rtl92c_dm_watchdog(struct ieee80211_hw *hw) { @@ -1380,9 +1388,11 @@ void rtl92c_dm_watchdog(struct ieee80211_hw *hw) rtl92c_dm_dig(hw); rtl92c_dm_false_alarm_counter_statistics(hw); rtl92c_dm_dynamic_bb_powersaving(hw); - rtl92c_dm_dynamic_txpower(hw); + rtlpriv->cfg->ops->dm_dynamic_txpower(hw); rtl92c_dm_check_txpower_tracking(hw); rtl92c_dm_refresh_rate_adaptive_mask(hw); rtl92c_dm_check_edca_turbo(hw); + } } +EXPORT_SYMBOL(rtl92c_dm_watchdog); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h new file mode 100644 index 00000000000..b9cbb0a3c03 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8192c/dm_common.h @@ -0,0 +1,204 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2010 Realtek Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL92COMMON_DM_H__ +#define __RTL92COMMON_DM_H__ + +#include "../wifi.h" +#include "../rtl8192ce/def.h" +#include "../rtl8192ce/reg.h" +#include "fw_common.h" + +#define HAL_DM_DIG_DISABLE BIT(0) +#define HAL_DM_HIPWR_DISABLE BIT(1) + +#define OFDM_TABLE_LENGTH 37 +#define CCK_TABLE_LENGTH 33 + +#define OFDM_TABLE_SIZE 37 +#define CCK_TABLE_SIZE 33 + +#define BW_AUTO_SWITCH_HIGH_LOW 25 +#define BW_AUTO_SWITCH_LOW_HIGH 30 + +#define DM_DIG_THRESH_HIGH 40 +#define DM_DIG_THRESH_LOW 35 + +#define DM_FALSEALARM_THRESH_LOW 400 +#define DM_FALSEALARM_THRESH_HIGH 1000 + +#define DM_DIG_MAX 0x3e +#define DM_DIG_MIN 0x1e + +#define DM_DIG_FA_UPPER 0x32 +#define DM_DIG_FA_LOWER 0x20 +#define DM_DIG_FA_TH0 0x20 +#define DM_DIG_FA_TH1 0x100 +#define DM_DIG_FA_TH2 0x200 + +#define DM_DIG_BACKOFF_MAX 12 +#define DM_DIG_BACKOFF_MIN -4 +#define DM_DIG_BACKOFF_DEFAULT 10 + +#define RXPATHSELECTION_SS_TH_lOW 30 +#define RXPATHSELECTION_DIFF_TH 18 + +#define DM_RATR_STA_INIT 0 +#define DM_RATR_STA_HIGH 1 +#define DM_RATR_STA_MIDDLE 2 +#define DM_RATR_STA_LOW 3 + +#define CTS2SELF_THVAL 30 +#define REGC38_TH 20 + +#define WAIOTTHVal 25 + +#define TXHIGHPWRLEVEL_NORMAL 0 +#define TXHIGHPWRLEVEL_LEVEL1 1 +#define TXHIGHPWRLEVEL_LEVEL2 2 +#define TXHIGHPWRLEVEL_BT1 3 +#define TXHIGHPWRLEVEL_BT2 4 + +#define DM_TYPE_BYFW 0 +#define DM_TYPE_BYDRIVER 1 + +#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 +#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 + +struct ps_t { + u8 pre_ccastate; + u8 cur_ccasate; + u8 pre_rfstate; + u8 cur_rfstate; + long rssi_val_min; +}; + +struct dig_t { + u8 dig_enable_flag; + u8 dig_ext_port_stage; + u32 rssi_lowthresh; + u32 rssi_highthresh; + u32 fa_lowthresh; + u32 fa_highthresh; + u8 cursta_connectctate; + u8 presta_connectstate; + u8 curmultista_connectstate; + u8 pre_igvalue; + u8 cur_igvalue; + char backoff_val; + char backoff_val_range_max; + char backoff_val_range_min; + u8 rx_gain_range_max; + u8 rx_gain_range_min; + u8 rssi_val_min; + u8 pre_cck_pd_state; + u8 cur_cck_pd_state; + u8 pre_cck_fa_state; + u8 cur_cck_fa_state; + u8 pre_ccastate; + u8 cur_ccasate; +}; + +struct swat_t { + u8 failure_cnt; + u8 try_flag; + u8 stop_trying; + long pre_rssi; + long trying_threshold; + u8 cur_antenna; + u8 pre_antenna; +}; + +enum tag_dynamic_init_gain_operation_type_definition { + DIG_TYPE_THRESH_HIGH = 0, + DIG_TYPE_THRESH_LOW = 1, + DIG_TYPE_BACKOFF = 2, + DIG_TYPE_RX_GAIN_MIN = 3, + DIG_TYPE_RX_GAIN_MAX = 4, + DIG_TYPE_ENABLE = 5, + DIG_TYPE_DISABLE = 6, + DIG_OP_TYPE_MAX +}; + +enum tag_cck_packet_detection_threshold_type_definition { + CCK_PD_STAGE_LowRssi = 0, + CCK_PD_STAGE_HighRssi = 1, + CCK_FA_STAGE_Low = 2, + CCK_FA_STAGE_High = 3, + CCK_PD_STAGE_MAX = 4, +}; + +enum dm_1r_cca_e { + CCA_1R = 0, + CCA_2R = 1, + CCA_MAX = 2, +}; + +enum dm_rf_e { + RF_SAVE = 0, + RF_NORMAL = 1, + RF_MAX = 2, +}; + +enum dm_sw_ant_switch_e { + ANS_ANTENNA_B = 1, + ANS_ANTENNA_A = 2, + ANS_ANTENNA_MAX = 3, +}; + +enum dm_dig_ext_port_alg_e { + DIG_EXT_PORT_STAGE_0 = 0, + DIG_EXT_PORT_STAGE_1 = 1, + DIG_EXT_PORT_STAGE_2 = 2, + DIG_EXT_PORT_STAGE_3 = 3, + DIG_EXT_PORT_STAGE_MAX = 4, +}; + +enum dm_dig_connect_e { + DIG_STA_DISCONNECT = 0, + DIG_STA_CONNECT = 1, + DIG_STA_BEFORE_CONNECT = 2, + DIG_MULTISTA_DISCONNECT = 3, + DIG_MULTISTA_CONNECT = 4, + DIG_CONNECT_MAX +}; + +extern struct dig_t dm_digtable; +void rtl92c_dm_init(struct ieee80211_hw *hw); +void rtl92c_dm_watchdog(struct ieee80211_hw *hw); +void rtl92c_dm_write_dig(struct ieee80211_hw *hw); +void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw); +void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw); +void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); +void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); +void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); +void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); +void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/fw.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c index 11c8bdb4af5..5ef91374b23 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c @@ -31,10 +31,9 @@ #include "../wifi.h" #include "../pci.h" #include "../base.h" -#include "reg.h" -#include "def.h" -#include "fw.h" -#include "table.h" +#include "../rtl8192ce/reg.h" +#include "../rtl8192ce/def.h" +#include "fw_common.h" static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable) { @@ -279,6 +278,7 @@ int rtl92c_download_fw(struct ieee80211_hw *hw) return 0; } +EXPORT_SYMBOL(rtl92c_download_fw); static bool _rtl92c_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum) { @@ -517,6 +517,7 @@ void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, return; } +EXPORT_SYMBOL(rtl92c_fill_h2c_cmd); void rtl92c_firmware_selfreset(struct ieee80211_hw *hw) { @@ -537,6 +538,7 @@ void rtl92c_firmware_selfreset(struct ieee80211_hw *hw) u1b_tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); } } +EXPORT_SYMBOL(rtl92c_firmware_selfreset); void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) { @@ -557,6 +559,7 @@ void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) rtl92c_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); } +EXPORT_SYMBOL(rtl92c_set_fw_pwrmode_cmd); #define BEACON_PG 0 /*->1*/ #define PSPOLL_PG 2 @@ -758,6 +761,7 @@ void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished) RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, ("Set RSVD page location to Fw FAIL!!!!!!.\n")); } +EXPORT_SYMBOL(rtl92c_set_fw_rsvdpagepkt); void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) { @@ -767,3 +771,4 @@ void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) rtl92c_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm); } +EXPORT_SYMBOL(rtl92c_set_fw_joinbss_report_cmd); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/fw.h b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h index 3db33bd1466..3db33bd1466 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/fw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.h diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/fw.c b/drivers/net/wireless/rtlwifi/rtl8192c/main.c index 8e350eea342..2f624fc2749 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/fw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/main.c @@ -27,4 +27,13 @@ * *****************************************************************************/ -#include "../rtl8192ce/fw.c" +#include "../wifi.h" + + +MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Georgia <georgia@realtek.com>"); +MODULE_AUTHOR("Ziv Huang <ziv_huang@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek 8192C/8188C 802.11n PCI wireless"); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 3728abc4df5..a7022827839 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -27,44 +27,15 @@ * *****************************************************************************/ +#include "../wifi.h" +#include "../rtl8192ce/reg.h" +#include "../rtl8192ce/def.h" +#include "dm_common.h" +#include "phy_common.h" + /* Define macro to shorten lines */ #define MCS_TXPWR mcs_txpwrlevel_origoffset -static u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); -static void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); -static u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset); -static void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, - enum radio_path rfpath, u32 offset, - u32 data); -static u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); -static bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); -static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); -static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, - u8 configtype); -static bool _rtl92c_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, - u8 configtype); -static void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); -static bool _rtl92c_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, - u32 cmdtableidx, u32 cmdtablesz, - enum swchnlcmd_id cmdid, u32 para1, - u32 para2, u32 msdelay); -static bool _rtl92c_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, - u8 channel, u8 *stage, u8 *step, - u32 *delay); -static u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, - long power_indbm); -static bool _rtl92c_phy_config_rf_external_pa(struct ieee80211_hw *hw, - enum radio_path rfpath); -static long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, - u8 txpwridx); -static void _rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); - u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -84,6 +55,7 @@ u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask) return returnvalue; } +EXPORT_SYMBOL(rtl92c_phy_query_bb_reg); void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) @@ -106,24 +78,26 @@ void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, ("regaddr(%#x), bitmask(%#x)," " data(%#x)\n", regaddr, bitmask, data)); - } +EXPORT_SYMBOL(rtl92c_phy_set_bb_reg); -static u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, +u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { RT_ASSERT(false, ("deprecated!\n")); return 0; } +EXPORT_SYMBOL(_rtl92c_phy_fw_rf_serial_read); -static void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, +void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { RT_ASSERT(false, ("deprecated!\n")); } +EXPORT_SYMBOL(_rtl92c_phy_fw_rf_serial_write); -static u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, +u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -172,8 +146,9 @@ static u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, retvalue)); return retvalue; } +EXPORT_SYMBOL(_rtl92c_phy_rf_serial_read); -static void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, +void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, enum radio_path rfpath, u32 offset, u32 data) { @@ -195,8 +170,9 @@ static void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, rfpath, pphyreg->rf3wire_offset, data_and_addr)); } +EXPORT_SYMBOL(_rtl92c_phy_rf_serial_write); -static u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask) +u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask) { u32 i; @@ -206,6 +182,7 @@ static u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask) } return i; } +EXPORT_SYMBOL(_rtl92c_phy_calculate_bit_shift); static void _rtl92c_phy_bb_config_1t(struct ieee80211_hw *hw) { @@ -222,10 +199,13 @@ static void _rtl92c_phy_bb_config_1t(struct ieee80211_hw *hw) } bool rtl92c_phy_rf_config(struct ieee80211_hw *hw) { - return rtl92c_phy_rf6052_config(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + return rtlpriv->cfg->ops->phy_rf6052_config(hw); } +EXPORT_SYMBOL(rtl92c_phy_rf_config); -static bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) +bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -233,7 +213,7 @@ static bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) bool rtstatus; RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, ("==>\n")); - rtstatus = _rtl92c_phy_config_bb_with_headerfile(hw, + rtstatus = rtlpriv->cfg->ops->config_bb_with_headerfile(hw, BASEBAND_CONFIG_PHY_REG); if (rtstatus != true) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("Write BB Reg Fail!!")); @@ -245,14 +225,14 @@ static bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) } if (rtlefuse->autoload_failflag == false) { rtlphy->pwrgroup_cnt = 0; - rtstatus = _rtl92c_phy_config_bb_with_pgheaderfile(hw, + rtstatus = rtlpriv->cfg->ops->config_bb_with_pgheaderfile(hw, BASEBAND_CONFIG_PHY_REG); } if (rtstatus != true) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("BB_PG Reg Fail!!")); return false; } - rtstatus = _rtl92c_phy_config_bb_with_headerfile(hw, + rtstatus = rtlpriv->cfg->ops->config_bb_with_headerfile(hw, BASEBAND_CONFIG_AGC_TAB); if (rtstatus != true) { RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, ("AGC Table Fail\n")); @@ -263,13 +243,9 @@ static bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw) 0x200)); return true; } +EXPORT_SYMBOL(_rtl92c_phy_bb8192c_config_parafile); - -void rtl92c_phy_config_bb_external_pa(struct ieee80211_hw *hw) -{ -} - -static void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, +void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, u32 regaddr, u32 bitmask, u32 data) { @@ -404,12 +380,7 @@ static void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, rtlphy->pwrgroup_cnt++; } } - -static bool _rtl92c_phy_config_rf_external_pa(struct ieee80211_hw *hw, - enum radio_path rfpath) -{ - return true; -} +EXPORT_SYMBOL(_rtl92c_store_pwrIndex_diffrate_offset); void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) { @@ -443,7 +414,7 @@ void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) ROFDM0_RXDETECTOR3, rtlphy->framesync)); } -static void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw) +void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -547,6 +518,7 @@ static void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw) TRANSCEIVEB_HSPI_READBACK; } +EXPORT_SYMBOL(_rtl92c_phy_init_bb_rf_register_definition); void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) { @@ -615,7 +587,8 @@ static void _rtl92c_ccxpower_index_check(struct ieee80211_hw *hw, void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) { - struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtlpriv); u8 cckpowerlevel[2], ofdmpowerlevel[2]; if (rtlefuse->txpwr_fromeprom == false) @@ -625,9 +598,11 @@ void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) _rtl92c_ccxpower_index_check(hw, channel, &cckpowerlevel[0], &ofdmpowerlevel[0]); - rtl92c_phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]); - rtl92c_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); + rtlpriv->cfg->ops->phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]); + rtlpriv->cfg->ops->phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], + channel); } +EXPORT_SYMBOL(rtl92c_phy_set_txpower_level); bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm) { @@ -662,14 +637,16 @@ bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm) rtl92c_phy_set_txpower_level(hw, rtlphy->current_channel); return true; } +EXPORT_SYMBOL(rtl92c_phy_update_txpower_dbm); void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, u16 beaconinterval) { } +EXPORT_SYMBOL(rtl92c_phy_set_beacon_hw_reg); -static u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, - long power_indbm) +u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + long power_indbm) { u8 txpwridx; long offset; @@ -697,10 +674,11 @@ static u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, return txpwridx; } +EXPORT_SYMBOL(_rtl92c_phy_dbm_to_txpwr_Idx); -static long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, - enum wireless_mode wirelessmode, - u8 txpwridx) +long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + u8 txpwridx) { long offset; long pwrout_dbm; @@ -720,6 +698,7 @@ static long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, pwrout_dbm = txpwridx / 2 + offset; return pwrout_dbm; } +EXPORT_SYMBOL(_rtl92c_phy_txpwr_idx_to_dbm); void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) { @@ -749,6 +728,7 @@ void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) } } } +EXPORT_SYMBOL(rtl92c_phy_scan_operation_backup); void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, enum nl80211_channel_type ch_type) @@ -762,7 +742,7 @@ void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, return; rtlphy->set_bwmode_inprogress = true; if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) - rtl92c_phy_set_bw_mode_callback(hw); + rtlpriv->cfg->ops->phy_set_bw_mode_callback(hw); else { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, ("FALSE driver sleep or unload\n")); @@ -770,6 +750,7 @@ void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, rtlphy->current_chan_bw = tmp_bw; } } +EXPORT_SYMBOL(rtl92c_phy_set_bw_mode); void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw) { @@ -798,6 +779,7 @@ void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw) } while (true); RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("<==\n")); } +EXPORT_SYMBOL(rtl92c_phy_sw_chnl_callback); u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) { @@ -827,6 +809,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw) } return 1; } +EXPORT_SYMBOL(rtl92c_phy_sw_chnl); static bool _rtl92c_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, u8 *stage, u8 *step, @@ -961,6 +944,7 @@ bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath) { return true; } +EXPORT_SYMBOL(rtl8192_phy_check_is_legal_rfpath); static u8 _rtl92c_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb) { @@ -1901,19 +1885,22 @@ void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) _rtl92c_phy_save_adda_registers(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); } +EXPORT_SYMBOL(rtl92c_phy_iq_calibrate); void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw) { + struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool start_conttx = false, singletone = false; if (start_conttx || singletone) return; if (IS_92C_SERIAL(rtlhal->version)) - _rtl92c_phy_lc_calibrate(hw, true); + rtlpriv->cfg->ops->phy_lc_calibrate(hw, true); else - _rtl92c_phy_lc_calibrate(hw, false); + rtlpriv->cfg->ops->phy_lc_calibrate(hw, false); } +EXPORT_SYMBOL(rtl92c_phy_lc_calibrate); void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta) { @@ -1928,6 +1915,7 @@ void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta) else _rtl92c_phy_ap_calibrate(hw, delta, false); } +EXPORT_SYMBOL(rtl92c_phy_ap_calibrate); void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) { @@ -1938,6 +1926,7 @@ void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) else _rtl92c_phy_set_rfpath_switch(hw, bmain, false); } +EXPORT_SYMBOL(rtl92c_phy_set_rfpath_switch); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) { @@ -1976,6 +1965,7 @@ bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, ("<--IO Type(%#x)\n", iotype)); return true; } +EXPORT_SYMBOL(rtl92c_phy_set_io_cmd); void rtl92c_phy_set_io(struct ieee80211_hw *hw) { @@ -2005,6 +1995,7 @@ void rtl92c_phy_set_io(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, ("<---(%#x)\n", rtlphy->current_io_type)); } +EXPORT_SYMBOL(rtl92c_phy_set_io); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw) { @@ -2017,8 +2008,9 @@ void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); } +EXPORT_SYMBOL(rtl92ce_phy_set_rf_on); -static void _rtl92ce_phy_set_rf_sleep(struct ieee80211_hw *hw) +void _rtl92c_phy_set_rf_sleep(struct ieee80211_hw *hw) { u32 u4b_tmp; u8 delay = 5; @@ -2047,3 +2039,4 @@ static void _rtl92ce_phy_set_rf_sleep(struct ieee80211_hw *hw) rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); } +EXPORT_SYMBOL(_rtl92c_phy_set_rf_sleep); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h new file mode 100644 index 00000000000..53ffb098158 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.h @@ -0,0 +1,246 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2010 Realtek Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA + * + * The full GNU General Public License is included in this distribution in the + * file called LICENSE. + * + * Contact Information: + * wlanfae <wlanfae@realtek.com> + * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park, + * Hsinchu 300, Taiwan. + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL92C_PHY_H__ +#define __RTL92C_PHY_H__ + +#define MAX_PRECMD_CNT 16 +#define MAX_RFDEPENDCMD_CNT 16 +#define MAX_POSTCMD_CNT 16 + +#define MAX_DOZE_WAITING_TIMES_9x 64 + +#define RT_CANNOT_IO(hw) false +#define HIGHPOWER_RADIOA_ARRAYLEN 22 + +#define MAX_TOLERANCE 5 +#define IQK_DELAY_TIME 1 + +#define APK_BB_REG_NUM 5 +#define APK_AFE_REG_NUM 16 +#define APK_CURVE_REG_NUM 4 +#define PATH_NUM 2 + +#define LOOP_LIMIT 5 +#define MAX_STALL_TIME 50 +#define AntennaDiversityValue 0x80 +#define MAX_TXPWR_IDX_NMODE_92S 63 +#define Reset_Cnt_Limit 3 + +#define IQK_ADDA_REG_NUM 16 +#define IQK_MAC_REG_NUM 4 + +#define RF90_PATH_MAX 2 + +#define CT_OFFSET_MAC_ADDR 0X16 + +#define CT_OFFSET_CCK_TX_PWR_IDX 0x5A +#define CT_OFFSET_HT401S_TX_PWR_IDX 0x60 +#define CT_OFFSET_HT402S_TX_PWR_IDX_DIF 0x66 +#define CT_OFFSET_HT20_TX_PWR_IDX_DIFF 0x69 +#define CT_OFFSET_OFDM_TX_PWR_IDX_DIFF 0x6C + +#define CT_OFFSET_HT40_MAX_PWR_OFFSET 0x6F +#define CT_OFFSET_HT20_MAX_PWR_OFFSET 0x72 + +#define CT_OFFSET_CHANNEL_PLAH 0x75 +#define CT_OFFSET_THERMAL_METER 0x78 +#define CT_OFFSET_RF_OPTION 0x79 +#define CT_OFFSET_VERSION 0x7E +#define CT_OFFSET_CUSTOMER_ID 0x7F + +#define RTL92C_MAX_PATH_NUM 2 +#define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER 255 +enum swchnlcmd_id { + CMDID_END, + CMDID_SET_TXPOWEROWER_LEVEL, + CMDID_BBREGWRITE10, + CMDID_WRITEPORT_ULONG, + CMDID_WRITEPORT_USHORT, + CMDID_WRITEPORT_UCHAR, + CMDID_RF_WRITEREG, +}; + +struct swchnlcmd { + enum swchnlcmd_id cmdid; + u32 para1; + u32 para2; + u32 msdelay; +}; + +enum hw90_block_e { + HW90_BLOCK_MAC = 0, + HW90_BLOCK_PHY0 = 1, + HW90_BLOCK_PHY1 = 2, + HW90_BLOCK_RF = 3, + HW90_BLOCK_MAXIMUM = 4, +}; + +enum baseband_config_type { + BASEBAND_CONFIG_PHY_REG = 0, + BASEBAND_CONFIG_AGC_TAB = 1, +}; + +enum ra_offset_area { + RA_OFFSET_LEGACY_OFDM1, + RA_OFFSET_LEGACY_OFDM2, + RA_OFFSET_HT_OFDM1, + RA_OFFSET_HT_OFDM2, + RA_OFFSET_HT_OFDM3, + RA_OFFSET_HT_OFDM4, + RA_OFFSET_HT_CCK, +}; + +enum antenna_path { + ANTENNA_NONE, + ANTENNA_D, + ANTENNA_C, + ANTENNA_CD, + ANTENNA_B, + ANTENNA_BD, + ANTENNA_BC, + ANTENNA_BCD, + ANTENNA_A, + ANTENNA_AD, + ANTENNA_AC, + ANTENNA_ACD, + ANTENNA_AB, + ANTENNA_ABD, + ANTENNA_ABC, + ANTENNA_ABCD +}; + +struct r_antenna_select_ofdm { + u32 r_tx_antenna:4; + u32 r_ant_l:4; + u32 r_ant_non_ht:4; + u32 r_ant_ht1:4; + u32 r_ant_ht2:4; + u32 r_ant_ht_s1:4; + u32 r_ant_non_ht_s1:4; + u32 ofdm_txsc:2; + u32 reserved:2; +}; + +struct r_antenna_select_cck { + u8 r_cckrx_enable_2:2; + u8 r_cckrx_enable:2; + u8 r_ccktx_enable:4; +}; + +struct efuse_contents { + u8 mac_addr[ETH_ALEN]; + u8 cck_tx_power_idx[6]; + u8 ht40_1s_tx_power_idx[6]; + u8 ht40_2s_tx_power_idx_diff[3]; + u8 ht20_tx_power_idx_diff[3]; + u8 ofdm_tx_power_idx_diff[3]; + u8 ht40_max_power_offset[3]; + u8 ht20_max_power_offset[3]; + u8 channel_plan; + u8 thermal_meter; + u8 rf_option[5]; + u8 version; + u8 oem_id; + u8 regulatory; +}; + +struct tx_power_struct { + u8 cck[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht40_1s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht40_2s[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 ht20_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 legacy_ht_diff[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 legacy_ht_txpowerdiff; + u8 groupht20[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 groupht40[RTL92C_MAX_PATH_NUM][CHANNEL_MAX_NUMBER]; + u8 pwrgroup_cnt; + u32 mcs_original_offset[4][16]; +}; + +extern u32 rtl92c_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +extern void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +extern u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +extern void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +extern bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); +extern bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); +extern bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); +extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +extern void rtl92c_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +extern void rtl92c_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +extern void rtl92c_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel); +extern bool rtl92c_phy_update_txpower_dbm(struct ieee80211_hw *hw, + long power_indbm); +extern void rtl92c_phy_scan_operation_backup(struct ieee80211_hw *hw, + u8 operation); +extern void rtl92c_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +extern void rtl92c_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +extern void rtl92c_phy_sw_chnl_callback(struct ieee80211_hw *hw); +extern u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw); +extern void rtl92c_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); +extern void rtl92c_phy_set_beacon_hw_reg(struct ieee80211_hw *hw, + u16 beaconinterval); +void rtl92c_phy_ap_calibrate(struct ieee80211_hw *hw, char delta); +void rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw); +void rtl92c_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); +bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +extern bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, + u32 rfpath); +extern bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); +void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); +void rtl92c_phy_set_io(struct ieee80211_hw *hw); +void rtl92c_bb_block_on(struct ieee80211_hw *hw); +u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); +long _rtl92c_phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + u8 txpwridx); +u8 _rtl92c_phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + long power_indbm); +void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); +static bool _rtl92c_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, + u32 cmdtableidx, u32 cmdtablesz, + enum swchnlcmd_id cmdid, u32 para1, + u32 para2, u32 msdelay); +static bool _rtl92c_phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, + u8 channel, u8 *stage, u8 *step, + u32 *delay); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/Makefile b/drivers/net/wireless/rtlwifi/rtl8192ce/Makefile index 5c5fdde637c..c0cb0cfe7d3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/Makefile +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/Makefile @@ -1,6 +1,5 @@ rtl8192ce-objs := \ dm.o \ - fw.o \ hw.o \ led.o \ phy.o \ diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c index 888df5e2d2f..7d76504df4d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.c @@ -33,11 +33,8 @@ #include "def.h" #include "phy.h" #include "dm.h" -#include "fw.h" -#include "../rtl8192c/dm_common.c" - -void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) +void rtl92ce_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -114,5 +111,3 @@ void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; } - - diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h index 5911d52a24a..36302ebae4a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/dm.h @@ -192,6 +192,6 @@ void rtl92c_dm_init_edca_turbo(struct ieee80211_hw *hw); void rtl92c_dm_check_txpower_tracking(struct ieee80211_hw *hw); void rtl92c_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); void rtl92c_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); -void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw); +void rtl92ce_dm_dynamic_txpower(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c index 0b910921e60..05477f465a7 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c @@ -37,7 +37,6 @@ #include "def.h" #include "phy.h" #include "dm.h" -#include "fw.h" #include "led.h" #include "hw.h" @@ -949,8 +948,8 @@ int rtl92ce_hw_init(struct ieee80211_hw *hw) } rtlhal->last_hmeboxnum = 0; - rtl92c_phy_mac_config(hw); - rtl92c_phy_bb_config(hw); + rtl92ce_phy_mac_config(hw); + rtl92ce_phy_bb_config(hw); rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; rtl92c_phy_rf_config(hw); rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h index 305c819c8c7..a3dfdb63516 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.h @@ -30,6 +30,8 @@ #ifndef __RTL92CE_HW_H__ #define __RTL92CE_HW_H__ +#define H2C_RA_MASK 6 + void rtl92ce_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); void rtl92ce_read_eeprom_info(struct ieee80211_hw *hw); void rtl92ce_interrupt_recognized(struct ieee80211_hw *hw, @@ -53,5 +55,14 @@ void rtl92ce_enable_hw_security_config(struct ieee80211_hw *hw); void rtl92ce_set_key(struct ieee80211_hw *hw, u32 key_index, u8 *p_macaddr, bool is_group, u8 enc_algo, bool is_wepkey, bool clear_all); +bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); +void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); +void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); +void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); +int rtl92c_download_fw(struct ieee80211_hw *hw); +void rtl92c_firmware_selfreset(struct ieee80211_hw *hw); +void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, + u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); +bool rtl92ce_phy_mac_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c index 191106033b3..d0541e8c601 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.c @@ -32,14 +32,13 @@ #include "../ps.h" #include "reg.h" #include "def.h" +#include "hw.h" #include "phy.h" #include "rf.h" #include "dm.h" #include "table.h" -#include "../rtl8192c/phy_common.c" - -u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, +u32 rtl92ce_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -74,7 +73,7 @@ u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, return readback_value; } -void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, +void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { @@ -122,19 +121,19 @@ void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, bitmask, data, rfpath)); } -bool rtl92c_phy_mac_config(struct ieee80211_hw *hw) +bool rtl92ce_phy_mac_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool is92c = IS_92C_SERIAL(rtlhal->version); - bool rtstatus = _rtl92c_phy_config_mac_with_headerfile(hw); + bool rtstatus = _rtl92ce_phy_config_mac_with_headerfile(hw); if (is92c) rtl_write_byte(rtlpriv, 0x14, 0x71); return rtstatus; } -bool rtl92c_phy_bb_config(struct ieee80211_hw *hw) +bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw) { bool rtstatus = true; struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -160,7 +159,7 @@ bool rtl92c_phy_bb_config(struct ieee80211_hw *hw) return rtstatus; } -static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) +bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); u32 i; @@ -177,7 +176,7 @@ static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) return true; } -static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, +bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { int i; @@ -221,7 +220,6 @@ static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, phy_regarray_table[i], phy_regarray_table[i + 1])); } - rtl92c_phy_config_bb_external_pa(hw); } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], MASKDWORD, @@ -237,7 +235,7 @@ static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, return true; } -static bool _rtl92c_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, +bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -276,7 +274,7 @@ static bool _rtl92c_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, return true; } -bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, +bool rtl92ce_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { @@ -331,7 +329,6 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, udelay(1); } } - _rtl92c_phy_config_rf_external_pa(hw, rfpath); break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { @@ -367,7 +364,7 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, return true; } -void rtl92c_phy_set_bw_mode_callback(struct ieee80211_hw *hw) +void rtl92ce_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -435,7 +432,7 @@ void rtl92c_phy_set_bw_mode_callback(struct ieee80211_hw *hw) RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("<==\n")); } -static void _rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) +void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) { u8 tmpreg; u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; @@ -602,7 +599,7 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies))); ppsc->last_sleep_jiffies = jiffies; - _rtl92ce_phy_set_rf_sleep(hw); + _rtl92c_phy_set_rf_sleep(hw); break; } default: @@ -617,7 +614,7 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, return bresult; } -bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, +bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h index 3fc60e434ce..a37267e3fc2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/phy.h @@ -191,11 +191,11 @@ extern void rtl92c_phy_set_bb_reg(struct ieee80211_hw *hw, extern u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask); -extern void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, +extern void rtl92ce_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); extern bool rtl92c_phy_mac_config(struct ieee80211_hw *hw); -extern bool rtl92c_phy_bb_config(struct ieee80211_hw *hw); +bool rtl92ce_phy_bb_config(struct ieee80211_hw *hw); extern bool rtl92c_phy_rf_config(struct ieee80211_hw *hw); extern bool rtl92c_phy_config_rf_with_feaderfile(struct ieee80211_hw *hw, enum radio_path rfpath); @@ -223,12 +223,32 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, extern bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); -extern bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, +bool rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state); -void rtl92c_phy_config_bb_external_pa(struct ieee80211_hw *hw); void rtl92ce_phy_set_rf_on(struct ieee80211_hw *hw); bool rtl92c_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); void rtl92c_phy_set_io(struct ieee80211_hw *hw); void rtl92c_bb_block_on(struct ieee80211_hw *hw); +u32 _rtl92c_phy_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset); +u32 _rtl92c_phy_fw_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset); +u32 _rtl92c_phy_calculate_bit_shift(u32 bitmask); +void _rtl92c_phy_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset, + u32 data); +void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, + u32 data); +void _rtl92c_phy_fw_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset, + u32 data); +void _rtl92c_store_pwrIndex_diffrate_offset(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, + u32 data); +bool _rtl92ce_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); +void _rtl92c_phy_init_bb_rf_register_definition(struct ieee80211_hw *hw); +bool _rtl92c_phy_bb8192c_config_parafile(struct ieee80211_hw *hw); +void _rtl92c_phy_set_rf_sleep(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c index ffd8e04c402..669b1168dbe 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.c @@ -61,7 +61,7 @@ void rtl92c_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) } } -void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, +void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -410,7 +410,7 @@ static void _rtl92c_write_ofdm_power_reg(struct ieee80211_hw *hw, } } -void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, +void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel) { u32 writeVal[2], powerBase0[2], powerBase1[2]; @@ -430,7 +430,7 @@ void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, } } -bool rtl92c_phy_rf6052_config(struct ieee80211_hw *hw) +bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -484,11 +484,11 @@ static bool _rtl92c_phy_rf6052_config_parafile(struct ieee80211_hw *hw) switch (rfpath) { case RF90_PATH_A: - rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, + rtstatus = rtl92ce_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_B: - rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, + rtstatus = rtl92ce_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_C: diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h index d3014f99bb7..3aa520c1c17 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/rf.h @@ -40,5 +40,8 @@ extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel); extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel); -extern bool rtl92c_phy_rf6052_config(struct ieee80211_hw *hw); +bool rtl92ce_phy_rf6052_config(struct ieee80211_hw *hw); +bool rtl92ce_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath); + #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c index b4df0b33283..b1cc4d44f53 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c @@ -37,6 +37,7 @@ #include "phy.h" #include "dm.h" #include "hw.h" +#include "rf.h" #include "sw.h" #include "trx.h" #include "led.h" @@ -122,7 +123,7 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, .scan_operation_backup = rtl92c_phy_scan_operation_backup, - .set_rf_power_state = rtl92c_phy_set_rf_power_state, + .set_rf_power_state = rtl92ce_phy_set_rf_power_state, .led_control = rtl92ce_led_control, .set_desc = rtl92ce_set_desc, .get_desc = rtl92ce_get_desc, @@ -133,9 +134,17 @@ static struct rtl_hal_ops rtl8192ce_hal_ops = { .deinit_sw_leds = rtl92ce_deinit_sw_leds, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, - .get_rfreg = rtl92c_phy_query_rf_reg, - .set_rfreg = rtl92c_phy_set_rf_reg, + .get_rfreg = rtl92ce_phy_query_rf_reg, + .set_rfreg = rtl92ce_phy_set_rf_reg, .cmd_send_packet = _rtl92c_cmd_send_packet, + .phy_rf6052_config = rtl92ce_phy_rf6052_config, + .phy_rf6052_set_cck_txpower = rtl92ce_phy_rf6052_set_cck_txpower, + .phy_rf6052_set_ofdm_txpower = rtl92ce_phy_rf6052_set_ofdm_txpower, + .config_bb_with_headerfile = _rtl92ce_phy_config_bb_with_headerfile, + .config_bb_with_pgheaderfile = _rtl92ce_phy_config_bb_with_pgheaderfile, + .phy_lc_calibrate = _rtl92ce_phy_lc_calibrate, + .phy_set_bw_mode_callback = rtl92ce_phy_set_bw_mode_callback, + .dm_dynamic_txpower = rtl92ce_dm_dynamic_txpower, }; static struct rtl_mod_params rtl92ce_mod_params = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.h b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.h index 0568d6dc83d..36e657668c1 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.h @@ -35,5 +35,17 @@ void rtl92c_deinit_sw_vars(struct ieee80211_hw *hw); void rtl92c_init_var_map(struct ieee80211_hw *hw); bool _rtl92c_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); +void rtl92ce_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +void rtl92ce_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool _rtl92ce_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, + u8 configtype); +bool _rtl92ce_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, + u8 configtype); +void _rtl92ce_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); +u32 rtl92ce_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, u32 bitmask); +void rtl92ce_phy_set_bw_mode_callback(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 01b95427fee..aa2b5815600 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -252,9 +252,9 @@ static void _rtl92ce_query_rxphystatus(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); struct phy_sts_cck_8192s_t *cck_buf; s8 rx_pwr_all, rx_pwr[4]; - u8 rf_rx_num, evm, pwdb_all; + u8 evm, pwdb_all, rf_rx_num = 0; u8 i, max_spatial_stream; - u32 rssi, total_rssi; + u32 rssi, total_rssi = 0; bool is_cck_rate; is_cck_rate = RX_HAL_IS_CCK_RATE(pdesc); @@ -463,7 +463,7 @@ static void _rtl92ce_update_rxsignalstatistics(struct ieee80211_hw *hw, struct rtl_stats *pstats) { struct rtl_priv *rtlpriv = rtl_priv(hw); - int weighting; + int weighting = 0; if (rtlpriv->stats.recv_signal_power == 0) rtlpriv->stats.recv_signal_power = pstats->recvsignalpower; @@ -691,7 +691,7 @@ bool rtl92ce_rx_query_desc(struct ieee80211_hw *hw, if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; @@ -730,7 +730,7 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; - struct ieee80211_sta *sta = ieee80211_find_sta(mac->vif, mac->bssid); + struct ieee80211_sta *sta; u8 *pdesc = (u8 *) pdesc_tx; struct rtl_tcb_desc tcb_desc; u8 *qc = ieee80211_get_qos_ctl(hdr); @@ -810,10 +810,13 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_LINIP(pdesc, 0); SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb->len); + rcu_read_lock(); + sta = ieee80211_find_sta(mac->vif, mac->bssid); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); } + rcu_read_unlock(); if (info->control.hw_key) { struct ieee80211_key_conf *keyconf = diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/Makefile b/drivers/net/wireless/rtlwifi/rtl8192cu/Makefile index 91c65122ca8..ad2de6b839e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/Makefile +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/Makefile @@ -1,6 +1,5 @@ rtl8192cu-objs := \ dm.o \ - fw.o \ hw.o \ led.o \ mac.o \ diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c index a4649a2f7e6..f311baee668 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.c @@ -33,11 +33,8 @@ #include "def.h" #include "phy.h" #include "dm.h" -#include "fw.h" -#include "../rtl8192c/dm_common.c" - -void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw) +void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h index 5e7fbfc2851..7f966c666b5 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/dm.h @@ -29,4 +29,4 @@ #include "../rtl8192ce/dm.h" -void rtl92c_dm_dynamic_txpower(struct ieee80211_hw *hw); +void rtl92cu_dm_dynamic_txpower(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c index df8fe3b51c9..9444e76838c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.c @@ -38,7 +38,6 @@ #include "phy.h" #include "mac.h" #include "dm.h" -#include "fw.h" #include "hw.h" #include "trx.h" #include "led.h" @@ -1190,8 +1189,8 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) } rtlhal->last_hmeboxnum = 0; /* h2c */ _rtl92cu_phy_param_tab_init(hw); - rtl92c_phy_mac_config(hw); - rtl92c_phy_bb_config(hw); + rtl92cu_phy_mac_config(hw); + rtl92cu_phy_bb_config(hw); rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; rtl92c_phy_rf_config(hw); if (IS_VENDOR_UMC_A_CUT(rtlhal->version) && @@ -1203,7 +1202,7 @@ int rtl92cu_hw_init(struct ieee80211_hw *hw) RF_CHNLBW, RFREG_OFFSET_MASK); rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, RF_CHNLBW, RFREG_OFFSET_MASK); - rtl92c_bb_block_on(hw); + rtl92cu_bb_block_on(hw); rtl_cam_reset_all_entry(hw); rtl92cu_enable_hw_security_config(hw); ppsc->rfpwr_state = ERFON; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h index 3c0ea5ea6db..62af555bb61 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/hw.h @@ -30,6 +30,8 @@ #ifndef __RTL92CU_HW_H__ #define __RTL92CU_HW_H__ +#define H2C_RA_MASK 6 + #define LLT_POLLING_LLT_THRESHOLD 20 #define LLT_POLLING_READY_TIMEOUT_COUNT 100 #define LLT_LAST_ENTRY_OF_TX_PKT_BUFFER 255 @@ -103,5 +105,12 @@ void rtl92cu_update_channel_access_setting(struct ieee80211_hw *hw); bool rtl92cu_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 * valid); void rtl92cu_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); u8 _rtl92c_get_chnl_group(u8 chnl); +int rtl92c_download_fw(struct ieee80211_hw *hw); +void rtl92c_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); +void rtl92c_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished); +void rtl92c_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); +void rtl92c_fill_h2c_cmd(struct ieee80211_hw *hw, + u8 element_id, u32 cmd_len, u8 *p_cmdbuffer); +bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw); #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c index dc65ef2bbea..4e020e654e6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.c @@ -37,9 +37,7 @@ #include "dm.h" #include "table.h" -#include "../rtl8192c/phy_common.c" - -u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, +u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -65,7 +63,7 @@ u32 rtl92c_phy_query_rf_reg(struct ieee80211_hw *hw, return readback_value; } -void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, +void rtl92cu_phy_set_rf_reg(struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data) { @@ -104,20 +102,20 @@ void rtl92c_phy_set_rf_reg(struct ieee80211_hw *hw, regaddr, bitmask, data, rfpath)); } -bool rtl92c_phy_mac_config(struct ieee80211_hw *hw) +bool rtl92cu_phy_mac_config(struct ieee80211_hw *hw) { bool rtstatus; struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); bool is92c = IS_92C_SERIAL(rtlhal->version); - rtstatus = _rtl92c_phy_config_mac_with_headerfile(hw); + rtstatus = _rtl92cu_phy_config_mac_with_headerfile(hw); if (is92c && IS_HARDWARE_TYPE_8192CE(rtlhal)) rtl_write_byte(rtlpriv, 0x14, 0x71); return rtstatus; } -bool rtl92c_phy_bb_config(struct ieee80211_hw *hw) +bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw) { bool rtstatus = true; struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -146,7 +144,7 @@ bool rtl92c_phy_bb_config(struct ieee80211_hw *hw) return rtstatus; } -static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) +bool _rtl92cu_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -164,7 +162,7 @@ static bool _rtl92c_phy_config_mac_with_headerfile(struct ieee80211_hw *hw) return true; } -static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, +bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, u8 configtype) { int i; @@ -209,7 +207,6 @@ static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, phy_regarray_table[i], phy_regarray_table[i + 1])); } - rtl92c_phy_config_bb_external_pa(hw); } else if (configtype == BASEBAND_CONFIG_AGC_TAB) { for (i = 0; i < agctab_arraylen; i = i + 2) { rtl_set_bbreg(hw, agctab_array_table[i], MASKDWORD, @@ -225,7 +222,7 @@ static bool _rtl92c_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, return true; } -static bool _rtl92c_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, +bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, u8 configtype) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -263,7 +260,7 @@ static bool _rtl92c_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, return true; } -bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, +bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, enum radio_path rfpath) { int i; @@ -316,7 +313,6 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, udelay(1); } } - _rtl92c_phy_config_rf_external_pa(hw, rfpath); break; case RF90_PATH_B: for (i = 0; i < radiob_arraylen; i = i + 2) { @@ -352,7 +348,7 @@ bool rtl92c_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, return true; } -void rtl92c_phy_set_bw_mode_callback(struct ieee80211_hw *hw) +void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); @@ -410,12 +406,12 @@ void rtl92c_phy_set_bw_mode_callback(struct ieee80211_hw *hw) ("unknown bandwidth: %#X\n", rtlphy->current_chan_bw)); break; } - rtl92c_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); + rtl92cu_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); rtlphy->set_bwmode_inprogress = false; RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, ("<==\n")); } -void rtl92c_bb_block_on(struct ieee80211_hw *hw) +void rtl92cu_bb_block_on(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -425,7 +421,7 @@ void rtl92c_bb_block_on(struct ieee80211_hw *hw) mutex_unlock(&rtlpriv->io.bb_mutex); } -static void _rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) +void _rtl92cu_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) { u8 tmpreg; u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; @@ -463,7 +459,7 @@ static void _rtl92c_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) } } -static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, +bool _rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -584,7 +580,7 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies))); ppsc->last_sleep_jiffies = jiffies; - _rtl92ce_phy_set_rf_sleep(hw); + _rtl92c_phy_set_rf_sleep(hw); break; default: RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, @@ -598,7 +594,7 @@ static bool _rtl92ce_phy_set_rf_power_state(struct ieee80211_hw *hw, return bresult; } -bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, +bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, enum rf_pwrstate rfpwr_state) { struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); @@ -606,6 +602,6 @@ bool rtl92c_phy_set_rf_power_state(struct ieee80211_hw *hw, if (rfpwr_state == ppsc->rfpwr_state) return bresult; - bresult = _rtl92ce_phy_set_rf_power_state(hw, rfpwr_state); + bresult = _rtl92cu_phy_set_rf_power_state(hw, rfpwr_state); return bresult; } diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.h b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.h index c456c15afbf..06299559ab6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/phy.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/phy.h @@ -29,6 +29,8 @@ #include "../rtl8192ce/phy.h" -void rtl92c_bb_block_on(struct ieee80211_hw *hw); +void rtl92cu_bb_block_on(struct ieee80211_hw *hw); bool rtl8192_phy_check_is_legal_rfpath(struct ieee80211_hw *hw, u32 rfpath); void rtl92c_phy_set_io(struct ieee80211_hw *hw); +bool _rtl92cu_phy_config_mac_with_headerfile(struct ieee80211_hw *hw); +bool rtl92cu_phy_bb_config(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c index 9149adcc8fa..1c79c226f14 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.c @@ -36,7 +36,7 @@ static bool _rtl92c_phy_rf6052_config_parafile(struct ieee80211_hw *hw); -void rtl92c_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) +void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -61,7 +61,7 @@ void rtl92c_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) } } -void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, +void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel) { struct rtl_priv *rtlpriv = rtl_priv(hw); @@ -388,7 +388,7 @@ static void _rtl92c_write_ofdm_power_reg(struct ieee80211_hw *hw, } } -void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, +void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, u8 *ppowerlevel, u8 channel) { u32 writeVal[2], powerBase0[2], powerBase1[2]; @@ -406,7 +406,7 @@ void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, } } -bool rtl92c_phy_rf6052_config(struct ieee80211_hw *hw) +bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_phy *rtlphy = &(rtlpriv->phy); @@ -456,11 +456,11 @@ static bool _rtl92c_phy_rf6052_config_parafile(struct ieee80211_hw *hw) udelay(1); switch (rfpath) { case RF90_PATH_A: - rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, + rtstatus = rtl92cu_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_B: - rtstatus = rtl92c_phy_config_rf_with_headerfile(hw, + rtstatus = rtl92cu_phy_config_rf_with_headerfile(hw, (enum radio_path) rfpath); break; case RF90_PATH_C: diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h index c4ed125ef4d..86c2728cfa0 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/rf.h @@ -27,4 +27,21 @@ * *****************************************************************************/ -#include "../rtl8192ce/rf.h" +#ifndef __RTL92CU_RF_H__ +#define __RTL92CU_RF_H__ + +#define RF6052_MAX_TX_PWR 0x3F +#define RF6052_MAX_REG 0x3F +#define RF6052_MAX_PATH 2 + +extern void rtl92cu_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, + u8 bandwidth); +extern void rtl92c_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +extern void rtl92c_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool rtl92cu_phy_rf6052_config(struct ieee80211_hw *hw); +bool rtl92cu_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c index 4e937e0da8e..71244a38d49 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.c @@ -36,11 +36,12 @@ #include "phy.h" #include "mac.h" #include "dm.h" +#include "rf.h" #include "sw.h" #include "trx.h" #include "led.h" #include "hw.h" - +#include <linux/vmalloc.h> MODULE_AUTHOR("Georgia <georgia@realtek.com>"); MODULE_AUTHOR("Ziv Huang <ziv_huang@realtek.com>"); @@ -106,7 +107,7 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = { .switch_channel = rtl92c_phy_sw_chnl, .dm_watchdog = rtl92c_dm_watchdog, .scan_operation_backup = rtl92c_phy_scan_operation_backup, - .set_rf_power_state = rtl92c_phy_set_rf_power_state, + .set_rf_power_state = rtl92cu_phy_set_rf_power_state, .led_control = rtl92cu_led_control, .enable_hw_sec = rtl92cu_enable_hw_security_config, .set_key = rtl92c_set_key, @@ -114,8 +115,16 @@ static struct rtl_hal_ops rtl8192cu_hal_ops = { .deinit_sw_leds = rtl92cu_deinit_sw_leds, .get_bbreg = rtl92c_phy_query_bb_reg, .set_bbreg = rtl92c_phy_set_bb_reg, - .get_rfreg = rtl92c_phy_query_rf_reg, - .set_rfreg = rtl92c_phy_set_rf_reg, + .get_rfreg = rtl92cu_phy_query_rf_reg, + .set_rfreg = rtl92cu_phy_set_rf_reg, + .phy_rf6052_config = rtl92cu_phy_rf6052_config, + .phy_rf6052_set_cck_txpower = rtl92cu_phy_rf6052_set_cck_txpower, + .phy_rf6052_set_ofdm_txpower = rtl92cu_phy_rf6052_set_ofdm_txpower, + .config_bb_with_headerfile = _rtl92cu_phy_config_bb_with_headerfile, + .config_bb_with_pgheaderfile = _rtl92cu_phy_config_bb_with_pgheaderfile, + .phy_lc_calibrate = _rtl92cu_phy_lc_calibrate, + .phy_set_bw_mode_callback = rtl92cu_phy_set_bw_mode_callback, + .dm_dynamic_txpower = rtl92cu_dm_dynamic_txpower, }; static struct rtl_mod_params rtl92cu_mod_params = { diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h index 3b2c6633955..43b1177924a 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/sw.h @@ -32,4 +32,22 @@ #define EFUSE_MAX_SECTION 16 +void rtl92cu_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *powerlevel); +void rtl92cu_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +bool _rtl92cu_phy_config_bb_with_headerfile(struct ieee80211_hw *hw, + u8 configtype); +bool _rtl92cu_phy_config_bb_with_pgheaderfile(struct ieee80211_hw *hw, + u8 configtype); +void _rtl92cu_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); +void rtl92cu_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 regaddr, u32 bitmask, u32 data); +bool rtl92cu_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); +u32 rtl92cu_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, u32 bitmask); +void rtl92cu_phy_set_bw_mode_callback(struct ieee80211_hw *hw); + #endif diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 9855c3e0a4b..d0b0d43b9a6 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c @@ -334,7 +334,7 @@ bool rtl92cu_rx_query_desc(struct ieee80211_hw *hw, rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RX_HT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = _rtl92c_rate_mapping(hw, @@ -504,7 +504,7 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); bool defaultadapter = true; - struct ieee80211_sta *sta = ieee80211_find_sta(mac->vif, mac->bssid); + struct ieee80211_sta *sta; struct rtl_tcb_desc tcb_desc; u8 *qc = ieee80211_get_qos_ctl(hdr); u8 tid = qc[0] & IEEE80211_QOS_CTL_TID_MASK; @@ -562,10 +562,13 @@ void rtl92cu_tx_fill_desc(struct ieee80211_hw *hw, SET_TX_DESC_DATA_BW(txdesc, 0); SET_TX_DESC_DATA_SC(txdesc, 0); } + rcu_read_lock(); + sta = ieee80211_find_sta(mac->vif, mac->bssid); if (sta) { u8 ampdu_density = sta->ht_cap.ampdu_density; SET_TX_DESC_AMPDU_DENSITY(txdesc, ampdu_density); } + rcu_read_unlock(); if (info->control.hw_key) { struct ieee80211_key_conf *keyconf = info->control.hw_key; switch (keyconf->cipher) { diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 7d47184d6bf..01226f8e70f 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -1384,6 +1384,18 @@ struct rtl_hal_ops { u32 regaddr, u32 bitmask); void (*set_rfreg) (struct ieee80211_hw *hw, enum radio_path rfpath, u32 regaddr, u32 bitmask, u32 data); + bool (*phy_rf6052_config) (struct ieee80211_hw *hw); + void (*phy_rf6052_set_cck_txpower) (struct ieee80211_hw *hw, + u8 *powerlevel); + void (*phy_rf6052_set_ofdm_txpower) (struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); + bool (*config_bb_with_headerfile) (struct ieee80211_hw *hw, + u8 configtype); + bool (*config_bb_with_pgheaderfile) (struct ieee80211_hw *hw, + u8 configtype); + void (*phy_lc_calibrate) (struct ieee80211_hw *hw, bool is2t); + void (*phy_set_bw_mode_callback) (struct ieee80211_hw *hw); + void (*dm_dynamic_txpower) (struct ieee80211_hw *hw); }; struct rtl_intf_ops { diff --git a/drivers/net/wireless/wl1251/main.c b/drivers/net/wireless/wl1251/main.c index 5a1c13878ea..12c9e635a6d 100644 --- a/drivers/net/wireless/wl1251/main.c +++ b/drivers/net/wireless/wl1251/main.c @@ -375,7 +375,7 @@ out: mutex_unlock(&wl->mutex); } -static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1251 *wl = hw->priv; unsigned long flags; @@ -401,8 +401,6 @@ static int wl1251_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) wl->tx_queue_stopped = true; spin_unlock_irqrestore(&wl->wl_lock, flags); } - - return NETDEV_TX_OK; } static int wl1251_op_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/wl1251/rx.c b/drivers/net/wireless/wl1251/rx.c index b659e15c78d..c1b3b3f03da 100644 --- a/drivers/net/wireless/wl1251/rx.c +++ b/drivers/net/wireless/wl1251/rx.c @@ -81,7 +81,7 @@ static void wl1251_rx_status(struct wl1251 *wl, status->freq = ieee80211_channel_to_frequency(desc->channel, status->band); - status->flag |= RX_FLAG_TSFT; + status->flag |= RX_FLAG_MACTIME_MPDU; if (desc->flags & RX_DESC_ENCRYPTION_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; diff --git a/drivers/net/wireless/wl12xx/acx.c b/drivers/net/wireless/wl12xx/acx.c index 33840d95d17..3badc6bb786 100644 --- a/drivers/net/wireless/wl12xx/acx.c +++ b/drivers/net/wireless/wl12xx/acx.c @@ -1328,10 +1328,9 @@ int wl1271_acx_set_ht_capabilities(struct wl1271 *wl, /* get data from A-MPDU parameters field */ acx->ampdu_max_length = ht_cap->ampdu_factor; acx->ampdu_min_spacing = ht_cap->ampdu_density; - - memcpy(acx->mac_address, mac_address, ETH_ALEN); } + memcpy(acx->mac_address, mac_address, ETH_ALEN); acx->ht_capabilites = cpu_to_le32(ht_capabilites); ret = wl1271_cmd_configure(wl, ACX_PEER_HT_CAP, acx, sizeof(*acx)); @@ -1542,3 +1541,28 @@ out: kfree(config_ps); return ret; } + +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr) +{ + struct wl1271_acx_inconnection_sta *acx = NULL; + int ret; + + wl1271_debug(DEBUG_ACX, "acx set inconnaction sta %pM", addr); + + acx = kzalloc(sizeof(*acx), GFP_KERNEL); + if (!acx) + return -ENOMEM; + + memcpy(acx->addr, addr, ETH_ALEN); + + ret = wl1271_cmd_configure(wl, ACX_UPDATE_INCONNECTION_STA_LIST, + acx, sizeof(*acx)); + if (ret < 0) { + wl1271_warning("acx set inconnaction sta failed: %d", ret); + goto out; + } + +out: + kfree(acx); + return ret; +} diff --git a/drivers/net/wireless/wl12xx/acx.h b/drivers/net/wireless/wl12xx/acx.h index 4e301de916b..dd19b01d807 100644 --- a/drivers/net/wireless/wl12xx/acx.h +++ b/drivers/net/wireless/wl12xx/acx.h @@ -1155,6 +1155,13 @@ struct wl1271_acx_config_ps { __le32 null_data_rate; } __packed; +struct wl1271_acx_inconnection_sta { + struct acx_header header; + + u8 addr[ETH_ALEN]; + u8 padding1[2]; +} __packed; + enum { ACX_WAKE_UP_CONDITIONS = 0x0002, ACX_MEM_CFG = 0x0003, @@ -1215,6 +1222,7 @@ enum { ACX_GEN_FW_CMD = 0x0070, ACX_HOST_IF_CFG_BITMAP = 0x0071, ACX_MAX_TX_FAILURE = 0x0072, + ACX_UPDATE_INCONNECTION_STA_LIST = 0x0073, DOT11_RX_MSDU_LIFE_TIME = 0x1004, DOT11_CUR_TX_PWR = 0x100D, DOT11_RX_DOT11_MODE = 0x1012, @@ -1290,5 +1298,6 @@ int wl1271_acx_set_ba_receiver_session(struct wl1271 *wl, u8 tid_index, u16 ssn, int wl1271_acx_tsf_info(struct wl1271 *wl, u64 *mactime); int wl1271_acx_max_tx_retry(struct wl1271 *wl); int wl1271_acx_config_ps(struct wl1271 *wl); +int wl1271_acx_set_inconnection_sta(struct wl1271 *wl, u8 *addr); #endif /* __WL1271_ACX_H__ */ diff --git a/drivers/net/wireless/wl12xx/init.c b/drivers/net/wireless/wl12xx/init.c index 62dc9839dd3..6072fe45713 100644 --- a/drivers/net/wireless/wl12xx/init.c +++ b/drivers/net/wireless/wl12xx/init.c @@ -483,7 +483,7 @@ static void wl1271_check_ba_support(struct wl1271 *wl) static int wl1271_set_ba_policies(struct wl1271 *wl) { u8 tid_index; - u8 ret = 0; + int ret = 0; /* Reset the BA RX indicators */ wl->ba_rx_bitmap = 0; diff --git a/drivers/net/wireless/wl12xx/main.c b/drivers/net/wireless/wl12xx/main.c index 61dea73f5fd..947491a1d9c 100644 --- a/drivers/net/wireless/wl12xx/main.c +++ b/drivers/net/wireless/wl12xx/main.c @@ -482,6 +482,10 @@ static int wl1271_plt_init(struct wl1271 *wl) if (ret < 0) goto out_free_memmap; + ret = wl1271_acx_sta_mem_cfg(wl); + if (ret < 0) + goto out_free_memmap; + /* Default fragmentation threshold */ ret = wl1271_acx_frag_threshold(wl, wl->conf.tx.frag_threshold); if (ret < 0) @@ -533,6 +537,57 @@ static int wl1271_plt_init(struct wl1271 *wl) return ret; } +static void wl1271_irq_ps_regulate_link(struct wl1271 *wl, u8 hlid, u8 tx_blks) +{ + bool fw_ps; + + /* only regulate station links */ + if (hlid < WL1271_AP_STA_HLID_START) + return; + + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + + /* + * Wake up from high level PS if the STA is asleep with too little + * blocks in FW or if the STA is awake. + */ + if (!fw_ps || tx_blks < WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_end(wl, hlid); + + /* Start high-level PS if the STA is asleep with enough blocks in FW */ + else if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_start(wl, hlid, true); +} + +static void wl1271_irq_update_links_status(struct wl1271 *wl, + struct wl1271_fw_ap_status *status) +{ + u32 cur_fw_ps_map; + u8 hlid; + + cur_fw_ps_map = le32_to_cpu(status->link_ps_bitmap); + if (wl->ap_fw_ps_map != cur_fw_ps_map) { + wl1271_debug(DEBUG_PSM, + "link ps prev 0x%x cur 0x%x changed 0x%x", + wl->ap_fw_ps_map, cur_fw_ps_map, + wl->ap_fw_ps_map ^ cur_fw_ps_map); + + wl->ap_fw_ps_map = cur_fw_ps_map; + } + + for (hlid = WL1271_AP_STA_HLID_START; hlid < AP_MAX_LINKS; hlid++) { + u8 cnt = status->tx_lnk_free_blks[hlid] - + wl->links[hlid].prev_freed_blks; + + wl->links[hlid].prev_freed_blks = + status->tx_lnk_free_blks[hlid]; + wl->links[hlid].allocated_blks -= cnt; + + wl1271_irq_ps_regulate_link(wl, hlid, + wl->links[hlid].allocated_blks); + } +} + static void wl1271_fw_status(struct wl1271 *wl, struct wl1271_fw_full_status *full_status) { @@ -570,6 +625,10 @@ static void wl1271_fw_status(struct wl1271 *wl, if (total) clear_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags); + /* for AP update num of allocated TX blocks per link and ps status */ + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl1271_irq_update_links_status(wl, &full_status->ap); + /* update the host-chipset time offset */ getnstimeofday(&ts); wl->time_offset = (timespec_to_ns(&ts) >> 10) - @@ -975,19 +1034,37 @@ int wl1271_plt_stop(struct wl1271 *wl) return ret; } -static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct wl1271 *wl = hw->priv; unsigned long flags; int q; + u8 hlid = 0; spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; + + /* + * The workqueue is slow to process the tx_queue and we need stop + * the queue here, otherwise the queue will get too long. + */ + if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { + wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); + ieee80211_stop_queues(wl->hw); + set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); + } + spin_unlock_irqrestore(&wl->wl_lock, flags); /* queue the packet */ q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - skb_queue_tail(&wl->tx_queue[q], skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + hlid = wl1271_tx_get_hlid(skb); + wl1271_debug(DEBUG_TX, "queue skb hlid %d q %d", hlid, q); + skb_queue_tail(&wl->links[hlid].tx_queue[q], skb); + } else { + skb_queue_tail(&wl->tx_queue[q], skb); + } /* * The chip specific setup must run before the first TX packet - @@ -996,21 +1073,6 @@ static int wl1271_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) if (!test_bit(WL1271_FLAG_FW_TX_BUSY, &wl->flags)) ieee80211_queue_work(wl->hw, &wl->tx_work); - - /* - * The workqueue is slow to process the tx_queue and we need stop - * the queue here, otherwise the queue will get too long. - */ - if (wl->tx_queue_count >= WL1271_TX_QUEUE_HIGH_WATERMARK) { - wl1271_debug(DEBUG_TX, "op_tx: stopping queues"); - - spin_lock_irqsave(&wl->wl_lock, flags); - ieee80211_stop_queues(wl->hw); - set_bit(WL1271_FLAG_TX_QUEUE_STOPPED, &wl->flags); - spin_unlock_irqrestore(&wl->wl_lock, flags); - } - - return NETDEV_TX_OK; } static struct notifier_block wl1271_dev_notifier = { @@ -1221,6 +1283,8 @@ static void __wl1271_op_remove_interface(struct wl1271 *wl) wl->filters = 0; wl1271_free_ap_keys(wl); memset(wl->ap_hlid_map, 0, sizeof(wl->ap_hlid_map)); + wl->ap_fw_ps_map = 0; + wl->ap_ps_map = 0; for (i = 0; i < NUM_TX_QUEUES; i++) wl->tx_blocks_freed[i] = 0; @@ -2218,6 +2282,8 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, u32 sta_rate_set = 0; int ret; struct ieee80211_sta *sta; + bool sta_exists = false; + struct ieee80211_sta_ht_cap sta_ht_cap; if (is_ibss) { ret = wl1271_bss_beacon_info_changed(wl, vif, bss_conf, @@ -2289,16 +2355,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (sta->ht_cap.ht_supported) sta_rate_set |= (sta->ht_cap.mcs.rx_mask[0] << HW_HT_RATES_OFFSET); + sta_ht_cap = sta->ht_cap; + sta_exists = true; + } + rcu_read_unlock(); + if (sta_exists) { /* handle new association with HT and HT information change */ if ((changed & BSS_CHANGED_HT) && (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true); if (ret < 0) { wl1271_warning("Set ht cap true failed %d", ret); - rcu_read_unlock(); goto out; } ret = wl1271_acx_set_ht_information(wl, @@ -2306,23 +2376,20 @@ static void wl1271_bss_info_changed_sta(struct wl1271 *wl, if (ret < 0) { wl1271_warning("Set ht information failed %d", ret); - rcu_read_unlock(); goto out; } } /* handle new association without HT and disassociation */ else if (changed & BSS_CHANGED_ASSOC) { - ret = wl1271_acx_set_ht_capabilities(wl, &sta->ht_cap, + ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, false); if (ret < 0) { wl1271_warning("Set ht cap false failed %d", ret); - rcu_read_unlock(); goto out; } } } - rcu_read_unlock(); if ((changed & BSS_CHANGED_ASSOC)) { if (bss_conf->assoc) { @@ -2612,7 +2679,7 @@ static int wl1271_op_get_survey(struct ieee80211_hw *hw, int idx, return 0; } -static int wl1271_allocate_hlid(struct wl1271 *wl, +static int wl1271_allocate_sta(struct wl1271 *wl, struct ieee80211_sta *sta, u8 *hlid) { @@ -2626,18 +2693,25 @@ static int wl1271_allocate_hlid(struct wl1271 *wl, } wl_sta = (struct wl1271_station *)sta->drv_priv; - __set_bit(id, wl->ap_hlid_map); wl_sta->hlid = WL1271_AP_STA_HLID_START + id; *hlid = wl_sta->hlid; + memcpy(wl->links[wl_sta->hlid].addr, sta->addr, ETH_ALEN); return 0; } -static void wl1271_free_hlid(struct wl1271 *wl, u8 hlid) +static void wl1271_free_sta(struct wl1271 *wl, u8 hlid) { int id = hlid - WL1271_AP_STA_HLID_START; + if (WARN_ON(!test_bit(id, wl->ap_hlid_map))) + return; + __clear_bit(id, wl->ap_hlid_map); + memset(wl->links[hlid].addr, 0, ETH_ALEN); + wl1271_tx_reset_link_queues(wl, hlid); + __clear_bit(hlid, &wl->ap_ps_map); + __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); } static int wl1271_op_sta_add(struct ieee80211_hw *hw, @@ -2658,13 +2732,13 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, wl1271_debug(DEBUG_MAC80211, "mac80211 add sta %d", (int)sta->aid); - ret = wl1271_allocate_hlid(wl, sta, &hlid); + ret = wl1271_allocate_sta(wl, sta, &hlid); if (ret < 0) goto out; ret = wl1271_ps_elp_wakeup(wl, false); if (ret < 0) - goto out; + goto out_free_sta; ret = wl1271_cmd_add_sta(wl, sta, hlid); if (ret < 0) @@ -2673,6 +2747,10 @@ static int wl1271_op_sta_add(struct ieee80211_hw *hw, out_sleep: wl1271_ps_elp_sleep(wl); +out_free_sta: + if (ret < 0) + wl1271_free_sta(wl, hlid); + out: mutex_unlock(&wl->mutex); return ret; @@ -2709,7 +2787,7 @@ static int wl1271_op_sta_remove(struct ieee80211_hw *hw, if (ret < 0) goto out_sleep; - wl1271_free_hlid(wl, wl_sta->hlid); + wl1271_free_sta(wl, wl_sta->hlid); out_sleep: wl1271_ps_elp_sleep(wl); @@ -3212,7 +3290,9 @@ int wl1271_init_ieee80211(struct wl1271 *wl) IEEE80211_HW_SUPPORTS_UAPSD | IEEE80211_HW_HAS_RATE_CONTROL | IEEE80211_HW_CONNECTION_MONITOR | - IEEE80211_HW_SUPPORTS_CQM_RSSI; + IEEE80211_HW_SUPPORTS_CQM_RSSI | + IEEE80211_HW_REPORTS_TX_ACK_STATUS | + IEEE80211_HW_AP_LINK_PS; wl->hw->wiphy->cipher_suites = cipher_suites; wl->hw->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites); @@ -3264,7 +3344,7 @@ struct ieee80211_hw *wl1271_alloc_hw(void) struct ieee80211_hw *hw; struct platform_device *plat_dev = NULL; struct wl1271 *wl; - int i, ret; + int i, j, ret; unsigned int order; hw = ieee80211_alloc_hw(sizeof(*wl), &wl1271_ops); @@ -3292,6 +3372,10 @@ struct ieee80211_hw *wl1271_alloc_hw(void) for (i = 0; i < NUM_TX_QUEUES; i++) skb_queue_head_init(&wl->tx_queue[i]); + for (i = 0; i < NUM_TX_QUEUES; i++) + for (j = 0; j < AP_MAX_LINKS; j++) + skb_queue_head_init(&wl->links[j].tx_queue[i]); + INIT_DELAYED_WORK(&wl->elp_work, wl1271_elp_work); INIT_DELAYED_WORK(&wl->pspoll_work, wl1271_pspoll_work); INIT_WORK(&wl->irq_work, wl1271_irq_work); @@ -3317,6 +3401,9 @@ struct ieee80211_hw *wl1271_alloc_hw(void) wl->bss_type = MAX_BSS_TYPE; wl->set_bss_type = MAX_BSS_TYPE; wl->fw_bss_type = MAX_BSS_TYPE; + wl->last_tx_hlid = 0; + wl->ap_ps_map = 0; + wl->ap_fw_ps_map = 0; memset(wl->tx_frames_map, 0, sizeof(wl->tx_frames_map)); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) @@ -3412,5 +3499,5 @@ module_param_named(debug_level, wl12xx_debug_level, uint, S_IRUSR | S_IWUSR); MODULE_PARM_DESC(debug_level, "wl12xx debugging level"); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Luciano Coelho <luciano.coelho@nokia.com>"); +MODULE_AUTHOR("Luciano Coelho <coelho@ti.com>"); MODULE_AUTHOR("Juuso Oikarinen <juuso.oikarinen@nokia.com>"); diff --git a/drivers/net/wireless/wl12xx/ps.c b/drivers/net/wireless/wl12xx/ps.c index 2d3086ae633..5c347b1bd17 100644 --- a/drivers/net/wireless/wl12xx/ps.c +++ b/drivers/net/wireless/wl12xx/ps.c @@ -24,6 +24,7 @@ #include "reg.h" #include "ps.h" #include "io.h" +#include "tx.h" #define WL1271_WAKEUP_TIMEOUT 500 @@ -173,4 +174,81 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, return ret; } +static void wl1271_ps_filter_frames(struct wl1271 *wl, u8 hlid) +{ + int i, filtered = 0; + struct sk_buff *skb; + struct ieee80211_tx_info *info; + unsigned long flags; + + /* filter all frames currently the low level queus for this hlid */ + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + info = IEEE80211_SKB_CB(skb); + info->flags |= IEEE80211_TX_STAT_TX_FILTERED; + info->status.rates[0].idx = -1; + ieee80211_tx_status(wl->hw, skb); + filtered++; + } + } + + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count -= filtered; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + wl1271_handle_tx_low_watermark(wl); +} + +void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues) +{ + struct ieee80211_sta *sta; + + if (test_bit(hlid, &wl->ap_ps_map)) + return; + + wl1271_debug(DEBUG_PSM, "start mac80211 PSM on hlid %d blks %d " + "clean_queues %d", hlid, wl->links[hlid].allocated_blks, + clean_queues); + + rcu_read_lock(); + sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); + if (!sta) { + wl1271_error("could not find sta %pM for starting ps", + wl->links[hlid].addr); + rcu_read_unlock(); + return; + } + ieee80211_sta_ps_transition_ni(sta, true); + rcu_read_unlock(); + + /* do we want to filter all frames from this link's queues? */ + if (clean_queues) + wl1271_ps_filter_frames(wl, hlid); + + __set_bit(hlid, &wl->ap_ps_map); +} + +void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid) +{ + struct ieee80211_sta *sta; + + if (!test_bit(hlid, &wl->ap_ps_map)) + return; + + wl1271_debug(DEBUG_PSM, "end mac80211 PSM on hlid %d", hlid); + + __clear_bit(hlid, &wl->ap_ps_map); + + rcu_read_lock(); + sta = ieee80211_find_sta(wl->vif, wl->links[hlid].addr); + if (!sta) { + wl1271_error("could not find sta %pM for ending ps", + wl->links[hlid].addr); + goto end; + } + + ieee80211_sta_ps_transition_ni(sta, false); +end: + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/wl12xx/ps.h b/drivers/net/wireless/wl12xx/ps.h index 8415060f08e..fc1f4c19359 100644 --- a/drivers/net/wireless/wl12xx/ps.h +++ b/drivers/net/wireless/wl12xx/ps.h @@ -32,5 +32,7 @@ int wl1271_ps_set_mode(struct wl1271 *wl, enum wl1271_cmd_ps_mode mode, void wl1271_ps_elp_sleep(struct wl1271 *wl); int wl1271_ps_elp_wakeup(struct wl1271 *wl, bool chip_awake); void wl1271_elp_work(struct work_struct *work); +void wl1271_ps_link_start(struct wl1271 *wl, u8 hlid, bool clean_queues); +void wl1271_ps_link_end(struct wl1271 *wl, u8 hlid); #endif /* __WL1271_PS_H__ */ diff --git a/drivers/net/wireless/wl12xx/rx.c b/drivers/net/wireless/wl12xx/rx.c index 00d250d8da1..3d13d7a83ea 100644 --- a/drivers/net/wireless/wl12xx/rx.c +++ b/drivers/net/wireless/wl12xx/rx.c @@ -92,7 +92,7 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) { struct wl1271_rx_descriptor *desc; struct sk_buff *skb; - u16 *fc; + struct ieee80211_hdr *hdr; u8 *buf; u8 beacon = 0; @@ -118,8 +118,8 @@ static int wl1271_rx_handle_data(struct wl1271 *wl, u8 *data, u32 length) /* now we pull the descriptor out of the buffer */ skb_pull(skb, sizeof(*desc)); - fc = (u16 *)skb->data; - if ((*fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_BEACON) + hdr = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_beacon(hdr->frame_control)) beacon = 1; wl1271_rx_status(wl, desc, IEEE80211_SKB_RXCB(skb), beacon); diff --git a/drivers/net/wireless/wl12xx/rx.h b/drivers/net/wireless/wl12xx/rx.h index 4cef8fa3dee..75fabf83649 100644 --- a/drivers/net/wireless/wl12xx/rx.h +++ b/drivers/net/wireless/wl12xx/rx.h @@ -30,10 +30,6 @@ #define WL1271_RX_MAX_RSSI -30 #define WL1271_RX_MIN_RSSI -95 -#define WL1271_RX_ALIGN_TO 4 -#define WL1271_RX_ALIGN(len) (((len) + WL1271_RX_ALIGN_TO - 1) & \ - ~(WL1271_RX_ALIGN_TO - 1)) - #define SHORT_PREAMBLE_BIT BIT(0) #define OFDM_RATE_BIT BIT(6) #define PBCC_RATE_BIT BIT(7) diff --git a/drivers/net/wireless/wl12xx/tx.c b/drivers/net/wireless/wl12xx/tx.c index 67a00946e3d..ac60d577319 100644 --- a/drivers/net/wireless/wl12xx/tx.c +++ b/drivers/net/wireless/wl12xx/tx.c @@ -70,8 +70,65 @@ static void wl1271_free_tx_id(struct wl1271 *wl, int id) } } +static void wl1271_tx_ap_update_inconnection_sta(struct wl1271 *wl, + struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr; + + /* + * add the station to the known list before transmitting the + * authentication response. this way it won't get de-authed by FW + * when transmitting too soon. + */ + hdr = (struct ieee80211_hdr *)(skb->data + + sizeof(struct wl1271_tx_hw_descr)); + if (ieee80211_is_auth(hdr->frame_control)) + wl1271_acx_set_inconnection_sta(wl, hdr->addr1); +} + +static void wl1271_tx_regulate_link(struct wl1271 *wl, u8 hlid) +{ + bool fw_ps; + u8 tx_blks; + + /* only regulate station links */ + if (hlid < WL1271_AP_STA_HLID_START) + return; + + fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + tx_blks = wl->links[hlid].allocated_blks; + + /* + * if in FW PS and there is enough data in FW we can put the link + * into high-level PS and clean out its TX queues. + */ + if (fw_ps && tx_blks >= WL1271_PS_STA_MAX_BLOCKS) + wl1271_ps_link_start(wl, hlid, true); +} + +u8 wl1271_tx_get_hlid(struct sk_buff *skb) +{ + struct ieee80211_tx_info *control = IEEE80211_SKB_CB(skb); + + if (control->control.sta) { + struct wl1271_station *wl_sta; + + wl_sta = (struct wl1271_station *) + control->control.sta->drv_priv; + return wl_sta->hlid; + } else { + struct ieee80211_hdr *hdr; + + hdr = (struct ieee80211_hdr *)skb->data; + if (ieee80211_is_mgmt(hdr->frame_control)) + return WL1271_AP_GLOBAL_HLID; + else + return WL1271_AP_BROADCAST_HLID; + } +} + static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, - u32 buf_offset) + u32 buf_offset, u8 hlid) { struct wl1271_tx_hw_descr *desc; u32 total_len = skb->len + sizeof(struct wl1271_tx_hw_descr) + extra; @@ -100,6 +157,9 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, wl->tx_blocks_available -= total_blocks; + if (wl->bss_type == BSS_TYPE_AP_BSS) + wl->links[hlid].allocated_blks += total_blocks; + ret = 0; wl1271_debug(DEBUG_TX, @@ -113,7 +173,8 @@ static int wl1271_tx_allocate(struct wl1271 *wl, struct sk_buff *skb, u32 extra, } static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, - u32 extra, struct ieee80211_tx_info *control) + u32 extra, struct ieee80211_tx_info *control, + u8 hlid) { struct timespec ts; struct wl1271_tx_hw_descr *desc; @@ -149,7 +210,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->tid = ac; if (wl->bss_type != BSS_TYPE_AP_BSS) { - desc->aid = TX_HW_DEFAULT_AID; + desc->aid = hlid; /* if the packets are destined for AP (have a STA entry) send them with AP rate policies, otherwise use default @@ -159,25 +220,17 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, else rate_idx = ACX_TX_BASIC_RATE; } else { - if (control->control.sta) { - struct wl1271_station *wl_sta; - - wl_sta = (struct wl1271_station *) - control->control.sta->drv_priv; - desc->hlid = wl_sta->hlid; + desc->hlid = hlid; + switch (hlid) { + case WL1271_AP_GLOBAL_HLID: + rate_idx = ACX_TX_AP_MODE_MGMT_RATE; + break; + case WL1271_AP_BROADCAST_HLID: + rate_idx = ACX_TX_AP_MODE_BCST_RATE; + break; + default: rate_idx = ac; - } else { - struct ieee80211_hdr *hdr; - - hdr = (struct ieee80211_hdr *) - (skb->data + sizeof(*desc)); - if (ieee80211_is_mgmt(hdr->frame_control)) { - desc->hlid = WL1271_AP_GLOBAL_HLID; - rate_idx = ACX_TX_AP_MODE_MGMT_RATE; - } else { - desc->hlid = WL1271_AP_BROADCAST_HLID; - rate_idx = ACX_TX_AP_MODE_BCST_RATE; - } + break; } } @@ -185,7 +238,7 @@ static void wl1271_tx_fill_hdr(struct wl1271 *wl, struct sk_buff *skb, desc->reserved = 0; /* align the length (and store in terms of words) */ - pad = WL1271_TX_ALIGN(skb->len); + pad = ALIGN(skb->len, WL1271_TX_ALIGN_TO); desc->length = cpu_to_le16(pad >> 2); /* calculate number of padding bytes */ @@ -208,6 +261,7 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, u32 extra = 0; int ret = 0; u32 total_len; + u8 hlid; if (!skb) return -EINVAL; @@ -234,18 +288,28 @@ static int wl1271_prepare_tx_frame(struct wl1271 *wl, struct sk_buff *skb, } } - ret = wl1271_tx_allocate(wl, skb, extra, buf_offset); + if (wl->bss_type == BSS_TYPE_AP_BSS) + hlid = wl1271_tx_get_hlid(skb); + else + hlid = TX_HW_DEFAULT_AID; + + ret = wl1271_tx_allocate(wl, skb, extra, buf_offset, hlid); if (ret < 0) return ret; - wl1271_tx_fill_hdr(wl, skb, extra, info); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + wl1271_tx_ap_update_inconnection_sta(wl, skb); + wl1271_tx_regulate_link(wl, hlid); + } + + wl1271_tx_fill_hdr(wl, skb, extra, info, hlid); /* * The length of each packet is stored in terms of words. Thus, we must * pad the skb data to make sure its length is aligned. * The number of padding bytes is computed and set in wl1271_tx_fill_hdr */ - total_len = WL1271_TX_ALIGN(skb->len); + total_len = ALIGN(skb->len, WL1271_TX_ALIGN_TO); memcpy(wl->aggr_buf + buf_offset, skb->data, skb->len); memset(wl->aggr_buf + buf_offset + skb->len, 0, total_len - skb->len); @@ -279,7 +343,7 @@ u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set) return enabled_rates; } -static void handle_tx_low_watermark(struct wl1271 *wl) +void wl1271_handle_tx_low_watermark(struct wl1271 *wl) { unsigned long flags; @@ -293,7 +357,7 @@ static void handle_tx_low_watermark(struct wl1271 *wl) } } -static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) +static struct sk_buff *wl1271_sta_skb_dequeue(struct wl1271 *wl) { struct sk_buff *skb = NULL; unsigned long flags; @@ -319,12 +383,69 @@ out: return skb; } +static struct sk_buff *wl1271_ap_skb_dequeue(struct wl1271 *wl) +{ + struct sk_buff *skb = NULL; + unsigned long flags; + int i, h, start_hlid; + + /* start from the link after the last one */ + start_hlid = (wl->last_tx_hlid + 1) % AP_MAX_LINKS; + + /* dequeue according to AC, round robin on each link */ + for (i = 0; i < AP_MAX_LINKS; i++) { + h = (start_hlid + i) % AP_MAX_LINKS; + + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VO]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_VI]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BE]); + if (skb) + goto out; + skb = skb_dequeue(&wl->links[h].tx_queue[CONF_TX_AC_BK]); + if (skb) + goto out; + } + +out: + if (skb) { + wl->last_tx_hlid = h; + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count--; + spin_unlock_irqrestore(&wl->wl_lock, flags); + } else { + wl->last_tx_hlid = 0; + } + + return skb; +} + +static struct sk_buff *wl1271_skb_dequeue(struct wl1271 *wl) +{ + if (wl->bss_type == BSS_TYPE_AP_BSS) + return wl1271_ap_skb_dequeue(wl); + + return wl1271_sta_skb_dequeue(wl); +} + static void wl1271_skb_queue_head(struct wl1271 *wl, struct sk_buff *skb) { unsigned long flags; int q = wl1271_tx_get_queue(skb_get_queue_mapping(skb)); - skb_queue_head(&wl->tx_queue[q], skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + u8 hlid = wl1271_tx_get_hlid(skb); + skb_queue_head(&wl->links[hlid].tx_queue[q], skb); + + /* make sure we dequeue the same packet next time */ + wl->last_tx_hlid = (hlid + AP_MAX_LINKS - 1) % AP_MAX_LINKS; + } else { + skb_queue_head(&wl->tx_queue[q], skb); + } + spin_lock_irqsave(&wl->wl_lock, flags); wl->tx_queue_count++; spin_unlock_irqrestore(&wl->wl_lock, flags); @@ -387,7 +508,7 @@ out_ack: if (sent_packets) { /* interrupt the firmware with the new packets */ wl1271_write32(wl, WL1271_HOST_WR_ACCESS, wl->tx_packets_count); - handle_tx_low_watermark(wl); + wl1271_handle_tx_low_watermark(wl); } out: @@ -504,32 +625,76 @@ void wl1271_tx_complete(struct wl1271 *wl) } } +void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid) +{ + struct sk_buff *skb; + int i, total = 0; + unsigned long flags; + struct ieee80211_tx_info *info; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->links[hlid].tx_queue[i]))) { + wl1271_debug(DEBUG_TX, "link freeing skb 0x%p", skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + ieee80211_tx_status(wl->hw, skb); + total++; + } + } + + spin_lock_irqsave(&wl->wl_lock, flags); + wl->tx_queue_count -= total; + spin_unlock_irqrestore(&wl->wl_lock, flags); + + wl1271_handle_tx_low_watermark(wl); +} + /* caller must hold wl->mutex */ void wl1271_tx_reset(struct wl1271 *wl) { int i; struct sk_buff *skb; + struct ieee80211_tx_info *info; /* TX failure */ - for (i = 0; i < NUM_TX_QUEUES; i++) { - while ((skb = skb_dequeue(&wl->tx_queue[i]))) { - wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); - ieee80211_tx_status(wl->hw, skb); + if (wl->bss_type == BSS_TYPE_AP_BSS) { + for (i = 0; i < AP_MAX_LINKS; i++) { + wl1271_tx_reset_link_queues(wl, i); + wl->links[i].allocated_blks = 0; + wl->links[i].prev_freed_blks = 0; + } + + wl->last_tx_hlid = 0; + } else { + for (i = 0; i < NUM_TX_QUEUES; i++) { + while ((skb = skb_dequeue(&wl->tx_queue[i]))) { + wl1271_debug(DEBUG_TX, "freeing skb 0x%p", + skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; + ieee80211_tx_status(wl->hw, skb); + } } } + wl->tx_queue_count = 0; /* * Make sure the driver is at a consistent state, in case this * function is called from a context other than interface removal. */ - handle_tx_low_watermark(wl); + wl1271_handle_tx_low_watermark(wl); for (i = 0; i < ACX_TX_DESCRIPTORS; i++) if (wl->tx_frames[i] != NULL) { skb = wl->tx_frames[i]; wl1271_free_tx_id(wl, i); wl1271_debug(DEBUG_TX, "freeing skb 0x%p", skb); + info = IEEE80211_SKB_CB(skb); + info->status.rates[0].idx = -1; + info->status.rates[0].count = 0; ieee80211_tx_status(wl->hw, skb); } } @@ -544,8 +709,8 @@ void wl1271_tx_flush(struct wl1271 *wl) while (!time_after(jiffies, timeout)) { mutex_lock(&wl->mutex); - wl1271_debug(DEBUG_TX, "flushing tx buffer: %d", - wl->tx_frames_cnt); + wl1271_debug(DEBUG_TX, "flushing tx buffer: %d %d", + wl->tx_frames_cnt, wl->tx_queue_count); if ((wl->tx_frames_cnt == 0) && (wl->tx_queue_count == 0)) { mutex_unlock(&wl->mutex); return; diff --git a/drivers/net/wireless/wl12xx/tx.h b/drivers/net/wireless/wl12xx/tx.h index 05722a560d9..02f07fa66e8 100644 --- a/drivers/net/wireless/wl12xx/tx.h +++ b/drivers/net/wireless/wl12xx/tx.h @@ -53,8 +53,6 @@ #define TX_HW_RESULT_QUEUE_LEN_MASK 0xf #define WL1271_TX_ALIGN_TO 4 -#define WL1271_TX_ALIGN(len) (((len) + WL1271_TX_ALIGN_TO - 1) & \ - ~(WL1271_TX_ALIGN_TO - 1)) #define WL1271_TKIP_IV_SPACE 4 struct wl1271_tx_hw_descr { @@ -152,5 +150,8 @@ void wl1271_tx_flush(struct wl1271 *wl); u8 wl1271_rate_to_idx(int rate, enum ieee80211_band band); u32 wl1271_tx_enabled_rates_get(struct wl1271 *wl, u32 rate_set); u32 wl1271_tx_min_rate_get(struct wl1271 *wl); +u8 wl1271_tx_get_hlid(struct sk_buff *skb); +void wl1271_tx_reset_link_queues(struct wl1271 *wl, u8 hlid); +void wl1271_handle_tx_low_watermark(struct wl1271 *wl); #endif diff --git a/drivers/net/wireless/wl12xx/wl12xx.h b/drivers/net/wireless/wl12xx/wl12xx.h index 1d6c94304b1..338acc9f60b 100644 --- a/drivers/net/wireless/wl12xx/wl12xx.h +++ b/drivers/net/wireless/wl12xx/wl12xx.h @@ -153,6 +153,17 @@ extern u32 wl12xx_debug_level; #define WL1271_AP_BROADCAST_HLID 1 #define WL1271_AP_STA_HLID_START 2 +/* + * When in AP-mode, we allow (at least) this number of mem-blocks + * to be transmitted to FW for a STA in PS-mode. Only when packets are + * present in the FW buffers it will wake the sleeping STA. We want to put + * enough packets for the driver to transmit all of its buffered data before + * the STA goes to sleep again. But we don't want to take too much mem-blocks + * as it might hurt the throughput of active STAs. + * The number of blocks (18) is enough for 2 large packets. + */ +#define WL1271_PS_STA_MAX_BLOCKS (2 * 9) + #define WL1271_AP_BSS_INDEX 0 #define WL1271_AP_DEF_INACTIV_SEC 300 #define WL1271_AP_DEF_BEACON_EXP 20 @@ -319,6 +330,17 @@ enum wl12xx_flags { WL1271_FLAG_AP_STARTED }; +struct wl1271_link { + /* AP-mode - TX queue per AC in link */ + struct sk_buff_head tx_queue[NUM_TX_QUEUES]; + + /* accounting for allocated / available TX blocks in FW */ + u8 allocated_blks; + u8 prev_freed_blks; + + u8 addr[ETH_ALEN]; +}; + struct wl1271 { struct platform_device *plat_dev; struct ieee80211_hw *hw; @@ -498,6 +520,21 @@ struct wl1271 { /* RX BA constraint value */ bool ba_support; u8 ba_rx_bitmap; + + /* + * AP-mode - links indexed by HLID. The global and broadcast links + * are always active. + */ + struct wl1271_link links[AP_MAX_LINKS]; + + /* the hlid of the link where the last transmitted skb came from */ + int last_tx_hlid; + + /* AP-mode - a bitmap of links currently in PS mode according to FW */ + u32 ap_fw_ps_map; + + /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ + unsigned long ap_ps_map; }; struct wl1271_station { diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index 74a269ebbeb..5037c8b2b41 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -850,7 +850,7 @@ static int fill_ctrlset(struct zd_mac *mac, * control block of the skbuff will be initialized. If necessary the incoming * mac80211 queues will be stopped. */ -static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { struct zd_mac *mac = zd_hw_mac(hw); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); @@ -865,11 +865,10 @@ static int zd_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb) r = zd_usb_tx(&mac->chip.usb, skb); if (r) goto fail; - return 0; + return; fail: dev_kfree_skb(skb); - return 0; } /** diff --git a/drivers/nfc/Kconfig b/drivers/nfc/Kconfig index ffedfd49275..ea158008534 100644 --- a/drivers/nfc/Kconfig +++ b/drivers/nfc/Kconfig @@ -3,7 +3,7 @@ # menuconfig NFC_DEVICES - bool "NFC devices" + bool "Near Field Communication (NFC) devices" default n ---help--- You'll have to say Y if your computer contains an NFC device that diff --git a/drivers/nfc/pn544.c b/drivers/nfc/pn544.c index bae647264dd..724f65d8f9e 100644 --- a/drivers/nfc/pn544.c +++ b/drivers/nfc/pn544.c @@ -60,7 +60,7 @@ enum pn544_irq { struct pn544_info { struct miscdevice miscdev; struct i2c_client *i2c_dev; - struct regulator_bulk_data regs[2]; + struct regulator_bulk_data regs[3]; enum pn544_state state; wait_queue_head_t read_wait; @@ -74,6 +74,7 @@ struct pn544_info { static const char reg_vdd_io[] = "Vdd_IO"; static const char reg_vbat[] = "VBat"; +static const char reg_vsim[] = "VSim"; /* sysfs interface */ static ssize_t pn544_test(struct device *dev, @@ -740,6 +741,7 @@ static int __devinit pn544_probe(struct i2c_client *client, info->regs[0].supply = reg_vdd_io; info->regs[1].supply = reg_vbat; + info->regs[2].supply = reg_vsim; r = regulator_bulk_get(&client->dev, ARRAY_SIZE(info->regs), info->regs); if (r < 0) diff --git a/drivers/of/pdt.c b/drivers/of/pdt.c index 28295d0a50f..4d87b5dc928 100644 --- a/drivers/of/pdt.c +++ b/drivers/of/pdt.c @@ -36,19 +36,55 @@ unsigned int of_pdt_unique_id __initdata; (p)->unique_id = of_pdt_unique_id++; \ } while (0) -static inline const char *of_pdt_node_name(struct device_node *dp) +static char * __init of_pdt_build_full_name(struct device_node *dp) { - return dp->path_component_name; + int len, ourlen, plen; + char *n; + + dp->path_component_name = build_path_component(dp); + + plen = strlen(dp->parent->full_name); + ourlen = strlen(dp->path_component_name); + len = ourlen + plen + 2; + + n = prom_early_alloc(len); + strcpy(n, dp->parent->full_name); + if (!of_node_is_root(dp->parent)) { + strcpy(n + plen, "/"); + plen++; + } + strcpy(n + plen, dp->path_component_name); + + return n; } -#else +#else /* CONFIG_SPARC */ static inline void of_pdt_incr_unique_id(void *p) { } static inline void irq_trans_init(struct device_node *dp) { } -static inline const char *of_pdt_node_name(struct device_node *dp) +static char * __init of_pdt_build_full_name(struct device_node *dp) { - return dp->name; + static int failsafe_id = 0; /* for generating unique names on failure */ + char *buf; + int len; + + if (of_pdt_prom_ops->pkg2path(dp->phandle, NULL, 0, &len)) + goto failsafe; + + buf = prom_early_alloc(len + 1); + if (of_pdt_prom_ops->pkg2path(dp->phandle, buf, len, &len)) + goto failsafe; + return buf; + + failsafe: + buf = prom_early_alloc(strlen(dp->parent->full_name) + + strlen(dp->name) + 16); + sprintf(buf, "%s/%s@unknown%i", + of_node_is_root(dp->parent) ? "" : dp->parent->full_name, + dp->name, failsafe_id++); + pr_err("%s: pkg2path failed; assigning %s\n", __func__, buf); + return buf; } #endif /* !CONFIG_SPARC */ @@ -132,47 +168,6 @@ static char * __init of_pdt_get_one_property(phandle node, const char *name) return buf; } -static char * __init of_pdt_try_pkg2path(phandle node) -{ - char *res, *buf = NULL; - int len; - - if (!of_pdt_prom_ops->pkg2path) - return NULL; - - if (of_pdt_prom_ops->pkg2path(node, buf, 0, &len)) - return NULL; - buf = prom_early_alloc(len + 1); - if (of_pdt_prom_ops->pkg2path(node, buf, len, &len)) { - pr_err("%s: package-to-path failed\n", __func__); - return NULL; - } - - res = strrchr(buf, '/'); - if (!res) { - pr_err("%s: couldn't find / in %s\n", __func__, buf); - return NULL; - } - return res+1; -} - -/* - * When fetching the node's name, first try using package-to-path; if - * that fails (either because the arch hasn't supplied a PROM callback, - * or some other random failure), fall back to just looking at the node's - * 'name' property. - */ -static char * __init of_pdt_build_name(phandle node) -{ - char *buf; - - buf = of_pdt_try_pkg2path(node); - if (!buf) - buf = of_pdt_get_one_property(node, "name"); - - return buf; -} - static struct device_node * __init of_pdt_create_node(phandle node, struct device_node *parent) { @@ -187,7 +182,7 @@ static struct device_node * __init of_pdt_create_node(phandle node, kref_init(&dp->kref); - dp->name = of_pdt_build_name(node); + dp->name = of_pdt_get_one_property(node, "name"); dp->type = of_pdt_get_one_property(node, "device_type"); dp->phandle = node; @@ -198,26 +193,6 @@ static struct device_node * __init of_pdt_create_node(phandle node, return dp; } -static char * __init of_pdt_build_full_name(struct device_node *dp) -{ - int len, ourlen, plen; - char *n; - - plen = strlen(dp->parent->full_name); - ourlen = strlen(of_pdt_node_name(dp)); - len = ourlen + plen + 2; - - n = prom_early_alloc(len); - strcpy(n, dp->parent->full_name); - if (!of_node_is_root(dp->parent)) { - strcpy(n + plen, "/"); - plen++; - } - strcpy(n + plen, of_pdt_node_name(dp)); - - return n; -} - static struct device_node * __init of_pdt_build_tree(struct device_node *parent, phandle node, struct device_node ***nextp) @@ -240,9 +215,6 @@ static struct device_node * __init of_pdt_build_tree(struct device_node *parent, *(*nextp) = dp; *nextp = &dp->allnext; -#if defined(CONFIG_SPARC) - dp->path_component_name = build_path_component(dp); -#endif dp->full_name = of_pdt_build_full_name(dp); dp->child = of_pdt_build_tree(dp, diff --git a/drivers/pcmcia/pcmcia_resource.c b/drivers/pcmcia/pcmcia_resource.c index 0bdda5b3ed5..42fbf1a7557 100644 --- a/drivers/pcmcia/pcmcia_resource.c +++ b/drivers/pcmcia/pcmcia_resource.c @@ -518,6 +518,8 @@ int pcmcia_enable_device(struct pcmcia_device *p_dev) flags |= CONF_ENABLE_IOCARD; if (flags & CONF_ENABLE_IOCARD) s->socket.flags |= SS_IOCARD; + if (flags & CONF_ENABLE_ZVCARD) + s->socket.flags |= SS_ZVCARD | SS_IOCARD; if (flags & CONF_ENABLE_SPKR) { s->socket.flags |= SS_SPKR_ENA; status = CCSR_AUDIO_ENA; diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c index 3755e7c8c71..2c540542b5a 100644 --- a/drivers/pcmcia/pxa2xx_base.c +++ b/drivers/pcmcia/pxa2xx_base.c @@ -215,7 +215,7 @@ pxa2xx_pcmcia_frequency_change(struct soc_pcmcia_socket *skt, } #endif -static void pxa2xx_configure_sockets(struct device *dev) +void pxa2xx_configure_sockets(struct device *dev) { struct pcmcia_low_level *ops = dev->platform_data; /* diff --git a/drivers/pcmcia/pxa2xx_base.h b/drivers/pcmcia/pxa2xx_base.h index bb62ea87b8f..b609b45469e 100644 --- a/drivers/pcmcia/pxa2xx_base.h +++ b/drivers/pcmcia/pxa2xx_base.h @@ -1,3 +1,4 @@ int pxa2xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt); void pxa2xx_drv_pcmcia_ops(struct pcmcia_low_level *ops); +void pxa2xx_configure_sockets(struct device *dev); diff --git a/drivers/pcmcia/pxa2xx_lubbock.c b/drivers/pcmcia/pxa2xx_lubbock.c index b9f8c8fb42b..25afe637c65 100644 --- a/drivers/pcmcia/pxa2xx_lubbock.c +++ b/drivers/pcmcia/pxa2xx_lubbock.c @@ -226,6 +226,7 @@ int pcmcia_lubbock_init(struct sa1111_dev *sadev) lubbock_set_misc_wr((1 << 15) | (1 << 14), 0); pxa2xx_drv_pcmcia_ops(&lubbock_pcmcia_ops); + pxa2xx_configure_sockets(&sadev->dev); ret = sa1111_pcmcia_add(sadev, &lubbock_pcmcia_ops, pxa2xx_drv_pcmcia_add_one); } diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index d163bc2e2b9..a59af5b24f0 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -227,7 +227,7 @@ config SONYPI_COMPAT config IDEAPAD_LAPTOP tristate "Lenovo IdeaPad Laptop Extras" depends on ACPI - depends on RFKILL + depends on RFKILL && INPUT select INPUT_SPARSEKMAP help This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c5c4b8c32eb..38b34a73866 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -84,7 +84,7 @@ MODULE_LICENSE("GPL"); */ #define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB" #define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C" -#define WMID_GUID1 "6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3" +#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3" #define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A" #define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531" @@ -1280,7 +1280,7 @@ static ssize_t set_bool_threeg(struct device *dev, return -EINVAL; return count; } -static DEVICE_ATTR(threeg, S_IWUGO | S_IRUGO | S_IWUSR, show_bool_threeg, +static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, set_bool_threeg); static ssize_t show_interface(struct device *dev, struct device_attribute *attr, diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index 4633fd8532c..fe495939c30 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -1081,14 +1081,8 @@ static int asus_hotk_add_fs(struct acpi_device *device) struct proc_dir_entry *proc; mode_t mode; - /* - * If parameter uid or gid is not changed, keep the default setting for - * our proc entries (-rw-rw-rw-) else, it means we care about security, - * and then set to -rw-rw---- - */ - if ((asus_uid == 0) && (asus_gid == 0)) { - mode = S_IFREG | S_IRUGO | S_IWUGO; + mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP; } else { mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; printk(KERN_WARNING " asus_uid and asus_gid parameters are " diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 34657f96b5a..ad24ef36f9f 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -290,9 +290,12 @@ static int dell_rfkill_set(void *data, bool blocked) dell_send_request(buffer, 17, 11); /* If the hardware switch controls this radio, and the hardware - switch is disabled, don't allow changing the software state */ + switch is disabled, don't allow changing the software state. + If the hardware switch is reported as not supported, always + fire the SMI to toggle the killswitch. */ if ((hwswitch_state & BIT(hwswitch_bit)) && - !(buffer->output[1] & BIT(16))) { + !(buffer->output[1] & BIT(16)) && + (buffer->output[1] & BIT(0))) { ret = -EINVAL; goto out; } @@ -398,6 +401,23 @@ static const struct file_operations dell_debugfs_fops = { static void dell_update_rfkill(struct work_struct *ignored) { + int status; + + get_buffer(); + dell_send_request(buffer, 17, 11); + status = buffer->output[1]; + release_buffer(); + + /* if hardware rfkill is not supported, set it explicitly */ + if (!(status & BIT(0))) { + if (wifi_rfkill) + dell_rfkill_set((void *)1, !((status & BIT(17)) >> 17)); + if (bluetooth_rfkill) + dell_rfkill_set((void *)2, !((status & BIT(18)) >> 18)); + if (wwan_rfkill) + dell_rfkill_set((void *)3, !((status & BIT(19)) >> 19)); + } + if (wifi_rfkill) dell_rfkill_query(wifi_rfkill, (void *)1); if (bluetooth_rfkill) diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 930e6276236..61433d49286 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -60,69 +60,20 @@ enum pmic_gpio_register { #define GPOSW_DOU 0x08 #define GPOSW_RDRV 0x30 +#define GPIO_UPDATE_TYPE 0x80000000 #define NUM_GPIO 24 -struct pmic_gpio_irq { - spinlock_t lock; - u32 trigger[NUM_GPIO]; - u32 dirty; - struct work_struct work; -}; - - struct pmic_gpio { + struct mutex buslock; struct gpio_chip chip; - struct pmic_gpio_irq irqtypes; void *gpiointr; int irq; unsigned irq_base; + unsigned int update_type; + u32 trigger_type; }; -static void pmic_program_irqtype(int gpio, int type) -{ - if (type & IRQ_TYPE_EDGE_RISING) - intel_scu_ipc_update_register(GPIO0 + gpio, 0x20, 0x20); - else - intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x20); - - if (type & IRQ_TYPE_EDGE_FALLING) - intel_scu_ipc_update_register(GPIO0 + gpio, 0x10, 0x10); - else - intel_scu_ipc_update_register(GPIO0 + gpio, 0x00, 0x10); -}; - -static void pmic_irqtype_work(struct work_struct *work) -{ - struct pmic_gpio_irq *t = - container_of(work, struct pmic_gpio_irq, work); - unsigned long flags; - int i; - u16 type; - - spin_lock_irqsave(&t->lock, flags); - /* As we drop the lock, we may need multiple scans if we race the - pmic_irq_type function */ - while (t->dirty) { - /* - * For each pin that has the dirty bit set send an IPC - * message to configure the hardware via the PMIC - */ - for (i = 0; i < NUM_GPIO; i++) { - if (!(t->dirty & (1 << i))) - continue; - t->dirty &= ~(1 << i); - /* We can't trust the array entry or dirty - once the lock is dropped */ - type = t->trigger[i]; - spin_unlock_irqrestore(&t->lock, flags); - pmic_program_irqtype(i, type); - spin_lock_irqsave(&t->lock, flags); - } - } - spin_unlock_irqrestore(&t->lock, flags); -} - static int pmic_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { if (offset > 8) { @@ -190,25 +141,24 @@ static void pmic_gpio_set(struct gpio_chip *chip, unsigned offset, int value) 1 << (offset - 16)); } -static int pmic_irq_type(unsigned irq, unsigned type) +/* + * This is called from genirq with pg->buslock locked and + * irq_desc->lock held. We can not access the scu bus here, so we + * store the change and update in the bus_sync_unlock() function below + */ +static int pmic_irq_type(struct irq_data *data, unsigned type) { - struct pmic_gpio *pg = get_irq_chip_data(irq); - u32 gpio = irq - pg->irq_base; - unsigned long flags; + struct pmic_gpio *pg = irq_data_get_irq_chip_data(data); + u32 gpio = data->irq - pg->irq_base; if (gpio >= pg->chip.ngpio) return -EINVAL; - spin_lock_irqsave(&pg->irqtypes.lock, flags); - pg->irqtypes.trigger[gpio] = type; - pg->irqtypes.dirty |= (1 << gpio); - spin_unlock_irqrestore(&pg->irqtypes.lock, flags); - schedule_work(&pg->irqtypes.work); + pg->trigger_type = type; + pg->update_type = gpio | GPIO_UPDATE_TYPE; return 0; } - - static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) { struct pmic_gpio *pg = container_of(chip, struct pmic_gpio, chip); @@ -217,38 +167,32 @@ static int pmic_gpio_to_irq(struct gpio_chip *chip, unsigned offset) } /* the gpiointr register is read-clear, so just do nothing. */ -static void pmic_irq_unmask(unsigned irq) -{ -}; +static void pmic_irq_unmask(struct irq_data *data) { } -static void pmic_irq_mask(unsigned irq) -{ -}; +static void pmic_irq_mask(struct irq_data *data) { } static struct irq_chip pmic_irqchip = { .name = "PMIC-GPIO", - .mask = pmic_irq_mask, - .unmask = pmic_irq_unmask, - .set_type = pmic_irq_type, + .irq_mask = pmic_irq_mask, + .irq_unmask = pmic_irq_unmask, + .irq_set_type = pmic_irq_type, }; -static void pmic_irq_handler(unsigned irq, struct irq_desc *desc) +static irqreturn_t pmic_irq_handler(int irq, void *data) { - struct pmic_gpio *pg = (struct pmic_gpio *)get_irq_data(irq); + struct pmic_gpio *pg = data; u8 intsts = *((u8 *)pg->gpiointr + 4); int gpio; + irqreturn_t ret = IRQ_NONE; for (gpio = 0; gpio < 8; gpio++) { if (intsts & (1 << gpio)) { pr_debug("pmic pin %d triggered\n", gpio); generic_handle_irq(pg->irq_base + gpio); + ret = IRQ_HANDLED; } } - - if (desc->chip->irq_eoi) - desc->chip->irq_eoi(irq_get_irq_data(irq)); - else - dev_warn(pg->chip.dev, "missing EOI handler for irq %d\n", irq); + return ret; } static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) @@ -297,8 +241,7 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) pg->chip.can_sleep = 1; pg->chip.dev = dev; - INIT_WORK(&pg->irqtypes.work, pmic_irqtype_work); - spin_lock_init(&pg->irqtypes.lock); + mutex_init(&pg->buslock); pg->chip.dev = dev; retval = gpiochip_add(&pg->chip); @@ -306,8 +249,13 @@ static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) printk(KERN_ERR "%s: Can not add pmic gpio chip.\n", __func__); goto err; } - set_irq_data(pg->irq, pg); - set_irq_chained_handler(pg->irq, pmic_irq_handler); + + retval = request_irq(pg->irq, pmic_irq_handler, 0, "pmic", pg); + if (retval) { + printk(KERN_WARNING "pmic: Interrupt request failed\n"); + goto err; + } + for (i = 0; i < 8; i++) { set_irq_chip_and_handler_name(i + pg->irq_base, &pmic_irqchip, handle_simple_irq, "demux"); diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index 1fe0f1feff7..865ef78d6f1 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -162,7 +162,7 @@ set_bool_##value(struct device *dev, struct device_attribute *attr, \ return -EINVAL; \ return count; \ } \ -static DEVICE_ATTR(value, S_IWUGO | S_IRUGO | S_IWUSR, \ +static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ show_bool_##value, set_bool_##value); show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index dd599585c6a..eb9922385ef 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -2275,16 +2275,12 @@ static void tpacpi_input_send_key(const unsigned int scancode) if (keycode != KEY_RESERVED) { mutex_lock(&tpacpi_inputdev_send_mutex); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 1); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); + input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, scancode); input_report_key(tpacpi_inputdev, keycode, 0); - if (keycode == KEY_UNKNOWN) - input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN, - scancode); input_sync(tpacpi_inputdev); mutex_unlock(&tpacpi_inputdev_send_mutex); diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index cba1b43f751..a4e8eb9fece 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -168,7 +168,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, { unsigned long flags; int captured = 0; - struct pps_ktime ts_real; + struct pps_ktime ts_real = { .sec = 0, .nsec = 0, .flags = 0 }; /* check event type */ BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index 76b41853a87..1269fbd2dec 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -77,9 +77,9 @@ rio_read_config(struct file *filp, struct kobject *kobj, /* Several chips lock up trying to read undefined config space */ if (capable(CAP_SYS_ADMIN)) - size = 0x200000; + size = RIO_MAINT_SPACE_SZ; - if (off > size) + if (off >= size) return 0; if (off + count > size) { size -= off; @@ -147,10 +147,10 @@ rio_write_config(struct file *filp, struct kobject *kobj, loff_t init_off = off; u8 *data = (u8 *) buf; - if (off > 0x200000) + if (off >= RIO_MAINT_SPACE_SZ) return 0; - if (off + count > 0x200000) { - size = 0x200000 - off; + if (off + count > RIO_MAINT_SPACE_SZ) { + size = RIO_MAINT_SPACE_SZ - off; count = size; } @@ -200,7 +200,7 @@ static struct bin_attribute rio_config_attr = { .name = "config", .mode = S_IRUGO | S_IWUSR, }, - .size = 0x200000, + .size = RIO_MAINT_SPACE_SZ, .read = rio_read_config, .write = rio_write_config, }; diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c index f53d31b950d..2bb5de1f242 100644 --- a/drivers/regulator/mc13xxx-regulator-core.c +++ b/drivers/regulator/mc13xxx-regulator-core.c @@ -174,7 +174,7 @@ static int mc13xxx_regulator_get_voltage(struct regulator_dev *rdev) dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val); - BUG_ON(val < 0 || val > mc13xxx_regulators[id].desc.n_voltages); + BUG_ON(val > mc13xxx_regulators[id].desc.n_voltages); return mc13xxx_regulators[id].voltages[val]; } diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index 8b0d2c4bde9..06df898842c 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -120,6 +120,7 @@ static unsigned int wm831x_dcdc_get_mode(struct regulator_dev *rdev) return REGULATOR_MODE_IDLE; default: BUG(); + return -EINVAL; } } diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index c36749e4c92..5469c52cba3 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -309,7 +309,7 @@ static const struct rtc_class_ops at91_rtc_ops = { .read_alarm = at91_rtc_readalarm, .set_alarm = at91_rtc_setalarm, .proc = at91_rtc_proc, - .alarm_irq_enabled = at91_rtc_alarm_irq_enable, + .alarm_irq_enable = at91_rtc_alarm_irq_enable, }; /* diff --git a/drivers/rtc/rtc-ds3232.c b/drivers/rtc/rtc-ds3232.c index 23a9ee19764..950735415a7 100644 --- a/drivers/rtc/rtc-ds3232.c +++ b/drivers/rtc/rtc-ds3232.c @@ -1,7 +1,7 @@ /* * RTC client/driver for the Maxim/Dallas DS3232 Real-Time Clock over I2C * - * Copyright (C) 2009-2010 Freescale Semiconductor. + * Copyright (C) 2009-2011 Freescale Semiconductor. * Author: Jack Lan <jack.lan@freescale.com> * * This program is free software; you can redistribute it and/or modify it @@ -141,9 +141,11 @@ static int ds3232_read_time(struct device *dev, struct rtc_time *time) time->tm_hour = bcd2bin(hour); } - time->tm_wday = bcd2bin(week); + /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ + time->tm_wday = bcd2bin(week) - 1; time->tm_mday = bcd2bin(day); - time->tm_mon = bcd2bin(month & 0x7F); + /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ + time->tm_mon = bcd2bin(month & 0x7F) - 1; if (century) add_century = 100; @@ -162,9 +164,11 @@ static int ds3232_set_time(struct device *dev, struct rtc_time *time) buf[0] = bin2bcd(time->tm_sec); buf[1] = bin2bcd(time->tm_min); buf[2] = bin2bcd(time->tm_hour); - buf[3] = bin2bcd(time->tm_wday); /* Day of the week */ + /* Day of the week in linux range is 0~6 while 1~7 in RTC chip */ + buf[3] = bin2bcd(time->tm_wday + 1); buf[4] = bin2bcd(time->tm_mday); /* Date */ - buf[5] = bin2bcd(time->tm_mon); + /* linux tm_mon range:0~11, while month range is 1~12 in RTC chip */ + buf[5] = bin2bcd(time->tm_mon + 1); if (time->tm_year >= 100) { buf[5] |= 0x80; buf[6] = bin2bcd(time->tm_year - 100); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 318672d0556..a9fe23d5bd0 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -72,7 +72,7 @@ static struct dasd_discipline dasd_eckd_discipline; static struct ccw_device_id dasd_eckd_ids[] = { { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3390, 0), .driver_info = 0x1}, { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3390, 0), .driver_info = 0x2}, - { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3390, 0), .driver_info = 0x3}, + { CCW_DEVICE_DEVTYPE (0x3880, 0, 0x3380, 0), .driver_info = 0x3}, { CCW_DEVICE_DEVTYPE (0x3990, 0, 0x3380, 0), .driver_info = 0x4}, { CCW_DEVICE_DEVTYPE (0x2105, 0, 0x3380, 0), .driver_info = 0x5}, { CCW_DEVICE_DEVTYPE (0x9343, 0, 0x9345, 0), .driver_info = 0x6}, diff --git a/drivers/scsi/cxgbi/cxgb3i/Kconfig b/drivers/scsi/cxgbi/cxgb3i/Kconfig index 5cf4e9831f1..11dff23f783 100644 --- a/drivers/scsi/cxgbi/cxgb3i/Kconfig +++ b/drivers/scsi/cxgbi/cxgb3i/Kconfig @@ -1,6 +1,8 @@ config SCSI_CXGB3_ISCSI tristate "Chelsio T3 iSCSI support" - depends on CHELSIO_T3_DEPENDS + depends on PCI && INET + select NETDEVICES + select NETDEV_10000 select CHELSIO_T3 select SCSI_ISCSI_ATTRS ---help--- diff --git a/drivers/scsi/cxgbi/cxgb4i/Kconfig b/drivers/scsi/cxgbi/cxgb4i/Kconfig index bb94b39b17b..d5302c27f37 100644 --- a/drivers/scsi/cxgbi/cxgb4i/Kconfig +++ b/drivers/scsi/cxgbi/cxgb4i/Kconfig @@ -1,6 +1,8 @@ config SCSI_CXGB4_ISCSI tristate "Chelsio T4 iSCSI support" - depends on CHELSIO_T4_DEPENDS + depends on PCI && INET + select NETDEVICES + select NETDEV_10000 select CHELSIO_T4 select SCSI_ISCSI_ATTRS ---help--- diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index d2ad3d67672..889199aa1f5 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -470,7 +470,8 @@ static struct rtable *find_route_ipv4(__be32 saddr, __be32 daddr, } }; - if (ip_route_output_flow(&init_net, &rt, &fl, NULL, 0)) + rt = ip_route_output_flow(&init_net, &fl, NULL); + if (IS_ERR(rt)) return NULL; return rt; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9045c52abd2..fb2bb35c62c 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -443,7 +443,7 @@ static void scsi_run_queue(struct request_queue *q) &sdev->request_queue->queue_flags); if (flagset) queue_flag_set(QUEUE_FLAG_REENTER, sdev->request_queue); - __blk_run_queue(sdev->request_queue); + __blk_run_queue(sdev->request_queue, false); if (flagset) queue_flag_clear(QUEUE_FLAG_REENTER, sdev->request_queue); spin_unlock(sdev->request_queue->queue_lock); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 998c01be323..5c3ccfc6b62 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3829,7 +3829,7 @@ fc_bsg_goose_queue(struct fc_rport *rport) !test_bit(QUEUE_FLAG_REENTER, &rport->rqst_q->queue_flags); if (flagset) queue_flag_set(QUEUE_FLAG_REENTER, rport->rqst_q); - __blk_run_queue(rport->rqst_q); + __blk_run_queue(rport->rqst_q, false); if (flagset) queue_flag_clear(QUEUE_FLAG_REENTER, rport->rqst_q); spin_unlock_irqrestore(rport->rqst_q->queue_lock, flags); diff --git a/drivers/staging/brcm80211/sys/wl_mac80211.c b/drivers/staging/brcm80211/sys/wl_mac80211.c index cd8392badff..6363077468f 100644 --- a/drivers/staging/brcm80211/sys/wl_mac80211.c +++ b/drivers/staging/brcm80211/sys/wl_mac80211.c @@ -104,9 +104,6 @@ static int wl_request_fw(struct wl_info *wl, struct pci_dev *pdev); static void wl_release_fw(struct wl_info *wl); /* local prototypes */ -static int wl_start(struct sk_buff *skb, struct wl_info *wl); -static int wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, - struct sk_buff *skb); static void wl_dpc(unsigned long data); MODULE_AUTHOR("Broadcom Corporation"); @@ -135,7 +132,6 @@ module_param(phymsglevel, int, 0); #define HW_TO_WL(hw) (hw->priv) #define WL_TO_HW(wl) (wl->pub->ieee_hw) -static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb); static int wl_ops_start(struct ieee80211_hw *hw); static void wl_ops_stop(struct ieee80211_hw *hw); static int wl_ops_add_interface(struct ieee80211_hw *hw, @@ -173,20 +169,18 @@ static int wl_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action, struct ieee80211_sta *sta, u16 tid, u16 *ssn); -static int wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) +static void wl_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb) { - int status; struct wl_info *wl = hw->priv; WL_LOCK(wl); if (!wl->pub->up) { WL_ERROR("ops->tx called while down\n"); - status = -ENETDOWN; + kfree_skb(skb); goto done; } - status = wl_start(skb, wl); + wlc_sendpkt_mac80211(wl->wlc, skb, hw); done: WL_UNLOCK(wl); - return status; } static int wl_ops_start(struct ieee80211_hw *hw) @@ -1325,22 +1319,6 @@ void wl_free(struct wl_info *wl) osl_detach(osh); } -/* transmit a packet */ -static int BCMFASTPATH wl_start(struct sk_buff *skb, struct wl_info *wl) -{ - if (!wl) - return -ENETDOWN; - - return wl_start_int(wl, WL_TO_HW(wl), skb); -} - -static int BCMFASTPATH -wl_start_int(struct wl_info *wl, struct ieee80211_hw *hw, struct sk_buff *skb) -{ - wlc_sendpkt_mac80211(wl->wlc, skb, hw); - return NETDEV_TX_OK; -} - void wl_txflowcontrol(struct wl_info *wl, struct wl_if *wlif, bool state, int prio) { diff --git a/drivers/staging/brcm80211/sys/wlc_mac80211.c b/drivers/staging/brcm80211/sys/wlc_mac80211.c index e37e8058e2b..aa12d1a6518 100644 --- a/drivers/staging/brcm80211/sys/wlc_mac80211.c +++ b/drivers/staging/brcm80211/sys/wlc_mac80211.c @@ -6818,11 +6818,14 @@ prep_mac80211_status(struct wlc_info *wlc, d11rxhdr_t *rxh, struct sk_buff *p, ratespec_t rspec; unsigned char *plcp; +#if 0 + /* Clearly, this is bogus -- reading the TSF now is wrong */ wlc_read_tsf(wlc, &tsf_l, &tsf_h); /* mactime */ rx_status->mactime = tsf_h; rx_status->mactime <<= 32; rx_status->mactime |= tsf_l; - rx_status->flag |= RX_FLAG_TSFT; + rx_status->flag |= RX_FLAG_MACTIME_MPDU; /* clearly wrong */ +#endif channel = WLC_CHAN_CHANNEL(rxh->RxChan); diff --git a/drivers/staging/pohmelfs/config.c b/drivers/staging/pohmelfs/config.c index 89279ba1b73..39413b7d387 100644 --- a/drivers/staging/pohmelfs/config.c +++ b/drivers/staging/pohmelfs/config.c @@ -525,7 +525,7 @@ static void pohmelfs_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *n { int err; - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; switch (msg->flags) { diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c index 2163d60c2ea..3724e1e67ec 100644 --- a/drivers/staging/winbond/wbusb.c +++ b/drivers/staging/winbond/wbusb.c @@ -118,13 +118,14 @@ static void wbsoft_configure_filter(struct ieee80211_hw *dev, *total_flags = new_flags; } -static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) +static void wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) { struct wbsoft_priv *priv = dev->priv; if (priv->sMlmeFrame.IsInUsed != PACKET_FREE_TO_USE) { priv->sMlmeFrame.wNumTxMMPDUDiscarded++; - return NETDEV_TX_BUSY; + kfree_skb(skb); + return; } priv->sMlmeFrame.IsInUsed = PACKET_COME_FROM_MLME; @@ -140,8 +141,6 @@ static int wbsoft_tx(struct ieee80211_hw *dev, struct sk_buff *skb) */ Mds_Tx(priv); - - return NETDEV_TX_OK; } static int wbsoft_start(struct ieee80211_hw *dev) diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig index f7a5dba3ca2..bf7c687519e 100644 --- a/drivers/thermal/Kconfig +++ b/drivers/thermal/Kconfig @@ -4,7 +4,6 @@ menuconfig THERMAL tristate "Generic Thermal sysfs driver" - depends on NET help Generic Thermal Sysfs driver offers a generic mechanism for thermal management. Usually it's made up of one or more thermal diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c index 7d0e63c7928..713b7ea4a60 100644 --- a/drivers/thermal/thermal_sys.c +++ b/drivers/thermal/thermal_sys.c @@ -62,20 +62,6 @@ static DEFINE_MUTEX(thermal_list_lock); static unsigned int thermal_event_seqnum; -static struct genl_family thermal_event_genl_family = { - .id = GENL_ID_GENERATE, - .name = THERMAL_GENL_FAMILY_NAME, - .version = THERMAL_GENL_VERSION, - .maxattr = THERMAL_GENL_ATTR_MAX, -}; - -static struct genl_multicast_group thermal_event_mcgrp = { - .name = THERMAL_GENL_MCAST_GROUP_NAME, -}; - -static int genetlink_init(void); -static void genetlink_exit(void); - static int get_idr(struct idr *idr, struct mutex *lock, int *id) { int err; @@ -1225,6 +1211,18 @@ void thermal_zone_device_unregister(struct thermal_zone_device *tz) EXPORT_SYMBOL(thermal_zone_device_unregister); +#ifdef CONFIG_NET +static struct genl_family thermal_event_genl_family = { + .id = GENL_ID_GENERATE, + .name = THERMAL_GENL_FAMILY_NAME, + .version = THERMAL_GENL_VERSION, + .maxattr = THERMAL_GENL_ATTR_MAX, +}; + +static struct genl_multicast_group thermal_event_mcgrp = { + .name = THERMAL_GENL_MCAST_GROUP_NAME, +}; + int generate_netlink_event(u32 orig, enum events event) { struct sk_buff *skb; @@ -1301,6 +1299,15 @@ static int genetlink_init(void) return result; } +static void genetlink_exit(void) +{ + genl_unregister_family(&thermal_event_genl_family); +} +#else /* !CONFIG_NET */ +static inline int genetlink_init(void) { return 0; } +static inline void genetlink_exit(void) {} +#endif /* !CONFIG_NET */ + static int __init thermal_init(void) { int result = 0; @@ -1316,11 +1323,6 @@ static int __init thermal_init(void) return result; } -static void genetlink_exit(void) -{ - genl_unregister_family(&thermal_event_genl_family); -} - static void __exit thermal_exit(void) { class_unregister(&thermal_class); diff --git a/drivers/tty/serial/serial_cs.c b/drivers/tty/serial/serial_cs.c index 93760b2ea17..1ef4df9bf7e 100644 --- a/drivers/tty/serial/serial_cs.c +++ b/drivers/tty/serial/serial_cs.c @@ -712,6 +712,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_PFC_DEVICE_PROD_ID12(1, "Xircom", "CreditCard Ethernet+Modem II", 0x2e3ee845, 0xeca401bf), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0e01), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0a05), + PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x0b05), PCMCIA_PFC_DEVICE_MANF_CARD(1, 0x0032, 0x1101), PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x0104, 0x0070), PCMCIA_MFC_DEVICE_MANF_CARD(1, 0x0101, 0x0562), diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d041c6826e4..0f299b7aad6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2681,17 +2681,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, mutex_lock(&usb_address0_mutex); - if (!udev->config && oldspeed == USB_SPEED_SUPER) { - /* Don't reset USB 3.0 devices during an initial setup */ - usb_set_device_state(udev, USB_STATE_DEFAULT); - } else { - /* Reset the device; full speed may morph to high speed */ - /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ - retval = hub_port_reset(hub, port1, udev, delay); - if (retval < 0) /* error or disconnect */ - goto fail; - /* success, speed is known */ - } + /* Reset the device; full speed may morph to high speed */ + /* FIXME a USB 2.0 device may morph into SuperSpeed on reset. */ + retval = hub_port_reset(hub, port1, udev, delay); + if (retval < 0) /* error or disconnect */ + goto fail; + /* success, speed is known */ + retval = -ENODEV; if (oldspeed != USB_SPEED_UNKNOWN && oldspeed != udev->speed) { diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 44c595432d6..81ce6a8e1d9 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -48,6 +48,10 @@ static const struct usb_device_id usb_quirk_list[] = { { USB_DEVICE(0x04b4, 0x0526), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Samsung Android phone modem - ID conflict with SPH-I500 */ + { USB_DEVICE(0x04e8, 0x6601), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* Roland SC-8820 */ { USB_DEVICE(0x0582, 0x0007), .driver_info = USB_QUIRK_RESET_RESUME }, @@ -68,6 +72,10 @@ static const struct usb_device_id usb_quirk_list[] = { /* M-Systems Flash Disk Pioneers */ { USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME }, + /* Keytouch QWERTY Panel keyboard */ + { USB_DEVICE(0x0926, 0x3333), .driver_info = + USB_QUIRK_CONFIG_INTF_STRINGS }, + /* X-Rite/Gretag-Macbeth Eye-One Pro display colorimeter */ { USB_DEVICE(0x0971, 0x2000), .driver_info = USB_QUIRK_NO_SET_INTF }, diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c index 3c6e1a05874..5e1495097ec 100644 --- a/drivers/usb/gadget/f_phonet.c +++ b/drivers/usb/gadget/f_phonet.c @@ -346,14 +346,19 @@ static void pn_rx_complete(struct usb_ep *ep, struct usb_request *req) if (unlikely(!skb)) break; - skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, 0, - req->actual); - page = NULL; - if (req->actual < req->length) { /* Last fragment */ + if (skb->len == 0) { /* First fragment */ skb->protocol = htons(ETH_P_PHONET); skb_reset_mac_header(skb); - pskb_pull(skb, 1); + /* Can't use pskb_pull() on page in IRQ */ + memcpy(skb_put(skb, 1), page_address(page), 1); + } + + skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, + skb->len == 0, req->actual); + page = NULL; + + if (req->actual < req->length) { /* Last fragment */ skb->dev = dev; dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index e8f4f36fdf0..a6f21b891f6 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -29,6 +29,7 @@ #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/of_address.h> /** * ehci_xilinx_of_setup - Initialize the device for ehci_reset() diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index fcbf4abbf38..0231814a97a 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -169,9 +169,10 @@ static void xhci_print_ports(struct xhci_hcd *xhci) } } -void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num) +void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num) { - void *addr; + struct xhci_intr_reg __iomem *ir_set = &xhci->run_regs->ir_set[set_num]; + void __iomem *addr; u32 temp; u64 temp_64; @@ -449,7 +450,7 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, } } -void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) +static void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) { /* Fields are 32 bits wide, DMA addresses are in bytes */ int field_size = 32 / 8; @@ -488,7 +489,7 @@ void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx) dbg_rsvd64(xhci, (u64 *)slot_ctx, dma); } -void xhci_dbg_ep_ctx(struct xhci_hcd *xhci, +static void xhci_dbg_ep_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int last_ep) { diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 1d0f45f0e7a..a9534396e85 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -307,7 +307,7 @@ struct xhci_ep_ctx *xhci_get_ep_ctx(struct xhci_hcd *xhci, /***************** Streams structures manipulation *************************/ -void xhci_free_stream_ctx(struct xhci_hcd *xhci, +static void xhci_free_stream_ctx(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, struct xhci_stream_ctx *stream_ctx, dma_addr_t dma) { @@ -335,7 +335,7 @@ void xhci_free_stream_ctx(struct xhci_hcd *xhci, * The stream context array must be a power of 2, and can be as small as * 64 bytes or as large as 1MB. */ -struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, +static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci, unsigned int num_stream_ctxs, dma_addr_t *dma, gfp_t mem_flags) { @@ -1900,11 +1900,11 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) val &= DBOFF_MASK; xhci_dbg(xhci, "// Doorbell array is located at offset 0x%x" " from cap regs base addr\n", val); - xhci->dba = (void *) xhci->cap_regs + val; + xhci->dba = (void __iomem *) xhci->cap_regs + val; xhci_dbg_regs(xhci); xhci_print_run_regs(xhci); /* Set ir_set to interrupt register set 0 */ - xhci->ir_set = (void *) xhci->run_regs->ir_set; + xhci->ir_set = &xhci->run_regs->ir_set[0]; /* * Event ring setup: Allocate a normal ring, but also setup @@ -1961,7 +1961,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* Set the event ring dequeue address */ xhci_set_hc_event_deq(xhci); xhci_dbg(xhci, "Wrote ERST address to ir_set 0.\n"); - xhci_print_ir_set(xhci, xhci->ir_set, 0); + xhci_print_ir_set(xhci, 0); /* * XXX: Might need to set the Interrupter Moderation Register to diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3e8211c1ce5..3289bf4832c 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -474,8 +474,11 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, state->new_deq_seg = find_trb_seg(cur_td->start_seg, dev->eps[ep_index].stopped_trb, &state->new_cycle_state); - if (!state->new_deq_seg) - BUG(); + if (!state->new_deq_seg) { + WARN_ON(1); + return; + } + /* Dig out the cycle state saved by the xHC during the stop ep cmd */ xhci_dbg(xhci, "Finding endpoint context\n"); ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); @@ -486,8 +489,10 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, state->new_deq_seg = find_trb_seg(state->new_deq_seg, state->new_deq_ptr, &state->new_cycle_state); - if (!state->new_deq_seg) - BUG(); + if (!state->new_deq_seg) { + WARN_ON(1); + return; + } trb = &state->new_deq_ptr->generic; if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) && @@ -2363,12 +2368,13 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) /* Scatter gather list entries may cross 64KB boundaries */ running_total = TRB_MAX_BUFF_SIZE - - (sg_dma_address(sg) & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + (sg_dma_address(sg) & (TRB_MAX_BUFF_SIZE - 1)); + running_total &= TRB_MAX_BUFF_SIZE - 1; if (running_total != 0) num_trbs++; /* How many more 64KB chunks to transfer, how many more TRBs? */ - while (running_total < sg_dma_len(sg)) { + while (running_total < sg_dma_len(sg) && running_total < temp) { num_trbs++; running_total += TRB_MAX_BUFF_SIZE; } @@ -2394,11 +2400,11 @@ static unsigned int count_sg_trbs_needed(struct xhci_hcd *xhci, struct urb *urb) static void check_trb_math(struct urb *urb, int num_trbs, int running_total) { if (num_trbs != 0) - dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated number of " + dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated number of " "TRBs, %d left\n", __func__, urb->ep->desc.bEndpointAddress, num_trbs); if (running_total != urb->transfer_buffer_length) - dev_dbg(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, " + dev_err(&urb->dev->dev, "%s - ep %#x - Miscalculated tx length, " "queued %#x (%d), asked for %#x (%d)\n", __func__, urb->ep->desc.bEndpointAddress, @@ -2533,8 +2539,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, sg = urb->sg; addr = (u64) sg_dma_address(sg); this_sg_len = sg_dma_len(sg); - trb_buff_len = TRB_MAX_BUFF_SIZE - - (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + trb_buff_len = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1)); trb_buff_len = min_t(int, trb_buff_len, this_sg_len); if (trb_buff_len > urb->transfer_buffer_length) trb_buff_len = urb->transfer_buffer_length; @@ -2572,7 +2577,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), (unsigned int) addr + trb_buff_len); if (TRB_MAX_BUFF_SIZE - - (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)) < trb_buff_len) { + (addr & (TRB_MAX_BUFF_SIZE - 1)) < trb_buff_len) { xhci_warn(xhci, "WARN: sg dma xfer crosses 64KB boundaries!\n"); xhci_dbg(xhci, "Next boundary at %#x, end dma = %#x\n", (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), @@ -2616,7 +2621,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } trb_buff_len = TRB_MAX_BUFF_SIZE - - (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + (addr & (TRB_MAX_BUFF_SIZE - 1)); trb_buff_len = min_t(int, trb_buff_len, this_sg_len); if (running_total + trb_buff_len > urb->transfer_buffer_length) trb_buff_len = @@ -2656,7 +2661,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = 0; /* How much data is (potentially) left before the 64KB boundary? */ running_total = TRB_MAX_BUFF_SIZE - - (urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + (urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1)); + running_total &= TRB_MAX_BUFF_SIZE - 1; /* If there's some data on this 64KB chunk, or we have to send a * zero-length transfer, we need at least one TRB @@ -2700,8 +2706,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - - (urb->transfer_dma & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); - if (urb->transfer_buffer_length < trb_buff_len) + (urb->transfer_dma & (TRB_MAX_BUFF_SIZE - 1)); + if (trb_buff_len > urb->transfer_buffer_length) trb_buff_len = urb->transfer_buffer_length; first_trb = true; @@ -2879,8 +2885,8 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci, addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset); td_len = urb->iso_frame_desc[i].length; - running_total = TRB_MAX_BUFF_SIZE - - (addr & ((1 << TRB_MAX_BUFF_SHIFT) - 1)); + running_total = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1)); + running_total &= TRB_MAX_BUFF_SIZE - 1; if (running_total != 0) num_trbs++; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 34cf4e16587..2083fc2179b 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -109,7 +109,7 @@ int xhci_halt(struct xhci_hcd *xhci) /* * Set the run bit and wait for the host to be running. */ -int xhci_start(struct xhci_hcd *xhci) +static int xhci_start(struct xhci_hcd *xhci) { u32 temp; int ret; @@ -329,7 +329,7 @@ int xhci_init(struct usb_hcd *hcd) #ifdef CONFIG_USB_XHCI_HCD_DEBUGGING -void xhci_event_ring_work(unsigned long arg) +static void xhci_event_ring_work(unsigned long arg) { unsigned long flags; int temp; @@ -473,7 +473,7 @@ int xhci_run(struct usb_hcd *hcd) xhci->ir_set, (unsigned int) ER_IRQ_ENABLE(temp)); xhci_writel(xhci, ER_IRQ_ENABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, xhci->ir_set, 0); + xhci_print_ir_set(xhci, 0); if (NUM_TEST_NOOPS > 0) doorbell = xhci_setup_one_noop(xhci); @@ -528,7 +528,7 @@ void xhci_stop(struct usb_hcd *hcd) temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_writel(xhci, ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, xhci->ir_set, 0); + xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -755,7 +755,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) temp = xhci_readl(xhci, &xhci->ir_set->irq_pending); xhci_writel(xhci, ER_IRQ_DISABLE(temp), &xhci->ir_set->irq_pending); - xhci_print_ir_set(xhci, xhci->ir_set, 0); + xhci_print_ir_set(xhci, 0); xhci_dbg(xhci, "cleaning up memory\n"); xhci_mem_cleanup(xhci); @@ -857,7 +857,7 @@ unsigned int xhci_last_valid_endpoint(u32 added_ctxs) /* Returns 1 if the arguments are OK; * returns 0 this is a root hub; returns -EINVAL for NULL pointers. */ -int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, +static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep, int check_ep, bool check_virt_dev, const char *func) { struct xhci_hcd *xhci; @@ -1693,7 +1693,7 @@ static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags)); } -void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci, +static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, struct xhci_dequeue_state *deq_state) { diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 7f236fd2201..7f127df6dd5 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1348,7 +1348,7 @@ static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) } /* xHCI debugging */ -void xhci_print_ir_set(struct xhci_hcd *xhci, struct xhci_intr_reg *ir_set, int set_num); +void xhci_print_ir_set(struct xhci_hcd *xhci, int set_num); void xhci_print_registers(struct xhci_hcd *xhci); void xhci_dbg_regs(struct xhci_hcd *xhci); void xhci_print_run_regs(struct xhci_hcd *xhci); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 54a8bd1047d..c292d5c499e 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1864,6 +1864,7 @@ allocate_instance(struct device *dev, INIT_LIST_HEAD(&musb->out_bulk); hcd->uses_new_polling = 1; + hcd->has_tt = 1; musb->vbuserr_retry = VBUSERR_RETRY_COUNT; musb->a_wait_bcon = OTG_TIME_A_WAIT_BCON; diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index d74a8113ae7..e6400be8a0f 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -488,6 +488,15 @@ struct musb { unsigned set_address:1; unsigned test_mode:1; unsigned softconnect:1; + + u8 address; + u8 test_mode_nr; + u16 ackpend; /* ep0 */ + enum musb_g_ep0_state ep0_state; + struct usb_gadget g; /* the gadget */ + struct usb_gadget_driver *gadget_driver; /* its driver */ +#endif + /* * FIXME: Remove this flag. * @@ -501,14 +510,6 @@ struct musb { */ unsigned double_buffer_not_ok:1 __deprecated; - u8 address; - u8 test_mode_nr; - u16 ackpend; /* ep0 */ - enum musb_g_ep0_state ep0_state; - struct usb_gadget g; /* the gadget */ - struct usb_gadget_driver *gadget_driver; /* its driver */ -#endif - struct musb_hdrc_config *config; #ifdef MUSB_CONFIG_PROC_FS diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index a3f12333fc4..bc8badd1689 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -362,6 +362,7 @@ static int omap2430_musb_init(struct musb *musb) static int omap2430_musb_exit(struct musb *musb) { + del_timer_sync(&musb_idle_timer); omap2430_low_level_exit(musb); otg_put_transceiver(musb->xceiv); diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 7481ff8a49e..0457813eebe 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -301,6 +301,9 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless Direct IP modems */ .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist }, + { USB_DEVICE(0x0f3d, 0x68A3), /* Airprime/Sierra Wireless Direct IP modems */ + .driver_info = (kernel_ulong_t)&direct_ip_interface_blacklist + }, { USB_DEVICE(0x413C, 0x08133) }, /* Dell Computer Corp. Wireless 5720 VZW Mobile Broadband (EVDO Rev-A) Minicard GPS Port */ { } diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index b004b2a485c..9c014e2ecd6 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -295,12 +295,15 @@ static void usb_wwan_indat_callback(struct urb *urb) __func__, status, endpoint); } else { tty = tty_port_tty_get(&port->port); - if (urb->actual_length) { - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); - } else - dbg("%s: empty read urb received", __func__); - tty_kref_put(tty); + if (tty) { + if (urb->actual_length) { + tty_insert_flip_string(tty, data, + urb->actual_length); + tty_flip_buffer_push(tty); + } else + dbg("%s: empty read urb received", __func__); + tty_kref_put(tty); + } /* Resubmit urb so we continue receiving */ if (status != -ESHUTDOWN) { diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 15a5d89b7f3..1c11959a7d5 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -27,6 +27,7 @@ #include <linux/uaccess.h> #include <linux/usb.h> #include <linux/usb/serial.h> +#include <linux/usb/cdc.h> #include "visor.h" /* @@ -479,6 +480,17 @@ static int visor_probe(struct usb_serial *serial, dbg("%s", __func__); + /* + * some Samsung Android phones in modem mode have the same ID + * as SPH-I500, but they are ACM devices, so dont bind to them + */ + if (id->idVendor == SAMSUNG_VENDOR_ID && + id->idProduct == SAMSUNG_SPH_I500_ID && + serial->dev->descriptor.bDeviceClass == USB_CLASS_COMM && + serial->dev->descriptor.bDeviceSubClass == + USB_CDC_SUBCLASS_ACM) + return -ENODEV; + if (serial->dev->actconfig->desc.bConfigurationValue != 1) { dev_err(&serial->dev->dev, "active config #%d != 1 ??\n", serial->dev->actconfig->desc.bConfigurationValue); diff --git a/drivers/video/uvesafb.c b/drivers/video/uvesafb.c index 52ec0959d46..5180a215d78 100644 --- a/drivers/video/uvesafb.c +++ b/drivers/video/uvesafb.c @@ -73,7 +73,7 @@ static void uvesafb_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *ns struct uvesafb_task *utask; struct uvesafb_ktask *task; - if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) + if (!cap_raised(current_cap(), CAP_SYS_ADMIN)) return; if (msg->seq >= UVESAFB_TASKS_MAX) diff --git a/fs/afs/write.c b/fs/afs/write.c index 15690bb1d3b..789b3afb342 100644 --- a/fs/afs/write.c +++ b/fs/afs/write.c @@ -140,6 +140,7 @@ int afs_write_begin(struct file *file, struct address_space *mapping, candidate->first = candidate->last = index; candidate->offset_first = from; candidate->to_last = to; + INIT_LIST_HEAD(&candidate->link); candidate->usage = 1; candidate->state = AFS_WBACK_PENDING; init_waitqueue_head(&candidate->waitq); @@ -239,15 +239,23 @@ static void __put_ioctx(struct kioctx *ctx) call_rcu(&ctx->rcu_head, ctx_rcu_free); } -#define get_ioctx(kioctx) do { \ - BUG_ON(atomic_read(&(kioctx)->users) <= 0); \ - atomic_inc(&(kioctx)->users); \ -} while (0) -#define put_ioctx(kioctx) do { \ - BUG_ON(atomic_read(&(kioctx)->users) <= 0); \ - if (unlikely(atomic_dec_and_test(&(kioctx)->users))) \ - __put_ioctx(kioctx); \ -} while (0) +static inline void get_ioctx(struct kioctx *kioctx) +{ + BUG_ON(atomic_read(&kioctx->users) <= 0); + atomic_inc(&kioctx->users); +} + +static inline int try_get_ioctx(struct kioctx *kioctx) +{ + return atomic_inc_not_zero(&kioctx->users); +} + +static inline void put_ioctx(struct kioctx *kioctx) +{ + BUG_ON(atomic_read(&kioctx->users) <= 0); + if (unlikely(atomic_dec_and_test(&kioctx->users))) + __put_ioctx(kioctx); +} /* ioctx_alloc * Allocates and initializes an ioctx. Returns an ERR_PTR if it failed. @@ -601,8 +609,13 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) rcu_read_lock(); hlist_for_each_entry_rcu(ctx, n, &mm->ioctx_list, list) { - if (ctx->user_id == ctx_id && !ctx->dead) { - get_ioctx(ctx); + /* + * RCU protects us against accessing freed memory but + * we have to be careful not to get a reference when the + * reference count already dropped to 0 (ctx->dead test + * is unreliable because of races). + */ + if (ctx->user_id == ctx_id && !ctx->dead && try_get_ioctx(ctx)){ ret = ctx; break; } @@ -1629,6 +1642,23 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb, goto out_put_req; spin_lock_irq(&ctx->ctx_lock); + /* + * We could have raced with io_destroy() and are currently holding a + * reference to ctx which should be destroyed. We cannot submit IO + * since ctx gets freed as soon as io_submit() puts its reference. The + * check here is reliable: io_destroy() sets ctx->dead before waiting + * for outstanding IO and the barrier between these two is realized by + * unlock of mm->ioctx_lock and lock of ctx->ctx_lock. Analogously we + * increment ctx->reqs_active before checking for ctx->dead and the + * barrier is realized by unlock and lock of ctx->ctx_lock. Thus if we + * don't see ctx->dead set here, io_destroy() waits for our IO to + * finish. + */ + if (ctx->dead) { + spin_unlock_irq(&ctx->ctx_lock); + ret = -EINVAL; + goto out_put_req; + } aio_run_iocb(req); if (!list_empty(&ctx->run_list)) { /* drain the run list */ diff --git a/fs/block_dev.c b/fs/block_dev.c index 4fb8a343153..88928701959 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -873,6 +873,11 @@ int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); if (ret) goto out_del; + /* + * bdev could be deleted beneath us which would implicitly destroy + * the holder directory. Hold on to it. + */ + kobject_get(bdev->bd_part->holder_dir); list_add(&holder->list, &bdev->bd_holder_disks); goto out_unlock; @@ -909,6 +914,7 @@ void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk) del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); del_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); + kobject_put(bdev->bd_part->holder_dir); list_del_init(&holder->list); kfree(holder); } @@ -922,14 +928,15 @@ EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); * flush_disk - invalidates all buffer-cache entries on a disk * * @bdev: struct block device to be flushed + * @kill_dirty: flag to guide handling of dirty inodes * * Invalidates all buffer-cache entries on a disk. It should be called * when a disk has been changed -- either by a media change or online * resize. */ -static void flush_disk(struct block_device *bdev) +static void flush_disk(struct block_device *bdev, bool kill_dirty) { - if (__invalidate_device(bdev)) { + if (__invalidate_device(bdev, kill_dirty)) { char name[BDEVNAME_SIZE] = ""; if (bdev->bd_disk) @@ -966,7 +973,7 @@ void check_disk_size_change(struct gendisk *disk, struct block_device *bdev) "%s: detected capacity change from %lld to %lld\n", name, bdev_size, disk_size); i_size_write(bdev->bd_inode, disk_size); - flush_disk(bdev); + flush_disk(bdev, false); } } EXPORT_SYMBOL(check_disk_size_change); @@ -1019,7 +1026,7 @@ int check_disk_change(struct block_device *bdev) if (!(events & DISK_EVENT_MEDIA_CHANGE)) return 0; - flush_disk(bdev); + flush_disk(bdev, true); if (bdops->revalidate_disk) bdops->revalidate_disk(bdev->bd_disk); return 1; @@ -1600,7 +1607,7 @@ fail: } EXPORT_SYMBOL(lookup_bdev); -int __invalidate_device(struct block_device *bdev) +int __invalidate_device(struct block_device *bdev, bool kill_dirty) { struct super_block *sb = get_super(bdev); int res = 0; @@ -1613,7 +1620,7 @@ int __invalidate_device(struct block_device *bdev) * hold). */ shrink_dcache_sb(sb); - res = invalidate_inodes(sb); + res = invalidate_inodes(sb, kill_dirty); drop_super(sb); } invalidate_bdev(bdev); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 2c98b3af605..6f820fa23df 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1254,6 +1254,7 @@ struct btrfs_root { #define BTRFS_MOUNT_SPACE_CACHE (1 << 12) #define BTRFS_MOUNT_CLEAR_CACHE (1 << 13) #define BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED (1 << 14) +#define BTRFS_MOUNT_ENOSPC_DEBUG (1 << 15) #define btrfs_clear_opt(o, opt) ((o) &= ~BTRFS_MOUNT_##opt) #define btrfs_set_opt(o, opt) ((o) |= BTRFS_MOUNT_##opt) @@ -2218,6 +2219,8 @@ int btrfs_error_unpin_extent_range(struct btrfs_root *root, u64 start, u64 end); int btrfs_error_discard_extent(struct btrfs_root *root, u64 bytenr, u64 num_bytes); +int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 type); /* ctree.c */ int btrfs_bin_search(struct extent_buffer *eb, struct btrfs_key *key, diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index f3c96fc0143..588ff984987 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -5376,7 +5376,7 @@ again: num_bytes, data, 1); goto again; } - if (ret == -ENOSPC) { + if (ret == -ENOSPC && btrfs_test_opt(root, ENOSPC_DEBUG)) { struct btrfs_space_info *sinfo; sinfo = __find_space_info(root->fs_info, data); @@ -8065,6 +8065,13 @@ out: return ret; } +int btrfs_force_chunk_alloc(struct btrfs_trans_handle *trans, + struct btrfs_root *root, u64 type) +{ + u64 alloc_flags = get_alloc_profile(root, type); + return do_chunk_alloc(trans, root, 2 * 1024 * 1024, alloc_flags, 1); +} + /* * helper to account the unused space of all the readonly block group in the * list. takes mirrors into account. diff --git a/fs/btrfs/extent_io.c b/fs/btrfs/extent_io.c index 92ac5192c51..fd3f172e94e 100644 --- a/fs/btrfs/extent_io.c +++ b/fs/btrfs/extent_io.c @@ -1433,12 +1433,13 @@ int extent_clear_unlock_delalloc(struct inode *inode, */ u64 count_range_bits(struct extent_io_tree *tree, u64 *start, u64 search_end, u64 max_bytes, - unsigned long bits) + unsigned long bits, int contig) { struct rb_node *node; struct extent_state *state; u64 cur_start = *start; u64 total_bytes = 0; + u64 last = 0; int found = 0; if (search_end <= cur_start) { @@ -1463,7 +1464,9 @@ u64 count_range_bits(struct extent_io_tree *tree, state = rb_entry(node, struct extent_state, rb_node); if (state->start > search_end) break; - if (state->end >= cur_start && (state->state & bits)) { + if (contig && found && state->start > last + 1) + break; + if (state->end >= cur_start && (state->state & bits) == bits) { total_bytes += min(search_end, state->end) + 1 - max(cur_start, state->start); if (total_bytes >= max_bytes) @@ -1472,6 +1475,9 @@ u64 count_range_bits(struct extent_io_tree *tree, *start = state->start; found = 1; } + last = state->end; + } else if (contig && found) { + break; } node = rb_next(node); if (!node) @@ -2912,6 +2918,46 @@ out: return sector; } +/* + * helper function for fiemap, which doesn't want to see any holes. + * This maps until we find something past 'last' + */ +static struct extent_map *get_extent_skip_holes(struct inode *inode, + u64 offset, + u64 last, + get_extent_t *get_extent) +{ + u64 sectorsize = BTRFS_I(inode)->root->sectorsize; + struct extent_map *em; + u64 len; + + if (offset >= last) + return NULL; + + while(1) { + len = last - offset; + if (len == 0) + break; + len = (len + sectorsize - 1) & ~(sectorsize - 1); + em = get_extent(inode, NULL, 0, offset, len, 0); + if (!em || IS_ERR(em)) + return em; + + /* if this isn't a hole return it */ + if (!test_bit(EXTENT_FLAG_VACANCY, &em->flags) && + em->block_start != EXTENT_MAP_HOLE) { + return em; + } + + /* this is a hole, advance to the next extent */ + offset = extent_map_end(em); + free_extent_map(em); + if (offset >= last) + break; + } + return NULL; +} + int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len, get_extent_t *get_extent) { @@ -2921,16 +2967,19 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, u32 flags = 0; u32 found_type; u64 last; + u64 last_for_get_extent = 0; u64 disko = 0; + u64 isize = i_size_read(inode); struct btrfs_key found_key; struct extent_map *em = NULL; struct extent_state *cached_state = NULL; struct btrfs_path *path; struct btrfs_file_extent_item *item; int end = 0; - u64 em_start = 0, em_len = 0; + u64 em_start = 0; + u64 em_len = 0; + u64 em_end = 0; unsigned long emflags; - int hole = 0; if (len == 0) return -EINVAL; @@ -2940,6 +2989,10 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, return -ENOMEM; path->leave_spinning = 1; + /* + * lookup the last file extent. We're not using i_size here + * because there might be preallocation past i_size + */ ret = btrfs_lookup_file_extent(NULL, BTRFS_I(inode)->root, path, inode->i_ino, -1, 0); if (ret < 0) { @@ -2953,18 +3006,38 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, btrfs_item_key_to_cpu(path->nodes[0], &found_key, path->slots[0]); found_type = btrfs_key_type(&found_key); - /* No extents, just return */ + /* No extents, but there might be delalloc bits */ if (found_key.objectid != inode->i_ino || found_type != BTRFS_EXTENT_DATA_KEY) { - btrfs_free_path(path); - return 0; + /* have to trust i_size as the end */ + last = (u64)-1; + last_for_get_extent = isize; + } else { + /* + * remember the start of the last extent. There are a + * bunch of different factors that go into the length of the + * extent, so its much less complex to remember where it started + */ + last = found_key.offset; + last_for_get_extent = last + 1; } - last = found_key.offset; btrfs_free_path(path); + /* + * we might have some extents allocated but more delalloc past those + * extents. so, we trust isize unless the start of the last extent is + * beyond isize + */ + if (last < isize) { + last = (u64)-1; + last_for_get_extent = isize; + } + lock_extent_bits(&BTRFS_I(inode)->io_tree, start, start + len, 0, &cached_state, GFP_NOFS); - em = get_extent(inode, NULL, 0, off, max - off, 0); + + em = get_extent_skip_holes(inode, off, last_for_get_extent, + get_extent); if (!em) goto out; if (IS_ERR(em)) { @@ -2973,19 +3046,14 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, } while (!end) { - hole = 0; - off = em->start + em->len; + off = extent_map_end(em); if (off >= max) end = 1; - if (em->block_start == EXTENT_MAP_HOLE) { - hole = 1; - goto next; - } - em_start = em->start; em_len = em->len; - + em_end = extent_map_end(em); + emflags = em->flags; disko = 0; flags = 0; @@ -3004,37 +3072,29 @@ int extent_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, if (test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)) flags |= FIEMAP_EXTENT_ENCODED; -next: - emflags = em->flags; free_extent_map(em); em = NULL; - if (!end) { - em = get_extent(inode, NULL, 0, off, max - off, 0); - if (!em) - goto out; - if (IS_ERR(em)) { - ret = PTR_ERR(em); - goto out; - } - emflags = em->flags; - } - - if (test_bit(EXTENT_FLAG_VACANCY, &emflags)) { + if ((em_start >= last) || em_len == (u64)-1 || + (last == (u64)-1 && isize <= em_end)) { flags |= FIEMAP_EXTENT_LAST; end = 1; } - if (em_start == last) { + /* now scan forward to see if this is really the last extent. */ + em = get_extent_skip_holes(inode, off, last_for_get_extent, + get_extent); + if (IS_ERR(em)) { + ret = PTR_ERR(em); + goto out; + } + if (!em) { flags |= FIEMAP_EXTENT_LAST; end = 1; } - - if (!hole) { - ret = fiemap_fill_next_extent(fieinfo, em_start, disko, - em_len, flags); - if (ret) - goto out_free; - } + ret = fiemap_fill_next_extent(fieinfo, em_start, disko, + em_len, flags); + if (ret) + goto out_free; } out_free: free_extent_map(em); diff --git a/fs/btrfs/extent_io.h b/fs/btrfs/extent_io.h index 7083cfafd06..9318dfefd59 100644 --- a/fs/btrfs/extent_io.h +++ b/fs/btrfs/extent_io.h @@ -191,7 +191,7 @@ void extent_io_exit(void); u64 count_range_bits(struct extent_io_tree *tree, u64 *start, u64 search_end, - u64 max_bytes, unsigned long bits); + u64 max_bytes, unsigned long bits, int contig); void free_extent_state(struct extent_state *state); int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index fb9bd7832b6..0efdb65953c 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -1913,7 +1913,7 @@ static int btrfs_clean_io_failures(struct inode *inode, u64 start) private = 0; if (count_range_bits(&BTRFS_I(inode)->io_failure_tree, &private, - (u64)-1, 1, EXTENT_DIRTY)) { + (u64)-1, 1, EXTENT_DIRTY, 0)) { ret = get_state_private(&BTRFS_I(inode)->io_failure_tree, start, &private_failure); if (ret == 0) { @@ -5280,6 +5280,128 @@ out: return em; } +struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page, + size_t pg_offset, u64 start, u64 len, + int create) +{ + struct extent_map *em; + struct extent_map *hole_em = NULL; + u64 range_start = start; + u64 end; + u64 found; + u64 found_end; + int err = 0; + + em = btrfs_get_extent(inode, page, pg_offset, start, len, create); + if (IS_ERR(em)) + return em; + if (em) { + /* + * if our em maps to a hole, there might + * actually be delalloc bytes behind it + */ + if (em->block_start != EXTENT_MAP_HOLE) + return em; + else + hole_em = em; + } + + /* check to see if we've wrapped (len == -1 or similar) */ + end = start + len; + if (end < start) + end = (u64)-1; + else + end -= 1; + + em = NULL; + + /* ok, we didn't find anything, lets look for delalloc */ + found = count_range_bits(&BTRFS_I(inode)->io_tree, &range_start, + end, len, EXTENT_DELALLOC, 1); + found_end = range_start + found; + if (found_end < range_start) + found_end = (u64)-1; + + /* + * we didn't find anything useful, return + * the original results from get_extent() + */ + if (range_start > end || found_end <= start) { + em = hole_em; + hole_em = NULL; + goto out; + } + + /* adjust the range_start to make sure it doesn't + * go backwards from the start they passed in + */ + range_start = max(start,range_start); + found = found_end - range_start; + + if (found > 0) { + u64 hole_start = start; + u64 hole_len = len; + + em = alloc_extent_map(GFP_NOFS); + if (!em) { + err = -ENOMEM; + goto out; + } + /* + * when btrfs_get_extent can't find anything it + * returns one huge hole + * + * make sure what it found really fits our range, and + * adjust to make sure it is based on the start from + * the caller + */ + if (hole_em) { + u64 calc_end = extent_map_end(hole_em); + + if (calc_end <= start || (hole_em->start > end)) { + free_extent_map(hole_em); + hole_em = NULL; + } else { + hole_start = max(hole_em->start, start); + hole_len = calc_end - hole_start; + } + } + em->bdev = NULL; + if (hole_em && range_start > hole_start) { + /* our hole starts before our delalloc, so we + * have to return just the parts of the hole + * that go until the delalloc starts + */ + em->len = min(hole_len, + range_start - hole_start); + em->start = hole_start; + em->orig_start = hole_start; + /* + * don't adjust block start at all, + * it is fixed at EXTENT_MAP_HOLE + */ + em->block_start = hole_em->block_start; + em->block_len = hole_len; + } else { + em->start = range_start; + em->len = found; + em->orig_start = range_start; + em->block_start = EXTENT_MAP_DELALLOC; + em->block_len = found; + } + } else if (hole_em) { + return hole_em; + } +out: + + free_extent_map(hole_em); + if (err) { + free_extent_map(em); + return ERR_PTR(err); + } + return em; +} + static struct extent_map *btrfs_new_extent_direct(struct inode *inode, u64 start, u64 len) { @@ -6102,7 +6224,7 @@ out: static int btrfs_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, __u64 start, __u64 len) { - return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent); + return extent_fiemap(inode, fieinfo, start, len, btrfs_get_extent_fiemap); } int btrfs_readpage(struct file *file, struct page *page) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index be2d4f6aaa5..5fdb2abc4fa 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1071,12 +1071,15 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, if (copy_from_user(&flags, arg, sizeof(flags))) return -EFAULT; - if (flags & ~BTRFS_SUBVOL_CREATE_ASYNC) + if (flags & BTRFS_SUBVOL_CREATE_ASYNC) return -EINVAL; if (flags & ~BTRFS_SUBVOL_RDONLY) return -EOPNOTSUPP; + if (!is_owner_or_cap(inode)) + return -EACCES; + down_write(&root->fs_info->subvol_sem); /* nothing to do */ @@ -1097,7 +1100,7 @@ static noinline int btrfs_ioctl_subvol_setflags(struct file *file, goto out_reset; } - ret = btrfs_update_root(trans, root, + ret = btrfs_update_root(trans, root->fs_info->tree_root, &root->root_key, &root->root_item); btrfs_commit_transaction(trans, root); diff --git a/fs/btrfs/lzo.c b/fs/btrfs/lzo.c index cc9b450399d..a178f5ebea7 100644 --- a/fs/btrfs/lzo.c +++ b/fs/btrfs/lzo.c @@ -280,6 +280,7 @@ static int lzo_decompress_biovec(struct list_head *ws, unsigned long tot_out; unsigned long tot_len; char *buf; + bool may_late_unmap, need_unmap; data_in = kmap(pages_in[0]); tot_len = read_compress_length(data_in); @@ -300,11 +301,13 @@ static int lzo_decompress_biovec(struct list_head *ws, tot_in += in_len; working_bytes = in_len; + may_late_unmap = need_unmap = false; /* fast path: avoid using the working buffer */ if (in_page_bytes_left >= in_len) { buf = data_in + in_offset; bytes = in_len; + may_late_unmap = true; goto cont; } @@ -329,14 +332,17 @@ cont: if (working_bytes == 0 && tot_in >= tot_len) break; - kunmap(pages_in[page_in_index]); - page_in_index++; - if (page_in_index >= total_pages_in) { + if (page_in_index + 1 >= total_pages_in) { ret = -1; - data_in = NULL; goto done; } - data_in = kmap(pages_in[page_in_index]); + + if (may_late_unmap) + need_unmap = true; + else + kunmap(pages_in[page_in_index]); + + data_in = kmap(pages_in[++page_in_index]); in_page_bytes_left = PAGE_CACHE_SIZE; in_offset = 0; @@ -346,6 +352,8 @@ cont: out_len = lzo1x_worst_compress(PAGE_CACHE_SIZE); ret = lzo1x_decompress_safe(buf, in_len, workspace->buf, &out_len); + if (need_unmap) + kunmap(pages_in[page_in_index - 1]); if (ret != LZO_E_OK) { printk(KERN_WARNING "btrfs decompress failed\n"); ret = -1; @@ -363,8 +371,7 @@ cont: break; } done: - if (data_in) - kunmap(pages_in[page_in_index]); + kunmap(pages_in[page_in_index]); return ret; } diff --git a/fs/btrfs/relocation.c b/fs/btrfs/relocation.c index 0825e4ed944..31ade5802ae 100644 --- a/fs/btrfs/relocation.c +++ b/fs/btrfs/relocation.c @@ -3654,6 +3654,7 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) u32 item_size; int ret; int err = 0; + int progress = 0; path = btrfs_alloc_path(); if (!path) @@ -3666,9 +3667,10 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) } while (1) { + progress++; trans = btrfs_start_transaction(rc->extent_root, 0); BUG_ON(IS_ERR(trans)); - +restart: if (update_backref_cache(trans, &rc->backref_cache)) { btrfs_end_transaction(trans, rc->extent_root); continue; @@ -3781,6 +3783,15 @@ static noinline_for_stack int relocate_block_group(struct reloc_control *rc) } } } + if (trans && progress && err == -ENOSPC) { + ret = btrfs_force_chunk_alloc(trans, rc->extent_root, + rc->block_group->flags); + if (ret == 0) { + err = 0; + progress = 0; + goto restart; + } + } btrfs_release_path(rc->extent_root, path); clear_extent_bits(&rc->processed_blocks, 0, (u64)-1, EXTENT_DIRTY, diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index a004008f7d2..d39a9895d93 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -155,7 +155,8 @@ enum { Opt_nossd, Opt_ssd_spread, Opt_thread_pool, Opt_noacl, Opt_compress, Opt_compress_type, Opt_compress_force, Opt_compress_force_type, Opt_notreelog, Opt_ratio, Opt_flushoncommit, Opt_discard, - Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, Opt_err, + Opt_space_cache, Opt_clear_cache, Opt_user_subvol_rm_allowed, + Opt_enospc_debug, Opt_err, }; static match_table_t tokens = { @@ -184,6 +185,7 @@ static match_table_t tokens = { {Opt_space_cache, "space_cache"}, {Opt_clear_cache, "clear_cache"}, {Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"}, + {Opt_enospc_debug, "enospc_debug"}, {Opt_err, NULL}, }; @@ -358,6 +360,9 @@ int btrfs_parse_options(struct btrfs_root *root, char *options) case Opt_user_subvol_rm_allowed: btrfs_set_opt(info->mount_opt, USER_SUBVOL_RM_ALLOWED); break; + case Opt_enospc_debug: + btrfs_set_opt(info->mount_opt, ENOSPC_DEBUG); + break; case Opt_err: printk(KERN_INFO "btrfs: unrecognized mount option " "'%s'\n", p); diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index af7dbca1527..dd13eb81ee4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -1338,11 +1338,11 @@ int btrfs_rm_device(struct btrfs_root *root, char *device_path) ret = btrfs_shrink_device(device, 0); if (ret) - goto error_brelse; + goto error_undo; ret = btrfs_rm_dev_item(root->fs_info->chunk_root, device); if (ret) - goto error_brelse; + goto error_undo; device->in_fs_metadata = 0; @@ -1416,6 +1416,13 @@ out: mutex_unlock(&root->fs_info->volume_mutex); mutex_unlock(&uuid_mutex); return ret; +error_undo: + if (device->writeable) { + list_add(&device->dev_alloc_list, + &root->fs_info->fs_devices->alloc_list); + root->fs_info->fs_devices->rw_devices++; + } + goto error_brelse; } /* @@ -1633,7 +1640,7 @@ int btrfs_init_new_device(struct btrfs_root *root, char *device_path) device->dev_root = root->fs_info->dev_root; device->bdev = bdev; device->in_fs_metadata = 1; - device->mode = 0; + device->mode = FMODE_EXCL; set_blocksize(device->bdev, 4096); if (seeding_dev) { diff --git a/fs/ceph/dir.c b/fs/ceph/dir.c index 0bc68de8edd..f0aef787a10 100644 --- a/fs/ceph/dir.c +++ b/fs/ceph/dir.c @@ -60,6 +60,7 @@ int ceph_init_dentry(struct dentry *dentry) } di->dentry = dentry; di->lease_session = NULL; + di->parent_inode = igrab(dentry->d_parent->d_inode); dentry->d_fsdata = di; dentry->d_time = jiffies; ceph_dentry_lru_add(dentry); @@ -1033,7 +1034,7 @@ static void ceph_dentry_release(struct dentry *dentry) u64 snapid = CEPH_NOSNAP; if (!IS_ROOT(dentry)) { - parent_inode = dentry->d_parent->d_inode; + parent_inode = di->parent_inode; if (parent_inode) snapid = ceph_snap(parent_inode); } @@ -1058,6 +1059,8 @@ static void ceph_dentry_release(struct dentry *dentry) kmem_cache_free(ceph_dentry_cachep, di); dentry->d_fsdata = NULL; } + if (parent_inode) + iput(parent_inode); } static int ceph_snapdir_d_revalidate(struct dentry *dentry, diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c index 39c243acd06..f40b9139e43 100644 --- a/fs/ceph/snap.c +++ b/fs/ceph/snap.c @@ -584,10 +584,14 @@ static void queue_realm_cap_snaps(struct ceph_snap_realm *realm) if (lastinode) iput(lastinode); - dout("queue_realm_cap_snaps %p %llx children\n", realm, realm->ino); - list_for_each_entry(child, &realm->children, child_item) - queue_realm_cap_snaps(child); + list_for_each_entry(child, &realm->children, child_item) { + dout("queue_realm_cap_snaps %p %llx queue child %p %llx\n", + realm, realm->ino, child, child->ino); + list_del_init(&child->dirty_item); + list_add(&child->dirty_item, &realm->dirty_item); + } + list_del_init(&realm->dirty_item); dout("queue_realm_cap_snaps %p %llx done\n", realm, realm->ino); } @@ -683,7 +687,9 @@ more: * queue cap snaps _after_ we've built the new snap contexts, * so that i_head_snapc can be set appropriately. */ - list_for_each_entry(realm, &dirty_realms, dirty_item) { + while (!list_empty(&dirty_realms)) { + realm = list_first_entry(&dirty_realms, struct ceph_snap_realm, + dirty_item); queue_realm_cap_snaps(realm); } diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 20b907d76ae..88fcaa21b80 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -207,6 +207,7 @@ struct ceph_dentry_info { struct dentry *dentry; u64 time; u64 offset; + struct inode *parent_inode; }; struct ceph_inode_xattrs_info { diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 4a3330235d5..a9371b6578c 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -127,5 +127,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.70" +#define CIFS_VERSION "1.71" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index 8d9189f6447..79f641eeda3 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -170,7 +170,7 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) { int rc, alen, slen; const char *pct; - char *endp, scope_id[13]; + char scope_id[13]; struct sockaddr_in *s4 = (struct sockaddr_in *) dst; struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) dst; @@ -197,9 +197,9 @@ cifs_convert_address(struct sockaddr *dst, const char *src, int len) memcpy(scope_id, pct + 1, slen); scope_id[slen] = '\0'; - s6->sin6_scope_id = (u32) simple_strtoul(pct, &endp, 0); - if (endp != scope_id + slen) - return 0; + rc = strict_strtoul(scope_id, 0, + (unsigned long *)&s6->sin6_scope_id); + rc = (rc == 0) ? 1 : 0; } return rc; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 1adc9625a34..16765703131 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -656,13 +656,13 @@ ssetup_ntlmssp_authenticate: if (type == LANMAN) { #ifdef CONFIG_CIFS_WEAK_PW_HASH - char lnm_session_key[CIFS_SESS_KEY_SIZE]; + char lnm_session_key[CIFS_AUTH_RESP_SIZE]; pSMB->req.hdr.Flags2 &= ~SMBFLG2_UNICODE; /* no capabilities flags in old lanman negotiation */ - pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_SESS_KEY_SIZE); + pSMB->old_req.PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE); /* Calculate hash with password and copy into bcc_ptr. * Encryption Key (stored as in cryptkey) gets used if the @@ -675,8 +675,8 @@ ssetup_ntlmssp_authenticate: true : false, lnm_session_key); ses->flags |= CIFS_SES_LANMAN; - memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_SESS_KEY_SIZE); - bcc_ptr += CIFS_SESS_KEY_SIZE; + memcpy(bcc_ptr, (char *)lnm_session_key, CIFS_AUTH_RESP_SIZE); + bcc_ptr += CIFS_AUTH_RESP_SIZE; /* can not sign if LANMAN negotiated so no need to calculate signing key? but what if server diff --git a/fs/ecryptfs/dentry.c b/fs/ecryptfs/dentry.c index 6fc4f319b55..534c1d46e69 100644 --- a/fs/ecryptfs/dentry.c +++ b/fs/ecryptfs/dentry.c @@ -46,24 +46,28 @@ static int ecryptfs_d_revalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; - struct dentry *dentry_save; - struct vfsmount *vfsmount_save; + struct dentry *dentry_save = NULL; + struct vfsmount *vfsmount_save = NULL; int rc = 1; - if (nd->flags & LOOKUP_RCU) + if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) goto out; - dentry_save = nd->path.dentry; - vfsmount_save = nd->path.mnt; - nd->path.dentry = lower_dentry; - nd->path.mnt = lower_mnt; + if (nd) { + dentry_save = nd->path.dentry; + vfsmount_save = nd->path.mnt; + nd->path.dentry = lower_dentry; + nd->path.mnt = lower_mnt; + } rc = lower_dentry->d_op->d_revalidate(lower_dentry, nd); - nd->path.dentry = dentry_save; - nd->path.mnt = vfsmount_save; + if (nd) { + nd->path.dentry = dentry_save; + nd->path.mnt = vfsmount_save; + } if (dentry->d_inode) { struct inode *lower_inode = ecryptfs_inode_to_lower(dentry->d_inode); diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index dbc84ed9633..e00753496e3 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -632,8 +632,7 @@ int ecryptfs_interpose(struct dentry *hidden_dentry, u32 flags); int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dentry, - struct inode *ecryptfs_dir_inode, - struct nameidata *ecryptfs_nd); + struct inode *ecryptfs_dir_inode); int ecryptfs_decode_and_decrypt_filename(char **decrypted_name, size_t *decrypted_name_size, struct dentry *ecryptfs_dentry, diff --git a/fs/ecryptfs/file.c b/fs/ecryptfs/file.c index 81e10e6a944..7d1050e254f 100644 --- a/fs/ecryptfs/file.c +++ b/fs/ecryptfs/file.c @@ -317,6 +317,7 @@ ecryptfs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) const struct file_operations ecryptfs_dir_fops = { .readdir = ecryptfs_readdir, + .read = generic_read_dir, .unlocked_ioctl = ecryptfs_unlocked_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ecryptfs_compat_ioctl, diff --git a/fs/ecryptfs/inode.c b/fs/ecryptfs/inode.c index bd33f87a190..b592938a84b 100644 --- a/fs/ecryptfs/inode.c +++ b/fs/ecryptfs/inode.c @@ -74,16 +74,20 @@ ecryptfs_create_underlying_file(struct inode *lower_dir_inode, unsigned int flags_save; int rc; - dentry_save = nd->path.dentry; - vfsmount_save = nd->path.mnt; - flags_save = nd->flags; - nd->path.dentry = lower_dentry; - nd->path.mnt = lower_mnt; - nd->flags &= ~LOOKUP_OPEN; + if (nd) { + dentry_save = nd->path.dentry; + vfsmount_save = nd->path.mnt; + flags_save = nd->flags; + nd->path.dentry = lower_dentry; + nd->path.mnt = lower_mnt; + nd->flags &= ~LOOKUP_OPEN; + } rc = vfs_create(lower_dir_inode, lower_dentry, mode, nd); - nd->path.dentry = dentry_save; - nd->path.mnt = vfsmount_save; - nd->flags = flags_save; + if (nd) { + nd->path.dentry = dentry_save; + nd->path.mnt = vfsmount_save; + nd->flags = flags_save; + } return rc; } @@ -241,8 +245,7 @@ out: */ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dentry, - struct inode *ecryptfs_dir_inode, - struct nameidata *ecryptfs_nd) + struct inode *ecryptfs_dir_inode) { struct dentry *lower_dir_dentry; struct vfsmount *lower_mnt; @@ -290,8 +293,6 @@ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, goto out; if (special_file(lower_inode->i_mode)) goto out; - if (!ecryptfs_nd) - goto out; /* Released in this function */ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER); if (!page_virt) { @@ -349,75 +350,6 @@ out: } /** - * ecryptfs_new_lower_dentry - * @name: The name of the new dentry. - * @lower_dir_dentry: Parent directory of the new dentry. - * @nd: nameidata from last lookup. - * - * Create a new dentry or get it from lower parent dir. - */ -static struct dentry * -ecryptfs_new_lower_dentry(struct qstr *name, struct dentry *lower_dir_dentry, - struct nameidata *nd) -{ - struct dentry *new_dentry; - struct dentry *tmp; - struct inode *lower_dir_inode; - - lower_dir_inode = lower_dir_dentry->d_inode; - - tmp = d_alloc(lower_dir_dentry, name); - if (!tmp) - return ERR_PTR(-ENOMEM); - - mutex_lock(&lower_dir_inode->i_mutex); - new_dentry = lower_dir_inode->i_op->lookup(lower_dir_inode, tmp, nd); - mutex_unlock(&lower_dir_inode->i_mutex); - - if (!new_dentry) - new_dentry = tmp; - else - dput(tmp); - - return new_dentry; -} - - -/** - * ecryptfs_lookup_one_lower - * @ecryptfs_dentry: The eCryptfs dentry that we are looking up - * @lower_dir_dentry: lower parent directory - * @name: lower file name - * - * Get the lower dentry from vfs. If lower dentry does not exist yet, - * create it. - */ -static struct dentry * -ecryptfs_lookup_one_lower(struct dentry *ecryptfs_dentry, - struct dentry *lower_dir_dentry, struct qstr *name) -{ - struct nameidata nd; - struct vfsmount *lower_mnt; - int err; - - lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( - ecryptfs_dentry->d_parent)); - err = vfs_path_lookup(lower_dir_dentry, lower_mnt, name->name , 0, &nd); - mntput(lower_mnt); - - if (!err) { - /* we dont need the mount */ - mntput(nd.path.mnt); - return nd.path.dentry; - } - if (err != -ENOENT) - return ERR_PTR(err); - - /* create a new lower dentry */ - return ecryptfs_new_lower_dentry(name, lower_dir_dentry, &nd); -} - -/** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up @@ -434,7 +366,6 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, size_t encrypted_and_encoded_name_size; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL; struct dentry *lower_dir_dentry, *lower_dentry; - struct qstr lower_name; int rc = 0; if ((ecryptfs_dentry->d_name.len == 1 @@ -444,20 +375,14 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, goto out_d_drop; } lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); - lower_name.name = ecryptfs_dentry->d_name.name; - lower_name.len = ecryptfs_dentry->d_name.len; - lower_name.hash = ecryptfs_dentry->d_name.hash; - if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) { - rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry, - lower_dir_dentry->d_inode, &lower_name); - if (rc < 0) - goto out_d_drop; - } - lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, - lower_dir_dentry, &lower_name); + mutex_lock(&lower_dir_dentry->d_inode->i_mutex); + lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name, + lower_dir_dentry, + ecryptfs_dentry->d_name.len); + mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; @@ -479,28 +404,21 @@ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, "filename; rc = [%d]\n", __func__, rc); goto out_d_drop; } - lower_name.name = encrypted_and_encoded_name; - lower_name.len = encrypted_and_encoded_name_size; - lower_name.hash = full_name_hash(lower_name.name, lower_name.len); - if (lower_dir_dentry->d_op && lower_dir_dentry->d_op->d_hash) { - rc = lower_dir_dentry->d_op->d_hash(lower_dir_dentry, - lower_dir_dentry->d_inode, &lower_name); - if (rc < 0) - goto out_d_drop; - } - lower_dentry = ecryptfs_lookup_one_lower(ecryptfs_dentry, - lower_dir_dentry, &lower_name); + mutex_lock(&lower_dir_dentry->d_inode->i_mutex); + lower_dentry = lookup_one_len(encrypted_and_encoded_name, + lower_dir_dentry, + encrypted_and_encoded_name_size); + mutex_unlock(&lower_dir_dentry->d_inode->i_mutex); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); - ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_lower() returned " + ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out_d_drop; } lookup_and_interpose: rc = ecryptfs_lookup_and_interpose_lower(ecryptfs_dentry, lower_dentry, - ecryptfs_dir_inode, - ecryptfs_nd); + ecryptfs_dir_inode); goto out; out_d_drop: d_drop(ecryptfs_dentry); @@ -1092,6 +1010,8 @@ int ecryptfs_getattr(struct vfsmount *mnt, struct dentry *dentry, rc = vfs_getattr(ecryptfs_dentry_to_lower_mnt(dentry), ecryptfs_dentry_to_lower(dentry), &lower_stat); if (!rc) { + fsstack_copy_attr_all(dentry->d_inode, + ecryptfs_inode_to_lower(dentry->d_inode)); generic_fillattr(dentry->d_inode, stat); stat->blocks = lower_stat.blocks; } diff --git a/fs/eventfd.c b/fs/eventfd.c index e0194b3e14d..d9a59177391 100644 --- a/fs/eventfd.c +++ b/fs/eventfd.c @@ -99,7 +99,7 @@ EXPORT_SYMBOL_GPL(eventfd_ctx_get); * @ctx: [in] Pointer to eventfd context. * * The eventfd context reference must have been previously acquired either - * with eventfd_ctx_get() or eventfd_ctx_fdget()). + * with eventfd_ctx_get() or eventfd_ctx_fdget(). */ void eventfd_ctx_put(struct eventfd_ctx *ctx) { @@ -146,9 +146,9 @@ static void eventfd_ctx_do_read(struct eventfd_ctx *ctx, __u64 *cnt) * eventfd_ctx_remove_wait_queue - Read the current counter and removes wait queue. * @ctx: [in] Pointer to eventfd context. * @wait: [in] Wait queue to be removed. - * @cnt: [out] Pointer to the 64bit conter value. + * @cnt: [out] Pointer to the 64-bit counter value. * - * Returns zero if successful, or the following error codes: + * Returns %0 if successful, or the following error codes: * * -EAGAIN : The operation would have blocked. * @@ -175,11 +175,11 @@ EXPORT_SYMBOL_GPL(eventfd_ctx_remove_wait_queue); * eventfd_ctx_read - Reads the eventfd counter or wait if it is zero. * @ctx: [in] Pointer to eventfd context. * @no_wait: [in] Different from zero if the operation should not block. - * @cnt: [out] Pointer to the 64bit conter value. + * @cnt: [out] Pointer to the 64-bit counter value. * - * Returns zero if successful, or the following error codes: + * Returns %0 if successful, or the following error codes: * - * -EAGAIN : The operation would have blocked but @no_wait was nonzero. + * -EAGAIN : The operation would have blocked but @no_wait was non-zero. * -ERESTARTSYS : A signal interrupted the wait operation. * * If @no_wait is zero, the function might sleep until the eventfd internal diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 267d0ada454..4a09af9e9a6 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -63,6 +63,13 @@ * cleanup path and it is also acquired by eventpoll_release_file() * if a file has been pushed inside an epoll set and it is then * close()d without a previous call toepoll_ctl(EPOLL_CTL_DEL). + * It is also acquired when inserting an epoll fd onto another epoll + * fd. We do this so that we walk the epoll tree and ensure that this + * insertion does not create a cycle of epoll file descriptors, which + * could lead to deadlock. We need a global mutex to prevent two + * simultaneous inserts (A into B and B into A) from racing and + * constructing a cycle without either insert observing that it is + * going to. * It is possible to drop the "ep->mtx" and to use the global * mutex "epmutex" (together with "ep->lock") to have it working, * but having "ep->mtx" will make the interface more scalable. @@ -224,6 +231,9 @@ static long max_user_watches __read_mostly; */ static DEFINE_MUTEX(epmutex); +/* Used to check for epoll file descriptor inclusion loops */ +static struct nested_calls poll_loop_ncalls; + /* Used for safe wake up implementation */ static struct nested_calls poll_safewake_ncalls; @@ -1198,6 +1208,62 @@ retry: return res; } +/** + * ep_loop_check_proc - Callback function to be passed to the @ep_call_nested() + * API, to verify that adding an epoll file inside another + * epoll structure, does not violate the constraints, in + * terms of closed loops, or too deep chains (which can + * result in excessive stack usage). + * + * @priv: Pointer to the epoll file to be currently checked. + * @cookie: Original cookie for this call. This is the top-of-the-chain epoll + * data structure pointer. + * @call_nests: Current dept of the @ep_call_nested() call stack. + * + * Returns: Returns zero if adding the epoll @file inside current epoll + * structure @ep does not violate the constraints, or -1 otherwise. + */ +static int ep_loop_check_proc(void *priv, void *cookie, int call_nests) +{ + int error = 0; + struct file *file = priv; + struct eventpoll *ep = file->private_data; + struct rb_node *rbp; + struct epitem *epi; + + mutex_lock(&ep->mtx); + for (rbp = rb_first(&ep->rbr); rbp; rbp = rb_next(rbp)) { + epi = rb_entry(rbp, struct epitem, rbn); + if (unlikely(is_file_epoll(epi->ffd.file))) { + error = ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, + ep_loop_check_proc, epi->ffd.file, + epi->ffd.file->private_data, current); + if (error != 0) + break; + } + } + mutex_unlock(&ep->mtx); + + return error; +} + +/** + * ep_loop_check - Performs a check to verify that adding an epoll file (@file) + * another epoll file (represented by @ep) does not create + * closed loops or too deep chains. + * + * @ep: Pointer to the epoll private data structure. + * @file: Pointer to the epoll file to be checked. + * + * Returns: Returns zero if adding the epoll @file inside current epoll + * structure @ep does not violate the constraints, or -1 otherwise. + */ +static int ep_loop_check(struct eventpoll *ep, struct file *file) +{ + return ep_call_nested(&poll_loop_ncalls, EP_MAX_NESTS, + ep_loop_check_proc, file, ep, current); +} + /* * Open an eventpoll file descriptor. */ @@ -1246,6 +1312,7 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, struct epoll_event __user *, event) { int error; + int did_lock_epmutex = 0; struct file *file, *tfile; struct eventpoll *ep; struct epitem *epi; @@ -1287,6 +1354,25 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, */ ep = file->private_data; + /* + * When we insert an epoll file descriptor, inside another epoll file + * descriptor, there is the change of creating closed loops, which are + * better be handled here, than in more critical paths. + * + * We hold epmutex across the loop check and the insert in this case, in + * order to prevent two separate inserts from racing and each doing the + * insert "at the same time" such that ep_loop_check passes on both + * before either one does the insert, thereby creating a cycle. + */ + if (unlikely(is_file_epoll(tfile) && op == EPOLL_CTL_ADD)) { + mutex_lock(&epmutex); + did_lock_epmutex = 1; + error = -ELOOP; + if (ep_loop_check(ep, tfile) != 0) + goto error_tgt_fput; + } + + mutex_lock(&ep->mtx); /* @@ -1322,6 +1408,9 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, mutex_unlock(&ep->mtx); error_tgt_fput: + if (unlikely(did_lock_epmutex)) + mutex_unlock(&epmutex); + fput(tfile); error_fput: fput(file); @@ -1441,6 +1530,12 @@ static int __init eventpoll_init(void) EP_ITEM_COST; BUG_ON(max_user_watches < 0); + /* + * Initialize the structure used to perform epoll file descriptor + * inclusion loops checks. + */ + ep_nested_calls_init(&poll_loop_ncalls); + /* Initialize the structure used to perform safe poll wait head wake ups */ ep_nested_calls_init(&poll_safewake_ncalls); diff --git a/fs/exofs/namei.c b/fs/exofs/namei.c index 264e95d0283..4d70db110cf 100644 --- a/fs/exofs/namei.c +++ b/fs/exofs/namei.c @@ -272,7 +272,6 @@ static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry, new_de = exofs_find_entry(new_dir, new_dentry, &new_page); if (!new_de) goto out_dir; - inode_inc_link_count(old_inode); err = exofs_set_link(new_dir, new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME; if (dir_de) @@ -286,12 +285,9 @@ static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir->i_nlink >= EXOFS_LINK_MAX) goto out_dir; } - inode_inc_link_count(old_inode); err = exofs_add_link(new_dentry, old_inode); - if (err) { - inode_dec_link_count(old_inode); + if (err) goto out_dir; - } if (dir_de) inode_inc_link_count(new_dir); } @@ -299,7 +295,7 @@ static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry, old_inode->i_ctime = CURRENT_TIME; exofs_delete_entry(old_de, old_page); - inode_dec_link_count(old_inode); + mark_inode_dirty(old_inode); if (dir_de) { err = exofs_set_link(old_inode, dir_de, dir_page, new_dir); diff --git a/fs/ext2/namei.c b/fs/ext2/namei.c index 2e1d8341d82..adb91855ccd 100644 --- a/fs/ext2/namei.c +++ b/fs/ext2/namei.c @@ -344,7 +344,6 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, new_de = ext2_find_entry (new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; - inode_inc_link_count(old_inode); ext2_set_link(new_dir, new_de, new_page, old_inode, 1); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) @@ -356,12 +355,9 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, if (new_dir->i_nlink >= EXT2_LINK_MAX) goto out_dir; } - inode_inc_link_count(old_inode); err = ext2_add_link(new_dentry, old_inode); - if (err) { - inode_dec_link_count(old_inode); + if (err) goto out_dir; - } if (dir_de) inode_inc_link_count(new_dir); } @@ -369,12 +365,11 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry, /* * Like most other Unix systems, set the ctime for inodes on a * rename. - * inode_dec_link_count() will mark the inode dirty. */ old_inode->i_ctime = CURRENT_TIME_SEC; + mark_inode_dirty(old_inode); ext2_delete_entry (old_de, old_page); - inode_dec_link_count(old_inode); if (dir_de) { if (old_dir != new_dir) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index bfed8447ed8..83543b5ff94 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1283,8 +1283,11 @@ static int fuse_do_setattr(struct dentry *entry, struct iattr *attr, if (err) return err; - if ((attr->ia_valid & ATTR_OPEN) && fc->atomic_o_trunc) - return 0; + if (attr->ia_valid & ATTR_OPEN) { + if (fc->atomic_o_trunc) + return 0; + file = NULL; + } if (attr->ia_valid & ATTR_SIZE) is_truncate = true; diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 95da1bc1c82..9e0832dbb1e 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -86,18 +86,52 @@ struct fuse_file *fuse_file_get(struct fuse_file *ff) return ff; } +static void fuse_release_async(struct work_struct *work) +{ + struct fuse_req *req; + struct fuse_conn *fc; + struct path path; + + req = container_of(work, struct fuse_req, misc.release.work); + path = req->misc.release.path; + fc = get_fuse_conn(path.dentry->d_inode); + + fuse_put_request(fc, req); + path_put(&path); +} + static void fuse_release_end(struct fuse_conn *fc, struct fuse_req *req) { - path_put(&req->misc.release.path); + if (fc->destroy_req) { + /* + * If this is a fuseblk mount, then it's possible that + * releasing the path will result in releasing the + * super block and sending the DESTROY request. If + * the server is single threaded, this would hang. + * For this reason do the path_put() in a separate + * thread. + */ + atomic_inc(&req->count); + INIT_WORK(&req->misc.release.work, fuse_release_async); + schedule_work(&req->misc.release.work); + } else { + path_put(&req->misc.release.path); + } } -static void fuse_file_put(struct fuse_file *ff) +static void fuse_file_put(struct fuse_file *ff, bool sync) { if (atomic_dec_and_test(&ff->count)) { struct fuse_req *req = ff->reserved_req; - req->end = fuse_release_end; - fuse_request_send_background(ff->fc, req); + if (sync) { + fuse_request_send(ff->fc, req); + path_put(&req->misc.release.path); + fuse_put_request(ff->fc, req); + } else { + req->end = fuse_release_end; + fuse_request_send_background(ff->fc, req); + } kfree(ff); } } @@ -219,8 +253,12 @@ void fuse_release_common(struct file *file, int opcode) * Normally this will send the RELEASE request, however if * some asynchronous READ or WRITE requests are outstanding, * the sending will be delayed. + * + * Make the release synchronous if this is a fuseblk mount, + * synchronous RELEASE is allowed (and desirable) in this case + * because the server can be trusted not to screw up. */ - fuse_file_put(ff); + fuse_file_put(ff, ff->fc->destroy_req != NULL); } static int fuse_open(struct inode *inode, struct file *file) @@ -558,7 +596,7 @@ static void fuse_readpages_end(struct fuse_conn *fc, struct fuse_req *req) page_cache_release(page); } if (req->ff) - fuse_file_put(req->ff); + fuse_file_put(req->ff, false); } static void fuse_send_readpages(struct fuse_req *req, struct file *file) @@ -1137,7 +1175,7 @@ static ssize_t fuse_direct_write(struct file *file, const char __user *buf, static void fuse_writepage_free(struct fuse_conn *fc, struct fuse_req *req) { __free_page(req->pages[0]); - fuse_file_put(req->ff); + fuse_file_put(req->ff, false); } static void fuse_writepage_finish(struct fuse_conn *fc, struct fuse_req *req) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index ae5744a2f9e..d4286947bc2 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -21,6 +21,7 @@ #include <linux/rwsem.h> #include <linux/rbtree.h> #include <linux/poll.h> +#include <linux/workqueue.h> /** Max number of pages that can be used in a single read request */ #define FUSE_MAX_PAGES_PER_REQ 32 @@ -262,7 +263,10 @@ struct fuse_req { /** Data for asynchronous requests */ union { struct { - struct fuse_release_in in; + union { + struct fuse_release_in in; + struct work_struct work; + }; struct path path; } release; struct fuse_init_in init_in; diff --git a/fs/gfs2/main.c b/fs/gfs2/main.c index 85ba027d1c4..72c31a315d9 100644 --- a/fs/gfs2/main.c +++ b/fs/gfs2/main.c @@ -59,14 +59,7 @@ static void gfs2_init_gl_aspace_once(void *foo) struct address_space *mapping = (struct address_space *)(gl + 1); gfs2_init_glock_once(gl); - memset(mapping, 0, sizeof(*mapping)); - INIT_RADIX_TREE(&mapping->page_tree, GFP_ATOMIC); - spin_lock_init(&mapping->tree_lock); - spin_lock_init(&mapping->i_mmap_lock); - INIT_LIST_HEAD(&mapping->private_list); - spin_lock_init(&mapping->private_lock); - INIT_RAW_PRIO_TREE_ROOT(&mapping->i_mmap); - INIT_LIST_HEAD(&mapping->i_mmap_nonlinear); + address_space_init_once(mapping); } /** diff --git a/fs/hfs/dir.c b/fs/hfs/dir.c index afa66aaa223..b4d70b13be9 100644 --- a/fs/hfs/dir.c +++ b/fs/hfs/dir.c @@ -238,46 +238,22 @@ static int hfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) } /* - * hfs_unlink() + * hfs_remove() * - * This is the unlink() entry in the inode_operations structure for - * regular HFS directories. The purpose is to delete an existing - * file, given the inode for the parent directory and the name - * (and its length) of the existing file. - */ -static int hfs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode; - int res; - - inode = dentry->d_inode; - res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); - if (res) - return res; - - drop_nlink(inode); - hfs_delete_inode(inode); - inode->i_ctime = CURRENT_TIME_SEC; - mark_inode_dirty(inode); - - return res; -} - -/* - * hfs_rmdir() + * This serves as both unlink() and rmdir() in the inode_operations + * structure for regular HFS directories. The purpose is to delete + * an existing child, given the inode for the parent directory and + * the name (and its length) of the existing directory. * - * This is the rmdir() entry in the inode_operations structure for - * regular HFS directories. The purpose is to delete an existing - * directory, given the inode for the parent directory and the name - * (and its length) of the existing directory. + * HFS does not have hardlinks, so both rmdir and unlink set the + * link count to 0. The only difference is the emptiness check. */ -static int hfs_rmdir(struct inode *dir, struct dentry *dentry) +static int hfs_remove(struct inode *dir, struct dentry *dentry) { - struct inode *inode; + struct inode *inode = dentry->d_inode; int res; - inode = dentry->d_inode; - if (inode->i_size != 2) + if (S_ISDIR(inode->i_mode) && inode->i_size != 2) return -ENOTEMPTY; res = hfs_cat_delete(inode->i_ino, dir, &dentry->d_name); if (res) @@ -307,7 +283,7 @@ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, /* Unlink destination if it already exists */ if (new_dentry->d_inode) { - res = hfs_unlink(new_dir, new_dentry); + res = hfs_remove(new_dir, new_dentry); if (res) return res; } @@ -332,9 +308,9 @@ const struct file_operations hfs_dir_operations = { const struct inode_operations hfs_dir_inode_operations = { .create = hfs_create, .lookup = hfs_lookup, - .unlink = hfs_unlink, + .unlink = hfs_remove, .mkdir = hfs_mkdir, - .rmdir = hfs_rmdir, + .rmdir = hfs_remove, .rename = hfs_rename, .setattr = hfs_inode_setattr, }; diff --git a/fs/inode.c b/fs/inode.c index da85e56378f..0647d80accf 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -295,6 +295,20 @@ static void destroy_inode(struct inode *inode) call_rcu(&inode->i_rcu, i_callback); } +void address_space_init_once(struct address_space *mapping) +{ + memset(mapping, 0, sizeof(*mapping)); + INIT_RADIX_TREE(&mapping->page_tree, GFP_ATOMIC); + spin_lock_init(&mapping->tree_lock); + spin_lock_init(&mapping->i_mmap_lock); + INIT_LIST_HEAD(&mapping->private_list); + spin_lock_init(&mapping->private_lock); + INIT_RAW_PRIO_TREE_ROOT(&mapping->i_mmap); + INIT_LIST_HEAD(&mapping->i_mmap_nonlinear); + mutex_init(&mapping->unmap_mutex); +} +EXPORT_SYMBOL(address_space_init_once); + /* * These are initializations that only need to be done * once, because the fields are idempotent across use @@ -308,13 +322,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_devices); INIT_LIST_HEAD(&inode->i_wb_list); INIT_LIST_HEAD(&inode->i_lru); - INIT_RADIX_TREE(&inode->i_data.page_tree, GFP_ATOMIC); - spin_lock_init(&inode->i_data.tree_lock); - spin_lock_init(&inode->i_data.i_mmap_lock); - INIT_LIST_HEAD(&inode->i_data.private_list); - spin_lock_init(&inode->i_data.private_lock); - INIT_RAW_PRIO_TREE_ROOT(&inode->i_data.i_mmap); - INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear); + address_space_init_once(&inode->i_data); i_size_ordered_init(inode); #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&inode->i_fsnotify_marks); @@ -540,11 +548,14 @@ void evict_inodes(struct super_block *sb) /** * invalidate_inodes - attempt to free all inodes on a superblock * @sb: superblock to operate on + * @kill_dirty: flag to guide handling of dirty inodes * * Attempts to free all inodes for a given superblock. If there were any * busy inodes return a non-zero value, else zero. + * If @kill_dirty is set, discard dirty inodes too, otherwise treat + * them as busy. */ -int invalidate_inodes(struct super_block *sb) +int invalidate_inodes(struct super_block *sb, bool kill_dirty) { int busy = 0; struct inode *inode, *next; @@ -556,6 +567,10 @@ int invalidate_inodes(struct super_block *sb) list_for_each_entry_safe(inode, next, &sb->s_inodes, i_sb_list) { if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) continue; + if (inode->i_state & I_DIRTY && !kill_dirty) { + busy = 1; + continue; + } if (atomic_read(&inode->i_count)) { busy = 1; continue; diff --git a/fs/internal.h b/fs/internal.h index 0663568b124..9b976b57d7f 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -112,4 +112,4 @@ extern void release_open_intent(struct nameidata *); */ extern int get_nr_dirty_inodes(void); extern void evict_inodes(struct super_block *); -extern int invalidate_inodes(struct super_block *); +extern int invalidate_inodes(struct super_block *, bool); diff --git a/fs/minix/namei.c b/fs/minix/namei.c index ce7337ddfdb..6e6777f1b4b 100644 --- a/fs/minix/namei.c +++ b/fs/minix/namei.c @@ -213,7 +213,6 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, new_de = minix_find_entry(new_dentry, &new_page); if (!new_de) goto out_dir; - inode_inc_link_count(old_inode); minix_set_link(new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) @@ -225,18 +224,15 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry, if (new_dir->i_nlink >= info->s_link_max) goto out_dir; } - inode_inc_link_count(old_inode); err = minix_add_link(new_dentry, old_inode); - if (err) { - inode_dec_link_count(old_inode); + if (err) goto out_dir; - } if (dir_de) inode_inc_link_count(new_dir); } minix_delete_entry(old_de, old_page); - inode_dec_link_count(old_inode); + mark_inode_dirty(old_inode); if (dir_de) { minix_set_link(dir_de, dir_page, new_dir); diff --git a/fs/namespace.c b/fs/namespace.c index 7b0b9537169..d1edf26025d 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1244,7 +1244,7 @@ static int do_umount(struct vfsmount *mnt, int flags) */ br_write_lock(vfsmount_lock); if (mnt_get_count(mnt) != 2) { - br_write_lock(vfsmount_lock); + br_write_unlock(vfsmount_lock); return -EBUSY; } br_write_unlock(vfsmount_lock); diff --git a/fs/nilfs2/btnode.c b/fs/nilfs2/btnode.c index 388e9e8f528..85f7baa15f5 100644 --- a/fs/nilfs2/btnode.c +++ b/fs/nilfs2/btnode.c @@ -35,11 +35,6 @@ #include "btnode.h" -void nilfs_btnode_cache_init_once(struct address_space *btnc) -{ - nilfs_mapping_init_once(btnc); -} - static const struct address_space_operations def_btnode_aops = { .sync_page = block_sync_page, }; diff --git a/fs/nilfs2/btnode.h b/fs/nilfs2/btnode.h index 79037494f1e..1b8ebd888c2 100644 --- a/fs/nilfs2/btnode.h +++ b/fs/nilfs2/btnode.h @@ -37,7 +37,6 @@ struct nilfs_btnode_chkey_ctxt { struct buffer_head *newbh; }; -void nilfs_btnode_cache_init_once(struct address_space *); void nilfs_btnode_cache_init(struct address_space *, struct backing_dev_info *); void nilfs_btnode_cache_clear(struct address_space *); struct buffer_head *nilfs_btnode_create_block(struct address_space *btnc, diff --git a/fs/nilfs2/mdt.c b/fs/nilfs2/mdt.c index 6a0e2a189f6..a0babd2bff6 100644 --- a/fs/nilfs2/mdt.c +++ b/fs/nilfs2/mdt.c @@ -454,9 +454,9 @@ int nilfs_mdt_setup_shadow_map(struct inode *inode, struct backing_dev_info *bdi = inode->i_sb->s_bdi; INIT_LIST_HEAD(&shadow->frozen_buffers); - nilfs_mapping_init_once(&shadow->frozen_data); + address_space_init_once(&shadow->frozen_data); nilfs_mapping_init(&shadow->frozen_data, bdi, &shadow_map_aops); - nilfs_mapping_init_once(&shadow->frozen_btnodes); + address_space_init_once(&shadow->frozen_btnodes); nilfs_mapping_init(&shadow->frozen_btnodes, bdi, &shadow_map_aops); mi->mi_shadow = shadow; return 0; diff --git a/fs/nilfs2/namei.c b/fs/nilfs2/namei.c index 98034271cd0..161791d2645 100644 --- a/fs/nilfs2/namei.c +++ b/fs/nilfs2/namei.c @@ -397,7 +397,6 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, new_de = nilfs_find_entry(new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; - inc_nlink(old_inode); nilfs_set_link(new_dir, new_de, new_page, old_inode); nilfs_mark_inode_dirty(new_dir); new_inode->i_ctime = CURRENT_TIME; @@ -411,13 +410,9 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir->i_nlink >= NILFS_LINK_MAX) goto out_dir; } - inc_nlink(old_inode); err = nilfs_add_link(new_dentry, old_inode); - if (err) { - drop_nlink(old_inode); - nilfs_mark_inode_dirty(old_inode); + if (err) goto out_dir; - } if (dir_de) { inc_nlink(new_dir); nilfs_mark_inode_dirty(new_dir); @@ -431,7 +426,6 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry, old_inode->i_ctime = CURRENT_TIME; nilfs_delete_entry(old_de, old_page); - drop_nlink(old_inode); if (dir_de) { nilfs_set_link(old_inode, dir_de, dir_page, new_dir); diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index 0c432416cfe..a585b35fd6b 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -492,19 +492,6 @@ unsigned nilfs_page_count_clean_buffers(struct page *page, return nc; } -void nilfs_mapping_init_once(struct address_space *mapping) -{ - memset(mapping, 0, sizeof(*mapping)); - INIT_RADIX_TREE(&mapping->page_tree, GFP_ATOMIC); - spin_lock_init(&mapping->tree_lock); - INIT_LIST_HEAD(&mapping->private_list); - spin_lock_init(&mapping->private_lock); - - spin_lock_init(&mapping->i_mmap_lock); - INIT_RAW_PRIO_TREE_ROOT(&mapping->i_mmap); - INIT_LIST_HEAD(&mapping->i_mmap_nonlinear); -} - void nilfs_mapping_init(struct address_space *mapping, struct backing_dev_info *bdi, const struct address_space_operations *aops) diff --git a/fs/nilfs2/page.h b/fs/nilfs2/page.h index 622df27cd89..2a00953ebd5 100644 --- a/fs/nilfs2/page.h +++ b/fs/nilfs2/page.h @@ -61,7 +61,6 @@ void nilfs_free_private_page(struct page *); int nilfs_copy_dirty_pages(struct address_space *, struct address_space *); void nilfs_copy_back_pages(struct address_space *, struct address_space *); void nilfs_clear_dirty_pages(struct address_space *); -void nilfs_mapping_init_once(struct address_space *mapping); void nilfs_mapping_init(struct address_space *mapping, struct backing_dev_info *bdi, const struct address_space_operations *aops); diff --git a/fs/nilfs2/segment.c b/fs/nilfs2/segment.c index 55ebae5c7f3..2de9f636792 100644 --- a/fs/nilfs2/segment.c +++ b/fs/nilfs2/segment.c @@ -430,7 +430,8 @@ static void nilfs_segctor_begin_finfo(struct nilfs_sc_info *sci, nilfs_segctor_map_segsum_entry( sci, &sci->sc_binfo_ptr, sizeof(struct nilfs_finfo)); - if (inode->i_sb && !test_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags)) + if (NILFS_I(inode)->i_root && + !test_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags)) set_bit(NILFS_SC_HAVE_DELTA, &sci->sc_flags); /* skip finfo */ } diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c index 58fd707174e..1673b3d9984 100644 --- a/fs/nilfs2/super.c +++ b/fs/nilfs2/super.c @@ -1279,7 +1279,7 @@ static void nilfs_inode_init_once(void *obj) #ifdef CONFIG_NILFS_XATTR init_rwsem(&ii->xattr_sem); #endif - nilfs_btnode_cache_init_once(&ii->i_btnode_cache); + address_space_init_once(&ii->i_btnode_cache); ii->i_bmap = &ii->i_bmap_data; inode_init_once(&ii->vfs_inode); } diff --git a/fs/ocfs2/journal.h b/fs/ocfs2/journal.h index 43e56b97f9c..6180da1e37e 100644 --- a/fs/ocfs2/journal.h +++ b/fs/ocfs2/journal.h @@ -405,9 +405,9 @@ static inline int ocfs2_remove_extent_credits(struct super_block *sb) ocfs2_quota_trans_credits(sb); } -/* data block for new dir/symlink, 2 for bitmap updates (bitmap fe + - * bitmap block for the new bit) dx_root update for free list */ -#define OCFS2_DIR_LINK_ADDITIONAL_CREDITS (1 + 2 + 1) +/* data block for new dir/symlink, allocation of directory block, dx_root + * update for free list */ +#define OCFS2_DIR_LINK_ADDITIONAL_CREDITS (1 + OCFS2_SUBALLOC_ALLOC + 1) static inline int ocfs2_add_dir_index_credits(struct super_block *sb) { diff --git a/fs/ocfs2/refcounttree.c b/fs/ocfs2/refcounttree.c index b5f9160e93e..19ebc5aad39 100644 --- a/fs/ocfs2/refcounttree.c +++ b/fs/ocfs2/refcounttree.c @@ -3228,7 +3228,7 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, u32 num_clusters, unsigned int e_flags) { int ret, delete, index, credits = 0; - u32 new_bit, new_len; + u32 new_bit, new_len, orig_num_clusters; unsigned int set_len; struct ocfs2_super *osb = OCFS2_SB(sb); handle_t *handle; @@ -3261,6 +3261,8 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, goto out; } + orig_num_clusters = num_clusters; + while (num_clusters) { ret = ocfs2_get_refcount_rec(ref_ci, context->ref_root_bh, p_cluster, num_clusters, @@ -3348,7 +3350,8 @@ static int ocfs2_make_clusters_writable(struct super_block *sb, * in write-back mode. */ if (context->get_clusters == ocfs2_di_get_clusters) { - ret = ocfs2_cow_sync_writeback(sb, context, cpos, num_clusters); + ret = ocfs2_cow_sync_writeback(sb, context, cpos, + orig_num_clusters); if (ret) mlog_errno(ret); } diff --git a/fs/ocfs2/super.c b/fs/ocfs2/super.c index 38f986d2447..36c423fb063 100644 --- a/fs/ocfs2/super.c +++ b/fs/ocfs2/super.c @@ -1316,7 +1316,7 @@ static int ocfs2_parse_options(struct super_block *sb, struct mount_options *mopt, int is_remount) { - int status; + int status, user_stack = 0; char *p; u32 tmp; @@ -1459,6 +1459,15 @@ static int ocfs2_parse_options(struct super_block *sb, memcpy(mopt->cluster_stack, args[0].from, OCFS2_STACK_LABEL_LEN); mopt->cluster_stack[OCFS2_STACK_LABEL_LEN] = '\0'; + /* + * Open code the memcmp here as we don't have + * an osb to pass to + * ocfs2_userspace_stack(). + */ + if (memcmp(mopt->cluster_stack, + OCFS2_CLASSIC_CLUSTER_STACK, + OCFS2_STACK_LABEL_LEN)) + user_stack = 1; break; case Opt_inode64: mopt->mount_opt |= OCFS2_MOUNT_INODE64; @@ -1514,13 +1523,16 @@ static int ocfs2_parse_options(struct super_block *sb, } } - /* Ensure only one heartbeat mode */ - tmp = mopt->mount_opt & (OCFS2_MOUNT_HB_LOCAL | OCFS2_MOUNT_HB_GLOBAL | - OCFS2_MOUNT_HB_NONE); - if (hweight32(tmp) != 1) { - mlog(ML_ERROR, "Invalid heartbeat mount options\n"); - status = 0; - goto bail; + if (user_stack == 0) { + /* Ensure only one heartbeat mode */ + tmp = mopt->mount_opt & (OCFS2_MOUNT_HB_LOCAL | + OCFS2_MOUNT_HB_GLOBAL | + OCFS2_MOUNT_HB_NONE); + if (hweight32(tmp) != 1) { + mlog(ML_ERROR, "Invalid heartbeat mount options\n"); + status = 0; + goto bail; + } } status = 1; diff --git a/fs/partitions/ldm.c b/fs/partitions/ldm.c index 789c625c7aa..b10e3540d5b 100644 --- a/fs/partitions/ldm.c +++ b/fs/partitions/ldm.c @@ -251,6 +251,11 @@ static bool ldm_parse_vmdb (const u8 *data, struct vmdb *vm) } vm->vblk_size = get_unaligned_be32(data + 0x08); + if (vm->vblk_size == 0) { + ldm_error ("Illegal VBLK size"); + return false; + } + vm->vblk_offset = get_unaligned_be32(data + 0x0C); vm->last_vblk_seq = get_unaligned_be32(data + 0x04); diff --git a/fs/proc/proc_devtree.c b/fs/proc/proc_devtree.c index d9396a4fc7f..927cbd115e5 100644 --- a/fs/proc/proc_devtree.c +++ b/fs/proc/proc_devtree.c @@ -233,7 +233,7 @@ void __init proc_device_tree_init(void) return; root = of_find_node_by_path("/"); if (root == NULL) { - printk(KERN_ERR "/proc/device-tree: can't find root\n"); + pr_debug("/proc/device-tree: can't find root\n"); return; } proc_device_tree_add_node(root, proc_device_tree); diff --git a/fs/reiserfs/namei.c b/fs/reiserfs/namei.c index ba5f51ec345..68fdf45cc6c 100644 --- a/fs/reiserfs/namei.c +++ b/fs/reiserfs/namei.c @@ -771,7 +771,7 @@ static int reiserfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) EMPTY_DIR_SIZE_V1 : EMPTY_DIR_SIZE, dentry, inode, &security); if (retval) { - dir->i_nlink--; + DEC_DIR_INODE_NLINK(dir) goto out_failed; } diff --git a/fs/sysv/namei.c b/fs/sysv/namei.c index b427b1208c2..e474fbcf8bd 100644 --- a/fs/sysv/namei.c +++ b/fs/sysv/namei.c @@ -245,7 +245,6 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, new_de = sysv_find_entry(new_dentry, &new_page); if (!new_de) goto out_dir; - inode_inc_link_count(old_inode); sysv_set_link(new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) @@ -257,18 +256,15 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry, if (new_dir->i_nlink >= SYSV_SB(new_dir->i_sb)->s_link_max) goto out_dir; } - inode_inc_link_count(old_inode); err = sysv_add_link(new_dentry, old_inode); - if (err) { - inode_dec_link_count(old_inode); + if (err) goto out_dir; - } if (dir_de) inode_inc_link_count(new_dir); } sysv_delete_entry(old_de, old_page); - inode_dec_link_count(old_inode); + mark_inode_dirty(old_inode); if (dir_de) { sysv_set_link(dir_de, dir_page, new_dir); diff --git a/fs/udf/namei.c b/fs/udf/namei.c index 2be0f9eb86d..b7c338d5e9d 100644 --- a/fs/udf/namei.c +++ b/fs/udf/namei.c @@ -32,6 +32,8 @@ #include <linux/crc-itu-t.h> #include <linux/exportfs.h> +enum { UDF_MAX_LINKS = 0xffff }; + static inline int udf_match(int len1, const unsigned char *name1, int len2, const unsigned char *name2) { @@ -650,7 +652,7 @@ static int udf_mkdir(struct inode *dir, struct dentry *dentry, int mode) struct udf_inode_info *iinfo; err = -EMLINK; - if (dir->i_nlink >= (256 << sizeof(dir->i_nlink)) - 1) + if (dir->i_nlink >= UDF_MAX_LINKS) goto out; err = -EIO; @@ -1034,9 +1036,8 @@ static int udf_link(struct dentry *old_dentry, struct inode *dir, struct fileIdentDesc cfi, *fi; int err; - if (inode->i_nlink >= (256 << sizeof(inode->i_nlink)) - 1) { + if (inode->i_nlink >= UDF_MAX_LINKS) return -EMLINK; - } fi = udf_add_entry(dir, dentry, &fibh, &cfi, &err); if (!fi) { @@ -1131,9 +1132,7 @@ static int udf_rename(struct inode *old_dir, struct dentry *old_dentry, goto end_rename; retval = -EMLINK; - if (!new_inode && - new_dir->i_nlink >= - (256 << sizeof(new_dir->i_nlink)) - 1) + if (!new_inode && new_dir->i_nlink >= UDF_MAX_LINKS) goto end_rename; } if (!nfi) { diff --git a/fs/ufs/namei.c b/fs/ufs/namei.c index 12f39b9e443..d6f681535eb 100644 --- a/fs/ufs/namei.c +++ b/fs/ufs/namei.c @@ -306,7 +306,6 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, new_de = ufs_find_entry(new_dir, &new_dentry->d_name, &new_page); if (!new_de) goto out_dir; - inode_inc_link_count(old_inode); ufs_set_link(new_dir, new_de, new_page, old_inode); new_inode->i_ctime = CURRENT_TIME_SEC; if (dir_de) @@ -318,12 +317,9 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, if (new_dir->i_nlink >= UFS_LINK_MAX) goto out_dir; } - inode_inc_link_count(old_inode); err = ufs_add_link(new_dentry, old_inode); - if (err) { - inode_dec_link_count(old_inode); + if (err) goto out_dir; - } if (dir_de) inode_inc_link_count(new_dir); } @@ -331,12 +327,11 @@ static int ufs_rename(struct inode *old_dir, struct dentry *old_dentry, /* * Like most other Unix systems, set the ctime for inodes on a * rename. - * inode_dec_link_count() will mark the inode dirty. */ old_inode->i_ctime = CURRENT_TIME_SEC; ufs_delete_entry(old_dir, old_de, old_page); - inode_dec_link_count(old_inode); + mark_inode_dirty(old_inode); if (dir_de) { ufs_set_link(old_inode, dir_de, dir_page, new_dir); diff --git a/fs/xfs/linux-2.6/xfs_discard.c b/fs/xfs/linux-2.6/xfs_discard.c index 05201ae719e..d61611c8801 100644 --- a/fs/xfs/linux-2.6/xfs_discard.c +++ b/fs/xfs/linux-2.6/xfs_discard.c @@ -152,6 +152,8 @@ xfs_ioc_trim( if (!capable(CAP_SYS_ADMIN)) return -XFS_ERROR(EPERM); + if (!blk_queue_discard(q)) + return -XFS_ERROR(EOPNOTSUPP); if (copy_from_user(&range, urange, sizeof(range))) return -XFS_ERROR(EFAULT); diff --git a/fs/xfs/linux-2.6/xfs_ioctl.c b/fs/xfs/linux-2.6/xfs_ioctl.c index f5e2a19e0f8..0ca0e3c024d 100644 --- a/fs/xfs/linux-2.6/xfs_ioctl.c +++ b/fs/xfs/linux-2.6/xfs_ioctl.c @@ -695,14 +695,19 @@ xfs_ioc_fsgeometry_v1( xfs_mount_t *mp, void __user *arg) { - xfs_fsop_geom_v1_t fsgeo; + xfs_fsop_geom_t fsgeo; int error; - error = xfs_fs_geometry(mp, (xfs_fsop_geom_t *)&fsgeo, 3); + error = xfs_fs_geometry(mp, &fsgeo, 3); if (error) return -error; - if (copy_to_user(arg, &fsgeo, sizeof(fsgeo))) + /* + * Caller should have passed an argument of type + * xfs_fsop_geom_v1_t. This is a proper subset of the + * xfs_fsop_geom_t that xfs_fs_geometry() fills in. + */ + if (copy_to_user(arg, &fsgeo, sizeof(xfs_fsop_geom_v1_t))) return -XFS_ERROR(EFAULT); return 0; } diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index cec89dd5d7d..85668efb3e3 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -53,6 +53,9 @@ xfs_fs_geometry( xfs_fsop_geom_t *geo, int new_version) { + + memset(geo, 0, sizeof(*geo)); + geo->blocksize = mp->m_sb.sb_blocksize; geo->rtextsize = mp->m_sb.sb_rextsize; geo->agblocks = mp->m_sb.sb_agblocks; diff --git a/include/asm-generic/pgtable.h b/include/asm-generic/pgtable.h index 31b6188df22..b4bfe338ea0 100644 --- a/include/asm-generic/pgtable.h +++ b/include/asm-generic/pgtable.h @@ -4,6 +4,8 @@ #ifndef __ASSEMBLY__ #ifdef CONFIG_MMU +#include <linux/mm_types.h> + #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS extern int ptep_set_access_flags(struct vm_area_struct *vma, unsigned long address, pte_t *ptep, diff --git a/include/drm/drmP.h b/include/drm/drmP.h index fe29aadb129..348843b8015 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -1101,7 +1101,7 @@ struct drm_device { struct platform_device *platformdev; /**< Platform device struture */ struct drm_sg_mem *sg; /**< Scatter gather memory */ - int num_crtcs; /**< Number of CRTCs on this device */ + unsigned int num_crtcs; /**< Number of CRTCs on this device */ void *dev_private; /**< device private data */ void *mm_private; struct address_space *dev_mapping; diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index 5cb86c307f5..fc487543381 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -99,7 +99,6 @@ struct rxrpc_key_token { * structure of raw payloads passed to add_key() or instantiate key */ struct rxrpc_key_data_v1 { - u32 kif_version; /* 1 */ u16 security_index; u16 ticket_length; u32 expiry; /* time_t */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 4d18ff34670..d5063e1b555 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -699,7 +699,7 @@ extern void blk_start_queue(struct request_queue *q); extern void blk_stop_queue(struct request_queue *q); extern void blk_sync_queue(struct request_queue *q); extern void __blk_stop_queue(struct request_queue *q); -extern void __blk_run_queue(struct request_queue *); +extern void __blk_run_queue(struct request_queue *q, bool force_kblockd); extern void blk_run_queue(struct request_queue *); extern int blk_rq_map_user(struct request_queue *, struct request *, struct rq_map_data *, void __user *, unsigned long, @@ -1088,7 +1088,6 @@ static inline void put_dev_sector(Sector p) struct work_struct; int kblockd_schedule_work(struct request_queue *q, struct work_struct *work); -int kblockd_schedule_delayed_work(struct request_queue *q, struct delayed_work *dwork, unsigned long delay); #ifdef CONFIG_BLK_CGROUP /* @@ -1136,7 +1135,6 @@ static inline uint64_t rq_io_start_time_ns(struct request *req) extern int blk_throtl_init(struct request_queue *q); extern void blk_throtl_exit(struct request_queue *q); extern int blk_throtl_bio(struct request_queue *q, struct bio **bio); -extern void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay); extern void throtl_shutdown_timer_wq(struct request_queue *q); #else /* CONFIG_BLK_DEV_THROTTLING */ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) @@ -1146,7 +1144,6 @@ static inline int blk_throtl_bio(struct request_queue *q, struct bio **bio) static inline int blk_throtl_init(struct request_queue *q) { return 0; } static inline int blk_throtl_exit(struct request_queue *q) { return 0; } -static inline void throtl_schedule_delayed_work(struct request_queue *q, unsigned long delay) {} static inline void throtl_shutdown_timer_wq(struct request_queue *q) {} #endif /* CONFIG_BLK_DEV_THROTTLING */ diff --git a/include/linux/blktrace_api.h b/include/linux/blktrace_api.h index 3395cf7130f..b22fb0d3db0 100644 --- a/include/linux/blktrace_api.h +++ b/include/linux/blktrace_api.h @@ -245,7 +245,6 @@ static inline int blk_cmd_buf_len(struct request *rq) extern void blk_dump_cmd(char *buf, struct request *rq); extern void blk_fill_rwbs(char *rwbs, u32 rw, int bytes); -extern void blk_fill_rwbs_rq(char *rwbs, struct request *rq); #endif /* CONFIG_EVENT_TRACING && CONFIG_BLOCK */ diff --git a/include/linux/dcbnl.h b/include/linux/dcbnl.h index 4c5b26e0cc4..a3680a16718 100644 --- a/include/linux/dcbnl.h +++ b/include/linux/dcbnl.h @@ -87,6 +87,45 @@ struct ieee_pfc { __u64 indications[IEEE_8021QAZ_MAX_TCS]; }; +/* CEE DCBX std supported values */ +#define CEE_DCBX_MAX_PGS 8 +#define CEE_DCBX_MAX_PRIO 8 + +/** + * struct cee_pg - CEE Prioity-Group managed object + * + * @willing: willing bit in the PG tlv + * @error: error bit in the PG tlv + * @pg_en: enable bit of the PG feature + * @tcs_supported: number of traffic classes supported + * @pg_bw: bandwidth percentage for each priority group + * @prio_pg: priority to PG mapping indexed by priority + */ +struct cee_pg { + __u8 willing; + __u8 error; + __u8 pg_en; + __u8 tcs_supported; + __u8 pg_bw[CEE_DCBX_MAX_PGS]; + __u8 prio_pg[CEE_DCBX_MAX_PGS]; +}; + +/** + * struct cee_pfc - CEE PFC managed object + * + * @willing: willing bit in the PFC tlv + * @error: error bit in the PFC tlv + * @pfc_en: bitmap indicating pfc enabled traffic classes + * @tcs_supported: number of traffic classes supported + */ +struct cee_pfc { + __u8 willing; + __u8 error; + __u8 pfc_en; + __u8 tcs_supported; +}; + + /* This structure contains the IEEE 802.1Qaz APP managed object. This * object is also used for the CEE std as well. There is no difference * between the objects. @@ -110,6 +149,20 @@ struct dcb_app { __u16 protocol; }; +/** + * struct dcb_peer_app_info - APP feature information sent by the peer + * + * @willing: willing bit in the peer APP tlv + * @error: error bit in the peer APP tlv + * + * In addition to this information the full peer APP tlv also contains + * a table of 'app_count' APP objects defined above. + */ +struct dcb_peer_app_info { + __u8 willing; + __u8 error; +}; + struct dcbmsg { __u8 dcb_family; __u8 cmd; @@ -144,6 +197,7 @@ struct dcbmsg { * @DCB_CMD_SDCBX: set DCBX engine configuration * @DCB_CMD_GFEATCFG: get DCBX features flags * @DCB_CMD_SFEATCFG: set DCBX features negotiation flags + * @DCB_CMD_CEE_GET: get CEE aggregated configuration */ enum dcbnl_commands { DCB_CMD_UNDEFINED, @@ -186,6 +240,8 @@ enum dcbnl_commands { DCB_CMD_GFEATCFG, DCB_CMD_SFEATCFG, + DCB_CMD_CEE_GET, + __DCB_CMD_ENUM_MAX, DCB_CMD_MAX = __DCB_CMD_ENUM_MAX - 1, }; @@ -208,6 +264,7 @@ enum dcbnl_commands { * @DCB_ATTR_IEEE: IEEE 802.1Qaz supported attributes (NLA_NESTED) * @DCB_ATTR_DCBX: DCBX engine configuration in the device (NLA_U8) * @DCB_ATTR_FEATCFG: DCBX features flags (NLA_NESTED) + * @DCB_ATTR_CEE: CEE std supported attributes (NLA_NESTED) */ enum dcbnl_attrs { DCB_ATTR_UNDEFINED, @@ -231,15 +288,32 @@ enum dcbnl_attrs { DCB_ATTR_DCBX, DCB_ATTR_FEATCFG, + /* CEE nested attributes */ + DCB_ATTR_CEE, + __DCB_ATTR_ENUM_MAX, DCB_ATTR_MAX = __DCB_ATTR_ENUM_MAX - 1, }; +/** + * enum ieee_attrs - IEEE 802.1Qaz get/set attributes + * + * @DCB_ATTR_IEEE_UNSPEC: unspecified + * @DCB_ATTR_IEEE_ETS: negotiated ETS configuration + * @DCB_ATTR_IEEE_PFC: negotiated PFC configuration + * @DCB_ATTR_IEEE_APP_TABLE: negotiated APP configuration + * @DCB_ATTR_IEEE_PEER_ETS: peer ETS configuration - get only + * @DCB_ATTR_IEEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_IEEE_PEER_APP: peer APP tlv - get only + */ enum ieee_attrs { DCB_ATTR_IEEE_UNSPEC, DCB_ATTR_IEEE_ETS, DCB_ATTR_IEEE_PFC, DCB_ATTR_IEEE_APP_TABLE, + DCB_ATTR_IEEE_PEER_ETS, + DCB_ATTR_IEEE_PEER_PFC, + DCB_ATTR_IEEE_PEER_APP, __DCB_ATTR_IEEE_MAX }; #define DCB_ATTR_IEEE_MAX (__DCB_ATTR_IEEE_MAX - 1) @@ -252,6 +326,31 @@ enum ieee_attrs_app { #define DCB_ATTR_IEEE_APP_MAX (__DCB_ATTR_IEEE_APP_MAX - 1) /** + * enum cee_attrs - CEE DCBX get attributes + * + * @DCB_ATTR_CEE_UNSPEC: unspecified + * @DCB_ATTR_CEE_PEER_PG: peer PG configuration - get only + * @DCB_ATTR_CEE_PEER_PFC: peer PFC configuration - get only + * @DCB_ATTR_CEE_PEER_APP: peer APP tlv - get only + */ +enum cee_attrs { + DCB_ATTR_CEE_UNSPEC, + DCB_ATTR_CEE_PEER_PG, + DCB_ATTR_CEE_PEER_PFC, + DCB_ATTR_CEE_PEER_APP_TABLE, + __DCB_ATTR_CEE_MAX +}; +#define DCB_ATTR_CEE_MAX (__DCB_ATTR_CEE_MAX - 1) + +enum peer_app_attr { + DCB_ATTR_CEE_PEER_APP_UNSPEC, + DCB_ATTR_CEE_PEER_APP_INFO, + DCB_ATTR_CEE_PEER_APP, + __DCB_ATTR_CEE_PEER_APP_MAX +}; +#define DCB_ATTR_CEE_PEER_APP_MAX (__DCB_ATTR_CEE_PEER_APP_MAX - 1) + +/** * enum dcbnl_pfc_attrs - DCB Priority Flow Control user priority nested attrs * * @DCB_PFC_UP_ATTR_UNDEFINED: unspecified attribute to catch errors diff --git a/include/linux/fs.h b/include/linux/fs.h index bd3215940c3..e38b50a4b9d 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -649,6 +649,7 @@ struct address_space { spinlock_t private_lock; /* for use by the address_space */ struct list_head private_list; /* ditto */ struct address_space *assoc_mapping; /* ditto */ + struct mutex unmap_mutex; /* to protect unmapping */ } __attribute__((aligned(sizeof(long)))); /* * On most architectures that alignment is already the case; but @@ -2139,7 +2140,7 @@ extern void check_disk_size_change(struct gendisk *disk, struct block_device *bdev); extern int revalidate_disk(struct gendisk *); extern int check_disk_change(struct block_device *); -extern int __invalidate_device(struct block_device *); +extern int __invalidate_device(struct block_device *, bool); extern int invalidate_partition(struct gendisk *, int); #endif unsigned long invalidate_mapping_pages(struct address_space *mapping, @@ -2225,6 +2226,7 @@ extern loff_t vfs_llseek(struct file *file, loff_t offset, int origin); extern int inode_init_always(struct super_block *, struct inode *); extern void inode_init_once(struct inode *); +extern void address_space_init_once(struct address_space *mapping); extern void ihold(struct inode * inode); extern void iput(struct inode *); extern struct inode * igrab(struct inode *); diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index 3fd36845ca4..ef4f0b6083a 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -71,6 +71,7 @@ struct wm8994 { u16 irq_masks_cache[WM8994_NUM_IRQ_REGS]; /* Used over suspend/resume */ + bool suspended; u16 ldo_regs[WM8994_NUM_LDO_REGS]; u16 gpio_regs[WM8994_NUM_GPIO_REGS]; diff --git a/include/linux/module.h b/include/linux/module.h index 9bdf27c7615..5de42043dff 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -62,7 +62,7 @@ struct module_version_attribute { struct module_attribute mattr; const char *module_name; const char *version; -}; +} __attribute__ ((__aligned__(sizeof(void *)))); struct module_kobject { diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index ffe56c16df8..71563e7b2bf 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -75,9 +75,6 @@ struct wireless_dev; #define NET_RX_SUCCESS 0 /* keep 'em coming, baby */ #define NET_RX_DROP 1 /* packet dropped */ -/* Initial net device group. All devices belong to group 0 by default. */ -#define INIT_NETDEV_GROUP 0 - /* * Transmit return codes: transmit return codes originate from three different * namespaces: @@ -141,6 +138,9 @@ static inline bool dev_xmit_complete(int rc) #define MAX_ADDR_LEN 32 /* Largest hardware address length */ +/* Initial net device group. All devices belong to group 0 by default. */ +#define INIT_NETDEV_GROUP 0 + #ifdef __KERNEL__ /* * Compute the worst case header length according to the protocols @@ -1765,8 +1765,7 @@ static inline void netif_tx_wake_all_queues(struct net_device *dev) static inline void netif_tx_stop_queue(struct netdev_queue *dev_queue) { if (WARN_ON(!dev_queue)) { - printk(KERN_INFO "netif_stop_queue() cannot be called before " - "register_netdev()"); + pr_info("netif_stop_queue() cannot be called before register_netdev()\n"); return; } set_bit(__QUEUE_STATE_XOFF, &dev_queue->state); diff --git a/include/linux/netlink.h b/include/linux/netlink.h index e2b9e63afa6..4c4ac3f3ce5 100644 --- a/include/linux/netlink.h +++ b/include/linux/netlink.h @@ -160,10 +160,6 @@ struct netlink_skb_parms { struct ucred creds; /* Skb credentials */ __u32 pid; __u32 dst_group; - kernel_cap_t eff_cap; - __u32 loginuid; /* Login (audit) uid */ - __u32 sessionid; /* Session id (audit) */ - __u32 sid; /* SELinux security id */ }; #define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 821ffb954f1..30022189104 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -1243,6 +1243,8 @@ enum nl80211_rate_info { * @NL80211_STA_INFO_LLID: the station's mesh LLID * @NL80211_STA_INFO_PLID: the station's mesh PLID * @NL80211_STA_INFO_PLINK_STATE: peer link state for the station + * @NL80211_STA_INFO_RX_BITRATE: last unicast data frame rx rate, nested + * attribute, like NL80211_STA_INFO_TX_BITRATE. * @__NL80211_STA_INFO_AFTER_LAST: internal * @NL80211_STA_INFO_MAX: highest possible station info attribute */ @@ -1261,6 +1263,7 @@ enum nl80211_sta_info { NL80211_STA_INFO_TX_RETRIES, NL80211_STA_INFO_TX_FAILED, NL80211_STA_INFO_SIGNAL_AVG, + NL80211_STA_INFO_RX_BITRATE, /* keep last */ __NL80211_STA_INFO_AFTER_LAST, diff --git a/include/linux/pm.h b/include/linux/pm.h index dd9c7ab3827..21415cc91cb 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -431,6 +431,8 @@ struct dev_pm_info { struct list_head entry; struct completion completion; struct wakeup_source *wakeup; +#else + unsigned int should_wakeup:1; #endif #ifdef CONFIG_PM_RUNTIME struct timer_list suspend_timer; diff --git a/include/linux/pm_wakeup.h b/include/linux/pm_wakeup.h index 9cff00dd6b6..03a67db03d0 100644 --- a/include/linux/pm_wakeup.h +++ b/include/linux/pm_wakeup.h @@ -109,11 +109,6 @@ static inline bool device_can_wakeup(struct device *dev) return dev->power.can_wakeup; } -static inline bool device_may_wakeup(struct device *dev) -{ - return false; -} - static inline struct wakeup_source *wakeup_source_create(const char *name) { return NULL; @@ -134,24 +129,32 @@ static inline void wakeup_source_unregister(struct wakeup_source *ws) {} static inline int device_wakeup_enable(struct device *dev) { - return -EINVAL; + dev->power.should_wakeup = true; + return 0; } static inline int device_wakeup_disable(struct device *dev) { + dev->power.should_wakeup = false; return 0; } -static inline int device_init_wakeup(struct device *dev, bool val) +static inline int device_set_wakeup_enable(struct device *dev, bool enable) { - dev->power.can_wakeup = val; - return val ? -EINVAL : 0; + dev->power.should_wakeup = enable; + return 0; } +static inline int device_init_wakeup(struct device *dev, bool val) +{ + device_set_wakeup_capable(dev, val); + device_set_wakeup_enable(dev, val); + return 0; +} -static inline int device_set_wakeup_enable(struct device *dev, bool enable) +static inline bool device_may_wakeup(struct device *dev) { - return -EINVAL; + return dev->power.can_wakeup && dev->power.should_wakeup; } static inline void __pm_stay_awake(struct wakeup_source *ws) {} diff --git a/include/linux/rio_regs.h b/include/linux/rio_regs.h index d63dcbaea16..9026b30238f 100644 --- a/include/linux/rio_regs.h +++ b/include/linux/rio_regs.h @@ -14,10 +14,12 @@ #define LINUX_RIO_REGS_H /* - * In RapidIO, each device has a 2MB configuration space that is + * In RapidIO, each device has a 16MB configuration space that is * accessed via maintenance transactions. Portions of configuration * space are standardized and/or reserved. */ +#define RIO_MAINT_SPACE_SZ 0x1000000 /* 16MB of RapidIO mainenance space */ + #define RIO_DEV_ID_CAR 0x00 /* [I] Device Identity CAR */ #define RIO_DEV_INFO_CAR 0x04 /* [I] Device Information CAR */ #define RIO_ASM_ID_CAR 0x08 /* [I] Assembly Identity CAR */ diff --git a/include/linux/thermal.h b/include/linux/thermal.h index 8651556dbd5..d3ec89fb412 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -172,6 +172,14 @@ void thermal_zone_device_update(struct thermal_zone_device *); struct thermal_cooling_device *thermal_cooling_device_register(char *, void *, const struct thermal_cooling_device_ops *); void thermal_cooling_device_unregister(struct thermal_cooling_device *); + +#ifdef CONFIG_NET extern int generate_netlink_event(u32 orig, enum events event); +#else +static inline int generate_netlink_event(u32 orig, enum events event) +{ + return 0; +} +#endif #endif /* __THERMAL_H__ */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index 679a0494b5f..1ac5786da14 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -413,7 +413,7 @@ struct station_parameters { * @STATION_INFO_PLID: @plid filled * @STATION_INFO_PLINK_STATE: @plink_state filled * @STATION_INFO_SIGNAL: @signal filled - * @STATION_INFO_TX_BITRATE: @tx_bitrate fields are filled + * @STATION_INFO_TX_BITRATE: @txrate fields are filled * (tx_bitrate, tx_bitrate_flags and tx_bitrate_mcs) * @STATION_INFO_RX_PACKETS: @rx_packets filled * @STATION_INFO_TX_PACKETS: @tx_packets filled @@ -421,6 +421,7 @@ struct station_parameters { * @STATION_INFO_TX_FAILED: @tx_failed filled * @STATION_INFO_RX_DROP_MISC: @rx_dropped_misc filled * @STATION_INFO_SIGNAL_AVG: @signal_avg filled + * @STATION_INFO_RX_BITRATE: @rxrate fields are filled */ enum station_info_flags { STATION_INFO_INACTIVE_TIME = 1<<0, @@ -437,6 +438,7 @@ enum station_info_flags { STATION_INFO_TX_FAILED = 1<<11, STATION_INFO_RX_DROP_MISC = 1<<12, STATION_INFO_SIGNAL_AVG = 1<<13, + STATION_INFO_RX_BITRATE = 1<<14, }; /** @@ -506,6 +508,7 @@ struct station_info { s8 signal; s8 signal_avg; struct rate_info txrate; + struct rate_info rxrate; u32 rx_packets; u32 tx_packets; u32 tx_retries; diff --git a/include/net/dcbnl.h b/include/net/dcbnl.h index a8e7852b10a..e5983c9053d 100644 --- a/include/net/dcbnl.h +++ b/include/net/dcbnl.h @@ -43,6 +43,8 @@ struct dcbnl_rtnl_ops { int (*ieee_setpfc) (struct net_device *, struct ieee_pfc *); int (*ieee_getapp) (struct net_device *, struct dcb_app *); int (*ieee_setapp) (struct net_device *, struct dcb_app *); + int (*ieee_peer_getets) (struct net_device *, struct ieee_ets *); + int (*ieee_peer_getpfc) (struct net_device *, struct ieee_pfc *); /* CEE std */ u8 (*getstate)(struct net_device *); @@ -77,7 +79,14 @@ struct dcbnl_rtnl_ops { u8 (*getdcbx)(struct net_device *); u8 (*setdcbx)(struct net_device *, u8); + /* peer apps */ + int (*peer_getappinfo)(struct net_device *, struct dcb_peer_app_info *, + u16 *); + int (*peer_getapptable)(struct net_device *, struct dcb_app *); + /* CEE peer */ + int (*cee_peer_getpg) (struct net_device *, struct cee_pg *); + int (*cee_peer_getpfc) (struct net_device *, struct cee_pfc *); }; #endif /* __NET_DCBNL_H__ */ diff --git a/include/net/dst.h b/include/net/dst.h index 4fedffd7c56..2a46cbaef92 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -421,29 +421,22 @@ extern void dst_init(void); /* Flags for xfrm_lookup flags argument. */ enum { - XFRM_LOOKUP_WAIT = 1 << 0, - XFRM_LOOKUP_ICMP = 1 << 1, + XFRM_LOOKUP_ICMP = 1 << 0, }; struct flowi; #ifndef CONFIG_XFRM -static inline int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, - int flags) +static inline struct dst_entry *xfrm_lookup(struct net *net, + struct dst_entry *dst_orig, + const struct flowi *fl, struct sock *sk, + int flags) { - return 0; + return dst_orig; } -static inline int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, - int flags) -{ - return 0; -} #else -extern int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, int flags); -extern int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, struct sock *sk, int flags); +extern struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, + const struct flowi *fl, struct sock *sk, + int flags); #endif #endif diff --git a/include/net/flow.h b/include/net/flow.h index f2080e65276..fd0413873b8 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -50,6 +50,7 @@ struct flowi { __u8 flags; #define FLOWI_FLAG_ANYSRC 0x01 #define FLOWI_FLAG_PRECOW_METRICS 0x02 +#define FLOWI_FLAG_CAN_SLEEP 0x04 union { struct { __be16 sport; diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 6e6dfd75768..7a37369f8ea 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -86,6 +86,19 @@ static inline struct inet_request_sock *inet_rsk(const struct request_sock *sk) return (struct inet_request_sock *)sk; } +struct inet_cork { + unsigned int flags; + unsigned int fragsize; + struct ip_options *opt; + struct dst_entry *dst; + int length; /* Total length of all frames */ + __be32 addr; + struct flowi fl; + struct page *page; + u32 off; + u8 tx_flags; +}; + struct ip_mc_socklist; struct ipv6_pinfo; struct rtable; @@ -143,15 +156,7 @@ struct inet_sock { int mc_index; __be32 mc_addr; struct ip_mc_socklist __rcu *mc_list; - struct { - unsigned int flags; - unsigned int fragsize; - struct ip_options *opt; - struct dst_entry *dst; - int length; /* Total length of all frames */ - __be32 addr; - struct flowi fl; - } cork; + struct inet_cork cork; }; #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ diff --git a/include/net/ip.h b/include/net/ip.h index 67fac78a186..a4f631108c5 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -116,8 +116,24 @@ extern int ip_append_data(struct sock *sk, extern int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb); extern ssize_t ip_append_page(struct sock *sk, struct page *page, int offset, size_t size, int flags); +extern struct sk_buff *__ip_make_skb(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork); +extern int ip_send_skb(struct sk_buff *skb); extern int ip_push_pending_frames(struct sock *sk); extern void ip_flush_pending_frames(struct sock *sk); +extern struct sk_buff *ip_make_skb(struct sock *sk, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, + struct rtable **rtp, + unsigned int flags); + +static inline struct sk_buff *ip_finish_skb(struct sock *sk) +{ + return __ip_make_skb(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); +} /* datagram.c */ extern int ip4_datagram_connect(struct sock *sk, diff --git a/include/net/ip_vs.h b/include/net/ip_vs.h index 5d75feadf4f..e74da41ebd1 100644 --- a/include/net/ip_vs.h +++ b/include/net/ip_vs.h @@ -494,7 +494,7 @@ struct ip_vs_conn_param { * IP_VS structure allocated for each dynamically scheduled connection */ struct ip_vs_conn { - struct list_head c_list; /* hashed list heads */ + struct hlist_node c_list; /* hashed list heads */ #ifdef CONFIG_NET_NS struct net *net; /* Name space */ #endif @@ -1019,6 +1019,8 @@ ip_vs_schedule(struct ip_vs_service *svc, struct sk_buff *skb, extern int ip_vs_leave(struct ip_vs_service *svc, struct sk_buff *skb, struct ip_vs_proto_data *pd); +extern void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg); + /* * IPVS control data and functions (from ip_vs_ctl.c) @@ -1241,6 +1243,20 @@ static inline void ip_vs_conn_drop_conntrack(struct ip_vs_conn *cp) /* CONFIG_IP_VS_NFCT */ #endif +static inline unsigned int +ip_vs_dest_conn_overhead(struct ip_vs_dest *dest) +{ + /* + * We think the overhead of processing active connections is 256 + * times higher than that of inactive connections in average. (This + * 256 times might not be accurate, we will change it later) We + * use the following formula to estimate the overhead now: + * dest->activeconns*256 + dest->inactconns + */ + return (atomic_read(&dest->activeconns) << 8) + + atomic_read(&dest->inactconns); +} + #endif /* __KERNEL__ */ #endif /* _NET_IP_VS_H */ diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 4a3cd2cd2f5..4635a5c8096 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -89,6 +89,18 @@ #define IPV6_ADDR_SCOPE_GLOBAL 0x0e /* + * Addr flags + */ +#ifdef __KERNEL__ +#define IPV6_ADDR_MC_FLAG_TRANSIENT(a) \ + ((a)->s6_addr[1] & 0x10) +#define IPV6_ADDR_MC_FLAG_PREFIX(a) \ + ((a)->s6_addr[1] & 0x20) +#define IPV6_ADDR_MC_FLAG_RENDEZVOUS(a) \ + ((a)->s6_addr[1] & 0x40) +#endif + +/* * fragmentation header */ @@ -512,12 +524,16 @@ extern void ip6_flush_pending_frames(struct sock *sk); extern int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl); -extern int ip6_dst_blackhole(struct sock *sk, - struct dst_entry **dst, - struct flowi *fl); -extern int ip6_sk_dst_lookup(struct sock *sk, - struct dst_entry **dst, - struct flowi *fl); +extern struct dst_entry * ip6_dst_lookup_flow(struct sock *sk, + struct flowi *fl, + const struct in6_addr *final_dst, + bool can_sleep); +extern struct dst_entry * ip6_sk_dst_lookup_flow(struct sock *sk, + struct flowi *fl, + const struct in6_addr *final_dst, + bool can_sleep); +extern struct dst_entry * ip6_blackhole_route(struct net *net, + struct dst_entry *orig_dst); /* * skb processing functions diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 8fcd1691cfb..2b072fa9939 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -599,9 +599,10 @@ ieee80211_tx_info_clear_status(struct ieee80211_tx_info *info) * the frame. * @RX_FLAG_FAILED_PLCP_CRC: Set this flag if the PCLP check failed on * the frame. - * @RX_FLAG_TSFT: The timestamp passed in the RX status (@mactime field) - * is valid. This is useful in monitor mode and necessary for beacon frames - * to enable IBSS merging. + * @RX_FLAG_MACTIME_MPDU: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the first symbol of the MPDU + * was received. This is useful in monitor mode and for proper IBSS + * merging. * @RX_FLAG_SHORTPRE: Short preamble was used for this frame * @RX_FLAG_HT: HT MCS was used and rate_idx is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used @@ -614,7 +615,7 @@ enum mac80211_rx_flags { RX_FLAG_IV_STRIPPED = 1<<4, RX_FLAG_FAILED_FCS_CRC = 1<<5, RX_FLAG_FAILED_PLCP_CRC = 1<<6, - RX_FLAG_TSFT = 1<<7, + RX_FLAG_MACTIME_MPDU = 1<<7, RX_FLAG_SHORTPRE = 1<<8, RX_FLAG_HT = 1<<9, RX_FLAG_40MHZ = 1<<10, @@ -1798,9 +1799,14 @@ enum ieee80211_ampdu_mlme_action { * ieee80211_remain_on_channel_expired(). This callback may sleep. * @cancel_remain_on_channel: Requests that an ongoing off-channel period is * aborted before it expires. This callback may sleep. + * @offchannel_tx: Transmit frame on another channel, wait for a response + * and return. Reliable TX status must be reported for the frame. If the + * return value is 1, then the @remain_on_channel will be used with a + * regular transmission (if supported.) + * @offchannel_tx_cancel_wait: cancel wait associated with offchannel TX */ struct ieee80211_ops { - int (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); + void (*tx)(struct ieee80211_hw *hw, struct sk_buff *skb); int (*start)(struct ieee80211_hw *hw); void (*stop)(struct ieee80211_hw *hw); int (*add_interface)(struct ieee80211_hw *hw, @@ -1877,6 +1883,11 @@ struct ieee80211_ops { enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); + int (*offchannel_tx)(struct ieee80211_hw *hw, struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait); + int (*offchannel_tx_cancel_wait)(struct ieee80211_hw *hw); }; /** diff --git a/include/net/route.h b/include/net/route.h index b3f89ad04e0..9257f5f1733 100644 --- a/include/net/route.h +++ b/include/net/route.h @@ -53,16 +53,20 @@ struct fib_info; struct rtable { struct dst_entry dst; - /* Cache lookup keys */ - struct flowi fl; + /* Lookup key. */ + __be32 rt_key_dst; + __be32 rt_key_src; int rt_genid; unsigned rt_flags; __u16 rt_type; + __u8 rt_tos; __be32 rt_dst; /* Path destination */ __be32 rt_src; /* Path source */ int rt_iif; + int rt_oif; + __u32 rt_mark; /* Info on neighbour */ __be32 rt_gateway; @@ -76,12 +80,12 @@ struct rtable { static inline bool rt_is_input_route(struct rtable *rt) { - return rt->fl.iif != 0; + return rt->rt_iif != 0; } static inline bool rt_is_output_route(struct rtable *rt) { - return rt->fl.iif == 0; + return rt->rt_iif == 0; } struct ip_rt_acct { @@ -118,9 +122,15 @@ extern void ip_rt_redirect(__be32 old_gw, __be32 dst, __be32 new_gw, __be32 src, struct net_device *dev); extern void rt_cache_flush(struct net *net, int how); extern void rt_cache_flush_batch(struct net *net); -extern int __ip_route_output_key(struct net *, struct rtable **, const struct flowi *flp); -extern int ip_route_output_key(struct net *, struct rtable **, struct flowi *flp); -extern int ip_route_output_flow(struct net *, struct rtable **rp, struct flowi *flp, struct sock *sk, int flags); +extern struct rtable *__ip_route_output_key(struct net *, const struct flowi *flp); +extern struct rtable *ip_route_output_flow(struct net *, struct flowi *flp, + struct sock *sk); +extern struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig); + +static inline struct rtable *ip_route_output_key(struct net *net, struct flowi *flp) +{ + return ip_route_output_flow(net, flp, NULL); +} extern int ip_route_input_common(struct sk_buff *skb, __be32 dst, __be32 src, u8 tos, struct net_device *devin, bool noref); @@ -165,10 +175,10 @@ static inline char rt_tos2priority(u8 tos) return ip_tos2prio[IPTOS_TOS(tos)>>1]; } -static inline int ip_route_connect(struct rtable **rp, __be32 dst, - __be32 src, u32 tos, int oif, u8 protocol, - __be16 sport, __be16 dport, struct sock *sk, - int flags) +static inline struct rtable *ip_route_connect(__be32 dst, __be32 src, u32 tos, + int oif, u8 protocol, + __be16 sport, __be16 dport, + struct sock *sk, bool can_sleep) { struct flowi fl = { .oif = oif, .mark = sk->sk_mark, @@ -178,38 +188,40 @@ static inline int ip_route_connect(struct rtable **rp, __be32 dst, .proto = protocol, .fl_ip_sport = sport, .fl_ip_dport = dport }; - int err; struct net *net = sock_net(sk); + struct rtable *rt; if (inet_sk(sk)->transparent) fl.flags |= FLOWI_FLAG_ANYSRC; if (protocol == IPPROTO_TCP) fl.flags |= FLOWI_FLAG_PRECOW_METRICS; + if (can_sleep) + fl.flags |= FLOWI_FLAG_CAN_SLEEP; if (!dst || !src) { - err = __ip_route_output_key(net, rp, &fl); - if (err) - return err; - fl.fl4_dst = (*rp)->rt_dst; - fl.fl4_src = (*rp)->rt_src; - ip_rt_put(*rp); - *rp = NULL; + rt = __ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return rt; + fl.fl4_dst = rt->rt_dst; + fl.fl4_src = rt->rt_src; + ip_rt_put(rt); } security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(net, rp, &fl, sk, flags); + return ip_route_output_flow(net, &fl, sk); } -static inline int ip_route_newports(struct rtable **rp, u8 protocol, - __be16 orig_sport, __be16 orig_dport, - __be16 sport, __be16 dport, struct sock *sk) +static inline struct rtable *ip_route_newports(struct rtable *rt, + u8 protocol, __be16 orig_sport, + __be16 orig_dport, __be16 sport, + __be16 dport, struct sock *sk) { if (sport != orig_sport || dport != orig_dport) { - struct flowi fl = { .oif = (*rp)->fl.oif, - .mark = (*rp)->fl.mark, - .fl4_dst = (*rp)->fl.fl4_dst, - .fl4_src = (*rp)->fl.fl4_src, - .fl4_tos = (*rp)->fl.fl4_tos, - .proto = (*rp)->fl.proto, + struct flowi fl = { .oif = rt->rt_oif, + .mark = rt->rt_mark, + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .proto = protocol, .fl_ip_sport = sport, .fl_ip_dport = dport }; @@ -217,12 +229,11 @@ static inline int ip_route_newports(struct rtable **rp, u8 protocol, fl.flags |= FLOWI_FLAG_ANYSRC; if (protocol == IPPROTO_TCP) fl.flags |= FLOWI_FLAG_PRECOW_METRICS; - ip_rt_put(*rp); - *rp = NULL; + ip_rt_put(rt); security_sk_classify_flow(sk, &fl); - return ip_route_output_flow(sock_net(sk), rp, &fl, sk, 0); + return ip_route_output_flow(sock_net(sk), &fl, sk); } - return 0; + return rt; } extern void rt_bind_peer(struct rtable *rt, int create); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 16626a04cb0..a9505b6a18e 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -83,6 +83,7 @@ struct Qdisc { struct gnet_stats_queue qstats; struct rcu_head rcu_head; spinlock_t busylock; + u32 limit; }; static inline bool qdisc_is_running(const struct Qdisc *qdisc) @@ -218,7 +219,7 @@ struct tcf_proto { struct qdisc_skb_cb { unsigned int pkt_len; - char data[]; + long data[]; }; static inline int qdisc_qlen(struct Qdisc *q) diff --git a/include/net/udp.h b/include/net/udp.h index e82f3a8c0f8..67ea6fcb3ec 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -144,6 +144,17 @@ static inline __wsum udp_csum_outgoing(struct sock *sk, struct sk_buff *skb) return csum; } +static inline __wsum udp_csum(struct sk_buff *skb) +{ + __wsum csum = csum_partial(skb_transport_header(skb), + sizeof(struct udphdr), skb->csum); + + for (skb = skb_shinfo(skb)->frag_list; skb; skb = skb->next) { + csum = csum_add(csum, skb->csum); + } + return csum; +} + /* hash routines shared between UDPv4/6 and UDP-Litev4/6 */ static inline void udp_lib_hash(struct sock *sk) { diff --git a/include/net/udplite.h b/include/net/udplite.h index afdffe607b2..673a024c6b2 100644 --- a/include/net/udplite.h +++ b/include/net/udplite.h @@ -115,6 +115,18 @@ static inline __wsum udplite_csum_outgoing(struct sock *sk, struct sk_buff *skb) return csum; } +static inline __wsum udplite_csum(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + int cscov = udplite_sender_cscov(udp_sk(sk), udp_hdr(skb)); + const int off = skb_transport_offset(skb); + const int len = skb->len - off; + + skb->ip_summed = CHECKSUM_NONE; /* no HW support for checksumming */ + + return skb_checksum(skb, off, min(cscov, len), 0); +} + extern void udplite4_register(void); extern int udplite_get_port(struct sock *sk, unsigned short snum, int (*scmp)(const struct sock *, const struct sock *)); diff --git a/include/net/xfrm.h b/include/net/xfrm.h index efded23dc4a..d5dcf397463 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -280,6 +280,7 @@ struct xfrm_policy_afinfo { int (*fill_dst)(struct xfrm_dst *xdst, struct net_device *dev, const struct flowi *fl); + struct dst_entry *(*blackhole_route)(struct net *net, struct dst_entry *orig); }; extern int xfrm_policy_register_afinfo(struct xfrm_policy_afinfo *afinfo); diff --git a/include/pcmcia/ds.h b/include/pcmcia/ds.h index 8479b66c067..3fd5064dd43 100644 --- a/include/pcmcia/ds.h +++ b/include/pcmcia/ds.h @@ -261,6 +261,7 @@ void pcmcia_disable_device(struct pcmcia_device *p_dev); #define CONF_ENABLE_ESR 0x0008 #define CONF_ENABLE_IOCARD 0x0010 /* auto-enabled if IO resources or IRQ * (CONF_ENABLE_IRQ) in use */ +#define CONF_ENABLE_ZVCARD 0x0020 /* flags used by pcmcia_loop_config() autoconfiguration */ #define CONF_AUTO_CHECK_VCC 0x0100 /* check for matching Vcc? */ diff --git a/include/sound/wm8903.h b/include/sound/wm8903.h index b4a0db2307e..1eeebd534f7 100644 --- a/include/sound/wm8903.h +++ b/include/sound/wm8903.h @@ -17,13 +17,9 @@ /* * R6 (0x06) - Mic Bias Control 0 */ -#define WM8903_MICDET_HYST_ENA 0x0080 /* MICDET_HYST_ENA */ -#define WM8903_MICDET_HYST_ENA_MASK 0x0080 /* MICDET_HYST_ENA */ -#define WM8903_MICDET_HYST_ENA_SHIFT 7 /* MICDET_HYST_ENA */ -#define WM8903_MICDET_HYST_ENA_WIDTH 1 /* MICDET_HYST_ENA */ -#define WM8903_MICDET_THR_MASK 0x0070 /* MICDET_THR - [6:4] */ -#define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [6:4] */ -#define WM8903_MICDET_THR_WIDTH 3 /* MICDET_THR - [6:4] */ +#define WM8903_MICDET_THR_MASK 0x0030 /* MICDET_THR - [5:4] */ +#define WM8903_MICDET_THR_SHIFT 4 /* MICDET_THR - [5:4] */ +#define WM8903_MICDET_THR_WIDTH 2 /* MICDET_THR - [5:4] */ #define WM8903_MICSHORT_THR_MASK 0x000C /* MICSHORT_THR - [3:2] */ #define WM8903_MICSHORT_THR_SHIFT 2 /* MICSHORT_THR - [3:2] */ #define WM8903_MICSHORT_THR_WIDTH 2 /* MICSHORT_THR - [3:2] */ diff --git a/include/trace/events/block.h b/include/trace/events/block.h index aba421d68f6..78f18adb49c 100644 --- a/include/trace/events/block.h +++ b/include/trace/events/block.h @@ -31,7 +31,7 @@ DECLARE_EVENT_CLASS(block_rq_with_error, 0 : blk_rq_sectors(rq); __entry->errors = rq->errors; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); blk_dump_cmd(__get_str(cmd), rq); ), @@ -118,7 +118,7 @@ DECLARE_EVENT_CLASS(block_rq, __entry->bytes = (rq->cmd_type == REQ_TYPE_BLOCK_PC) ? blk_rq_bytes(rq) : 0; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); blk_dump_cmd(__get_str(cmd), rq); memcpy(__entry->comm, current->comm, TASK_COMM_LEN); ), @@ -563,7 +563,7 @@ TRACE_EVENT(block_rq_remap, __entry->nr_sector = blk_rq_sectors(rq); __entry->old_dev = dev; __entry->old_sector = from; - blk_fill_rwbs_rq(__entry->rwbs, rq); + blk_fill_rwbs(__entry->rwbs, rq->cmd_flags, blk_rq_bytes(rq)); ), TP_printk("%d,%d %s %llu + %u <- (%d,%d) %llu", diff --git a/kernel/audit.c b/kernel/audit.c index 162e88e33bc..93950031706 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -673,9 +673,9 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) pid = NETLINK_CREDS(skb)->pid; uid = NETLINK_CREDS(skb)->uid; - loginuid = NETLINK_CB(skb).loginuid; - sessionid = NETLINK_CB(skb).sessionid; - sid = NETLINK_CB(skb).sid; + loginuid = audit_get_loginuid(current); + sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &sid); seq = nlh->nlmsg_seq; data = NLMSG_DATA(nlh); diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index add2819af71..f8277c80d67 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1238,6 +1238,7 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, for (i = 0; i < rule->field_count; i++) { struct audit_field *f = &rule->fields[i]; int result = 0; + u32 sid; switch (f->type) { case AUDIT_PID: @@ -1250,19 +1251,22 @@ static int audit_filter_user_rules(struct netlink_skb_parms *cb, result = audit_comparator(cb->creds.gid, f->op, f->val); break; case AUDIT_LOGINUID: - result = audit_comparator(cb->loginuid, f->op, f->val); + result = audit_comparator(audit_get_loginuid(current), + f->op, f->val); break; case AUDIT_SUBJ_USER: case AUDIT_SUBJ_ROLE: case AUDIT_SUBJ_TYPE: case AUDIT_SUBJ_SEN: case AUDIT_SUBJ_CLR: - if (f->lsm_rule) - result = security_audit_rule_match(cb->sid, + if (f->lsm_rule) { + security_task_getsecid(current, &sid); + result = security_audit_rule_match(sid, f->type, f->op, f->lsm_rule, NULL); + } break; } diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 4571ae7e085..99c3bc8a6fb 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -3,6 +3,12 @@ */ #include <linux/irqdesc.h> +#ifdef CONFIG_SPARSE_IRQ +# define IRQ_BITMAP_BITS (NR_IRQS + 8196) +#else +# define IRQ_BITMAP_BITS NR_IRQS +#endif + extern int noirqdebug; #define irq_data_to_desc(data) container_of(data, struct irq_desc, irq_data) diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 282f20230e6..2039bea31bd 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -94,7 +94,7 @@ int nr_irqs = NR_IRQS; EXPORT_SYMBOL_GPL(nr_irqs); static DEFINE_MUTEX(sparse_irq_lock); -static DECLARE_BITMAP(allocated_irqs, NR_IRQS); +static DECLARE_BITMAP(allocated_irqs, IRQ_BITMAP_BITS); #ifdef CONFIG_SPARSE_IRQ @@ -217,6 +217,15 @@ int __init early_irq_init(void) initcnt = arch_probe_nr_irqs(); printk(KERN_INFO "NR_IRQS:%d nr_irqs:%d %d\n", NR_IRQS, nr_irqs, initcnt); + if (WARN_ON(nr_irqs > IRQ_BITMAP_BITS)) + nr_irqs = IRQ_BITMAP_BITS; + + if (WARN_ON(initcnt > IRQ_BITMAP_BITS)) + initcnt = IRQ_BITMAP_BITS; + + if (initcnt > nr_irqs) + nr_irqs = initcnt; + for (i = 0; i < initcnt; i++) { desc = alloc_desc(i, node); set_bit(i, allocated_irqs); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 0587c5ceaed..094fafe86c9 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1182,7 +1182,7 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, if (retval) kfree(action); -#ifdef CONFIG_DEBUG_SHIRQ +#ifdef CONFIG_DEBUG_SHIRQ_FIXME if (!retval && (irqflags & IRQF_SHARED)) { /* * It's a shared IRQ -- the driver ought to be prepared for it diff --git a/kernel/irq/resend.c b/kernel/irq/resend.c index 891115a929a..dc49358b73f 100644 --- a/kernel/irq/resend.c +++ b/kernel/irq/resend.c @@ -23,7 +23,7 @@ #ifdef CONFIG_HARDIRQS_SW_RESEND /* Bitmap to handle software resend of interrupts: */ -static DECLARE_BITMAP(irqs_resend, NR_IRQS); +static DECLARE_BITMAP(irqs_resend, IRQ_BITMAP_BITS); /* * Run software resends of IRQ's diff --git a/kernel/perf_event.c b/kernel/perf_event.c index 999835b6112..656222fcf76 100644 --- a/kernel/perf_event.c +++ b/kernel/perf_event.c @@ -782,6 +782,10 @@ retry: raw_spin_unlock_irq(&ctx->lock); } +#define MAX_INTERRUPTS (~0ULL) + +static void perf_log_throttle(struct perf_event *event, int enable); + static int event_sched_in(struct perf_event *event, struct perf_cpu_context *cpuctx, @@ -794,6 +798,17 @@ event_sched_in(struct perf_event *event, event->state = PERF_EVENT_STATE_ACTIVE; event->oncpu = smp_processor_id(); + + /* + * Unthrottle events, since we scheduled we might have missed several + * ticks already, also for a heavily scheduling task there is little + * guarantee it'll get a tick in a timely manner. + */ + if (unlikely(event->hw.interrupts == MAX_INTERRUPTS)) { + perf_log_throttle(event, 1); + event->hw.interrupts = 0; + } + /* * The new state must be visible before we turn it on in the hardware: */ @@ -1596,10 +1611,6 @@ void __perf_event_task_sched_in(struct task_struct *task) } } -#define MAX_INTERRUPTS (~0ULL) - -static void perf_log_throttle(struct perf_event *event, int enable); - static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count) { u64 frequency = event->attr.sample_freq; diff --git a/kernel/time/tick-broadcast.c b/kernel/time/tick-broadcast.c index 48b2761b566..a3b5aff6260 100644 --- a/kernel/time/tick-broadcast.c +++ b/kernel/time/tick-broadcast.c @@ -600,4 +600,14 @@ int tick_broadcast_oneshot_active(void) return tick_broadcast_device.mode == TICKDEV_MODE_ONESHOT; } +/* + * Check whether the broadcast device supports oneshot. + */ +bool tick_broadcast_oneshot_available(void) +{ + struct clock_event_device *bc = tick_broadcast_device.evtdev; + + return bc ? bc->features & CLOCK_EVT_FEAT_ONESHOT : false; +} + #endif diff --git a/kernel/time/tick-common.c b/kernel/time/tick-common.c index 051bc80a0c4..ed228ef6f6b 100644 --- a/kernel/time/tick-common.c +++ b/kernel/time/tick-common.c @@ -51,7 +51,11 @@ int tick_is_oneshot_available(void) { struct clock_event_device *dev = __this_cpu_read(tick_cpu_device.evtdev); - return dev && (dev->features & CLOCK_EVT_FEAT_ONESHOT); + if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT)) + return 0; + if (!(dev->features & CLOCK_EVT_FEAT_C3STOP)) + return 1; + return tick_broadcast_oneshot_available(); } /* diff --git a/kernel/time/tick-internal.h b/kernel/time/tick-internal.h index 290eefbc1f6..f65d3a723a6 100644 --- a/kernel/time/tick-internal.h +++ b/kernel/time/tick-internal.h @@ -36,6 +36,7 @@ extern void tick_shutdown_broadcast_oneshot(unsigned int *cpup); extern int tick_resume_broadcast_oneshot(struct clock_event_device *bc); extern int tick_broadcast_oneshot_active(void); extern void tick_check_oneshot_broadcast(int cpu); +bool tick_broadcast_oneshot_available(void); # else /* BROADCAST */ static inline void tick_broadcast_setup_oneshot(struct clock_event_device *bc) { @@ -46,6 +47,7 @@ static inline void tick_broadcast_switch_to_oneshot(void) { } static inline void tick_shutdown_broadcast_oneshot(unsigned int *cpup) { } static inline int tick_broadcast_oneshot_active(void) { return 0; } static inline void tick_check_oneshot_broadcast(int cpu) { } +static inline bool tick_broadcast_oneshot_available(void) { return true; } # endif /* !BROADCAST */ #else /* !ONESHOT */ @@ -76,6 +78,7 @@ static inline int tick_resume_broadcast_oneshot(struct clock_event_device *bc) return 0; } static inline int tick_broadcast_oneshot_active(void) { return 0; } +static inline bool tick_broadcast_oneshot_available(void) { return false; } #endif /* !TICK_ONESHOT */ /* diff --git a/kernel/trace/blktrace.c b/kernel/trace/blktrace.c index d95721f3370..cbafed7d4f3 100644 --- a/kernel/trace/blktrace.c +++ b/kernel/trace/blktrace.c @@ -1827,21 +1827,5 @@ void blk_fill_rwbs(char *rwbs, u32 rw, int bytes) rwbs[i] = '\0'; } -void blk_fill_rwbs_rq(char *rwbs, struct request *rq) -{ - int rw = rq->cmd_flags & 0x03; - int bytes; - - if (rq->cmd_flags & REQ_DISCARD) - rw |= REQ_DISCARD; - - if (rq->cmd_flags & REQ_SECURE) - rw |= REQ_SECURE; - - bytes = blk_rq_bytes(rq); - - blk_fill_rwbs(rwbs, rw, bytes); -} - #endif /* CONFIG_EVENT_TRACING */ diff --git a/lib/nlattr.c b/lib/nlattr.c index 5021cbc3441..ac09f2226dc 100644 --- a/lib/nlattr.c +++ b/lib/nlattr.c @@ -148,7 +148,7 @@ nla_policy_len(const struct nla_policy *p, int n) { int i, len = 0; - for (i = 0; i < n; i++) { + for (i = 0; i < n; i++, p++) { if (p->len) len += nla_total_size(p->len); else if (nla_attr_minlen[p->type]) diff --git a/lib/swiotlb.c b/lib/swiotlb.c index c47bbe11b80..93ca08b8a45 100644 --- a/lib/swiotlb.c +++ b/lib/swiotlb.c @@ -686,8 +686,10 @@ dma_addr_t swiotlb_map_page(struct device *dev, struct page *page, /* * Ensure that the address returned is DMA'ble */ - if (!dma_capable(dev, dev_addr, size)) - panic("map_single: bounce buffer is not DMA'ble"); + if (!dma_capable(dev, dev_addr, size)) { + swiotlb_tbl_unmap_single(dev, map, size, dir); + dev_addr = swiotlb_virt_to_bus(dev, io_tlb_overflow_buffer); + } return dev_addr; } diff --git a/mm/memory.c b/mm/memory.c index 8e8c1832486..5823698c2b7 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2648,6 +2648,7 @@ void unmap_mapping_range(struct address_space *mapping, details.last_index = ULONG_MAX; details.i_mmap_lock = &mapping->i_mmap_lock; + mutex_lock(&mapping->unmap_mutex); spin_lock(&mapping->i_mmap_lock); /* Protect against endless unmapping loops */ @@ -2664,6 +2665,7 @@ void unmap_mapping_range(struct address_space *mapping, if (unlikely(!list_empty(&mapping->i_mmap_nonlinear))) unmap_mapping_range_list(&mapping->i_mmap_nonlinear, &details); spin_unlock(&mapping->i_mmap_lock); + mutex_unlock(&mapping->unmap_mutex); } EXPORT_SYMBOL(unmap_mapping_range); diff --git a/mm/mempolicy.c b/mm/mempolicy.c index 368fc9d2361..49355a970be 100644 --- a/mm/mempolicy.c +++ b/mm/mempolicy.c @@ -1830,7 +1830,7 @@ alloc_pages_vma(gfp_t gfp, int order, struct vm_area_struct *vma, if (unlikely(pol->mode == MPOL_INTERLEAVE)) { unsigned nid; - nid = interleave_nid(pol, vma, addr, PAGE_SHIFT); + nid = interleave_nid(pol, vma, addr, PAGE_SHIFT + order); mpol_cond_put(pol); page = alloc_page_interleave(gfp, order, nid); put_mems_allowed(); diff --git a/mm/migrate.c b/mm/migrate.c index 76611525380..352de555626 100644 --- a/mm/migrate.c +++ b/mm/migrate.c @@ -1287,14 +1287,14 @@ SYSCALL_DEFINE6(move_pages, pid_t, pid, unsigned long, nr_pages, return -EPERM; /* Find the mm_struct */ - read_lock(&tasklist_lock); + rcu_read_lock(); task = pid ? find_task_by_vpid(pid) : current; if (!task) { - read_unlock(&tasklist_lock); + rcu_read_unlock(); return -ESRCH; } mm = get_task_mm(task); - read_unlock(&tasklist_lock); + rcu_read_unlock(); if (!mm) return -EINVAL; diff --git a/mm/mremap.c b/mm/mremap.c index 9925b6391b8..1de98d492dd 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -94,9 +94,7 @@ static void move_ptes(struct vm_area_struct *vma, pmd_t *old_pmd, */ mapping = vma->vm_file->f_mapping; spin_lock(&mapping->i_mmap_lock); - if (new_vma->vm_truncate_count && - new_vma->vm_truncate_count != vma->vm_truncate_count) - new_vma->vm_truncate_count = 0; + new_vma->vm_truncate_count = 0; } /* diff --git a/mm/page_alloc.c b/mm/page_alloc.c index a873e61e312..cdef1d4b4e4 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -5376,10 +5376,9 @@ __count_immobile_pages(struct zone *zone, struct page *page, int count) for (found = 0, iter = 0; iter < pageblock_nr_pages; iter++) { unsigned long check = pfn + iter; - if (!pfn_valid_within(check)) { - iter++; + if (!pfn_valid_within(check)) continue; - } + page = pfn_to_page(check); if (!page_count(page)) { if (PageBuddy(page)) diff --git a/mm/swapfile.c b/mm/swapfile.c index 07a458d72fa..0341c5700e3 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -1940,7 +1940,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) error = -EINVAL; if (S_ISBLK(inode->i_mode)) { - bdev = I_BDEV(inode); + bdev = bdgrab(I_BDEV(inode)); error = blkdev_get(bdev, FMODE_READ | FMODE_WRITE | FMODE_EXCL, sys_swapon); if (error < 0) { diff --git a/mm/truncate.c b/mm/truncate.c index 49feb46e77b..d64296be00d 100644 --- a/mm/truncate.c +++ b/mm/truncate.c @@ -225,6 +225,7 @@ void truncate_inode_pages_range(struct address_space *mapping, next = start; while (next <= end && pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) { + mem_cgroup_uncharge_start(); for (i = 0; i < pagevec_count(&pvec); i++) { struct page *page = pvec.pages[i]; pgoff_t page_index = page->index; @@ -247,6 +248,7 @@ void truncate_inode_pages_range(struct address_space *mapping, unlock_page(page); } pagevec_release(&pvec); + mem_cgroup_uncharge_end(); cond_resched(); } diff --git a/mm/vmscan.c b/mm/vmscan.c index 17497d0cd8b..6771ea70bfe 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1841,16 +1841,28 @@ static inline bool should_continue_reclaim(struct zone *zone, if (!(sc->reclaim_mode & RECLAIM_MODE_COMPACTION)) return false; - /* - * If we failed to reclaim and have scanned the full list, stop. - * NOTE: Checking just nr_reclaimed would exit reclaim/compaction far - * faster but obviously would be less likely to succeed - * allocation. If this is desirable, use GFP_REPEAT to decide - * if both reclaimed and scanned should be checked or just - * reclaimed - */ - if (!nr_reclaimed && !nr_scanned) - return false; + /* Consider stopping depending on scan and reclaim activity */ + if (sc->gfp_mask & __GFP_REPEAT) { + /* + * For __GFP_REPEAT allocations, stop reclaiming if the + * full LRU list has been scanned and we are still failing + * to reclaim pages. This full LRU scan is potentially + * expensive but a __GFP_REPEAT caller really wants to succeed + */ + if (!nr_reclaimed && !nr_scanned) + return false; + } else { + /* + * For non-__GFP_REPEAT allocations which can presumably + * fail without consequence, stop if we failed to reclaim + * any pages from the last SWAP_CLUSTER_MAX number of + * pages that were scanned. This will return to the + * caller faster at the risk reclaim/compaction and + * the resulting allocation attempt fails + */ + if (!nr_reclaimed) + return false; + } /* * If we have not reclaimed enough pages for compaction and the diff --git a/net/atm/clip.c b/net/atm/clip.c index d257da50fcf..810a1294edd 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -520,9 +520,9 @@ static int clip_setentry(struct atm_vcc *vcc, __be32 ip) unlink_clip_vcc(clip_vcc); return 0; } - error = ip_route_output_key(&init_net, &rt, &fl); - if (error) - return error; + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); neigh = __neigh_lookup(&clip_tbl, &ip, rt->dst.dev, 1); ip_rt_put(rt); if (!neigh) diff --git a/net/batman-adv/aggregation.c b/net/batman-adv/aggregation.c index 1997725a243..af45d6b2031 100644 --- a/net/batman-adv/aggregation.c +++ b/net/batman-adv/aggregation.c @@ -35,7 +35,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet, int packet_len, unsigned long send_time, bool directlink, - struct batman_if *if_incoming, + struct hard_iface *if_incoming, struct forw_packet *forw_packet) { struct batman_packet *batman_packet = @@ -99,7 +99,7 @@ static bool can_aggregate_with(struct batman_packet *new_batman_packet, /* create a new aggregated packet and add this packet to it */ static void new_aggregated_packet(unsigned char *packet_buff, int packet_len, unsigned long send_time, bool direct_link, - struct batman_if *if_incoming, + struct hard_iface *if_incoming, int own_packet) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); @@ -188,7 +188,7 @@ static void aggregate(struct forw_packet *forw_packet_aggr, void add_bat_packet_to_list(struct bat_priv *bat_priv, unsigned char *packet_buff, int packet_len, - struct batman_if *if_incoming, char own_packet, + struct hard_iface *if_incoming, char own_packet, unsigned long send_time) { /** @@ -247,7 +247,7 @@ void add_bat_packet_to_list(struct bat_priv *bat_priv, /* unpack the aggregated packets and process them one by one */ void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct batman_if *if_incoming) + int packet_len, struct hard_iface *if_incoming) { struct batman_packet *batman_packet; int buff_pos = 0; diff --git a/net/batman-adv/aggregation.h b/net/batman-adv/aggregation.h index 6ce305b4001..062204289d1 100644 --- a/net/batman-adv/aggregation.h +++ b/net/batman-adv/aggregation.h @@ -35,9 +35,9 @@ static inline int aggregated_packet(int buff_pos, int packet_len, int num_hna) void add_bat_packet_to_list(struct bat_priv *bat_priv, unsigned char *packet_buff, int packet_len, - struct batman_if *if_incoming, char own_packet, + struct hard_iface *if_incoming, char own_packet, unsigned long send_time); void receive_aggr_bat_packet(struct ethhdr *ethhdr, unsigned char *packet_buff, - int packet_len, struct batman_if *if_incoming); + int packet_len, struct hard_iface *if_incoming); #endif /* _NET_BATMAN_ADV_AGGREGATION_H_ */ diff --git a/net/batman-adv/bat_sysfs.c b/net/batman-adv/bat_sysfs.c index f7b93a0805f..e449bf6353e 100644 --- a/net/batman-adv/bat_sysfs.c +++ b/net/batman-adv/bat_sysfs.c @@ -441,16 +441,16 @@ static ssize_t show_mesh_iface(struct kobject *kobj, struct attribute *attr, char *buff) { struct net_device *net_dev = kobj_to_netdev(kobj); - struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); ssize_t length; - if (!batman_if) + if (!hard_iface) return 0; - length = sprintf(buff, "%s\n", batman_if->if_status == IF_NOT_IN_USE ? - "none" : batman_if->soft_iface->name); + length = sprintf(buff, "%s\n", hard_iface->if_status == IF_NOT_IN_USE ? + "none" : hard_iface->soft_iface->name); - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(hard_iface); return length; } @@ -459,11 +459,11 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, char *buff, size_t count) { struct net_device *net_dev = kobj_to_netdev(kobj); - struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); int status_tmp = -1; - int ret; + int ret = count; - if (!batman_if) + if (!hard_iface) return count; if (buff[count - 1] == '\n') @@ -472,7 +472,7 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, if (strlen(buff) >= IFNAMSIZ) { pr_err("Invalid parameter for 'mesh_iface' setting received: " "interface name too long '%s'\n", buff); - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(hard_iface); return -EINVAL; } @@ -481,30 +481,31 @@ static ssize_t store_mesh_iface(struct kobject *kobj, struct attribute *attr, else status_tmp = IF_I_WANT_YOU; - if ((batman_if->if_status == status_tmp) || ((batman_if->soft_iface) && - (strncmp(batman_if->soft_iface->name, buff, IFNAMSIZ) == 0))) { - kref_put(&batman_if->refcount, hardif_free_ref); - return count; - } + if (hard_iface->if_status == status_tmp) + goto out; + + if ((hard_iface->soft_iface) && + (strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0)) + goto out; if (status_tmp == IF_NOT_IN_USE) { rtnl_lock(); - hardif_disable_interface(batman_if); + hardif_disable_interface(hard_iface); rtnl_unlock(); - kref_put(&batman_if->refcount, hardif_free_ref); - return count; + goto out; } /* if the interface already is in use */ - if (batman_if->if_status != IF_NOT_IN_USE) { + if (hard_iface->if_status != IF_NOT_IN_USE) { rtnl_lock(); - hardif_disable_interface(batman_if); + hardif_disable_interface(hard_iface); rtnl_unlock(); } - ret = hardif_enable_interface(batman_if, buff); - kref_put(&batman_if->refcount, hardif_free_ref); + ret = hardif_enable_interface(hard_iface, buff); +out: + hardif_free_ref(hard_iface); return ret; } @@ -512,13 +513,13 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, char *buff) { struct net_device *net_dev = kobj_to_netdev(kobj); - struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); ssize_t length; - if (!batman_if) + if (!hard_iface) return 0; - switch (batman_if->if_status) { + switch (hard_iface->if_status) { case IF_TO_BE_REMOVED: length = sprintf(buff, "disabling\n"); break; @@ -537,7 +538,7 @@ static ssize_t show_iface_status(struct kobject *kobj, struct attribute *attr, break; } - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(hard_iface); return length; } diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index 429a013d2e0..3cc43558cf9 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -28,58 +28,75 @@ #include <linux/udp.h> #include <linux/if_vlan.h> -static void gw_node_free_ref(struct kref *refcount) +static void gw_node_free_rcu(struct rcu_head *rcu) { struct gw_node *gw_node; - gw_node = container_of(refcount, struct gw_node, refcount); + gw_node = container_of(rcu, struct gw_node, rcu); kfree(gw_node); } -static void gw_node_free_rcu(struct rcu_head *rcu) +static void gw_node_free_ref(struct gw_node *gw_node) { - struct gw_node *gw_node; - - gw_node = container_of(rcu, struct gw_node, rcu); - kref_put(&gw_node->refcount, gw_node_free_ref); + if (atomic_dec_and_test(&gw_node->refcount)) + call_rcu(&gw_node->rcu, gw_node_free_rcu); } void *gw_get_selected(struct bat_priv *bat_priv) { - struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + struct gw_node *curr_gateway_tmp; + struct orig_node *orig_node = NULL; + rcu_read_lock(); + curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); if (!curr_gateway_tmp) - return NULL; + goto out; + + orig_node = curr_gateway_tmp->orig_node; + if (!orig_node) + goto out; - return curr_gateway_tmp->orig_node; + if (!atomic_inc_not_zero(&orig_node->refcount)) + orig_node = NULL; + +out: + rcu_read_unlock(); + return orig_node; } void gw_deselect(struct bat_priv *bat_priv) { - struct gw_node *gw_node = bat_priv->curr_gw; + struct gw_node *gw_node; - bat_priv->curr_gw = NULL; + spin_lock_bh(&bat_priv->gw_list_lock); + gw_node = rcu_dereference(bat_priv->curr_gw); + rcu_assign_pointer(bat_priv->curr_gw, NULL); + spin_unlock_bh(&bat_priv->gw_list_lock); if (gw_node) - kref_put(&gw_node->refcount, gw_node_free_ref); + gw_node_free_ref(gw_node); } -static struct gw_node *gw_select(struct bat_priv *bat_priv, - struct gw_node *new_gw_node) +static void gw_select(struct bat_priv *bat_priv, struct gw_node *new_gw_node) { - struct gw_node *curr_gw_node = bat_priv->curr_gw; + struct gw_node *curr_gw_node; - if (new_gw_node) - kref_get(&new_gw_node->refcount); + if (new_gw_node && !atomic_inc_not_zero(&new_gw_node->refcount)) + new_gw_node = NULL; + + spin_lock_bh(&bat_priv->gw_list_lock); + curr_gw_node = rcu_dereference(bat_priv->curr_gw); + rcu_assign_pointer(bat_priv->curr_gw, new_gw_node); + spin_unlock_bh(&bat_priv->gw_list_lock); - bat_priv->curr_gw = new_gw_node; - return curr_gw_node; + if (curr_gw_node) + gw_node_free_ref(curr_gw_node); } void gw_election(struct bat_priv *bat_priv) { struct hlist_node *node; - struct gw_node *gw_node, *curr_gw_tmp = NULL, *old_gw_node = NULL; + struct gw_node *gw_node, *curr_gw, *curr_gw_tmp = NULL; uint8_t max_tq = 0; uint32_t max_gw_factor = 0, tmp_gw_factor = 0; int down, up; @@ -93,19 +110,23 @@ void gw_election(struct bat_priv *bat_priv) if (atomic_read(&bat_priv->gw_mode) != GW_MODE_CLIENT) return; - if (bat_priv->curr_gw) + rcu_read_lock(); + curr_gw = rcu_dereference(bat_priv->curr_gw); + if (curr_gw) { + rcu_read_unlock(); return; + } - rcu_read_lock(); if (hlist_empty(&bat_priv->gw_list)) { - rcu_read_unlock(); - if (bat_priv->curr_gw) { + if (curr_gw) { + rcu_read_unlock(); bat_dbg(DBG_BATMAN, bat_priv, "Removing selected gateway - " "no gateway in range\n"); gw_deselect(bat_priv); - } + } else + rcu_read_unlock(); return; } @@ -154,12 +175,12 @@ void gw_election(struct bat_priv *bat_priv) max_gw_factor = tmp_gw_factor; } - if (bat_priv->curr_gw != curr_gw_tmp) { - if ((bat_priv->curr_gw) && (!curr_gw_tmp)) + if (curr_gw != curr_gw_tmp) { + if ((curr_gw) && (!curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, "Removing selected gateway - " "no gateway in range\n"); - else if ((!bat_priv->curr_gw) && (curr_gw_tmp)) + else if ((!curr_gw) && (curr_gw_tmp)) bat_dbg(DBG_BATMAN, bat_priv, "Adding route to gateway %pM " "(gw_flags: %i, tq: %i)\n", @@ -174,43 +195,43 @@ void gw_election(struct bat_priv *bat_priv) curr_gw_tmp->orig_node->gw_flags, curr_gw_tmp->orig_node->router->tq_avg); - old_gw_node = gw_select(bat_priv, curr_gw_tmp); + gw_select(bat_priv, curr_gw_tmp); } rcu_read_unlock(); - - /* the kfree() has to be outside of the rcu lock */ - if (old_gw_node) - kref_put(&old_gw_node->refcount, gw_node_free_ref); } void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) { - struct gw_node *curr_gateway_tmp = bat_priv->curr_gw; + struct gw_node *curr_gateway_tmp; uint8_t gw_tq_avg, orig_tq_avg; + rcu_read_lock(); + curr_gateway_tmp = rcu_dereference(bat_priv->curr_gw); if (!curr_gateway_tmp) - return; + goto out_rcu; if (!curr_gateway_tmp->orig_node) - goto deselect; + goto deselect_rcu; if (!curr_gateway_tmp->orig_node->router) - goto deselect; + goto deselect_rcu; /* this node already is the gateway */ if (curr_gateway_tmp->orig_node == orig_node) - return; + goto out_rcu; if (!orig_node->router) - return; + goto out_rcu; gw_tq_avg = curr_gateway_tmp->orig_node->router->tq_avg; + rcu_read_unlock(); + orig_tq_avg = orig_node->router->tq_avg; /* the TQ value has to be better */ if (orig_tq_avg < gw_tq_avg) - return; + goto out; /** * if the routing class is greater than 3 the value tells us how much @@ -218,15 +239,23 @@ void gw_check_election(struct bat_priv *bat_priv, struct orig_node *orig_node) **/ if ((atomic_read(&bat_priv->gw_sel_class) > 3) && (orig_tq_avg - gw_tq_avg < atomic_read(&bat_priv->gw_sel_class))) - return; + goto out; bat_dbg(DBG_BATMAN, bat_priv, "Restarting gateway selection: better gateway found (tq curr: " "%i, tq new: %i)\n", gw_tq_avg, orig_tq_avg); + goto deselect; +out_rcu: + rcu_read_unlock(); + goto out; +deselect_rcu: + rcu_read_unlock(); deselect: gw_deselect(bat_priv); +out: + return; } static void gw_node_add(struct bat_priv *bat_priv, @@ -242,7 +271,7 @@ static void gw_node_add(struct bat_priv *bat_priv, memset(gw_node, 0, sizeof(struct gw_node)); INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; - kref_init(&gw_node->refcount); + atomic_set(&gw_node->refcount, 1); spin_lock_bh(&bat_priv->gw_list_lock); hlist_add_head_rcu(&gw_node->list, &bat_priv->gw_list); @@ -283,7 +312,7 @@ void gw_node_update(struct bat_priv *bat_priv, "Gateway %pM removed from gateway list\n", orig_node->orig); - if (gw_node == bat_priv->curr_gw) { + if (gw_node == rcu_dereference(bat_priv->curr_gw)) { rcu_read_unlock(); gw_deselect(bat_priv); return; @@ -321,11 +350,11 @@ void gw_node_purge(struct bat_priv *bat_priv) atomic_read(&bat_priv->mesh_state) == MESH_ACTIVE) continue; - if (bat_priv->curr_gw == gw_node) + if (rcu_dereference(bat_priv->curr_gw) == gw_node) gw_deselect(bat_priv); hlist_del_rcu(&gw_node->list); - call_rcu(&gw_node->rcu, gw_node_free_rcu); + gw_node_free_ref(gw_node); } @@ -335,12 +364,16 @@ void gw_node_purge(struct bat_priv *bat_priv) static int _write_buffer_text(struct bat_priv *bat_priv, struct seq_file *seq, struct gw_node *gw_node) { - int down, up; + struct gw_node *curr_gw; + int down, up, ret; gw_bandwidth_to_kbit(gw_node->orig_node->gw_flags, &down, &up); - return seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", - (bat_priv->curr_gw == gw_node ? "=>" : " "), + rcu_read_lock(); + curr_gw = rcu_dereference(bat_priv->curr_gw); + + ret = seq_printf(seq, "%s %pM (%3i) %pM [%10s]: %3i - %i%s/%i%s\n", + (curr_gw == gw_node ? "=>" : " "), gw_node->orig_node->orig, gw_node->orig_node->router->tq_avg, gw_node->orig_node->router->addr, @@ -350,6 +383,9 @@ static int _write_buffer_text(struct bat_priv *bat_priv, (down > 2048 ? "MBit" : "KBit"), (up > 2048 ? up / 1024 : up), (up > 2048 ? "MBit" : "KBit")); + + rcu_read_unlock(); + return ret; } int gw_client_seq_print_text(struct seq_file *seq, void *offset) @@ -470,8 +506,12 @@ int gw_is_target(struct bat_priv *bat_priv, struct sk_buff *skb) if (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER) return -1; - if (!bat_priv->curr_gw) + rcu_read_lock(); + if (!rcu_dereference(bat_priv->curr_gw)) { + rcu_read_unlock(); return 0; + } + rcu_read_unlock(); return 1; } diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index f2131f45aa9..b3058e46ee6 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -31,8 +31,8 @@ #include <linux/if_arp.h> -/* protect update critical side of if_list - but not the content */ -static DEFINE_SPINLOCK(if_list_lock); +/* protect update critical side of hardif_list - but not the content */ +static DEFINE_SPINLOCK(hardif_list_lock); static int batman_skb_recv(struct sk_buff *skb, @@ -40,33 +40,31 @@ static int batman_skb_recv(struct sk_buff *skb, struct packet_type *ptype, struct net_device *orig_dev); -static void hardif_free_rcu(struct rcu_head *rcu) +void hardif_free_rcu(struct rcu_head *rcu) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; - batman_if = container_of(rcu, struct batman_if, rcu); - dev_put(batman_if->net_dev); - kref_put(&batman_if->refcount, hardif_free_ref); + hard_iface = container_of(rcu, struct hard_iface, rcu); + dev_put(hard_iface->net_dev); + kfree(hard_iface); } -struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev) +struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->net_dev == net_dev) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->net_dev == net_dev && + atomic_inc_not_zero(&hard_iface->refcount)) goto out; } - batman_if = NULL; + hard_iface = NULL; out: - if (batman_if) - kref_get(&batman_if->refcount); - rcu_read_unlock(); - return batman_if; + return hard_iface; } static int is_valid_iface(struct net_device *net_dev) @@ -81,13 +79,8 @@ static int is_valid_iface(struct net_device *net_dev) return 0; /* no batman over batman */ -#ifdef HAVE_NET_DEVICE_OPS - if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) - return 0; -#else - if (net_dev->hard_start_xmit == interface_tx) + if (softif_is_valid(net_dev)) return 0; -#endif /* Device is being bridged */ /* if (net_dev->priv_flags & IFF_BRIDGE_PORT) @@ -96,27 +89,25 @@ static int is_valid_iface(struct net_device *net_dev) return 1; } -static struct batman_if *get_active_batman_if(struct net_device *soft_iface) +static struct hard_iface *hardif_get_active(struct net_device *soft_iface) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->soft_iface != soft_iface) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->soft_iface != soft_iface) continue; - if (batman_if->if_status == IF_ACTIVE) + if (hard_iface->if_status == IF_ACTIVE && + atomic_inc_not_zero(&hard_iface->refcount)) goto out; } - batman_if = NULL; + hard_iface = NULL; out: - if (batman_if) - kref_get(&batman_if->refcount); - rcu_read_unlock(); - return batman_if; + return hard_iface; } static void update_primary_addr(struct bat_priv *bat_priv) @@ -132,24 +123,24 @@ static void update_primary_addr(struct bat_priv *bat_priv) } static void set_primary_if(struct bat_priv *bat_priv, - struct batman_if *batman_if) + struct hard_iface *hard_iface) { struct batman_packet *batman_packet; - struct batman_if *old_if; + struct hard_iface *old_if; - if (batman_if) - kref_get(&batman_if->refcount); + if (hard_iface && !atomic_inc_not_zero(&hard_iface->refcount)) + hard_iface = NULL; old_if = bat_priv->primary_if; - bat_priv->primary_if = batman_if; + bat_priv->primary_if = hard_iface; if (old_if) - kref_put(&old_if->refcount, hardif_free_ref); + hardif_free_ref(old_if); if (!bat_priv->primary_if) return; - batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet = (struct batman_packet *)(hard_iface->packet_buff); batman_packet->flags = PRIMARIES_FIRST_HOP; batman_packet->ttl = TTL; @@ -162,42 +153,42 @@ static void set_primary_if(struct bat_priv *bat_priv, atomic_set(&bat_priv->hna_local_changed, 1); } -static bool hardif_is_iface_up(struct batman_if *batman_if) +static bool hardif_is_iface_up(struct hard_iface *hard_iface) { - if (batman_if->net_dev->flags & IFF_UP) + if (hard_iface->net_dev->flags & IFF_UP) return true; return false; } -static void update_mac_addresses(struct batman_if *batman_if) +static void update_mac_addresses(struct hard_iface *hard_iface) { - memcpy(((struct batman_packet *)(batman_if->packet_buff))->orig, - batman_if->net_dev->dev_addr, ETH_ALEN); - memcpy(((struct batman_packet *)(batman_if->packet_buff))->prev_sender, - batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(((struct batman_packet *)(hard_iface->packet_buff))->orig, + hard_iface->net_dev->dev_addr, ETH_ALEN); + memcpy(((struct batman_packet *)(hard_iface->packet_buff))->prev_sender, + hard_iface->net_dev->dev_addr, ETH_ALEN); } static void check_known_mac_addr(struct net_device *net_dev) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if ((batman_if->if_status != IF_ACTIVE) && - (batman_if->if_status != IF_TO_BE_ACTIVATED)) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if ((hard_iface->if_status != IF_ACTIVE) && + (hard_iface->if_status != IF_TO_BE_ACTIVATED)) continue; - if (batman_if->net_dev == net_dev) + if (hard_iface->net_dev == net_dev) continue; - if (!compare_orig(batman_if->net_dev->dev_addr, - net_dev->dev_addr)) + if (!compare_eth(hard_iface->net_dev->dev_addr, + net_dev->dev_addr)) continue; pr_warning("The newly added mac address (%pM) already exists " "on: %s\n", net_dev->dev_addr, - batman_if->net_dev->name); + hard_iface->net_dev->name); pr_warning("It is strongly recommended to keep mac addresses " "unique to avoid problems!\n"); } @@ -207,7 +198,7 @@ static void check_known_mac_addr(struct net_device *net_dev) int hardif_min_mtu(struct net_device *soft_iface) { struct bat_priv *bat_priv = netdev_priv(soft_iface); - struct batman_if *batman_if; + struct hard_iface *hard_iface; /* allow big frames if all devices are capable to do so * (have MTU > 1500 + BAT_HEADER_LEN) */ int min_mtu = ETH_DATA_LEN; @@ -216,15 +207,15 @@ int hardif_min_mtu(struct net_device *soft_iface) goto out; rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if ((batman_if->if_status != IF_ACTIVE) && - (batman_if->if_status != IF_TO_BE_ACTIVATED)) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if ((hard_iface->if_status != IF_ACTIVE) && + (hard_iface->if_status != IF_TO_BE_ACTIVATED)) continue; - if (batman_if->soft_iface != soft_iface) + if (hard_iface->soft_iface != soft_iface) continue; - min_mtu = min_t(int, batman_if->net_dev->mtu - BAT_HEADER_LEN, + min_mtu = min_t(int, hard_iface->net_dev->mtu - BAT_HEADER_LEN, min_mtu); } rcu_read_unlock(); @@ -242,77 +233,95 @@ void update_min_mtu(struct net_device *soft_iface) soft_iface->mtu = min_mtu; } -static void hardif_activate_interface(struct batman_if *batman_if) +static void hardif_activate_interface(struct hard_iface *hard_iface) { struct bat_priv *bat_priv; - if (batman_if->if_status != IF_INACTIVE) + if (hard_iface->if_status != IF_INACTIVE) return; - bat_priv = netdev_priv(batman_if->soft_iface); + bat_priv = netdev_priv(hard_iface->soft_iface); - update_mac_addresses(batman_if); - batman_if->if_status = IF_TO_BE_ACTIVATED; + update_mac_addresses(hard_iface); + hard_iface->if_status = IF_TO_BE_ACTIVATED; /** * the first active interface becomes our primary interface or * the next active interface after the old primay interface was removed */ if (!bat_priv->primary_if) - set_primary_if(bat_priv, batman_if); + set_primary_if(bat_priv, hard_iface); - bat_info(batman_if->soft_iface, "Interface activated: %s\n", - batman_if->net_dev->name); + bat_info(hard_iface->soft_iface, "Interface activated: %s\n", + hard_iface->net_dev->name); - update_min_mtu(batman_if->soft_iface); + update_min_mtu(hard_iface->soft_iface); return; } -static void hardif_deactivate_interface(struct batman_if *batman_if) +static void hardif_deactivate_interface(struct hard_iface *hard_iface) { - if ((batman_if->if_status != IF_ACTIVE) && - (batman_if->if_status != IF_TO_BE_ACTIVATED)) + if ((hard_iface->if_status != IF_ACTIVE) && + (hard_iface->if_status != IF_TO_BE_ACTIVATED)) return; - batman_if->if_status = IF_INACTIVE; + hard_iface->if_status = IF_INACTIVE; - bat_info(batman_if->soft_iface, "Interface deactivated: %s\n", - batman_if->net_dev->name); + bat_info(hard_iface->soft_iface, "Interface deactivated: %s\n", + hard_iface->net_dev->name); - update_min_mtu(batman_if->soft_iface); + update_min_mtu(hard_iface->soft_iface); } -int hardif_enable_interface(struct batman_if *batman_if, char *iface_name) +int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name) { struct bat_priv *bat_priv; struct batman_packet *batman_packet; + struct net_device *soft_iface; + int ret; + + if (hard_iface->if_status != IF_NOT_IN_USE) + goto out; - if (batman_if->if_status != IF_NOT_IN_USE) + if (!atomic_inc_not_zero(&hard_iface->refcount)) goto out; - batman_if->soft_iface = dev_get_by_name(&init_net, iface_name); + soft_iface = dev_get_by_name(&init_net, iface_name); - if (!batman_if->soft_iface) { - batman_if->soft_iface = softif_create(iface_name); + if (!soft_iface) { + soft_iface = softif_create(iface_name); - if (!batman_if->soft_iface) + if (!soft_iface) { + ret = -ENOMEM; goto err; + } /* dev_get_by_name() increases the reference counter for us */ - dev_hold(batman_if->soft_iface); + dev_hold(soft_iface); } - bat_priv = netdev_priv(batman_if->soft_iface); - batman_if->packet_len = BAT_PACKET_LEN; - batman_if->packet_buff = kmalloc(batman_if->packet_len, GFP_ATOMIC); + if (!softif_is_valid(soft_iface)) { + pr_err("Can't create batman mesh interface %s: " + "already exists as regular interface\n", + soft_iface->name); + dev_put(soft_iface); + ret = -EINVAL; + goto err; + } + + hard_iface->soft_iface = soft_iface; + bat_priv = netdev_priv(hard_iface->soft_iface); + hard_iface->packet_len = BAT_PACKET_LEN; + hard_iface->packet_buff = kmalloc(hard_iface->packet_len, GFP_ATOMIC); - if (!batman_if->packet_buff) { - bat_err(batman_if->soft_iface, "Can't add interface packet " - "(%s): out of memory\n", batman_if->net_dev->name); + if (!hard_iface->packet_buff) { + bat_err(hard_iface->soft_iface, "Can't add interface packet " + "(%s): out of memory\n", hard_iface->net_dev->name); + ret = -ENOMEM; goto err; } - batman_packet = (struct batman_packet *)(batman_if->packet_buff); + batman_packet = (struct batman_packet *)(hard_iface->packet_buff); batman_packet->packet_type = BAT_PACKET; batman_packet->version = COMPAT_VERSION; batman_packet->flags = 0; @@ -320,107 +329,107 @@ int hardif_enable_interface(struct batman_if *batman_if, char *iface_name) batman_packet->tq = TQ_MAX_VALUE; batman_packet->num_hna = 0; - batman_if->if_num = bat_priv->num_ifaces; + hard_iface->if_num = bat_priv->num_ifaces; bat_priv->num_ifaces++; - batman_if->if_status = IF_INACTIVE; - orig_hash_add_if(batman_if, bat_priv->num_ifaces); + hard_iface->if_status = IF_INACTIVE; + orig_hash_add_if(hard_iface, bat_priv->num_ifaces); - batman_if->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN); - batman_if->batman_adv_ptype.func = batman_skb_recv; - batman_if->batman_adv_ptype.dev = batman_if->net_dev; - kref_get(&batman_if->refcount); - dev_add_pack(&batman_if->batman_adv_ptype); + hard_iface->batman_adv_ptype.type = __constant_htons(ETH_P_BATMAN); + hard_iface->batman_adv_ptype.func = batman_skb_recv; + hard_iface->batman_adv_ptype.dev = hard_iface->net_dev; + dev_add_pack(&hard_iface->batman_adv_ptype); - atomic_set(&batman_if->seqno, 1); - atomic_set(&batman_if->frag_seqno, 1); - bat_info(batman_if->soft_iface, "Adding interface: %s\n", - batman_if->net_dev->name); + atomic_set(&hard_iface->seqno, 1); + atomic_set(&hard_iface->frag_seqno, 1); + bat_info(hard_iface->soft_iface, "Adding interface: %s\n", + hard_iface->net_dev->name); - if (atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < + if (atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu < ETH_DATA_LEN + BAT_HEADER_LEN) - bat_info(batman_if->soft_iface, + bat_info(hard_iface->soft_iface, "The MTU of interface %s is too small (%i) to handle " "the transport of batman-adv packets. Packets going " "over this interface will be fragmented on layer2 " "which could impact the performance. Setting the MTU " "to %zi would solve the problem.\n", - batman_if->net_dev->name, batman_if->net_dev->mtu, + hard_iface->net_dev->name, hard_iface->net_dev->mtu, ETH_DATA_LEN + BAT_HEADER_LEN); - if (!atomic_read(&bat_priv->fragmentation) && batman_if->net_dev->mtu < + if (!atomic_read(&bat_priv->fragmentation) && hard_iface->net_dev->mtu < ETH_DATA_LEN + BAT_HEADER_LEN) - bat_info(batman_if->soft_iface, + bat_info(hard_iface->soft_iface, "The MTU of interface %s is too small (%i) to handle " "the transport of batman-adv packets. If you experience" " problems getting traffic through try increasing the " "MTU to %zi.\n", - batman_if->net_dev->name, batman_if->net_dev->mtu, + hard_iface->net_dev->name, hard_iface->net_dev->mtu, ETH_DATA_LEN + BAT_HEADER_LEN); - if (hardif_is_iface_up(batman_if)) - hardif_activate_interface(batman_if); + if (hardif_is_iface_up(hard_iface)) + hardif_activate_interface(hard_iface); else - bat_err(batman_if->soft_iface, "Not using interface %s " + bat_err(hard_iface->soft_iface, "Not using interface %s " "(retrying later): interface not active\n", - batman_if->net_dev->name); + hard_iface->net_dev->name); /* begin scheduling originator messages on that interface */ - schedule_own_packet(batman_if); + schedule_own_packet(hard_iface); out: return 0; err: - return -ENOMEM; + hardif_free_ref(hard_iface); + return ret; } -void hardif_disable_interface(struct batman_if *batman_if) +void hardif_disable_interface(struct hard_iface *hard_iface) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); - if (batman_if->if_status == IF_ACTIVE) - hardif_deactivate_interface(batman_if); + if (hard_iface->if_status == IF_ACTIVE) + hardif_deactivate_interface(hard_iface); - if (batman_if->if_status != IF_INACTIVE) + if (hard_iface->if_status != IF_INACTIVE) return; - bat_info(batman_if->soft_iface, "Removing interface: %s\n", - batman_if->net_dev->name); - dev_remove_pack(&batman_if->batman_adv_ptype); - kref_put(&batman_if->refcount, hardif_free_ref); + bat_info(hard_iface->soft_iface, "Removing interface: %s\n", + hard_iface->net_dev->name); + dev_remove_pack(&hard_iface->batman_adv_ptype); bat_priv->num_ifaces--; - orig_hash_del_if(batman_if, bat_priv->num_ifaces); + orig_hash_del_if(hard_iface, bat_priv->num_ifaces); - if (batman_if == bat_priv->primary_if) { - struct batman_if *new_if; + if (hard_iface == bat_priv->primary_if) { + struct hard_iface *new_if; - new_if = get_active_batman_if(batman_if->soft_iface); + new_if = hardif_get_active(hard_iface->soft_iface); set_primary_if(bat_priv, new_if); if (new_if) - kref_put(&new_if->refcount, hardif_free_ref); + hardif_free_ref(new_if); } - kfree(batman_if->packet_buff); - batman_if->packet_buff = NULL; - batman_if->if_status = IF_NOT_IN_USE; + kfree(hard_iface->packet_buff); + hard_iface->packet_buff = NULL; + hard_iface->if_status = IF_NOT_IN_USE; - /* delete all references to this batman_if */ + /* delete all references to this hard_iface */ purge_orig_ref(bat_priv); - purge_outstanding_packets(bat_priv, batman_if); - dev_put(batman_if->soft_iface); + purge_outstanding_packets(bat_priv, hard_iface); + dev_put(hard_iface->soft_iface); /* nobody uses this interface anymore */ if (!bat_priv->num_ifaces) - softif_destroy(batman_if->soft_iface); + softif_destroy(hard_iface->soft_iface); - batman_if->soft_iface = NULL; + hard_iface->soft_iface = NULL; + hardif_free_ref(hard_iface); } -static struct batman_if *hardif_add_interface(struct net_device *net_dev) +static struct hard_iface *hardif_add_interface(struct net_device *net_dev) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; int ret; ret = is_valid_iface(net_dev); @@ -429,73 +438,73 @@ static struct batman_if *hardif_add_interface(struct net_device *net_dev) dev_hold(net_dev); - batman_if = kmalloc(sizeof(struct batman_if), GFP_ATOMIC); - if (!batman_if) { + hard_iface = kmalloc(sizeof(struct hard_iface), GFP_ATOMIC); + if (!hard_iface) { pr_err("Can't add interface (%s): out of memory\n", net_dev->name); goto release_dev; } - ret = sysfs_add_hardif(&batman_if->hardif_obj, net_dev); + ret = sysfs_add_hardif(&hard_iface->hardif_obj, net_dev); if (ret) goto free_if; - batman_if->if_num = -1; - batman_if->net_dev = net_dev; - batman_if->soft_iface = NULL; - batman_if->if_status = IF_NOT_IN_USE; - INIT_LIST_HEAD(&batman_if->list); - kref_init(&batman_if->refcount); + hard_iface->if_num = -1; + hard_iface->net_dev = net_dev; + hard_iface->soft_iface = NULL; + hard_iface->if_status = IF_NOT_IN_USE; + INIT_LIST_HEAD(&hard_iface->list); + /* extra reference for return */ + atomic_set(&hard_iface->refcount, 2); - check_known_mac_addr(batman_if->net_dev); + check_known_mac_addr(hard_iface->net_dev); - spin_lock(&if_list_lock); - list_add_tail_rcu(&batman_if->list, &if_list); - spin_unlock(&if_list_lock); + spin_lock(&hardif_list_lock); + list_add_tail_rcu(&hard_iface->list, &hardif_list); + spin_unlock(&hardif_list_lock); - /* extra reference for return */ - kref_get(&batman_if->refcount); - return batman_if; + return hard_iface; free_if: - kfree(batman_if); + kfree(hard_iface); release_dev: dev_put(net_dev); out: return NULL; } -static void hardif_remove_interface(struct batman_if *batman_if) +static void hardif_remove_interface(struct hard_iface *hard_iface) { /* first deactivate interface */ - if (batman_if->if_status != IF_NOT_IN_USE) - hardif_disable_interface(batman_if); + if (hard_iface->if_status != IF_NOT_IN_USE) + hardif_disable_interface(hard_iface); - if (batman_if->if_status != IF_NOT_IN_USE) + if (hard_iface->if_status != IF_NOT_IN_USE) return; - batman_if->if_status = IF_TO_BE_REMOVED; - sysfs_del_hardif(&batman_if->hardif_obj); - call_rcu(&batman_if->rcu, hardif_free_rcu); + hard_iface->if_status = IF_TO_BE_REMOVED; + sysfs_del_hardif(&hard_iface->hardif_obj); + hardif_free_ref(hard_iface); } void hardif_remove_interfaces(void) { - struct batman_if *batman_if, *batman_if_tmp; + struct hard_iface *hard_iface, *hard_iface_tmp; struct list_head if_queue; INIT_LIST_HEAD(&if_queue); - spin_lock(&if_list_lock); - list_for_each_entry_safe(batman_if, batman_if_tmp, &if_list, list) { - list_del_rcu(&batman_if->list); - list_add_tail(&batman_if->list, &if_queue); + spin_lock(&hardif_list_lock); + list_for_each_entry_safe(hard_iface, hard_iface_tmp, + &hardif_list, list) { + list_del_rcu(&hard_iface->list); + list_add_tail(&hard_iface->list, &if_queue); } - spin_unlock(&if_list_lock); + spin_unlock(&hardif_list_lock); rtnl_lock(); - list_for_each_entry_safe(batman_if, batman_if_tmp, &if_queue, list) { - hardif_remove_interface(batman_if); + list_for_each_entry_safe(hard_iface, hard_iface_tmp, &if_queue, list) { + hardif_remove_interface(hard_iface); } rtnl_unlock(); } @@ -504,43 +513,43 @@ static int hard_if_event(struct notifier_block *this, unsigned long event, void *ptr) { struct net_device *net_dev = (struct net_device *)ptr; - struct batman_if *batman_if = get_batman_if_by_netdev(net_dev); + struct hard_iface *hard_iface = hardif_get_by_netdev(net_dev); struct bat_priv *bat_priv; - if (!batman_if && event == NETDEV_REGISTER) - batman_if = hardif_add_interface(net_dev); + if (!hard_iface && event == NETDEV_REGISTER) + hard_iface = hardif_add_interface(net_dev); - if (!batman_if) + if (!hard_iface) goto out; switch (event) { case NETDEV_UP: - hardif_activate_interface(batman_if); + hardif_activate_interface(hard_iface); break; case NETDEV_GOING_DOWN: case NETDEV_DOWN: - hardif_deactivate_interface(batman_if); + hardif_deactivate_interface(hard_iface); break; case NETDEV_UNREGISTER: - spin_lock(&if_list_lock); - list_del_rcu(&batman_if->list); - spin_unlock(&if_list_lock); + spin_lock(&hardif_list_lock); + list_del_rcu(&hard_iface->list); + spin_unlock(&hardif_list_lock); - hardif_remove_interface(batman_if); + hardif_remove_interface(hard_iface); break; case NETDEV_CHANGEMTU: - if (batman_if->soft_iface) - update_min_mtu(batman_if->soft_iface); + if (hard_iface->soft_iface) + update_min_mtu(hard_iface->soft_iface); break; case NETDEV_CHANGEADDR: - if (batman_if->if_status == IF_NOT_IN_USE) + if (hard_iface->if_status == IF_NOT_IN_USE) goto hardif_put; - check_known_mac_addr(batman_if->net_dev); - update_mac_addresses(batman_if); + check_known_mac_addr(hard_iface->net_dev); + update_mac_addresses(hard_iface); - bat_priv = netdev_priv(batman_if->soft_iface); - if (batman_if == bat_priv->primary_if) + bat_priv = netdev_priv(hard_iface->soft_iface); + if (hard_iface == bat_priv->primary_if) update_primary_addr(bat_priv); break; default: @@ -548,7 +557,7 @@ static int hard_if_event(struct notifier_block *this, }; hardif_put: - kref_put(&batman_if->refcount, hardif_free_ref); + hardif_free_ref(hard_iface); out: return NOTIFY_DONE; } @@ -561,10 +570,10 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, { struct bat_priv *bat_priv; struct batman_packet *batman_packet; - struct batman_if *batman_if; + struct hard_iface *hard_iface; int ret; - batman_if = container_of(ptype, struct batman_if, batman_adv_ptype); + hard_iface = container_of(ptype, struct hard_iface, batman_adv_ptype); skb = skb_share_check(skb, GFP_ATOMIC); /* skb was released by skb_share_check() */ @@ -580,16 +589,16 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, || !skb_mac_header(skb))) goto err_free; - if (!batman_if->soft_iface) + if (!hard_iface->soft_iface) goto err_free; - bat_priv = netdev_priv(batman_if->soft_iface); + bat_priv = netdev_priv(hard_iface->soft_iface); if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto err_free; /* discard frames on not active interfaces */ - if (batman_if->if_status != IF_ACTIVE) + if (hard_iface->if_status != IF_ACTIVE) goto err_free; batman_packet = (struct batman_packet *)skb->data; @@ -607,32 +616,32 @@ static int batman_skb_recv(struct sk_buff *skb, struct net_device *dev, switch (batman_packet->packet_type) { /* batman originator packet */ case BAT_PACKET: - ret = recv_bat_packet(skb, batman_if); + ret = recv_bat_packet(skb, hard_iface); break; /* batman icmp packet */ case BAT_ICMP: - ret = recv_icmp_packet(skb, batman_if); + ret = recv_icmp_packet(skb, hard_iface); break; /* unicast packet */ case BAT_UNICAST: - ret = recv_unicast_packet(skb, batman_if); + ret = recv_unicast_packet(skb, hard_iface); break; /* fragmented unicast packet */ case BAT_UNICAST_FRAG: - ret = recv_ucast_frag_packet(skb, batman_if); + ret = recv_ucast_frag_packet(skb, hard_iface); break; /* broadcast packet */ case BAT_BCAST: - ret = recv_bcast_packet(skb, batman_if); + ret = recv_bcast_packet(skb, hard_iface); break; /* vis packet */ case BAT_VIS: - ret = recv_vis_packet(skb, batman_if); + ret = recv_vis_packet(skb, hard_iface); break; default: ret = NET_RX_DROP; diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index ad195438428..a9ddf36e51c 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -31,19 +31,18 @@ extern struct notifier_block hard_if_notifier; -struct batman_if *get_batman_if_by_netdev(struct net_device *net_dev); -int hardif_enable_interface(struct batman_if *batman_if, char *iface_name); -void hardif_disable_interface(struct batman_if *batman_if); +struct hard_iface *hardif_get_by_netdev(struct net_device *net_dev); +int hardif_enable_interface(struct hard_iface *hard_iface, char *iface_name); +void hardif_disable_interface(struct hard_iface *hard_iface); void hardif_remove_interfaces(void); int hardif_min_mtu(struct net_device *soft_iface); void update_min_mtu(struct net_device *soft_iface); +void hardif_free_rcu(struct rcu_head *rcu); -static inline void hardif_free_ref(struct kref *refcount) +static inline void hardif_free_ref(struct hard_iface *hard_iface) { - struct batman_if *batman_if; - - batman_if = container_of(refcount, struct batman_if, refcount); - kfree(batman_if); + if (atomic_dec_and_test(&hard_iface->refcount)) + call_rcu(&hard_iface->rcu, hardif_free_rcu); } #endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */ diff --git a/net/batman-adv/hash.c b/net/batman-adv/hash.c index fa2693973ab..c5213d8f2cc 100644 --- a/net/batman-adv/hash.c +++ b/net/batman-adv/hash.c @@ -27,13 +27,16 @@ static void hash_init(struct hashtable_t *hash) { int i; - for (i = 0 ; i < hash->size; i++) + for (i = 0 ; i < hash->size; i++) { INIT_HLIST_HEAD(&hash->table[i]); + spin_lock_init(&hash->list_locks[i]); + } } /* free only the hashtable and the hash itself. */ void hash_destroy(struct hashtable_t *hash) { + kfree(hash->list_locks); kfree(hash->table); kfree(hash); } @@ -43,20 +46,25 @@ struct hashtable_t *hash_new(int size) { struct hashtable_t *hash; - hash = kmalloc(sizeof(struct hashtable_t) , GFP_ATOMIC); - + hash = kmalloc(sizeof(struct hashtable_t), GFP_ATOMIC); if (!hash) return NULL; - hash->size = size; hash->table = kmalloc(sizeof(struct element_t *) * size, GFP_ATOMIC); + if (!hash->table) + goto free_hash; - if (!hash->table) { - kfree(hash); - return NULL; - } + hash->list_locks = kmalloc(sizeof(spinlock_t) * size, GFP_ATOMIC); + if (!hash->list_locks) + goto free_table; + hash->size = size; hash_init(hash); - return hash; + +free_table: + kfree(hash->table); +free_hash: + kfree(hash); + return NULL; } diff --git a/net/batman-adv/hash.h b/net/batman-adv/hash.h index eae24402fd0..434822b2747 100644 --- a/net/batman-adv/hash.h +++ b/net/batman-adv/hash.h @@ -28,21 +28,17 @@ * compare 2 element datas for their keys, * return 0 if same and not 0 if not * same */ -typedef int (*hashdata_compare_cb)(void *, void *); +typedef int (*hashdata_compare_cb)(struct hlist_node *, void *); /* the hashfunction, should return an index * based on the key in the data of the first * argument and the size the second */ typedef int (*hashdata_choose_cb)(void *, int); -typedef void (*hashdata_free_cb)(void *, void *); - -struct element_t { - void *data; /* pointer to the data */ - struct hlist_node hlist; /* bucket list pointer */ -}; +typedef void (*hashdata_free_cb)(struct hlist_node *, void *); struct hashtable_t { - struct hlist_head *table; /* the hashtable itself, with the buckets */ + struct hlist_head *table; /* the hashtable itself with the buckets */ + spinlock_t *list_locks; /* spinlock for each hash list entry */ int size; /* size of hashtable */ }; @@ -59,21 +55,22 @@ static inline void hash_delete(struct hashtable_t *hash, hashdata_free_cb free_cb, void *arg) { struct hlist_head *head; - struct hlist_node *walk, *safe; - struct element_t *bucket; + struct hlist_node *node, *node_tmp; + spinlock_t *list_lock; /* spinlock to protect write access */ int i; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; + list_lock = &hash->list_locks[i]; - hlist_for_each_safe(walk, safe, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (free_cb) - free_cb(bucket->data, arg); + spin_lock_bh(list_lock); + hlist_for_each_safe(node, node_tmp, head) { + hlist_del_rcu(node); - hlist_del(walk); - kfree(bucket); + if (free_cb) + free_cb(node, arg); } + spin_unlock_bh(list_lock); } hash_destroy(hash); @@ -82,35 +79,41 @@ static inline void hash_delete(struct hashtable_t *hash, /* adds data to the hashtable. returns 0 on success, -1 on error */ static inline int hash_add(struct hashtable_t *hash, hashdata_compare_cb compare, - hashdata_choose_cb choose, void *data) + hashdata_choose_cb choose, + void *data, struct hlist_node *data_node) { int index; struct hlist_head *head; - struct hlist_node *walk, *safe; - struct element_t *bucket; + struct hlist_node *node; + spinlock_t *list_lock; /* spinlock to protect write access */ if (!hash) - return -1; + goto err; index = choose(data, hash->size); head = &hash->table[index]; + list_lock = &hash->list_locks[index]; + + rcu_read_lock(); + __hlist_for_each_rcu(node, head) { + if (!compare(node, data)) + continue; - hlist_for_each_safe(walk, safe, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (compare(bucket->data, data)) - return -1; + goto err_unlock; } + rcu_read_unlock(); /* no duplicate found in list, add new element */ - bucket = kmalloc(sizeof(struct element_t), GFP_ATOMIC); - - if (!bucket) - return -1; - - bucket->data = data; - hlist_add_head(&bucket->hlist, head); + spin_lock_bh(list_lock); + hlist_add_head_rcu(data_node, head); + spin_unlock_bh(list_lock); return 0; + +err_unlock: + rcu_read_unlock(); +err: + return -1; } /* removes data from hash, if found. returns pointer do data on success, so you @@ -122,50 +125,25 @@ static inline void *hash_remove(struct hashtable_t *hash, hashdata_choose_cb choose, void *data) { size_t index; - struct hlist_node *walk; - struct element_t *bucket; + struct hlist_node *node; struct hlist_head *head; - void *data_save; + void *data_save = NULL; index = choose(data, hash->size); head = &hash->table[index]; - hlist_for_each_entry(bucket, walk, head, hlist) { - if (compare(bucket->data, data)) { - data_save = bucket->data; - hlist_del(walk); - kfree(bucket); - return data_save; - } - } - - return NULL; -} - -/* finds data, based on the key in keydata. returns the found data on success, - * or NULL on error */ -static inline void *hash_find(struct hashtable_t *hash, - hashdata_compare_cb compare, - hashdata_choose_cb choose, void *keydata) -{ - int index; - struct hlist_head *head; - struct hlist_node *walk; - struct element_t *bucket; - - if (!hash) - return NULL; - - index = choose(keydata , hash->size); - head = &hash->table[index]; + spin_lock_bh(&hash->list_locks[index]); + hlist_for_each(node, head) { + if (!compare(node, data)) + continue; - hlist_for_each(walk, head) { - bucket = hlist_entry(walk, struct element_t, hlist); - if (compare(bucket->data, keydata)) - return bucket->data; + data_save = node; + hlist_del_rcu(node); + break; } + spin_unlock_bh(&hash->list_locks[index]); - return NULL; + return data_save; } #endif /* _NET_BATMAN_ADV_HASH_H_ */ diff --git a/net/batman-adv/icmp_socket.c b/net/batman-adv/icmp_socket.c index 319a7ccf6ef..34ce56c358e 100644 --- a/net/batman-adv/icmp_socket.c +++ b/net/batman-adv/icmp_socket.c @@ -156,10 +156,9 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, struct sk_buff *skb; struct icmp_packet_rr *icmp_packet; - struct orig_node *orig_node; - struct batman_if *batman_if; + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; size_t packet_len = sizeof(struct icmp_packet); - uint8_t dstaddr[ETH_ALEN]; if (len < sizeof(struct icmp_packet)) { bat_dbg(DBG_BATMAN, bat_priv, @@ -219,47 +218,52 @@ static ssize_t bat_socket_write(struct file *file, const char __user *buff, if (atomic_read(&bat_priv->mesh_state) != MESH_ACTIVE) goto dst_unreach; - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, - compare_orig, choose_orig, - icmp_packet->dst)); + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, icmp_packet->dst); if (!orig_node) goto unlock; - if (!orig_node->router) + neigh_node = orig_node->router; + + if (!neigh_node) goto unlock; - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); + if (!atomic_inc_not_zero(&neigh_node->refcount)) { + neigh_node = NULL; + goto unlock; + } - spin_unlock_bh(&bat_priv->orig_hash_lock); + rcu_read_unlock(); - if (!batman_if) + if (!neigh_node->if_incoming) goto dst_unreach; - if (batman_if->if_status != IF_ACTIVE) + if (neigh_node->if_incoming->if_status != IF_ACTIVE) goto dst_unreach; memcpy(icmp_packet->orig, bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); if (packet_len == sizeof(struct icmp_packet_rr)) - memcpy(icmp_packet->rr, batman_if->net_dev->dev_addr, ETH_ALEN); - - - send_skb_packet(skb, batman_if, dstaddr); + memcpy(icmp_packet->rr, + neigh_node->if_incoming->net_dev->dev_addr, ETH_ALEN); + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); goto out; unlock: - spin_unlock_bh(&bat_priv->orig_hash_lock); + rcu_read_unlock(); dst_unreach: icmp_packet->msg_type = DESTINATION_UNREACHABLE; bat_socket_add_packet(socket_client, icmp_packet, packet_len); free_skb: kfree_skb(skb); out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); return len; } diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c index 06d956c91c2..709b33bbdf4 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -33,7 +33,7 @@ #include "vis.h" #include "hash.h" -struct list_head if_list; +struct list_head hardif_list; unsigned char broadcast_addr[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -41,7 +41,7 @@ struct workqueue_struct *bat_event_workqueue; static int __init batman_init(void) { - INIT_LIST_HEAD(&if_list); + INIT_LIST_HEAD(&hardif_list); /* the name should not be longer than 10 chars - see * http://lwn.net/Articles/23634/ */ @@ -79,7 +79,6 @@ int mesh_init(struct net_device *soft_iface) { struct bat_priv *bat_priv = netdev_priv(soft_iface); - spin_lock_init(&bat_priv->orig_hash_lock); spin_lock_init(&bat_priv->forw_bat_list_lock); spin_lock_init(&bat_priv->forw_bcast_list_lock); spin_lock_init(&bat_priv->hna_lhash_lock); @@ -154,14 +153,14 @@ void dec_module_count(void) int is_my_mac(uint8_t *addr) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->if_status != IF_ACTIVE) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->if_status != IF_ACTIVE) continue; - if (compare_orig(batman_if->net_dev->dev_addr, addr)) { + if (compare_eth(hard_iface->net_dev->dev_addr, addr)) { rcu_read_unlock(); return 1; } diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index e235d7bbe04..dc248697de7 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -122,7 +122,7 @@ #define REVISION_VERSION_STR " "REVISION_VERSION #endif -extern struct list_head if_list; +extern struct list_head hardif_list; extern unsigned char broadcast_addr[]; extern struct workqueue_struct *bat_event_workqueue; @@ -165,4 +165,14 @@ static inline void bat_dbg(char type __always_unused, pr_err("%s: " fmt, _netdev->name, ## arg); \ } while (0) +/** + * returns 1 if they are the same ethernet addr + * + * note: can't use compare_ether_addr() as it requires aligned memory + */ +static inline int compare_eth(void *data1, void *data2) +{ + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + #endif /* _NET_BATMAN_ADV_MAIN_H_ */ diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 54863c9385d..0b9133022d2 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c @@ -44,24 +44,36 @@ int originator_init(struct bat_priv *bat_priv) if (bat_priv->orig_hash) return 1; - spin_lock_bh(&bat_priv->orig_hash_lock); bat_priv->orig_hash = hash_new(1024); if (!bat_priv->orig_hash) goto err; - spin_unlock_bh(&bat_priv->orig_hash_lock); start_purge_timer(bat_priv); return 1; err: - spin_unlock_bh(&bat_priv->orig_hash_lock); return 0; } -struct neigh_node * -create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, struct batman_if *if_incoming) +static void neigh_node_free_rcu(struct rcu_head *rcu) +{ + struct neigh_node *neigh_node; + + neigh_node = container_of(rcu, struct neigh_node, rcu); + kfree(neigh_node); +} + +void neigh_node_free_ref(struct neigh_node *neigh_node) +{ + if (atomic_dec_and_test(&neigh_node->refcount)) + call_rcu(&neigh_node->rcu, neigh_node_free_rcu); +} + +struct neigh_node *create_neighbor(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + uint8_t *neigh, + struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct neigh_node *neigh_node; @@ -73,50 +85,94 @@ create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, if (!neigh_node) return NULL; - INIT_LIST_HEAD(&neigh_node->list); + INIT_HLIST_NODE(&neigh_node->list); + INIT_LIST_HEAD(&neigh_node->bonding_list); memcpy(neigh_node->addr, neigh, ETH_ALEN); neigh_node->orig_node = orig_neigh_node; neigh_node->if_incoming = if_incoming; - list_add_tail(&neigh_node->list, &orig_node->neigh_list); + /* extra reference for return */ + atomic_set(&neigh_node->refcount, 2); + + spin_lock_bh(&orig_node->neigh_list_lock); + hlist_add_head_rcu(&neigh_node->list, &orig_node->neigh_list); + spin_unlock_bh(&orig_node->neigh_list_lock); return neigh_node; } -static void free_orig_node(void *data, void *arg) +static void orig_node_free_rcu(struct rcu_head *rcu) { - struct list_head *list_pos, *list_pos_tmp; - struct neigh_node *neigh_node; - struct orig_node *orig_node = (struct orig_node *)data; - struct bat_priv *bat_priv = (struct bat_priv *)arg; + struct hlist_node *node, *node_tmp; + struct neigh_node *neigh_node, *tmp_neigh_node; + struct orig_node *orig_node; - /* for all neighbors towards this originator ... */ - list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { - neigh_node = list_entry(list_pos, struct neigh_node, list); + orig_node = container_of(rcu, struct orig_node, rcu); + + spin_lock_bh(&orig_node->neigh_list_lock); + + /* for all bonding members ... */ + list_for_each_entry_safe(neigh_node, tmp_neigh_node, + &orig_node->bond_list, bonding_list) { + list_del_rcu(&neigh_node->bonding_list); + neigh_node_free_ref(neigh_node); + } - list_del(list_pos); - kfree(neigh_node); + /* for all neighbors towards this originator ... */ + hlist_for_each_entry_safe(neigh_node, node, node_tmp, + &orig_node->neigh_list, list) { + hlist_del_rcu(&neigh_node->list); + neigh_node_free_ref(neigh_node); } + spin_unlock_bh(&orig_node->neigh_list_lock); + frag_list_free(&orig_node->frag_list); - hna_global_del_orig(bat_priv, orig_node, "originator timed out"); + hna_global_del_orig(orig_node->bat_priv, orig_node, + "originator timed out"); kfree(orig_node->bcast_own); kfree(orig_node->bcast_own_sum); kfree(orig_node); } +void orig_node_free_ref(struct orig_node *orig_node) +{ + if (atomic_dec_and_test(&orig_node->refcount)) + call_rcu(&orig_node->rcu, orig_node_free_rcu); +} + void originator_free(struct bat_priv *bat_priv) { - if (!bat_priv->orig_hash) + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_node *node, *node_tmp; + struct hlist_head *head; + spinlock_t *list_lock; /* spinlock to protect write access */ + struct orig_node *orig_node; + int i; + + if (!hash) return; cancel_delayed_work_sync(&bat_priv->orig_work); - spin_lock_bh(&bat_priv->orig_hash_lock); - hash_delete(bat_priv->orig_hash, free_orig_node, bat_priv); bat_priv->orig_hash = NULL; - spin_unlock_bh(&bat_priv->orig_hash_lock); + + for (i = 0; i < hash->size; i++) { + head = &hash->table[i]; + list_lock = &hash->list_locks[i]; + + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(orig_node, node, node_tmp, + head, hash_entry) { + + hlist_del_rcu(node); + orig_node_free_ref(orig_node); + } + spin_unlock_bh(list_lock); + } + + hash_destroy(hash); } /* this function finds or creates an originator entry for the given @@ -127,10 +183,7 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) int size; int hash_added; - orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, - compare_orig, choose_orig, - addr)); - + orig_node = orig_hash_find(bat_priv, addr); if (orig_node) return orig_node; @@ -141,8 +194,16 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) if (!orig_node) return NULL; - INIT_LIST_HEAD(&orig_node->neigh_list); + INIT_HLIST_HEAD(&orig_node->neigh_list); + INIT_LIST_HEAD(&orig_node->bond_list); + spin_lock_init(&orig_node->ogm_cnt_lock); + spin_lock_init(&orig_node->bcast_seqno_lock); + spin_lock_init(&orig_node->neigh_list_lock); + + /* extra reference for return */ + atomic_set(&orig_node->refcount, 2); + orig_node->bat_priv = bat_priv; memcpy(orig_node->orig, addr, ETH_ALEN); orig_node->router = NULL; orig_node->hna_buff = NULL; @@ -151,6 +212,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) orig_node->batman_seqno_reset = jiffies - 1 - msecs_to_jiffies(RESET_PROTECTION_MS); + atomic_set(&orig_node->bond_candidates, 0); + size = bat_priv->num_ifaces * sizeof(unsigned long) * NUM_WORDS; orig_node->bcast_own = kzalloc(size, GFP_ATOMIC); @@ -166,8 +229,8 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr) if (!orig_node->bcast_own_sum) goto free_bcast_own; - hash_added = hash_add(bat_priv->orig_hash, compare_orig, choose_orig, - orig_node); + hash_added = hash_add(bat_priv->orig_hash, compare_orig, + choose_orig, orig_node, &orig_node->hash_entry); if (hash_added < 0) goto free_bcast_own_sum; @@ -185,23 +248,30 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv, struct orig_node *orig_node, struct neigh_node **best_neigh_node) { - struct list_head *list_pos, *list_pos_tmp; + struct hlist_node *node, *node_tmp; struct neigh_node *neigh_node; bool neigh_purged = false; *best_neigh_node = NULL; + spin_lock_bh(&orig_node->neigh_list_lock); + /* for all neighbors towards this originator ... */ - list_for_each_safe(list_pos, list_pos_tmp, &orig_node->neigh_list) { - neigh_node = list_entry(list_pos, struct neigh_node, list); + hlist_for_each_entry_safe(neigh_node, node, node_tmp, + &orig_node->neigh_list, list) { if ((time_after(jiffies, neigh_node->last_valid + PURGE_TIMEOUT * HZ)) || (neigh_node->if_incoming->if_status == IF_INACTIVE) || + (neigh_node->if_incoming->if_status == IF_NOT_IN_USE) || (neigh_node->if_incoming->if_status == IF_TO_BE_REMOVED)) { - if (neigh_node->if_incoming->if_status == - IF_TO_BE_REMOVED) + if ((neigh_node->if_incoming->if_status == + IF_INACTIVE) || + (neigh_node->if_incoming->if_status == + IF_NOT_IN_USE) || + (neigh_node->if_incoming->if_status == + IF_TO_BE_REMOVED)) bat_dbg(DBG_BATMAN, bat_priv, "neighbor purge: originator %pM, " "neighbor: %pM, iface: %s\n", @@ -215,14 +285,18 @@ static bool purge_orig_neighbors(struct bat_priv *bat_priv, (neigh_node->last_valid / HZ)); neigh_purged = true; - list_del(list_pos); - kfree(neigh_node); + + hlist_del_rcu(&neigh_node->list); + bonding_candidate_del(orig_node, neigh_node); + neigh_node_free_ref(neigh_node); } else { if ((!*best_neigh_node) || (neigh_node->tq_avg > (*best_neigh_node)->tq_avg)) *best_neigh_node = neigh_node; } } + + spin_unlock_bh(&orig_node->neigh_list_lock); return neigh_purged; } @@ -245,9 +319,6 @@ static bool purge_orig_node(struct bat_priv *bat_priv, best_neigh_node, orig_node->hna_buff, orig_node->hna_buff_len); - /* update bonding candidates, we could have lost - * some candidates. */ - update_bonding_candidates(orig_node); } } @@ -257,40 +328,38 @@ static bool purge_orig_node(struct bat_priv *bat_priv, static void _purge_orig(struct bat_priv *bat_priv) { struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk, *safe; + struct hlist_node *node, *node_tmp; struct hlist_head *head; - struct element_t *bucket; + spinlock_t *list_lock; /* spinlock to protect write access */ struct orig_node *orig_node; int i; if (!hash) return; - spin_lock_bh(&bat_priv->orig_hash_lock); - /* for all origins... */ for (i = 0; i < hash->size; i++) { head = &hash->table[i]; + list_lock = &hash->list_locks[i]; - hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { - orig_node = bucket->data; - + spin_lock_bh(list_lock); + hlist_for_each_entry_safe(orig_node, node, node_tmp, + head, hash_entry) { if (purge_orig_node(bat_priv, orig_node)) { if (orig_node->gw_flags) gw_node_delete(bat_priv, orig_node); - hlist_del(walk); - kfree(bucket); - free_orig_node(orig_node, bat_priv); + hlist_del_rcu(node); + orig_node_free_ref(orig_node); + continue; } if (time_after(jiffies, orig_node->last_frag_packet + msecs_to_jiffies(FRAG_TIMEOUT))) frag_list_free(&orig_node->frag_list); } + spin_unlock_bh(list_lock); } - spin_unlock_bh(&bat_priv->orig_hash_lock); - gw_node_purge(bat_priv); gw_election(bat_priv); @@ -318,9 +387,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) struct net_device *net_dev = (struct net_device *)seq->private; struct bat_priv *bat_priv = netdev_priv(net_dev); struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node, *node_tmp; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; struct neigh_node *neigh_node; int batman_count = 0; @@ -348,14 +416,11 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) "Originator", "last-seen", "#", TQ_MAX_VALUE, "Nexthop", "outgoingIF", "Potential nexthops"); - spin_lock_bh(&bat_priv->orig_hash_lock); - for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; - + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { if (!orig_node->router) continue; @@ -374,8 +439,8 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) neigh_node->addr, neigh_node->if_incoming->net_dev->name); - list_for_each_entry(neigh_node, &orig_node->neigh_list, - list) { + hlist_for_each_entry_rcu(neigh_node, node_tmp, + &orig_node->neigh_list, list) { seq_printf(seq, " %pM (%3i)", neigh_node->addr, neigh_node->tq_avg); } @@ -383,10 +448,9 @@ int orig_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "\n"); batman_count++; } + rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->orig_hash_lock); - if ((batman_count == 0)) seq_printf(seq, "No batman nodes in range ...\n"); @@ -423,36 +487,36 @@ static int orig_node_add_if(struct orig_node *orig_node, int max_if_num) return 0; } -int orig_hash_add_if(struct batman_if *batman_if, int max_if_num) +int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; - int i; + int i, ret; /* resize all orig nodes because orig_node->bcast_own(_sum) depend on * if_num */ - spin_lock_bh(&bat_priv->orig_hash_lock); - for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + spin_lock_bh(&orig_node->ogm_cnt_lock); + ret = orig_node_add_if(orig_node, max_if_num); + spin_unlock_bh(&orig_node->ogm_cnt_lock); - if (orig_node_add_if(orig_node, max_if_num) == -1) + if (ret == -1) goto err; } + rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->orig_hash_lock); return 0; err: - spin_unlock_bh(&bat_priv->orig_hash_lock); + rcu_read_unlock(); return -ENOMEM; } @@ -508,57 +572,55 @@ free_own_sum: return 0; } -int orig_hash_del_if(struct batman_if *batman_if, int max_if_num) +int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; - struct batman_if *batman_if_tmp; + struct hard_iface *hard_iface_tmp; struct orig_node *orig_node; int i, ret; /* resize all orig nodes because orig_node->bcast_own(_sum) depend on * if_num */ - spin_lock_bh(&bat_priv->orig_hash_lock); - for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; - + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + spin_lock_bh(&orig_node->ogm_cnt_lock); ret = orig_node_del_if(orig_node, max_if_num, - batman_if->if_num); + hard_iface->if_num); + spin_unlock_bh(&orig_node->ogm_cnt_lock); if (ret == -1) goto err; } + rcu_read_unlock(); } /* renumber remaining batman interfaces _inside_ of orig_hash_lock */ rcu_read_lock(); - list_for_each_entry_rcu(batman_if_tmp, &if_list, list) { - if (batman_if_tmp->if_status == IF_NOT_IN_USE) + list_for_each_entry_rcu(hard_iface_tmp, &hardif_list, list) { + if (hard_iface_tmp->if_status == IF_NOT_IN_USE) continue; - if (batman_if == batman_if_tmp) + if (hard_iface == hard_iface_tmp) continue; - if (batman_if->soft_iface != batman_if_tmp->soft_iface) + if (hard_iface->soft_iface != hard_iface_tmp->soft_iface) continue; - if (batman_if_tmp->if_num > batman_if->if_num) - batman_if_tmp->if_num--; + if (hard_iface_tmp->if_num > hard_iface->if_num) + hard_iface_tmp->if_num--; } rcu_read_unlock(); - batman_if->if_num = -1; - spin_unlock_bh(&bat_priv->orig_hash_lock); + hard_iface->if_num = -1; return 0; err: - spin_unlock_bh(&bat_priv->orig_hash_lock); + rcu_read_unlock(); return -ENOMEM; } diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 8019fbddffd..5cc011057da 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h @@ -22,21 +22,28 @@ #ifndef _NET_BATMAN_ADV_ORIGINATOR_H_ #define _NET_BATMAN_ADV_ORIGINATOR_H_ +#include "hash.h" + int originator_init(struct bat_priv *bat_priv); void originator_free(struct bat_priv *bat_priv); void purge_orig_ref(struct bat_priv *bat_priv); +void orig_node_free_ref(struct orig_node *orig_node); struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr); -struct neigh_node * -create_neighbor(struct orig_node *orig_node, struct orig_node *orig_neigh_node, - uint8_t *neigh, struct batman_if *if_incoming); +struct neigh_node *create_neighbor(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + uint8_t *neigh, + struct hard_iface *if_incoming); +void neigh_node_free_ref(struct neigh_node *neigh_node); int orig_seq_print_text(struct seq_file *seq, void *offset); -int orig_hash_add_if(struct batman_if *batman_if, int max_if_num); -int orig_hash_del_if(struct batman_if *batman_if, int max_if_num); +int orig_hash_add_if(struct hard_iface *hard_iface, int max_if_num); +int orig_hash_del_if(struct hard_iface *hard_iface, int max_if_num); /* returns 1 if they are the same originator */ -static inline int compare_orig(void *data1, void *data2) +static inline int compare_orig(struct hlist_node *node, void *data2) { + void *data1 = container_of(node, struct orig_node, hash_entry); + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); } @@ -61,4 +68,35 @@ static inline int choose_orig(void *data, int32_t size) return hash % size; } +static inline struct orig_node *orig_hash_find(struct bat_priv *bat_priv, + void *data) +{ + struct hashtable_t *hash = bat_priv->orig_hash; + struct hlist_head *head; + struct hlist_node *node; + struct orig_node *orig_node, *orig_node_tmp = NULL; + int index; + + if (!hash) + return NULL; + + index = choose_orig(data, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + if (!compare_eth(orig_node, data)) + continue; + + if (!atomic_inc_not_zero(&orig_node->refcount)) + continue; + + orig_node_tmp = orig_node; + break; + } + rcu_read_unlock(); + + return orig_node_tmp; +} + #endif /* _NET_BATMAN_ADV_ORIGINATOR_H_ */ diff --git a/net/batman-adv/routing.c b/net/batman-adv/routing.c index 827414067e4..c172f5d0e05 100644 --- a/net/batman-adv/routing.c +++ b/net/batman-adv/routing.c @@ -35,35 +35,33 @@ #include "gateway_client.h" #include "unicast.h" -void slide_own_bcast_window(struct batman_if *batman_if) +void slide_own_bcast_window(struct hard_iface *hard_iface) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; unsigned long *word; int i; size_t word_index; - spin_lock_bh(&bat_priv->orig_hash_lock); - for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; - word_index = batman_if->if_num * NUM_WORDS; + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { + spin_lock_bh(&orig_node->ogm_cnt_lock); + word_index = hard_iface->if_num * NUM_WORDS; word = &(orig_node->bcast_own[word_index]); bit_get_packet(bat_priv, word, 1, 0); - orig_node->bcast_own_sum[batman_if->if_num] = + orig_node->bcast_own_sum[hard_iface->if_num] = bit_packet_count(word); + spin_unlock_bh(&orig_node->ogm_cnt_lock); } + rcu_read_unlock(); } - - spin_unlock_bh(&bat_priv->orig_hash_lock); } static void update_HNA(struct bat_priv *bat_priv, struct orig_node *orig_node, @@ -89,6 +87,8 @@ static void update_route(struct bat_priv *bat_priv, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len) { + struct neigh_node *neigh_node_tmp; + /* route deleted */ if ((orig_node->router) && (!neigh_node)) { @@ -115,7 +115,12 @@ static void update_route(struct bat_priv *bat_priv, orig_node->router->addr); } + if (neigh_node && !atomic_inc_not_zero(&neigh_node->refcount)) + neigh_node = NULL; + neigh_node_tmp = orig_node->router; orig_node->router = neigh_node; + if (neigh_node_tmp) + neigh_node_free_ref(neigh_node_tmp); } @@ -138,73 +143,93 @@ void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, static int is_bidirectional_neigh(struct orig_node *orig_node, struct orig_node *orig_neigh_node, struct batman_packet *batman_packet, - struct batman_if *if_incoming) + struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + struct neigh_node *neigh_node = NULL, *tmp_neigh_node; + struct hlist_node *node; unsigned char total_count; + uint8_t orig_eq_count, neigh_rq_count, tq_own; + int tq_asym_penalty, ret = 0; if (orig_node == orig_neigh_node) { - list_for_each_entry(tmp_neigh_node, - &orig_node->neigh_list, - list) { - - if (compare_orig(tmp_neigh_node->addr, - orig_neigh_node->orig) && - (tmp_neigh_node->if_incoming == if_incoming)) - neigh_node = tmp_neigh_node; + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_node->neigh_list, list) { + + if (!compare_eth(tmp_neigh_node->addr, + orig_neigh_node->orig)) + continue; + + if (tmp_neigh_node->if_incoming != if_incoming) + continue; + + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue; + + neigh_node = tmp_neigh_node; } + rcu_read_unlock(); if (!neigh_node) neigh_node = create_neighbor(orig_node, orig_neigh_node, orig_neigh_node->orig, if_incoming); - /* create_neighbor failed, return 0 */ if (!neigh_node) - return 0; + goto out; neigh_node->last_valid = jiffies; } else { /* find packet count of corresponding one hop neighbor */ - list_for_each_entry(tmp_neigh_node, - &orig_neigh_node->neigh_list, list) { + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_neigh_node->neigh_list, list) { + + if (!compare_eth(tmp_neigh_node->addr, + orig_neigh_node->orig)) + continue; + + if (tmp_neigh_node->if_incoming != if_incoming) + continue; - if (compare_orig(tmp_neigh_node->addr, - orig_neigh_node->orig) && - (tmp_neigh_node->if_incoming == if_incoming)) - neigh_node = tmp_neigh_node; + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue; + + neigh_node = tmp_neigh_node; } + rcu_read_unlock(); if (!neigh_node) neigh_node = create_neighbor(orig_neigh_node, orig_neigh_node, orig_neigh_node->orig, if_incoming); - /* create_neighbor failed, return 0 */ if (!neigh_node) - return 0; + goto out; } orig_node->last_valid = jiffies; + spin_lock_bh(&orig_node->ogm_cnt_lock); + orig_eq_count = orig_neigh_node->bcast_own_sum[if_incoming->if_num]; + neigh_rq_count = neigh_node->real_packet_count; + spin_unlock_bh(&orig_node->ogm_cnt_lock); + /* pay attention to not get a value bigger than 100 % */ - total_count = (orig_neigh_node->bcast_own_sum[if_incoming->if_num] > - neigh_node->real_packet_count ? - neigh_node->real_packet_count : - orig_neigh_node->bcast_own_sum[if_incoming->if_num]); + total_count = (orig_eq_count > neigh_rq_count ? + neigh_rq_count : orig_eq_count); /* if we have too few packets (too less data) we set tq_own to zero */ /* if we receive too few packets it is not considered bidirectional */ if ((total_count < TQ_LOCAL_BIDRECT_SEND_MINIMUM) || - (neigh_node->real_packet_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) - orig_neigh_node->tq_own = 0; + (neigh_rq_count < TQ_LOCAL_BIDRECT_RECV_MINIMUM)) + tq_own = 0; else /* neigh_node->real_packet_count is never zero as we * only purge old information when getting new * information */ - orig_neigh_node->tq_own = (TQ_MAX_VALUE * total_count) / - neigh_node->real_packet_count; + tq_own = (TQ_MAX_VALUE * total_count) / neigh_rq_count; /* * 1 - ((1-x) ** 3), normalized to TQ_MAX_VALUE this does @@ -212,20 +237,16 @@ static int is_bidirectional_neigh(struct orig_node *orig_node, * punishes asymmetric links more. This will give a value * between 0 and TQ_MAX_VALUE */ - orig_neigh_node->tq_asym_penalty = - TQ_MAX_VALUE - - (TQ_MAX_VALUE * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count) * - (TQ_LOCAL_WINDOW_SIZE - neigh_node->real_packet_count)) / - (TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE * - TQ_LOCAL_WINDOW_SIZE); - - batman_packet->tq = ((batman_packet->tq * - orig_neigh_node->tq_own * - orig_neigh_node->tq_asym_penalty) / - (TQ_MAX_VALUE * TQ_MAX_VALUE)); + tq_asym_penalty = TQ_MAX_VALUE - (TQ_MAX_VALUE * + (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count) * + (TQ_LOCAL_WINDOW_SIZE - neigh_rq_count)) / + (TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE * + TQ_LOCAL_WINDOW_SIZE); + + batman_packet->tq = ((batman_packet->tq * tq_own * tq_asym_penalty) / + (TQ_MAX_VALUE * TQ_MAX_VALUE)); bat_dbg(DBG_BATMAN, bat_priv, "bidirectional: " @@ -233,34 +254,141 @@ static int is_bidirectional_neigh(struct orig_node *orig_node, "real recv = %2i, local tq: %3i, asym_penalty: %3i, " "total tq: %3i\n", orig_node->orig, orig_neigh_node->orig, total_count, - neigh_node->real_packet_count, orig_neigh_node->tq_own, - orig_neigh_node->tq_asym_penalty, batman_packet->tq); + neigh_rq_count, tq_own, tq_asym_penalty, batman_packet->tq); /* if link has the minimum required transmission quality * consider it bidirectional */ if (batman_packet->tq >= TQ_TOTAL_BIDRECT_LIMIT) - return 1; + ret = 1; - return 0; +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + return ret; +} + +/* caller must hold the neigh_list_lock */ +void bonding_candidate_del(struct orig_node *orig_node, + struct neigh_node *neigh_node) +{ + /* this neighbor is not part of our candidate list */ + if (list_empty(&neigh_node->bonding_list)) + goto out; + + list_del_rcu(&neigh_node->bonding_list); + INIT_LIST_HEAD(&neigh_node->bonding_list); + neigh_node_free_ref(neigh_node); + atomic_dec(&orig_node->bond_candidates); + +out: + return; +} + +static void bonding_candidate_add(struct orig_node *orig_node, + struct neigh_node *neigh_node) +{ + struct hlist_node *node; + struct neigh_node *tmp_neigh_node; + uint8_t best_tq, interference_candidate = 0; + + spin_lock_bh(&orig_node->neigh_list_lock); + + /* only consider if it has the same primary address ... */ + if (!compare_eth(orig_node->orig, + neigh_node->orig_node->primary_addr)) + goto candidate_del; + + if (!orig_node->router) + goto candidate_del; + + best_tq = orig_node->router->tq_avg; + + /* ... and is good enough to be considered */ + if (neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) + goto candidate_del; + + /** + * check if we have another candidate with the same mac address or + * interface. If we do, we won't select this candidate because of + * possible interference. + */ + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_node->neigh_list, list) { + + if (tmp_neigh_node == neigh_node) + continue; + + /* we only care if the other candidate is even + * considered as candidate. */ + if (list_empty(&tmp_neigh_node->bonding_list)) + continue; + + if ((neigh_node->if_incoming == tmp_neigh_node->if_incoming) || + (compare_eth(neigh_node->addr, tmp_neigh_node->addr))) { + interference_candidate = 1; + break; + } + } + + /* don't care further if it is an interference candidate */ + if (interference_candidate) + goto candidate_del; + + /* this neighbor already is part of our candidate list */ + if (!list_empty(&neigh_node->bonding_list)) + goto out; + + if (!atomic_inc_not_zero(&neigh_node->refcount)) + goto out; + + list_add_rcu(&neigh_node->bonding_list, &orig_node->bond_list); + atomic_inc(&orig_node->bond_candidates); + goto out; + +candidate_del: + bonding_candidate_del(orig_node, neigh_node); + +out: + spin_unlock_bh(&orig_node->neigh_list_lock); + return; +} + +/* copy primary address for bonding */ +static void bonding_save_primary(struct orig_node *orig_node, + struct orig_node *orig_neigh_node, + struct batman_packet *batman_packet) +{ + if (!(batman_packet->flags & PRIMARIES_FIRST_HOP)) + return; + + memcpy(orig_neigh_node->primary_addr, orig_node->orig, ETH_ALEN); } static void update_orig(struct bat_priv *bat_priv, struct orig_node *orig_node, struct ethhdr *ethhdr, struct batman_packet *batman_packet, - struct batman_if *if_incoming, + struct hard_iface *if_incoming, unsigned char *hna_buff, int hna_buff_len, char is_duplicate) { struct neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL; + struct orig_node *orig_node_tmp; + struct hlist_node *node; int tmp_hna_buff_len; + uint8_t bcast_own_sum_orig, bcast_own_sum_neigh; bat_dbg(DBG_BATMAN, bat_priv, "update_originator(): " "Searching and updating originator entry of received packet\n"); - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && - (tmp_neigh_node->if_incoming == if_incoming)) { + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_node->neigh_list, list) { + if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) && + (tmp_neigh_node->if_incoming == if_incoming) && + atomic_inc_not_zero(&tmp_neigh_node->refcount)) { + if (neigh_node) + neigh_node_free_ref(neigh_node); neigh_node = tmp_neigh_node; continue; } @@ -279,16 +407,20 @@ static void update_orig(struct bat_priv *bat_priv, orig_tmp = get_orig_node(bat_priv, ethhdr->h_source); if (!orig_tmp) - return; + goto unlock; neigh_node = create_neighbor(orig_node, orig_tmp, ethhdr->h_source, if_incoming); + + orig_node_free_ref(orig_tmp); if (!neigh_node) - return; + goto unlock; } else bat_dbg(DBG_BATMAN, bat_priv, "Updating existing last-hop neighbor of originator\n"); + rcu_read_unlock(); + orig_node->flags = batman_packet->flags; neigh_node->last_valid = jiffies; @@ -302,6 +434,8 @@ static void update_orig(struct bat_priv *bat_priv, neigh_node->last_ttl = batman_packet->ttl; } + bonding_candidate_add(orig_node, neigh_node); + tmp_hna_buff_len = (hna_buff_len > batman_packet->num_hna * ETH_ALEN ? batman_packet->num_hna * ETH_ALEN : hna_buff_len); @@ -318,10 +452,22 @@ static void update_orig(struct bat_priv *bat_priv, /* if the TQ is the same and the link not more symetric we * won't consider it either */ if ((orig_node->router) && - ((neigh_node->tq_avg == orig_node->router->tq_avg) && - (orig_node->router->orig_node->bcast_own_sum[if_incoming->if_num] - >= neigh_node->orig_node->bcast_own_sum[if_incoming->if_num]))) - goto update_hna; + (neigh_node->tq_avg == orig_node->router->tq_avg)) { + orig_node_tmp = orig_node->router->orig_node; + spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); + bcast_own_sum_orig = + orig_node_tmp->bcast_own_sum[if_incoming->if_num]; + spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); + + orig_node_tmp = neigh_node->orig_node; + spin_lock_bh(&orig_node_tmp->ogm_cnt_lock); + bcast_own_sum_neigh = + orig_node_tmp->bcast_own_sum[if_incoming->if_num]; + spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock); + + if (bcast_own_sum_orig >= bcast_own_sum_neigh) + goto update_hna; + } update_routes(bat_priv, orig_node, neigh_node, hna_buff, tmp_hna_buff_len); @@ -342,6 +488,14 @@ update_gw: (atomic_read(&bat_priv->gw_mode) == GW_MODE_CLIENT) && (atomic_read(&bat_priv->gw_sel_class) > 2)) gw_check_election(bat_priv, orig_node); + + goto out; + +unlock: + rcu_read_unlock(); +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); } /* checks whether the host restarted and is in the protection time. @@ -379,34 +533,38 @@ static int window_protected(struct bat_priv *bat_priv, */ static char count_real_packets(struct ethhdr *ethhdr, struct batman_packet *batman_packet, - struct batman_if *if_incoming) + struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); struct orig_node *orig_node; struct neigh_node *tmp_neigh_node; + struct hlist_node *node; char is_duplicate = 0; int32_t seq_diff; int need_update = 0; - int set_mark; + int set_mark, ret = -1; orig_node = get_orig_node(bat_priv, batman_packet->orig); if (!orig_node) return 0; + spin_lock_bh(&orig_node->ogm_cnt_lock); seq_diff = batman_packet->seqno - orig_node->last_real_seqno; /* signalize caller that the packet is to be dropped. */ if (window_protected(bat_priv, seq_diff, &orig_node->batman_seqno_reset)) - return -1; + goto out; - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { + rcu_read_lock(); + hlist_for_each_entry_rcu(tmp_neigh_node, node, + &orig_node->neigh_list, list) { is_duplicate |= get_bit_status(tmp_neigh_node->real_bits, orig_node->last_real_seqno, batman_packet->seqno); - if (compare_orig(tmp_neigh_node->addr, ethhdr->h_source) && + if (compare_eth(tmp_neigh_node->addr, ethhdr->h_source) && (tmp_neigh_node->if_incoming == if_incoming)) set_mark = 1; else @@ -420,6 +578,7 @@ static char count_real_packets(struct ethhdr *ethhdr, tmp_neigh_node->real_packet_count = bit_packet_count(tmp_neigh_node->real_bits); } + rcu_read_unlock(); if (need_update) { bat_dbg(DBG_BATMAN, bat_priv, @@ -428,121 +587,21 @@ static char count_real_packets(struct ethhdr *ethhdr, orig_node->last_real_seqno = batman_packet->seqno; } - return is_duplicate; -} - -/* copy primary address for bonding */ -static void mark_bonding_address(struct orig_node *orig_node, - struct orig_node *orig_neigh_node, - struct batman_packet *batman_packet) - -{ - if (batman_packet->flags & PRIMARIES_FIRST_HOP) - memcpy(orig_neigh_node->primary_addr, - orig_node->orig, ETH_ALEN); - - return; -} - -/* mark possible bond.candidates in the neighbor list */ -void update_bonding_candidates(struct orig_node *orig_node) -{ - int candidates; - int interference_candidate; - int best_tq; - struct neigh_node *tmp_neigh_node, *tmp_neigh_node2; - struct neigh_node *first_candidate, *last_candidate; - - /* update the candidates for this originator */ - if (!orig_node->router) { - orig_node->bond.candidates = 0; - return; - } - - best_tq = orig_node->router->tq_avg; - - /* update bond.candidates */ - - candidates = 0; - - /* mark other nodes which also received "PRIMARIES FIRST HOP" packets - * as "bonding partner" */ - - /* first, zero the list */ - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - tmp_neigh_node->next_bond_candidate = NULL; - } - - first_candidate = NULL; - last_candidate = NULL; - list_for_each_entry(tmp_neigh_node, &orig_node->neigh_list, list) { - - /* only consider if it has the same primary address ... */ - if (memcmp(orig_node->orig, - tmp_neigh_node->orig_node->primary_addr, - ETH_ALEN) != 0) - continue; - - /* ... and is good enough to be considered */ - if (tmp_neigh_node->tq_avg < best_tq - BONDING_TQ_THRESHOLD) - continue; - - /* check if we have another candidate with the same - * mac address or interface. If we do, we won't - * select this candidate because of possible interference. */ - - interference_candidate = 0; - list_for_each_entry(tmp_neigh_node2, - &orig_node->neigh_list, list) { - - if (tmp_neigh_node2 == tmp_neigh_node) - continue; - - /* we only care if the other candidate is even - * considered as candidate. */ - if (!tmp_neigh_node2->next_bond_candidate) - continue; - - - if ((tmp_neigh_node->if_incoming == - tmp_neigh_node2->if_incoming) - || (memcmp(tmp_neigh_node->addr, - tmp_neigh_node2->addr, ETH_ALEN) == 0)) { - - interference_candidate = 1; - break; - } - } - /* don't care further if it is an interference candidate */ - if (interference_candidate) - continue; - - if (!first_candidate) { - first_candidate = tmp_neigh_node; - tmp_neigh_node->next_bond_candidate = first_candidate; - } else - tmp_neigh_node->next_bond_candidate = last_candidate; - - last_candidate = tmp_neigh_node; - - candidates++; - } - - if (candidates > 0) { - first_candidate->next_bond_candidate = last_candidate; - orig_node->bond.selected = first_candidate; - } + ret = is_duplicate; - orig_node->bond.candidates = candidates; +out: + spin_unlock_bh(&orig_node->ogm_cnt_lock); + orig_node_free_ref(orig_node); + return ret; } void receive_bat_packet(struct ethhdr *ethhdr, - struct batman_packet *batman_packet, - unsigned char *hna_buff, int hna_buff_len, - struct batman_if *if_incoming) + struct batman_packet *batman_packet, + unsigned char *hna_buff, int hna_buff_len, + struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); - struct batman_if *batman_if; + struct hard_iface *hard_iface; struct orig_node *orig_neigh_node, *orig_node; char has_directlink_flag; char is_my_addr = 0, is_my_orig = 0, is_my_oldorig = 0; @@ -570,8 +629,8 @@ void receive_bat_packet(struct ethhdr *ethhdr, has_directlink_flag = (batman_packet->flags & DIRECTLINK ? 1 : 0); - is_single_hop_neigh = (compare_orig(ethhdr->h_source, - batman_packet->orig) ? 1 : 0); + is_single_hop_neigh = (compare_eth(ethhdr->h_source, + batman_packet->orig) ? 1 : 0); bat_dbg(DBG_BATMAN, bat_priv, "Received BATMAN packet via NB: %pM, IF: %s [%pM] " @@ -584,26 +643,26 @@ void receive_bat_packet(struct ethhdr *ethhdr, has_directlink_flag); rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->if_status != IF_ACTIVE) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->if_status != IF_ACTIVE) continue; - if (batman_if->soft_iface != if_incoming->soft_iface) + if (hard_iface->soft_iface != if_incoming->soft_iface) continue; - if (compare_orig(ethhdr->h_source, - batman_if->net_dev->dev_addr)) + if (compare_eth(ethhdr->h_source, + hard_iface->net_dev->dev_addr)) is_my_addr = 1; - if (compare_orig(batman_packet->orig, - batman_if->net_dev->dev_addr)) + if (compare_eth(batman_packet->orig, + hard_iface->net_dev->dev_addr)) is_my_orig = 1; - if (compare_orig(batman_packet->prev_sender, - batman_if->net_dev->dev_addr)) + if (compare_eth(batman_packet->prev_sender, + hard_iface->net_dev->dev_addr)) is_my_oldorig = 1; - if (compare_orig(ethhdr->h_source, broadcast_addr)) + if (compare_eth(ethhdr->h_source, broadcast_addr)) is_broadcast = 1; } rcu_read_unlock(); @@ -635,7 +694,6 @@ void receive_bat_packet(struct ethhdr *ethhdr, int offset; orig_neigh_node = get_orig_node(bat_priv, ethhdr->h_source); - if (!orig_neigh_node) return; @@ -644,18 +702,22 @@ void receive_bat_packet(struct ethhdr *ethhdr, /* if received seqno equals last send seqno save new * seqno for bidirectional check */ if (has_directlink_flag && - compare_orig(if_incoming->net_dev->dev_addr, - batman_packet->orig) && + compare_eth(if_incoming->net_dev->dev_addr, + batman_packet->orig) && (batman_packet->seqno - if_incoming_seqno + 2 == 0)) { offset = if_incoming->if_num * NUM_WORDS; + + spin_lock_bh(&orig_neigh_node->ogm_cnt_lock); word = &(orig_neigh_node->bcast_own[offset]); bit_mark(word, 0); orig_neigh_node->bcast_own_sum[if_incoming->if_num] = bit_packet_count(word); + spin_unlock_bh(&orig_neigh_node->ogm_cnt_lock); } bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: " "originator packet from myself (via neighbor)\n"); + orig_node_free_ref(orig_neigh_node); return; } @@ -676,27 +738,27 @@ void receive_bat_packet(struct ethhdr *ethhdr, bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: packet within seqno protection time " "(sender: %pM)\n", ethhdr->h_source); - return; + goto out; } if (batman_packet->tq == 0) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: originator packet with tq equal 0\n"); - return; + goto out; } /* avoid temporary routing loops */ if ((orig_node->router) && (orig_node->router->orig_node->router) && - (compare_orig(orig_node->router->addr, - batman_packet->prev_sender)) && - !(compare_orig(batman_packet->orig, batman_packet->prev_sender)) && - (compare_orig(orig_node->router->addr, - orig_node->router->orig_node->router->addr))) { + (compare_eth(orig_node->router->addr, + batman_packet->prev_sender)) && + !(compare_eth(batman_packet->orig, batman_packet->prev_sender)) && + (compare_eth(orig_node->router->addr, + orig_node->router->orig_node->router->addr))) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: ignoring all rebroadcast packets that " "may make me loop (sender: %pM)\n", ethhdr->h_source); - return; + goto out; } /* if sender is a direct neighbor the sender mac equals @@ -705,19 +767,21 @@ void receive_bat_packet(struct ethhdr *ethhdr, orig_node : get_orig_node(bat_priv, ethhdr->h_source)); if (!orig_neigh_node) - return; + goto out; /* drop packet if sender is not a direct neighbor and if we * don't route towards it */ if (!is_single_hop_neigh && (!orig_neigh_node->router)) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: OGM via unknown neighbor!\n"); - return; + goto out_neigh; } is_bidirectional = is_bidirectional_neigh(orig_node, orig_neigh_node, batman_packet, if_incoming); + bonding_save_primary(orig_node, orig_neigh_node, batman_packet); + /* update ranking if it is not a duplicate or has the same * seqno and similar ttl as the non-duplicate */ if (is_bidirectional && @@ -727,9 +791,6 @@ void receive_bat_packet(struct ethhdr *ethhdr, update_orig(bat_priv, orig_node, ethhdr, batman_packet, if_incoming, hna_buff, hna_buff_len, is_duplicate); - mark_bonding_address(orig_node, orig_neigh_node, batman_packet); - update_bonding_candidates(orig_node); - /* is single hop (direct) neighbor */ if (is_single_hop_neigh) { @@ -739,31 +800,36 @@ void receive_bat_packet(struct ethhdr *ethhdr, bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: " "rebroadcast neighbor packet with direct link flag\n"); - return; + goto out_neigh; } /* multihop originator */ if (!is_bidirectional) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: not received via bidirectional link\n"); - return; + goto out_neigh; } if (is_duplicate) { bat_dbg(DBG_BATMAN, bat_priv, "Drop packet: duplicate packet received\n"); - return; + goto out_neigh; } bat_dbg(DBG_BATMAN, bat_priv, "Forwarding packet: rebroadcast originator packet\n"); schedule_forward_packet(orig_node, ethhdr, batman_packet, 0, hna_buff_len, if_incoming); + +out_neigh: + if ((orig_neigh_node) && (!is_single_hop_neigh)) + orig_node_free_ref(orig_neigh_node); +out: + orig_node_free_ref(orig_node); } -int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) +int recv_bat_packet(struct sk_buff *skb, struct hard_iface *hard_iface) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); struct ethhdr *ethhdr; /* drop packet if it has not necessary minimum size */ @@ -790,12 +856,10 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) ethhdr = (struct ethhdr *)skb_mac_header(skb); - spin_lock_bh(&bat_priv->orig_hash_lock); receive_aggr_bat_packet(ethhdr, skb->data, skb_headlen(skb), - batman_if); - spin_unlock_bh(&bat_priv->orig_hash_lock); + hard_iface); kfree_skb(skb); return NET_RX_SUCCESS; @@ -804,68 +868,75 @@ int recv_bat_packet(struct sk_buff *skb, struct batman_if *batman_if) static int recv_my_icmp_packet(struct bat_priv *bat_priv, struct sk_buff *skb, size_t icmp_len) { - struct orig_node *orig_node; + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; struct icmp_packet_rr *icmp_packet; - struct batman_if *batman_if; - int ret; - uint8_t dstaddr[ETH_ALEN]; + int ret = NET_RX_DROP; icmp_packet = (struct icmp_packet_rr *)skb->data; /* add data to device queue */ if (icmp_packet->msg_type != ECHO_REQUEST) { bat_socket_receive_packet(icmp_packet, icmp_len); - return NET_RX_DROP; + goto out; } if (!bat_priv->primary_if) - return NET_RX_DROP; + goto out; /* answer echo request (ping) */ /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, - compare_orig, choose_orig, - icmp_packet->orig)); - ret = NET_RX_DROP; - - if ((orig_node) && (orig_node->router)) { - - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); - - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, icmp_packet->orig); + + if (!orig_node) + goto unlock; - icmp_packet = (struct icmp_packet_rr *)skb->data; + neigh_node = orig_node->router; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = ECHO_REPLY; - icmp_packet->ttl = TTL; + if (!neigh_node) + goto unlock; - send_skb_packet(skb, batman_if, dstaddr); - ret = NET_RX_SUCCESS; + if (!atomic_inc_not_zero(&neigh_node->refcount)) { + neigh_node = NULL; + goto unlock; + } - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); + rcu_read_unlock(); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + goto out; + + icmp_packet = (struct icmp_packet_rr *)skb->data; + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + icmp_packet->msg_type = ECHO_REPLY; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = NET_RX_SUCCESS; + goto out; + +unlock: + rcu_read_unlock(); +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); return ret; } static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, struct sk_buff *skb) { - struct orig_node *orig_node; + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; struct icmp_packet *icmp_packet; - struct batman_if *batman_if; - int ret; - uint8_t dstaddr[ETH_ALEN]; + int ret = NET_RX_DROP; icmp_packet = (struct icmp_packet *)skb->data; @@ -874,59 +945,67 @@ static int recv_icmp_ttl_exceeded(struct bat_priv *bat_priv, pr_debug("Warning - can't forward icmp packet from %pM to " "%pM: ttl exceeded\n", icmp_packet->orig, icmp_packet->dst); - return NET_RX_DROP; + goto out; } if (!bat_priv->primary_if) - return NET_RX_DROP; + goto out; /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - icmp_packet->orig)); - ret = NET_RX_DROP; - - if ((orig_node) && (orig_node->router)) { - - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); - - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, icmp_packet->orig); - icmp_packet = (struct icmp_packet *) skb->data; + if (!orig_node) + goto unlock; - memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); - memcpy(icmp_packet->orig, - bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); - icmp_packet->msg_type = TTL_EXCEEDED; - icmp_packet->ttl = TTL; + neigh_node = orig_node->router; - send_skb_packet(skb, batman_if, dstaddr); - ret = NET_RX_SUCCESS; + if (!neigh_node) + goto unlock; - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (!atomic_inc_not_zero(&neigh_node->refcount)) { + neigh_node = NULL; + goto unlock; + } + + rcu_read_unlock(); + + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + goto out; + icmp_packet = (struct icmp_packet *)skb->data; + + memcpy(icmp_packet->dst, icmp_packet->orig, ETH_ALEN); + memcpy(icmp_packet->orig, + bat_priv->primary_if->net_dev->dev_addr, ETH_ALEN); + icmp_packet->msg_type = TTL_EXCEEDED; + icmp_packet->ttl = TTL; + + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = NET_RX_SUCCESS; + goto out; + +unlock: + rcu_read_unlock(); +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); return ret; } -int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) +int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct icmp_packet_rr *icmp_packet; struct ethhdr *ethhdr; - struct orig_node *orig_node; - struct batman_if *batman_if; + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; int hdr_size = sizeof(struct icmp_packet); - int ret; - uint8_t dstaddr[ETH_ALEN]; + int ret = NET_RX_DROP; /** * we truncate all incoming icmp packets if they don't match our size @@ -936,21 +1015,21 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; + goto out; ethhdr = (struct ethhdr *)skb_mac_header(skb); /* packet with unicast indication but broadcast recipient */ if (is_broadcast_ether_addr(ethhdr->h_dest)) - return NET_RX_DROP; + goto out; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - return NET_RX_DROP; + goto out; /* not for me */ if (!is_my_mac(ethhdr->h_dest)) - return NET_RX_DROP; + goto out; icmp_packet = (struct icmp_packet_rr *)skb->data; @@ -970,50 +1049,59 @@ int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if) if (icmp_packet->ttl < 2) return recv_icmp_ttl_exceeded(bat_priv, skb); - ret = NET_RX_DROP; - /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - icmp_packet->dst)); + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, icmp_packet->dst); - if ((orig_node) && (orig_node->router)) { + if (!orig_node) + goto unlock; - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); + neigh_node = orig_node->router; - /* create a copy of the skb, if needed, to modify it. */ - if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + if (!neigh_node) + goto unlock; - icmp_packet = (struct icmp_packet_rr *)skb->data; + if (!atomic_inc_not_zero(&neigh_node->refcount)) { + neigh_node = NULL; + goto unlock; + } - /* decrement ttl */ - icmp_packet->ttl--; + rcu_read_unlock(); - /* route it */ - send_skb_packet(skb, batman_if, dstaddr); - ret = NET_RX_SUCCESS; + /* create a copy of the skb, if needed, to modify it. */ + if (skb_cow(skb, sizeof(struct ethhdr)) < 0) + goto out; - } else - spin_unlock_bh(&bat_priv->orig_hash_lock); + icmp_packet = (struct icmp_packet_rr *)skb->data; + + /* decrement ttl */ + icmp_packet->ttl--; + + /* route it */ + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = NET_RX_SUCCESS; + goto out; +unlock: + rcu_read_unlock(); +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); return ret; } /* find a suitable router for this originator, and use - * bonding if possible. */ + * bonding if possible. increases the found neighbors + * refcount.*/ struct neigh_node *find_router(struct bat_priv *bat_priv, struct orig_node *orig_node, - struct batman_if *recv_if) + struct hard_iface *recv_if) { struct orig_node *primary_orig_node; struct orig_node *router_orig; - struct neigh_node *router, *first_candidate, *best_router; + struct neigh_node *router, *first_candidate, *tmp_neigh_node; static uint8_t zero_mac[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; int bonding_enabled; @@ -1025,78 +1113,128 @@ struct neigh_node *find_router(struct bat_priv *bat_priv, /* without bonding, the first node should * always choose the default router. */ - bonding_enabled = atomic_read(&bat_priv->bonding); - if ((!recv_if) && (!bonding_enabled)) - return orig_node->router; - + rcu_read_lock(); + /* select default router to output */ + router = orig_node->router; router_orig = orig_node->router->orig_node; + if (!router_orig || !atomic_inc_not_zero(&router->refcount)) { + rcu_read_unlock(); + return NULL; + } + + if ((!recv_if) && (!bonding_enabled)) + goto return_router; /* if we have something in the primary_addr, we can search * for a potential bonding candidate. */ - if (memcmp(router_orig->primary_addr, zero_mac, ETH_ALEN) == 0) - return orig_node->router; + if (compare_eth(router_orig->primary_addr, zero_mac)) + goto return_router; /* find the orig_node which has the primary interface. might * even be the same as our router_orig in many cases */ - if (memcmp(router_orig->primary_addr, - router_orig->orig, ETH_ALEN) == 0) { + if (compare_eth(router_orig->primary_addr, router_orig->orig)) { primary_orig_node = router_orig; } else { - primary_orig_node = hash_find(bat_priv->orig_hash, compare_orig, - choose_orig, - router_orig->primary_addr); - + primary_orig_node = orig_hash_find(bat_priv, + router_orig->primary_addr); if (!primary_orig_node) - return orig_node->router; + goto return_router; + + orig_node_free_ref(primary_orig_node); } /* with less than 2 candidates, we can't do any * bonding and prefer the original router. */ - - if (primary_orig_node->bond.candidates < 2) - return orig_node->router; + if (atomic_read(&primary_orig_node->bond_candidates) < 2) + goto return_router; /* all nodes between should choose a candidate which * is is not on the interface where the packet came * in. */ - first_candidate = primary_orig_node->bond.selected; - router = first_candidate; + + neigh_node_free_ref(router); + first_candidate = NULL; + router = NULL; if (bonding_enabled) { /* in the bonding case, send the packets in a round * robin fashion over the remaining interfaces. */ - do { + + list_for_each_entry_rcu(tmp_neigh_node, + &primary_orig_node->bond_list, bonding_list) { + if (!first_candidate) + first_candidate = tmp_neigh_node; /* recv_if == NULL on the first node. */ - if (router->if_incoming != recv_if) + if (tmp_neigh_node->if_incoming != recv_if && + atomic_inc_not_zero(&tmp_neigh_node->refcount)) { + router = tmp_neigh_node; break; + } + } - router = router->next_bond_candidate; - } while (router != first_candidate); + /* use the first candidate if nothing was found. */ + if (!router && first_candidate && + atomic_inc_not_zero(&first_candidate->refcount)) + router = first_candidate; - primary_orig_node->bond.selected = router->next_bond_candidate; + if (!router) { + rcu_read_unlock(); + return NULL; + } + + /* selected should point to the next element + * after the current router */ + spin_lock_bh(&primary_orig_node->neigh_list_lock); + /* this is a list_move(), which unfortunately + * does not exist as rcu version */ + list_del_rcu(&primary_orig_node->bond_list); + list_add_rcu(&primary_orig_node->bond_list, + &router->bonding_list); + spin_unlock_bh(&primary_orig_node->neigh_list_lock); } else { /* if bonding is disabled, use the best of the * remaining candidates which are not using * this interface. */ - best_router = first_candidate; + list_for_each_entry_rcu(tmp_neigh_node, + &primary_orig_node->bond_list, bonding_list) { + if (!first_candidate) + first_candidate = tmp_neigh_node; - do { /* recv_if == NULL on the first node. */ - if ((router->if_incoming != recv_if) && - (router->tq_avg > best_router->tq_avg)) - best_router = router; + if (tmp_neigh_node->if_incoming == recv_if) + continue; - router = router->next_bond_candidate; - } while (router != first_candidate); + if (!atomic_inc_not_zero(&tmp_neigh_node->refcount)) + continue; - router = best_router; - } + /* if we don't have a router yet + * or this one is better, choose it. */ + if ((!router) || + (tmp_neigh_node->tq_avg > router->tq_avg)) { + /* decrement refcount of + * previously selected router */ + if (router) + neigh_node_free_ref(router); + + router = tmp_neigh_node; + atomic_inc_not_zero(&router->refcount); + } + + neigh_node_free_ref(tmp_neigh_node); + } + /* use the first candidate if nothing was found. */ + if (!router && first_candidate && + atomic_inc_not_zero(&first_candidate->refcount)) + router = first_candidate; + } +return_router: + rcu_read_unlock(); return router; } @@ -1125,17 +1263,14 @@ static int check_unicast_packet(struct sk_buff *skb, int hdr_size) return 0; } -int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, - int hdr_size) +int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct orig_node *orig_node; - struct neigh_node *router; - struct batman_if *batman_if; - uint8_t dstaddr[ETH_ALEN]; + struct orig_node *orig_node = NULL; + struct neigh_node *neigh_node = NULL; struct unicast_packet *unicast_packet; struct ethhdr *ethhdr = (struct ethhdr *)skb_mac_header(skb); - int ret; + int ret = NET_RX_DROP; struct sk_buff *new_skb; unicast_packet = (struct unicast_packet *)skb->data; @@ -1145,53 +1280,51 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, pr_debug("Warning - can't forward unicast packet from %pM to " "%pM: ttl exceeded\n", ethhdr->h_source, unicast_packet->dest); - return NET_RX_DROP; + goto out; } /* get routing information */ - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - unicast_packet->dest)); - - router = find_router(bat_priv, orig_node, recv_if); + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, unicast_packet->dest); - if (!router) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (!orig_node) + goto unlock; - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ + rcu_read_unlock(); - batman_if = router->if_incoming; - memcpy(dstaddr, router->addr, ETH_ALEN); + /* find_router() increases neigh_nodes refcount if found. */ + neigh_node = find_router(bat_priv, orig_node, recv_if); - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (!neigh_node) + goto out; /* create a copy of the skb, if needed, to modify it. */ if (skb_cow(skb, sizeof(struct ethhdr)) < 0) - return NET_RX_DROP; + goto out; unicast_packet = (struct unicast_packet *)skb->data; if (unicast_packet->packet_type == BAT_UNICAST && atomic_read(&bat_priv->fragmentation) && - skb->len > batman_if->net_dev->mtu) - return frag_send_skb(skb, bat_priv, batman_if, - dstaddr); + skb->len > neigh_node->if_incoming->net_dev->mtu) { + ret = frag_send_skb(skb, bat_priv, + neigh_node->if_incoming, neigh_node->addr); + goto out; + } if (unicast_packet->packet_type == BAT_UNICAST_FRAG && - frag_can_reassemble(skb, batman_if->net_dev->mtu)) { + frag_can_reassemble(skb, neigh_node->if_incoming->net_dev->mtu)) { ret = frag_reassemble_skb(skb, bat_priv, &new_skb); if (ret == NET_RX_DROP) - return NET_RX_DROP; + goto out; /* packet was buffered for late merge */ - if (!new_skb) - return NET_RX_SUCCESS; + if (!new_skb) { + ret = NET_RX_SUCCESS; + goto out; + } skb = new_skb; unicast_packet = (struct unicast_packet *)skb->data; @@ -1201,12 +1334,21 @@ int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, unicast_packet->ttl--; /* route it */ - send_skb_packet(skb, batman_if, dstaddr); + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = NET_RX_SUCCESS; + goto out; - return NET_RX_SUCCESS; +unlock: + rcu_read_unlock(); +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); + return ret; } -int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) +int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct unicast_packet *unicast_packet; int hdr_size = sizeof(struct unicast_packet); @@ -1222,10 +1364,10 @@ int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if) return NET_RX_SUCCESS; } - return route_unicast_packet(skb, recv_if, hdr_size); + return route_unicast_packet(skb, recv_if); } -int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) +int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); struct unicast_frag_packet *unicast_packet; @@ -1255,89 +1397,96 @@ int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if) return NET_RX_SUCCESS; } - return route_unicast_packet(skb, recv_if, hdr_size); + return route_unicast_packet(skb, recv_if); } -int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if) +int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct bat_priv *bat_priv = netdev_priv(recv_if->soft_iface); - struct orig_node *orig_node; + struct orig_node *orig_node = NULL; struct bcast_packet *bcast_packet; struct ethhdr *ethhdr; int hdr_size = sizeof(struct bcast_packet); + int ret = NET_RX_DROP; int32_t seq_diff; /* drop packet if it has not necessary minimum size */ if (unlikely(!pskb_may_pull(skb, hdr_size))) - return NET_RX_DROP; + goto out; ethhdr = (struct ethhdr *)skb_mac_header(skb); /* packet with broadcast indication but unicast recipient */ if (!is_broadcast_ether_addr(ethhdr->h_dest)) - return NET_RX_DROP; + goto out; /* packet with broadcast sender address */ if (is_broadcast_ether_addr(ethhdr->h_source)) - return NET_RX_DROP; + goto out; /* ignore broadcasts sent by myself */ if (is_my_mac(ethhdr->h_source)) - return NET_RX_DROP; + goto out; bcast_packet = (struct bcast_packet *)skb->data; /* ignore broadcasts originated by myself */ if (is_my_mac(bcast_packet->orig)) - return NET_RX_DROP; + goto out; if (bcast_packet->ttl < 2) - return NET_RX_DROP; + goto out; - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - bcast_packet->orig)); + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, bcast_packet->orig); - if (!orig_node) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (!orig_node) + goto rcu_unlock; + + rcu_read_unlock(); + + spin_lock_bh(&orig_node->bcast_seqno_lock); /* check whether the packet is a duplicate */ - if (get_bit_status(orig_node->bcast_bits, - orig_node->last_bcast_seqno, - ntohl(bcast_packet->seqno))) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + if (get_bit_status(orig_node->bcast_bits, orig_node->last_bcast_seqno, + ntohl(bcast_packet->seqno))) + goto spin_unlock; seq_diff = ntohl(bcast_packet->seqno) - orig_node->last_bcast_seqno; /* check whether the packet is old and the host just restarted. */ if (window_protected(bat_priv, seq_diff, - &orig_node->bcast_seqno_reset)) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return NET_RX_DROP; - } + &orig_node->bcast_seqno_reset)) + goto spin_unlock; /* mark broadcast in flood history, update window position * if required. */ if (bit_get_packet(bat_priv, orig_node->bcast_bits, seq_diff, 1)) orig_node->last_bcast_seqno = ntohl(bcast_packet->seqno); - spin_unlock_bh(&bat_priv->orig_hash_lock); + spin_unlock_bh(&orig_node->bcast_seqno_lock); + /* rebroadcast packet */ add_bcast_packet_to_list(bat_priv, skb); /* broadcast for me */ interface_rx(recv_if->soft_iface, skb, recv_if, hdr_size); + ret = NET_RX_SUCCESS; + goto out; - return NET_RX_SUCCESS; +rcu_unlock: + rcu_read_unlock(); + goto out; +spin_unlock: + spin_unlock_bh(&orig_node->bcast_seqno_lock); +out: + if (orig_node) + orig_node_free_ref(orig_node); + return ret; } -int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if) +int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if) { struct vis_packet *vis_packet; struct ethhdr *ethhdr; diff --git a/net/batman-adv/routing.h b/net/batman-adv/routing.h index a09d16f0c3a..b5a064c88a4 100644 --- a/net/batman-adv/routing.h +++ b/net/batman-adv/routing.h @@ -22,24 +22,25 @@ #ifndef _NET_BATMAN_ADV_ROUTING_H_ #define _NET_BATMAN_ADV_ROUTING_H_ -void slide_own_bcast_window(struct batman_if *batman_if); +void slide_own_bcast_window(struct hard_iface *hard_iface); void receive_bat_packet(struct ethhdr *ethhdr, struct batman_packet *batman_packet, unsigned char *hna_buff, int hna_buff_len, - struct batman_if *if_incoming); + struct hard_iface *if_incoming); void update_routes(struct bat_priv *bat_priv, struct orig_node *orig_node, struct neigh_node *neigh_node, unsigned char *hna_buff, int hna_buff_len); -int route_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if, - int hdr_size); -int recv_icmp_packet(struct sk_buff *skb, struct batman_if *recv_if); -int recv_unicast_packet(struct sk_buff *skb, struct batman_if *recv_if); -int recv_ucast_frag_packet(struct sk_buff *skb, struct batman_if *recv_if); -int recv_bcast_packet(struct sk_buff *skb, struct batman_if *recv_if); -int recv_vis_packet(struct sk_buff *skb, struct batman_if *recv_if); -int recv_bat_packet(struct sk_buff *skb, struct batman_if *recv_if); +int route_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_icmp_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_unicast_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_ucast_frag_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_bcast_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_vis_packet(struct sk_buff *skb, struct hard_iface *recv_if); +int recv_bat_packet(struct sk_buff *skb, struct hard_iface *recv_if); struct neigh_node *find_router(struct bat_priv *bat_priv, - struct orig_node *orig_node, struct batman_if *recv_if); -void update_bonding_candidates(struct orig_node *orig_node); + struct orig_node *orig_node, + struct hard_iface *recv_if); +void bonding_candidate_del(struct orig_node *orig_node, + struct neigh_node *neigh_node); #endif /* _NET_BATMAN_ADV_ROUTING_H_ */ diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index 831427694fc..d49e54d932a 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -56,20 +56,20 @@ static unsigned long forward_send_time(void) /* send out an already prepared packet to the given address via the * specified batman interface */ int send_skb_packet(struct sk_buff *skb, - struct batman_if *batman_if, + struct hard_iface *hard_iface, uint8_t *dst_addr) { struct ethhdr *ethhdr; - if (batman_if->if_status != IF_ACTIVE) + if (hard_iface->if_status != IF_ACTIVE) goto send_skb_err; - if (unlikely(!batman_if->net_dev)) + if (unlikely(!hard_iface->net_dev)) goto send_skb_err; - if (!(batman_if->net_dev->flags & IFF_UP)) { + if (!(hard_iface->net_dev->flags & IFF_UP)) { pr_warning("Interface %s is not up - can't send packet via " - "that interface!\n", batman_if->net_dev->name); + "that interface!\n", hard_iface->net_dev->name); goto send_skb_err; } @@ -80,7 +80,7 @@ int send_skb_packet(struct sk_buff *skb, skb_reset_mac_header(skb); ethhdr = (struct ethhdr *) skb_mac_header(skb); - memcpy(ethhdr->h_source, batman_if->net_dev->dev_addr, ETH_ALEN); + memcpy(ethhdr->h_source, hard_iface->net_dev->dev_addr, ETH_ALEN); memcpy(ethhdr->h_dest, dst_addr, ETH_ALEN); ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); @@ -88,7 +88,7 @@ int send_skb_packet(struct sk_buff *skb, skb->priority = TC_PRIO_CONTROL; skb->protocol = __constant_htons(ETH_P_BATMAN); - skb->dev = batman_if->net_dev; + skb->dev = hard_iface->net_dev; /* dev_queue_xmit() returns a negative result on error. However on * congestion and traffic shaping, it drops and returns NET_XMIT_DROP @@ -102,16 +102,16 @@ send_skb_err: /* Send a packet to a given interface */ static void send_packet_to_if(struct forw_packet *forw_packet, - struct batman_if *batman_if) + struct hard_iface *hard_iface) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); char *fwd_str; uint8_t packet_num; int16_t buff_pos; struct batman_packet *batman_packet; struct sk_buff *skb; - if (batman_if->if_status != IF_ACTIVE) + if (hard_iface->if_status != IF_ACTIVE) return; packet_num = 0; @@ -126,7 +126,7 @@ static void send_packet_to_if(struct forw_packet *forw_packet, /* we might have aggregated direct link packets with an * ordinary base packet */ if ((forw_packet->direct_link_flags & (1 << packet_num)) && - (forw_packet->if_incoming == batman_if)) + (forw_packet->if_incoming == hard_iface)) batman_packet->flags |= DIRECTLINK; else batman_packet->flags &= ~DIRECTLINK; @@ -142,7 +142,8 @@ static void send_packet_to_if(struct forw_packet *forw_packet, batman_packet->tq, batman_packet->ttl, (batman_packet->flags & DIRECTLINK ? "on" : "off"), - batman_if->net_dev->name, batman_if->net_dev->dev_addr); + hard_iface->net_dev->name, + hard_iface->net_dev->dev_addr); buff_pos += sizeof(struct batman_packet) + (batman_packet->num_hna * ETH_ALEN); @@ -154,13 +155,13 @@ static void send_packet_to_if(struct forw_packet *forw_packet, /* create clone because function is called more than once */ skb = skb_clone(forw_packet->skb, GFP_ATOMIC); if (skb) - send_skb_packet(skb, batman_if, broadcast_addr); + send_skb_packet(skb, hard_iface, broadcast_addr); } /* send a batman packet */ static void send_packet(struct forw_packet *forw_packet) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; struct net_device *soft_iface; struct bat_priv *bat_priv; struct batman_packet *batman_packet = @@ -204,17 +205,17 @@ static void send_packet(struct forw_packet *forw_packet) /* broadcast on every interface */ rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->soft_iface != soft_iface) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->soft_iface != soft_iface) continue; - send_packet_to_if(forw_packet, batman_if); + send_packet_to_if(forw_packet, hard_iface); } rcu_read_unlock(); } static void rebuild_batman_packet(struct bat_priv *bat_priv, - struct batman_if *batman_if) + struct hard_iface *hard_iface) { int new_len; unsigned char *new_buff; @@ -226,7 +227,7 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, /* keep old buffer if kmalloc should fail */ if (new_buff) { - memcpy(new_buff, batman_if->packet_buff, + memcpy(new_buff, hard_iface->packet_buff, sizeof(struct batman_packet)); batman_packet = (struct batman_packet *)new_buff; @@ -234,21 +235,21 @@ static void rebuild_batman_packet(struct bat_priv *bat_priv, new_buff + sizeof(struct batman_packet), new_len - sizeof(struct batman_packet)); - kfree(batman_if->packet_buff); - batman_if->packet_buff = new_buff; - batman_if->packet_len = new_len; + kfree(hard_iface->packet_buff); + hard_iface->packet_buff = new_buff; + hard_iface->packet_len = new_len; } } -void schedule_own_packet(struct batman_if *batman_if) +void schedule_own_packet(struct hard_iface *hard_iface) { - struct bat_priv *bat_priv = netdev_priv(batman_if->soft_iface); + struct bat_priv *bat_priv = netdev_priv(hard_iface->soft_iface); unsigned long send_time; struct batman_packet *batman_packet; int vis_server; - if ((batman_if->if_status == IF_NOT_IN_USE) || - (batman_if->if_status == IF_TO_BE_REMOVED)) + if ((hard_iface->if_status == IF_NOT_IN_USE) || + (hard_iface->if_status == IF_TO_BE_REMOVED)) return; vis_server = atomic_read(&bat_priv->vis_mode); @@ -260,51 +261,51 @@ void schedule_own_packet(struct batman_if *batman_if) * outdated packets (especially uninitialized mac addresses) in the * packet queue */ - if (batman_if->if_status == IF_TO_BE_ACTIVATED) - batman_if->if_status = IF_ACTIVE; + if (hard_iface->if_status == IF_TO_BE_ACTIVATED) + hard_iface->if_status = IF_ACTIVE; /* if local hna has changed and interface is a primary interface */ if ((atomic_read(&bat_priv->hna_local_changed)) && - (batman_if == bat_priv->primary_if)) - rebuild_batman_packet(bat_priv, batman_if); + (hard_iface == bat_priv->primary_if)) + rebuild_batman_packet(bat_priv, hard_iface); /** * NOTE: packet_buff might just have been re-allocated in * rebuild_batman_packet() */ - batman_packet = (struct batman_packet *)batman_if->packet_buff; + batman_packet = (struct batman_packet *)hard_iface->packet_buff; /* change sequence number to network order */ batman_packet->seqno = - htonl((uint32_t)atomic_read(&batman_if->seqno)); + htonl((uint32_t)atomic_read(&hard_iface->seqno)); if (vis_server == VIS_TYPE_SERVER_SYNC) batman_packet->flags |= VIS_SERVER; else batman_packet->flags &= ~VIS_SERVER; - if ((batman_if == bat_priv->primary_if) && + if ((hard_iface == bat_priv->primary_if) && (atomic_read(&bat_priv->gw_mode) == GW_MODE_SERVER)) batman_packet->gw_flags = (uint8_t)atomic_read(&bat_priv->gw_bandwidth); else batman_packet->gw_flags = 0; - atomic_inc(&batman_if->seqno); + atomic_inc(&hard_iface->seqno); - slide_own_bcast_window(batman_if); + slide_own_bcast_window(hard_iface); send_time = own_send_time(bat_priv); add_bat_packet_to_list(bat_priv, - batman_if->packet_buff, - batman_if->packet_len, - batman_if, 1, send_time); + hard_iface->packet_buff, + hard_iface->packet_len, + hard_iface, 1, send_time); } void schedule_forward_packet(struct orig_node *orig_node, struct ethhdr *ethhdr, struct batman_packet *batman_packet, uint8_t directlink, int hna_buff_len, - struct batman_if *if_incoming) + struct hard_iface *if_incoming) { struct bat_priv *bat_priv = netdev_priv(if_incoming->soft_iface); unsigned char in_tq, in_ttl, tq_avg = 0; @@ -326,7 +327,7 @@ void schedule_forward_packet(struct orig_node *orig_node, if ((orig_node->router) && (orig_node->router->tq_avg != 0)) { /* rebroadcast ogm of best ranking neighbor as is */ - if (!compare_orig(orig_node->router->addr, ethhdr->h_source)) { + if (!compare_eth(orig_node->router->addr, ethhdr->h_source)) { batman_packet->tq = orig_node->router->tq_avg; if (orig_node->router->last_ttl) @@ -443,7 +444,7 @@ out: static void send_outstanding_bcast_packet(struct work_struct *work) { - struct batman_if *batman_if; + struct hard_iface *hard_iface; struct delayed_work *delayed_work = container_of(work, struct delayed_work, work); struct forw_packet *forw_packet = @@ -461,14 +462,14 @@ static void send_outstanding_bcast_packet(struct work_struct *work) /* rebroadcast packet */ rcu_read_lock(); - list_for_each_entry_rcu(batman_if, &if_list, list) { - if (batman_if->soft_iface != soft_iface) + list_for_each_entry_rcu(hard_iface, &hardif_list, list) { + if (hard_iface->soft_iface != soft_iface) continue; /* send a copy of the saved skb */ skb1 = skb_clone(forw_packet->skb, GFP_ATOMIC); if (skb1) - send_skb_packet(skb1, batman_if, broadcast_addr); + send_skb_packet(skb1, hard_iface, broadcast_addr); } rcu_read_unlock(); @@ -521,15 +522,15 @@ out: } void purge_outstanding_packets(struct bat_priv *bat_priv, - struct batman_if *batman_if) + struct hard_iface *hard_iface) { struct forw_packet *forw_packet; struct hlist_node *tmp_node, *safe_tmp_node; - if (batman_if) + if (hard_iface) bat_dbg(DBG_BATMAN, bat_priv, "purge_outstanding_packets(): %s\n", - batman_if->net_dev->name); + hard_iface->net_dev->name); else bat_dbg(DBG_BATMAN, bat_priv, "purge_outstanding_packets()\n"); @@ -543,8 +544,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, * if purge_outstanding_packets() was called with an argmument * we delete only packets belonging to the given interface */ - if ((batman_if) && - (forw_packet->if_incoming != batman_if)) + if ((hard_iface) && + (forw_packet->if_incoming != hard_iface)) continue; spin_unlock_bh(&bat_priv->forw_bcast_list_lock); @@ -567,8 +568,8 @@ void purge_outstanding_packets(struct bat_priv *bat_priv, * if purge_outstanding_packets() was called with an argmument * we delete only packets belonging to the given interface */ - if ((batman_if) && - (forw_packet->if_incoming != batman_if)) + if ((hard_iface) && + (forw_packet->if_incoming != hard_iface)) continue; spin_unlock_bh(&bat_priv->forw_bat_list_lock); diff --git a/net/batman-adv/send.h b/net/batman-adv/send.h index b68c272cb84..7b2ff19c05e 100644 --- a/net/batman-adv/send.h +++ b/net/batman-adv/send.h @@ -23,17 +23,17 @@ #define _NET_BATMAN_ADV_SEND_H_ int send_skb_packet(struct sk_buff *skb, - struct batman_if *batman_if, + struct hard_iface *hard_iface, uint8_t *dst_addr); -void schedule_own_packet(struct batman_if *batman_if); +void schedule_own_packet(struct hard_iface *hard_iface); void schedule_forward_packet(struct orig_node *orig_node, struct ethhdr *ethhdr, struct batman_packet *batman_packet, uint8_t directlink, int hna_buff_len, - struct batman_if *if_outgoing); + struct hard_iface *if_outgoing); int add_bcast_packet_to_list(struct bat_priv *bat_priv, struct sk_buff *skb); void send_outstanding_bat_packet(struct work_struct *work); void purge_outstanding_packets(struct bat_priv *bat_priv, - struct batman_if *batman_if); + struct hard_iface *hard_iface); #endif /* _NET_BATMAN_ADV_SEND_H_ */ diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index bd088f877e3..9ed26140a26 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -29,14 +29,12 @@ #include "hash.h" #include "gateway_common.h" #include "gateway_client.h" -#include "send.h" #include "bat_sysfs.h" #include <linux/slab.h> #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/if_vlan.h> #include "unicast.h" -#include "routing.h" static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd); @@ -78,20 +76,18 @@ int my_skb_head_push(struct sk_buff *skb, unsigned int len) return 0; } -static void softif_neigh_free_ref(struct kref *refcount) +static void softif_neigh_free_rcu(struct rcu_head *rcu) { struct softif_neigh *softif_neigh; - softif_neigh = container_of(refcount, struct softif_neigh, refcount); + softif_neigh = container_of(rcu, struct softif_neigh, rcu); kfree(softif_neigh); } -static void softif_neigh_free_rcu(struct rcu_head *rcu) +static void softif_neigh_free_ref(struct softif_neigh *softif_neigh) { - struct softif_neigh *softif_neigh; - - softif_neigh = container_of(rcu, struct softif_neigh, rcu); - kref_put(&softif_neigh->refcount, softif_neigh_free_ref); + if (atomic_dec_and_test(&softif_neigh->refcount)) + call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); } void softif_neigh_purge(struct bat_priv *bat_priv) @@ -118,11 +114,10 @@ void softif_neigh_purge(struct bat_priv *bat_priv) softif_neigh->addr, softif_neigh->vid); softif_neigh_tmp = bat_priv->softif_neigh; bat_priv->softif_neigh = NULL; - kref_put(&softif_neigh_tmp->refcount, - softif_neigh_free_ref); + softif_neigh_free_ref(softif_neigh_tmp); } - call_rcu(&softif_neigh->rcu, softif_neigh_free_rcu); + softif_neigh_free_ref(softif_neigh); } spin_unlock_bh(&bat_priv->softif_neigh_lock); @@ -137,14 +132,17 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, rcu_read_lock(); hlist_for_each_entry_rcu(softif_neigh, node, &bat_priv->softif_neigh_list, list) { - if (memcmp(softif_neigh->addr, addr, ETH_ALEN) != 0) + if (!compare_eth(softif_neigh->addr, addr)) continue; if (softif_neigh->vid != vid) continue; + if (!atomic_inc_not_zero(&softif_neigh->refcount)) + continue; + softif_neigh->last_seen = jiffies; - goto found; + goto out; } softif_neigh = kzalloc(sizeof(struct softif_neigh), GFP_ATOMIC); @@ -154,15 +152,14 @@ static struct softif_neigh *softif_neigh_get(struct bat_priv *bat_priv, memcpy(softif_neigh->addr, addr, ETH_ALEN); softif_neigh->vid = vid; softif_neigh->last_seen = jiffies; - kref_init(&softif_neigh->refcount); + /* initialize with 2 - caller decrements counter by one */ + atomic_set(&softif_neigh->refcount, 2); INIT_HLIST_NODE(&softif_neigh->list); spin_lock_bh(&bat_priv->softif_neigh_lock); hlist_add_head_rcu(&softif_neigh->list, &bat_priv->softif_neigh_list); spin_unlock_bh(&bat_priv->softif_neigh_lock); -found: - kref_get(&softif_neigh->refcount); out: rcu_read_unlock(); return softif_neigh; @@ -174,8 +171,6 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) struct bat_priv *bat_priv = netdev_priv(net_dev); struct softif_neigh *softif_neigh; struct hlist_node *node; - size_t buf_size, pos; - char *buff; if (!bat_priv->primary_if) { return seq_printf(seq, "BATMAN mesh %s disabled - " @@ -185,33 +180,15 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset) seq_printf(seq, "Softif neighbor list (%s)\n", net_dev->name); - buf_size = 1; - /* Estimate length for: " xx:xx:xx:xx:xx:xx\n" */ rcu_read_lock(); hlist_for_each_entry_rcu(softif_neigh, node, &bat_priv->softif_neigh_list, list) - buf_size += 30; - rcu_read_unlock(); - - buff = kmalloc(buf_size, GFP_ATOMIC); - if (!buff) - return -ENOMEM; - - buff[0] = '\0'; - pos = 0; - - rcu_read_lock(); - hlist_for_each_entry_rcu(softif_neigh, node, - &bat_priv->softif_neigh_list, list) { - pos += snprintf(buff + pos, 31, "%s %pM (vid: %d)\n", + seq_printf(seq, "%s %pM (vid: %d)\n", bat_priv->softif_neigh == softif_neigh ? "=>" : " ", softif_neigh->addr, softif_neigh->vid); - } rcu_read_unlock(); - seq_printf(seq, "%s", buff); - kfree(buff); return 0; } @@ -266,7 +243,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, softif_neigh->addr, softif_neigh->vid); softif_neigh_tmp = bat_priv->softif_neigh; bat_priv->softif_neigh = softif_neigh; - kref_put(&softif_neigh_tmp->refcount, softif_neigh_free_ref); + softif_neigh_free_ref(softif_neigh_tmp); /* we need to hold the additional reference */ goto err; } @@ -284,7 +261,7 @@ static void softif_batman_recv(struct sk_buff *skb, struct net_device *dev, } out: - kref_put(&softif_neigh->refcount, softif_neigh_free_ref); + softif_neigh_free_ref(softif_neigh); err: kfree_skb(skb); return; @@ -437,7 +414,7 @@ end: } void interface_rx(struct net_device *soft_iface, - struct sk_buff *skb, struct batman_if *recv_if, + struct sk_buff *skb, struct hard_iface *recv_if, int hdr_size) { struct bat_priv *bat_priv = netdev_priv(soft_iface); @@ -485,7 +462,7 @@ void interface_rx(struct net_device *soft_iface, memcpy(unicast_packet->dest, bat_priv->softif_neigh->addr, ETH_ALEN); - ret = route_unicast_packet(skb, recv_if, hdr_size); + ret = route_unicast_packet(skb, recv_if); if (ret == NET_RX_DROP) goto dropped; @@ -645,6 +622,19 @@ void softif_destroy(struct net_device *soft_iface) unregister_netdevice(soft_iface); } +int softif_is_valid(struct net_device *net_dev) +{ +#ifdef HAVE_NET_DEVICE_OPS + if (net_dev->netdev_ops->ndo_start_xmit == interface_tx) + return 1; +#else + if (net_dev->hard_start_xmit == interface_tx) + return 1; +#endif + + return 0; +} + /* ethtool */ static int bat_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { diff --git a/net/batman-adv/soft-interface.h b/net/batman-adv/soft-interface.h index e7b0e1a34a5..4789b6f2a0b 100644 --- a/net/batman-adv/soft-interface.h +++ b/net/batman-adv/soft-interface.h @@ -27,9 +27,10 @@ int softif_neigh_seq_print_text(struct seq_file *seq, void *offset); void softif_neigh_purge(struct bat_priv *bat_priv); int interface_tx(struct sk_buff *skb, struct net_device *soft_iface); void interface_rx(struct net_device *soft_iface, - struct sk_buff *skb, struct batman_if *recv_if, + struct sk_buff *skb, struct hard_iface *recv_if, int hdr_size); struct net_device *softif_create(char *name); void softif_destroy(struct net_device *soft_iface); +int softif_is_valid(struct net_device *net_dev); #endif /* _NET_BATMAN_ADV_SOFT_INTERFACE_H_ */ diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 7fb6726ccbd..8d15b48d169 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -30,12 +30,85 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv, struct hna_global_entry *hna_global_entry, char *message); +/* returns 1 if they are the same mac addr */ +static int compare_lhna(struct hlist_node *node, void *data2) +{ + void *data1 = container_of(node, struct hna_local_entry, hash_entry); + + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + +/* returns 1 if they are the same mac addr */ +static int compare_ghna(struct hlist_node *node, void *data2) +{ + void *data1 = container_of(node, struct hna_global_entry, hash_entry); + + return (memcmp(data1, data2, ETH_ALEN) == 0 ? 1 : 0); +} + static void hna_local_start_timer(struct bat_priv *bat_priv) { INIT_DELAYED_WORK(&bat_priv->hna_work, hna_local_purge); queue_delayed_work(bat_event_workqueue, &bat_priv->hna_work, 10 * HZ); } +static struct hna_local_entry *hna_local_hash_find(struct bat_priv *bat_priv, + void *data) +{ + struct hashtable_t *hash = bat_priv->hna_local_hash; + struct hlist_head *head; + struct hlist_node *node; + struct hna_local_entry *hna_local_entry, *hna_local_entry_tmp = NULL; + int index; + + if (!hash) + return NULL; + + index = choose_orig(data, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(hna_local_entry, node, head, hash_entry) { + if (!compare_eth(hna_local_entry, data)) + continue; + + hna_local_entry_tmp = hna_local_entry; + break; + } + rcu_read_unlock(); + + return hna_local_entry_tmp; +} + +static struct hna_global_entry *hna_global_hash_find(struct bat_priv *bat_priv, + void *data) +{ + struct hashtable_t *hash = bat_priv->hna_global_hash; + struct hlist_head *head; + struct hlist_node *node; + struct hna_global_entry *hna_global_entry; + struct hna_global_entry *hna_global_entry_tmp = NULL; + int index; + + if (!hash) + return NULL; + + index = choose_orig(data, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(hna_global_entry, node, head, hash_entry) { + if (!compare_eth(hna_global_entry, data)) + continue; + + hna_global_entry_tmp = hna_global_entry; + break; + } + rcu_read_unlock(); + + return hna_global_entry_tmp; +} + int hna_local_init(struct bat_priv *bat_priv) { if (bat_priv->hna_local_hash) @@ -60,10 +133,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) int required_bytes; spin_lock_bh(&bat_priv->hna_lhash_lock); - hna_local_entry = - ((struct hna_local_entry *)hash_find(bat_priv->hna_local_hash, - compare_orig, choose_orig, - addr)); + hna_local_entry = hna_local_hash_find(bat_priv, addr); spin_unlock_bh(&bat_priv->hna_lhash_lock); if (hna_local_entry) { @@ -99,15 +169,15 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) hna_local_entry->last_seen = jiffies; /* the batman interface mac address should never be purged */ - if (compare_orig(addr, soft_iface->dev_addr)) + if (compare_eth(addr, soft_iface->dev_addr)) hna_local_entry->never_purge = 1; else hna_local_entry->never_purge = 0; spin_lock_bh(&bat_priv->hna_lhash_lock); - hash_add(bat_priv->hna_local_hash, compare_orig, choose_orig, - hna_local_entry); + hash_add(bat_priv->hna_local_hash, compare_lhna, choose_orig, + hna_local_entry, &hna_local_entry->hash_entry); bat_priv->num_local_hna++; atomic_set(&bat_priv->hna_local_changed, 1); @@ -116,9 +186,7 @@ void hna_local_add(struct net_device *soft_iface, uint8_t *addr) /* remove address from global hash if present */ spin_lock_bh(&bat_priv->hna_ghash_lock); - hna_global_entry = ((struct hna_global_entry *) - hash_find(bat_priv->hna_global_hash, - compare_orig, choose_orig, addr)); + hna_global_entry = hna_global_hash_find(bat_priv, addr); if (hna_global_entry) _hna_global_del_orig(bat_priv, hna_global_entry, @@ -132,28 +200,27 @@ int hna_local_fill_buffer(struct bat_priv *bat_priv, { struct hashtable_t *hash = bat_priv->hna_local_hash; struct hna_local_entry *hna_local_entry; - struct element_t *bucket; - int i; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - int count = 0; + int i, count = 0; spin_lock_bh(&bat_priv->hna_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - + rcu_read_lock(); + hlist_for_each_entry_rcu(hna_local_entry, node, + head, hash_entry) { if (buff_len < (count + 1) * ETH_ALEN) break; - hna_local_entry = bucket->data; memcpy(buff + (count * ETH_ALEN), hna_local_entry->addr, ETH_ALEN); count++; } + rcu_read_unlock(); } /* if we did not get all new local hnas see you next time ;-) */ @@ -170,12 +237,11 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) struct bat_priv *bat_priv = netdev_priv(net_dev); struct hashtable_t *hash = bat_priv->hna_local_hash; struct hna_local_entry *hna_local_entry; - int i; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; size_t buf_size, pos; char *buff; + int i; if (!bat_priv->primary_if) { return seq_printf(seq, "BATMAN mesh %s disabled - " @@ -194,8 +260,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each(walk, head) + rcu_read_lock(); + __hlist_for_each_rcu(node, head) buf_size += 21; + rcu_read_unlock(); } buff = kmalloc(buf_size, GFP_ATOMIC); @@ -203,18 +271,20 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) spin_unlock_bh(&bat_priv->hna_lhash_lock); return -ENOMEM; } + buff[0] = '\0'; pos = 0; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - hna_local_entry = bucket->data; - + rcu_read_lock(); + hlist_for_each_entry_rcu(hna_local_entry, node, + head, hash_entry) { pos += snprintf(buff + pos, 22, " * %pM\n", hna_local_entry->addr); } + rcu_read_unlock(); } spin_unlock_bh(&bat_priv->hna_lhash_lock); @@ -224,9 +294,10 @@ int hna_local_seq_print_text(struct seq_file *seq, void *offset) return 0; } -static void _hna_local_del(void *data, void *arg) +static void _hna_local_del(struct hlist_node *node, void *arg) { struct bat_priv *bat_priv = (struct bat_priv *)arg; + void *data = container_of(node, struct hna_local_entry, hash_entry); kfree(data); bat_priv->num_local_hna--; @@ -240,9 +311,9 @@ static void hna_local_del(struct bat_priv *bat_priv, bat_dbg(DBG_ROUTES, bat_priv, "Deleting local hna entry (%pM): %s\n", hna_local_entry->addr, message); - hash_remove(bat_priv->hna_local_hash, compare_orig, choose_orig, + hash_remove(bat_priv->hna_local_hash, compare_lhna, choose_orig, hna_local_entry->addr); - _hna_local_del(hna_local_entry, bat_priv); + _hna_local_del(&hna_local_entry->hash_entry, bat_priv); } void hna_local_remove(struct bat_priv *bat_priv, @@ -252,9 +323,7 @@ void hna_local_remove(struct bat_priv *bat_priv, spin_lock_bh(&bat_priv->hna_lhash_lock); - hna_local_entry = (struct hna_local_entry *) - hash_find(bat_priv->hna_local_hash, compare_orig, choose_orig, - addr); + hna_local_entry = hna_local_hash_find(bat_priv, addr); if (hna_local_entry) hna_local_del(bat_priv, hna_local_entry, message); @@ -270,27 +339,29 @@ static void hna_local_purge(struct work_struct *work) container_of(delayed_work, struct bat_priv, hna_work); struct hashtable_t *hash = bat_priv->hna_local_hash; struct hna_local_entry *hna_local_entry; - int i; - struct hlist_node *walk, *safe; + struct hlist_node *node, *node_tmp; struct hlist_head *head; - struct element_t *bucket; unsigned long timeout; + int i; spin_lock_bh(&bat_priv->hna_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { - hna_local_entry = bucket->data; + hlist_for_each_entry_safe(hna_local_entry, node, node_tmp, + head, hash_entry) { + if (hna_local_entry->never_purge) + continue; timeout = hna_local_entry->last_seen; timeout += LOCAL_HNA_TIMEOUT * HZ; - if ((!hna_local_entry->never_purge) && - time_after(jiffies, timeout)) - hna_local_del(bat_priv, hna_local_entry, - "address timed out"); + if (time_before(jiffies, timeout)) + continue; + + hna_local_del(bat_priv, hna_local_entry, + "address timed out"); } } @@ -334,9 +405,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv, spin_lock_bh(&bat_priv->hna_ghash_lock); hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); - hna_global_entry = (struct hna_global_entry *) - hash_find(bat_priv->hna_global_hash, compare_orig, - choose_orig, hna_ptr); + hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr); if (!hna_global_entry) { spin_unlock_bh(&bat_priv->hna_ghash_lock); @@ -356,8 +425,9 @@ void hna_global_add_orig(struct bat_priv *bat_priv, hna_global_entry->addr, orig_node->orig); spin_lock_bh(&bat_priv->hna_ghash_lock); - hash_add(bat_priv->hna_global_hash, compare_orig, - choose_orig, hna_global_entry); + hash_add(bat_priv->hna_global_hash, compare_ghna, + choose_orig, hna_global_entry, + &hna_global_entry->hash_entry); } @@ -368,9 +438,7 @@ void hna_global_add_orig(struct bat_priv *bat_priv, spin_lock_bh(&bat_priv->hna_lhash_lock); hna_ptr = hna_buff + (hna_buff_count * ETH_ALEN); - hna_local_entry = (struct hna_local_entry *) - hash_find(bat_priv->hna_local_hash, compare_orig, - choose_orig, hna_ptr); + hna_local_entry = hna_local_hash_find(bat_priv, hna_ptr); if (hna_local_entry) hna_local_del(bat_priv, hna_local_entry, @@ -400,12 +468,11 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) struct bat_priv *bat_priv = netdev_priv(net_dev); struct hashtable_t *hash = bat_priv->hna_global_hash; struct hna_global_entry *hna_global_entry; - int i; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; size_t buf_size, pos; char *buff; + int i; if (!bat_priv->primary_if) { return seq_printf(seq, "BATMAN mesh %s disabled - " @@ -423,8 +490,10 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each(walk, head) + rcu_read_lock(); + __hlist_for_each_rcu(node, head) buf_size += 43; + rcu_read_unlock(); } buff = kmalloc(buf_size, GFP_ATOMIC); @@ -438,14 +507,15 @@ int hna_global_seq_print_text(struct seq_file *seq, void *offset) for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - hna_global_entry = bucket->data; - + rcu_read_lock(); + hlist_for_each_entry_rcu(hna_global_entry, node, + head, hash_entry) { pos += snprintf(buff + pos, 44, " * %pM via %pM\n", hna_global_entry->addr, hna_global_entry->orig_node->orig); } + rcu_read_unlock(); } spin_unlock_bh(&bat_priv->hna_ghash_lock); @@ -464,7 +534,7 @@ static void _hna_global_del_orig(struct bat_priv *bat_priv, hna_global_entry->addr, hna_global_entry->orig_node->orig, message); - hash_remove(bat_priv->hna_global_hash, compare_orig, choose_orig, + hash_remove(bat_priv->hna_global_hash, compare_ghna, choose_orig, hna_global_entry->addr); kfree(hna_global_entry); } @@ -483,9 +553,7 @@ void hna_global_del_orig(struct bat_priv *bat_priv, while ((hna_buff_count + 1) * ETH_ALEN <= orig_node->hna_buff_len) { hna_ptr = orig_node->hna_buff + (hna_buff_count * ETH_ALEN); - hna_global_entry = (struct hna_global_entry *) - hash_find(bat_priv->hna_global_hash, compare_orig, - choose_orig, hna_ptr); + hna_global_entry = hna_global_hash_find(bat_priv, hna_ptr); if ((hna_global_entry) && (hna_global_entry->orig_node == orig_node)) @@ -502,8 +570,10 @@ void hna_global_del_orig(struct bat_priv *bat_priv, orig_node->hna_buff = NULL; } -static void hna_global_del(void *data, void *arg) +static void hna_global_del(struct hlist_node *node, void *arg) { + void *data = container_of(node, struct hna_global_entry, hash_entry); + kfree(data); } @@ -519,15 +589,20 @@ void hna_global_free(struct bat_priv *bat_priv) struct orig_node *transtable_search(struct bat_priv *bat_priv, uint8_t *addr) { struct hna_global_entry *hna_global_entry; + struct orig_node *orig_node = NULL; spin_lock_bh(&bat_priv->hna_ghash_lock); - hna_global_entry = (struct hna_global_entry *) - hash_find(bat_priv->hna_global_hash, - compare_orig, choose_orig, addr); - spin_unlock_bh(&bat_priv->hna_ghash_lock); + hna_global_entry = hna_global_hash_find(bat_priv, addr); if (!hna_global_entry) - return NULL; + goto out; - return hna_global_entry->orig_node; + if (!atomic_inc_not_zero(&hna_global_entry->orig_node->refcount)) + goto out; + + orig_node = hna_global_entry->orig_node; + +out: + spin_unlock_bh(&bat_priv->hna_ghash_lock); + return orig_node; } diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h index 7270405046e..83445cf0cc9 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -33,7 +33,7 @@ sizeof(struct bcast_packet)))) -struct batman_if { +struct hard_iface { struct list_head list; int16_t if_num; char if_status; @@ -43,7 +43,7 @@ struct batman_if { unsigned char *packet_buff; int packet_len; struct kobject *hardif_obj; - struct kref refcount; + atomic_t refcount; struct packet_type batman_adv_ptype; struct net_device *soft_iface; struct rcu_head rcu; @@ -70,8 +70,6 @@ struct orig_node { struct neigh_node *router; unsigned long *bcast_own; uint8_t *bcast_own_sum; - uint8_t tq_own; - int tq_asym_penalty; unsigned long last_valid; unsigned long bcast_seqno_reset; unsigned long batman_seqno_reset; @@ -83,20 +81,28 @@ struct orig_node { uint8_t last_ttl; unsigned long bcast_bits[NUM_WORDS]; uint32_t last_bcast_seqno; - struct list_head neigh_list; + struct hlist_head neigh_list; struct list_head frag_list; + spinlock_t neigh_list_lock; /* protects neighbor list */ + atomic_t refcount; + struct rcu_head rcu; + struct hlist_node hash_entry; + struct bat_priv *bat_priv; unsigned long last_frag_packet; - struct { - uint8_t candidates; - struct neigh_node *selected; - } bond; + spinlock_t ogm_cnt_lock; /* protects: bcast_own, bcast_own_sum, + * neigh_node->real_bits, + * neigh_node->real_packet_count */ + spinlock_t bcast_seqno_lock; /* protects bcast_bits, + * last_bcast_seqno */ + atomic_t bond_candidates; + struct list_head bond_list; }; struct gw_node { struct hlist_node list; struct orig_node *orig_node; unsigned long deleted; - struct kref refcount; + atomic_t refcount; struct rcu_head rcu; }; @@ -105,18 +111,20 @@ struct gw_node { * @last_valid: when last packet via this neighbor was received */ struct neigh_node { - struct list_head list; + struct hlist_node list; uint8_t addr[ETH_ALEN]; uint8_t real_packet_count; uint8_t tq_recv[TQ_GLOBAL_WINDOW_SIZE]; uint8_t tq_index; uint8_t tq_avg; uint8_t last_ttl; - struct neigh_node *next_bond_candidate; + struct list_head bonding_list; unsigned long last_valid; unsigned long real_bits[NUM_WORDS]; + atomic_t refcount; + struct rcu_head rcu; struct orig_node *orig_node; - struct batman_if *if_incoming; + struct hard_iface *if_incoming; }; @@ -140,7 +148,7 @@ struct bat_priv { struct hlist_head softif_neigh_list; struct softif_neigh *softif_neigh; struct debug_log *debug_log; - struct batman_if *primary_if; + struct hard_iface *primary_if; struct kobject *mesh_obj; struct dentry *debug_dir; struct hlist_head forw_bat_list; @@ -151,12 +159,11 @@ struct bat_priv { struct hashtable_t *hna_local_hash; struct hashtable_t *hna_global_hash; struct hashtable_t *vis_hash; - spinlock_t orig_hash_lock; /* protects orig_hash */ spinlock_t forw_bat_list_lock; /* protects forw_bat_list */ spinlock_t forw_bcast_list_lock; /* protects */ spinlock_t hna_lhash_lock; /* protects hna_local_hash */ spinlock_t hna_ghash_lock; /* protects hna_global_hash */ - spinlock_t gw_list_lock; /* protects gw_list */ + spinlock_t gw_list_lock; /* protects gw_list and curr_gw */ spinlock_t vis_hash_lock; /* protects vis_hash */ spinlock_t vis_list_lock; /* protects vis_info::recv_list */ spinlock_t softif_neigh_lock; /* protects soft-interface neigh list */ @@ -165,7 +172,7 @@ struct bat_priv { struct delayed_work hna_work; struct delayed_work orig_work; struct delayed_work vis_work; - struct gw_node *curr_gw; + struct gw_node __rcu *curr_gw; /* rcu protected pointer */ struct vis_info *my_vis_info; }; @@ -188,11 +195,13 @@ struct hna_local_entry { uint8_t addr[ETH_ALEN]; unsigned long last_seen; char never_purge; + struct hlist_node hash_entry; }; struct hna_global_entry { uint8_t addr[ETH_ALEN]; struct orig_node *orig_node; + struct hlist_node hash_entry; }; /** @@ -208,7 +217,7 @@ struct forw_packet { uint32_t direct_link_flags; uint8_t num_packets; struct delayed_work delayed_work; - struct batman_if *if_incoming; + struct hard_iface *if_incoming; }; /* While scanning for vis-entries of a particular vis-originator @@ -242,6 +251,7 @@ struct vis_info { * from. we should not reply to them. */ struct list_head send_list; struct kref refcount; + struct hlist_node hash_entry; struct bat_priv *bat_priv; /* this packet might be part of the vis send queue. */ struct sk_buff *skb_packet; @@ -264,7 +274,7 @@ struct softif_neigh { uint8_t addr[ETH_ALEN]; unsigned long last_seen; short vid; - struct kref refcount; + atomic_t refcount; struct rcu_head rcu; }; diff --git a/net/batman-adv/unicast.c b/net/batman-adv/unicast.c index 121b11d2a23..19f84bd443a 100644 --- a/net/batman-adv/unicast.c +++ b/net/batman-adv/unicast.c @@ -183,15 +183,10 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, (struct unicast_frag_packet *)skb->data; *new_skb = NULL; - spin_lock_bh(&bat_priv->orig_hash_lock); - orig_node = ((struct orig_node *) - hash_find(bat_priv->orig_hash, compare_orig, choose_orig, - unicast_packet->orig)); - if (!orig_node) { - pr_debug("couldn't find originator in orig_hash\n"); + orig_node = orig_hash_find(bat_priv, unicast_packet->orig); + if (!orig_node) goto out; - } orig_node->last_frag_packet = jiffies; @@ -215,14 +210,15 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, /* if not, merge failed */ if (*new_skb) ret = NET_RX_SUCCESS; -out: - spin_unlock_bh(&bat_priv->orig_hash_lock); +out: + if (orig_node) + orig_node_free_ref(orig_node); return ret; } int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct batman_if *batman_if, uint8_t dstaddr[]) + struct hard_iface *hard_iface, uint8_t dstaddr[]) { struct unicast_packet tmp_uc, *unicast_packet; struct sk_buff *frag_skb; @@ -267,12 +263,12 @@ int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, frag1->flags = UNI_FRAG_HEAD | large_tail; frag2->flags = large_tail; - seqno = atomic_add_return(2, &batman_if->frag_seqno); + seqno = atomic_add_return(2, &hard_iface->frag_seqno); frag1->seqno = htons(seqno - 1); frag2->seqno = htons(seqno); - send_skb_packet(skb, batman_if, dstaddr); - send_skb_packet(frag_skb, batman_if, dstaddr); + send_skb_packet(skb, hard_iface, dstaddr); + send_skb_packet(frag_skb, hard_iface, dstaddr); return NET_RX_SUCCESS; drop_frag: @@ -286,40 +282,37 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) { struct ethhdr *ethhdr = (struct ethhdr *)skb->data; struct unicast_packet *unicast_packet; - struct orig_node *orig_node = NULL; - struct batman_if *batman_if; - struct neigh_node *router; + struct orig_node *orig_node; + struct neigh_node *neigh_node; int data_len = skb->len; - uint8_t dstaddr[6]; - - spin_lock_bh(&bat_priv->orig_hash_lock); + int ret = 1; /* get routing information */ - if (is_multicast_ether_addr(ethhdr->h_dest)) + if (is_multicast_ether_addr(ethhdr->h_dest)) { orig_node = (struct orig_node *)gw_get_selected(bat_priv); + if (orig_node) + goto find_router; + } - /* check for hna host */ - if (!orig_node) - orig_node = transtable_search(bat_priv, ethhdr->h_dest); - - router = find_router(bat_priv, orig_node, NULL); - - if (!router) - goto unlock; - - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ + /* check for hna host - increases orig_node refcount */ + orig_node = transtable_search(bat_priv, ethhdr->h_dest); - batman_if = router->if_incoming; - memcpy(dstaddr, router->addr, ETH_ALEN); +find_router: + /** + * find_router(): + * - if orig_node is NULL it returns NULL + * - increases neigh_nodes refcount if found. + */ + neigh_node = find_router(bat_priv, orig_node, NULL); - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (!neigh_node) + goto out; - if (batman_if->if_status != IF_ACTIVE) - goto dropped; + if (neigh_node->if_incoming->if_status != IF_ACTIVE) + goto out; if (my_skb_head_push(skb, sizeof(struct unicast_packet)) < 0) - goto dropped; + goto out; unicast_packet = (struct unicast_packet *)skb->data; @@ -333,18 +326,24 @@ int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv) if (atomic_read(&bat_priv->fragmentation) && data_len + sizeof(struct unicast_packet) > - batman_if->net_dev->mtu) { + neigh_node->if_incoming->net_dev->mtu) { /* send frag skb decreases ttl */ unicast_packet->ttl++; - return frag_send_skb(skb, bat_priv, batman_if, - dstaddr); + ret = frag_send_skb(skb, bat_priv, + neigh_node->if_incoming, neigh_node->addr); + goto out; } - send_skb_packet(skb, batman_if, dstaddr); - return 0; -unlock: - spin_unlock_bh(&bat_priv->orig_hash_lock); -dropped: - kfree_skb(skb); - return 1; + send_skb_packet(skb, neigh_node->if_incoming, neigh_node->addr); + ret = 0; + goto out; + +out: + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); + if (ret == 1) + kfree_skb(skb); + return ret; } diff --git a/net/batman-adv/unicast.h b/net/batman-adv/unicast.h index 8897308281d..16ad7a9242b 100644 --- a/net/batman-adv/unicast.h +++ b/net/batman-adv/unicast.h @@ -32,7 +32,7 @@ int frag_reassemble_skb(struct sk_buff *skb, struct bat_priv *bat_priv, void frag_list_free(struct list_head *head); int unicast_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv); int frag_send_skb(struct sk_buff *skb, struct bat_priv *bat_priv, - struct batman_if *batman_if, uint8_t dstaddr[]); + struct hard_iface *hard_iface, uint8_t dstaddr[]); static inline int frag_can_reassemble(struct sk_buff *skb, int mtu) { diff --git a/net/batman-adv/vis.c b/net/batman-adv/vis.c index 7db9ad82cc0..f90212f4208 100644 --- a/net/batman-adv/vis.c +++ b/net/batman-adv/vis.c @@ -68,15 +68,16 @@ static void free_info(struct kref *ref) } /* Compare two vis packets, used by the hashing algorithm */ -static int vis_info_cmp(void *data1, void *data2) +static int vis_info_cmp(struct hlist_node *node, void *data2) { struct vis_info *d1, *d2; struct vis_packet *p1, *p2; - d1 = data1; + + d1 = container_of(node, struct vis_info, hash_entry); d2 = data2; p1 = (struct vis_packet *)d1->skb_packet->data; p2 = (struct vis_packet *)d2->skb_packet->data; - return compare_orig(p1->vis_orig, p2->vis_orig); + return compare_eth(p1->vis_orig, p2->vis_orig); } /* hash function to choose an entry in a hash table of given size */ @@ -104,6 +105,34 @@ static int vis_info_choose(void *data, int size) return hash % size; } +static struct vis_info *vis_hash_find(struct bat_priv *bat_priv, + void *data) +{ + struct hashtable_t *hash = bat_priv->vis_hash; + struct hlist_head *head; + struct hlist_node *node; + struct vis_info *vis_info, *vis_info_tmp = NULL; + int index; + + if (!hash) + return NULL; + + index = vis_info_choose(data, hash->size); + head = &hash->table[index]; + + rcu_read_lock(); + hlist_for_each_entry_rcu(vis_info, node, head, hash_entry) { + if (!vis_info_cmp(node, data)) + continue; + + vis_info_tmp = vis_info; + break; + } + rcu_read_unlock(); + + return vis_info_tmp; +} + /* insert interface to the list of interfaces of one originator, if it * does not already exist in the list */ static void vis_data_insert_interface(const uint8_t *interface, @@ -114,7 +143,7 @@ static void vis_data_insert_interface(const uint8_t *interface, struct hlist_node *pos; hlist_for_each_entry(entry, pos, if_list, list) { - if (compare_orig(entry->addr, (void *)interface)) + if (compare_eth(entry->addr, (void *)interface)) return; } @@ -166,7 +195,7 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, /* maximal length: max(4+17+2, 3+17+1+3+2) == 26 */ if (primary && entry->quality == 0) return sprintf(buff, "HNA %pM, ", entry->dest); - else if (compare_orig(entry->src, src)) + else if (compare_eth(entry->src, src)) return sprintf(buff, "TQ %pM %d, ", entry->dest, entry->quality); @@ -175,9 +204,8 @@ static ssize_t vis_data_read_entry(char *buff, struct vis_info_entry *entry, int vis_seq_print_text(struct seq_file *seq, void *offset) { - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct vis_info *info; struct vis_packet *packet; struct vis_info_entry *entries; @@ -203,8 +231,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - info = bucket->data; + rcu_read_lock(); + hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) ((char *)packet + sizeof(struct vis_packet)); @@ -213,7 +241,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) if (entries[j].quality == 0) continue; compare = - compare_orig(entries[j].src, packet->vis_orig); + compare_eth(entries[j].src, packet->vis_orig); vis_data_insert_interface(entries[j].src, &vis_if_list, compare); @@ -223,7 +251,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) buf_size += 18 + 26 * packet->entries; /* add primary/secondary records */ - if (compare_orig(entry->addr, packet->vis_orig)) + if (compare_eth(entry->addr, packet->vis_orig)) buf_size += vis_data_count_prim_sec(&vis_if_list); @@ -236,6 +264,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) kfree(entry); } } + rcu_read_unlock(); } buff = kmalloc(buf_size, GFP_ATOMIC); @@ -249,8 +278,8 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - info = bucket->data; + rcu_read_lock(); + hlist_for_each_entry_rcu(info, node, head, hash_entry) { packet = (struct vis_packet *)info->skb_packet->data; entries = (struct vis_info_entry *) ((char *)packet + sizeof(struct vis_packet)); @@ -259,7 +288,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) if (entries[j].quality == 0) continue; compare = - compare_orig(entries[j].src, packet->vis_orig); + compare_eth(entries[j].src, packet->vis_orig); vis_data_insert_interface(entries[j].src, &vis_if_list, compare); @@ -277,7 +306,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) entry->primary); /* add primary/secondary records */ - if (compare_orig(entry->addr, packet->vis_orig)) + if (compare_eth(entry->addr, packet->vis_orig)) buff_pos += vis_data_read_prim_sec(buff + buff_pos, &vis_if_list); @@ -291,6 +320,7 @@ int vis_seq_print_text(struct seq_file *seq, void *offset) kfree(entry); } } + rcu_read_unlock(); } spin_unlock_bh(&bat_priv->vis_hash_lock); @@ -345,7 +375,7 @@ static int recv_list_is_in(struct bat_priv *bat_priv, spin_lock_bh(&bat_priv->vis_list_lock); list_for_each_entry(entry, recv_list, list) { - if (memcmp(entry->mac, mac, ETH_ALEN) == 0) { + if (compare_eth(entry->mac, mac)) { spin_unlock_bh(&bat_priv->vis_list_lock); return 1; } @@ -381,8 +411,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, sizeof(struct vis_packet)); memcpy(search_packet->vis_orig, vis_packet->vis_orig, ETH_ALEN); - old_info = hash_find(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, - &search_elem); + old_info = vis_hash_find(bat_priv, &search_elem); kfree_skb(search_elem.skb_packet); if (old_info) { @@ -442,7 +471,7 @@ static struct vis_info *add_packet(struct bat_priv *bat_priv, /* try to add it */ hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, - info); + info, &info->hash_entry); if (hash_added < 0) { /* did not work (for some reason) */ kref_put(&info->refcount, free_info); @@ -529,9 +558,8 @@ static int find_best_vis_server(struct bat_priv *bat_priv, struct vis_info *info) { struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; struct vis_packet *packet; int best_tq = -1, i; @@ -541,16 +569,17 @@ static int find_best_vis_server(struct bat_priv *bat_priv, for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { if ((orig_node) && (orig_node->router) && - (orig_node->flags & VIS_SERVER) && - (orig_node->router->tq_avg > best_tq)) { + (orig_node->flags & VIS_SERVER) && + (orig_node->router->tq_avg > best_tq)) { best_tq = orig_node->router->tq_avg; memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); } } + rcu_read_unlock(); } return best_tq; @@ -573,9 +602,8 @@ static bool vis_packet_full(struct vis_info *info) static int generate_vis_packet(struct bat_priv *bat_priv) { struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; struct neigh_node *neigh_node; struct vis_info *info = (struct vis_info *)bat_priv->my_vis_info; @@ -587,7 +615,6 @@ static int generate_vis_packet(struct bat_priv *bat_priv) info->first_seen = jiffies; packet->vis_type = atomic_read(&bat_priv->vis_mode); - spin_lock_bh(&bat_priv->orig_hash_lock); memcpy(packet->target_orig, broadcast_addr, ETH_ALEN); packet->ttl = TTL; packet->seqno = htonl(ntohl(packet->seqno) + 1); @@ -597,23 +624,21 @@ static int generate_vis_packet(struct bat_priv *bat_priv) if (packet->vis_type == VIS_TYPE_CLIENT_UPDATE) { best_tq = find_best_vis_server(bat_priv, info); - if (best_tq < 0) { - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (best_tq < 0) return -1; - } } for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { neigh_node = orig_node->router; if (!neigh_node) continue; - if (!compare_orig(neigh_node->addr, orig_node->orig)) + if (!compare_eth(neigh_node->addr, orig_node->orig)) continue; if (neigh_node->if_incoming->if_status != IF_ACTIVE) @@ -632,23 +657,19 @@ static int generate_vis_packet(struct bat_priv *bat_priv) entry->quality = neigh_node->tq_avg; packet->entries++; - if (vis_packet_full(info)) { - spin_unlock_bh(&bat_priv->orig_hash_lock); - return 0; - } + if (vis_packet_full(info)) + goto unlock; } + rcu_read_unlock(); } - spin_unlock_bh(&bat_priv->orig_hash_lock); - hash = bat_priv->hna_local_hash; spin_lock_bh(&bat_priv->hna_lhash_lock); for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - hna_local_entry = bucket->data; + hlist_for_each_entry(hna_local_entry, node, head, hash_entry) { entry = (struct vis_info_entry *) skb_put(info->skb_packet, sizeof(*entry)); @@ -666,6 +687,10 @@ static int generate_vis_packet(struct bat_priv *bat_priv) spin_unlock_bh(&bat_priv->hna_lhash_lock); return 0; + +unlock: + rcu_read_unlock(); + return 0; } /* free old vis packets. Must be called with this vis_hash_lock @@ -674,25 +699,22 @@ static void purge_vis_packets(struct bat_priv *bat_priv) { int i; struct hashtable_t *hash = bat_priv->vis_hash; - struct hlist_node *walk, *safe; + struct hlist_node *node, *node_tmp; struct hlist_head *head; - struct element_t *bucket; struct vis_info *info; for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry_safe(bucket, walk, safe, head, hlist) { - info = bucket->data; - + hlist_for_each_entry_safe(info, node, node_tmp, + head, hash_entry) { /* never purge own data. */ if (info == bat_priv->my_vis_info) continue; if (time_after(jiffies, info->first_seen + VIS_TIMEOUT * HZ)) { - hlist_del(walk); - kfree(bucket); + hlist_del(node); send_list_del(info); kref_put(&info->refcount, free_info); } @@ -704,27 +726,24 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv, struct vis_info *info) { struct hashtable_t *hash = bat_priv->orig_hash; - struct hlist_node *walk; + struct hlist_node *node; struct hlist_head *head; - struct element_t *bucket; struct orig_node *orig_node; struct vis_packet *packet; struct sk_buff *skb; - struct batman_if *batman_if; + struct hard_iface *hard_iface; uint8_t dstaddr[ETH_ALEN]; int i; - spin_lock_bh(&bat_priv->orig_hash_lock); packet = (struct vis_packet *)info->skb_packet->data; /* send to all routers in range. */ for (i = 0; i < hash->size; i++) { head = &hash->table[i]; - hlist_for_each_entry(bucket, walk, head, hlist) { - orig_node = bucket->data; - + rcu_read_lock(); + hlist_for_each_entry_rcu(orig_node, node, head, hash_entry) { /* if it's a vis server and reachable, send it. */ if ((!orig_node) || (!orig_node->router)) continue; @@ -737,54 +756,61 @@ static void broadcast_vis_packet(struct bat_priv *bat_priv, continue; memcpy(packet->target_orig, orig_node->orig, ETH_ALEN); - batman_if = orig_node->router->if_incoming; + hard_iface = orig_node->router->if_incoming; memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); skb = skb_clone(info->skb_packet, GFP_ATOMIC); if (skb) - send_skb_packet(skb, batman_if, dstaddr); + send_skb_packet(skb, hard_iface, dstaddr); - spin_lock_bh(&bat_priv->orig_hash_lock); } - + rcu_read_unlock(); } - - spin_unlock_bh(&bat_priv->orig_hash_lock); } static void unicast_vis_packet(struct bat_priv *bat_priv, struct vis_info *info) { struct orig_node *orig_node; + struct neigh_node *neigh_node = NULL; struct sk_buff *skb; struct vis_packet *packet; - struct batman_if *batman_if; - uint8_t dstaddr[ETH_ALEN]; - spin_lock_bh(&bat_priv->orig_hash_lock); packet = (struct vis_packet *)info->skb_packet->data; - orig_node = ((struct orig_node *)hash_find(bat_priv->orig_hash, - compare_orig, choose_orig, - packet->target_orig)); - if ((!orig_node) || (!orig_node->router)) - goto out; + rcu_read_lock(); + orig_node = orig_hash_find(bat_priv, packet->target_orig); - /* don't lock while sending the packets ... we therefore - * copy the required data before sending */ - batman_if = orig_node->router->if_incoming; - memcpy(dstaddr, orig_node->router->addr, ETH_ALEN); - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (!orig_node) + goto unlock; + + neigh_node = orig_node->router; + + if (!neigh_node) + goto unlock; + + if (!atomic_inc_not_zero(&neigh_node->refcount)) { + neigh_node = NULL; + goto unlock; + } + + rcu_read_unlock(); skb = skb_clone(info->skb_packet, GFP_ATOMIC); if (skb) - send_skb_packet(skb, batman_if, dstaddr); + send_skb_packet(skb, neigh_node->if_incoming, + neigh_node->addr); - return; + goto out; +unlock: + rcu_read_unlock(); out: - spin_unlock_bh(&bat_priv->orig_hash_lock); + if (neigh_node) + neigh_node_free_ref(neigh_node); + if (orig_node) + orig_node_free_ref(orig_node); + return; } /* only send one vis packet. called from send_vis_packets() */ @@ -896,7 +922,8 @@ int vis_init(struct bat_priv *bat_priv) INIT_LIST_HEAD(&bat_priv->vis_send_list); hash_added = hash_add(bat_priv->vis_hash, vis_info_cmp, vis_info_choose, - bat_priv->my_vis_info); + bat_priv->my_vis_info, + &bat_priv->my_vis_info->hash_entry); if (hash_added < 0) { pr_err("Can't add own vis packet into hash\n"); /* not in hash, need to remove it manually. */ @@ -918,10 +945,11 @@ err: } /* Decrease the reference count on a hash item info */ -static void free_info_ref(void *data, void *arg) +static void free_info_ref(struct hlist_node *node, void *arg) { - struct vis_info *info = data; + struct vis_info *info; + info = container_of(node, struct vis_info, hash_entry); send_list_del(info); kref_put(&info->refcount, free_info); } diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index c6f9c2fb489..6ae5ec50858 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -31,9 +31,10 @@ menuconfig BT to Bluetooth kernel modules are provided in the BlueZ packages. For more information, see <http://www.bluez.org/>. +if BT != n + config BT_L2CAP bool "L2CAP protocol support" - depends on BT select CRC16 help L2CAP (Logical Link Control and Adaptation Protocol) provides @@ -42,11 +43,12 @@ config BT_L2CAP config BT_SCO bool "SCO links support" - depends on BT help SCO link provides voice transport over Bluetooth. SCO support is required for voice applications like Headset and Audio. +endif + source "net/bluetooth/rfcomm/Kconfig" source "net/bluetooth/bnep/Kconfig" diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 09d5c098792..030a002ff8e 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -37,10 +37,9 @@ rcu_dereference_protected(X, lockdep_is_held(&br->multicast_lock)) #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) -static inline int ipv6_is_local_multicast(const struct in6_addr *addr) +static inline int ipv6_is_transient_multicast(const struct in6_addr *addr) { - if (ipv6_addr_is_multicast(addr) && - IPV6_ADDR_MC_SCOPE(addr) <= IPV6_ADDR_SCOPE_LINKLOCAL) + if (ipv6_addr_is_multicast(addr) && IPV6_ADDR_MC_FLAG_TRANSIENT(addr)) return 1; return 0; } @@ -435,7 +434,6 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, eth = eth_hdr(skb); memcpy(eth->h_source, br->dev->dev_addr, 6); - ipv6_eth_mc_map(group, eth->h_dest); eth->h_proto = htons(ETH_P_IPV6); skb_put(skb, sizeof(*eth)); @@ -447,8 +445,10 @@ static struct sk_buff *br_ip6_multicast_alloc_query(struct net_bridge *br, ip6h->payload_len = htons(8 + sizeof(*mldq)); ip6h->nexthdr = IPPROTO_HOPOPTS; ip6h->hop_limit = 1; - ipv6_addr_set(&ip6h->saddr, 0, 0, 0, 0); + ipv6_dev_get_saddr(dev_net(br->dev), br->dev, &ip6h->daddr, 0, + &ip6h->saddr); ipv6_addr_set(&ip6h->daddr, htonl(0xff020000), 0, 0, htonl(1)); + ipv6_eth_mc_map(&ip6h->daddr, eth->h_dest); hopopt = (u8 *)(ip6h + 1); hopopt[0] = IPPROTO_ICMPV6; /* next hdr */ @@ -780,11 +780,11 @@ static int br_ip6_multicast_add_group(struct net_bridge *br, { struct br_ip br_group; - if (ipv6_is_local_multicast(group)) + if (!ipv6_is_transient_multicast(group)) return 0; ipv6_addr_copy(&br_group.u.ip6, group); - br_group.proto = htons(ETH_P_IP); + br_group.proto = htons(ETH_P_IPV6); return br_multicast_add_group(br, port, &br_group); } @@ -1013,18 +1013,19 @@ static int br_ip6_multicast_mld2_report(struct net_bridge *br, nsrcs = skb_header_pointer(skb, len + offsetof(struct mld2_grec, - grec_mca), + grec_nsrcs), sizeof(_nsrcs), &_nsrcs); if (!nsrcs) return -EINVAL; if (!pskb_may_pull(skb, len + sizeof(*grec) + - sizeof(struct in6_addr) * (*nsrcs))) + sizeof(struct in6_addr) * ntohs(*nsrcs))) return -EINVAL; grec = (struct mld2_grec *)(skb->data + len); - len += sizeof(*grec) + sizeof(struct in6_addr) * (*nsrcs); + len += sizeof(*grec) + + sizeof(struct in6_addr) * ntohs(*nsrcs); /* We treat these as MLDv1 reports for now. */ switch (grec->grec_type) { @@ -1340,7 +1341,7 @@ static void br_ip6_multicast_leave_group(struct net_bridge *br, { struct br_ip br_group; - if (ipv6_is_local_multicast(group)) + if (!ipv6_is_transient_multicast(group)) return; ipv6_addr_copy(&br_group.u.ip6, group); diff --git a/net/bridge/br_netfilter.c b/net/bridge/br_netfilter.c index 4b5b66d07bb..45b57b173f7 100644 --- a/net/bridge/br_netfilter.c +++ b/net/bridge/br_netfilter.c @@ -428,14 +428,15 @@ static int br_nf_pre_routing_finish(struct sk_buff *skb) if (err != -EHOSTUNREACH || !in_dev || IN_DEV_FORWARD(in_dev)) goto free_skb; - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (!IS_ERR(rt)) { /* - Bridged-and-DNAT'ed traffic doesn't * require ip_forwarding. */ - if (((struct dst_entry *)rt)->dev == dev) { - skb_dst_set(skb, (struct dst_entry *)rt); + if (rt->dst.dev == dev) { + skb_dst_set(skb, &rt->dst); goto bridged_dnat; } - dst_release((struct dst_entry *)rt); + ip_rt_put(rt); } free_skb: kfree_skb(skb); diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 5f1825df9dc..893669caa8d 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1107,6 +1107,8 @@ static int do_replace(struct net *net, const void __user *user, if (tmp.num_counters >= INT_MAX / sizeof(struct ebt_counter)) return -ENOMEM; + tmp.name[sizeof(tmp.name) - 1] = 0; + countersize = COUNTER_OFFSET(tmp.nentries) * nr_cpu_ids; newinfo = vmalloc(sizeof(*newinfo) + countersize); if (!newinfo) diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index dff633d62e5..35b36b86d76 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -252,8 +252,12 @@ static int ceph_tcp_recvmsg(struct socket *sock, void *buf, size_t len) { struct kvec iov = {buf, len}; struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; + int r; - return kernel_recvmsg(sock, &msg, &iov, 1, len, msg.msg_flags); + r = kernel_recvmsg(sock, &msg, &iov, 1, len, msg.msg_flags); + if (r == -EAGAIN) + r = 0; + return r; } /* @@ -264,13 +268,17 @@ static int ceph_tcp_sendmsg(struct socket *sock, struct kvec *iov, size_t kvlen, size_t len, int more) { struct msghdr msg = { .msg_flags = MSG_DONTWAIT | MSG_NOSIGNAL }; + int r; if (more) msg.msg_flags |= MSG_MORE; else msg.msg_flags |= MSG_EOR; /* superfluous, but what the hell */ - return kernel_sendmsg(sock, &msg, iov, kvlen, len); + r = kernel_sendmsg(sock, &msg, iov, kvlen, len); + if (r == -EAGAIN) + r = 0; + return r; } @@ -847,6 +855,8 @@ static int write_partial_msg_pages(struct ceph_connection *con) (msg->pages || msg->pagelist || msg->bio || in_trail)) kunmap(page); + if (ret == -EAGAIN) + ret = 0; if (ret <= 0) goto out; @@ -1737,16 +1747,12 @@ more_kvec: if (con->out_skip) { ret = write_partial_skip(con); if (ret <= 0) - goto done; - if (ret < 0) { - dout("try_write write_partial_skip err %d\n", ret); - goto done; - } + goto out; } if (con->out_kvec_left) { ret = write_partial_kvec(con); if (ret <= 0) - goto done; + goto out; } /* msg pages? */ @@ -1761,11 +1767,11 @@ more_kvec: if (ret == 1) goto more_kvec; /* we need to send the footer, too! */ if (ret == 0) - goto done; + goto out; if (ret < 0) { dout("try_write write_partial_msg_pages err %d\n", ret); - goto done; + goto out; } } @@ -1789,10 +1795,9 @@ do_next: /* Nothing to do! */ clear_bit(WRITE_PENDING, &con->state); dout("try_write nothing else to write.\n"); -done: ret = 0; out: - dout("try_write done on %p\n", con); + dout("try_write done on %p ret %d\n", con, ret); return ret; } @@ -1821,19 +1826,17 @@ more: dout("try_read connecting\n"); ret = read_partial_banner(con); if (ret <= 0) - goto done; - if (process_banner(con) < 0) { - ret = -1; goto out; - } + ret = process_banner(con); + if (ret < 0) + goto out; } ret = read_partial_connect(con); if (ret <= 0) - goto done; - if (process_connect(con) < 0) { - ret = -1; goto out; - } + ret = process_connect(con); + if (ret < 0) + goto out; goto more; } @@ -1848,7 +1851,7 @@ more: dout("skipping %d / %d bytes\n", skip, -con->in_base_pos); ret = ceph_tcp_recvmsg(con->sock, buf, skip); if (ret <= 0) - goto done; + goto out; con->in_base_pos += ret; if (con->in_base_pos) goto more; @@ -1859,7 +1862,7 @@ more: */ ret = ceph_tcp_recvmsg(con->sock, &con->in_tag, 1); if (ret <= 0) - goto done; + goto out; dout("try_read got tag %d\n", (int)con->in_tag); switch (con->in_tag) { case CEPH_MSGR_TAG_MSG: @@ -1870,7 +1873,7 @@ more: break; case CEPH_MSGR_TAG_CLOSE: set_bit(CLOSED, &con->state); /* fixme */ - goto done; + goto out; default: goto bad_tag; } @@ -1882,13 +1885,12 @@ more: case -EBADMSG: con->error_msg = "bad crc"; ret = -EIO; - goto out; + break; case -EIO: con->error_msg = "io error"; - goto out; - default: - goto done; + break; } + goto out; } if (con->in_tag == CEPH_MSGR_TAG_READY) goto more; @@ -1898,15 +1900,13 @@ more: if (con->in_tag == CEPH_MSGR_TAG_ACK) { ret = read_partial_ack(con); if (ret <= 0) - goto done; + goto out; process_ack(con); goto more; } -done: - ret = 0; out: - dout("try_read done on %p\n", con); + dout("try_read done on %p ret %d\n", con, ret); return ret; bad_tag: diff --git a/net/core/dev.c b/net/core/dev.c index 30440e7b296..9f66de9c057 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3208,7 +3208,8 @@ ncls: list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & PTYPE_HASH_MASK], list) { if (ptype->type == type && - (ptype->dev == null_or_dev || ptype->dev == skb->dev)) { + (ptype->dev == null_or_dev || ptype->dev == skb->dev || + ptype->dev == orig_dev)) { if (pt_prev) ret = deliver_skb(skb, pt_prev, orig_dev); pt_prev = ptype; diff --git a/net/core/dev_addr_lists.c b/net/core/dev_addr_lists.c index 508f9c18992..133fd22ea28 100644 --- a/net/core/dev_addr_lists.c +++ b/net/core/dev_addr_lists.c @@ -144,7 +144,7 @@ void __hw_addr_del_multiple(struct netdev_hw_addr_list *to_list, list_for_each_entry(ha, &from_list->list, list) { type = addr_type ? addr_type : ha->type; - __hw_addr_del(to_list, ha->addr, addr_len, addr_type); + __hw_addr_del(to_list, ha->addr, addr_len, type); } } EXPORT_SYMBOL(__hw_addr_del_multiple); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 14cf560b4a3..1eb526a848f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2434,8 +2434,6 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, return -ENOMEM; /* initialize the next frag */ - sk->sk_sndmsg_page = page; - sk->sk_sndmsg_off = 0; skb_fill_page_desc(skb, frg_cnt, page, 0, 0); skb->truesize += PAGE_SIZE; atomic_add(PAGE_SIZE, &sk->sk_wmem_alloc); @@ -2455,7 +2453,6 @@ int skb_append_datato_frags(struct sock *sk, struct sk_buff *skb, return -EFAULT; /* copy was successful so update the size parameters */ - sk->sk_sndmsg_off += copy; frag->size += copy; skb->len += copy; skb->data_len += copy; diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index d5074a56728..118392f3872 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1193,7 +1193,7 @@ static int dcbnl_ieee_set(struct net_device *netdev, struct nlattr **tb, goto err; } - if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setets) { + if (ieee[DCB_ATTR_IEEE_PFC] && ops->ieee_setpfc) { struct ieee_pfc *pfc = nla_data(ieee[DCB_ATTR_IEEE_PFC]); err = ops->ieee_setpfc(netdev, pfc); if (err) @@ -1224,6 +1224,59 @@ err: return err; } +static int dcbnl_build_peer_app(struct net_device *netdev, struct sk_buff* skb, + int app_nested_type, int app_info_type, + int app_entry_type) +{ + struct dcb_peer_app_info info; + struct dcb_app *table = NULL; + const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + u16 app_count; + int err; + + + /** + * retrieve the peer app configuration form the driver. If the driver + * handlers fail exit without doing anything + */ + err = ops->peer_getappinfo(netdev, &info, &app_count); + if (!err && app_count) { + table = kmalloc(sizeof(struct dcb_app) * app_count, GFP_KERNEL); + if (!table) + return -ENOMEM; + + err = ops->peer_getapptable(netdev, table); + } + + if (!err) { + u16 i; + struct nlattr *app; + + /** + * build the message, from here on the only possible failure + * is due to the skb size + */ + err = -EMSGSIZE; + + app = nla_nest_start(skb, app_nested_type); + if (!app) + goto nla_put_failure; + + if (app_info_type) + NLA_PUT(skb, app_info_type, sizeof(info), &info); + + for (i = 0; i < app_count; i++) + NLA_PUT(skb, app_entry_type, sizeof(struct dcb_app), + &table[i]); + + nla_nest_end(skb, app); + } + err = 0; + +nla_put_failure: + kfree(table); + return err; +} /* Handle IEEE 802.1Qaz GET commands. */ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, @@ -1288,6 +1341,30 @@ static int dcbnl_ieee_get(struct net_device *netdev, struct nlattr **tb, spin_unlock(&dcb_lock); nla_nest_end(skb, app); + /* get peer info if available */ + if (ops->ieee_peer_getets) { + struct ieee_ets ets; + err = ops->ieee_peer_getets(netdev, &ets); + if (!err) + NLA_PUT(skb, DCB_ATTR_IEEE_PEER_ETS, sizeof(ets), &ets); + } + + if (ops->ieee_peer_getpfc) { + struct ieee_pfc pfc; + err = ops->ieee_peer_getpfc(netdev, &pfc); + if (!err) + NLA_PUT(skb, DCB_ATTR_IEEE_PEER_PFC, sizeof(pfc), &pfc); + } + + if (ops->peer_getappinfo && ops->peer_getapptable) { + err = dcbnl_build_peer_app(netdev, skb, + DCB_ATTR_IEEE_PEER_APP, + DCB_ATTR_IEEE_APP_UNSPEC, + DCB_ATTR_IEEE_APP); + if (err) + goto nla_put_failure; + } + nla_nest_end(skb, ieee); nlmsg_end(skb, nlh); @@ -1441,6 +1518,71 @@ err: return ret; } +/* Handle CEE DCBX GET commands. */ +static int dcbnl_cee_get(struct net_device *netdev, struct nlattr **tb, + u32 pid, u32 seq, u16 flags) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + struct dcbmsg *dcb; + struct nlattr *cee; + const struct dcbnl_rtnl_ops *ops = netdev->dcbnl_ops; + int err; + + if (!ops) + return -EOPNOTSUPP; + + skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + nlh = NLMSG_NEW(skb, pid, seq, RTM_GETDCB, sizeof(*dcb), flags); + + dcb = NLMSG_DATA(nlh); + dcb->dcb_family = AF_UNSPEC; + dcb->cmd = DCB_CMD_CEE_GET; + + NLA_PUT_STRING(skb, DCB_ATTR_IFNAME, netdev->name); + + cee = nla_nest_start(skb, DCB_ATTR_CEE); + if (!cee) + goto nla_put_failure; + + /* get peer info if available */ + if (ops->cee_peer_getpg) { + struct cee_pg pg; + err = ops->cee_peer_getpg(netdev, &pg); + if (!err) + NLA_PUT(skb, DCB_ATTR_CEE_PEER_PG, sizeof(pg), &pg); + } + + if (ops->cee_peer_getpfc) { + struct cee_pfc pfc; + err = ops->cee_peer_getpfc(netdev, &pfc); + if (!err) + NLA_PUT(skb, DCB_ATTR_CEE_PEER_PFC, sizeof(pfc), &pfc); + } + + if (ops->peer_getappinfo && ops->peer_getapptable) { + err = dcbnl_build_peer_app(netdev, skb, + DCB_ATTR_CEE_PEER_APP_TABLE, + DCB_ATTR_CEE_PEER_APP_INFO, + DCB_ATTR_CEE_PEER_APP); + if (err) + goto nla_put_failure; + } + + nla_nest_end(skb, cee); + nlmsg_end(skb, nlh); + + return rtnl_unicast(skb, &init_net, pid); +nla_put_failure: + nlmsg_cancel(skb, nlh); +nlmsg_failure: + kfree_skb(skb); + return -1; +} + static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); @@ -1570,6 +1712,10 @@ static int dcb_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ret = dcbnl_setfeatcfg(netdev, tb, pid, nlh->nlmsg_seq, nlh->nlmsg_flags); goto out; + case DCB_CMD_CEE_GET: + ret = dcbnl_cee_get(netdev, tb, pid, nlh->nlmsg_seq, + nlh->nlmsg_flags); + goto out; default: goto errout; } diff --git a/net/dccp/input.c b/net/dccp/input.c index 8cde009e8b8..4222e7a654b 100644 --- a/net/dccp/input.c +++ b/net/dccp/input.c @@ -614,6 +614,9 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, /* Caller (dccp_v4_do_rcv) will send Reset */ dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; return 1; + } else if (sk->sk_state == DCCP_CLOSED) { + dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; + return 1; } if (sk->sk_state != DCCP_REQUESTING && sk->sk_state != DCCP_RESPOND) { @@ -668,10 +671,6 @@ int dccp_rcv_state_process(struct sock *sk, struct sk_buff *skb, } switch (sk->sk_state) { - case DCCP_CLOSED: - dcb->dccpd_reset_code = DCCP_RESET_CODE_NO_CONNECTION; - return 1; - case DCCP_REQUESTING: queued = dccp_rcv_request_sent_state_process(sk, skb, dh, len); if (queued >= 0) diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 937989199c8..7882377bc62 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -46,7 +46,6 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; - int tmp; int err; dp->dccps_role = DCCP_ROLE_CLIENT; @@ -66,12 +65,12 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, - RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, - IPPROTO_DCCP, - orig_sport, orig_dport, sk, 1); - if (tmp < 0) - return tmp; + rt = ip_route_connect(nexthop, inet->inet_saddr, + RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, + IPPROTO_DCCP, + orig_sport, orig_dport, sk, true); + if (IS_ERR(rt)) + return PTR_ERR(rt); if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { ip_rt_put(rt); @@ -102,12 +101,13 @@ int dccp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err != 0) goto failure; - err = ip_route_newports(&rt, IPPROTO_DCCP, - orig_sport, orig_dport, - inet->inet_sport, inet->inet_dport, sk); - if (err != 0) + rt = ip_route_newports(rt, IPPROTO_DCCP, + orig_sport, orig_dport, + inet->inet_sport, inet->inet_dport, sk); + if (IS_ERR(rt)) { + rt = NULL; goto failure; - + } /* OK, now commit destination to socket. */ sk_setup_caps(sk, &rt->dst); @@ -475,7 +475,8 @@ static struct dst_entry* dccp_v4_route_skb(struct net *net, struct sock *sk, }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, 0)) { + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) { IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); return NULL; } diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 460d545a650..5efc57f5e60 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -162,15 +162,9 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl_ip_sport = inet->inet_sport; security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) { - sk->sk_err_soft = -err; - goto out; - } - - err = xfrm_lookup(net, &dst, &fl, sk, 0); - if (err < 0) { - sk->sk_err_soft = -err; + dst = ip6_dst_lookup_flow(sk, &fl, NULL, false); + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); goto out; } } else @@ -267,16 +261,12 @@ static int dccp_v6_send_response(struct sock *sk, struct request_sock *req, final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) - goto done; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0); - if (err < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; goto done; + } skb = dccp_make_response(sk, dst, req); if (skb != NULL) { @@ -338,14 +328,13 @@ static void dccp_v6_ctl_send_reset(struct sock *sk, struct sk_buff *rxskb) security_skb_classify_flow(rxskb, &fl); /* sk = NULL, but it is safe for now. RST socket required. */ - if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) { - if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) { - skb_dst_set(skb, dst); - ip6_xmit(ctl_sk, skb, &fl, NULL); - DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); - DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); - return; - } + dst = ip6_dst_lookup_flow(ctl_sk, &fl, NULL, false); + if (!IS_ERR(dst)) { + skb_dst_set(skb, dst); + ip6_xmit(ctl_sk, skb, &fl, NULL); + DCCP_INC_STATS_BH(DCCP_MIB_OUTSEGS); + DCCP_INC_STATS_BH(DCCP_MIB_OUTRSTS); + return; } kfree_skb(skb); @@ -550,13 +539,8 @@ static struct sock *dccp_v6_request_recv_sock(struct sock *sk, fl.fl_ip_sport = inet_rsk(req)->loc_port; security_sk_classify_flow(sk, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - goto out; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) goto out; } @@ -979,19 +963,10 @@ static int dccp_v6_connect(struct sock *sk, struct sockaddr *uaddr, final_p = fl6_update_dst(&fl, np->opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto failure; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto failure; } if (saddr == NULL) { diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index 06c054d5ccb..484fdbf92bd 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1222,7 +1222,11 @@ static int dn_route_output_key(struct dst_entry **pprt, struct flowi *flp, int f err = __dn_route_output_key(pprt, flp, flags); if (err == 0 && flp->proto) { - err = xfrm_lookup(&init_net, pprt, flp, NULL, 0); + *pprt = xfrm_lookup(&init_net, *pprt, flp, NULL, 0); + if (IS_ERR(*pprt)) { + err = PTR_ERR(*pprt); + *pprt = NULL; + } } return err; } @@ -1233,8 +1237,13 @@ int dn_route_output_sock(struct dst_entry **pprt, struct flowi *fl, struct sock err = __dn_route_output_key(pprt, fl, flags & MSG_TRYHARD); if (err == 0 && fl->proto) { - err = xfrm_lookup(&init_net, pprt, fl, sk, - (flags & MSG_DONTWAIT) ? 0 : XFRM_LOOKUP_WAIT); + if (!(flags & MSG_DONTWAIT)) + fl->flags |= FLOWI_FLAG_CAN_SLEEP; + *pprt = xfrm_lookup(&init_net, *pprt, fl, sk, 0); + if (IS_ERR(*pprt)) { + err = PTR_ERR(*pprt); + *pprt = NULL; + } } return err; } diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 739435a6af3..cfa7a5e1c5c 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -67,8 +67,9 @@ dns_resolver_instantiate(struct key *key, const void *_data, size_t datalen) size_t result_len = 0; const char *data = _data, *end, *opt; - kenter("%%%d,%s,'%s',%zu", - key->serial, key->description, data, datalen); + kenter("%%%d,%s,'%*.*s',%zu", + key->serial, key->description, + (int)datalen, (int)datalen, data, datalen); if (datalen <= 1 || !data || data[datalen - 1] != '\0') return -EINVAL; @@ -217,6 +218,19 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m) seq_printf(m, ": %u", key->datalen); } +/* + * read the DNS data + * - the key's semaphore is read-locked + */ +static long dns_resolver_read(const struct key *key, + char __user *buffer, size_t buflen) +{ + if (key->type_data.x[0]) + return key->type_data.x[0]; + + return user_read(key, buffer, buflen); +} + struct key_type key_type_dns_resolver = { .name = "dns_resolver", .instantiate = dns_resolver_instantiate, @@ -224,7 +238,7 @@ struct key_type key_type_dns_resolver = { .revoke = user_revoke, .destroy = user_destroy, .describe = dns_resolver_describe, - .read = user_read, + .read = dns_resolver_read, }; static int __init init_dns_resolver(void) diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 7ceb8044763..35a50205501 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1101,23 +1101,20 @@ int sysctl_ip_dynaddr __read_mostly; static int inet_sk_reselect_saddr(struct sock *sk) { struct inet_sock *inet = inet_sk(sk); - int err; - struct rtable *rt; __be32 old_saddr = inet->inet_saddr; - __be32 new_saddr; __be32 daddr = inet->inet_daddr; + struct rtable *rt; + __be32 new_saddr; if (inet->opt && inet->opt->srr) daddr = inet->opt->faddr; /* Query new route. */ - err = ip_route_connect(&rt, daddr, 0, - RT_CONN_FLAGS(sk), - sk->sk_bound_dev_if, - sk->sk_protocol, - inet->inet_sport, inet->inet_dport, sk, 0); - if (err) - return err; + rt = ip_route_connect(daddr, 0, RT_CONN_FLAGS(sk), + sk->sk_bound_dev_if, sk->sk_protocol, + inet->inet_sport, inet->inet_dport, sk, false); + if (IS_ERR(rt)) + return PTR_ERR(rt); sk_setup_caps(sk, &rt->dst); @@ -1160,7 +1157,7 @@ int inet_sk_rebuild_header(struct sock *sk) daddr = inet->inet_daddr; if (inet->opt && inet->opt->srr) daddr = inet->opt->faddr; -{ + { struct flowi fl = { .oif = sk->sk_bound_dev_if, .mark = sk->sk_mark, @@ -1174,11 +1171,14 @@ int inet_sk_rebuild_header(struct sock *sk) }; security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0); -} - if (!err) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + } + if (!IS_ERR(rt)) { + err = 0; sk_setup_caps(sk, &rt->dst); - else { + } else { + err = PTR_ERR(rt); + /* Routing failed... */ sk->sk_route_caps = 0; /* diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 7927589813b..fa9988da1da 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -440,7 +440,8 @@ static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev) /*unsigned long now; */ struct net *net = dev_net(dev); - if (ip_route_output_key(net, &rt, &fl) < 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return 1; if (rt->dst.dev != dev) { NET_INC_STATS_BH(net, LINUX_MIB_ARPFILTER); @@ -1063,10 +1064,10 @@ static int arp_req_set(struct net *net, struct arpreq *r, if (dev == NULL) { struct flowi fl = { .fl4_dst = ip, .fl4_tos = RTO_ONLINK }; - struct rtable *rt; - err = ip_route_output_key(net, &rt, &fl); - if (err != 0) - return err; + struct rtable *rt = ip_route_output_key(net, &fl); + + if (IS_ERR(rt)) + return PTR_ERR(rt); dev = rt->dst.dev; ip_rt_put(rt); if (!dev) @@ -1177,7 +1178,6 @@ static int arp_req_delete_public(struct net *net, struct arpreq *r, static int arp_req_delete(struct net *net, struct arpreq *r, struct net_device *dev) { - int err; __be32 ip; if (r->arp_flags & ATF_PUBL) @@ -1187,10 +1187,9 @@ static int arp_req_delete(struct net *net, struct arpreq *r, if (dev == NULL) { struct flowi fl = { .fl4_dst = ip, .fl4_tos = RTO_ONLINK }; - struct rtable *rt; - err = ip_route_output_key(net, &rt, &fl); - if (err != 0) - return err; + struct rtable *rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); dev = rt->dst.dev; ip_rt_put(rt); if (!dev) diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index 174be6caa5c..85bd24ca4f6 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -46,11 +46,12 @@ int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (!saddr) saddr = inet->mc_addr; } - err = ip_route_connect(&rt, usin->sin_addr.s_addr, saddr, - RT_CONN_FLAGS(sk), oif, - sk->sk_protocol, - inet->inet_sport, usin->sin_port, sk, 1); - if (err) { + rt = ip_route_connect(usin->sin_addr.s_addr, saddr, + RT_CONN_FLAGS(sk), oif, + sk->sk_protocol, + inet->inet_sport, usin->sin_port, sk, true); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); return err; diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 90389281d97..ff53860d1e5 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -111,7 +111,7 @@ static inline unsigned int inet_addr_hash(struct net *net, __be32 addr) static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) { - unsigned int hash = inet_addr_hash(net, ifa->ifa_address); + unsigned int hash = inet_addr_hash(net, ifa->ifa_local); spin_lock(&inet_addr_hash_lock); hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); @@ -146,7 +146,7 @@ struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) if (!net_eq(dev_net(dev), net)) continue; - if (ifa->ifa_address == addr) { + if (ifa->ifa_local == addr) { result = dev; break; } diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index d5c40d8f663..84db2da5c84 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -51,4 +51,11 @@ static inline void fib_result_assign(struct fib_result *res, res->fi = fi; } +struct fib_prop { + int error; + u8 scope; +}; + +extern const struct fib_prop fib_props[RTN_MAX + 1]; + #endif /* _FIB_LOOKUP_H */ diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 562f34cd930..6349a21692e 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -90,11 +90,7 @@ static DEFINE_SPINLOCK(fib_multipath_lock); #define endfor_nexthops(fi) } -static const struct -{ - int error; - u8 scope; -} fib_props[RTN_MAX + 1] = { +const struct fib_prop fib_props[RTN_MAX + 1] = { [RTN_UNSPEC] = { .error = 0, .scope = RT_SCOPE_NOWHERE, @@ -707,6 +703,9 @@ struct fib_info *fib_create_info(struct fib_config *cfg) int nhs = 1; struct net *net = cfg->fc_nlinfo.nl_net; + if (cfg->fc_type > RTN_MAX) + goto err_inval; + /* Fast check to catch the most weird cases */ if (fib_props[cfg->fc_type].scope > cfg->fc_scope) goto err_inval; @@ -812,6 +811,17 @@ struct fib_info *fib_create_info(struct fib_config *cfg) if (cfg->fc_gw || cfg->fc_oif || cfg->fc_mp) goto err_inval; goto link_it; + } else { + switch (cfg->fc_type) { + case RTN_UNICAST: + case RTN_LOCAL: + case RTN_BROADCAST: + case RTN_ANYCAST: + case RTN_MULTICAST: + break; + default: + goto err_inval; + } } if (cfg->fc_scope > RT_SCOPE_HOST) @@ -888,80 +898,6 @@ failure: return ERR_PTR(err); } -/* Note! fib_semantic_match intentionally uses RCU list functions. */ -int fib_semantic_match(struct fib_table *tb, struct list_head *head, - const struct flowi *flp, struct fib_result *res, - int prefixlen, int fib_flags) -{ - struct fib_alias *fa; - int nh_sel = 0; - - list_for_each_entry_rcu(fa, head, fa_list) { - int err; - - if (fa->fa_tos && - fa->fa_tos != flp->fl4_tos) - continue; - - if (fa->fa_scope < flp->fl4_scope) - continue; - - fib_alias_accessed(fa); - - err = fib_props[fa->fa_type].error; - if (err == 0) { - struct fib_info *fi = fa->fa_info; - - if (fi->fib_flags & RTNH_F_DEAD) - continue; - - switch (fa->fa_type) { - case RTN_UNICAST: - case RTN_LOCAL: - case RTN_BROADCAST: - case RTN_ANYCAST: - case RTN_MULTICAST: - for_nexthops(fi) { - if (nh->nh_flags & RTNH_F_DEAD) - continue; - if (!flp->oif || flp->oif == nh->nh_oif) - break; - } -#ifdef CONFIG_IP_ROUTE_MULTIPATH - if (nhsel < fi->fib_nhs) { - nh_sel = nhsel; - goto out_fill_res; - } -#else - if (nhsel < 1) - goto out_fill_res; -#endif - endfor_nexthops(fi); - continue; - - default: - pr_warning("fib_semantic_match bad type %#x\n", - fa->fa_type); - return -EINVAL; - } - } - return err; - } - return 1; - -out_fill_res: - res->prefixlen = prefixlen; - res->nh_sel = nh_sel; - res->type = fa->fa_type; - res->scope = fa->fa_scope; - res->fi = fa->fa_info; - res->table = tb; - res->fa_head = head; - if (!(fib_flags & FIB_LOOKUP_NOREF)) - atomic_inc(&res->fi->fib_clntref); - return 0; -} - /* Find appropriate source address to this destination */ __be32 __fib_res_prefsrc(struct fib_result *res) diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index edf3b0997e0..a4109a54477 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1349,23 +1349,58 @@ static int check_leaf(struct fib_table *tb, struct trie *t, struct leaf *l, struct hlist_node *node; hlist_for_each_entry_rcu(li, node, hhead, hlist) { - int err; + struct fib_alias *fa; int plen = li->plen; __be32 mask = inet_make_mask(plen); if (l->key != (key & ntohl(mask))) continue; - err = fib_semantic_match(tb, &li->falh, flp, res, plen, fib_flags); + list_for_each_entry_rcu(fa, &li->falh, fa_list) { + struct fib_info *fi = fa->fa_info; + int nhsel, err; + if (fa->fa_tos && fa->fa_tos != flp->fl4_tos) + continue; + if (fa->fa_scope < flp->fl4_scope) + continue; + fib_alias_accessed(fa); + err = fib_props[fa->fa_type].error; + if (err) { #ifdef CONFIG_IP_FIB_TRIE_STATS - if (err <= 0) - t->stats.semantic_match_passed++; - else - t->stats.semantic_match_miss++; + t->stats.semantic_match_miss++; +#endif + return 1; + } + if (fi->fib_flags & RTNH_F_DEAD) + continue; + for (nhsel = 0; nhsel < fi->fib_nhs; nhsel++) { + const struct fib_nh *nh = &fi->fib_nh[nhsel]; + + if (nh->nh_flags & RTNH_F_DEAD) + continue; + if (flp->oif && flp->oif != nh->nh_oif) + continue; + +#ifdef CONFIG_IP_FIB_TRIE_STATS + t->stats.semantic_match_passed++; +#endif + res->prefixlen = plen; + res->nh_sel = nhsel; + res->type = fa->fa_type; + res->scope = fa->fa_scope; + res->fi = fi; + res->table = tb; + res->fa_head = &li->falh; + if (!(fib_flags & FIB_LOOKUP_NOREF)) + atomic_inc(&res->fi->fib_clntref); + return 0; + } + } + +#ifdef CONFIG_IP_FIB_TRIE_STATS + t->stats.semantic_match_miss++; #endif - if (err <= 0) - return err; } return 1; diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index ad2bcf1b69a..1771ce66254 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -358,7 +358,8 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) .fl4_tos = RT_TOS(ip_hdr(skb)->tos), .proto = IPPROTO_ICMP }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_unlock; } if (icmpv4_xrlim_allow(net, rt, icmp_param->data.icmph.type, @@ -369,6 +370,94 @@ out_unlock: icmp_xmit_unlock(sk); } +static struct rtable *icmp_route_lookup(struct net *net, struct sk_buff *skb_in, + struct iphdr *iph, + __be32 saddr, u8 tos, + int type, int code, + struct icmp_bxm *param) +{ + struct flowi fl = { + .fl4_dst = (param->replyopts.srr ? + param->replyopts.faddr : iph->saddr), + .fl4_src = saddr, + .fl4_tos = RT_TOS(tos), + .proto = IPPROTO_ICMP, + .fl_icmp_type = type, + .fl_icmp_code = code, + }; + struct rtable *rt, *rt2; + int err; + + security_skb_classify_flow(skb_in, &fl); + rt = __ip_route_output_key(net, &fl); + if (IS_ERR(rt)) + return rt; + + /* No need to clone since we're just using its address. */ + rt2 = rt; + + if (!fl.fl4_src) + fl.fl4_src = rt->rt_src; + + rt = (struct rtable *) xfrm_lookup(net, &rt->dst, &fl, NULL, 0); + if (!IS_ERR(rt)) { + if (rt != rt2) + return rt; + } else if (PTR_ERR(rt) == -EPERM) { + rt = NULL; + } else + return rt; + + err = xfrm_decode_session_reverse(skb_in, &fl, AF_INET); + if (err) + goto relookup_failed; + + if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) { + rt2 = __ip_route_output_key(net, &fl); + if (IS_ERR(rt2)) + err = PTR_ERR(rt2); + } else { + struct flowi fl2 = {}; + unsigned long orefdst; + + fl2.fl4_dst = fl.fl4_src; + rt2 = ip_route_output_key(net, &fl2); + if (IS_ERR(rt2)) { + err = PTR_ERR(rt2); + goto relookup_failed; + } + /* Ugh! */ + orefdst = skb_in->_skb_refdst; /* save old refdst */ + err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, + RT_TOS(tos), rt2->dst.dev); + + dst_release(&rt2->dst); + rt2 = skb_rtable(skb_in); + skb_in->_skb_refdst = orefdst; /* restore old refdst */ + } + + if (err) + goto relookup_failed; + + rt2 = (struct rtable *) xfrm_lookup(net, &rt2->dst, &fl, NULL, XFRM_LOOKUP_ICMP); + if (!IS_ERR(rt2)) { + dst_release(&rt->dst); + rt = rt2; + } else if (PTR_ERR(rt2) == -EPERM) { + if (rt) + dst_release(&rt->dst); + return rt2; + } else { + err = PTR_ERR(rt2); + goto relookup_failed; + } + return rt; + +relookup_failed: + if (rt) + return rt; + return ERR_PTR(err); +} /* * Send an ICMP message in response to a situation @@ -474,7 +563,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) rcu_read_lock(); if (rt_is_input_route(rt) && net->ipv4.sysctl_icmp_errors_use_inbound_ifaddr) - dev = dev_get_by_index_rcu(net, rt->fl.iif); + dev = dev_get_by_index_rcu(net, rt->rt_iif); if (dev) saddr = inet_select_addr(dev, 0, RT_SCOPE_LINK); @@ -506,86 +595,11 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) ipc.opt = &icmp_param.replyopts; ipc.tx_flags = 0; - { - struct flowi fl = { - .fl4_dst = icmp_param.replyopts.srr ? - icmp_param.replyopts.faddr : iph->saddr, - .fl4_src = saddr, - .fl4_tos = RT_TOS(tos), - .proto = IPPROTO_ICMP, - .fl_icmp_type = type, - .fl_icmp_code = code, - }; - int err; - struct rtable *rt2; - - security_skb_classify_flow(skb_in, &fl); - if (__ip_route_output_key(net, &rt, &fl)) - goto out_unlock; - - /* No need to clone since we're just using its address. */ - rt2 = rt; - - if (!fl.nl_u.ip4_u.saddr) - fl.nl_u.ip4_u.saddr = rt->rt_src; - - err = xfrm_lookup(net, (struct dst_entry **)&rt, &fl, NULL, 0); - switch (err) { - case 0: - if (rt != rt2) - goto route_done; - break; - case -EPERM: - rt = NULL; - break; - default: - goto out_unlock; - } - - if (xfrm_decode_session_reverse(skb_in, &fl, AF_INET)) - goto relookup_failed; - - if (inet_addr_type(net, fl.fl4_src) == RTN_LOCAL) - err = __ip_route_output_key(net, &rt2, &fl); - else { - struct flowi fl2 = {}; - unsigned long orefdst; - - fl2.fl4_dst = fl.fl4_src; - if (ip_route_output_key(net, &rt2, &fl2)) - goto relookup_failed; - - /* Ugh! */ - orefdst = skb_in->_skb_refdst; /* save old refdst */ - err = ip_route_input(skb_in, fl.fl4_dst, fl.fl4_src, - RT_TOS(tos), rt2->dst.dev); - - dst_release(&rt2->dst); - rt2 = skb_rtable(skb_in); - skb_in->_skb_refdst = orefdst; /* restore old refdst */ - } - - if (err) - goto relookup_failed; - - err = xfrm_lookup(net, (struct dst_entry **)&rt2, &fl, NULL, - XFRM_LOOKUP_ICMP); - switch (err) { - case 0: - dst_release(&rt->dst); - rt = rt2; - break; - case -EPERM: - goto ende; - default: -relookup_failed: - if (!rt) - goto out_unlock; - break; - } - } + rt = icmp_route_lookup(net, skb_in, iph, saddr, tos, + type, code, &icmp_param); + if (IS_ERR(rt)) + goto out_unlock; -route_done: if (!icmpv4_xrlim_allow(net, rt, type, code)) goto ende; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index e0e77e297de..44ba9068b72 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -325,7 +325,8 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size) struct flowi fl = { .oif = dev->ifindex, .fl4_dst = IGMPV3_ALL_MCR, .proto = IPPROTO_IGMP }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { kfree_skb(skb); return NULL; } @@ -670,7 +671,8 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc, struct flowi fl = { .oif = dev->ifindex, .fl4_dst = dst, .proto = IPPROTO_IGMP }; - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; } if (rt->rt_src == 0) { @@ -1440,7 +1442,6 @@ void ip_mc_destroy_dev(struct in_device *in_dev) static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) { struct flowi fl = { .fl4_dst = imr->imr_multiaddr.s_addr }; - struct rtable *rt; struct net_device *dev = NULL; struct in_device *idev = NULL; @@ -1454,9 +1455,12 @@ static struct in_device *ip_mc_find_dev(struct net *net, struct ip_mreqn *imr) return NULL; } - if (!dev && !ip_route_output_key(net, &rt, &fl)) { - dev = rt->dst.dev; - ip_rt_put(rt); + if (!dev) { + struct rtable *rt = ip_route_output_key(net, &fl); + if (!IS_ERR(rt)) { + dev = rt->dst.dev; + ip_rt_put(rt); + } } if (dev) { imr->imr_ifindex = dev->ifindex; diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 97e5fb76526..e4e301a61c5 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -369,7 +369,8 @@ struct dst_entry *inet_csk_route_req(struct sock *sk, struct net *net = sock_net(sk); security_req_classify_flow(req, &fl); - if (ip_route_output_flow(net, &rt, &fl, sk, 0)) + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) goto no_route; if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto route_err; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 48f8d4592cc..f604ffdbea2 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -81,19 +81,19 @@ static const struct inet_peer peer_fake_node = { struct inet_peer_base { struct inet_peer __rcu *root; - spinlock_t lock; + seqlock_t lock; int total; }; static struct inet_peer_base v4_peers = { .root = peer_avl_empty_rcu, - .lock = __SPIN_LOCK_UNLOCKED(v4_peers.lock), + .lock = __SEQLOCK_UNLOCKED(v4_peers.lock), .total = 0, }; static struct inet_peer_base v6_peers = { .root = peer_avl_empty_rcu, - .lock = __SPIN_LOCK_UNLOCKED(v6_peers.lock), + .lock = __SEQLOCK_UNLOCKED(v6_peers.lock), .total = 0, }; @@ -177,6 +177,9 @@ static int addr_compare(const struct inetpeer_addr *a, return 0; } +#define rcu_deref_locked(X, BASE) \ + rcu_dereference_protected(X, lockdep_is_held(&(BASE)->lock.lock)) + /* * Called with local BH disabled and the pool lock held. */ @@ -187,8 +190,7 @@ static int addr_compare(const struct inetpeer_addr *a, \ stackptr = _stack; \ *stackptr++ = &_base->root; \ - for (u = rcu_dereference_protected(_base->root, \ - lockdep_is_held(&_base->lock)); \ + for (u = rcu_deref_locked(_base->root, _base); \ u != peer_avl_empty; ) { \ int cmp = addr_compare(_daddr, &u->daddr); \ if (cmp == 0) \ @@ -198,8 +200,7 @@ static int addr_compare(const struct inetpeer_addr *a, else \ v = &u->avl_right; \ *stackptr++ = v; \ - u = rcu_dereference_protected(*v, \ - lockdep_is_held(&_base->lock)); \ + u = rcu_deref_locked(*v, _base); \ } \ u; \ }) @@ -246,13 +247,11 @@ static struct inet_peer *lookup_rcu_bh(const struct inetpeer_addr *daddr, struct inet_peer __rcu **v; \ *stackptr++ = &start->avl_left; \ v = &start->avl_left; \ - for (u = rcu_dereference_protected(*v, \ - lockdep_is_held(&base->lock)); \ + for (u = rcu_deref_locked(*v, base); \ u->avl_right != peer_avl_empty_rcu; ) { \ v = &u->avl_right; \ *stackptr++ = v; \ - u = rcu_dereference_protected(*v, \ - lockdep_is_held(&base->lock)); \ + u = rcu_deref_locked(*v, base); \ } \ u; \ }) @@ -271,21 +270,16 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], while (stackend > stack) { nodep = *--stackend; - node = rcu_dereference_protected(*nodep, - lockdep_is_held(&base->lock)); - l = rcu_dereference_protected(node->avl_left, - lockdep_is_held(&base->lock)); - r = rcu_dereference_protected(node->avl_right, - lockdep_is_held(&base->lock)); + node = rcu_deref_locked(*nodep, base); + l = rcu_deref_locked(node->avl_left, base); + r = rcu_deref_locked(node->avl_right, base); lh = node_height(l); rh = node_height(r); if (lh > rh + 1) { /* l: RH+2 */ struct inet_peer *ll, *lr, *lrl, *lrr; int lrh; - ll = rcu_dereference_protected(l->avl_left, - lockdep_is_held(&base->lock)); - lr = rcu_dereference_protected(l->avl_right, - lockdep_is_held(&base->lock)); + ll = rcu_deref_locked(l->avl_left, base); + lr = rcu_deref_locked(l->avl_right, base); lrh = node_height(lr); if (lrh <= node_height(ll)) { /* ll: RH+1 */ RCU_INIT_POINTER(node->avl_left, lr); /* lr: RH or RH+1 */ @@ -296,10 +290,8 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], l->avl_height = node->avl_height + 1; RCU_INIT_POINTER(*nodep, l); } else { /* ll: RH, lr: RH+1 */ - lrl = rcu_dereference_protected(lr->avl_left, - lockdep_is_held(&base->lock)); /* lrl: RH or RH-1 */ - lrr = rcu_dereference_protected(lr->avl_right, - lockdep_is_held(&base->lock)); /* lrr: RH or RH-1 */ + lrl = rcu_deref_locked(lr->avl_left, base);/* lrl: RH or RH-1 */ + lrr = rcu_deref_locked(lr->avl_right, base);/* lrr: RH or RH-1 */ RCU_INIT_POINTER(node->avl_left, lrr); /* lrr: RH or RH-1 */ RCU_INIT_POINTER(node->avl_right, r); /* r: RH */ node->avl_height = rh + 1; /* node: RH+1 */ @@ -314,10 +306,8 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], } else if (rh > lh + 1) { /* r: LH+2 */ struct inet_peer *rr, *rl, *rlr, *rll; int rlh; - rr = rcu_dereference_protected(r->avl_right, - lockdep_is_held(&base->lock)); - rl = rcu_dereference_protected(r->avl_left, - lockdep_is_held(&base->lock)); + rr = rcu_deref_locked(r->avl_right, base); + rl = rcu_deref_locked(r->avl_left, base); rlh = node_height(rl); if (rlh <= node_height(rr)) { /* rr: LH+1 */ RCU_INIT_POINTER(node->avl_right, rl); /* rl: LH or LH+1 */ @@ -328,10 +318,8 @@ static void peer_avl_rebalance(struct inet_peer __rcu **stack[], r->avl_height = node->avl_height + 1; RCU_INIT_POINTER(*nodep, r); } else { /* rr: RH, rl: RH+1 */ - rlr = rcu_dereference_protected(rl->avl_right, - lockdep_is_held(&base->lock)); /* rlr: LH or LH-1 */ - rll = rcu_dereference_protected(rl->avl_left, - lockdep_is_held(&base->lock)); /* rll: LH or LH-1 */ + rlr = rcu_deref_locked(rl->avl_right, base);/* rlr: LH or LH-1 */ + rll = rcu_deref_locked(rl->avl_left, base);/* rll: LH or LH-1 */ RCU_INIT_POINTER(node->avl_right, rll); /* rll: LH or LH-1 */ RCU_INIT_POINTER(node->avl_left, l); /* l: LH */ node->avl_height = lh + 1; /* node: LH+1 */ @@ -372,7 +360,7 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) do_free = 0; - spin_lock_bh(&base->lock); + write_seqlock_bh(&base->lock); /* Check the reference counter. It was artificially incremented by 1 * in cleanup() function to prevent sudden disappearing. If we can * atomically (because of lockless readers) take this last reference, @@ -392,8 +380,7 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) /* look for a node to insert instead of p */ struct inet_peer *t; t = lookup_rightempty(p, base); - BUG_ON(rcu_dereference_protected(*stackptr[-1], - lockdep_is_held(&base->lock)) != t); + BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); **--stackptr = t->avl_left; /* t is removed, t->daddr > x->daddr for any * x in p->avl_left subtree. @@ -409,7 +396,7 @@ static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base) base->total--; do_free = 1; } - spin_unlock_bh(&base->lock); + write_sequnlock_bh(&base->lock); if (do_free) call_rcu_bh(&p->rcu, inetpeer_free_rcu); @@ -477,12 +464,16 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; struct inet_peer_base *base = family_to_base(daddr->family); struct inet_peer *p; + unsigned int sequence; + int invalidated; /* Look up for the address quickly, lockless. * Because of a concurrent writer, we might not find an existing entry. */ rcu_read_lock_bh(); + sequence = read_seqbegin(&base->lock); p = lookup_rcu_bh(daddr, base); + invalidated = read_seqretry(&base->lock, sequence); rcu_read_unlock_bh(); if (p) { @@ -493,14 +484,18 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) return p; } + /* If no writer did a change during our lookup, we can return early. */ + if (!create && !invalidated) + return NULL; + /* retry an exact lookup, taking the lock before. * At least, nodes should be hot in our cache. */ - spin_lock_bh(&base->lock); + write_seqlock_bh(&base->lock); p = lookup(daddr, stack, base); if (p != peer_avl_empty) { atomic_inc(&p->refcnt); - spin_unlock_bh(&base->lock); + write_sequnlock_bh(&base->lock); /* Remove the entry from unused list if it was there. */ unlink_from_unused(p); return p; @@ -524,7 +519,7 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create) link_to_pool(p, base); base->total++; } - spin_unlock_bh(&base->lock); + write_sequnlock_bh(&base->lock); if (base->total >= inet_peer_threshold) /* Remove one less-recently-used entry. */ diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6613edfac28..f9af98dd756 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -778,7 +778,8 @@ static netdev_tx_t ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev .proto = IPPROTO_GRE, .fl_gre_key = tunnel->parms.o_key }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error; } @@ -953,9 +954,9 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) .proto = IPPROTO_GRE, .fl_gre_key = tunnel->parms.o_key }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } @@ -1215,9 +1216,9 @@ static int ipgre_open(struct net_device *dev) .proto = IPPROTO_GRE, .fl_gre_key = t->parms.o_key }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (ip_route_output_key(dev_net(dev), &rt, &fl)) + if (IS_ERR(rt)) return -EADDRNOTAVAIL; dev = rt->dst.dev; ip_rt_put(rt); diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 04c7b3ba6b3..171f483b21d 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -355,7 +355,8 @@ int ip_queue_xmit(struct sk_buff *skb) * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0)) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) goto no_route; } sk_setup_caps(sk, &rt->dst); @@ -733,6 +734,7 @@ csum_page(struct page *page, int offset, int copy) } static inline int ip_ufo_append_data(struct sock *sk, + struct sk_buff_head *queue, int getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb), void *from, int length, int hh_len, int fragheaderlen, @@ -745,7 +747,7 @@ static inline int ip_ufo_append_data(struct sock *sk, * device, so create one single skb packet containing complete * udp datagram */ - if ((skb = skb_peek_tail(&sk->sk_write_queue)) == NULL) { + if ((skb = skb_peek_tail(queue)) == NULL) { skb = sock_alloc_send_skb(sk, hh_len + fragheaderlen + transhdrlen + 20, (flags & MSG_DONTWAIT), &err); @@ -767,40 +769,28 @@ static inline int ip_ufo_append_data(struct sock *sk, skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; - sk->sk_sndmsg_off = 0; /* specify the length of each IP datagram fragment */ skb_shinfo(skb)->gso_size = mtu - fragheaderlen; skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - __skb_queue_tail(&sk->sk_write_queue, skb); + __skb_queue_tail(queue, skb); } return skb_append_datato_frags(sk, skb, getfrag, from, (length - transhdrlen)); } -/* - * ip_append_data() and ip_append_page() can make one large IP datagram - * from many pieces of data. Each pieces will be holded on the socket - * until ip_push_pending_frames() is called. Each piece can be a page - * or non-page data. - * - * Not only UDP, other transport protocols - e.g. raw sockets - can use - * this interface potentially. - * - * LATER: length must be adjusted by pad at tail, when it is required. - */ -int ip_append_data(struct sock *sk, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int length, int transhdrlen, - struct ipcm_cookie *ipc, struct rtable **rtp, - unsigned int flags) +static int __ip_append_data(struct sock *sk, struct sk_buff_head *queue, + struct inet_cork *cork, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + unsigned int flags) { struct inet_sock *inet = inet_sk(sk); struct sk_buff *skb; - struct ip_options *opt = NULL; + struct ip_options *opt = cork->opt; int hh_len; int exthdrlen; int mtu; @@ -809,58 +799,19 @@ int ip_append_data(struct sock *sk, int offset = 0; unsigned int maxfraglen, fragheaderlen; int csummode = CHECKSUM_NONE; - struct rtable *rt; + struct rtable *rt = (struct rtable *)cork->dst; - if (flags&MSG_PROBE) - return 0; - - if (skb_queue_empty(&sk->sk_write_queue)) { - /* - * setup for corking. - */ - opt = ipc->opt; - if (opt) { - if (inet->cork.opt == NULL) { - inet->cork.opt = kmalloc(sizeof(struct ip_options) + 40, sk->sk_allocation); - if (unlikely(inet->cork.opt == NULL)) - return -ENOBUFS; - } - memcpy(inet->cork.opt, opt, sizeof(struct ip_options)+opt->optlen); - inet->cork.flags |= IPCORK_OPT; - inet->cork.addr = ipc->addr; - } - rt = *rtp; - if (unlikely(!rt)) - return -EFAULT; - /* - * We steal reference to this route, caller should not release it - */ - *rtp = NULL; - inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ? - rt->dst.dev->mtu : - dst_mtu(rt->dst.path); - inet->cork.dst = &rt->dst; - inet->cork.length = 0; - sk->sk_sndmsg_page = NULL; - sk->sk_sndmsg_off = 0; - exthdrlen = rt->dst.header_len; - length += exthdrlen; - transhdrlen += exthdrlen; - } else { - rt = (struct rtable *)inet->cork.dst; - if (inet->cork.flags & IPCORK_OPT) - opt = inet->cork.opt; + exthdrlen = transhdrlen ? rt->dst.header_len : 0; + length += exthdrlen; + transhdrlen += exthdrlen; + mtu = cork->fragsize; - transhdrlen = 0; - exthdrlen = 0; - mtu = inet->cork.fragsize; - } hh_len = LL_RESERVED_SPACE(rt->dst.dev); fragheaderlen = sizeof(struct iphdr) + (opt ? opt->optlen : 0); maxfraglen = ((mtu - fragheaderlen) & ~7) + fragheaderlen; - if (inet->cork.length + length > 0xFFFF - fragheaderlen) { + if (cork->length + length > 0xFFFF - fragheaderlen) { ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport, mtu-exthdrlen); return -EMSGSIZE; @@ -876,15 +827,15 @@ int ip_append_data(struct sock *sk, !exthdrlen) csummode = CHECKSUM_PARTIAL; - skb = skb_peek_tail(&sk->sk_write_queue); + skb = skb_peek_tail(queue); - inet->cork.length += length; + cork->length += length; if (((length > mtu) || (skb && skb_is_gso(skb))) && (sk->sk_protocol == IPPROTO_UDP) && (rt->dst.dev->features & NETIF_F_UFO)) { - err = ip_ufo_append_data(sk, getfrag, from, length, hh_len, - fragheaderlen, transhdrlen, mtu, - flags); + err = ip_ufo_append_data(sk, queue, getfrag, from, length, + hh_len, fragheaderlen, transhdrlen, + mtu, flags); if (err) goto error; return 0; @@ -961,7 +912,7 @@ alloc_new_skb: else /* only the initial fragment is time stamped */ - ipc->tx_flags = 0; + cork->tx_flags = 0; } if (skb == NULL) goto error; @@ -972,7 +923,7 @@ alloc_new_skb: skb->ip_summed = csummode; skb->csum = 0; skb_reserve(skb, hh_len); - skb_shinfo(skb)->tx_flags = ipc->tx_flags; + skb_shinfo(skb)->tx_flags = cork->tx_flags; /* * Find where to start putting bytes. @@ -1009,7 +960,7 @@ alloc_new_skb: /* * Put the packet on the pending queue. */ - __skb_queue_tail(&sk->sk_write_queue, skb); + __skb_queue_tail(queue, skb); continue; } @@ -1029,8 +980,8 @@ alloc_new_skb: } else { int i = skb_shinfo(skb)->nr_frags; skb_frag_t *frag = &skb_shinfo(skb)->frags[i-1]; - struct page *page = sk->sk_sndmsg_page; - int off = sk->sk_sndmsg_off; + struct page *page = cork->page; + int off = cork->off; unsigned int left; if (page && (left = PAGE_SIZE - off) > 0) { @@ -1042,7 +993,7 @@ alloc_new_skb: goto error; } get_page(page); - skb_fill_page_desc(skb, i, page, sk->sk_sndmsg_off, 0); + skb_fill_page_desc(skb, i, page, off, 0); frag = &skb_shinfo(skb)->frags[i]; } } else if (i < MAX_SKB_FRAGS) { @@ -1053,8 +1004,8 @@ alloc_new_skb: err = -ENOMEM; goto error; } - sk->sk_sndmsg_page = page; - sk->sk_sndmsg_off = 0; + cork->page = page; + cork->off = 0; skb_fill_page_desc(skb, i, page, 0, 0); frag = &skb_shinfo(skb)->frags[i]; @@ -1066,7 +1017,7 @@ alloc_new_skb: err = -EFAULT; goto error; } - sk->sk_sndmsg_off += copy; + cork->off += copy; frag->size += copy; skb->len += copy; skb->data_len += copy; @@ -1080,11 +1031,87 @@ alloc_new_skb: return 0; error: - inet->cork.length -= length; + cork->length -= length; IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTDISCARDS); return err; } +static int ip_setup_cork(struct sock *sk, struct inet_cork *cork, + struct ipcm_cookie *ipc, struct rtable **rtp) +{ + struct inet_sock *inet = inet_sk(sk); + struct ip_options *opt; + struct rtable *rt; + + /* + * setup for corking. + */ + opt = ipc->opt; + if (opt) { + if (cork->opt == NULL) { + cork->opt = kmalloc(sizeof(struct ip_options) + 40, + sk->sk_allocation); + if (unlikely(cork->opt == NULL)) + return -ENOBUFS; + } + memcpy(cork->opt, opt, sizeof(struct ip_options) + opt->optlen); + cork->flags |= IPCORK_OPT; + cork->addr = ipc->addr; + } + rt = *rtp; + if (unlikely(!rt)) + return -EFAULT; + /* + * We steal reference to this route, caller should not release it + */ + *rtp = NULL; + cork->fragsize = inet->pmtudisc == IP_PMTUDISC_PROBE ? + rt->dst.dev->mtu : dst_mtu(rt->dst.path); + cork->dst = &rt->dst; + cork->length = 0; + cork->tx_flags = ipc->tx_flags; + cork->page = NULL; + cork->off = 0; + + return 0; +} + +/* + * ip_append_data() and ip_append_page() can make one large IP datagram + * from many pieces of data. Each pieces will be holded on the socket + * until ip_push_pending_frames() is called. Each piece can be a page + * or non-page data. + * + * Not only UDP, other transport protocols - e.g. raw sockets - can use + * this interface potentially. + * + * LATER: length must be adjusted by pad at tail, when it is required. + */ +int ip_append_data(struct sock *sk, + int getfrag(void *from, char *to, int offset, int len, + int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, struct rtable **rtp, + unsigned int flags) +{ + struct inet_sock *inet = inet_sk(sk); + int err; + + if (flags&MSG_PROBE) + return 0; + + if (skb_queue_empty(&sk->sk_write_queue)) { + err = ip_setup_cork(sk, &inet->cork, ipc, rtp); + if (err) + return err; + } else { + transhdrlen = 0; + } + + return __ip_append_data(sk, &sk->sk_write_queue, &inet->cork, getfrag, + from, length, transhdrlen, flags); +} + ssize_t ip_append_page(struct sock *sk, struct page *page, int offset, size_t size, int flags) { @@ -1228,40 +1255,41 @@ error: return err; } -static void ip_cork_release(struct inet_sock *inet) +static void ip_cork_release(struct inet_cork *cork) { - inet->cork.flags &= ~IPCORK_OPT; - kfree(inet->cork.opt); - inet->cork.opt = NULL; - dst_release(inet->cork.dst); - inet->cork.dst = NULL; + cork->flags &= ~IPCORK_OPT; + kfree(cork->opt); + cork->opt = NULL; + dst_release(cork->dst); + cork->dst = NULL; } /* * Combined all pending IP fragments on the socket as one IP datagram * and push them out. */ -int ip_push_pending_frames(struct sock *sk) +struct sk_buff *__ip_make_skb(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork) { struct sk_buff *skb, *tmp_skb; struct sk_buff **tail_skb; struct inet_sock *inet = inet_sk(sk); struct net *net = sock_net(sk); struct ip_options *opt = NULL; - struct rtable *rt = (struct rtable *)inet->cork.dst; + struct rtable *rt = (struct rtable *)cork->dst; struct iphdr *iph; __be16 df = 0; __u8 ttl; - int err = 0; - if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL) + if ((skb = __skb_dequeue(queue)) == NULL) goto out; tail_skb = &(skb_shinfo(skb)->frag_list); /* move skb->data to ip header from ext header */ if (skb->data < skb_network_header(skb)) __skb_pull(skb, skb_network_offset(skb)); - while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { + while ((tmp_skb = __skb_dequeue(queue)) != NULL) { __skb_pull(tmp_skb, skb_network_header_len(skb)); *tail_skb = tmp_skb; tail_skb = &(tmp_skb->next); @@ -1287,8 +1315,8 @@ int ip_push_pending_frames(struct sock *sk) ip_dont_fragment(sk, &rt->dst))) df = htons(IP_DF); - if (inet->cork.flags & IPCORK_OPT) - opt = inet->cork.opt; + if (cork->flags & IPCORK_OPT) + opt = cork->opt; if (rt->rt_type == RTN_MULTICAST) ttl = inet->mc_ttl; @@ -1300,7 +1328,7 @@ int ip_push_pending_frames(struct sock *sk) iph->ihl = 5; if (opt) { iph->ihl += opt->optlen>>2; - ip_options_build(skb, opt, inet->cork.addr, rt, 0); + ip_options_build(skb, opt, cork->addr, rt, 0); } iph->tos = inet->tos; iph->frag_off = df; @@ -1316,44 +1344,95 @@ int ip_push_pending_frames(struct sock *sk) * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec * on dst refcount */ - inet->cork.dst = NULL; + cork->dst = NULL; skb_dst_set(skb, &rt->dst); if (iph->protocol == IPPROTO_ICMP) icmp_out_count(net, ((struct icmphdr *) skb_transport_header(skb))->type); - /* Netfilter gets whole the not fragmented skb. */ + ip_cork_release(cork); +out: + return skb; +} + +int ip_send_skb(struct sk_buff *skb) +{ + struct net *net = sock_net(skb->sk); + int err; + err = ip_local_out(skb); if (err) { if (err > 0) err = net_xmit_errno(err); if (err) - goto error; + IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); } -out: - ip_cork_release(inet); return err; +} -error: - IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS); - goto out; +int ip_push_pending_frames(struct sock *sk) +{ + struct sk_buff *skb; + + skb = ip_finish_skb(sk); + if (!skb) + return 0; + + /* Netfilter gets whole the not fragmented skb. */ + return ip_send_skb(skb); } /* * Throw away all pending data on the socket. */ -void ip_flush_pending_frames(struct sock *sk) +static void __ip_flush_pending_frames(struct sock *sk, + struct sk_buff_head *queue, + struct inet_cork *cork) { struct sk_buff *skb; - while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL) + while ((skb = __skb_dequeue_tail(queue)) != NULL) kfree_skb(skb); - ip_cork_release(inet_sk(sk)); + ip_cork_release(cork); +} + +void ip_flush_pending_frames(struct sock *sk) +{ + __ip_flush_pending_frames(sk, &sk->sk_write_queue, &inet_sk(sk)->cork); } +struct sk_buff *ip_make_skb(struct sock *sk, + int getfrag(void *from, char *to, int offset, + int len, int odd, struct sk_buff *skb), + void *from, int length, int transhdrlen, + struct ipcm_cookie *ipc, struct rtable **rtp, + unsigned int flags) +{ + struct inet_cork cork = {}; + struct sk_buff_head queue; + int err; + + if (flags & MSG_PROBE) + return NULL; + + __skb_queue_head_init(&queue); + + err = ip_setup_cork(sk, &cork, ipc, rtp); + if (err) + return ERR_PTR(err); + + err = __ip_append_data(sk, &queue, &cork, getfrag, + from, length, transhdrlen, flags); + if (err) { + __ip_flush_pending_frames(sk, &queue, &cork); + return ERR_PTR(err); + } + + return __ip_make_skb(sk, &queue, &cork); +} /* * Fetch data from kernel space and fill in checksum if needed. @@ -1411,7 +1490,8 @@ void ip_send_reply(struct sock *sk, struct sk_buff *skb, struct ip_reply_arg *ar .proto = sk->sk_protocol, .flags = ip_reply_arg_flowi_flags(arg) }; security_skb_classify_flow(skb, &fl); - if (ip_route_output_key(sock_net(sk), &rt, &fl)) + rt = ip_route_output_key(sock_net(sk), &fl); + if (IS_ERR(rt)) return; } diff --git a/net/ipv4/ipip.c b/net/ipv4/ipip.c index 988f52fba54..e1e17576baa 100644 --- a/net/ipv4/ipip.c +++ b/net/ipv4/ipip.c @@ -469,7 +469,8 @@ static netdev_tx_t ipip_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) .proto = IPPROTO_IPIP }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error_icmp; } @@ -590,9 +591,9 @@ static void ipip_tunnel_bind_dev(struct net_device *dev) .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - struct rtable *rt; + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 8b65a12654e..9d5f6340af1 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1618,8 +1618,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_free; encap = sizeof(struct iphdr); } else { @@ -1629,8 +1629,8 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt, .fl4_tos = RT_TOS(iph->tos), .proto = IPPROTO_IPIP }; - - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) goto out_free; } @@ -1813,12 +1813,22 @@ int ip_mr_input(struct sk_buff *skb) if (IPCB(skb)->flags & IPSKB_FORWARDED) goto dont_forward; - err = ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt); - if (err < 0) { - kfree_skb(skb); - return err; + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + err = ipmr_fib_lookup(net, &fl, &mrt); + if (err < 0) { + kfree_skb(skb); + return err; + } } - if (!local) { if (IPCB(skb)->opt.router_alert) { if (ip_call_ra_chain(skb)) @@ -1946,9 +1956,19 @@ int pim_rcv_v1(struct sk_buff *skb) pim = igmp_hdr(skb); - if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) - goto drop; - + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + if (ipmr_fib_lookup(net, &fl, &mrt) < 0) + goto drop; + } if (!mrt->mroute_do_pim || pim->group != PIM_V1_VERSION || pim->code != PIM_V1_REGISTER) goto drop; @@ -1978,9 +1998,19 @@ static int pim_rcv(struct sk_buff *skb) csum_fold(skb_checksum(skb, 0, skb->len, 0)))) goto drop; - if (ipmr_fib_lookup(net, &skb_rtable(skb)->fl, &mrt) < 0) - goto drop; - + { + struct rtable *rt = skb_rtable(skb); + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + if (ipmr_fib_lookup(net, &fl, &mrt) < 0) + goto drop; + } if (__pim_rcv(mrt, skb, sizeof(*pim))) { drop: kfree_skb(skb); diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c index 994a1f29ebb..67bf709180d 100644 --- a/net/ipv4/netfilter.c +++ b/net/ipv4/netfilter.c @@ -38,7 +38,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) fl.oif = skb->sk ? skb->sk->sk_bound_dev_if : 0; fl.mark = skb->mark; fl.flags = skb->sk ? inet_sk_flowi_flags(skb->sk) : 0; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; /* Drop old route. */ @@ -48,7 +49,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) /* non-local src, find valid iif to satisfy * rp-filter when calling ip_route_input. */ fl.fl4_dst = iph->saddr; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return -1; orefdst = skb->_skb_refdst; @@ -69,7 +71,8 @@ int ip_route_me_harder(struct sk_buff *skb, unsigned addr_type) xfrm_decode_session(skb, &fl, AF_INET) == 0) { struct dst_entry *dst = skb_dst(skb); skb_dst_set(skb, NULL); - if (xfrm_lookup(net, &dst, &fl, skb->sk, 0)) + dst = xfrm_lookup(net, dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_set(skb, dst); } @@ -102,7 +105,8 @@ int ip_xfrm_me_harder(struct sk_buff *skb) dst = ((struct xfrm_dst *)dst)->route; dst_hold(dst); - if (xfrm_lookup(dev_net(dst->dev), &dst, &fl, skb->sk, 0) < 0) + dst = xfrm_lookup(dev_net(dst->dev), dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_drop(skb); @@ -219,7 +223,11 @@ static __sum16 nf_ip_checksum_partial(struct sk_buff *skb, unsigned int hook, static int nf_ip_route(struct dst_entry **dst, struct flowi *fl) { - return ip_route_output_key(&init_net, (struct rtable **)dst, fl); + struct rtable *rt = ip_route_output_key(&init_net, fl); + if (IS_ERR(rt)) + return PTR_ERR(rt); + *dst = &rt->dst; + return 0; } static const struct nf_afinfo nf_ip_afinfo = { diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 6390ba299b3..467d570d087 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -555,7 +555,8 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .fl4_tos = tos, .proto = inet->hdrincl ? IPPROTO_RAW : sk->sk_protocol, - }; + .flags = FLOWI_FLAG_CAN_SLEEP, + }; if (!inet->hdrincl) { err = raw_probe_proto_opt(&fl, msg); if (err) @@ -563,10 +564,12 @@ static int raw_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, } security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 1); + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + goto done; + } } - if (err) - goto done; err = -EACCES; if (rt->rt_flags & RTCF_BROADCAST && !sock_flag(sk, SOCK_BROADCAST)) diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 52b077d4520..92a24ea34c1 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -424,7 +424,7 @@ static int rt_cache_seq_show(struct seq_file *seq, void *v) dst_metric(&r->dst, RTAX_WINDOW), (int)((dst_metric(&r->dst, RTAX_RTT) >> 3) + dst_metric(&r->dst, RTAX_RTTVAR)), - r->fl.fl4_tos, + r->rt_tos, r->dst.hh ? atomic_read(&r->dst.hh->hh_refcnt) : -1, r->dst.hh ? (r->dst.hh->hh_output == dev_queue_xmit) : 0, @@ -711,22 +711,22 @@ static inline bool rt_caching(const struct net *net) net->ipv4.sysctl_rt_cache_rebuild_count; } -static inline bool compare_hash_inputs(const struct flowi *fl1, - const struct flowi *fl2) +static inline bool compare_hash_inputs(const struct rtable *rt1, + const struct rtable *rt2) { - return ((((__force u32)fl1->fl4_dst ^ (__force u32)fl2->fl4_dst) | - ((__force u32)fl1->fl4_src ^ (__force u32)fl2->fl4_src) | - (fl1->iif ^ fl2->iif)) == 0); + return ((((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) | + ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) | + (rt1->rt_iif ^ rt2->rt_iif)) == 0); } -static inline int compare_keys(struct flowi *fl1, struct flowi *fl2) +static inline int compare_keys(struct rtable *rt1, struct rtable *rt2) { - return (((__force u32)fl1->fl4_dst ^ (__force u32)fl2->fl4_dst) | - ((__force u32)fl1->fl4_src ^ (__force u32)fl2->fl4_src) | - (fl1->mark ^ fl2->mark) | - (*(u16 *)&fl1->fl4_tos ^ *(u16 *)&fl2->fl4_tos) | - (fl1->oif ^ fl2->oif) | - (fl1->iif ^ fl2->iif)) == 0; + return (((__force u32)rt1->rt_key_dst ^ (__force u32)rt2->rt_key_dst) | + ((__force u32)rt1->rt_key_src ^ (__force u32)rt2->rt_key_src) | + (rt1->rt_mark ^ rt2->rt_mark) | + (rt1->rt_tos ^ rt2->rt_tos) | + (rt1->rt_oif ^ rt2->rt_oif) | + (rt1->rt_iif ^ rt2->rt_iif)) == 0; } static inline int compare_netns(struct rtable *rt1, struct rtable *rt2) @@ -813,7 +813,7 @@ static int has_noalias(const struct rtable *head, const struct rtable *rth) const struct rtable *aux = head; while (aux != rth) { - if (compare_hash_inputs(&aux->fl, &rth->fl)) + if (compare_hash_inputs(aux, rth)) return 0; aux = rcu_dereference_protected(aux->dst.rt_next, 1); } @@ -1014,8 +1014,8 @@ static int slow_chain_length(const struct rtable *head) return length >> FRACT_BITS; } -static int rt_intern_hash(unsigned hash, struct rtable *rt, - struct rtable **rp, struct sk_buff *skb, int ifindex) +static struct rtable *rt_intern_hash(unsigned hash, struct rtable *rt, + struct sk_buff *skb, int ifindex) { struct rtable *rth, *cand; struct rtable __rcu **rthp, **candp; @@ -1056,7 +1056,7 @@ restart: printk(KERN_WARNING "Neighbour table failure & not caching routes.\n"); ip_rt_put(rt); - return err; + return ERR_PTR(err); } } @@ -1073,7 +1073,7 @@ restart: rt_free(rth); continue; } - if (compare_keys(&rth->fl, &rt->fl) && compare_netns(rth, rt)) { + if (compare_keys(rth, rt) && compare_netns(rth, rt)) { /* Put it first */ *rthp = rth->dst.rt_next; /* @@ -1093,11 +1093,9 @@ restart: spin_unlock_bh(rt_hash_lock_addr(hash)); rt_drop(rt); - if (rp) - *rp = rth; - else + if (skb) skb_dst_set(skb, &rth->dst); - return 0; + return rth; } if (!atomic_read(&rth->dst.__refcnt)) { @@ -1138,7 +1136,7 @@ restart: rt_emergency_hash_rebuild(net); spin_unlock_bh(rt_hash_lock_addr(hash)); - hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, + hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, ifindex, rt_genid(net)); goto restart; } @@ -1154,7 +1152,7 @@ restart: if (err != -ENOBUFS) { rt_drop(rt); - return err; + return ERR_PTR(err); } /* Neighbour tables are full and nothing @@ -1175,7 +1173,7 @@ restart: if (net_ratelimit()) printk(KERN_WARNING "ipv4: Neighbour table overflow.\n"); rt_drop(rt); - return -ENOBUFS; + return ERR_PTR(-ENOBUFS); } } @@ -1201,11 +1199,9 @@ restart: spin_unlock_bh(rt_hash_lock_addr(hash)); skip_hashing: - if (rp) - *rp = rt; - else + if (skb) skb_dst_set(skb, &rt->dst); - return 0; + return rt; } static atomic_t __rt_peer_genid = ATOMIC_INIT(0); @@ -1348,12 +1344,12 @@ static struct dst_entry *ipv4_negative_advice(struct dst_entry *dst) ip_rt_put(rt); ret = NULL; } else if (rt->rt_flags & RTCF_REDIRECTED) { - unsigned hash = rt_hash(rt->fl.fl4_dst, rt->fl.fl4_src, - rt->fl.oif, + unsigned hash = rt_hash(rt->rt_key_dst, rt->rt_key_src, + rt->rt_oif, rt_genid(dev_net(dst->dev))); #if RT_CACHE_DEBUG >= 1 printk(KERN_DEBUG "ipv4_negative_advice: redirect to %pI4/%02x dropped\n", - &rt->rt_dst, rt->fl.fl4_tos); + &rt->rt_dst, rt->rt_tos); #endif rt_del(hash, rt); ret = NULL; @@ -1701,8 +1697,17 @@ void ip_rt_get_source(u8 *addr, struct rtable *rt) if (rt_is_output_route(rt)) src = rt->rt_src; else { + struct flowi fl = { + .fl4_dst = rt->rt_key_dst, + .fl4_src = rt->rt_key_src, + .fl4_tos = rt->rt_tos, + .oif = rt->rt_oif, + .iif = rt->rt_iif, + .mark = rt->rt_mark, + }; + rcu_read_lock(); - if (fib_lookup(dev_net(rt->dst.dev), &rt->fl, &res) == 0) + if (fib_lookup(dev_net(rt->dst.dev), &fl, &res) == 0) src = FIB_RES_PREFSRC(res); else src = inet_select_addr(rt->dst.dev, rt->rt_gateway, @@ -1752,7 +1757,8 @@ static unsigned int ipv4_default_mtu(const struct dst_entry *dst) return mtu; } -static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) +static void rt_init_metrics(struct rtable *rt, const struct flowi *oldflp, + struct fib_info *fi) { struct inet_peer *peer; int create = 0; @@ -1760,12 +1766,12 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) /* If a peer entry exists for this destination, we must hook * it up in order to get at cached metrics. */ - if (rt->fl.flags & FLOWI_FLAG_PRECOW_METRICS) + if (oldflp && (oldflp->flags & FLOWI_FLAG_PRECOW_METRICS)) create = 1; - rt_bind_peer(rt, create); - peer = rt->peer; + rt->peer = peer = inet_getpeer_v4(rt->rt_dst, create); if (peer) { + rt->rt_peer_genid = rt_peer_genid(); if (inet_metrics_new(peer)) memcpy(peer->metrics, fi->fib_metrics, sizeof(u32) * RTAX_MAX); @@ -1787,7 +1793,8 @@ static void rt_init_metrics(struct rtable *rt, struct fib_info *fi) } } -static void rt_set_nexthop(struct rtable *rt, const struct fib_result *res, +static void rt_set_nexthop(struct rtable *rt, const struct flowi *oldflp, + const struct fib_result *res, struct fib_info *fi, u16 type, u32 itag) { struct dst_entry *dst = &rt->dst; @@ -1796,7 +1803,7 @@ static void rt_set_nexthop(struct rtable *rt, const struct fib_result *res, if (FIB_RES_GW(*res) && FIB_RES_NH(*res).nh_scope == RT_SCOPE_LINK) rt->rt_gateway = FIB_RES_GW(*res); - rt_init_metrics(rt, fi); + rt_init_metrics(rt, oldflp, fi); #ifdef CONFIG_IP_ROUTE_CLASSID dst->tclassid = FIB_RES_NH(*res).nh_tclassid; #endif @@ -1865,20 +1872,19 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, rth->dst.output = ip_rt_bug; - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; #ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif - rth->rt_iif = - rth->fl.iif = dev->ifindex; + rth->rt_iif = dev->ifindex; rth->dst.dev = init_net.loopback_dev; dev_hold(rth->dst.dev); - rth->fl.oif = 0; + rth->rt_oif = 0; rth->rt_gateway = daddr; rth->rt_spec_dst= spec_dst; rth->rt_genid = rt_genid(dev_net(dev)); @@ -1896,7 +1902,10 @@ static int ip_route_input_mc(struct sk_buff *skb, __be32 daddr, __be32 saddr, RT_CACHE_STAT_INC(in_slow_mc); hash = rt_hash(daddr, saddr, dev->ifindex, rt_genid(dev_net(dev))); - return rt_intern_hash(hash, rth, NULL, skb, dev->ifindex); + rth = rt_intern_hash(hash, rth, skb, dev->ifindex); + err = 0; + if (IS_ERR(rth)) + err = PTR_ERR(rth); e_nobufs: return -ENOBUFS; @@ -2000,25 +2009,24 @@ static int __mkroute_input(struct sk_buff *skb, goto cleanup; } - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; rth->rt_gateway = daddr; - rth->rt_iif = - rth->fl.iif = in_dev->dev->ifindex; + rth->rt_iif = in_dev->dev->ifindex; rth->dst.dev = (out_dev)->dev; dev_hold(rth->dst.dev); - rth->fl.oif = 0; + rth->rt_oif = 0; rth->rt_spec_dst= spec_dst; rth->dst.input = ip_forward; rth->dst.output = ip_output; rth->rt_genid = rt_genid(dev_net(rth->dst.dev)); - rt_set_nexthop(rth, res, res->fi, res->type, itag); + rt_set_nexthop(rth, NULL, res, res->fi, res->type, itag); rth->rt_flags = flags; @@ -2051,7 +2059,10 @@ static int ip_mkroute_input(struct sk_buff *skb, /* put it into the cache */ hash = rt_hash(daddr, saddr, fl->iif, rt_genid(dev_net(rth->dst.dev))); - return rt_intern_hash(hash, rth, NULL, skb, fl->iif); + rth = rt_intern_hash(hash, rth, skb, fl->iif); + if (IS_ERR(rth)) + return PTR_ERR(rth); + return 0; } /* @@ -2170,17 +2181,16 @@ local_input: rth->dst.output= ip_rt_bug; rth->rt_genid = rt_genid(net); - rth->fl.fl4_dst = daddr; + rth->rt_key_dst = daddr; rth->rt_dst = daddr; - rth->fl.fl4_tos = tos; - rth->fl.mark = skb->mark; - rth->fl.fl4_src = saddr; + rth->rt_tos = tos; + rth->rt_mark = skb->mark; + rth->rt_key_src = saddr; rth->rt_src = saddr; #ifdef CONFIG_IP_ROUTE_CLASSID rth->dst.tclassid = itag; #endif - rth->rt_iif = - rth->fl.iif = dev->ifindex; + rth->rt_iif = dev->ifindex; rth->dst.dev = net->loopback_dev; dev_hold(rth->dst.dev); rth->rt_gateway = daddr; @@ -2194,7 +2204,10 @@ local_input: } rth->rt_type = res.type; hash = rt_hash(daddr, saddr, fl.iif, rt_genid(net)); - err = rt_intern_hash(hash, rth, NULL, skb, fl.iif); + rth = rt_intern_hash(hash, rth, skb, fl.iif); + err = 0; + if (IS_ERR(rth)) + err = PTR_ERR(rth); goto out; no_route: @@ -2256,12 +2269,12 @@ int ip_route_input_common(struct sk_buff *skb, __be32 daddr, __be32 saddr, for (rth = rcu_dereference(rt_hash_table[hash].chain); rth; rth = rcu_dereference(rth->dst.rt_next)) { - if ((((__force u32)rth->fl.fl4_dst ^ (__force u32)daddr) | - ((__force u32)rth->fl.fl4_src ^ (__force u32)saddr) | - (rth->fl.iif ^ iif) | - rth->fl.oif | - (rth->fl.fl4_tos ^ tos)) == 0 && - rth->fl.mark == skb->mark && + if ((((__force u32)rth->rt_key_dst ^ (__force u32)daddr) | + ((__force u32)rth->rt_key_src ^ (__force u32)saddr) | + (rth->rt_iif ^ iif) | + rth->rt_oif | + (rth->rt_tos ^ tos)) == 0 && + rth->rt_mark == skb->mark && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { if (noref) { @@ -2369,14 +2382,14 @@ static struct rtable *__mkroute_output(const struct fib_result *res, if (!rth) return ERR_PTR(-ENOBUFS); - rth->fl.fl4_dst = oldflp->fl4_dst; - rth->fl.fl4_tos = tos; - rth->fl.fl4_src = oldflp->fl4_src; - rth->fl.oif = oldflp->oif; - rth->fl.mark = oldflp->mark; + rth->rt_key_dst = oldflp->fl4_dst; + rth->rt_tos = tos; + rth->rt_key_src = oldflp->fl4_src; + rth->rt_oif = oldflp->oif; + rth->rt_mark = oldflp->mark; rth->rt_dst = fl->fl4_dst; rth->rt_src = fl->fl4_src; - rth->rt_iif = oldflp->oif ? : dev_out->ifindex; + rth->rt_iif = 0; /* get references to the devices that are to be hold by the routing cache entry */ rth->dst.dev = dev_out; @@ -2411,7 +2424,7 @@ static struct rtable *__mkroute_output(const struct fib_result *res, #endif } - rt_set_nexthop(rth, res, fi, type, 0); + rt_set_nexthop(rth, oldflp, res, fi, type, 0); rth->rt_flags = flags; return rth; @@ -2422,33 +2435,33 @@ static struct rtable *__mkroute_output(const struct fib_result *res, * called with rcu_read_lock(); */ -static int ip_route_output_slow(struct net *net, struct rtable **rp, - const struct flowi *oldflp) +static struct rtable *ip_route_output_slow(struct net *net, + const struct flowi *oldflp) { u32 tos = RT_FL_TOS(oldflp); - struct flowi fl = { .fl4_dst = oldflp->fl4_dst, - .fl4_src = oldflp->fl4_src, - .fl4_tos = tos & IPTOS_RT_MASK, - .fl4_scope = ((tos & RTO_ONLINK) ? - RT_SCOPE_LINK : RT_SCOPE_UNIVERSE), - .mark = oldflp->mark, - .iif = net->loopback_dev->ifindex, - .oif = oldflp->oif }; + struct flowi fl; struct fib_result res; unsigned int flags = 0; struct net_device *dev_out = NULL; struct rtable *rth; - int err; - res.fi = NULL; #ifdef CONFIG_IP_MULTIPLE_TABLES res.r = NULL; #endif + fl.oif = oldflp->oif; + fl.iif = net->loopback_dev->ifindex; + fl.mark = oldflp->mark; + fl.fl4_dst = oldflp->fl4_dst; + fl.fl4_src = oldflp->fl4_src; + fl.fl4_tos = tos & IPTOS_RT_MASK; + fl.fl4_scope = ((tos & RTO_ONLINK) ? + RT_SCOPE_LINK : RT_SCOPE_UNIVERSE); + rcu_read_lock(); if (oldflp->fl4_src) { - err = -EINVAL; + rth = ERR_PTR(-EINVAL); if (ipv4_is_multicast(oldflp->fl4_src) || ipv4_is_lbcast(oldflp->fl4_src) || ipv4_is_zeronet(oldflp->fl4_src)) @@ -2499,13 +2512,13 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, if (oldflp->oif) { dev_out = dev_get_by_index_rcu(net, oldflp->oif); - err = -ENODEV; + rth = ERR_PTR(-ENODEV); if (dev_out == NULL) goto out; /* RACE: Check return value of inet_select_addr instead. */ if (!(dev_out->flags & IFF_UP) || !__in_dev_get_rcu(dev_out)) { - err = -ENETUNREACH; + rth = ERR_PTR(-ENETUNREACH); goto out; } if (ipv4_is_local_multicast(oldflp->fl4_dst) || @@ -2563,7 +2576,7 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, res.type = RTN_UNICAST; goto make_route; } - err = -ENETUNREACH; + rth = ERR_PTR(-ENETUNREACH); goto out; } @@ -2598,23 +2611,20 @@ static int ip_route_output_slow(struct net *net, struct rtable **rp, make_route: rth = __mkroute_output(&res, &fl, oldflp, dev_out, flags); - if (IS_ERR(rth)) - err = PTR_ERR(rth); - else { + if (!IS_ERR(rth)) { unsigned int hash; hash = rt_hash(oldflp->fl4_dst, oldflp->fl4_src, oldflp->oif, rt_genid(dev_net(dev_out))); - err = rt_intern_hash(hash, rth, rp, NULL, oldflp->oif); + rth = rt_intern_hash(hash, rth, NULL, oldflp->oif); } out: rcu_read_unlock(); - return err; + return rth; } -int __ip_route_output_key(struct net *net, struct rtable **rp, - const struct flowi *flp) +struct rtable *__ip_route_output_key(struct net *net, const struct flowi *flp) { struct rtable *rth; unsigned int hash; @@ -2627,27 +2637,26 @@ int __ip_route_output_key(struct net *net, struct rtable **rp, rcu_read_lock_bh(); for (rth = rcu_dereference_bh(rt_hash_table[hash].chain); rth; rth = rcu_dereference_bh(rth->dst.rt_next)) { - if (rth->fl.fl4_dst == flp->fl4_dst && - rth->fl.fl4_src == flp->fl4_src && + if (rth->rt_key_dst == flp->fl4_dst && + rth->rt_key_src == flp->fl4_src && rt_is_output_route(rth) && - rth->fl.oif == flp->oif && - rth->fl.mark == flp->mark && - !((rth->fl.fl4_tos ^ flp->fl4_tos) & + rth->rt_oif == flp->oif && + rth->rt_mark == flp->mark && + !((rth->rt_tos ^ flp->fl4_tos) & (IPTOS_RT_MASK | RTO_ONLINK)) && net_eq(dev_net(rth->dst.dev), net) && !rt_is_expired(rth)) { dst_use(&rth->dst, jiffies); RT_CACHE_STAT_INC(out_hit); rcu_read_unlock_bh(); - *rp = rth; - return 0; + return rth; } RT_CACHE_STAT_INC(out_hlist_search); } rcu_read_unlock_bh(); slow_output: - return ip_route_output_slow(net, rp, flp); + return ip_route_output_slow(net, flp); } EXPORT_SYMBOL_GPL(__ip_route_output_key); @@ -2675,12 +2684,10 @@ static struct dst_ops ipv4_dst_blackhole_ops = { .update_pmtu = ipv4_rt_blackhole_update_pmtu, }; - -static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi *flp) +struct dst_entry *ipv4_blackhole_route(struct net *net, struct dst_entry *dst_orig) { - struct rtable *ort = *rp; - struct rtable *rt = (struct rtable *) - dst_alloc(&ipv4_dst_blackhole_ops, 1); + struct rtable *rt = dst_alloc(&ipv4_dst_blackhole_ops, 1); + struct rtable *ort = (struct rtable *) dst_orig; if (rt) { struct dst_entry *new = &rt->dst; @@ -2694,7 +2701,12 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi if (new->dev) dev_hold(new->dev); - rt->fl = ort->fl; + rt->rt_key_dst = ort->rt_key_dst; + rt->rt_key_src = ort->rt_key_src; + rt->rt_tos = ort->rt_tos; + rt->rt_iif = ort->rt_iif; + rt->rt_oif = ort->rt_oif; + rt->rt_mark = ort->rt_mark; rt->rt_genid = rt_genid(net); rt->rt_flags = ort->rt_flags; @@ -2714,42 +2726,31 @@ static int ipv4_dst_blackhole(struct net *net, struct rtable **rp, struct flowi dst_free(new); } - dst_release(&(*rp)->dst); - *rp = rt; - return rt ? 0 : -ENOMEM; + dst_release(dst_orig); + + return rt ? &rt->dst : ERR_PTR(-ENOMEM); } -int ip_route_output_flow(struct net *net, struct rtable **rp, struct flowi *flp, - struct sock *sk, int flags) +struct rtable *ip_route_output_flow(struct net *net, struct flowi *flp, + struct sock *sk) { - int err; + struct rtable *rt = __ip_route_output_key(net, flp); - if ((err = __ip_route_output_key(net, rp, flp)) != 0) - return err; + if (IS_ERR(rt)) + return rt; if (flp->proto) { if (!flp->fl4_src) - flp->fl4_src = (*rp)->rt_src; + flp->fl4_src = rt->rt_src; if (!flp->fl4_dst) - flp->fl4_dst = (*rp)->rt_dst; - err = __xfrm_lookup(net, (struct dst_entry **)rp, flp, sk, - flags ? XFRM_LOOKUP_WAIT : 0); - if (err == -EREMOTE) - err = ipv4_dst_blackhole(net, rp, flp); - - return err; + flp->fl4_dst = rt->rt_dst; + rt = (struct rtable *) xfrm_lookup(net, &rt->dst, flp, sk, 0); } - return 0; + return rt; } EXPORT_SYMBOL_GPL(ip_route_output_flow); -int ip_route_output_key(struct net *net, struct rtable **rp, struct flowi *flp) -{ - return ip_route_output_flow(net, rp, flp, NULL, 0); -} -EXPORT_SYMBOL(ip_route_output_key); - static int rt_fill_info(struct net *net, struct sk_buff *skb, u32 pid, u32 seq, int event, int nowait, unsigned int flags) @@ -2768,7 +2769,7 @@ static int rt_fill_info(struct net *net, r->rtm_family = AF_INET; r->rtm_dst_len = 32; r->rtm_src_len = 0; - r->rtm_tos = rt->fl.fl4_tos; + r->rtm_tos = rt->rt_tos; r->rtm_table = RT_TABLE_MAIN; NLA_PUT_U32(skb, RTA_TABLE, RT_TABLE_MAIN); r->rtm_type = rt->rt_type; @@ -2780,9 +2781,9 @@ static int rt_fill_info(struct net *net, NLA_PUT_BE32(skb, RTA_DST, rt->rt_dst); - if (rt->fl.fl4_src) { + if (rt->rt_key_src) { r->rtm_src_len = 32; - NLA_PUT_BE32(skb, RTA_SRC, rt->fl.fl4_src); + NLA_PUT_BE32(skb, RTA_SRC, rt->rt_key_src); } if (rt->dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->dst.dev->ifindex); @@ -2792,7 +2793,7 @@ static int rt_fill_info(struct net *net, #endif if (rt_is_input_route(rt)) NLA_PUT_BE32(skb, RTA_PREFSRC, rt->rt_spec_dst); - else if (rt->rt_src != rt->fl.fl4_src) + else if (rt->rt_src != rt->rt_key_src) NLA_PUT_BE32(skb, RTA_PREFSRC, rt->rt_src); if (rt->rt_dst != rt->rt_gateway) @@ -2801,8 +2802,8 @@ static int rt_fill_info(struct net *net, if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0) goto nla_put_failure; - if (rt->fl.mark) - NLA_PUT_BE32(skb, RTA_MARK, rt->fl.mark); + if (rt->rt_mark) + NLA_PUT_BE32(skb, RTA_MARK, rt->rt_mark); error = rt->dst.error; expires = (rt->peer && rt->peer->pmtu_expires) ? @@ -2836,7 +2837,7 @@ static int rt_fill_info(struct net *net, } } else #endif - NLA_PUT_U32(skb, RTA_IIF, rt->fl.iif); + NLA_PUT_U32(skb, RTA_IIF, rt->rt_iif); } if (rtnl_put_cacheinfo(skb, &rt->dst, id, ts, tsage, @@ -2917,7 +2918,11 @@ static int inet_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void .oif = tb[RTA_OIF] ? nla_get_u32(tb[RTA_OIF]) : 0, .mark = mark, }; - err = ip_route_output_key(net, &rt, &fl); + rt = ip_route_output_key(net, &fl); + + err = 0; + if (IS_ERR(rt)) + err = PTR_ERR(rt); } if (err) diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 47519205a01..0ad6ddf638a 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -355,7 +355,8 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb, .fl_ip_sport = th->dest, .fl_ip_dport = th->source }; security_req_classify_flow(req, &fl); - if (ip_route_output_key(sock_net(sk), &rt, &fl)) { + rt = ip_route_output_key(sock_net(sk), &fl); + if (IS_ERR(rt)) { reqsk_free(req); goto out; } diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2f692cefd3b..08ea735b9d7 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1222,7 +1222,7 @@ static int tcp_check_dsack(struct sock *sk, struct sk_buff *ack_skb, } /* D-SACK for already forgotten data... Do dumb counting. */ - if (dup_sack && + if (dup_sack && tp->undo_marker && tp->undo_retrans && !after(end_seq_0, prior_snd_una) && after(end_seq_0, tp->undo_marker)) tp->undo_retrans--; @@ -1299,7 +1299,8 @@ static u8 tcp_sacktag_one(struct sk_buff *skb, struct sock *sk, /* Account D-SACK for retransmitted packet. */ if (dup_sack && (sacked & TCPCB_RETRANS)) { - if (after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) + if (tp->undo_marker && tp->undo_retrans && + after(TCP_SKB_CB(skb)->end_seq, tp->undo_marker)) tp->undo_retrans--; if (sacked & TCPCB_SACKED_ACKED) state->reord = min(fack_count, state->reord); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 27a0cc8cc88..f7e6c2c2d2b 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -152,7 +152,6 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) __be16 orig_sport, orig_dport; struct rtable *rt; __be32 daddr, nexthop; - int tmp; int err; if (addr_len < sizeof(struct sockaddr_in)) @@ -170,14 +169,15 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) orig_sport = inet->inet_sport; orig_dport = usin->sin_port; - tmp = ip_route_connect(&rt, nexthop, inet->inet_saddr, - RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, - IPPROTO_TCP, - orig_sport, orig_dport, sk, 1); - if (tmp < 0) { - if (tmp == -ENETUNREACH) + rt = ip_route_connect(nexthop, inet->inet_saddr, + RT_CONN_FLAGS(sk), sk->sk_bound_dev_if, + IPPROTO_TCP, + orig_sport, orig_dport, sk, true); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + if (err == -ENETUNREACH) IP_INC_STATS_BH(sock_net(sk), IPSTATS_MIB_OUTNOROUTES); - return tmp; + return err; } if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) { @@ -236,12 +236,14 @@ int tcp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) if (err) goto failure; - err = ip_route_newports(&rt, IPPROTO_TCP, - orig_sport, orig_dport, - inet->inet_sport, inet->inet_dport, sk); - if (err) + rt = ip_route_newports(rt, IPPROTO_TCP, + orig_sport, orig_dport, + inet->inet_sport, inet->inet_dport, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; goto failure; - + } /* OK, now commit destination to socket. */ sk->sk_gso_type = SKB_GSO_TCPV4; sk_setup_caps(sk, &rt->dst); diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 406f320336e..dfa5beb0c1c 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2162,7 +2162,7 @@ int tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb) if (!tp->retrans_stamp) tp->retrans_stamp = TCP_SKB_CB(skb)->when; - tp->undo_retrans++; + tp->undo_retrans += tcp_skb_pcount(skb); /* snd_nxt is stored to detect loss of retransmitted segment, * see tcp_input.c tcp_sacktag_write_queue(). diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index d37baaa1dbe..c9a73e5b26a 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -663,75 +663,72 @@ void udp_flush_pending_frames(struct sock *sk) EXPORT_SYMBOL(udp_flush_pending_frames); /** - * udp4_hwcsum_outgoing - handle outgoing HW checksumming - * @sk: socket we are sending on + * udp4_hwcsum - handle outgoing HW checksumming * @skb: sk_buff containing the filled-in UDP header * (checksum field must be zeroed out) + * @src: source IP address + * @dst: destination IP address */ -static void udp4_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb, - __be32 src, __be32 dst, int len) +static void udp4_hwcsum(struct sk_buff *skb, __be32 src, __be32 dst) { - unsigned int offset; struct udphdr *uh = udp_hdr(skb); + struct sk_buff *frags = skb_shinfo(skb)->frag_list; + int offset = skb_transport_offset(skb); + int len = skb->len - offset; + int hlen = len; __wsum csum = 0; - if (skb_queue_len(&sk->sk_write_queue) == 1) { + if (!frags) { /* * Only one fragment on the socket. */ skb->csum_start = skb_transport_header(skb) - skb->head; skb->csum_offset = offsetof(struct udphdr, check); - uh->check = ~csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, 0); + uh->check = ~csum_tcpudp_magic(src, dst, len, + IPPROTO_UDP, 0); } else { /* * HW-checksum won't work as there are two or more * fragments on the socket so that all csums of sk_buffs * should be together */ - offset = skb_transport_offset(skb); - skb->csum = skb_checksum(skb, offset, skb->len - offset, 0); + do { + csum = csum_add(csum, frags->csum); + hlen -= frags->len; + } while ((frags = frags->next)); + csum = skb_checksum(skb, offset, hlen, csum); skb->ip_summed = CHECKSUM_NONE; - skb_queue_walk(&sk->sk_write_queue, skb) { - csum = csum_add(csum, skb->csum); - } - uh->check = csum_tcpudp_magic(src, dst, len, IPPROTO_UDP, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; } } -/* - * Push out all pending data as one UDP datagram. Socket is locked. - */ -static int udp_push_pending_frames(struct sock *sk) +static int udp_send_skb(struct sk_buff *skb, __be32 daddr, __be32 dport) { - struct udp_sock *up = udp_sk(sk); + struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); - struct flowi *fl = &inet->cork.fl; - struct sk_buff *skb; struct udphdr *uh; + struct rtable *rt = (struct rtable *)skb_dst(skb); int err = 0; int is_udplite = IS_UDPLITE(sk); + int offset = skb_transport_offset(skb); + int len = skb->len - offset; __wsum csum = 0; - /* Grab the skbuff where UDP header space exists. */ - if ((skb = skb_peek(&sk->sk_write_queue)) == NULL) - goto out; - /* * Create a UDP header */ uh = udp_hdr(skb); - uh->source = fl->fl_ip_sport; - uh->dest = fl->fl_ip_dport; - uh->len = htons(up->len); + uh->source = inet->inet_sport; + uh->dest = dport; + uh->len = htons(len); uh->check = 0; if (is_udplite) /* UDP-Lite */ - csum = udplite_csum_outgoing(sk, skb); + csum = udplite_csum(skb); else if (sk->sk_no_check == UDP_CSUM_NOXMIT) { /* UDP csum disabled */ @@ -740,20 +737,20 @@ static int udp_push_pending_frames(struct sock *sk) } else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ - udp4_hwcsum_outgoing(sk, skb, fl->fl4_src, fl->fl4_dst, up->len); + udp4_hwcsum(skb, rt->rt_src, daddr); goto send; - } else /* `normal' UDP */ - csum = udp_csum_outgoing(sk, skb); + } else + csum = udp_csum(skb); /* add protocol-dependent pseudo-header */ - uh->check = csum_tcpudp_magic(fl->fl4_src, fl->fl4_dst, up->len, + uh->check = csum_tcpudp_magic(rt->rt_src, daddr, len, sk->sk_protocol, csum); if (uh->check == 0) uh->check = CSUM_MANGLED_0; send: - err = ip_push_pending_frames(sk); + err = ip_send_skb(skb); if (err) { if (err == -ENOBUFS && !inet->recverr) { UDP_INC_STATS_USER(sock_net(sk), @@ -763,6 +760,26 @@ send: } else UDP_INC_STATS_USER(sock_net(sk), UDP_MIB_OUTDATAGRAMS, is_udplite); + return err; +} + +/* + * Push out all pending data as one UDP datagram. Socket is locked. + */ +static int udp_push_pending_frames(struct sock *sk) +{ + struct udp_sock *up = udp_sk(sk); + struct inet_sock *inet = inet_sk(sk); + struct flowi *fl = &inet->cork.fl; + struct sk_buff *skb; + int err = 0; + + skb = ip_finish_skb(sk); + if (!skb) + goto out; + + err = udp_send_skb(skb, fl->fl4_dst, fl->fl_ip_dport); + out: up->len = 0; up->pending = 0; @@ -785,6 +802,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, int err, is_udplite = IS_UDPLITE(sk); int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); + struct sk_buff *skb; if (len > 0xFFFF) return -EMSGSIZE; @@ -799,6 +817,8 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, ipc.opt = NULL; ipc.tx_flags = 0; + getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; + if (up->pending) { /* * There are pending frames. @@ -894,14 +914,18 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, .fl4_src = saddr, .fl4_tos = tos, .proto = sk->sk_protocol, - .flags = inet_sk_flowi_flags(sk), + .flags = (inet_sk_flowi_flags(sk) | + FLOWI_FLAG_CAN_SLEEP), .fl_ip_sport = inet->inet_sport, - .fl_ip_dport = dport }; + .fl_ip_dport = dport + }; struct net *net = sock_net(sk); security_sk_classify_flow(sk, &fl); - err = ip_route_output_flow(net, &rt, &fl, sk, 1); - if (err) { + rt = ip_route_output_flow(net, &fl, sk); + if (IS_ERR(rt)) { + err = PTR_ERR(rt); + rt = NULL; if (err == -ENETUNREACH) IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES); goto out; @@ -923,6 +947,17 @@ back_from_confirm: if (!ipc.addr) daddr = ipc.addr = rt->rt_dst; + /* Lockless fast path for the non-corking case. */ + if (!corkreq) { + skb = ip_make_skb(sk, getfrag, msg->msg_iov, ulen, + sizeof(struct udphdr), &ipc, &rt, + msg->msg_flags); + err = PTR_ERR(skb); + if (skb && !IS_ERR(skb)) + err = udp_send_skb(skb, daddr, dport); + goto out; + } + lock_sock(sk); if (unlikely(up->pending)) { /* The socket is already corked while preparing it. */ @@ -944,7 +979,6 @@ back_from_confirm: do_append_data: up->len += ulen; - getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; err = ip_append_data(sk, getfrag, msg->msg_iov, ulen, sizeof(struct udphdr), &ipc, &rt, corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags); diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 63aa88efdce..c70c42e7e77 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -26,18 +26,16 @@ static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, .fl4_dst = daddr->a4, .fl4_tos = tos, }; - struct dst_entry *dst; struct rtable *rt; - int err; if (saddr) fl.fl4_src = saddr->a4; - err = __ip_route_output_key(net, &rt, &fl); - dst = &rt->dst; - if (err) - dst = ERR_PTR(err); - return dst; + rt = __ip_route_output_key(net, &fl); + if (!IS_ERR(rt)) + return &rt->dst; + + return ERR_CAST(rt); } static int xfrm4_get_saddr(struct net *net, @@ -72,7 +70,12 @@ static int xfrm4_fill_dst(struct xfrm_dst *xdst, struct net_device *dev, { struct rtable *rt = (struct rtable *)xdst->route; - xdst->u.rt.fl = *fl; + rt->rt_key_dst = fl->fl4_dst; + rt->rt_key_src = fl->fl4_src; + rt->rt_tos = fl->fl4_tos; + rt->rt_iif = fl->iif; + rt->rt_oif = fl->oif; + rt->rt_mark = fl->mark; xdst->u.dst.dev = dev; dev_hold(dev); @@ -234,6 +237,7 @@ static struct xfrm_policy_afinfo xfrm4_policy_afinfo = { .get_tos = xfrm4_get_tos, .init_path = xfrm4_init_path, .fill_dst = xfrm4_fill_dst, + .blackhole_route = ipv4_blackhole_route, }; #ifdef CONFIG_SYSCTL diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3194aa90987..a88b2e9d25f 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -644,9 +644,8 @@ EXPORT_SYMBOL(inet6_unregister_protosw); int inet6_sk_rebuild_header(struct sock *sk) { - int err; - struct dst_entry *dst; struct ipv6_pinfo *np = inet6_sk(sk); + struct dst_entry *dst; dst = __sk_dst_check(sk, np->dst_cookie); @@ -668,17 +667,11 @@ int inet6_sk_rebuild_header(struct sock *sk) final_p = fl6_update_dst(&fl, np->opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) { + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { sk->sk_route_caps = 0; - return err; - } - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) { - sk->sk_err_soft = -err; - return err; + sk->sk_err_soft = -PTR_ERR(dst); + return PTR_ERR(dst); } __ip6_dst_store(sk, dst, NULL, NULL); diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c index 320bdb877ee..be3a781c008 100644 --- a/net/ipv6/datagram.c +++ b/net/ipv6/datagram.c @@ -162,18 +162,11 @@ ipv4_connected: opt = flowlabel ? flowlabel->opt : np->opt; final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + err = 0; + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } /* source address lookup done in ip6_dst_lookup */ diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index a31d91b04c8..55665956b3a 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -300,6 +300,68 @@ static void mip6_addr_swap(struct sk_buff *skb) static inline void mip6_addr_swap(struct sk_buff *skb) {} #endif +static struct dst_entry *icmpv6_route_lookup(struct net *net, struct sk_buff *skb, + struct sock *sk, struct flowi *fl) +{ + struct dst_entry *dst, *dst2; + struct flowi fl2; + int err; + + err = ip6_dst_lookup(sk, &dst, fl); + if (err) + return ERR_PTR(err); + + /* + * We won't send icmp if the destination is known + * anycast. + */ + if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { + LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); + dst_release(dst); + return ERR_PTR(-EINVAL); + } + + /* No need to clone since we're just using its address. */ + dst2 = dst; + + dst = xfrm_lookup(net, dst, fl, sk, 0); + if (!IS_ERR(dst)) { + if (dst != dst2) + return dst; + } else { + if (PTR_ERR(dst) == -EPERM) + dst = NULL; + else + return dst; + } + + err = xfrm_decode_session_reverse(skb, &fl2, AF_INET6); + if (err) + goto relookup_failed; + + err = ip6_dst_lookup(sk, &dst2, &fl2); + if (err) + goto relookup_failed; + + dst2 = xfrm_lookup(net, dst2, &fl2, sk, XFRM_LOOKUP_ICMP); + if (!IS_ERR(dst2)) { + dst_release(dst); + dst = dst2; + } else { + err = PTR_ERR(dst2); + if (err == -EPERM) { + dst_release(dst); + return dst2; + } else + goto relookup_failed; + } + +relookup_failed: + if (dst) + return dst; + return ERR_PTR(err); +} + /* * Send an ICMP message in response to a packet in error */ @@ -312,10 +374,8 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) struct ipv6_pinfo *np; struct in6_addr *saddr = NULL; struct dst_entry *dst; - struct dst_entry *dst2; struct icmp6hdr tmp_hdr; struct flowi fl; - struct flowi fl2; struct icmpv6_msg msg; int iif = 0; int addr_type = 0; @@ -408,57 +468,10 @@ void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst)) fl.oif = np->mcast_oif; - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = icmpv6_route_lookup(net, skb, sk, &fl); + if (IS_ERR(dst)) goto out; - /* - * We won't send icmp if the destination is known - * anycast. - */ - if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) { - LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n"); - goto out_dst_release; - } - - /* No need to clone since we're just using its address. */ - dst2 = dst; - - err = xfrm_lookup(net, &dst, &fl, sk, 0); - switch (err) { - case 0: - if (dst != dst2) - goto route_done; - break; - case -EPERM: - dst = NULL; - break; - default: - goto out; - } - - if (xfrm_decode_session_reverse(skb, &fl2, AF_INET6)) - goto relookup_failed; - - if (ip6_dst_lookup(sk, &dst2, &fl2)) - goto relookup_failed; - - err = xfrm_lookup(net, &dst2, &fl2, sk, XFRM_LOOKUP_ICMP); - switch (err) { - case 0: - dst_release(dst); - dst = dst2; - break; - case -EPERM: - goto out_dst_release; - default: -relookup_failed: - if (!dst) - goto out; - break; - } - -route_done: if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; else @@ -545,7 +558,8 @@ static void icmpv6_echo_reply(struct sk_buff *skb) err = ip6_dst_lookup(sk, &dst, &fl); if (err) goto out; - if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) + dst = xfrm_lookup(net, dst, &fl, sk, 0); + if (IS_ERR(dst)) goto out; if (ipv6_addr_is_multicast(&fl.fl6_dst)) diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c index d144e629d2b..d687e139733 100644 --- a/net/ipv6/inet6_connection_sock.c +++ b/net/ipv6/inet6_connection_sock.c @@ -74,13 +74,8 @@ struct dst_entry *inet6_csk_route_req(struct sock *sk, fl.fl_ip_sport = inet_rsk(req)->loc_port; security_req_classify_flow(req, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - return NULL; - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) return NULL; return dst; @@ -234,21 +229,13 @@ int inet6_csk_xmit(struct sk_buff *skb) dst = __inet6_csk_dst_check(sk, np->dst_cookie); if (dst == NULL) { - int err = ip6_dst_lookup(sk, &dst, &fl); - - if (err) { - sk->sk_err_soft = -err; - kfree_skb(skb); - return err; - } - - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) { + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); sk->sk_route_caps = 0; kfree_skb(skb); - return err; + return PTR_ERR(dst); } __inet6_csk_dst_store(sk, dst, NULL, NULL); diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 065b3f7614f..adaffaf8455 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1002,29 +1002,71 @@ int ip6_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) EXPORT_SYMBOL_GPL(ip6_dst_lookup); /** - * ip6_sk_dst_lookup - perform socket cached route lookup on flow + * ip6_dst_lookup_flow - perform route lookup on flow with ipsec + * @sk: socket which provides route info + * @fl: flow to lookup + * @final_dst: final destination address for ipsec lookup + * @can_sleep: we are in a sleepable context + * + * This function performs a route lookup on the given flow. + * + * It returns a valid dst pointer on success, or a pointer encoded + * error code. + */ +struct dst_entry *ip6_dst_lookup_flow(struct sock *sk, struct flowi *fl, + const struct in6_addr *final_dst, + bool can_sleep) +{ + struct dst_entry *dst = NULL; + int err; + + err = ip6_dst_lookup_tail(sk, &dst, fl); + if (err) + return ERR_PTR(err); + if (final_dst) + ipv6_addr_copy(&fl->fl6_dst, final_dst); + if (can_sleep) + fl->flags |= FLOWI_FLAG_CAN_SLEEP; + + return xfrm_lookup(sock_net(sk), dst, fl, sk, 0); +} +EXPORT_SYMBOL_GPL(ip6_dst_lookup_flow); + +/** + * ip6_sk_dst_lookup_flow - perform socket cached route lookup on flow * @sk: socket which provides the dst cache and route info - * @dst: pointer to dst_entry * for result * @fl: flow to lookup + * @final_dst: final destination address for ipsec lookup + * @can_sleep: we are in a sleepable context * * This function performs a route lookup on the given flow with the * possibility of using the cached route in the socket if it is valid. * It will take the socket dst lock when operating on the dst cache. * As a result, this function can only be used in process context. * - * It returns zero on success, or a standard errno code on error. + * It returns a valid dst pointer on success, or a pointer encoded + * error code. */ -int ip6_sk_dst_lookup(struct sock *sk, struct dst_entry **dst, struct flowi *fl) +struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi *fl, + const struct in6_addr *final_dst, + bool can_sleep) { - *dst = NULL; - if (sk) { - *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); - *dst = ip6_sk_dst_check(sk, *dst, fl); - } + struct dst_entry *dst = sk_dst_check(sk, inet6_sk(sk)->dst_cookie); + int err; - return ip6_dst_lookup_tail(sk, dst, fl); + dst = ip6_sk_dst_check(sk, dst, fl); + + err = ip6_dst_lookup_tail(sk, &dst, fl); + if (err) + return ERR_PTR(err); + if (final_dst) + ipv6_addr_copy(&fl->fl6_dst, final_dst); + if (can_sleep) + fl->flags |= FLOWI_FLAG_CAN_SLEEP; + + return xfrm_lookup(sock_net(sk), dst, fl, sk, 0); } -EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup); +EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); static inline int ip6_ufo_append_data(struct sock *sk, int getfrag(void *from, char *to, int offset, int len, @@ -1061,7 +1103,6 @@ static inline int ip6_ufo_append_data(struct sock *sk, skb->ip_summed = CHECKSUM_PARTIAL; skb->csum = 0; - sk->sk_sndmsg_off = 0; } err = skb_append_datato_frags(sk,skb, getfrag, from, diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 4f4483e697b..02730ef26b0 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -581,7 +581,8 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl4_dst = eiph->saddr; fl.fl4_tos = RT_TOS(eiph->tos); fl.proto = IPPROTO_IPIP; - if (ip_route_output_key(dev_net(skb->dev), &rt, &fl)) + rt = ip_route_output_key(dev_net(skb->dev), &fl); + if (IS_ERR(rt)) goto out; skb2->dev = rt->dst.dev; @@ -593,12 +594,14 @@ ip4ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl4_dst = eiph->daddr; fl.fl4_src = eiph->saddr; fl.fl4_tos = eiph->tos; - if (ip_route_output_key(dev_net(skb->dev), &rt, &fl) || + rt = ip_route_output_key(dev_net(skb->dev), &fl); + if (IS_ERR(rt) || rt->dst.dev->type != ARPHRD_TUNNEL) { - ip_rt_put(rt); + if (!IS_ERR(rt)) + ip_rt_put(rt); goto out; } - skb_dst_set(skb2, (struct dst_entry *)rt); + skb_dst_set(skb2, &rt->dst); } else { ip_rt_put(rt); if (ip_route_input(skb2, eiph->daddr, eiph->saddr, eiph->tos, @@ -903,8 +906,14 @@ static int ip6_tnl_xmit2(struct sk_buff *skb, else { dst = ip6_route_output(net, NULL, fl); - if (dst->error || xfrm_lookup(net, &dst, fl, NULL, 0) < 0) + if (dst->error) goto tx_err_link_failure; + dst = xfrm_lookup(net, dst, fl, NULL, 0); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + goto tx_err_link_failure; + } } tdev = dst->dev; diff --git a/net/ipv6/mcast.c b/net/ipv6/mcast.c index 49f986d626a..7b27d08ee28 100644 --- a/net/ipv6/mcast.c +++ b/net/ipv6/mcast.c @@ -1429,7 +1429,12 @@ static void mld_sendpack(struct sk_buff *skb) &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - err = xfrm_lookup(net, &dst, &fl, NULL, 0); + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + err = 0; + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; + } skb_dst_set(skb, dst); if (err) goto err_out; @@ -1796,9 +1801,11 @@ static void igmp6_send(struct in6_addr *addr, struct net_device *dev, int type) &ipv6_hdr(skb)->saddr, &ipv6_hdr(skb)->daddr, skb->dev->ifindex); - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto err_out; + } skb_dst_set(skb, dst); err = NF_HOOK(NFPROTO_IPV6, NF_INET_LOCAL_OUT, skb, NULL, skb->dev, diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 7254ce36400..9360d3be94f 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -529,8 +529,8 @@ void ndisc_send_skb(struct sk_buff *skb, return; } - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err < 0) { + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { kfree_skb(skb); return; } @@ -1542,8 +1542,8 @@ void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh, if (dst == NULL) return; - err = xfrm_lookup(net, &dst, &fl, NULL, 0); - if (err) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) return; rt = (struct rt6_info *) dst; diff --git a/net/ipv6/netfilter.c b/net/ipv6/netfilter.c index 35915e8617f..8d74116ae27 100644 --- a/net/ipv6/netfilter.c +++ b/net/ipv6/netfilter.c @@ -39,7 +39,8 @@ int ip6_route_me_harder(struct sk_buff *skb) if (!(IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) && xfrm_decode_session(skb, &fl, AF_INET6) == 0) { skb_dst_set(skb, NULL); - if (xfrm_lookup(net, &dst, &fl, skb->sk, 0)) + dst = xfrm_lookup(net, dst, &fl, skb->sk, 0); + if (IS_ERR(dst)) return -1; skb_dst_set(skb, dst); } diff --git a/net/ipv6/netfilter/ip6t_REJECT.c b/net/ipv6/netfilter/ip6t_REJECT.c index bf998feac14..91f6a61cefa 100644 --- a/net/ipv6/netfilter/ip6t_REJECT.c +++ b/net/ipv6/netfilter/ip6t_REJECT.c @@ -101,7 +101,8 @@ static void send_reset(struct net *net, struct sk_buff *oldskb) dst_release(dst); return; } - if (xfrm_lookup(net, &dst, &fl, NULL, 0)) + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) return; hh_len = (dst->dev->hard_header_len + 15)&~15; diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 364e8668338..dc29b07caf4 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -856,20 +856,11 @@ static int rawv6_sendmsg(struct kiocb *iocb, struct sock *sk, fl.oif = np->mcast_oif; security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } - if (hlimit < 0) { if (ipv6_addr_is_multicast(&fl.fl6_dst)) hlimit = np->mcast_hops; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 7e9443f835f..d55d00c2a82 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -870,11 +870,10 @@ struct dst_entry * ip6_route_output(struct net *net, struct sock *sk, EXPORT_SYMBOL(ip6_route_output); -int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl) +struct dst_entry *ip6_blackhole_route(struct net *net, struct dst_entry *dst_orig) { - struct rt6_info *ort = (struct rt6_info *) *dstp; - struct rt6_info *rt = (struct rt6_info *) - dst_alloc(&ip6_dst_blackhole_ops, 1); + struct rt6_info *rt = dst_alloc(&ip6_dst_blackhole_ops, 1); + struct rt6_info *ort = (struct rt6_info *) dst_orig; struct dst_entry *new = NULL; if (rt) { @@ -905,11 +904,9 @@ int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl dst_free(new); } - dst_release(*dstp); - *dstp = new; - return new ? 0 : -ENOMEM; + dst_release(dst_orig); + return new ? new : ERR_PTR(-ENOMEM); } -EXPORT_SYMBOL_GPL(ip6_dst_blackhole); /* * Destination cache support functions @@ -2025,12 +2022,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, if (IS_ERR(neigh)) { dst_free(&rt->dst); - /* We are casting this because that is the return - * value type. But an errno encoded pointer is the - * same regardless of the underlying pointer type, - * and that's what we are returning. So this is OK. - */ - return (struct rt6_info *) neigh; + return ERR_CAST(neigh); } rt->rt6i_nexthop = neigh; @@ -2602,14 +2594,16 @@ static int ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, void __user *buffer, size_t *lenp, loff_t *ppos) { - struct net *net = current->nsproxy->net_ns; - int delay = net->ipv6.sysctl.flush_delay; - if (write) { - proc_dointvec(ctl, write, buffer, lenp, ppos); - fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); - return 0; - } else + struct net *net; + int delay; + if (!write) return -EINVAL; + + net = (struct net *)ctl->extra1; + delay = net->ipv6.sysctl.flush_delay; + proc_dointvec(ctl, write, buffer, lenp, ppos); + fib6_run_gc(delay <= 0 ? ~0UL : (unsigned long)delay, net); + return 0; } ctl_table ipv6_route_table_template[] = { @@ -2696,6 +2690,7 @@ struct ctl_table * __net_init ipv6_route_sysctl_init(struct net *net) if (table) { table[0].data = &net->ipv6.sysctl.flush_delay; + table[0].extra1 = net; table[1].data = &net->ipv6.ip6_dst_ops.gc_thresh; table[2].data = &net->ipv6.sysctl.ip6_rt_max_size; table[3].data = &net->ipv6.sysctl.ip6_rt_gc_min_interval; diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index b1599a345c1..b8c8adbd7cf 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -738,7 +738,8 @@ static netdev_tx_t ipip6_tunnel_xmit(struct sk_buff *skb, .fl4_tos = RT_TOS(tos), .oif = tunnel->parms.link, .proto = IPPROTO_IPV6 }; - if (ip_route_output_key(dev_net(dev), &rt, &fl)) { + rt = ip_route_output_key(dev_net(dev), &fl); + if (IS_ERR(rt)) { dev->stats.tx_carrier_errors++; goto tx_error_icmp; } @@ -862,8 +863,9 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev) .fl4_tos = RT_TOS(iph->tos), .oif = tunnel->parms.link, .proto = IPPROTO_IPV6 }; - struct rtable *rt; - if (!ip_route_output_key(dev_net(dev), &rt, &fl)) { + struct rtable *rt = ip_route_output_key(dev_net(dev), &fl); + + if (!IS_ERR(rt)) { tdev = rt->dst.dev; ip_rt_put(rt); } diff --git a/net/ipv6/syncookies.c b/net/ipv6/syncookies.c index 09fd34f0dbf..0b4cf350631 100644 --- a/net/ipv6/syncookies.c +++ b/net/ipv6/syncookies.c @@ -243,12 +243,9 @@ struct sock *cookie_v6_check(struct sock *sk, struct sk_buff *skb) fl.fl_ip_dport = inet_rsk(req)->rmt_port; fl.fl_ip_sport = inet_sk(sk)->inet_sport; security_req_classify_flow(req, &fl); - if (ip6_dst_lookup(sk, &dst, &fl)) - goto out_free; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) goto out_free; } diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 1d0ab557090..e59a31c48ba 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -255,18 +255,10 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr, security_sk_classify_flow(sk, &fl); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto failure; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto failure; } if (saddr == NULL) { @@ -385,7 +377,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, np = inet6_sk(sk); if (type == ICMPV6_PKT_TOOBIG) { - struct dst_entry *dst = NULL; + struct dst_entry *dst; if (sock_owned_by_user(sk)) goto out; @@ -413,13 +405,9 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, fl.fl_ip_sport = inet->inet_sport; security_skb_classify_flow(skb, &fl); - if ((err = ip6_dst_lookup(sk, &dst, &fl))) { - sk->sk_err_soft = -err; - goto out; - } - - if ((err = xfrm_lookup(net, &dst, &fl, sk, 0)) < 0) { - sk->sk_err_soft = -err; + dst = ip6_dst_lookup_flow(sk, &fl, NULL, false); + if (IS_ERR(dst)) { + sk->sk_err_soft = -PTR_ERR(dst); goto out; } @@ -496,7 +484,7 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, struct in6_addr * final_p, final; struct flowi fl; struct dst_entry *dst; - int err = -1; + int err; memset(&fl, 0, sizeof(fl)); fl.proto = IPPROTO_TCP; @@ -512,15 +500,13 @@ static int tcp_v6_send_synack(struct sock *sk, struct request_sock *req, opt = np->opt; final_p = fl6_update_dst(&fl, opt, &final); - err = ip6_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_dst_lookup_flow(sk, &fl, final_p, false); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); goto done; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - if ((err = xfrm_lookup(sock_net(sk), &dst, &fl, sk, 0)) < 0) - goto done; - + } skb = tcp_make_synack(sk, dst, req, rvp); + err = -ENOMEM; if (skb) { __tcp_v6_send_check(skb, &treq->loc_addr, &treq->rmt_addr); @@ -1079,15 +1065,14 @@ static void tcp_v6_send_response(struct sk_buff *skb, u32 seq, u32 ack, u32 win, * Underlying function will use this to retrieve the network * namespace */ - if (!ip6_dst_lookup(ctl_sk, &dst, &fl)) { - if (xfrm_lookup(net, &dst, &fl, NULL, 0) >= 0) { - skb_dst_set(buff, dst); - ip6_xmit(ctl_sk, buff, &fl, NULL); - TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); - if (rst) - TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); - return; - } + dst = ip6_dst_lookup_flow(ctl_sk, &fl, NULL, false); + if (!IS_ERR(dst)) { + skb_dst_set(buff, dst); + ip6_xmit(ctl_sk, buff, &fl, NULL); + TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS); + if (rst) + TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS); + return; } kfree_skb(buff); diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index a419a787eb6..d86d7f67a59 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1125,18 +1125,11 @@ do_udp_sendmsg: security_sk_classify_flow(sk, &fl); - err = ip6_sk_dst_lookup(sk, &dst, &fl); - if (err) + dst = ip6_sk_dst_lookup_flow(sk, &fl, final_p, true); + if (IS_ERR(dst)) { + err = PTR_ERR(dst); + dst = NULL; goto out; - if (final_p) - ipv6_addr_copy(&fl.fl6_dst, final_p); - - err = __xfrm_lookup(sock_net(sk), &dst, &fl, sk, XFRM_LOOKUP_WAIT); - if (err < 0) { - if (err == -EREMOTE) - err = ip6_dst_blackhole(sk, &dst, &fl); - if (err < 0) - goto out; } if (hlimit < 0) { diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index c128ca1affe..48ce496802f 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -274,6 +274,7 @@ static struct xfrm_policy_afinfo xfrm6_policy_afinfo = { .get_tos = xfrm6_get_tos, .init_path = xfrm6_init_path, .fill_dst = xfrm6_fill_dst, + .blackhole_route = ip6_blackhole_route, }; static int __init xfrm6_policy_init(void) diff --git a/net/key/af_key.c b/net/key/af_key.c index 7fb54577f5b..7db86ffcf07 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2560,7 +2560,7 @@ static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, } #else static int pfkey_migrate(struct sock *sk, struct sk_buff *skb, - struct sadb_msg *hdr, void **ext_hdrs) + const struct sadb_msg *hdr, void * const *ext_hdrs) { return -ENOPROTOOPT; } diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c index 110efb704c9..2a698ff89db 100644 --- a/net/l2tp/l2tp_ip.c +++ b/net/l2tp/l2tp_ip.c @@ -320,11 +320,12 @@ static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len if (ipv4_is_multicast(lsa->l2tp_addr.s_addr)) goto out; - rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr, + rt = ip_route_connect(lsa->l2tp_addr.s_addr, saddr, RT_CONN_FLAGS(sk), oif, IPPROTO_L2TP, - 0, 0, sk, 1); - if (rc) { + 0, 0, sk, true); + if (IS_ERR(rt)) { + rc = PTR_ERR(rt); if (rc == -ENETUNREACH) IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES); goto out; @@ -489,7 +490,8 @@ static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *m * itself out. */ security_sk_classify_flow(sk, &fl); - if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0)) + rt = ip_route_output_flow(sock_net(sk), &fl, sk); + if (IS_ERR(rt)) goto no_route; } sk_setup_caps(sk, &rt->dst); diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig index dbf5e4006bc..513f85cc2ae 100644 --- a/net/mac80211/Kconfig +++ b/net/mac80211/Kconfig @@ -78,7 +78,7 @@ config MAC80211_RC_DEFAULT endif comment "Some wireless drivers require a rate control algorithm" - depends on MAC80211_HAS_RC=n + depends on MAC80211 && MAC80211_HAS_RC=n config MAC80211_MESH bool "Enable mac80211 mesh networking (pre-802.11s) support" diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 140503d4c97..7b701dcddb5 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -316,6 +316,17 @@ static int ieee80211_config_default_mgmt_key(struct wiphy *wiphy, return 0; } +static void rate_idx_to_bitrate(struct rate_info *rate, struct sta_info *sta, int idx) +{ + if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { + struct ieee80211_supported_band *sband; + sband = sta->local->hw.wiphy->bands[ + sta->local->hw.conf.channel->band]; + rate->legacy = sband->bitrates[idx].bitrate; + } else + rate->mcs = idx; +} + static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) { struct ieee80211_sub_if_data *sdata = sta->sdata; @@ -330,6 +341,7 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) STATION_INFO_TX_RETRIES | STATION_INFO_TX_FAILED | STATION_INFO_TX_BITRATE | + STATION_INFO_RX_BITRATE | STATION_INFO_RX_DROP_MISC; sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); @@ -355,15 +367,16 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sinfo->txrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; if (sta->last_tx_rate.flags & IEEE80211_TX_RC_SHORT_GI) sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + rate_idx_to_bitrate(&sinfo->txrate, sta, sta->last_tx_rate.idx); - if (!(sta->last_tx_rate.flags & IEEE80211_TX_RC_MCS)) { - struct ieee80211_supported_band *sband; - sband = sta->local->hw.wiphy->bands[ - sta->local->hw.conf.channel->band]; - sinfo->txrate.legacy = - sband->bitrates[sta->last_tx_rate.idx].bitrate; - } else - sinfo->txrate.mcs = sta->last_tx_rate.idx; + sinfo->rxrate.flags = 0; + if (sta->last_rx_rate_flag & RX_FLAG_HT) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; + if (sta->last_rx_rate_flag & RX_FLAG_40MHZ) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_SHORT_GI) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + rate_idx_to_bitrate(&sinfo->rxrate, sta, sta->last_rx_rate_idx); if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH @@ -1800,6 +1813,33 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct net_device *dev, *cookie = (unsigned long) skb; + if (is_offchan && local->ops->offchannel_tx) { + int ret; + + IEEE80211_SKB_CB(skb)->band = chan->band; + + mutex_lock(&local->mtx); + + if (local->hw_offchan_tx_cookie) { + mutex_unlock(&local->mtx); + return -EBUSY; + } + + /* TODO: bitrate control, TX processing? */ + ret = drv_offchannel_tx(local, skb, chan, channel_type, wait); + + if (ret == 0) + local->hw_offchan_tx_cookie = *cookie; + mutex_unlock(&local->mtx); + + /* + * Allow driver to return 1 to indicate it wants to have the + * frame transmitted with a remain_on_channel + regular TX. + */ + if (ret != 1) + return ret; + } + if (is_offchan && local->ops->remain_on_channel) { unsigned int duration; int ret; @@ -1886,6 +1926,18 @@ static int ieee80211_mgmt_tx_cancel_wait(struct wiphy *wiphy, mutex_lock(&local->mtx); + if (local->ops->offchannel_tx_cancel_wait && + local->hw_offchan_tx_cookie == cookie) { + ret = drv_offchannel_tx_cancel_wait(local); + + if (!ret) + local->hw_offchan_tx_cookie = 0; + + mutex_unlock(&local->mtx); + + return ret; + } + if (local->ops->cancel_remain_on_channel) { cookie ^= 2; ret = ieee80211_cancel_remain_on_channel_hw(local, cookie); diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 78af32d4bc5..3729296f6f9 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -5,9 +5,9 @@ #include "ieee80211_i.h" #include "driver-trace.h" -static inline int drv_tx(struct ieee80211_local *local, struct sk_buff *skb) +static inline void drv_tx(struct ieee80211_local *local, struct sk_buff *skb) { - return local->ops->tx(&local->hw, skb); + local->ops->tx(&local->hw, skb); } static inline int drv_start(struct ieee80211_local *local) @@ -495,4 +495,35 @@ static inline int drv_cancel_remain_on_channel(struct ieee80211_local *local) return ret; } +static inline int drv_offchannel_tx(struct ieee80211_local *local, + struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait) +{ + int ret; + + might_sleep(); + + trace_drv_offchannel_tx(local, skb, chan, channel_type, wait); + ret = local->ops->offchannel_tx(&local->hw, skb, chan, + channel_type, wait); + trace_drv_return_int(local, ret); + + return ret; +} + +static inline int drv_offchannel_tx_cancel_wait(struct ieee80211_local *local) +{ + int ret; + + might_sleep(); + + trace_drv_offchannel_tx_cancel_wait(local); + ret = local->ops->offchannel_tx_cancel_wait(&local->hw); + trace_drv_return_int(local, ret); + + return ret; +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/driver-trace.h b/net/mac80211/driver-trace.h index e5cce19a7d6..520fe244489 100644 --- a/net/mac80211/driver-trace.h +++ b/net/mac80211/driver-trace.h @@ -884,6 +884,39 @@ DEFINE_EVENT(local_only_evt, drv_cancel_remain_on_channel, TP_ARGS(local) ); +TRACE_EVENT(drv_offchannel_tx, + TP_PROTO(struct ieee80211_local *local, struct sk_buff *skb, + struct ieee80211_channel *chan, + enum nl80211_channel_type channel_type, + unsigned int wait), + + TP_ARGS(local, skb, chan, channel_type, wait), + + TP_STRUCT__entry( + LOCAL_ENTRY + __field(int, center_freq) + __field(int, channel_type) + __field(unsigned int, wait) + ), + + TP_fast_assign( + LOCAL_ASSIGN; + __entry->center_freq = chan->center_freq; + __entry->channel_type = channel_type; + __entry->wait = wait; + ), + + TP_printk( + LOCAL_PR_FMT " freq:%dMHz, wait:%dms", + LOCAL_PR_ARG, __entry->center_freq, __entry->wait + ) +); + +DEFINE_EVENT(local_only_evt, drv_offchannel_tx_cancel_wait, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 75d679d75e6..b9e4b9bd217 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -66,6 +66,9 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, /* own MCS TX capabilities */ tx_mcs_set_cap = sband->ht_cap.mcs.tx_params; + /* Copy peer MCS TX capabilities, the driver might need them. */ + ht_cap->mcs.tx_params = ht_cap_ie->mcs.tx_params; + /* can we TX with MCS rates? */ if (!(tx_mcs_set_cap & IEEE80211_HT_MCS_TX_DEFINED)) return; @@ -79,7 +82,7 @@ void ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_supported_band *sband, max_tx_streams = IEEE80211_HT_MCS_TX_MAX_STREAMS; /* - * 802.11n D5.0 20.3.5 / 20.6 says: + * 802.11n-2009 20.3.5 / 20.6 says: * - indices 0 to 7 and 32 are single spatial stream * - 8 to 31 are multiple spatial streams using equal modulation * [8..15 for two streams, 16..23 for three and 24..31 for four] diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index a42aa61269e..3e81af1fce5 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -31,7 +31,6 @@ #define IEEE80211_IBSS_JOIN_TIMEOUT (7 * HZ) #define IEEE80211_IBSS_MERGE_INTERVAL (30 * HZ) -#define IEEE80211_IBSS_MERGE_DELAY 0x400000 #define IEEE80211_IBSS_INACTIVITY_LIMIT (60 * HZ) #define IEEE80211_IBSS_MAX_STA_ENTRIES 128 @@ -355,7 +354,7 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (memcmp(cbss->bssid, sdata->u.ibss.bssid, ETH_ALEN) == 0) goto put_bss; - if (rx_status->flag & RX_FLAG_TSFT) { + if (rx_status->flag & RX_FLAG_MACTIME_MPDU) { /* * For correct IBSS merging we need mactime; since mactime is * defined as the time the first data symbol of the frame hits @@ -397,10 +396,6 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, jiffies); #endif - /* give slow hardware some time to do the TSF sync */ - if (rx_timestamp < IEEE80211_IBSS_MERGE_DELAY) - goto put_bss; - if (beacon_timestamp > rx_timestamp) { #ifdef CONFIG_MAC80211_IBSS_DEBUG printk(KERN_DEBUG "%s: beacon TSF higher than " diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0a570a111a8..a4040170142 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -957,6 +957,7 @@ struct ieee80211_local { unsigned int hw_roc_duration; u32 hw_roc_cookie; bool hw_roc_for_tx; + unsigned long hw_offchan_tx_cookie; /* dummy netdev for use w/ NAPI */ struct net_device napi_dev; diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5a4e19b8803..4054399be90 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1236,6 +1236,7 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local) } mutex_unlock(&local->iflist_mtx); unregister_netdevice_many(&unreg_list); + list_del(&unreg_list); } static u32 ieee80211_idle_off(struct ieee80211_local *local, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 7b3f9df725b..cc984bd861c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -145,6 +145,9 @@ void ieee80211_sta_reset_conn_monitor(struct ieee80211_sub_if_data *sdata) { struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; + if (unlikely(!sdata->u.mgd.associated)) + return; + if (sdata->local->hw.flags & IEEE80211_HW_CONNECTION_MONITOR) return; @@ -738,9 +741,19 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) return; if ((local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK) && - (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) + (!(ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED))) { + netif_tx_stop_all_queues(sdata->dev); + /* + * Flush all the frames queued in the driver before + * going to power save + */ + drv_flush(local, false); ieee80211_send_nullfunc(local, sdata, 1); + /* Flush once again to get the tx status of nullfunc frame */ + drv_flush(local, false); + } + if (!((local->hw.flags & IEEE80211_HW_REPORTS_TX_ACK_STATUS) && (local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) || (ifmgd->flags & IEEE80211_STA_NULLFUNC_ACKED)) { @@ -748,6 +761,8 @@ void ieee80211_dynamic_ps_enable_work(struct work_struct *work) local->hw.conf.flags |= IEEE80211_CONF_PS; ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); } + + netif_tx_start_all_queues(sdata->dev); } void ieee80211_dynamic_ps_timer(unsigned long data) @@ -1071,12 +1086,6 @@ void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, if (is_multicast_ether_addr(hdr->addr1)) return; - /* - * In case we receive frames after disassociation. - */ - if (!sdata->u.mgd.associated) - return; - ieee80211_sta_reset_conn_monitor(sdata); } diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index f502634d43a..5c1930ba8eb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -77,7 +77,7 @@ ieee80211_rx_radiotap_len(struct ieee80211_local *local, /* always present fields */ len = sizeof(struct ieee80211_radiotap_header) + 9; - if (status->flag & RX_FLAG_TSFT) + if (status->flag & RX_FLAG_MACTIME_MPDU) len += 8; if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; @@ -123,7 +123,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* the order of the following fields is important */ /* IEEE80211_RADIOTAP_TSFT */ - if (status->flag & RX_FLAG_TSFT) { + if (status->flag & RX_FLAG_MACTIME_MPDU) { put_unaligned_le64(status->mactime, pos); rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); @@ -1156,14 +1156,23 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, NL80211_IFTYPE_ADHOC); - if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) + if (compare_ether_addr(bssid, rx->sdata->u.ibss.bssid) == 0) { sta->last_rx = jiffies; + if (ieee80211_is_data(hdr->frame_control)) { + sta->last_rx_rate_idx = status->rate_idx; + sta->last_rx_rate_flag = status->flag; + } + } } else if (!is_multicast_ether_addr(hdr->addr1)) { /* * Mesh beacons will update last_rx when if they are found to * match the current local configuration when processed. */ sta->last_rx = jiffies; + if (ieee80211_is_data(hdr->frame_control)) { + sta->last_rx_rate_idx = status->rate_idx; + sta->last_rx_rate_flag = status->flag; + } } if (!(status->rx_flags & IEEE80211_RX_RA_MATCH)) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index ca0b69060ef..57681149e37 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -209,6 +209,8 @@ enum plink_state { * @rate_ctrl_priv: rate control private per-STA pointer * @last_tx_rate: rate used for last transmit, to report to userspace as * "the" transmit rate + * @last_rx_rate_idx: rx status rate index of the last data packet + * @last_rx_rate_flag: rx status flag of the last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. * @flaglock: spinlock for flags accesses @@ -311,6 +313,8 @@ struct sta_info { unsigned long tx_bytes; unsigned long tx_fragments; struct ieee80211_tx_rate last_tx_rate; + int last_rx_rate_idx; + int last_rx_rate_flag; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; /* diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 010a559bd87..b936dd29e92 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -318,8 +318,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) if (info->flags & IEEE80211_TX_STAT_ACK) { local->ps_sdata->u.mgd.flags |= IEEE80211_STA_NULLFUNC_ACKED; - ieee80211_queue_work(&local->hw, - &local->dynamic_ps_enable_work); } else mod_timer(&local->dynamic_ps_timer, jiffies + msecs_to_jiffies(10)); @@ -343,6 +341,10 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) cookie = local->hw_roc_cookie ^ 2; local->hw_roc_skb_for_status = NULL; } + + if (cookie == local->hw_offchan_tx_cookie) + local->hw_offchan_tx_cookie = 0; + cfg80211_mgmt_tx_status( skb->dev, cookie, skb->data, skb->len, !!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC); diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 34edf7f22b0..081dcaf6577 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -33,10 +33,6 @@ #include "wme.h" #include "rate.h" -#define IEEE80211_TX_OK 0 -#define IEEE80211_TX_AGAIN 1 -#define IEEE80211_TX_PENDING 2 - /* misc utils */ static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr, @@ -1285,16 +1281,17 @@ ieee80211_tx_prepare(struct ieee80211_sub_if_data *sdata, return TX_CONTINUE; } -static int __ieee80211_tx(struct ieee80211_local *local, - struct sk_buff **skbp, - struct sta_info *sta, - bool txpending) +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool __ieee80211_tx(struct ieee80211_local *local, struct sk_buff **skbp, + struct sta_info *sta, bool txpending) { struct sk_buff *skb = *skbp, *next; struct ieee80211_tx_info *info; struct ieee80211_sub_if_data *sdata; unsigned long flags; - int ret, len; + int len; bool fragm = false; while (skb) { @@ -1302,13 +1299,37 @@ static int __ieee80211_tx(struct ieee80211_local *local, __le16 fc; spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - ret = IEEE80211_TX_OK; if (local->queue_stop_reasons[q] || - (!txpending && !skb_queue_empty(&local->pending[q]))) - ret = IEEE80211_TX_PENDING; + (!txpending && !skb_queue_empty(&local->pending[q]))) { + /* + * Since queue is stopped, queue up frames for later + * transmission from the tx-pending tasklet when the + * queue is woken again. + */ + + do { + next = skb->next; + skb->next = NULL; + /* + * NB: If txpending is true, next must already + * be NULL since we must've gone through this + * loop before already; therefore we can just + * queue the frame to the head without worrying + * about reordering of fragments. + */ + if (unlikely(txpending)) + __skb_queue_head(&local->pending[q], + skb); + else + __skb_queue_tail(&local->pending[q], + skb); + } while ((skb = next)); + + spin_unlock_irqrestore(&local->queue_stop_reason_lock, + flags); + return false; + } spin_unlock_irqrestore(&local->queue_stop_reason_lock, flags); - if (ret != IEEE80211_TX_OK) - return ret; info = IEEE80211_SKB_CB(skb); @@ -1343,15 +1364,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, info->control.sta = NULL; fc = ((struct ieee80211_hdr *)skb->data)->frame_control; - ret = drv_tx(local, skb); - if (WARN_ON(ret != NETDEV_TX_OK && skb->len != len)) { - dev_kfree_skb(skb); - ret = NETDEV_TX_OK; - } - if (ret != NETDEV_TX_OK) { - info->control.vif = &sdata->vif; - return IEEE80211_TX_AGAIN; - } + drv_tx(local, skb); ieee80211_tpt_led_trig_tx(local, fc, len); *skbp = skb = next; @@ -1359,7 +1372,7 @@ static int __ieee80211_tx(struct ieee80211_local *local, fragm = true; } - return IEEE80211_TX_OK; + return true; } /* @@ -1419,23 +1432,24 @@ static int invoke_tx_handlers(struct ieee80211_tx_data *tx) return 0; } -static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, +/* + * Returns false if the frame couldn't be transmitted but was queued instead. + */ +static bool ieee80211_tx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool txpending) { struct ieee80211_local *local = sdata->local; struct ieee80211_tx_data tx; ieee80211_tx_result res_prepare; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - struct sk_buff *next; - unsigned long flags; - int ret, retries; u16 queue; + bool result = true; queue = skb_get_queue_mapping(skb); if (unlikely(skb->len < 10)) { dev_kfree_skb(skb); - return; + return true; } rcu_read_lock(); @@ -1445,85 +1459,19 @@ static void ieee80211_tx(struct ieee80211_sub_if_data *sdata, if (unlikely(res_prepare == TX_DROP)) { dev_kfree_skb(skb); - rcu_read_unlock(); - return; + goto out; } else if (unlikely(res_prepare == TX_QUEUED)) { - rcu_read_unlock(); - return; + goto out; } tx.channel = local->hw.conf.channel; info->band = tx.channel->band; - if (invoke_tx_handlers(&tx)) - goto out; - - retries = 0; - retry: - ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); - switch (ret) { - case IEEE80211_TX_OK: - break; - case IEEE80211_TX_AGAIN: - /* - * Since there are no fragmented frames on A-MPDU - * queues, there's no reason for a driver to reject - * a frame there, warn and drop it. - */ - if (WARN_ON(info->flags & IEEE80211_TX_CTL_AMPDU)) - goto drop; - /* fall through */ - case IEEE80211_TX_PENDING: - skb = tx.skb; - - spin_lock_irqsave(&local->queue_stop_reason_lock, flags); - - if (local->queue_stop_reasons[queue] || - !skb_queue_empty(&local->pending[queue])) { - /* - * if queue is stopped, queue up frames for later - * transmission from the tasklet - */ - do { - next = skb->next; - skb->next = NULL; - if (unlikely(txpending)) - __skb_queue_head(&local->pending[queue], - skb); - else - __skb_queue_tail(&local->pending[queue], - skb); - } while ((skb = next)); - - spin_unlock_irqrestore(&local->queue_stop_reason_lock, - flags); - } else { - /* - * otherwise retry, but this is a race condition or - * a driver bug (which we warn about if it persists) - */ - spin_unlock_irqrestore(&local->queue_stop_reason_lock, - flags); - - retries++; - if (WARN(retries > 10, "tx refused but queue active\n")) - goto drop; - goto retry; - } - } + if (!invoke_tx_handlers(&tx)) + result = __ieee80211_tx(local, &tx.skb, tx.sta, txpending); out: rcu_read_unlock(); - return; - - drop: - rcu_read_unlock(); - - skb = tx.skb; - while (skb) { - next = skb->next; - dev_kfree_skb(skb); - skb = next; - } + return result; } /* device xmit handlers */ @@ -2070,6 +2018,11 @@ void ieee80211_clear_tx_pending(struct ieee80211_local *local) skb_queue_purge(&local->pending[i]); } +/* + * Returns false if the frame couldn't be transmitted but was queued instead, + * which in this case means re-queued -- take as an indication to stop sending + * more pending frames. + */ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct sk_buff *skb) { @@ -2077,20 +2030,17 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata; struct sta_info *sta; struct ieee80211_hdr *hdr; - int ret; - bool result = true; + bool result; sdata = vif_to_sdata(info->control.vif); if (info->flags & IEEE80211_TX_INTFL_NEED_TXPROCESSING) { - ieee80211_tx(sdata, skb, true); + result = ieee80211_tx(sdata, skb, true); } else { hdr = (struct ieee80211_hdr *)skb->data; sta = sta_info_get(sdata, hdr->addr1); - ret = __ieee80211_tx(local, &skb, sta, true); - if (ret != IEEE80211_TX_OK) - result = false; + result = __ieee80211_tx(local, &skb, sta, true); } return result; @@ -2132,8 +2082,6 @@ void ieee80211_tx_pending(unsigned long data) flags); txok = ieee80211_tx_pending_skb(local, skb); - if (!txok) - __skb_queue_head(&local->pending[i], skb); spin_lock_irqsave(&local->queue_stop_reason_lock, flags); if (!txok) diff --git a/net/netfilter/ipset/Kconfig b/net/netfilter/ipset/Kconfig index 3b970d34302..2c5b348eb3a 100644 --- a/net/netfilter/ipset/Kconfig +++ b/net/netfilter/ipset/Kconfig @@ -1,6 +1,7 @@ menuconfig IP_SET tristate "IP set support" depends on INET && NETFILTER + depends on NETFILTER_NETLINK help This option adds IP set support to the kernel. In order to define and use the sets, you need the userspace utility diff --git a/net/netfilter/ipvs/ip_vs_conn.c b/net/netfilter/ipvs/ip_vs_conn.c index 83233fe24a0..9c2a517b69c 100644 --- a/net/netfilter/ipvs/ip_vs_conn.c +++ b/net/netfilter/ipvs/ip_vs_conn.c @@ -59,7 +59,7 @@ static int ip_vs_conn_tab_mask __read_mostly; /* * Connection hash table: for input and output packets lookups of IPVS */ -static struct list_head *ip_vs_conn_tab __read_mostly; +static struct hlist_head *ip_vs_conn_tab __read_mostly; /* SLAB cache for IPVS connections */ static struct kmem_cache *ip_vs_conn_cachep __read_mostly; @@ -201,7 +201,7 @@ static inline int ip_vs_conn_hash(struct ip_vs_conn *cp) spin_lock(&cp->lock); if (!(cp->flags & IP_VS_CONN_F_HASHED)) { - list_add(&cp->c_list, &ip_vs_conn_tab[hash]); + hlist_add_head(&cp->c_list, &ip_vs_conn_tab[hash]); cp->flags |= IP_VS_CONN_F_HASHED; atomic_inc(&cp->refcnt); ret = 1; @@ -234,7 +234,7 @@ static inline int ip_vs_conn_unhash(struct ip_vs_conn *cp) spin_lock(&cp->lock); if (cp->flags & IP_VS_CONN_F_HASHED) { - list_del(&cp->c_list); + hlist_del(&cp->c_list); cp->flags &= ~IP_VS_CONN_F_HASHED; atomic_dec(&cp->refcnt); ret = 1; @@ -259,12 +259,13 @@ __ip_vs_conn_in_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp; + struct hlist_node *n; hash = ip_vs_conn_hashkey_param(p, false); ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[hash], c_list) { if (cp->af == p->af && p->cport == cp->cport && p->vport == cp->vport && ip_vs_addr_equal(p->af, p->caddr, &cp->caddr) && @@ -345,12 +346,13 @@ struct ip_vs_conn *ip_vs_ct_in_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp; + struct hlist_node *n; hash = ip_vs_conn_hashkey_param(p, false); ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[hash], c_list) { if (!ip_vs_conn_net_eq(cp, p->net)) continue; if (p->pe_data && p->pe->ct_match) { @@ -394,6 +396,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) { unsigned hash; struct ip_vs_conn *cp, *ret=NULL; + struct hlist_node *n; /* * Check for "full" addressed entries @@ -402,7 +405,7 @@ struct ip_vs_conn *ip_vs_conn_out_get(const struct ip_vs_conn_param *p) ct_read_lock(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[hash], c_list) { if (cp->af == p->af && p->vport == cp->cport && p->cport == cp->dport && ip_vs_addr_equal(p->af, p->vaddr, &cp->caddr) && @@ -818,7 +821,7 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, return NULL; } - INIT_LIST_HEAD(&cp->c_list); + INIT_HLIST_NODE(&cp->c_list); setup_timer(&cp->timer, ip_vs_conn_expire, (unsigned long)cp); ip_vs_conn_net_set(cp, p->net); cp->af = p->af; @@ -894,8 +897,8 @@ ip_vs_conn_new(const struct ip_vs_conn_param *p, */ #ifdef CONFIG_PROC_FS struct ip_vs_iter_state { - struct seq_net_private p; - struct list_head *l; + struct seq_net_private p; + struct hlist_head *l; }; static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) @@ -903,13 +906,14 @@ static void *ip_vs_conn_array(struct seq_file *seq, loff_t pos) int idx; struct ip_vs_conn *cp; struct ip_vs_iter_state *iter = seq->private; + struct hlist_node *n; for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { ct_read_lock_bh(idx); - list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[idx], c_list) { if (pos-- == 0) { iter->l = &ip_vs_conn_tab[idx]; - return cp; + return cp; } } ct_read_unlock_bh(idx); @@ -930,7 +934,8 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct ip_vs_conn *cp = v; struct ip_vs_iter_state *iter = seq->private; - struct list_head *e, *l = iter->l; + struct hlist_node *e; + struct hlist_head *l = iter->l; int idx; ++*pos; @@ -938,15 +943,15 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) return ip_vs_conn_array(seq, 0); /* more on same hash chain? */ - if ((e = cp->c_list.next) != l) - return list_entry(e, struct ip_vs_conn, c_list); + if ((e = cp->c_list.next)) + return hlist_entry(e, struct ip_vs_conn, c_list); idx = l - ip_vs_conn_tab; ct_read_unlock_bh(idx); while (++idx < ip_vs_conn_tab_size) { ct_read_lock_bh(idx); - list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { + hlist_for_each_entry(cp, e, &ip_vs_conn_tab[idx], c_list) { iter->l = &ip_vs_conn_tab[idx]; return cp; } @@ -959,7 +964,7 @@ static void *ip_vs_conn_seq_next(struct seq_file *seq, void *v, loff_t *pos) static void ip_vs_conn_seq_stop(struct seq_file *seq, void *v) { struct ip_vs_iter_state *iter = seq->private; - struct list_head *l = iter->l; + struct hlist_head *l = iter->l; if (l) ct_read_unlock_bh(l - ip_vs_conn_tab); @@ -1148,13 +1153,14 @@ void ip_vs_random_dropentry(struct net *net) */ for (idx = 0; idx < (ip_vs_conn_tab_size>>5); idx++) { unsigned hash = net_random() & ip_vs_conn_tab_mask; + struct hlist_node *n; /* * Lock is actually needed in this loop. */ ct_write_lock_bh(hash); - list_for_each_entry(cp, &ip_vs_conn_tab[hash], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[hash], c_list) { if (cp->flags & IP_VS_CONN_F_TEMPLATE) /* connection template */ continue; @@ -1202,12 +1208,14 @@ static void ip_vs_conn_flush(struct net *net) flush_again: for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { + struct hlist_node *n; + /* * Lock is actually needed in this loop. */ ct_write_lock_bh(idx); - list_for_each_entry(cp, &ip_vs_conn_tab[idx], c_list) { + hlist_for_each_entry(cp, n, &ip_vs_conn_tab[idx], c_list) { if (!ip_vs_conn_net_eq(cp, net)) continue; IP_VS_DBG(4, "del connection\n"); @@ -1265,8 +1273,7 @@ int __init ip_vs_conn_init(void) /* * Allocate the connection hash table and initialize its list heads */ - ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * - sizeof(struct list_head)); + ip_vs_conn_tab = vmalloc(ip_vs_conn_tab_size * sizeof(*ip_vs_conn_tab)); if (!ip_vs_conn_tab) return -ENOMEM; @@ -1286,9 +1293,8 @@ int __init ip_vs_conn_init(void) IP_VS_DBG(0, "Each connection entry needs %Zd bytes at least\n", sizeof(struct ip_vs_conn)); - for (idx = 0; idx < ip_vs_conn_tab_size; idx++) { - INIT_LIST_HEAD(&ip_vs_conn_tab[idx]); - } + for (idx = 0; idx < ip_vs_conn_tab_size; idx++) + INIT_HLIST_HEAD(&ip_vs_conn_tab[idx]); for (idx = 0; idx < CT_LOCKARRAY_SIZE; idx++) { rwlock_init(&__ip_vs_conntbl_lock_array[idx].l); diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 4d06617fab6..2d1f932add4 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -729,7 +729,7 @@ void ip_vs_nat_icmp_v6(struct sk_buff *skb, struct ip_vs_protocol *pp, #endif /* Handle relevant response ICMP messages - forward to the right - * destination host. Used for NAT and local client. + * destination host. */ static int handle_response_icmp(int af, struct sk_buff *skb, union nf_inet_addr *snet, @@ -979,7 +979,6 @@ static inline int is_tcp_reset(const struct sk_buff *skb, int nh_len) } /* Handle response packets: rewrite addresses and send away... - * Used for NAT and local client. */ static unsigned int handle_response(int af, struct sk_buff *skb, struct ip_vs_proto_data *pd, @@ -1280,7 +1279,6 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; unsigned int offset, ihl, verdict; - union nf_inet_addr snet; *related = 1; @@ -1339,17 +1337,8 @@ ip_vs_in_icmp(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_fill_iphdr(AF_INET, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ cp = pp->conn_in_get(AF_INET, skb, &ciph, offset, 1); - if (!cp) { - /* The packet could also belong to a local client */ - cp = pp->conn_out_get(AF_INET, skb, &ciph, offset, 1); - if (cp) { - snet.ip = iph->saddr; - return handle_response_icmp(AF_INET, skb, &snet, - cih->protocol, cp, pp, - offset, ihl); - } + if (!cp) return NF_ACCEPT; - } verdict = NF_DROP; @@ -1395,7 +1384,6 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) struct ip_vs_protocol *pp; struct ip_vs_proto_data *pd; unsigned int offset, verdict; - union nf_inet_addr snet; struct rt6_info *rt; *related = 1; @@ -1455,18 +1443,8 @@ ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, unsigned int hooknum) ip_vs_fill_iphdr(AF_INET6, cih, &ciph); /* The embedded headers contain source and dest in reverse order */ cp = pp->conn_in_get(AF_INET6, skb, &ciph, offset, 1); - if (!cp) { - /* The packet could also belong to a local client */ - cp = pp->conn_out_get(AF_INET6, skb, &ciph, offset, 1); - if (cp) { - ipv6_addr_copy(&snet.in6, &iph->saddr); - return handle_response_icmp(AF_INET6, skb, &snet, - cih->nexthdr, - cp, pp, offset, - sizeof(struct ipv6hdr)); - } + if (!cp) return NF_ACCEPT; - } verdict = NF_DROP; diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index c73b0c831a2..d69ec26b6bd 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -769,9 +769,9 @@ __ip_vs_update_dest(struct ip_vs_service *svc, struct ip_vs_dest *dest, dest->u_threshold = udest->u_threshold; dest->l_threshold = udest->l_threshold; - spin_lock(&dest->dst_lock); + spin_lock_bh(&dest->dst_lock); ip_vs_dst_reset(dest); - spin_unlock(&dest->dst_lock); + spin_unlock_bh(&dest->dst_lock); if (add) ip_vs_new_estimator(svc->net, &dest->stats); diff --git a/net/netfilter/ipvs/ip_vs_lblc.c b/net/netfilter/ipvs/ip_vs_lblc.c index 00b5ffab376..6bf7a807649 100644 --- a/net/netfilter/ipvs/ip_vs_lblc.c +++ b/net/netfilter/ipvs/ip_vs_lblc.c @@ -389,12 +389,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) int loh, doh; /* - * We think the overhead of processing active connections is fifty - * times higher than that of inactive connections in average. (This - * fifty times might not be accurate, we will change it later.) We - * use the following formula to estimate the overhead: - * dest->activeconns*50 + dest->inactconns - * and the load: + * We use the following formula to estimate the load: * (dest overhead) / dest->weight * * Remember -- no floats in kernel mode!!! @@ -410,8 +405,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) continue; if (atomic_read(&dest->weight) > 0) { least = dest; - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -425,8 +419,7 @@ __ip_vs_lblc_schedule(struct ip_vs_service *svc) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; @@ -510,7 +503,7 @@ ip_vs_lblc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* No cache entry or it is invalid, time to schedule */ dest = __ip_vs_lblc_schedule(svc); if (!dest) { - IP_VS_ERR_RL("LBLC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lblcr.c b/net/netfilter/ipvs/ip_vs_lblcr.c index bfa25f1ea9e..00631765b92 100644 --- a/net/netfilter/ipvs/ip_vs_lblcr.c +++ b/net/netfilter/ipvs/ip_vs_lblcr.c @@ -178,8 +178,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if ((atomic_read(&least->weight) > 0) && (least->flags & IP_VS_DEST_F_AVAILABLE)) { - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -192,8 +191,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_min(struct ip_vs_dest_set *set) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if ((loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) && (dest->flags & IP_VS_DEST_F_AVAILABLE)) { @@ -228,8 +226,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) list_for_each_entry(e, &set->list, list) { most = e->dest; if (atomic_read(&most->weight) > 0) { - moh = atomic_read(&most->activeconns) * 50 - + atomic_read(&most->inactconns); + moh = ip_vs_dest_conn_overhead(most); goto nextstage; } } @@ -239,8 +236,7 @@ static inline struct ip_vs_dest *ip_vs_dest_set_max(struct ip_vs_dest_set *set) nextstage: list_for_each_entry(e, &set->list, list) { dest = e->dest; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); /* moh/mw < doh/dw ==> moh*dw < doh*mw, where mw,dw>0 */ if ((moh * atomic_read(&dest->weight) < doh * atomic_read(&most->weight)) @@ -563,12 +559,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) int loh, doh; /* - * We think the overhead of processing active connections is fifty - * times higher than that of inactive connections in average. (This - * fifty times might not be accurate, we will change it later.) We - * use the following formula to estimate the overhead: - * dest->activeconns*50 + dest->inactconns - * and the load: + * We use the following formula to estimate the load: * (dest overhead) / dest->weight * * Remember -- no floats in kernel mode!!! @@ -585,8 +576,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) if (atomic_read(&dest->weight) > 0) { least = dest; - loh = atomic_read(&least->activeconns) * 50 - + atomic_read(&least->inactconns); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } @@ -600,8 +590,7 @@ __ip_vs_lblcr_schedule(struct ip_vs_service *svc) if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = atomic_read(&dest->activeconns) * 50 - + atomic_read(&dest->inactconns); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; @@ -692,7 +681,7 @@ ip_vs_lblcr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* The cache entry is invalid, time to schedule */ dest = __ip_vs_lblcr_schedule(svc); if (!dest) { - IP_VS_ERR_RL("LBLCR: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); read_unlock(&svc->sched_lock); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_lc.c b/net/netfilter/ipvs/ip_vs_lc.c index 4f69db1fac5..f391819c0cc 100644 --- a/net/netfilter/ipvs/ip_vs_lc.c +++ b/net/netfilter/ipvs/ip_vs_lc.c @@ -22,22 +22,6 @@ #include <net/ip_vs.h> - -static inline unsigned int -ip_vs_lc_dest_overhead(struct ip_vs_dest *dest) -{ - /* - * We think the overhead of processing active connections is 256 - * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We - * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns - */ - return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); -} - - /* * Least Connection scheduling */ @@ -62,7 +46,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if ((dest->flags & IP_VS_DEST_F_OVERLOAD) || atomic_read(&dest->weight) == 0) continue; - doh = ip_vs_lc_dest_overhead(dest); + doh = ip_vs_dest_conn_overhead(dest); if (!least || doh < loh) { least = dest; loh = doh; @@ -70,7 +54,7 @@ ip_vs_lc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } if (!least) - IP_VS_ERR_RL("LC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); else IP_VS_DBG_BUF(6, "LC: server %s:%u activeconns %d " "inactconns %d\n", diff --git a/net/netfilter/ipvs/ip_vs_nq.c b/net/netfilter/ipvs/ip_vs_nq.c index c413e183082..984d9c137d8 100644 --- a/net/netfilter/ipvs/ip_vs_nq.c +++ b/net/netfilter/ipvs/ip_vs_nq.c @@ -99,7 +99,7 @@ ip_vs_nq_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) } if (!least) { - IP_VS_ERR_RL("NQ: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_rr.c b/net/netfilter/ipvs/ip_vs_rr.c index e210f37d8ea..c49b388d108 100644 --- a/net/netfilter/ipvs/ip_vs_rr.c +++ b/net/netfilter/ipvs/ip_vs_rr.c @@ -72,7 +72,7 @@ ip_vs_rr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) q = q->next; } while (q != p); write_unlock(&svc->sched_lock); - IP_VS_ERR_RL("RR: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; out: diff --git a/net/netfilter/ipvs/ip_vs_sched.c b/net/netfilter/ipvs/ip_vs_sched.c index 076ebe00435..08dbdd5bc18 100644 --- a/net/netfilter/ipvs/ip_vs_sched.c +++ b/net/netfilter/ipvs/ip_vs_sched.c @@ -29,6 +29,7 @@ #include <net/ip_vs.h> +EXPORT_SYMBOL(ip_vs_scheduler_err); /* * IPVS scheduler list */ @@ -146,6 +147,30 @@ void ip_vs_scheduler_put(struct ip_vs_scheduler *scheduler) module_put(scheduler->module); } +/* + * Common error output helper for schedulers + */ + +void ip_vs_scheduler_err(struct ip_vs_service *svc, const char *msg) +{ + if (svc->fwmark) { + IP_VS_ERR_RL("%s: FWM %u 0x%08X - %s\n", + svc->scheduler->name, svc->fwmark, + svc->fwmark, msg); +#ifdef CONFIG_IP_VS_IPV6 + } else if (svc->af == AF_INET6) { + IP_VS_ERR_RL("%s: %s [%pI6]:%d - %s\n", + svc->scheduler->name, + ip_vs_proto_name(svc->protocol), + &svc->addr.in6, ntohs(svc->port), msg); +#endif + } else { + IP_VS_ERR_RL("%s: %s %pI4:%d - %s\n", + svc->scheduler->name, + ip_vs_proto_name(svc->protocol), + &svc->addr.ip, ntohs(svc->port), msg); + } +} /* * Register a scheduler in the scheduler list diff --git a/net/netfilter/ipvs/ip_vs_sed.c b/net/netfilter/ipvs/ip_vs_sed.c index 1ab75a9dc40..89ead246ed3 100644 --- a/net/netfilter/ipvs/ip_vs_sed.c +++ b/net/netfilter/ipvs/ip_vs_sed.c @@ -87,7 +87,7 @@ ip_vs_sed_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) goto nextstage; } } - IP_VS_ERR_RL("SED: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; /* diff --git a/net/netfilter/ipvs/ip_vs_sh.c b/net/netfilter/ipvs/ip_vs_sh.c index e6cc174fbc0..b5e2556c581 100644 --- a/net/netfilter/ipvs/ip_vs_sh.c +++ b/net/netfilter/ipvs/ip_vs_sh.c @@ -223,7 +223,7 @@ ip_vs_sh_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) || !(dest->flags & IP_VS_DEST_F_AVAILABLE) || atomic_read(&dest->weight) <= 0 || is_overloaded(dest)) { - IP_VS_ERR_RL("SH: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; } diff --git a/net/netfilter/ipvs/ip_vs_sync.c b/net/netfilter/ipvs/ip_vs_sync.c index d1b7298e589..fecf24de4af 100644 --- a/net/netfilter/ipvs/ip_vs_sync.c +++ b/net/netfilter/ipvs/ip_vs_sync.c @@ -374,8 +374,8 @@ get_curr_sync_buff(struct netns_ipvs *ipvs, unsigned long time) struct ip_vs_sync_buff *sb; spin_lock_bh(&ipvs->sync_buff_lock); - if (ipvs->sync_buff && (time == 0 || - time_before(jiffies - ipvs->sync_buff->firstuse, time))) { + if (ipvs->sync_buff && + time_after_eq(jiffies - ipvs->sync_buff->firstuse, time)) { sb = ipvs->sync_buff; ipvs->sync_buff = NULL; } else diff --git a/net/netfilter/ipvs/ip_vs_wlc.c b/net/netfilter/ipvs/ip_vs_wlc.c index bbddfdb10db..bc1bfc48a17 100644 --- a/net/netfilter/ipvs/ip_vs_wlc.c +++ b/net/netfilter/ipvs/ip_vs_wlc.c @@ -27,22 +27,6 @@ #include <net/ip_vs.h> - -static inline unsigned int -ip_vs_wlc_dest_overhead(struct ip_vs_dest *dest) -{ - /* - * We think the overhead of processing active connections is 256 - * times higher than that of inactive connections in average. (This - * 256 times might not be accurate, we will change it later) We - * use the following formula to estimate the overhead now: - * dest->activeconns*256 + dest->inactconns - */ - return (atomic_read(&dest->activeconns) << 8) + - atomic_read(&dest->inactconns); -} - - /* * Weighted Least Connection scheduling */ @@ -71,11 +55,11 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if (!(dest->flags & IP_VS_DEST_F_OVERLOAD) && atomic_read(&dest->weight) > 0) { least = dest; - loh = ip_vs_wlc_dest_overhead(least); + loh = ip_vs_dest_conn_overhead(least); goto nextstage; } } - IP_VS_ERR_RL("WLC: no destination available\n"); + ip_vs_scheduler_err(svc, "no destination available"); return NULL; /* @@ -85,7 +69,7 @@ ip_vs_wlc_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) list_for_each_entry_continue(dest, &svc->destinations, n_list) { if (dest->flags & IP_VS_DEST_F_OVERLOAD) continue; - doh = ip_vs_wlc_dest_overhead(dest); + doh = ip_vs_dest_conn_overhead(dest); if (loh * atomic_read(&dest->weight) > doh * atomic_read(&least->weight)) { least = dest; diff --git a/net/netfilter/ipvs/ip_vs_wrr.c b/net/netfilter/ipvs/ip_vs_wrr.c index 30db633f88f..1ef41f50723 100644 --- a/net/netfilter/ipvs/ip_vs_wrr.c +++ b/net/netfilter/ipvs/ip_vs_wrr.c @@ -147,8 +147,9 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) if (mark->cl == mark->cl->next) { /* no dest entry */ - IP_VS_ERR_RL("WRR: no destination available: " - "no destinations present\n"); + ip_vs_scheduler_err(svc, + "no destination available: " + "no destinations present"); dest = NULL; goto out; } @@ -162,8 +163,8 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) */ if (mark->cw == 0) { mark->cl = &svc->destinations; - IP_VS_ERR_RL("WRR: no destination " - "available\n"); + ip_vs_scheduler_err(svc, + "no destination available"); dest = NULL; goto out; } @@ -185,8 +186,9 @@ ip_vs_wrr_schedule(struct ip_vs_service *svc, const struct sk_buff *skb) /* back to the start, and no dest is found. It is only possible when all dests are OVERLOADED */ dest = NULL; - IP_VS_ERR_RL("WRR: no destination available: " - "all destinations are overloaded\n"); + ip_vs_scheduler_err(svc, + "no destination available: " + "all destinations are overloaded"); goto out; } } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 1f2a4e35fb1..878f6dd9dba 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -43,6 +43,13 @@ #include <net/ip_vs.h> +enum { + IP_VS_RT_MODE_LOCAL = 1, /* Allow local dest */ + IP_VS_RT_MODE_NON_LOCAL = 2, /* Allow non-local dest */ + IP_VS_RT_MODE_RDR = 4, /* Allow redirect from remote daddr to + * local + */ +}; /* * Destination cache to speed up outgoing route lookup @@ -77,11 +84,7 @@ __ip_vs_dst_check(struct ip_vs_dest *dest, u32 rtos) return dst; } -/* - * Get route to destination or remote server - * rt_mode: flags, &1=Allow local dest, &2=Allow non-local dest, - * &4=Allow redirect from remote daddr to local - */ +/* Get route to destination or remote server */ static struct rtable * __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, __be32 daddr, u32 rtos, int rt_mode) @@ -100,7 +103,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, .fl4_tos = rtos, }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { spin_unlock(&dest->dst_lock); IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &dest->addr.ip); @@ -118,7 +122,8 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, .fl4_tos = rtos, }; - if (ip_route_output_key(net, &rt, &fl)) { + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) { IP_VS_DBG_RL("ip_route_output error, dest: %pI4\n", &daddr); return NULL; @@ -126,15 +131,16 @@ __ip_vs_get_out_rt(struct sk_buff *skb, struct ip_vs_dest *dest, } local = rt->rt_flags & RTCF_LOCAL; - if (!((local ? 1 : 2) & rt_mode)) { + if (!((local ? IP_VS_RT_MODE_LOCAL : IP_VS_RT_MODE_NON_LOCAL) & + rt_mode)) { IP_VS_DBG_RL("Stopping traffic to %s address, dest: %pI4\n", (rt->rt_flags & RTCF_LOCAL) ? "local":"non-local", &rt->rt_dst); ip_rt_put(rt); return NULL; } - if (local && !(rt_mode & 4) && !((ort = skb_rtable(skb)) && - ort->rt_flags & RTCF_LOCAL)) { + if (local && !(rt_mode & IP_VS_RT_MODE_RDR) && + !((ort = skb_rtable(skb)) && ort->rt_flags & RTCF_LOCAL)) { IP_VS_DBG_RL("Redirect from non-local address %pI4 to local " "requires NAT method, dest: %pI4\n", &ip_hdr(skb)->daddr, &rt->rt_dst); @@ -176,7 +182,8 @@ __ip_vs_reroute_locally(struct sk_buff *skb) .mark = skb->mark, }; - if (ip_route_output_key(net, &rt, &fl)) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return 0; if (!(rt->rt_flags & RTCF_LOCAL)) { ip_rt_put(rt); @@ -214,8 +221,13 @@ __ip_vs_route_output_v6(struct net *net, struct in6_addr *daddr, ipv6_dev_get_saddr(net, ip6_dst_idev(dst)->dev, &fl.fl6_dst, 0, &fl.fl6_src) < 0) goto out_err; - if (do_xfrm && xfrm_lookup(net, &dst, &fl, NULL, 0) < 0) - goto out_err; + if (do_xfrm) { + dst = xfrm_lookup(net, dst, &fl, NULL, 0); + if (IS_ERR(dst)) { + dst = NULL; + goto out_err; + } + } ipv6_addr_copy(ret_saddr, &fl.fl6_src); return dst; @@ -383,8 +395,8 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - if (!(rt = __ip_vs_get_out_rt(skb, NULL, iph->daddr, - RT_TOS(iph->tos), 2))) + if (!(rt = __ip_vs_get_out_rt(skb, NULL, iph->daddr, RT_TOS(iph->tos), + IP_VS_RT_MODE_NON_LOCAL))) goto tx_error_icmp; /* MTU checking */ @@ -512,7 +524,10 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, } if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, - RT_TOS(iph->tos), 1|2|4))) + RT_TOS(iph->tos), + IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR))) goto tx_error_icmp; local = rt->rt_flags & RTCF_LOCAL; /* @@ -755,7 +770,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, - RT_TOS(tos), 1|2))) + RT_TOS(tos), IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL))) goto tx_error_icmp; if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); @@ -984,7 +1000,9 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, - RT_TOS(iph->tos), 1|2))) + RT_TOS(iph->tos), + IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL))) goto tx_error_icmp; if (rt->rt_flags & RTCF_LOCAL) { ip_rt_put(rt); @@ -1128,7 +1146,10 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, */ if (!(rt = __ip_vs_get_out_rt(skb, cp->dest, cp->daddr.ip, - RT_TOS(ip_hdr(skb)->tos), 1|2|4))) + RT_TOS(ip_hdr(skb)->tos), + IP_VS_RT_MODE_LOCAL | + IP_VS_RT_MODE_NON_LOCAL | + IP_VS_RT_MODE_RDR))) goto tx_error_icmp; local = rt->rt_flags & RTCF_LOCAL; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 6f38d0e2ea4..37bf94394be 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -227,11 +227,11 @@ static const u8 tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = { * sCL -> sIV */ /* sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sS2 */ -/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sSR }, +/*synack*/ { sIV, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIG, sSR }, /* * sSS -> sSR Standard open. * sS2 -> sSR Simultaneous open - * sSR -> sSR Retransmitted SYN/ACK. + * sSR -> sIG Retransmitted SYN/ACK, ignore it. * sES -> sIG Late retransmitted SYN/ACK? * sFW -> sIG Might be SYN/ACK answering ignored SYN * sCW -> sIG diff --git a/net/netfilter/nf_log.c b/net/netfilter/nf_log.c index 20c775cff2a..20714edf6cd 100644 --- a/net/netfilter/nf_log.c +++ b/net/netfilter/nf_log.c @@ -85,6 +85,8 @@ EXPORT_SYMBOL(nf_log_unregister); int nf_log_bind_pf(u_int8_t pf, const struct nf_logger *logger) { + if (pf >= ARRAY_SIZE(nf_loggers)) + return -EINVAL; mutex_lock(&nf_log_mutex); if (__find_logger(pf, logger->name) == NULL) { mutex_unlock(&nf_log_mutex); @@ -98,6 +100,8 @@ EXPORT_SYMBOL(nf_log_bind_pf); void nf_log_unbind_pf(u_int8_t pf) { + if (pf >= ARRAY_SIZE(nf_loggers)) + return; mutex_lock(&nf_log_mutex); rcu_assign_pointer(nf_loggers[pf], NULL); mutex_unlock(&nf_log_mutex); diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index 91592da504b..985e9b76c91 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -376,7 +376,6 @@ __build_packet_message(struct nfulnl_instance *inst, unsigned int hooknum, const struct net_device *indev, const struct net_device *outdev, - const struct nf_loginfo *li, const char *prefix, unsigned int plen) { struct nfulnl_msg_packet_hdr pmsg; @@ -652,7 +651,7 @@ nfulnl_log_packet(u_int8_t pf, inst->qlen++; __build_packet_message(inst, skb, data_len, pf, - hooknum, in, out, li, prefix, plen); + hooknum, in, out, prefix, plen); if (inst->qlen >= qthreshold) __nfulnl_flush(inst); diff --git a/net/netfilter/xt_TEE.c b/net/netfilter/xt_TEE.c index 5128a6c4cb2..624725b5286 100644 --- a/net/netfilter/xt_TEE.c +++ b/net/netfilter/xt_TEE.c @@ -73,7 +73,8 @@ tee_tg_route4(struct sk_buff *skb, const struct xt_tee_tginfo *info) fl.fl4_dst = info->gw.ip; fl.fl4_tos = RT_TOS(iph->tos); fl.fl4_scope = RT_SCOPE_UNIVERSE; - if (ip_route_output_key(net, &rt, &fl) != 0) + rt = ip_route_output_key(net, &fl); + if (IS_ERR(rt)) return false; skb_dst_drop(skb); diff --git a/net/netfilter/xt_conntrack.c b/net/netfilter/xt_conntrack.c index 4ef1b63ad73..2c0086a4751 100644 --- a/net/netfilter/xt_conntrack.c +++ b/net/netfilter/xt_conntrack.c @@ -272,6 +272,11 @@ static int conntrack_mt_check(const struct xt_mtchk_param *par) { int ret; + if (strcmp(par->table, "raw") == 0) { + pr_info("state is undetermined at the time of raw table\n"); + return -EINVAL; + } + ret = nf_ct_l3proto_try_module_get(par->family); if (ret < 0) pr_info("cannot load conntrack support for proto=%u\n", diff --git a/net/netlabel/netlabel_user.h b/net/netlabel/netlabel_user.h index 6caef8b2061..f4fc4c9ad56 100644 --- a/net/netlabel/netlabel_user.h +++ b/net/netlabel/netlabel_user.h @@ -49,9 +49,9 @@ static inline void netlbl_netlink_auditinfo(struct sk_buff *skb, struct netlbl_audit *audit_info) { - audit_info->secid = NETLINK_CB(skb).sid; - audit_info->loginuid = NETLINK_CB(skb).loginuid; - audit_info->sessionid = NETLINK_CB(skb).sessionid; + security_task_getsecid(current, &audit_info->secid); + audit_info->loginuid = audit_get_loginuid(current); + audit_info->sessionid = audit_get_sessionid(current); } /* NetLabel NETLINK I/O functions */ diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index 478181d53c5..c8f35b5d2ee 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1362,17 +1362,8 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock, NETLINK_CB(skb).pid = nlk->pid; NETLINK_CB(skb).dst_group = dst_group; - NETLINK_CB(skb).loginuid = audit_get_loginuid(current); - NETLINK_CB(skb).sessionid = audit_get_sessionid(current); - security_task_getsecid(current, &(NETLINK_CB(skb).sid)); memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred)); - /* What can I do? Netlink is asynchronous, so that - we will have to save current capabilities to - check them, when this message will be delivered - to corresponding kernel module. --ANK (980802) - */ - err = -EFAULT; if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) { kfree_skb(skb); @@ -1407,7 +1398,7 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, int noblock = flags&MSG_DONTWAIT; size_t copied; struct sk_buff *skb, *data_skb; - int err; + int err, ret; if (flags&MSG_OOB) return -EOPNOTSUPP; @@ -1470,8 +1461,13 @@ static int netlink_recvmsg(struct kiocb *kiocb, struct socket *sock, skb_free_datagram(sk, skb); - if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) - netlink_dump(sk); + if (nlk->cb && atomic_read(&sk->sk_rmem_alloc) <= sk->sk_rcvbuf / 2) { + ret = netlink_dump(sk); + if (ret) { + sk->sk_err = ret; + sk->sk_error_report(sk); + } + } scm_recv(sock, msg, siocb->scm, flags); out: @@ -1736,6 +1732,7 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, struct netlink_callback *cb; struct sock *sk; struct netlink_sock *nlk; + int ret; cb = kzalloc(sizeof(*cb), GFP_KERNEL); if (cb == NULL) @@ -1764,9 +1761,13 @@ int netlink_dump_start(struct sock *ssk, struct sk_buff *skb, nlk->cb = cb; mutex_unlock(nlk->cb_mutex); - netlink_dump(sk); + ret = netlink_dump(sk); + sock_put(sk); + if (ret) + return ret; + /* We successfully started a dump, by returning -EINTR we * signal not to send ACK even if it was requested. */ diff --git a/net/rxrpc/ar-input.c b/net/rxrpc/ar-input.c index 89315009bab..1a2b0633fec 100644 --- a/net/rxrpc/ar-input.c +++ b/net/rxrpc/ar-input.c @@ -423,6 +423,7 @@ void rxrpc_fast_process_packet(struct rxrpc_call *call, struct sk_buff *skb) goto protocol_error; } + case RXRPC_PACKET_TYPE_ACKALL: case RXRPC_PACKET_TYPE_ACK: /* ACK processing is done in process context */ read_lock_bh(&call->state_lock); diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index 5ee16f0353f..d763793d39d 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -89,11 +89,11 @@ static int rxrpc_instantiate_xdr_rxkad(struct key *key, const __be32 *xdr, return ret; plen -= sizeof(*token); - token = kmalloc(sizeof(*token), GFP_KERNEL); + token = kzalloc(sizeof(*token), GFP_KERNEL); if (!token) return -ENOMEM; - token->kad = kmalloc(plen, GFP_KERNEL); + token->kad = kzalloc(plen, GFP_KERNEL); if (!token->kad) { kfree(token); return -ENOMEM; @@ -731,10 +731,10 @@ static int rxrpc_instantiate(struct key *key, const void *data, size_t datalen) goto error; ret = -ENOMEM; - token = kmalloc(sizeof(*token), GFP_KERNEL); + token = kzalloc(sizeof(*token), GFP_KERNEL); if (!token) goto error; - token->kad = kmalloc(plen, GFP_KERNEL); + token->kad = kzalloc(plen, GFP_KERNEL); if (!token->kad) goto error_free; diff --git a/net/rxrpc/ar-peer.c b/net/rxrpc/ar-peer.c index a53fb25a64e..3620c569275 100644 --- a/net/rxrpc/ar-peer.c +++ b/net/rxrpc/ar-peer.c @@ -37,7 +37,6 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) { struct rtable *rt; struct flowi fl; - int ret; peer->if_mtu = 1500; @@ -58,9 +57,9 @@ static void rxrpc_assess_MTU_size(struct rxrpc_peer *peer) BUG(); } - ret = ip_route_output_key(&init_net, &rt, &fl); - if (ret < 0) { - _leave(" [route err %d]", ret); + rt = ip_route_output_key(&init_net, &fl); + if (IS_ERR(rt)) { + _leave(" [route err %ld]", PTR_ERR(rt)); return; } diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index d580cdfca09..a907905376d 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -143,7 +143,7 @@ static int route4_classify(struct sk_buff *skb, struct tcf_proto *tp, if (head == NULL) goto old_method; - iif = ((struct rtable *)dst)->fl.iif; + iif = ((struct rtable *)dst)->rt_iif; h = route4_fastmap_hash(id, iif); if (id == head->fastmap[h].id && diff --git a/net/sched/em_meta.c b/net/sched/em_meta.c index e5e17478267..a4de67eca82 100644 --- a/net/sched/em_meta.c +++ b/net/sched/em_meta.c @@ -264,7 +264,7 @@ META_COLLECTOR(int_rtiif) if (unlikely(skb_rtable(skb) == NULL)) *err = -1; else - dst->value = skb_rtable(skb)->fl.iif; + dst->value = skb_rtable(skb)->rt_iif; } /************************************************************************** diff --git a/net/sched/sch_fifo.c b/net/sched/sch_fifo.c index be33f9ddf9d..66effe2da8e 100644 --- a/net/sched/sch_fifo.c +++ b/net/sched/sch_fifo.c @@ -19,15 +19,9 @@ /* 1 band FIFO pseudo-"scheduler" */ -struct fifo_sched_data { - u32 limit; -}; - static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= q->limit)) + if (likely(sch->qstats.backlog + qdisc_pkt_len(skb) <= sch->limit)) return qdisc_enqueue_tail(skb, sch); return qdisc_reshape_fail(skb, sch); @@ -35,9 +29,7 @@ static int bfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(skb_queue_len(&sch->q) < q->limit)) + if (likely(skb_queue_len(&sch->q) < sch->limit)) return qdisc_enqueue_tail(skb, sch); return qdisc_reshape_fail(skb, sch); @@ -45,9 +37,7 @@ static int pfifo_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch) { - struct fifo_sched_data *q = qdisc_priv(sch); - - if (likely(skb_queue_len(&sch->q) < q->limit)) + if (likely(skb_queue_len(&sch->q) < sch->limit)) return qdisc_enqueue_tail(skb, sch); /* queue full, remove one skb to fulfill the limit */ @@ -60,7 +50,6 @@ static int pfifo_tail_enqueue(struct sk_buff *skb, struct Qdisc *sch) static int fifo_init(struct Qdisc *sch, struct nlattr *opt) { - struct fifo_sched_data *q = qdisc_priv(sch); bool bypass; bool is_bfifo = sch->ops == &bfifo_qdisc_ops; @@ -70,20 +59,20 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt) if (is_bfifo) limit *= psched_mtu(qdisc_dev(sch)); - q->limit = limit; + sch->limit = limit; } else { struct tc_fifo_qopt *ctl = nla_data(opt); if (nla_len(opt) < sizeof(*ctl)) return -EINVAL; - q->limit = ctl->limit; + sch->limit = ctl->limit; } if (is_bfifo) - bypass = q->limit >= psched_mtu(qdisc_dev(sch)); + bypass = sch->limit >= psched_mtu(qdisc_dev(sch)); else - bypass = q->limit >= 1; + bypass = sch->limit >= 1; if (bypass) sch->flags |= TCQ_F_CAN_BYPASS; @@ -94,8 +83,7 @@ static int fifo_init(struct Qdisc *sch, struct nlattr *opt) static int fifo_dump(struct Qdisc *sch, struct sk_buff *skb) { - struct fifo_sched_data *q = qdisc_priv(sch); - struct tc_fifo_qopt opt = { .limit = q->limit }; + struct tc_fifo_qopt opt = { .limit = sch->limit }; NLA_PUT(skb, TCA_OPTIONS, sizeof(opt), &opt); return skb->len; @@ -106,7 +94,7 @@ nla_put_failure: struct Qdisc_ops pfifo_qdisc_ops __read_mostly = { .id = "pfifo", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = pfifo_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, @@ -121,7 +109,7 @@ EXPORT_SYMBOL(pfifo_qdisc_ops); struct Qdisc_ops bfifo_qdisc_ops __read_mostly = { .id = "bfifo", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = bfifo_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, @@ -136,7 +124,7 @@ EXPORT_SYMBOL(bfifo_qdisc_ops); struct Qdisc_ops pfifo_head_drop_qdisc_ops __read_mostly = { .id = "pfifo_head_drop", - .priv_size = sizeof(struct fifo_sched_data), + .priv_size = 0, .enqueue = pfifo_tail_enqueue, .dequeue = qdisc_dequeue_head, .peek = qdisc_peek_head, diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 0da09d50873..c84b65920d1 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -550,21 +550,25 @@ struct Qdisc *qdisc_alloc(struct netdev_queue *dev_queue, { void *p; struct Qdisc *sch; - unsigned int size; + unsigned int size = QDISC_ALIGN(sizeof(*sch)) + ops->priv_size; int err = -ENOBUFS; - /* ensure that the Qdisc and the private data are 64-byte aligned */ - size = QDISC_ALIGN(sizeof(*sch)); - size += ops->priv_size + (QDISC_ALIGNTO - 1); - p = kzalloc_node(size, GFP_KERNEL, netdev_queue_numa_node_read(dev_queue)); if (!p) goto errout; sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); - sch->padded = (char *) sch - (char *) p; - + /* if we got non aligned memory, ask more and do alignment ourself */ + if (sch != p) { + kfree(p); + p = kzalloc_node(size + QDISC_ALIGNTO - 1, GFP_KERNEL, + netdev_queue_numa_node_read(dev_queue)); + if (!p) + goto errout; + sch = (struct Qdisc *) QDISC_ALIGN((unsigned long) p); + sch->padded = (char *) sch - (char *) p; + } INIT_LIST_HEAD(&sch->list); skb_queue_head_init(&sch->q); spin_lock_init(&sch->busylock); @@ -840,6 +844,7 @@ void dev_deactivate(struct net_device *dev) list_add(&dev->unreg_list, &single); dev_deactivate_many(&single); + list_del(&single); } EXPORT_SYMBOL(dev_deactivate); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index e58f9476f29..4e55e6c49ec 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -491,9 +491,9 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, SCTP_DEBUG_PRINTK("%s: DST:%pI4, SRC:%pI4 - ", __func__, &fl.fl4_dst, &fl.fl4_src); - if (!ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (!IS_ERR(rt)) dst = &rt->dst; - } /* If there is no association or if a source address is passed, no * more validation is required. @@ -535,7 +535,8 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, (AF_INET == laddr->a.sa.sa_family)) { fl.fl4_src = laddr->a.v4.sin_addr.s_addr; fl.fl_ip_sport = laddr->a.v4.sin_port; - if (!ip_route_output_key(&init_net, &rt, &fl)) { + rt = ip_route_output_key(&init_net, &fl); + if (!IS_ERR(rt)) { dst = &rt->dst; goto out_unlock; } diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 864ddfbeff2..4ebce4284e9 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1968,13 +1968,41 @@ static int parse_station_flags(struct genl_info *info, return 0; } +static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, + int attr) +{ + struct nlattr *rate; + u16 bitrate; + + rate = nla_nest_start(msg, attr); + if (!rate) + goto nla_put_failure; + + /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ + bitrate = cfg80211_calculate_bitrate(info); + if (bitrate > 0) + NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); + + if (info->flags & RATE_INFO_FLAGS_MCS) + NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs); + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); + if (info->flags & RATE_INFO_FLAGS_SHORT_GI) + NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); + + nla_nest_end(msg, rate); + return true; + +nla_put_failure: + return false; +} + static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, int flags, struct net_device *dev, const u8 *mac_addr, struct station_info *sinfo) { void *hdr; - struct nlattr *sinfoattr, *txrate; - u16 bitrate; + struct nlattr *sinfoattr; hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); if (!hdr) @@ -2013,24 +2041,14 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, sinfo->signal_avg); if (sinfo->filled & STATION_INFO_TX_BITRATE) { - txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); - if (!txrate) + if (!nl80211_put_sta_rate(msg, &sinfo->txrate, + NL80211_STA_INFO_TX_BITRATE)) + goto nla_put_failure; + } + if (sinfo->filled & STATION_INFO_RX_BITRATE) { + if (!nl80211_put_sta_rate(msg, &sinfo->rxrate, + NL80211_STA_INFO_RX_BITRATE)) goto nla_put_failure; - - /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ - bitrate = cfg80211_calculate_bitrate(&sinfo->txrate); - if (bitrate > 0) - NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); - - if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) - NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, - sinfo->txrate.mcs); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); - if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) - NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); - - nla_nest_end(msg, txrate); } if (sinfo->filled & STATION_INFO_RX_PACKETS) NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS, diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 7f1f4ec4904..0bf169bb770 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -805,11 +805,11 @@ int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; - wdev_lock(wdev); mutex_lock(&rdev->devlist_mtx); + wdev_lock(wdev); err = cfg80211_set_freq(rdev, wdev, freq, NL80211_CHAN_NO_HT); - mutex_unlock(&rdev->devlist_mtx); wdev_unlock(wdev); + mutex_unlock(&rdev->devlist_mtx); return err; default: return -EOPNOTSUPP; diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 41a91d27d3e..b1932a629ef 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1735,19 +1735,36 @@ error: return ERR_PTR(err); } +static struct dst_entry *make_blackhole(struct net *net, u16 family, + struct dst_entry *dst_orig) +{ + struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); + struct dst_entry *ret; + + if (!afinfo) { + dst_release(dst_orig); + ret = ERR_PTR(-EINVAL); + } else { + ret = afinfo->blackhole_route(net, dst_orig); + } + xfrm_policy_put_afinfo(afinfo); + + return ret; +} + /* Main function: finds/creates a bundle for given flow. * * At the moment we eat a raw IP route. Mostly to speed up lookups * on interfaces with disabled IPsec. */ -int __xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, - struct sock *sk, int flags) +struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, + const struct flowi *fl, + struct sock *sk, int flags) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; struct flow_cache_object *flo; struct xfrm_dst *xdst; - struct dst_entry *dst, *dst_orig = *dst_p, *route; + struct dst_entry *dst, *route; u16 family = dst_orig->ops->family; u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); int i, err, num_pols, num_xfrms = 0, drop_pols = 0; @@ -1829,9 +1846,10 @@ restart: dst_release(dst); xfrm_pols_put(pols, drop_pols); XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTNOSTATES); - return -EREMOTE; + + return make_blackhole(net, family, dst_orig); } - if (flags & XFRM_LOOKUP_WAIT) { + if (fl->flags & FLOWI_FLAG_CAN_SLEEP) { DECLARE_WAITQUEUE(wait, current); add_wait_queue(&net->xfrm.km_waitq, &wait); @@ -1873,43 +1891,28 @@ no_transform: goto error; } else if (num_xfrms > 0) { /* Flow transformed */ - *dst_p = dst; dst_release(dst_orig); } else { /* Flow passes untransformed */ dst_release(dst); + dst = dst_orig; } ok: xfrm_pols_put(pols, drop_pols); - return 0; + return dst; nopol: - if (!(flags & XFRM_LOOKUP_ICMP)) + if (!(flags & XFRM_LOOKUP_ICMP)) { + dst = dst_orig; goto ok; + } err = -ENOENT; error: dst_release(dst); dropdst: dst_release(dst_orig); - *dst_p = NULL; xfrm_pols_put(pols, drop_pols); - return err; -} -EXPORT_SYMBOL(__xfrm_lookup); - -int xfrm_lookup(struct net *net, struct dst_entry **dst_p, - const struct flowi *fl, - struct sock *sk, int flags) -{ - int err = __xfrm_lookup(net, dst_p, fl, sk, flags); - - if (err == -EREMOTE) { - dst_release(*dst_p); - *dst_p = NULL; - err = -EAGAIN; - } - - return err; + return ERR_PTR(err); } EXPORT_SYMBOL(xfrm_lookup); @@ -2169,7 +2172,7 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) struct net *net = dev_net(skb->dev); struct flowi fl; struct dst_entry *dst; - int res; + int res = 0; if (xfrm_decode_session(skb, &fl, family) < 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMFWDHDRERROR); @@ -2177,9 +2180,12 @@ int __xfrm_route_forward(struct sk_buff *skb, unsigned short family) } skb_dst_force(skb); - dst = skb_dst(skb); - res = xfrm_lookup(net, &dst, &fl, NULL, 0) == 0; + dst = xfrm_lookup(net, skb_dst(skb), &fl, NULL, 0); + if (IS_ERR(dst)) { + res = 1; + dst = NULL; + } skb_dst_set(skb, dst); return res; } diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 673698d380d..468ab60d3dc 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -497,9 +497,9 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_state *x; int err; struct km_event c; - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; err = verify_newsa_info(p, attrs); if (err) @@ -515,6 +515,7 @@ static int xfrm_add_sa(struct sk_buff *skb, struct nlmsghdr *nlh, else err = xfrm_state_update(x); + security_task_getsecid(current, &sid); xfrm_audit_state_add(x, err ? 0 : 1, loginuid, sessionid, sid); if (err < 0) { @@ -575,9 +576,9 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, int err = -ESRCH; struct km_event c; struct xfrm_usersa_id *p = nlmsg_data(nlh); - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; x = xfrm_user_state_lookup(net, p, attrs, &err); if (x == NULL) @@ -602,6 +603,7 @@ static int xfrm_del_sa(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_notify(x, &c); out: + security_task_getsecid(current, &sid); xfrm_audit_state_delete(x, err ? 0 : 1, loginuid, sessionid, sid); xfrm_state_put(x); return err; @@ -1265,9 +1267,9 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, struct km_event c; int err; int excl; - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; err = verify_newpolicy_info(p); if (err) @@ -1286,6 +1288,7 @@ static int xfrm_add_policy(struct sk_buff *skb, struct nlmsghdr *nlh, * a type XFRM_MSG_UPDPOLICY - JHS */ excl = nlh->nlmsg_type == XFRM_MSG_NEWPOLICY; err = xfrm_policy_insert(p->dir, xp, excl); + security_task_getsecid(current, &sid); xfrm_audit_policy_add(xp, err ? 0 : 1, loginuid, sessionid, sid); if (err) { @@ -1522,10 +1525,11 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, NETLINK_CB(skb).pid); } } else { - uid_t loginuid = NETLINK_CB(skb).loginuid; - u32 sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + security_task_getsecid(current, &sid); xfrm_audit_policy_delete(xp, err ? 0 : 1, loginuid, sessionid, sid); @@ -1553,9 +1557,9 @@ static int xfrm_flush_sa(struct sk_buff *skb, struct nlmsghdr *nlh, struct xfrm_audit audit_info; int err; - audit_info.loginuid = NETLINK_CB(skb).loginuid; - audit_info.sessionid = NETLINK_CB(skb).sessionid; - audit_info.secid = NETLINK_CB(skb).sid; + audit_info.loginuid = audit_get_loginuid(current); + audit_info.sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &audit_info.secid); err = xfrm_state_flush(net, p->proto, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1720,9 +1724,9 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, if (err) return err; - audit_info.loginuid = NETLINK_CB(skb).loginuid; - audit_info.sessionid = NETLINK_CB(skb).sessionid; - audit_info.secid = NETLINK_CB(skb).sid; + audit_info.loginuid = audit_get_loginuid(current); + audit_info.sessionid = audit_get_sessionid(current); + security_task_getsecid(current, &audit_info.secid); err = xfrm_policy_flush(net, type, &audit_info); if (err) { if (err == -ESRCH) /* empty table */ @@ -1789,9 +1793,11 @@ static int xfrm_add_pol_expire(struct sk_buff *skb, struct nlmsghdr *nlh, err = 0; if (up->hard) { - uid_t loginuid = NETLINK_CB(skb).loginuid; - uid_t sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + + security_task_getsecid(current, &sid); xfrm_policy_delete(xp, p->dir); xfrm_audit_policy_delete(xp, 1, loginuid, sessionid, sid); @@ -1830,9 +1836,11 @@ static int xfrm_add_sa_expire(struct sk_buff *skb, struct nlmsghdr *nlh, km_state_expired(x, ue->hard, current->pid); if (ue->hard) { - uid_t loginuid = NETLINK_CB(skb).loginuid; - uid_t sessionid = NETLINK_CB(skb).sessionid; - u32 sid = NETLINK_CB(skb).sid; + uid_t loginuid = audit_get_loginuid(current); + u32 sessionid = audit_get_sessionid(current); + u32 sid; + + security_task_getsecid(current, &sid); __xfrm_state_delete(x); xfrm_audit_state_delete(x, 1, loginuid, sessionid, sid); } diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index c9a16abacab..6c94c6ce292 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c @@ -315,6 +315,7 @@ static void parse_dep_file(void *map, size_t len) char *end = m + len; char *p; char s[PATH_MAX]; + int first; p = strchr(m, ':'); if (!p) { @@ -327,6 +328,7 @@ static void parse_dep_file(void *map, size_t len) clear_config(); + first = 1; while (m < end) { while (m < end && (*m == ' ' || *m == '\\' || *m == '\n')) m++; @@ -340,9 +342,17 @@ static void parse_dep_file(void *map, size_t len) if (strrcmp(s, "include/generated/autoconf.h") && strrcmp(s, "arch/um/include/uml-config.h") && strrcmp(s, ".ver")) { - printf(" %s \\\n", s); + /* + * Do not output the first dependency (the + * source file), so that kbuild is not confused + * if a .c file is rewritten into .S or vice + * versa. + */ + if (!first) + printf(" %s \\\n", s); do_config_file(s); } + first = 0; m = p + 1; } printf("\n%s: $(deps_%s)\n\n", target, target); diff --git a/security/commoncap.c b/security/commoncap.c index 64c2ed9c901..a83e607d91c 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -52,13 +52,12 @@ static void warn_setuid_and_fcaps_mixed(const char *fname) int cap_netlink_send(struct sock *sk, struct sk_buff *skb) { - NETLINK_CB(skb).eff_cap = current_cap(); return 0; } int cap_netlink_recv(struct sk_buff *skb, int cap) { - if (!cap_raised(NETLINK_CB(skb).eff_cap, cap)) + if (!cap_raised(current_cap(), cap)) return -EPERM; return 0; } diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c8d69927068..cef42f5d69a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4669,6 +4669,7 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability) { int err; struct common_audit_data ad; + u32 sid; err = cap_netlink_recv(skb, capability); if (err) @@ -4677,8 +4678,9 @@ static int selinux_netlink_recv(struct sk_buff *skb, int capability) COMMON_AUDIT_DATA_INIT(&ad, CAP); ad.u.cap = capability; - return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid, - SECCLASS_CAPABILITY, CAP_TO_MASK(capability), &ad); + security_task_getsecid(current, &sid); + return avc_has_perm(sid, sid, SECCLASS_CAPABILITY, + CAP_TO_MASK(capability), &ad); } static int ipc_alloc_security(struct task_struct *task, diff --git a/sound/core/jack.c b/sound/core/jack.c index 4902ae56873..53b53e97c89 100644 --- a/sound/core/jack.c +++ b/sound/core/jack.c @@ -141,6 +141,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type, fail_input: input_free_device(jack->input_dev); + kfree(jack->id); kfree(jack); return err; } diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c index 23f49f356e0..16c0bdfbb16 100644 --- a/sound/pci/au88x0/au88x0_core.c +++ b/sound/pci/au88x0/au88x0_core.c @@ -1252,11 +1252,19 @@ static void vortex_adbdma_resetup(vortex_t *vortex, int adbdma) { static int inline vortex_adbdma_getlinearpos(vortex_t * vortex, int adbdma) { stream_t *dma = &vortex->dma_adb[adbdma]; - int temp; + int temp, page, delta; temp = hwread(vortex->mmio, VORTEX_ADBDMA_STAT + (adbdma << 2)); - temp = (dma->period_virt * dma->period_bytes) + (temp & (dma->period_bytes - 1)); - return temp; + page = (temp & ADB_SUBBUF_MASK) >> ADB_SUBBUF_SHIFT; + if (dma->nr_periods >= 4) + delta = (page - dma->period_real) & 3; + else { + delta = (page - dma->period_real); + if (delta < 0) + delta += dma->nr_periods; + } + return (dma->period_virt + delta) * dma->period_bytes + + (temp & (dma->period_bytes - 1)); } static void vortex_adbdma_startfifo(vortex_t * vortex, int adbdma) diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 0baffcdee8f..fcedad9a5fe 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2308,6 +2308,7 @@ static struct snd_pci_quirk position_fix_list[] __devinitdata = { SND_PCI_QUIRK(0x1043, 0x813d, "ASUS P5AD2", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x1043, 0x81e7, "ASUS M2V", POS_FIX_LPIB), + SND_PCI_QUIRK(0x1043, 0x8410, "ASUS", POS_FIX_LPIB), SND_PCI_QUIRK(0x104d, 0x9069, "Sony VPCS11V9E", POS_FIX_LPIB), SND_PCI_QUIRK(0x1106, 0x3288, "ASUS M2V-MX SE", POS_FIX_LPIB), SND_PCI_QUIRK(0x1179, 0xff10, "Toshiba A100-259", POS_FIX_LPIB), diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index fbe97d32140..4d5004e693f 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -3114,6 +3114,8 @@ static struct snd_pci_quirk cxt5066_cfg_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO), SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD), + SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD), SND_PCI_QUIRK(0x103c, 0x360b, "HP G60", CXT5066_HP_LAPTOP), SND_PCI_QUIRK(0x1043, 0x13f3, "Asus A52J", CXT5066_ASUS), SND_PCI_QUIRK(0x1043, 0x1643, "Asus K52JU", CXT5066_ASUS), @@ -3410,7 +3412,7 @@ static void cx_auto_parse_output(struct hda_codec *codec) } } spec->multiout.dac_nids = spec->private_dac_nids; - spec->multiout.max_channels = nums * 2; + spec->multiout.max_channels = spec->multiout.num_dacs * 2; if (cfg->hp_outs > 0) spec->auto_mute = 1; @@ -3729,9 +3731,9 @@ static int cx_auto_init(struct hda_codec *codec) return 0; } -static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, +static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename, const char *dir, int cidx, - hda_nid_t nid, int hda_dir) + hda_nid_t nid, int hda_dir, int amp_idx) { static char name[32]; static struct snd_kcontrol_new knew[] = { @@ -3743,7 +3745,8 @@ static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, for (i = 0; i < 2; i++) { struct snd_kcontrol *kctl; - knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, 0, hda_dir); + knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx, + hda_dir); knew[i].subdevice = HDA_SUBDEV_AMP_FLAG; knew[i].index = cidx; snprintf(name, sizeof(name), "%s%s %s", basename, dir, sfx[i]); @@ -3759,6 +3762,9 @@ static int cx_auto_add_volume(struct hda_codec *codec, const char *basename, return 0; } +#define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir) \ + cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0) + #define cx_auto_add_pb_volume(codec, nid, str, idx) \ cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT) @@ -3808,29 +3814,60 @@ static int cx_auto_build_input_controls(struct hda_codec *codec) struct conexant_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; static const char *prev_label; - int i, err, cidx; + int i, err, cidx, conn_len; + hda_nid_t conn[HDA_MAX_CONNECTIONS]; + + int multi_adc_volume = 0; /* If the ADC nid has several input volumes */ + int adc_nid = spec->adc_nids[0]; + + conn_len = snd_hda_get_connections(codec, adc_nid, conn, + HDA_MAX_CONNECTIONS); + if (conn_len < 0) + return conn_len; + + multi_adc_volume = cfg->num_inputs > 1 && conn_len > 1; + if (!multi_adc_volume) { + err = cx_auto_add_volume(codec, "Capture", "", 0, adc_nid, + HDA_INPUT); + if (err < 0) + return err; + } - err = cx_auto_add_volume(codec, "Capture", "", 0, spec->adc_nids[0], - HDA_INPUT); - if (err < 0) - return err; prev_label = NULL; cidx = 0; for (i = 0; i < cfg->num_inputs; i++) { hda_nid_t nid = cfg->inputs[i].pin; const char *label; - if (!(get_wcaps(codec, nid) & AC_WCAP_IN_AMP)) + int j; + int pin_amp = get_wcaps(codec, nid) & AC_WCAP_IN_AMP; + if (!pin_amp && !multi_adc_volume) continue; + label = hda_get_autocfg_input_label(codec, cfg, i); if (label == prev_label) cidx++; else cidx = 0; prev_label = label; - err = cx_auto_add_volume(codec, label, " Capture", cidx, - nid, HDA_INPUT); - if (err < 0) - return err; + + if (pin_amp) { + err = cx_auto_add_volume(codec, label, " Boost", cidx, + nid, HDA_INPUT); + if (err < 0) + return err; + } + + if (!multi_adc_volume) + continue; + for (j = 0; j < conn_len; j++) { + if (conn[j] == nid) { + err = cx_auto_add_volume_idx(codec, label, + " Capture", cidx, adc_nid, HDA_INPUT, j); + if (err < 0) + return err; + break; + } + } } return 0; } @@ -3902,6 +3939,8 @@ static struct hda_codec_preset snd_hda_preset_conexant[] = { .patch = patch_cxt5066 }, { .id = 0x14f15069, .name = "CX20585", .patch = patch_cxt5066 }, + { .id = 0x14f1506e, .name = "CX20590", + .patch = patch_cxt5066 }, { .id = 0x14f15097, .name = "CX20631", .patch = patch_conexant_auto }, { .id = 0x14f15098, .name = "CX20632", @@ -3928,6 +3967,7 @@ MODULE_ALIAS("snd-hda-codec-id:14f15066"); MODULE_ALIAS("snd-hda-codec-id:14f15067"); MODULE_ALIAS("snd-hda-codec-id:14f15068"); MODULE_ALIAS("snd-hda-codec-id:14f15069"); +MODULE_ALIAS("snd-hda-codec-id:14f1506e"); MODULE_ALIAS("snd-hda-codec-id:14f15097"); MODULE_ALIAS("snd-hda-codec-id:14f15098"); MODULE_ALIAS("snd-hda-codec-id:14f150a1"); diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index 9ea48b425d0..bd7b123f644 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -586,7 +586,12 @@ static hda_nid_t stac92hd83xxx_pin_nids[10] = { 0x0f, 0x10, 0x11, 0x1f, 0x20, }; -static hda_nid_t stac92hd88xxx_pin_nids[10] = { +static hda_nid_t stac92hd87xxx_pin_nids[6] = { + 0x0a, 0x0b, 0x0c, 0x0d, + 0x0f, 0x11, +}; + +static hda_nid_t stac92hd88xxx_pin_nids[8] = { 0x0a, 0x0b, 0x0c, 0x0d, 0x0f, 0x11, 0x1f, 0x20, }; @@ -5430,12 +5435,13 @@ again: switch (codec->vendor_id) { case 0x111d76d1: case 0x111d76d9: + case 0x111d76e5: spec->dmic_nids = stac92hd87b_dmic_nids; spec->num_dmics = stac92xx_connected_ports(codec, stac92hd87b_dmic_nids, STAC92HD87B_NUM_DMICS); - spec->num_pins = ARRAY_SIZE(stac92hd88xxx_pin_nids); - spec->pin_nids = stac92hd88xxx_pin_nids; + spec->num_pins = ARRAY_SIZE(stac92hd87xxx_pin_nids); + spec->pin_nids = stac92hd87xxx_pin_nids; spec->mono_nid = 0; spec->num_pwrs = 0; break; @@ -5443,6 +5449,7 @@ again: case 0x111d7667: case 0x111d7668: case 0x111d7669: + case 0x111d76e3: spec->num_dmics = stac92xx_connected_ports(codec, stac92hd88xxx_dmic_nids, STAC92HD88XXX_NUM_DMICS); @@ -6387,6 +6394,8 @@ static struct hda_codec_preset snd_hda_preset_sigmatel[] = { { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx }, { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx }, { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx}, + { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx}, + { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx}, { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx}, {} /* terminator */ }; diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index a76c3260d94..63b0054200a 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c @@ -567,7 +567,7 @@ static void via_auto_init_analog_input(struct hda_codec *codec) hda_nid_t nid = cfg->inputs[i].pin; if (spec->smart51_enabled && is_smart51_pins(spec, nid)) ctl = PIN_OUT; - else if (i == AUTO_PIN_MIC) + else if (cfg->inputs[i].type == AUTO_PIN_MIC) ctl = PIN_VREF50; else ctl = PIN_IN; diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index bb4bf65b9e7..0bb424af956 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -367,7 +367,7 @@ static int cx20442_codec_remove(struct snd_soc_codec *codec) return 0; } -static const u8 cx20442_reg = CX20442_TELOUT | CX20442_MIC; +static const u8 cx20442_reg; static struct snd_soc_codec_driver cx20442_codec_dev = { .probe = cx20442_codec_probe, diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 987476a5895..017d99ceb42 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -1482,7 +1482,7 @@ int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, WM8903_MICDET_EINT | WM8903_MICSHRT_EINT, irq_mask); - if (det && shrt) { + if (det || shrt) { /* Enable mic detection, this may not have been set through * platform data (eg, if the defaults are OK). */ snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index e8490f3edd0..e3ec2433b21 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h @@ -165,7 +165,7 @@ extern int wm8903_mic_detect(struct snd_soc_codec *codec, #define WM8903_VMID_RES_50K 2 #define WM8903_VMID_RES_250K 3 -#define WM8903_VMID_RES_5K 4 +#define WM8903_VMID_RES_5K 6 /* * R8 (0x08) - Analogue DAC 0 diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 37b8aa8a680..ebaee5ca743 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -107,6 +107,9 @@ struct wm8994_priv { int revision; struct wm8994_pdata *pdata; + + unsigned int aif1clk_enable:1; + unsigned int aif2clk_enable:1; }; static int wm8994_readable(unsigned int reg) @@ -1004,6 +1007,93 @@ static void wm8994_update_class_w(struct snd_soc_codec *codec) } } +static int late_enable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (wm8994->aif1clk_enable) + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, + WM8994_AIF1CLK_ENA); + if (wm8994->aif2clk_enable) + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, + WM8994_AIF2CLK_ENA); + break; + } + + return 0; +} + +static int late_disable_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + if (wm8994->aif1clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF1_CLOCKING_1, + WM8994_AIF1CLK_ENA_MASK, 0); + wm8994->aif1clk_enable = 0; + } + if (wm8994->aif2clk_enable) { + snd_soc_update_bits(codec, WM8994_AIF2_CLOCKING_1, + WM8994_AIF2CLK_ENA_MASK, 0); + wm8994->aif2clk_enable = 0; + } + break; + } + + return 0; +} + +static int aif1clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif1clk_enable = 1; + break; + } + + return 0; +} + +static int aif2clk_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + wm8994->aif2clk_enable = 1; + break; + } + + return 0; +} + +static int dac_ev(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_codec *codec = w->codec; + unsigned int mask = 1 << w->shift; + + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, mask); + return 0; +} + static const char *hp_mux_text[] = { "Mixer", "DAC", @@ -1272,6 +1362,47 @@ static const struct soc_enum aif2dacr_src_enum = static const struct snd_kcontrol_new aif2dacr_src_mux = SOC_DAPM_ENUM("AIF2DACR Mux", aif2dacr_src_enum); +static const struct snd_soc_dapm_widget wm8994_lateclk_revd_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", SND_SOC_NOPM, 0, 0, aif1clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_SUPPLY("AIF2CLK", SND_SOC_NOPM, 0, 0, aif2clk_ev, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + +SND_SOC_DAPM_PGA_E("Late DAC1L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC1R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2L Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_PGA_E("Late DAC2R Enable PGA", SND_SOC_NOPM, 0, 0, NULL, 0, + late_enable_ev, SND_SOC_DAPM_PRE_PMU), + +SND_SOC_DAPM_POST("Late Disable PGA", late_disable_ev) +}; + +static const struct snd_soc_dapm_widget wm8994_lateclk_widgets[] = { +SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0) +}; + +static const struct snd_soc_dapm_widget wm8994_dac_revd_widgets[] = { +SND_SOC_DAPM_DAC_E("DAC2L", NULL, SND_SOC_NOPM, 3, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC2R", NULL, SND_SOC_NOPM, 2, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1L", NULL, SND_SOC_NOPM, 1, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +SND_SOC_DAPM_DAC_E("DAC1R", NULL, SND_SOC_NOPM, 0, 0, + dac_ev, SND_SOC_DAPM_PRE_PMU), +}; + +static const struct snd_soc_dapm_widget wm8994_dac_widgets[] = { +SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), +SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), +SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), +}; + static const struct snd_soc_dapm_widget wm8994_dapm_widgets[] = { SND_SOC_DAPM_INPUT("DMIC1DAT"), SND_SOC_DAPM_INPUT("DMIC2DAT"), @@ -1284,9 +1415,6 @@ SND_SOC_DAPM_SUPPLY("DSP1CLK", WM8994_CLOCKING_1, 3, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSP2CLK", WM8994_CLOCKING_1, 2, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("DSPINTCLK", WM8994_CLOCKING_1, 1, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF1CLK", WM8994_AIF1_CLOCKING_1, 0, 0, NULL, 0), -SND_SOC_DAPM_SUPPLY("AIF2CLK", WM8994_AIF2_CLOCKING_1, 0, 0, NULL, 0), - SND_SOC_DAPM_AIF_OUT("AIF1ADC1L", NULL, 0, WM8994_POWER_MANAGEMENT_4, 9, 0), SND_SOC_DAPM_AIF_OUT("AIF1ADC1R", NULL, @@ -1372,11 +1500,6 @@ SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ADCL Mux", WM8994_POWER_MANAGEMENT_4, 1, 0, &adcl_mux), SND_SOC_DAPM_MUX("ADCR Mux", WM8994_POWER_MANAGEMENT_4, 0, 0, &adcr_mux), -SND_SOC_DAPM_DAC("DAC2L", NULL, WM8994_POWER_MANAGEMENT_5, 3, 0), -SND_SOC_DAPM_DAC("DAC2R", NULL, WM8994_POWER_MANAGEMENT_5, 2, 0), -SND_SOC_DAPM_DAC("DAC1L", NULL, WM8994_POWER_MANAGEMENT_5, 1, 0), -SND_SOC_DAPM_DAC("DAC1R", NULL, WM8994_POWER_MANAGEMENT_5, 0, 0), - SND_SOC_DAPM_MUX("Left Headphone Mux", SND_SOC_NOPM, 0, 0, &hpl_mux), SND_SOC_DAPM_MUX("Right Headphone Mux", SND_SOC_NOPM, 0, 0, &hpr_mux), @@ -1516,14 +1639,12 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2ADC Mux", "AIF3DACDAT", "AIF3ADCDAT" }, /* DAC1 inputs */ - { "DAC1L", NULL, "DAC1L Mixer" }, { "DAC1L Mixer", "AIF2 Switch", "AIF2DACL" }, { "DAC1L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "DAC1L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, { "DAC1L Mixer", "Left Sidetone Switch", "Left Sidetone" }, { "DAC1L Mixer", "Right Sidetone Switch", "Right Sidetone" }, - { "DAC1R", NULL, "DAC1R Mixer" }, { "DAC1R Mixer", "AIF2 Switch", "AIF2DACR" }, { "DAC1R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "DAC1R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, @@ -1532,7 +1653,6 @@ static const struct snd_soc_dapm_route intercon[] = { /* DAC2/AIF2 outputs */ { "AIF2ADCL", NULL, "AIF2DAC2L Mixer" }, - { "DAC2L", NULL, "AIF2DAC2L Mixer" }, { "AIF2DAC2L Mixer", "AIF2 Switch", "AIF2DACL" }, { "AIF2DAC2L Mixer", "AIF1.2 Switch", "AIF1DAC2L" }, { "AIF2DAC2L Mixer", "AIF1.1 Switch", "AIF1DAC1L" }, @@ -1540,7 +1660,6 @@ static const struct snd_soc_dapm_route intercon[] = { { "AIF2DAC2L Mixer", "Right Sidetone Switch", "Right Sidetone" }, { "AIF2ADCR", NULL, "AIF2DAC2R Mixer" }, - { "DAC2R", NULL, "AIF2DAC2R Mixer" }, { "AIF2DAC2R Mixer", "AIF2 Switch", "AIF2DACR" }, { "AIF2DAC2R Mixer", "AIF1.2 Switch", "AIF1DAC2R" }, { "AIF2DAC2R Mixer", "AIF1.1 Switch", "AIF1DAC1R" }, @@ -1584,6 +1703,24 @@ static const struct snd_soc_dapm_route intercon[] = { { "Right Headphone Mux", "DAC", "DAC1R" }, }; +static const struct snd_soc_dapm_route wm8994_lateclk_revd_intercon[] = { + { "DAC1L", NULL, "Late DAC1L Enable PGA" }, + { "Late DAC1L Enable PGA", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "Late DAC1R Enable PGA" }, + { "Late DAC1R Enable PGA", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "Late DAC2L Enable PGA" }, + { "Late DAC2L Enable PGA", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "Late DAC2R Enable PGA" }, + { "Late DAC2R Enable PGA", NULL, "AIF2DAC2R Mixer" } +}; + +static const struct snd_soc_dapm_route wm8994_lateclk_intercon[] = { + { "DAC1L", NULL, "DAC1L Mixer" }, + { "DAC1R", NULL, "DAC1R Mixer" }, + { "DAC2L", NULL, "AIF2DAC2L Mixer" }, + { "DAC2R", NULL, "AIF2DAC2R Mixer" }, +}; + static const struct snd_soc_dapm_route wm8994_revd_intercon[] = { { "AIF1DACDAT", NULL, "AIF2DACDAT" }, { "AIF2DACDAT", NULL, "AIF1DACDAT" }, @@ -2514,6 +2651,22 @@ static int wm8994_resume(struct snd_soc_codec *codec) { struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); int i, ret; + unsigned int val, mask; + + if (wm8994->revision < 4) { + /* force a HW read */ + val = wm8994_reg_read(codec->control_data, + WM8994_POWER_MANAGEMENT_5); + + /* modify the cache only */ + codec->cache_only = 1; + mask = WM8994_DAC1R_ENA | WM8994_DAC1L_ENA | + WM8994_DAC2R_ENA | WM8994_DAC2L_ENA; + val &= mask; + snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5, + mask, val); + codec->cache_only = 0; + } /* Restore the registers */ ret = snd_soc_cache_sync(codec); @@ -2847,11 +3000,10 @@ static void wm8958_default_micdet(u16 status, void *data) report |= SND_JACK_BTN_5; done: - snd_soc_jack_report(wm8994->micdet[0].jack, + snd_soc_jack_report(wm8994->micdet[0].jack, report, SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5 | - SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT, - report); + SND_JACK_MICROPHONE | SND_JACK_VIDEOOUT); } /** @@ -3125,6 +3277,17 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) case WM8994: snd_soc_dapm_new_controls(dapm, wm8994_specific_dapm_widgets, ARRAY_SIZE(wm8994_specific_dapm_widgets)); + if (wm8994->revision < 4) { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_revd_widgets, + ARRAY_SIZE(wm8994_lateclk_revd_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_revd_widgets, + ARRAY_SIZE(wm8994_dac_revd_widgets)); + } else { + snd_soc_dapm_new_controls(dapm, wm8994_lateclk_widgets, + ARRAY_SIZE(wm8994_lateclk_widgets)); + snd_soc_dapm_new_controls(dapm, wm8994_dac_widgets, + ARRAY_SIZE(wm8994_dac_widgets)); + } break; case WM8958: snd_soc_add_controls(codec, wm8958_snd_controls, @@ -3143,10 +3306,15 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(dapm, wm8994_intercon, ARRAY_SIZE(wm8994_intercon)); - if (wm8994->revision < 4) + if (wm8994->revision < 4) { snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon, ARRAY_SIZE(wm8994_revd_intercon)); - + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon, + ARRAY_SIZE(wm8994_lateclk_revd_intercon)); + } else { + snd_soc_dapm_add_routes(dapm, wm8994_lateclk_intercon, + ARRAY_SIZE(wm8994_lateclk_intercon)); + } break; case WM8958: snd_soc_dapm_add_routes(dapm, wm8958_intercon, diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c index 613df5db0b3..51689270606 100644 --- a/sound/soc/codecs/wm_hubs.c +++ b/sound/soc/codecs/wm_hubs.c @@ -674,6 +674,9 @@ SND_SOC_DAPM_OUTPUT("LINEOUT2N"), }; static const struct snd_soc_dapm_route analogue_routes[] = { + { "MICBIAS1", NULL, "CLK_SYS" }, + { "MICBIAS2", NULL, "CLK_SYS" }, + { "IN1L PGA", "IN1LP Switch", "IN1LP" }, { "IN1L PGA", "IN1LN Switch", "IN1LN" }, diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c index e20c9e1457c..1e9bccae4e8 100644 --- a/sound/soc/imx/eukrea-tlv320.c +++ b/sound/soc/imx/eukrea-tlv320.c @@ -79,7 +79,7 @@ static struct snd_soc_dai_link eukrea_tlv320_dai = { .name = "tlv320aic23", .stream_name = "TLV320AIC23", .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-pcm-audio.0", + .platform_name = "imx-fiq-pcm-audio.0", .codec_name = "tlv320aic23-codec.0-001a", .cpu_dai_name = "imx-ssi.0", .ops = &eukrea_tlv320_snd_ops, diff --git a/sound/soc/pxa/e740_wm9705.c b/sound/soc/pxa/e740_wm9705.c index 28333e7d9c5..dc65650a6fa 100644 --- a/sound/soc/pxa/e740_wm9705.c +++ b/sound/soc/pxa/e740_wm9705.c @@ -117,7 +117,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -126,7 +126,7 @@ static struct snd_soc_dai_link e740_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c index 01bf31675c5..51897fcd911 100644 --- a/sound/soc/pxa/e750_wm9705.c +++ b/sound/soc/pxa/e750_wm9705.c @@ -99,7 +99,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9705-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", @@ -109,7 +109,7 @@ static struct snd_soc_dai_link e750_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9705-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9705-codec", diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c index c6a37c6ef23..053ed208e59 100644 --- a/sound/soc/pxa/e800_wm9712.c +++ b/sound/soc/pxa/e800_wm9712.c @@ -89,7 +89,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -98,7 +98,7 @@ static struct snd_soc_dai_link e800_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c index fc22e6eefc9..b13a4252812 100644 --- a/sound/soc/pxa/em-x270.c +++ b/sound/soc/pxa/em-x270.c @@ -37,7 +37,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -45,7 +45,7 @@ static struct snd_soc_dai_link em_x270_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c index 0d70fc8c12b..38ca6759907 100644 --- a/sound/soc/pxa/mioa701_wm9713.c +++ b/sound/soc/pxa/mioa701_wm9713.c @@ -162,7 +162,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9713-hifi", .codec_name = "wm9713-codec", .init = mioa701_wm9713_init, @@ -172,7 +172,7 @@ static struct snd_soc_dai_link mioa701_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name ="wm9713-aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/palm27x.c b/sound/soc/pxa/palm27x.c index 857db96d4a4..504e4004f00 100644 --- a/sound/soc/pxa/palm27x.c +++ b/sound/soc/pxa/palm27x.c @@ -132,7 +132,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 HiFi", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", @@ -141,7 +141,7 @@ static struct snd_soc_dai_link palm27x_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .codec_name = "wm9712-codec", .platform_name = "pxa-pcm-audio", diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index f75804ef089..4b6e5d608b4 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -219,7 +219,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97", .stream_name = "AC97 HiFi", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_dai_name = "wm9712-hifi", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", @@ -229,7 +229,7 @@ static struct snd_soc_dai_link tosa_dai[] = { { .name = "AC97 Aux", .stream_name = "AC97 Aux", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_dai_name = "wm9712-aux", .platform_name = "pxa-pcm-audio", .codec_name = "wm9712-codec", diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c index b222a7d7202..25bba108fea 100644 --- a/sound/soc/pxa/zylonite.c +++ b/sound/soc/pxa/zylonite.c @@ -166,7 +166,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 HiFi", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.0", + .cpu_dai_name = "pxa2xx-ac97", .codec_name = "wm9713-hifi", .init = zylonite_wm9713_init, }, @@ -175,7 +175,7 @@ static struct snd_soc_dai_link zylonite_dai[] = { .stream_name = "AC97 Aux", .codec_name = "wm9713-codec", .platform_name = "pxa-pcm-audio", - .cpu_dai_name = "pxa-ac97.1", + .cpu_dai_name = "pxa2xx-ac97-aux", .codec_name = "wm9713-aux", }, { diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8194f150bab..25e54230cc6 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -712,7 +712,15 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) !path->connected(path->source, path->sink)) continue; - if (path->sink && path->sink->power_check && + if (!path->sink) + continue; + + if (path->sink->force) { + power = 1; + break; + } + + if (path->sink->power_check && path->sink->power_check(path->sink)) { power = 1; break; @@ -1627,6 +1635,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) { struct snd_soc_dapm_widget *w; + unsigned int val; list_for_each_entry(w, &dapm->card->widgets, list) { @@ -1675,6 +1684,18 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) case snd_soc_dapm_post: break; } + + /* Read the initial power state from the device */ + if (w->reg >= 0) { + val = snd_soc_read(w->codec, w->reg); + val &= 1 << w->shift; + if (w->invert) + val = !val; + + if (val) + w->power = 1; + } + w->new = 1; } diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c index 68b97477577..66eabafb1c2 100644 --- a/sound/usb/caiaq/audio.c +++ b/sound/usb/caiaq/audio.c @@ -785,7 +785,7 @@ int snd_usb_caiaq_audio_init(struct snd_usb_caiaqdev *dev) } dev->pcm->private_data = dev; - strcpy(dev->pcm->name, dev->product_name); + strlcpy(dev->pcm->name, dev->product_name, sizeof(dev->pcm->name)); memset(dev->sub_playback, 0, sizeof(dev->sub_playback)); memset(dev->sub_capture, 0, sizeof(dev->sub_capture)); diff --git a/sound/usb/caiaq/midi.c b/sound/usb/caiaq/midi.c index 2f218c77fff..a1a47088fd0 100644 --- a/sound/usb/caiaq/midi.c +++ b/sound/usb/caiaq/midi.c @@ -136,7 +136,7 @@ int snd_usb_caiaq_midi_init(struct snd_usb_caiaqdev *device) if (ret < 0) return ret; - strcpy(rmidi->name, device->product_name); + strlcpy(rmidi->name, device->product_name, sizeof(rmidi->name)); rmidi->info_flags = SNDRV_RAWMIDI_INFO_DUPLEX; rmidi->private_data = device; diff --git a/sound/usb/card.c b/sound/usb/card.c index 800f7cb4f25..c0f8270bc19 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -323,6 +323,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, return -ENOMEM; } + mutex_init(&chip->shutdown_mutex); chip->index = idx; chip->dev = dev; chip->card = card; @@ -531,6 +532,7 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) chip = ptr; card = chip->card; mutex_lock(®ister_mutex); + mutex_lock(&chip->shutdown_mutex); chip->shutdown = 1; chip->num_interfaces--; if (chip->num_interfaces <= 0) { @@ -548,9 +550,11 @@ static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr) snd_usb_mixer_disconnect(p); } usb_chip[chip->index] = NULL; + mutex_unlock(&chip->shutdown_mutex); mutex_unlock(®ister_mutex); snd_card_free_when_closed(card); } else { + mutex_unlock(&chip->shutdown_mutex); mutex_unlock(®ister_mutex); } } diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 4132522ac90..e3f680526cb 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -361,6 +361,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, } if (changed) { + mutex_lock(&subs->stream->chip->shutdown_mutex); /* format changed */ snd_usb_release_substream_urbs(subs, 0); /* influenced: period_bytes, channels, rate, format, */ @@ -368,6 +369,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, params_rate(hw_params), snd_pcm_format_physical_width(params_format(hw_params)) * params_channels(hw_params)); + mutex_unlock(&subs->stream->chip->shutdown_mutex); } return ret; @@ -385,8 +387,9 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->cur_audiofmt = NULL; subs->cur_rate = 0; subs->period_bytes = 0; - if (!subs->stream->chip->shutdown) - snd_usb_release_substream_urbs(subs, 0); + mutex_lock(&subs->stream->chip->shutdown_mutex); + snd_usb_release_substream_urbs(subs, 0); + mutex_unlock(&subs->stream->chip->shutdown_mutex); return snd_pcm_lib_free_vmalloc_buffer(substream); } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index db3eb21627e..6e66fffe87f 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -36,6 +36,7 @@ struct snd_usb_audio { struct snd_card *card; u32 usb_id; int shutdown; + struct mutex shutdown_mutex; unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */ int num_interfaces; int num_suspended_intf; diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 746cf03cb05..0ace786e83e 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c @@ -264,9 +264,6 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end) c->start_time = start; if (p->start_time == 0 || p->start_time > start) p->start_time = start; - - if (cpu > numcpus) - numcpus = cpu; } #define MAX_CPUS 4096 @@ -511,6 +508,9 @@ static int process_sample_event(event_t *event __used, if (!event_str) return 0; + if (sample->cpu > numcpus) + numcpus = sample->cpu; + if (strcmp(event_str, "power:cpu_idle") == 0) { struct power_processor_entry *ppe = (void *)te; if (ppe->state == (u32)PWR_EVENT_EXIT) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 32f4f1f2f6e..df51560f16f 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -585,6 +585,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, { struct sort_entry *se; u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us; + u64 nr_events; const char *sep = symbol_conf.field_sep; int ret; @@ -593,6 +594,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, if (pair_hists) { period = self->pair ? self->pair->period : 0; + nr_events = self->pair ? self->pair->nr_events : 0; total = pair_hists->stats.total_period; period_sys = self->pair ? self->pair->period_sys : 0; period_us = self->pair ? self->pair->period_us : 0; @@ -600,6 +602,7 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, period_guest_us = self->pair ? self->pair->period_guest_us : 0; } else { period = self->period; + nr_events = self->nr_events; total = session_total; period_sys = self->period_sys; period_us = self->period_us; @@ -640,9 +643,9 @@ int hist_entry__snprintf(struct hist_entry *self, char *s, size_t size, if (symbol_conf.show_nr_samples) { if (sep) - ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period); + ret += snprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events); else - ret += snprintf(s + ret, size - ret, "%11" PRIu64, period); + ret += snprintf(s + ret, size - ret, "%11" PRIu64, nr_events); } if (pair_hists) { diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index fb737fe9be9..96c866045d6 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -456,9 +456,9 @@ void svg_legenda(void) return; svg_legenda_box(0, "Running", "sample"); - svg_legenda_box(100, "Idle","rect.c1"); - svg_legenda_box(200, "Deeper Idle", "rect.c3"); - svg_legenda_box(350, "Deepest Idle", "rect.c6"); + svg_legenda_box(100, "Idle","c1"); + svg_legenda_box(200, "Deeper Idle", "c3"); + svg_legenda_box(350, "Deepest Idle", "c6"); svg_legenda_box(550, "Sleeping", "process2"); svg_legenda_box(650, "Waiting for cpu", "waiting"); svg_legenda_box(800, "Blocked on IO", "blocked"); |