diff options
515 files changed, 37559 insertions, 15218 deletions
diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 98ac0d7552a..c6d5fee888c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -199,15 +199,16 @@ tcp_early_retrans - INTEGER Default: 2 tcp_ecn - INTEGER - Enable Explicit Congestion Notification (ECN) in TCP. ECN is only - used when both ends of the TCP flow support it. It is useful to - avoid losses due to congestion (when the bottleneck router supports - ECN). + Control use of Explicit Congestion Notification (ECN) by TCP. + ECN is used only when both ends of the TCP connection indicate + support for it. This feature is useful in avoiding losses due + to congestion by allowing supporting routers to signal + congestion before having to drop packets. Possible values are: - 0 disable ECN - 1 ECN enabled - 2 Only server-side ECN enabled. If the other end does - not support ECN, behavior is like with ECN disabled. + 0 Disable ECN. Neither initiate nor accept ECN. + 1 Always request ECN on outgoing connection attempts. + 2 Enable ECN when requested by incomming connections + but do not request ECN on outgoing connections. Default: 2 tcp_fack - BOOLEAN diff --git a/Documentation/networking/stmmac.txt b/Documentation/networking/stmmac.txt index ef9ee71b4d7..f9fa6db40a5 100644 --- a/Documentation/networking/stmmac.txt +++ b/Documentation/networking/stmmac.txt @@ -29,11 +29,9 @@ The kernel configuration option is STMMAC_ETH: dma_txsize: DMA tx ring size; buf_sz: DMA buffer size; tc: control the HW FIFO threshold; - tx_coe: Enable/Disable Tx Checksum Offload engine; watchdog: transmit timeout (in milliseconds); flow_ctrl: Flow control ability [on/off]; pause: Flow Control Pause Time; - tmrate: timer period (only if timer optimisation is configured). 3) Command line options Driver parameters can be also passed in command line by using: @@ -60,17 +58,19 @@ Then the poll method will be scheduled at some future point. The incoming packets are stored, by the DMA, in a list of pre-allocated socket buffers in order to avoid the memcpy (Zero-copy). -4.3) Timer-Driver Interrupt -Instead of having the device that asynchronously notifies the frame receptions, -the driver configures a timer to generate an interrupt at regular intervals. -Based on the granularity of the timer, the frames that are received by the -device will experience different levels of latency. Some NICs have dedicated -timer device to perform this task. STMMAC can use either the RTC device or the -TMU channel 2 on STLinux platforms. -The timers frequency can be passed to the driver as parameter; when change it, -take care of both hardware capability and network stability/performance impact. -Several performance tests on STM platforms showed this optimisation allows to -spare the CPU while having the maximum throughput. +4.3) Interrupt Mitigation +The driver is able to mitigate the number of its DMA interrupts +using NAPI for the reception on chips older than the 3.50. +New chips have an HW RX-Watchdog used for this mitigation. + +On Tx-side, the mitigation schema is based on a SW timer that calls the +tx function (stmmac_tx) to reclaim the resource after transmitting the +frames. +Also there is another parameter (like a threshold) used to program +the descriptors avoiding to set the interrupt on completion bit in +when the frame is sent (xmit). + +Mitigation parameters can be tuned by ethtool. 4.4) WOL Wake up on Lan feature through Magic and Unicast frames are supported for the @@ -121,6 +121,7 @@ struct plat_stmmacenet_data { int bugged_jumbo; int pmt; int force_sf_dma_mode; + int riwt_off; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); int (*init)(struct platform_device *pdev); @@ -156,6 +157,7 @@ Where: o pmt: core has the embedded power module (optional). o force_sf_dma_mode: force DMA to use the Store and Forward mode instead of the Threshold. + o riwt_off: force to disable the RX watchdog feature and switch to NAPI mode. o fix_mac_speed: this callback is used for modifying some syscfg registers (on ST SoCs) according to the link speed negotiated by the physical layer . diff --git a/MAINTAINERS b/MAINTAINERS index 9087e0d6f1a..13f472188e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5157,6 +5157,7 @@ F: net/nfc/ F: include/linux/nfc.h F: include/net/nfc/ F: drivers/nfc/ +F: include/linux/platform_data/pn544.h NFS, SUNRPC, AND LOCKD CLIENTS M: Trond Myklebust <Trond.Myklebust@netapp.com> @@ -7453,8 +7454,7 @@ S: Maintained F: drivers/net/ethernet/dec/tulip/ TUN/TAP driver -M: Maxim Krasnyansky <maxk@qualcomm.com> -L: vtun@office.satix.net +M: Maxim Krasnyansky <maxk@qti.qualcomm.com> W: http://vtun.sourceforge.net/tun S: Maintained F: Documentation/networking/tuntap.txt @@ -1,7 +1,7 @@ VERSION = 3 PATCHLEVEL = 7 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = -rc7 NAME = Terrified Chipmunk # *DOCUMENTATION* diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 90275f036cd..49ca86e37b8 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -652,6 +652,15 @@ __setup_mmu: sub r3, r4, #16384 @ Page directory size mov pc, lr ENDPROC(__setup_mmu) +@ Enable unaligned access on v6, to allow better code generation +@ for the decompressor C code: +__armv6_mmu_cache_on: + mrc p15, 0, r0, c1, c0, 0 @ read SCTLR + bic r0, r0, #2 @ A (no unaligned access fault) + orr r0, r0, #1 << 22 @ U (v6 unaligned access model) + mcr p15, 0, r0, c1, c0, 0 @ write SCTLR + b __armv4_mmu_cache_on + __arm926ejs_mmu_cache_on: #ifdef CONFIG_CPU_DCACHE_WRITETHROUGH mov r0, #4 @ put dcache in WT mode @@ -694,6 +703,9 @@ __armv7_mmu_cache_on: bic r0, r0, #1 << 28 @ clear SCTLR.TRE orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement orr r0, r0, #0x003c @ write buffer + bic r0, r0, #2 @ A (no unaligned access fault) + orr r0, r0, #1 << 22 @ U (v6 unaligned access model) + @ (needed for ARM1176) #ifdef CONFIG_MMU #ifdef CONFIG_CPU_ENDIAN_BE8 orr r0, r0, #1 << 25 @ big-endian page tables @@ -914,7 +926,7 @@ proc_types: .word 0x0007b000 @ ARMv6 .word 0x000ff000 - W(b) __armv4_mmu_cache_on + W(b) __armv6_mmu_cache_on W(b) __armv4_mmu_cache_off W(b) __armv6_mmu_cache_flush diff --git a/arch/arm/mm/proc-v6.S b/arch/arm/mm/proc-v6.S index 86b8b480634..09c5233f4df 100644 --- a/arch/arm/mm/proc-v6.S +++ b/arch/arm/mm/proc-v6.S @@ -89,7 +89,7 @@ ENTRY(cpu_v6_dcache_clean_area) mov pc, lr /* - * cpu_arm926_switch_mm(pgd_phys, tsk) + * cpu_v6_switch_mm(pgd_phys, tsk) * * Set the translation table base pointer to be pgd_phys * diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index a53f8ec37aa..290dc6a1d7a 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -79,7 +79,7 @@ static struct resource data_resource = { .name = "Kernel data", }; void __init add_memory_region(phys_t start, phys_t size, long type) { int x = boot_mem_map.nr_map; - struct boot_mem_map_entry *prev = boot_mem_map.map + x - 1; + int i; /* Sanity check */ if (start + size < start) { @@ -88,15 +88,29 @@ void __init add_memory_region(phys_t start, phys_t size, long type) } /* - * Try to merge with previous entry if any. This is far less than - * perfect but is sufficient for most real world cases. + * Try to merge with existing entry, if any. */ - if (x && prev->addr + prev->size == start && prev->type == type) { - prev->size += size; + for (i = 0; i < boot_mem_map.nr_map; i++) { + struct boot_mem_map_entry *entry = boot_mem_map.map + i; + unsigned long top; + + if (entry->type != type) + continue; + + if (start + size < entry->addr) + continue; /* no overlap */ + + if (entry->addr + entry->size < start) + continue; /* no overlap */ + + top = max(entry->addr + entry->size, start + size); + entry->addr = min(entry->addr, start); + entry->size = top - entry->addr; + return; } - if (x == BOOT_MEM_MAP_MAX) { + if (boot_mem_map.nr_map == BOOT_MEM_MAP_MAX) { pr_err("Ooops! Too many entries in the memory map!\n"); return; } diff --git a/arch/mips/lib/mips-atomic.c b/arch/mips/lib/mips-atomic.c index e091430dbeb..cd160be3ce4 100644 --- a/arch/mips/lib/mips-atomic.c +++ b/arch/mips/lib/mips-atomic.c @@ -56,7 +56,7 @@ __asm__( " .set pop \n" " .endm \n"); -void arch_local_irq_disable(void) +notrace void arch_local_irq_disable(void) { preempt_disable(); __asm__ __volatile__( @@ -93,7 +93,7 @@ __asm__( " .set pop \n" " .endm \n"); -unsigned long arch_local_irq_save(void) +notrace unsigned long arch_local_irq_save(void) { unsigned long flags; preempt_disable(); @@ -135,7 +135,7 @@ __asm__( " .set pop \n" " .endm \n"); -void arch_local_irq_restore(unsigned long flags) +notrace void arch_local_irq_restore(unsigned long flags) { unsigned long __tmp1; @@ -159,7 +159,7 @@ void arch_local_irq_restore(unsigned long flags) EXPORT_SYMBOL(arch_local_irq_restore); -void __arch_local_irq_restore(unsigned long flags) +notrace void __arch_local_irq_restore(unsigned long flags) { unsigned long __tmp1; diff --git a/arch/powerpc/platforms/pseries/eeh_pe.c b/arch/powerpc/platforms/pseries/eeh_pe.c index 797cd181dc3..d16c8ded108 100644 --- a/arch/powerpc/platforms/pseries/eeh_pe.c +++ b/arch/powerpc/platforms/pseries/eeh_pe.c @@ -449,7 +449,7 @@ int eeh_rmv_from_parent_pe(struct eeh_dev *edev, int purge_pe) if (list_empty(&pe->edevs)) { cnt = 0; list_for_each_entry(child, &pe->child_list, child) { - if (!(pe->type & EEH_PE_INVALID)) { + if (!(child->type & EEH_PE_INVALID)) { cnt++; break; } diff --git a/arch/powerpc/platforms/pseries/msi.c b/arch/powerpc/platforms/pseries/msi.c index d19f4977c83..e5b08472313 100644 --- a/arch/powerpc/platforms/pseries/msi.c +++ b/arch/powerpc/platforms/pseries/msi.c @@ -220,7 +220,8 @@ static struct device_node *find_pe_dn(struct pci_dev *dev, int *total) /* Get the top level device in the PE */ edev = of_node_to_eeh_dev(dn); - edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list); + if (edev->pe) + edev = list_first_entry(&edev->pe->edevs, struct eeh_dev, list); dn = eeh_dev_to_of_node(edev); if (!dn) return NULL; diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 89b30f32ba6..ff7bb8a42ed 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -1961,6 +1961,7 @@ static int __devinit ucode_init (loader_block * lb, amb_dev * dev) { res = loader_verify(lb, dev, rec); if (res) break; + rec = ihex_next_binrec(rec); } release_firmware(fw); if (!res) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index ee82f2fb65f..a1d4ede5b89 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -96,6 +96,7 @@ static struct usb_device_id btusb_table[] = { { USB_DEVICE(0x0c10, 0x0000) }, /* Broadcom BCM20702A0 */ + { USB_DEVICE(0x0b05, 0x17b5) }, { USB_DEVICE(0x04ca, 0x2003) }, { USB_DEVICE(0x0489, 0xe042) }, { USB_DEVICE(0x413c, 0x8197) }, diff --git a/drivers/isdn/mISDN/tei.c b/drivers/isdn/mISDN/tei.c index be88728f110..592f597d895 100644 --- a/drivers/isdn/mISDN/tei.c +++ b/drivers/isdn/mISDN/tei.c @@ -250,7 +250,7 @@ tei_debug(struct FsmInst *fi, char *fmt, ...) static int get_free_id(struct manager *mgr) { - u64 ids = 0; + DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; int i; struct layer2 *l2; @@ -261,11 +261,11 @@ get_free_id(struct manager *mgr) __func__); return -EBUSY; } - test_and_set_bit(l2->ch.nr, (u_long *)&ids); + __set_bit(l2->ch.nr, ids); } - for (i = 1; i < 64; i++) - if (!test_bit(i, (u_long *)&ids)) - return i; + i = find_next_zero_bit(ids, 64, 1); + if (i < 64) + return i; printk(KERN_WARNING "%s: more as 63 layer2 for one device\n", __func__); return -EBUSY; @@ -274,7 +274,7 @@ get_free_id(struct manager *mgr) static int get_free_tei(struct manager *mgr) { - u64 ids = 0; + DECLARE_BITMAP(ids, 64) = { [0 ... BITS_TO_LONGS(64) - 1] = 0 }; int i; struct layer2 *l2; @@ -288,11 +288,11 @@ get_free_tei(struct manager *mgr) continue; i -= 64; - test_and_set_bit(i, (u_long *)&ids); + __set_bit(i, ids); } - for (i = 0; i < 64; i++) - if (!test_bit(i, (u_long *)&ids)) - return i + 64; + i = find_first_zero_bit(ids, 64); + if (i < 64) + return i + 64; printk(KERN_WARNING "%s: more as 63 dynamic tei for one device\n", __func__); return -1; diff --git a/drivers/media/dvb-frontends/stv0900_core.c b/drivers/media/dvb-frontends/stv0900_core.c index 262dfa503c2..b551ca350e0 100644 --- a/drivers/media/dvb-frontends/stv0900_core.c +++ b/drivers/media/dvb-frontends/stv0900_core.c @@ -300,15 +300,15 @@ static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 { u32 m_div, clk_sel; - dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, - intp->quartz); - if (intp == NULL) return STV0900_INVALID_HANDLE; if (intp->errs) return STV0900_I2C_ERROR; + dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, + intp->quartz); + clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); m_div = ((clk_sel * mclk) / intp->quartz) - 1; stv0900_write_bits(intp, F0900_M_DIV, m_div); diff --git a/drivers/media/i2c/adv7604.c b/drivers/media/i2c/adv7604.c index 109bc9b12e7..05f8950f6f9 100644 --- a/drivers/media/i2c/adv7604.c +++ b/drivers/media/i2c/adv7604.c @@ -53,8 +53,7 @@ MODULE_LICENSE("GPL"); /* ADV7604 system clock frequency */ #define ADV7604_fsc (28636360) -#define DIGITAL_INPUT ((state->prim_mode == ADV7604_PRIM_MODE_HDMI_COMP) || \ - (state->prim_mode == ADV7604_PRIM_MODE_HDMI_GR)) +#define DIGITAL_INPUT (state->mode == ADV7604_MODE_HDMI) /* ********************************************************************** @@ -68,7 +67,7 @@ struct adv7604_state { struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; - enum adv7604_prim_mode prim_mode; + enum adv7604_mode mode; struct v4l2_dv_timings timings; u8 edid[256]; unsigned edid_blocks; @@ -77,6 +76,7 @@ struct adv7604_state { struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; bool connector_hdmi; + bool restart_stdi_once; /* i2c clients */ struct i2c_client *i2c_avlink; @@ -106,7 +106,6 @@ static const struct v4l2_dv_timings adv7604_timings[] = { V4L2_DV_BT_CEA_720X576P50, V4L2_DV_BT_CEA_1280X720P24, V4L2_DV_BT_CEA_1280X720P25, - V4L2_DV_BT_CEA_1280X720P30, V4L2_DV_BT_CEA_1280X720P50, V4L2_DV_BT_CEA_1280X720P60, V4L2_DV_BT_CEA_1920X1080P24, @@ -115,6 +114,7 @@ static const struct v4l2_dv_timings adv7604_timings[] = { V4L2_DV_BT_CEA_1920X1080P50, V4L2_DV_BT_CEA_1920X1080P60, + /* sorted by DMT ID */ V4L2_DV_BT_DMT_640X350P85, V4L2_DV_BT_DMT_640X400P85, V4L2_DV_BT_DMT_720X400P85, @@ -164,6 +164,89 @@ static const struct v4l2_dv_timings adv7604_timings[] = { { }, }; +struct adv7604_video_standards { + struct v4l2_dv_timings timings; + u8 vid_std; + u8 v_freq; +}; + +/* sorted by number of lines */ +static const struct adv7604_video_standards adv7604_prim_mode_comp[] = { + /* { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, TODO flickering */ + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x19, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x19, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + /* TODO add 1920x1080P60_RB (CVT timing) */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7604_video_standards adv7604_prim_mode_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { V4L2_DV_BT_DMT_1360X768P60, 0x12, 0x00 }, + { V4L2_DV_BT_DMT_1366X768P60, 0x13, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P60, 0x14, 0x00 }, + { V4L2_DV_BT_DMT_1400X1050P75, 0x15, 0x00 }, + { V4L2_DV_BT_DMT_1600X1200P60, 0x16, 0x00 }, /* TODO not tested */ + /* TODO add 1600X1200P60_RB (not a DMT timing) */ + { V4L2_DV_BT_DMT_1680X1050P60, 0x18, 0x00 }, + { V4L2_DV_BT_DMT_1920X1200P60_RB, 0x19, 0x00 }, /* TODO not tested */ + { }, +}; + +/* sorted by number of lines */ +static const struct adv7604_video_standards adv7604_prim_mode_hdmi_comp[] = { + { V4L2_DV_BT_CEA_720X480P59_94, 0x0a, 0x00 }, + { V4L2_DV_BT_CEA_720X576P50, 0x0b, 0x00 }, + { V4L2_DV_BT_CEA_1280X720P50, 0x13, 0x01 }, + { V4L2_DV_BT_CEA_1280X720P60, 0x13, 0x00 }, + { V4L2_DV_BT_CEA_1920X1080P24, 0x1e, 0x04 }, + { V4L2_DV_BT_CEA_1920X1080P25, 0x1e, 0x03 }, + { V4L2_DV_BT_CEA_1920X1080P30, 0x1e, 0x02 }, + { V4L2_DV_BT_CEA_1920X1080P50, 0x1e, 0x01 }, + { V4L2_DV_BT_CEA_1920X1080P60, 0x1e, 0x00 }, + { }, +}; + +/* sorted by number of lines */ +static const struct adv7604_video_standards adv7604_prim_mode_hdmi_gr[] = { + { V4L2_DV_BT_DMT_640X480P60, 0x08, 0x00 }, + { V4L2_DV_BT_DMT_640X480P72, 0x09, 0x00 }, + { V4L2_DV_BT_DMT_640X480P75, 0x0a, 0x00 }, + { V4L2_DV_BT_DMT_640X480P85, 0x0b, 0x00 }, + { V4L2_DV_BT_DMT_800X600P56, 0x00, 0x00 }, + { V4L2_DV_BT_DMT_800X600P60, 0x01, 0x00 }, + { V4L2_DV_BT_DMT_800X600P72, 0x02, 0x00 }, + { V4L2_DV_BT_DMT_800X600P75, 0x03, 0x00 }, + { V4L2_DV_BT_DMT_800X600P85, 0x04, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P60, 0x0c, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P70, 0x0d, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P75, 0x0e, 0x00 }, + { V4L2_DV_BT_DMT_1024X768P85, 0x0f, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P60, 0x05, 0x00 }, + { V4L2_DV_BT_DMT_1280X1024P75, 0x06, 0x00 }, + { }, +}; + /* ----------------------------------------------------------------------- */ static inline struct adv7604_state *to_state(struct v4l2_subdev *sd) @@ -672,64 +755,144 @@ static int adv7604_s_detect_tx_5v_ctrl(struct v4l2_subdev *sd) ((io_read(sd, 0x6f) & 0x10) >> 4)); } -static void configure_free_run(struct v4l2_subdev *sd, const struct v4l2_bt_timings *timings) +static int find_and_set_predefined_video_timings(struct v4l2_subdev *sd, + u8 prim_mode, + const struct adv7604_video_standards *predef_vid_timings, + const struct v4l2_dv_timings *timings) +{ + struct adv7604_state *state = to_state(sd); + int i; + + for (i = 0; predef_vid_timings[i].timings.bt.width; i++) { + if (!v4l_match_dv_timings(timings, &predef_vid_timings[i].timings, + DIGITAL_INPUT ? 250000 : 1000000)) + continue; + io_write(sd, 0x00, predef_vid_timings[i].vid_std); /* video std */ + io_write(sd, 0x01, (predef_vid_timings[i].v_freq << 4) + + prim_mode); /* v_freq and prim mode */ + return 0; + } + + return -1; +} + +static int configure_predefined_video_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) { + struct adv7604_state *state = to_state(sd); + int err; + + v4l2_dbg(1, debug, sd, "%s", __func__); + + /* reset to default values */ + io_write(sd, 0x16, 0x43); + io_write(sd, 0x17, 0x5a); + /* disable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x00); + cp_write(sd, 0x8f, 0x00); + cp_write(sd, 0x90, 0x00); + cp_write(sd, 0xa2, 0x00); + cp_write(sd, 0xa3, 0x00); + cp_write(sd, 0xa4, 0x00); + cp_write(sd, 0xa5, 0x00); + cp_write(sd, 0xa6, 0x00); + cp_write(sd, 0xa7, 0x00); + cp_write(sd, 0xab, 0x00); + cp_write(sd, 0xac, 0x00); + + switch (state->mode) { + case ADV7604_MODE_COMP: + case ADV7604_MODE_GR: + err = find_and_set_predefined_video_timings(sd, + 0x01, adv7604_prim_mode_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x02, adv7604_prim_mode_gr, timings); + break; + case ADV7604_MODE_HDMI: + err = find_and_set_predefined_video_timings(sd, + 0x05, adv7604_prim_mode_hdmi_comp, timings); + if (err) + err = find_and_set_predefined_video_timings(sd, + 0x06, adv7604_prim_mode_hdmi_gr, timings); + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + err = -1; + break; + } + + + return err; +} + +static void configure_custom_video_timings(struct v4l2_subdev *sd, + const struct v4l2_bt_timings *bt) +{ + struct adv7604_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - u32 width = htotal(timings); - u32 height = vtotal(timings); - u16 ch1_fr_ll = (((u32)timings->pixelclock / 100) > 0) ? - ((width * (ADV7604_fsc / 100)) / ((u32)timings->pixelclock / 100)) : 0; + u32 width = htotal(bt); + u32 height = vtotal(bt); + u16 cp_start_sav = bt->hsync + bt->hbackporch - 4; + u16 cp_start_eav = width - bt->hfrontporch; + u16 cp_start_vbi = height - bt->vfrontporch; + u16 cp_end_vbi = bt->vsync + bt->vbackporch; + u16 ch1_fr_ll = (((u32)bt->pixelclock / 100) > 0) ? + ((width * (ADV7604_fsc / 100)) / ((u32)bt->pixelclock / 100)) : 0; + const u8 pll[2] = { + 0xc0 | ((width >> 8) & 0x1f), + width & 0xff + }; v4l2_dbg(2, debug, sd, "%s\n", __func__); - cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); /* CH1_FR_LL */ - cp_write(sd, 0x90, ch1_fr_ll & 0xff); /* CH1_FR_LL */ - cp_write(sd, 0xab, (height >> 4) & 0xff); /* CP_LCOUNT_MAX */ - cp_write(sd, 0xac, (height & 0x0f) << 4); /* CP_LCOUNT_MAX */ - /* TODO support interlaced */ - cp_write(sd, 0x91, 0x10); /* INTERLACED */ - - /* Should only be set in auto-graphics mode [REF_02 p. 91-92] */ - if ((io_read(sd, 0x00) == 0x07) && (io_read(sd, 0x01) == 0x02)) { - u16 cp_start_sav, cp_start_eav, cp_start_vbi, cp_end_vbi; - const u8 pll[2] = { - (0xc0 | ((width >> 8) & 0x1f)), - (width & 0xff) - }; + switch (state->mode) { + case ADV7604_MODE_COMP: + case ADV7604_MODE_GR: + /* auto graphics */ + io_write(sd, 0x00, 0x07); /* video std */ + io_write(sd, 0x01, 0x02); /* prim mode */ + /* enable embedded syncs for auto graphics mode */ + cp_write_and_or(sd, 0x81, 0xef, 0x10); + /* Should only be set in auto-graphics mode [REF_02, p. 91-92] */ /* setup PLL_DIV_MAN_EN and PLL_DIV_RATIO */ /* IO-map reg. 0x16 and 0x17 should be written in sequence */ if (adv_smbus_write_i2c_block_data(client, 0x16, 2, pll)) { v4l2_err(sd, "writing to reg 0x16 and 0x17 failed\n"); - return; + break; } /* active video - horizontal timing */ - cp_start_sav = timings->hsync + timings->hbackporch - 4; - cp_start_eav = width - timings->hfrontporch; cp_write(sd, 0xa2, (cp_start_sav >> 4) & 0xff); - cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | ((cp_start_eav >> 8) & 0x0f)); + cp_write(sd, 0xa3, ((cp_start_sav & 0x0f) << 4) | + ((cp_start_eav >> 8) & 0x0f)); cp_write(sd, 0xa4, cp_start_eav & 0xff); /* active video - vertical timing */ - cp_start_vbi = height - timings->vfrontporch; - cp_end_vbi = timings->vsync + timings->vbackporch; cp_write(sd, 0xa5, (cp_start_vbi >> 4) & 0xff); - cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | ((cp_end_vbi >> 8) & 0xf)); + cp_write(sd, 0xa6, ((cp_start_vbi & 0xf) << 4) | + ((cp_end_vbi >> 8) & 0xf)); cp_write(sd, 0xa7, cp_end_vbi & 0xff); - } else { - /* reset to default values */ - io_write(sd, 0x16, 0x43); - io_write(sd, 0x17, 0x5a); - cp_write(sd, 0xa2, 0x00); - cp_write(sd, 0xa3, 0x00); - cp_write(sd, 0xa4, 0x00); - cp_write(sd, 0xa5, 0x00); - cp_write(sd, 0xa6, 0x00); - cp_write(sd, 0xa7, 0x00); + break; + case ADV7604_MODE_HDMI: + /* set default prim_mode/vid_std for HDMI + accoring to [REF_03, c. 4.2] */ + io_write(sd, 0x00, 0x02); /* video std */ + io_write(sd, 0x01, 0x06); /* prim mode */ + break; + default: + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); + break; } -} + cp_write(sd, 0x8f, (ch1_fr_ll >> 8) & 0x7); + cp_write(sd, 0x90, ch1_fr_ll & 0xff); + cp_write(sd, 0xab, (height >> 4) & 0xff); + cp_write(sd, 0xac, (height & 0x0f) << 4); +} static void set_rgb_quantization_range(struct v4l2_subdev *sd) { @@ -738,12 +901,7 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: /* automatic */ - if ((hdmi_read(sd, 0x05) & 0x80) || - (state->prim_mode == ADV7604_PRIM_MODE_COMP) || - (state->prim_mode == ADV7604_PRIM_MODE_RGB)) { - /* receiving HDMI or analog signal */ - io_write_and_or(sd, 0x02, 0x0f, 0xf0); - } else { + if (DIGITAL_INPUT && !(hdmi_read(sd, 0x05) & 0x80)) { /* receiving DVI-D signal */ /* ADV7604 selects RGB limited range regardless of @@ -756,6 +914,9 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) /* RGB full range (0-255) */ io_write_and_or(sd, 0x02, 0x0f, 0x10); } + } else { + /* receiving HDMI or analog signal, set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); } break; case V4L2_DV_RGB_RANGE_LIMITED: @@ -967,8 +1128,10 @@ static int stdi2dv_timings(struct v4l2_subdev *sd, state->aspect_ratio, timings)) return 0; - v4l2_dbg(2, debug, sd, "%s: No format candidate found for lcf=%d, bl = %d\n", - __func__, stdi->lcf, stdi->bl); + v4l2_dbg(2, debug, sd, + "%s: No format candidate found for lcvs = %d, lcf=%d, bl = %d, %chsync, %cvsync\n", + __func__, stdi->lcvs, stdi->lcf, stdi->bl, + stdi->hs_pol, stdi->vs_pol); return -1; } @@ -1123,7 +1286,7 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, adv7604_fill_optional_dv_timings_fields(sd, timings); } else { /* find format - * Since LCVS values are inaccurate (REF_03, page 275-276), + * Since LCVS values are inaccurate [REF_03, p. 275-276], * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. */ if (!stdi2dv_timings(sd, &stdi, timings)) @@ -1135,9 +1298,31 @@ static int adv7604_query_dv_timings(struct v4l2_subdev *sd, stdi.lcvs -= 2; v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs); if (stdi2dv_timings(sd, &stdi, timings)) { + /* + * The STDI block may measure wrong values, especially + * for lcvs and lcf. If the driver can not find any + * valid timing, the STDI block is restarted to measure + * the video timings again. The function will return an + * error, but the restart of STDI will generate a new + * STDI interrupt and the format detection process will + * restart. + */ + if (state->restart_stdi_once) { + v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); + /* TODO restart STDI for Sync Channel 2 */ + /* enter one-shot mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x00); + /* trigger STDI restart */ + cp_write_and_or(sd, 0x86, 0xf9, 0x04); + /* reset to continuous mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x02); + state->restart_stdi_once = false; + return -ENOLINK; + } v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); return -ERANGE; } + state->restart_stdi_once = true; } found: @@ -1166,6 +1351,7 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, { struct adv7604_state *state = to_state(sd); struct v4l2_bt_timings *bt; + int err; if (!timings) return -EINVAL; @@ -1178,12 +1364,20 @@ static int adv7604_s_dv_timings(struct v4l2_subdev *sd, __func__, (u32)bt->pixelclock); return -ERANGE; } + adv7604_fill_optional_dv_timings_fields(sd, timings); state->timings = *timings; - /* freerun */ - configure_free_run(sd, bt); + cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + + /* Use prim_mode and vid_std when available */ + err = configure_predefined_video_timings(sd, timings); + if (err) { + /* custom settings when the video format + does not have prim_mode/vid_std */ + configure_custom_video_timings(sd, bt); + } set_rgb_quantization_range(sd); @@ -1203,24 +1397,25 @@ static int adv7604_g_dv_timings(struct v4l2_subdev *sd, return 0; } -static void enable_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mode) +static void enable_input(struct v4l2_subdev *sd) { - switch (prim_mode) { - case ADV7604_PRIM_MODE_COMP: - case ADV7604_PRIM_MODE_RGB: + struct adv7604_state *state = to_state(sd); + + switch (state->mode) { + case ADV7604_MODE_COMP: + case ADV7604_MODE_GR: /* enable */ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ break; - case ADV7604_PRIM_MODE_HDMI_COMP: - case ADV7604_PRIM_MODE_HDMI_GR: + case ADV7604_MODE_HDMI: /* enable */ hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ break; default: - v4l2_err(sd, "%s: reserved primary mode 0x%0x\n", - __func__, prim_mode); + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); break; } } @@ -1233,17 +1428,13 @@ static void disable_input(struct v4l2_subdev *sd) hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ } -static void select_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mode) +static void select_input(struct v4l2_subdev *sd) { - switch (prim_mode) { - case ADV7604_PRIM_MODE_COMP: - case ADV7604_PRIM_MODE_RGB: - /* set mode and select free run resolution */ - io_write(sd, 0x00, 0x07); /* video std */ - io_write(sd, 0x01, 0x02); /* prim mode */ - /* enable embedded syncs for auto graphics mode */ - cp_write_and_or(sd, 0x81, 0xef, 0x10); + struct adv7604_state *state = to_state(sd); + switch (state->mode) { + case ADV7604_MODE_COMP: + case ADV7604_MODE_GR: /* reset ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ hdmi_write(sd, 0x0d, 0x04); /* HDMI filter optimization */ @@ -1271,16 +1462,7 @@ static void select_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mod cp_write(sd, 0x40, 0x5c); /* CP core pre-gain control. Graphics mode */ break; - case ADV7604_PRIM_MODE_HDMI_COMP: - case ADV7604_PRIM_MODE_HDMI_GR: - /* set mode and select free run resolution */ - /* video std */ - io_write(sd, 0x00, - (prim_mode == ADV7604_PRIM_MODE_HDMI_GR) ? 0x02 : 0x1e); - io_write(sd, 0x01, prim_mode); /* prim mode */ - /* disable embedded syncs for auto graphics mode */ - cp_write_and_or(sd, 0x81, 0xef, 0x00); - + case ADV7604_MODE_HDMI: /* set ADI recommended settings for HDMI: */ /* "ADV7604 Register Settings Recommendations (rev. 2.5, June 2010)" p. 4. */ hdmi_write(sd, 0x0d, 0x84); /* HDMI filter optimization */ @@ -1309,7 +1491,8 @@ static void select_input(struct v4l2_subdev *sd, enum adv7604_prim_mode prim_mod break; default: - v4l2_err(sd, "%s: reserved primary mode 0x%0x\n", __func__, prim_mode); + v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", + __func__, state->mode); break; } } @@ -1321,26 +1504,13 @@ static int adv7604_s_routing(struct v4l2_subdev *sd, v4l2_dbg(2, debug, sd, "%s: input %d", __func__, input); - switch (input) { - case 0: - /* TODO select HDMI_COMP or HDMI_GR */ - state->prim_mode = ADV7604_PRIM_MODE_HDMI_COMP; - break; - case 1: - state->prim_mode = ADV7604_PRIM_MODE_RGB; - break; - case 2: - state->prim_mode = ADV7604_PRIM_MODE_COMP; - break; - default: - return -EINVAL; - } + state->mode = input; disable_input(sd); - select_input(sd, state->prim_mode); + select_input(sd); - enable_input(sd, state->prim_mode); + enable_input(sd); return 0; } @@ -1549,8 +1719,9 @@ static int adv7604_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "CP locked: %s\n", no_lock_cp(sd) ? "false" : "true"); v4l2_info(sd, "CP free run: %s\n", (!!(cp_read(sd, 0xff) & 0x10) ? "on" : "off")); - v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x\n", - io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f); + v4l2_info(sd, "Prim-mode = 0x%x, video std = 0x%x, v_freq = 0x%x\n", + io_read(sd, 0x01) & 0x0f, io_read(sd, 0x00) & 0x3f, + (io_read(sd, 0x01) & 0x70) >> 4); v4l2_info(sd, "-----Video Timings-----\n"); if (read_stdi(sd, &stdi)) @@ -1712,9 +1883,9 @@ static int adv7604_core_init(struct v4l2_subdev *sd) cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); /* HDMI free run */ cp_write(sd, 0xf3, 0xdc); /* Low threshold to enter/exit free run mode */ cp_write(sd, 0xf9, 0x23); /* STDI ch. 1 - LCVS change threshold - - ADI recommended setting [REF_01 c. 2.3.3] */ + ADI recommended setting [REF_01, c. 2.3.3] */ cp_write(sd, 0x45, 0x23); /* STDI ch. 2 - LCVS change threshold - - ADI recommended setting [REF_01 c. 2.3.3] */ + ADI recommended setting [REF_01, c. 2.3.3] */ cp_write(sd, 0xc9, 0x2d); /* use prim_mode and vid_std as free run resolution for digital formats */ @@ -1724,11 +1895,6 @@ static int adv7604_core_init(struct v4l2_subdev *sd) afe_write(sd, 0x02, pdata->ain_sel); /* Select analog input muxing mode */ io_write_and_or(sd, 0x30, ~(1 << 4), pdata->output_bus_lsb_to_msb << 4); - state->prim_mode = pdata->prim_mode; - select_input(sd, pdata->prim_mode); - - enable_input(sd, pdata->prim_mode); - /* interrupts */ io_write(sd, 0x40, 0xc2); /* Configure INT1 */ io_write(sd, 0x41, 0xd7); /* STDI irq for any change, disable INT2 */ @@ -1883,6 +2049,7 @@ static int adv7604_probe(struct i2c_client *client, v4l2_err(sd, "failed to create all i2c clients\n"); goto err_i2c; } + state->restart_stdi_once = true; /* work queues */ state->work_queues = create_singlethread_workqueue(client->name); diff --git a/drivers/media/i2c/soc_camera/mt9v022.c b/drivers/media/i2c/soc_camera/mt9v022.c index 13057b966ee..333ef178d6f 100644 --- a/drivers/media/i2c/soc_camera/mt9v022.c +++ b/drivers/media/i2c/soc_camera/mt9v022.c @@ -263,9 +263,14 @@ static int mt9v022_s_crop(struct v4l2_subdev *sd, const struct v4l2_crop *a) if (ret & 1) /* Autoexposure */ ret = reg_write(client, mt9v022->reg->max_total_shutter_width, rect.height + mt9v022->y_skip_top + 43); - else - ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, - rect.height + mt9v022->y_skip_top + 43); + /* + * If autoexposure is off, there is no need to set + * MT9V022_TOTAL_SHUTTER_WIDTH here. Autoexposure can be off + * only if the user has set exposure manually, using the + * V4L2_CID_EXPOSURE_AUTO with the value V4L2_EXPOSURE_MANUAL. + * In this case the register MT9V022_TOTAL_SHUTTER_WIDTH + * already contains the correct value. + */ } /* Setup frame format: defaults apart from width and height */ if (!ret) diff --git a/drivers/media/platform/exynos-gsc/gsc-core.c b/drivers/media/platform/exynos-gsc/gsc-core.c index bfec9e65aef..19cbb12a12a 100644 --- a/drivers/media/platform/exynos-gsc/gsc-core.c +++ b/drivers/media/platform/exynos-gsc/gsc-core.c @@ -965,8 +965,10 @@ static struct platform_device_id gsc_driver_ids[] = { MODULE_DEVICE_TABLE(platform, gsc_driver_ids); static const struct of_device_id exynos_gsc_match[] = { - { .compatible = "samsung,exynos5250-gsc", - .data = &gsc_v_100_drvdata, }, + { + .compatible = "samsung,exynos5-gsc", + .data = &gsc_v_100_drvdata, + }, {}, }; MODULE_DEVICE_TABLE(of, exynos_gsc_match); diff --git a/drivers/media/platform/omap3isp/ispccdc.c b/drivers/media/platform/omap3isp/ispccdc.c index 60181ab9606..aa9df9d71a7 100644 --- a/drivers/media/platform/omap3isp/ispccdc.c +++ b/drivers/media/platform/omap3isp/ispccdc.c @@ -1706,7 +1706,7 @@ static long ccdc_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) } static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) + struct v4l2_event_subscription *sub) { if (sub->type != V4L2_EVENT_FRAME_SYNC) return -EINVAL; @@ -1719,7 +1719,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, } static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) + struct v4l2_event_subscription *sub) { return v4l2_event_unsubscribe(fh, sub); } diff --git a/drivers/media/platform/omap3isp/ispstat.c b/drivers/media/platform/omap3isp/ispstat.c index d7ac76b5c2a..b8640be692f 100644 --- a/drivers/media/platform/omap3isp/ispstat.c +++ b/drivers/media/platform/omap3isp/ispstat.c @@ -1025,7 +1025,7 @@ void omap3isp_stat_dma_isr(struct ispstat *stat) int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) + struct v4l2_event_subscription *sub) { struct ispstat *stat = v4l2_get_subdevdata(subdev); @@ -1037,7 +1037,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub) + struct v4l2_event_subscription *sub) { return v4l2_event_unsubscribe(fh, sub); } diff --git a/drivers/media/platform/omap3isp/ispstat.h b/drivers/media/platform/omap3isp/ispstat.h index a6fe653eb23..9b7c8654dc8 100644 --- a/drivers/media/platform/omap3isp/ispstat.h +++ b/drivers/media/platform/omap3isp/ispstat.h @@ -147,10 +147,10 @@ int omap3isp_stat_init(struct ispstat *stat, const char *name, void omap3isp_stat_cleanup(struct ispstat *stat); int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub); + struct v4l2_event_subscription *sub); int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, struct v4l2_fh *fh, - const struct v4l2_event_subscription *sub); + struct v4l2_event_subscription *sub); int omap3isp_stat_s_stream(struct v4l2_subdev *subdev, int enable); int omap3isp_stat_busy(struct ispstat *stat); diff --git a/drivers/media/platform/omap3isp/ispvideo.c b/drivers/media/platform/omap3isp/ispvideo.c index a0b737fecf1..75cd309035f 100644 --- a/drivers/media/platform/omap3isp/ispvideo.c +++ b/drivers/media/platform/omap3isp/ispvideo.c @@ -792,7 +792,7 @@ isp_video_get_crop(struct file *file, void *fh, struct v4l2_crop *crop) } static int -isp_video_set_crop(struct file *file, void *fh, struct v4l2_crop *crop) +isp_video_set_crop(struct file *file, void *fh, const struct v4l2_crop *crop) { struct isp_video *video = video_drvdata(file); struct v4l2_subdev *subdev; diff --git a/drivers/media/platform/s5p-fimc/Kconfig b/drivers/media/platform/s5p-fimc/Kconfig index 8f090a8f270..c16b20d86ed 100644 --- a/drivers/media/platform/s5p-fimc/Kconfig +++ b/drivers/media/platform/s5p-fimc/Kconfig @@ -24,6 +24,7 @@ config VIDEO_S5P_FIMC config VIDEO_S5P_MIPI_CSIS tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver" depends on REGULATOR + select S5P_SETUP_MIPIPHY help This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2 receiver (MIPI-CSIS) devices. diff --git a/drivers/media/platform/s5p-fimc/fimc-capture.c b/drivers/media/platform/s5p-fimc/fimc-capture.c index 367efd164d0..3d39d97abaa 100644 --- a/drivers/media/platform/s5p-fimc/fimc-capture.c +++ b/drivers/media/platform/s5p-fimc/fimc-capture.c @@ -1736,7 +1736,9 @@ static int fimc_register_capture_device(struct fimc_dev *fimc, q->mem_ops = &vb2_dma_contig_memops; q->buf_struct_size = sizeof(struct fimc_vid_buffer); - vb2_queue_init(q); + ret = vb2_queue_init(q); + if (ret) + goto err_ent; vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); diff --git a/drivers/media/platform/s5p-fimc/fimc-lite.c b/drivers/media/platform/s5p-fimc/fimc-lite.c index 70bcf39de87..9db246bed84 100644 --- a/drivers/media/platform/s5p-fimc/fimc-lite.c +++ b/drivers/media/platform/s5p-fimc/fimc-lite.c @@ -1253,7 +1253,9 @@ static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) q->buf_struct_size = sizeof(struct flite_buffer); q->drv_priv = fimc; - vb2_queue_init(q); + ret = vb2_queue_init(q); + if (ret < 0) + return ret; fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); diff --git a/drivers/media/platform/s5p-fimc/fimc-mdevice.c b/drivers/media/platform/s5p-fimc/fimc-mdevice.c index 80ada5882f6..38ea4d143a4 100644 --- a/drivers/media/platform/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/platform/s5p-fimc/fimc-mdevice.c @@ -343,53 +343,50 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) static int fimc_register_callback(struct device *dev, void *p) { struct fimc_dev *fimc = dev_get_drvdata(dev); - struct v4l2_subdev *sd = &fimc->vid_cap.subdev; + struct v4l2_subdev *sd; struct fimc_md *fmd = p; - int ret = 0; - - if (!fimc || !fimc->pdev) - return 0; + int ret; - if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) + if (fimc == NULL || fimc->id >= FIMC_MAX_DEVS) return 0; - fimc->pipeline_ops = &fimc_pipeline_ops; - fmd->fimc[fimc->pdev->id] = fimc; + sd = &fimc->vid_cap.subdev; sd->grp_id = FIMC_GROUP_ID; ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); if (ret) { v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", fimc->id, ret); + return ret; } - return ret; + fimc->pipeline_ops = &fimc_pipeline_ops; + fmd->fimc[fimc->id] = fimc; + return 0; } static int fimc_lite_register_callback(struct device *dev, void *p) { struct fimc_lite *fimc = dev_get_drvdata(dev); - struct v4l2_subdev *sd = &fimc->subdev; struct fimc_md *fmd = p; int ret; - if (fimc == NULL) + if (fimc == NULL || fimc->index >= FIMC_LITE_MAX_DEVS) return 0; - if (fimc->index >= FIMC_LITE_MAX_DEVS) - return 0; + fimc->subdev.grp_id = FLITE_GROUP_ID; - fimc->pipeline_ops = &fimc_pipeline_ops; - fmd->fimc_lite[fimc->index] = fimc; - sd->grp_id = FLITE_GROUP_ID; - - ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, &fimc->subdev); if (ret) { v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC-LITE.%d (%d)\n", fimc->index, ret); + return ret; } - return ret; + + fimc->pipeline_ops = &fimc_pipeline_ops; + fmd->fimc_lite[fimc->index] = fimc; + return 0; } static int csis_register_callback(struct device *dev, void *p) @@ -407,10 +404,12 @@ static int csis_register_callback(struct device *dev, void *p) v4l2_info(sd, "csis%d sd: %s\n", pdev->id, sd->name); id = pdev->id < 0 ? 0 : pdev->id; - fmd->csis[id].sd = sd; sd->grp_id = CSIS_GROUP_ID; + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); - if (ret) + if (!ret) + fmd->csis[id].sd = sd; + else v4l2_err(&fmd->v4l2_dev, "Failed to register CSIS subdevice: %d\n", ret); return ret; diff --git a/drivers/media/platform/sh_vou.c b/drivers/media/platform/sh_vou.c index 85fd312f0a8..a1c87f0ceaa 100644 --- a/drivers/media/platform/sh_vou.c +++ b/drivers/media/platform/sh_vou.c @@ -935,9 +935,10 @@ static int sh_vou_g_crop(struct file *file, void *fh, struct v4l2_crop *a) /* Assume a dull encoder, do all the work ourselves. */ static int sh_vou_s_crop(struct file *file, void *fh, const struct v4l2_crop *a) { + struct v4l2_crop a_writable = *a; struct video_device *vdev = video_devdata(file); struct sh_vou_device *vou_dev = video_get_drvdata(vdev); - struct v4l2_rect *rect = &a->c; + struct v4l2_rect *rect = &a_writable.c; struct v4l2_crop sd_crop = {.type = V4L2_BUF_TYPE_VIDEO_OUTPUT}; struct v4l2_pix_format *pix = &vou_dev->pix; struct sh_vou_geometry geo; diff --git a/drivers/media/platform/soc_camera/mx1_camera.c b/drivers/media/platform/soc_camera/mx1_camera.c index bbe70991d30..032b8c9097f 100644 --- a/drivers/media/platform/soc_camera/mx1_camera.c +++ b/drivers/media/platform/soc_camera/mx1_camera.c @@ -470,14 +470,6 @@ static void mx1_camera_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; } -static int mx1_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) -{ - struct v4l2_subdev *sd = soc_camera_to_subdev(icd); - - return v4l2_subdev_call(sd, video, s_crop, a); -} - static int mx1_camera_set_bus_param(struct soc_camera_device *icd) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -689,7 +681,6 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = { .add = mx1_camera_add_device, .remove = mx1_camera_remove_device, .set_bus_param = mx1_camera_set_bus_param, - .set_crop = mx1_camera_set_crop, .set_fmt = mx1_camera_set_fmt, .try_fmt = mx1_camera_try_fmt, .init_videobuf = mx1_camera_init_videobuf, diff --git a/drivers/media/platform/soc_camera/mx2_camera.c b/drivers/media/platform/soc_camera/mx2_camera.c index 9fd9d1c5b21..9a55f4c4c7f 100644 --- a/drivers/media/platform/soc_camera/mx2_camera.c +++ b/drivers/media/platform/soc_camera/mx2_camera.c @@ -864,8 +864,10 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) bytesperline = soc_mbus_bytes_per_line(icd->user_width, icd->current_fmt->host_fmt); - if (bytesperline < 0) + if (bytesperline < 0) { + spin_unlock_irqrestore(&pcdev->lock, flags); return bytesperline; + } /* * I didn't manage to properly enable/disable the prp @@ -878,8 +880,10 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) pcdev->discard_buffer = dma_alloc_coherent(ici->v4l2_dev.dev, pcdev->discard_size, &pcdev->discard_buffer_dma, GFP_KERNEL); - if (!pcdev->discard_buffer) + if (!pcdev->discard_buffer) { + spin_unlock_irqrestore(&pcdev->lock, flags); return -ENOMEM; + } pcdev->buf_discard[0].discard = true; list_add_tail(&pcdev->buf_discard[0].queue, @@ -1099,9 +1103,10 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) } static int mx2_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + struct v4l2_crop a_writable = *a; + struct v4l2_rect *rect = &a_writable.c; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_mbus_framefmt mf; int ret; diff --git a/drivers/media/platform/soc_camera/mx3_camera.c b/drivers/media/platform/soc_camera/mx3_camera.c index 3557ac97e43..261f6e9e1b1 100644 --- a/drivers/media/platform/soc_camera/mx3_camera.c +++ b/drivers/media/platform/soc_camera/mx3_camera.c @@ -799,9 +799,10 @@ static inline void stride_align(__u32 *width) * default g_crop and cropcap from soc_camera.c */ static int mx3_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + struct v4l2_crop a_writable = *a; + struct v4l2_rect *rect = &a_writable.c; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); diff --git a/drivers/media/platform/soc_camera/omap1_camera.c b/drivers/media/platform/soc_camera/omap1_camera.c index fa08c7695cc..13636a58510 100644 --- a/drivers/media/platform/soc_camera/omap1_camera.c +++ b/drivers/media/platform/soc_camera/omap1_camera.c @@ -1215,9 +1215,9 @@ static int set_mbus_format(struct omap1_cam_dev *pcdev, struct device *dev, } static int omap1_cam_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *crop) + const struct v4l2_crop *crop) { - struct v4l2_rect *rect = &crop->c; + const struct v4l2_rect *rect = &crop->c; const struct soc_camera_format_xlate *xlate = icd->current_fmt; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct device *dev = icd->parent; diff --git a/drivers/media/platform/soc_camera/pxa_camera.c b/drivers/media/platform/soc_camera/pxa_camera.c index 1e3776d08da..3434ffe79c6 100644 --- a/drivers/media/platform/soc_camera/pxa_camera.c +++ b/drivers/media/platform/soc_camera/pxa_camera.c @@ -1337,9 +1337,9 @@ static int pxa_camera_check_frame(u32 width, u32 height) } static int pxa_camera_set_crop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + const struct v4l2_rect *rect = &a->c; struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct pxa_camera_dev *pcdev = ici->priv; diff --git a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c index 0a24253dcda..2d8861c0e8f 100644 --- a/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c +++ b/drivers/media/platform/soc_camera/sh_mobile_ceu_camera.c @@ -1182,13 +1182,13 @@ static void sh_mobile_ceu_put_formats(struct soc_camera_device *icd) } /* Check if any dimension of r1 is smaller than respective one of r2 */ -static bool is_smaller(struct v4l2_rect *r1, struct v4l2_rect *r2) +static bool is_smaller(const struct v4l2_rect *r1, const struct v4l2_rect *r2) { return r1->width < r2->width || r1->height < r2->height; } /* Check if r1 fails to cover r2 */ -static bool is_inside(struct v4l2_rect *r1, struct v4l2_rect *r2) +static bool is_inside(const struct v4l2_rect *r1, const struct v4l2_rect *r2) { return r1->left > r2->left || r1->top > r2->top || r1->left + r1->width < r2->left + r2->width || @@ -1263,7 +1263,7 @@ static void update_subrect(struct sh_mobile_ceu_cam *cam) * 3. if (2) failed, try to request the maximum image */ static int client_s_crop(struct soc_camera_device *icd, struct v4l2_crop *crop, - const struct v4l2_crop *cam_crop) + struct v4l2_crop *cam_crop) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct v4l2_rect *rect = &crop->c, *cam_rect = &cam_crop->c; @@ -1519,7 +1519,8 @@ static int client_scale(struct soc_camera_device *icd, static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, const struct v4l2_crop *a) { - struct v4l2_rect *rect = &a->c; + struct v4l2_crop a_writable = *a; + const struct v4l2_rect *rect = &a_writable.c; struct device *dev = icd->parent; struct soc_camera_host *ici = to_soc_camera_host(dev); struct sh_mobile_ceu_dev *pcdev = ici->priv; @@ -1545,7 +1546,7 @@ static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, * 1. - 2. Apply iterative camera S_CROP for new input window, read back * actual camera rectangle. */ - ret = client_s_crop(icd, a, &cam_crop); + ret = client_s_crop(icd, &a_writable, &cam_crop); if (ret < 0) return ret; @@ -1946,7 +1947,7 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, } static int sh_mobile_ceu_set_livecrop(struct soc_camera_device *icd, - struct v4l2_crop *a) + const struct v4l2_crop *a) { struct v4l2_subdev *sd = soc_camera_to_subdev(icd); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c index 9859d2a2449..ba51f65204d 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_core.c @@ -283,14 +283,13 @@ static inline int dvb_usb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, /* activate the pid on the device pid filter */ if (adap->props->caps & DVB_USB_ADAP_HAS_PID_FILTER && - adap->pid_filtering && - adap->props->pid_filter) + adap->pid_filtering && adap->props->pid_filter) { ret = adap->props->pid_filter(adap, dvbdmxfeed->index, dvbdmxfeed->pid, (count == 1) ? 1 : 0); - if (ret < 0) - dev_err(&d->udev->dev, "%s: pid_filter() " \ - "failed=%d\n", KBUILD_MODNAME, - ret); + if (ret < 0) + dev_err(&d->udev->dev, "%s: pid_filter() failed=%d\n", + KBUILD_MODNAME, ret); + } /* start feeding if it is first pid */ if (adap->feed_count == 1 && count == 1) { diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c index 0431beed0ef..5716662b483 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -32,9 +32,7 @@ int dvb_usbv2_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, return -EINVAL; } - ret = mutex_lock_interruptible(&d->usb_mutex); - if (ret < 0) - return ret; + mutex_lock(&d->usb_mutex); dev_dbg(&d->udev->dev, "%s: >>> %*ph\n", __func__, wlen, wbuf); diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index adabba8d28b..093f1acce40 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1346,6 +1346,10 @@ static const struct usb_device_id rtl28xxu_id_table[] = { &rtl2832u_props, "DigitalNow Quad DVB-T Receiver", NULL) }, { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d3, &rtl2832u_props, "TerraTec Cinergy T Stick RC (Rev. 3)", NULL) }, + { DVB_USB_DEVICE(USB_VID_DEXATEK, 0x1102, + &rtl2832u_props, "Dexatek DK mini DVB-T Dongle", NULL) }, + { DVB_USB_DEVICE(USB_VID_TERRATEC, 0x00d7, + &rtl2832u_props, "TerraTec Cinergy T Stick+", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1b48f209480..f4f9bf84bc7 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -98,9 +98,9 @@ static irqreturn_t arizona_underclocked(int irq, void *data) if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) dev_err(arizona->dev, "AIF3 underclocked\n"); - if (val & ARIZONA_AIF3_UNDERCLOCKED_STS) - dev_err(arizona->dev, "AIF3 underclocked\n"); if (val & ARIZONA_AIF2_UNDERCLOCKED_STS) + dev_err(arizona->dev, "AIF2 underclocked\n"); + if (val & ARIZONA_AIF1_UNDERCLOCKED_STS) dev_err(arizona->dev, "AIF1 underclocked\n"); if (val & ARIZONA_ISRC2_UNDERCLOCKED_STS) dev_err(arizona->dev, "ISRC2 underclocked\n"); @@ -415,11 +415,19 @@ int __devinit arizona_dev_init(struct arizona *arizona) /* If we have a /RESET GPIO we'll already be reset */ if (!arizona->pdata.reset) { + regcache_mark_dirty(arizona->regmap); + ret = regmap_write(arizona->regmap, ARIZONA_SOFTWARE_RESET, 0); if (ret != 0) { dev_err(dev, "Failed to reset device: %d\n", ret); goto err_reset; } + + ret = regcache_sync(arizona->regmap); + if (ret != 0) { + dev_err(dev, "Failed to sync device: %d\n", ret); + goto err_reset; + } } ret = arizona_wait_for_boot(arizona); @@ -520,7 +528,7 @@ int __devinit arizona_dev_init(struct arizona *arizona) break; case WM5110: ret = mfd_add_devices(arizona->dev, -1, wm5110_devs, - ARRAY_SIZE(wm5102_devs), NULL, 0, NULL); + ARRAY_SIZE(wm5110_devs), NULL, 0, NULL); break; } diff --git a/drivers/mfd/arizona-irq.c b/drivers/mfd/arizona-irq.c index ef0f2d001df..b1b00917740 100644 --- a/drivers/mfd/arizona-irq.c +++ b/drivers/mfd/arizona-irq.c @@ -178,6 +178,7 @@ int arizona_irq_init(struct arizona *arizona) switch (arizona->rev) { case 0: + case 1: ctrlif_error = false; break; default: diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 4ae64232020..a071a8643a4 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -671,7 +671,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base, } if (IS_ENABLED(CONFIG_PWM_TWL6030) && twl_class_is_6030()) { - child = add_child(TWL6030_MODULE_ID1, "twl6030-pwm", NULL, 0, + child = add_child(SUB_CHIP_ID1, "twl6030-pwm", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index ad733d76207..cdd1173ed4e 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -672,7 +672,8 @@ int twl4030_sih_setup(struct device *dev, int module, int irq_base) irq = sih_mod + twl4030_irq_base; irq_set_handler_data(irq, agent); agent->irq_name = kasprintf(GFP_KERNEL, "twl4030_%s", sih->name); - status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0, + status = request_threaded_irq(irq, NULL, handle_twl4030_sih, + IRQF_EARLY_RESUME, agent->irq_name ?: sih->name, NULL); dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name, diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 01b9255ed63..14490cc785d 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -43,6 +43,7 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x479, 0x0A30 }, { 0x47B, 0x0810 }, { 0x47D, 0x0510 }, + { 0x4D1, 0x017F }, { 0x500, 0x000D }, { 0x507, 0x1820 }, { 0x508, 0x1820 }, @@ -52,524 +53,6 @@ static const struct reg_default wm5102_reva_patch[] = { { 0x580, 0x000D }, { 0x587, 0x1820 }, { 0x588, 0x1820 }, - { 0x101, 0x8140 }, - { 0x3000, 0x2225 }, - { 0x3001, 0x3a03 }, - { 0x3002, 0x0225 }, - { 0x3003, 0x0801 }, - { 0x3004, 0x6249 }, - { 0x3005, 0x0c04 }, - { 0x3006, 0x0225 }, - { 0x3007, 0x5901 }, - { 0x3008, 0xe249 }, - { 0x3009, 0x030d }, - { 0x300a, 0x0249 }, - { 0x300b, 0x2c01 }, - { 0x300c, 0xe249 }, - { 0x300d, 0x4342 }, - { 0x300e, 0xe249 }, - { 0x300f, 0x73c0 }, - { 0x3010, 0x4249 }, - { 0x3011, 0x0c00 }, - { 0x3012, 0x0225 }, - { 0x3013, 0x1f01 }, - { 0x3014, 0x0225 }, - { 0x3015, 0x1e01 }, - { 0x3016, 0x0225 }, - { 0x3017, 0xfa00 }, - { 0x3018, 0x0000 }, - { 0x3019, 0xf000 }, - { 0x301a, 0x0000 }, - { 0x301b, 0xf000 }, - { 0x301c, 0x0000 }, - { 0x301d, 0xf000 }, - { 0x301e, 0x0000 }, - { 0x301f, 0xf000 }, - { 0x3020, 0x0000 }, - { 0x3021, 0xf000 }, - { 0x3022, 0x0000 }, - { 0x3023, 0xf000 }, - { 0x3024, 0x0000 }, - { 0x3025, 0xf000 }, - { 0x3026, 0x0000 }, - { 0x3027, 0xf000 }, - { 0x3028, 0x0000 }, - { 0x3029, 0xf000 }, - { 0x302a, 0x0000 }, - { 0x302b, 0xf000 }, - { 0x302c, 0x0000 }, - { 0x302d, 0xf000 }, - { 0x302e, 0x0000 }, - { 0x302f, 0xf000 }, - { 0x3030, 0x0225 }, - { 0x3031, 0x1a01 }, - { 0x3032, 0x0225 }, - { 0x3033, 0x1e00 }, - { 0x3034, 0x0225 }, - { 0x3035, 0x1f00 }, - { 0x3036, 0x6225 }, - { 0x3037, 0xf800 }, - { 0x3038, 0x0000 }, - { 0x3039, 0xf000 }, - { 0x303a, 0x0000 }, - { 0x303b, 0xf000 }, - { 0x303c, 0x0000 }, - { 0x303d, 0xf000 }, - { 0x303e, 0x0000 }, - { 0x303f, 0xf000 }, - { 0x3040, 0x2226 }, - { 0x3041, 0x3a03 }, - { 0x3042, 0x0226 }, - { 0x3043, 0x0801 }, - { 0x3044, 0x6249 }, - { 0x3045, 0x0c06 }, - { 0x3046, 0x0226 }, - { 0x3047, 0x5901 }, - { 0x3048, 0xe249 }, - { 0x3049, 0x030d }, - { 0x304a, 0x0249 }, - { 0x304b, 0x2c01 }, - { 0x304c, 0xe249 }, - { 0x304d, 0x4342 }, - { 0x304e, 0xe249 }, - { 0x304f, 0x73c0 }, - { 0x3050, 0x4249 }, - { 0x3051, 0x0c00 }, - { 0x3052, 0x0226 }, - { 0x3053, 0x1f01 }, - { 0x3054, 0x0226 }, - { 0x3055, 0x1e01 }, - { 0x3056, 0x0226 }, - { 0x3057, 0xfa00 }, - { 0x3058, 0x0000 }, - { 0x3059, 0xf000 }, - { 0x305a, 0x0000 }, - { 0x305b, 0xf000 }, - { 0x305c, 0x0000 }, - { 0x305d, 0xf000 }, - { 0x305e, 0x0000 }, - { 0x305f, 0xf000 }, - { 0x3060, 0x0000 }, - { 0x3061, 0xf000 }, - { 0x3062, 0x0000 }, - { 0x3063, 0xf000 }, - { 0x3064, 0x0000 }, - { 0x3065, 0xf000 }, - { 0x3066, 0x0000 }, - { 0x3067, 0xf000 }, - { 0x3068, 0x0000 }, - { 0x3069, 0xf000 }, - { 0x306a, 0x0000 }, - { 0x306b, 0xf000 }, - { 0x306c, 0x0000 }, - { 0x306d, 0xf000 }, - { 0x306e, 0x0000 }, - { 0x306f, 0xf000 }, - { 0x3070, 0x0226 }, - { 0x3071, 0x1a01 }, - { 0x3072, 0x0226 }, - { 0x3073, 0x1e00 }, - { 0x3074, 0x0226 }, - { 0x3075, 0x1f00 }, - { 0x3076, 0x6226 }, - { 0x3077, 0xf800 }, - { 0x3078, 0x0000 }, - { 0x3079, 0xf000 }, - { 0x307a, 0x0000 }, - { 0x307b, 0xf000 }, - { 0x307c, 0x0000 }, - { 0x307d, 0xf000 }, - { 0x307e, 0x0000 }, - { 0x307f, 0xf000 }, - { 0x3080, 0x2227 }, - { 0x3081, 0x3a03 }, - { 0x3082, 0x0227 }, - { 0x3083, 0x0801 }, - { 0x3084, 0x6255 }, - { 0x3085, 0x0c04 }, - { 0x3086, 0x0227 }, - { 0x3087, 0x5901 }, - { 0x3088, 0xe255 }, - { 0x3089, 0x030d }, - { 0x308a, 0x0255 }, - { 0x308b, 0x2c01 }, - { 0x308c, 0xe255 }, - { 0x308d, 0x4342 }, - { 0x308e, 0xe255 }, - { 0x308f, 0x73c0 }, - { 0x3090, 0x4255 }, - { 0x3091, 0x0c00 }, - { 0x3092, 0x0227 }, - { 0x3093, 0x1f01 }, - { 0x3094, 0x0227 }, - { 0x3095, 0x1e01 }, - { 0x3096, 0x0227 }, - { 0x3097, 0xfa00 }, - { 0x3098, 0x0000 }, - { 0x3099, 0xf000 }, - { 0x309a, 0x0000 }, - { 0x309b, 0xf000 }, - { 0x309c, 0x0000 }, - { 0x309d, 0xf000 }, - { 0x309e, 0x0000 }, - { 0x309f, 0xf000 }, - { 0x30a0, 0x0000 }, - { 0x30a1, 0xf000 }, - { 0x30a2, 0x0000 }, - { 0x30a3, 0xf000 }, - { 0x30a4, 0x0000 }, - { 0x30a5, 0xf000 }, - { 0x30a6, 0x0000 }, - { 0x30a7, 0xf000 }, - { 0x30a8, 0x0000 }, - { 0x30a9, 0xf000 }, - { 0x30aa, 0x0000 }, - { 0x30ab, 0xf000 }, - { 0x30ac, 0x0000 }, - { 0x30ad, 0xf000 }, - { 0x30ae, 0x0000 }, - { 0x30af, 0xf000 }, - { 0x30b0, 0x0227 }, - { 0x30b1, 0x1a01 }, - { 0x30b2, 0x0227 }, - { 0x30b3, 0x1e00 }, - { 0x30b4, 0x0227 }, - { 0x30b5, 0x1f00 }, - { 0x30b6, 0x6227 }, - { 0x30b7, 0xf800 }, - { 0x30b8, 0x0000 }, - { 0x30b9, 0xf000 }, - { 0x30ba, 0x0000 }, - { 0x30bb, 0xf000 }, - { 0x30bc, 0x0000 }, - { 0x30bd, 0xf000 }, - { 0x30be, 0x0000 }, - { 0x30bf, 0xf000 }, - { 0x30c0, 0x2228 }, - { 0x30c1, 0x3a03 }, - { 0x30c2, 0x0228 }, - { 0x30c3, 0x0801 }, - { 0x30c4, 0x6255 }, - { 0x30c5, 0x0c06 }, - { 0x30c6, 0x0228 }, - { 0x30c7, 0x5901 }, - { 0x30c8, 0xe255 }, - { 0x30c9, 0x030d }, - { 0x30ca, 0x0255 }, - { 0x30cb, 0x2c01 }, - { 0x30cc, 0xe255 }, - { 0x30cd, 0x4342 }, - { 0x30ce, 0xe255 }, - { 0x30cf, 0x73c0 }, - { 0x30d0, 0x4255 }, - { 0x30d1, 0x0c00 }, - { 0x30d2, 0x0228 }, - { 0x30d3, 0x1f01 }, - { 0x30d4, 0x0228 }, - { 0x30d5, 0x1e01 }, - { 0x30d6, 0x0228 }, - { 0x30d7, 0xfa00 }, - { 0x30d8, 0x0000 }, - { 0x30d9, 0xf000 }, - { 0x30da, 0x0000 }, - { 0x30db, 0xf000 }, - { 0x30dc, 0x0000 }, - { 0x30dd, 0xf000 }, - { 0x30de, 0x0000 }, - { 0x30df, 0xf000 }, - { 0x30e0, 0x0000 }, - { 0x30e1, 0xf000 }, - { 0x30e2, 0x0000 }, - { 0x30e3, 0xf000 }, - { 0x30e4, 0x0000 }, - { 0x30e5, 0xf000 }, - { 0x30e6, 0x0000 }, - { 0x30e7, 0xf000 }, - { 0x30e8, 0x0000 }, - { 0x30e9, 0xf000 }, - { 0x30ea, 0x0000 }, - { 0x30eb, 0xf000 }, - { 0x30ec, 0x0000 }, - { 0x30ed, 0xf000 }, - { 0x30ee, 0x0000 }, - { 0x30ef, 0xf000 }, - { 0x30f0, 0x0228 }, - { 0x30f1, 0x1a01 }, - { 0x30f2, 0x0228 }, - { 0x30f3, 0x1e00 }, - { 0x30f4, 0x0228 }, - { 0x30f5, 0x1f00 }, - { 0x30f6, 0x6228 }, - { 0x30f7, 0xf800 }, - { 0x30f8, 0x0000 }, - { 0x30f9, 0xf000 }, - { 0x30fa, 0x0000 }, - { 0x30fb, 0xf000 }, - { 0x30fc, 0x0000 }, - { 0x30fd, 0xf000 }, - { 0x30fe, 0x0000 }, - { 0x30ff, 0xf000 }, - { 0x3100, 0x222b }, - { 0x3101, 0x3a03 }, - { 0x3102, 0x222b }, - { 0x3103, 0x5803 }, - { 0x3104, 0xe26f }, - { 0x3105, 0x030d }, - { 0x3106, 0x626f }, - { 0x3107, 0x2c01 }, - { 0x3108, 0xe26f }, - { 0x3109, 0x4342 }, - { 0x310a, 0xe26f }, - { 0x310b, 0x73c0 }, - { 0x310c, 0x026f }, - { 0x310d, 0x0c00 }, - { 0x310e, 0x022b }, - { 0x310f, 0x1f01 }, - { 0x3110, 0x022b }, - { 0x3111, 0x1e01 }, - { 0x3112, 0x022b }, - { 0x3113, 0xfa00 }, - { 0x3114, 0x0000 }, - { 0x3115, 0xf000 }, - { 0x3116, 0x0000 }, - { 0x3117, 0xf000 }, - { 0x3118, 0x0000 }, - { 0x3119, 0xf000 }, - { 0x311a, 0x0000 }, - { 0x311b, 0xf000 }, - { 0x311c, 0x0000 }, - { 0x311d, 0xf000 }, - { 0x311e, 0x0000 }, - { 0x311f, 0xf000 }, - { 0x3120, 0x022b }, - { 0x3121, 0x0a01 }, - { 0x3122, 0x022b }, - { 0x3123, 0x1e00 }, - { 0x3124, 0x022b }, - { 0x3125, 0x1f00 }, - { 0x3126, 0x622b }, - { 0x3127, 0xf800 }, - { 0x3128, 0x0000 }, - { 0x3129, 0xf000 }, - { 0x312a, 0x0000 }, - { 0x312b, 0xf000 }, - { 0x312c, 0x0000 }, - { 0x312d, 0xf000 }, - { 0x312e, 0x0000 }, - { 0x312f, 0xf000 }, - { 0x3130, 0x0000 }, - { 0x3131, 0xf000 }, - { 0x3132, 0x0000 }, - { 0x3133, 0xf000 }, - { 0x3134, 0x0000 }, - { 0x3135, 0xf000 }, - { 0x3136, 0x0000 }, - { 0x3137, 0xf000 }, - { 0x3138, 0x0000 }, - { 0x3139, 0xf000 }, - { 0x313a, 0x0000 }, - { 0x313b, 0xf000 }, - { 0x313c, 0x0000 }, - { 0x313d, 0xf000 }, - { 0x313e, 0x0000 }, - { 0x313f, 0xf000 }, - { 0x3140, 0x0000 }, - { 0x3141, 0xf000 }, - { 0x3142, 0x0000 }, - { 0x3143, 0xf000 }, - { 0x3144, 0x0000 }, - { 0x3145, 0xf000 }, - { 0x3146, 0x0000 }, - { 0x3147, 0xf000 }, - { 0x3148, 0x0000 }, - { 0x3149, 0xf000 }, - { 0x314a, 0x0000 }, - { 0x314b, 0xf000 }, - { 0x314c, 0x0000 }, - { 0x314d, 0xf000 }, - { 0x314e, 0x0000 }, - { 0x314f, 0xf000 }, - { 0x3150, 0x0000 }, - { 0x3151, 0xf000 }, - { 0x3152, 0x0000 }, - { 0x3153, 0xf000 }, - { 0x3154, 0x0000 }, - { 0x3155, 0xf000 }, - { 0x3156, 0x0000 }, - { 0x3157, 0xf000 }, - { 0x3158, 0x0000 }, - { 0x3159, 0xf000 }, - { 0x315a, 0x0000 }, - { 0x315b, 0xf000 }, - { 0x315c, 0x0000 }, - { 0x315d, 0xf000 }, - { 0x315e, 0x0000 }, - { 0x315f, 0xf000 }, - { 0x3160, 0x0000 }, - { 0x3161, 0xf000 }, - { 0x3162, 0x0000 }, - { 0x3163, 0xf000 }, - { 0x3164, 0x0000 }, - { 0x3165, 0xf000 }, - { 0x3166, 0x0000 }, - { 0x3167, 0xf000 }, - { 0x3168, 0x0000 }, - { 0x3169, 0xf000 }, - { 0x316a, 0x0000 }, - { 0x316b, 0xf000 }, - { 0x316c, 0x0000 }, - { 0x316d, 0xf000 }, - { 0x316e, 0x0000 }, - { 0x316f, 0xf000 }, - { 0x3170, 0x0000 }, - { 0x3171, 0xf000 }, - { 0x3172, 0x0000 }, - { 0x3173, 0xf000 }, - { 0x3174, 0x0000 }, - { 0x3175, 0xf000 }, - { 0x3176, 0x0000 }, - { 0x3177, 0xf000 }, - { 0x3178, 0x0000 }, - { 0x3179, 0xf000 }, - { 0x317a, 0x0000 }, - { 0x317b, 0xf000 }, - { 0x317c, 0x0000 }, - { 0x317d, 0xf000 }, - { 0x317e, 0x0000 }, - { 0x317f, 0xf000 }, - { 0x3180, 0x2001 }, - { 0x3181, 0xf101 }, - { 0x3182, 0x0000 }, - { 0x3183, 0xf000 }, - { 0x3184, 0x0000 }, - { 0x3185, 0xf000 }, - { 0x3186, 0x0000 }, - { 0x3187, 0xf000 }, - { 0x3188, 0x0000 }, - { 0x3189, 0xf000 }, - { 0x318a, 0x0000 }, - { 0x318b, 0xf000 }, - { 0x318c, 0x0000 }, - { 0x318d, 0xf000 }, - { 0x318e, 0x0000 }, - { 0x318f, 0xf000 }, - { 0x3190, 0x0000 }, - { 0x3191, 0xf000 }, - { 0x3192, 0x0000 }, - { 0x3193, 0xf000 }, - { 0x3194, 0x0000 }, - { 0x3195, 0xf000 }, - { 0x3196, 0x0000 }, - { 0x3197, 0xf000 }, - { 0x3198, 0x0000 }, - { 0x3199, 0xf000 }, - { 0x319a, 0x0000 }, - { 0x319b, 0xf000 }, - { 0x319c, 0x0000 }, - { 0x319d, 0xf000 }, - { 0x319e, 0x0000 }, - { 0x319f, 0xf000 }, - { 0x31a0, 0x0000 }, - { 0x31a1, 0xf000 }, - { 0x31a2, 0x0000 }, - { 0x31a3, 0xf000 }, - { 0x31a4, 0x0000 }, - { 0x31a5, 0xf000 }, - { 0x31a6, 0x0000 }, - { 0x31a7, 0xf000 }, - { 0x31a8, 0x0000 }, - { 0x31a9, 0xf000 }, - { 0x31aa, 0x0000 }, - { 0x31ab, 0xf000 }, - { 0x31ac, 0x0000 }, - { 0x31ad, 0xf000 }, - { 0x31ae, 0x0000 }, - { 0x31af, 0xf000 }, - { 0x31b0, 0x0000 }, - { 0x31b1, 0xf000 }, - { 0x31b2, 0x0000 }, - { 0x31b3, 0xf000 }, - { 0x31b4, 0x0000 }, - { 0x31b5, 0xf000 }, - { 0x31b6, 0x0000 }, - { 0x31b7, 0xf000 }, - { 0x31b8, 0x0000 }, - { 0x31b9, 0xf000 }, - { 0x31ba, 0x0000 }, - { 0x31bb, 0xf000 }, - { 0x31bc, 0x0000 }, - { 0x31bd, 0xf000 }, - { 0x31be, 0x0000 }, - { 0x31bf, 0xf000 }, - { 0x31c0, 0x0000 }, - { 0x31c1, 0xf000 }, - { 0x31c2, 0x0000 }, - { 0x31c3, 0xf000 }, - { 0x31c4, 0x0000 }, - { 0x31c5, 0xf000 }, - { 0x31c6, 0x0000 }, - { 0x31c7, 0xf000 }, - { 0x31c8, 0x0000 }, - { 0x31c9, 0xf000 }, - { 0x31ca, 0x0000 }, - { 0x31cb, 0xf000 }, - { 0x31cc, 0x0000 }, - { 0x31cd, 0xf000 }, - { 0x31ce, 0x0000 }, - { 0x31cf, 0xf000 }, - { 0x31d0, 0x0000 }, - { 0x31d1, 0xf000 }, - { 0x31d2, 0x0000 }, - { 0x31d3, 0xf000 }, - { 0x31d4, 0x0000 }, - { 0x31d5, 0xf000 }, - { 0x31d6, 0x0000 }, - { 0x31d7, 0xf000 }, - { 0x31d8, 0x0000 }, - { 0x31d9, 0xf000 }, - { 0x31da, 0x0000 }, - { 0x31db, 0xf000 }, - { 0x31dc, 0x0000 }, - { 0x31dd, 0xf000 }, - { 0x31de, 0x0000 }, - { 0x31df, 0xf000 }, - { 0x31e0, 0x0000 }, - { 0x31e1, 0xf000 }, - { 0x31e2, 0x0000 }, - { 0x31e3, 0xf000 }, - { 0x31e4, 0x0000 }, - { 0x31e5, 0xf000 }, - { 0x31e6, 0x0000 }, - { 0x31e7, 0xf000 }, - { 0x31e8, 0x0000 }, - { 0x31e9, 0xf000 }, - { 0x31ea, 0x0000 }, - { 0x31eb, 0xf000 }, - { 0x31ec, 0x0000 }, - { 0x31ed, 0xf000 }, - { 0x31ee, 0x0000 }, - { 0x31ef, 0xf000 }, - { 0x31f0, 0x0000 }, - { 0x31f1, 0xf000 }, - { 0x31f2, 0x0000 }, - { 0x31f3, 0xf000 }, - { 0x31f4, 0x0000 }, - { 0x31f5, 0xf000 }, - { 0x31f6, 0x0000 }, - { 0x31f7, 0xf000 }, - { 0x31f8, 0x0000 }, - { 0x31f9, 0xf000 }, - { 0x31fa, 0x0000 }, - { 0x31fb, 0xf000 }, - { 0x31fc, 0x0000 }, - { 0x31fd, 0xf000 }, - { 0x31fe, 0x0000 }, - { 0x31ff, 0xf000 }, - { 0x024d, 0xff50 }, - { 0x0252, 0xff50 }, - { 0x0259, 0x0112 }, - { 0x025e, 0x0112 }, - { 0x101, 0x0304 }, { 0x80, 0x0000 }, }; diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 374c46dff7d..ec794a72975 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1077,7 +1077,8 @@ EXPORT_SYMBOL_GPL(mtd_writev); * until the request succeeds or until the allocation size falls below * the system page size. This attempts to make sure it does not adversely * impact system performance, so when allocating more than one page, we - * ask the memory allocator to avoid re-trying. + * ask the memory allocator to avoid re-trying, swapping, writing back + * or performing I/O. * * Note, this function also makes sure that the allocated buffer is aligned to * the MTD device's min. I/O unit, i.e. the "mtd->writesize" value. @@ -1091,7 +1092,8 @@ EXPORT_SYMBOL_GPL(mtd_writev); */ void *mtd_kmalloc_up_to(const struct mtd_info *mtd, size_t *size) { - gfp_t flags = __GFP_NOWARN | __GFP_WAIT | __GFP_NORETRY; + gfp_t flags = __GFP_NOWARN | __GFP_WAIT | + __GFP_NORETRY | __GFP_NO_KSWAPD; size_t min_alloc = max_t(size_t, mtd->writesize, PAGE_SIZE); void *kbuf; diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index e15cc11edbb..7c9d136e74b 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -84,6 +84,10 @@ static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb) /* Forward declaration */ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); +static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp); +static void rlb_src_unlink(struct bonding *bond, u32 index); +static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, + u32 ip_dst_hash); static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { @@ -354,6 +358,18 @@ static int rlb_arp_recv(const struct sk_buff *skb, struct bonding *bond, if (!arp) goto out; + /* We received an ARP from arp->ip_src. + * We might have used this IP address previously (on the bonding host + * itself or on a system that is bridged together with the bond). + * However, if arp->mac_src is different than what is stored in + * rx_hashtbl, some other host is now using the IP and we must prevent + * sending out client updates with this IP address and the old MAC + * address. + * Clean up all hash table entries that have this address as ip_src but + * have a different mac_src. + */ + rlb_purge_src_ip(bond, arp); + if (arp->op_code == htons(ARPOP_REPLY)) { /* update rx hash table for this ARP */ rlb_update_entry_from_arp(bond, arp); @@ -432,9 +448,9 @@ static void rlb_clear_slave(struct bonding *bond, struct slave *slave) _lock_rx_hashtbl_bh(bond); rx_hash_table = bond_info->rx_hashtbl; - index = bond_info->rx_hashtbl_head; + index = bond_info->rx_hashtbl_used_head; for (; index != RLB_NULL_INDEX; index = next_index) { - next_index = rx_hash_table[index].next; + next_index = rx_hash_table[index].used_next; if (rx_hash_table[index].slave == slave) { struct slave *assigned_slave = rlb_next_rx_slave(bond); @@ -519,8 +535,9 @@ static void rlb_update_rx_clients(struct bonding *bond) _lock_rx_hashtbl_bh(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if (client_info->ntt) { rlb_update_client(client_info); @@ -548,8 +565,9 @@ static void rlb_req_update_slave_clients(struct bonding *bond, struct slave *sla _lock_rx_hashtbl_bh(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if ((client_info->slave == slave) && @@ -578,8 +596,9 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, __be32 src_ip) _lock_rx_hashtbl(bond); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); if (!client_info->slave) { @@ -625,6 +644,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon /* update mac address from arp */ memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN); } + memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN); assigned_slave = client_info->slave; if (assigned_slave) { @@ -647,6 +667,17 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon assigned_slave = rlb_next_rx_slave(bond); if (assigned_slave) { + if (!(client_info->assigned && + client_info->ip_src == arp->ip_src)) { + /* ip_src is going to be updated, + * fix the src hash list + */ + u32 hash_src = _simple_hash((u8 *)&arp->ip_src, + sizeof(arp->ip_src)); + rlb_src_unlink(bond, hash_index); + rlb_src_link(bond, hash_src, hash_index); + } + client_info->ip_src = arp->ip_src; client_info->ip_dst = arp->ip_dst; /* arp->mac_dst is broadcast for arp reqeusts. @@ -654,6 +685,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon * upon receiving an arp reply. */ memcpy(client_info->mac_dst, arp->mac_dst, ETH_ALEN); + memcpy(client_info->mac_src, arp->mac_src, ETH_ALEN); client_info->slave = assigned_slave; if (!ether_addr_equal_64bits(client_info->mac_dst, mac_bcast)) { @@ -669,11 +701,11 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon } if (!client_info->assigned) { - u32 prev_tbl_head = bond_info->rx_hashtbl_head; - bond_info->rx_hashtbl_head = hash_index; - client_info->next = prev_tbl_head; + u32 prev_tbl_head = bond_info->rx_hashtbl_used_head; + bond_info->rx_hashtbl_used_head = hash_index; + client_info->used_next = prev_tbl_head; if (prev_tbl_head != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[prev_tbl_head].prev = + bond_info->rx_hashtbl[prev_tbl_head].used_prev = hash_index; } client_info->assigned = 1; @@ -694,6 +726,12 @@ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) struct arp_pkt *arp = arp_pkt(skb); struct slave *tx_slave = NULL; + /* Don't modify or load balance ARPs that do not originate locally + * (e.g.,arrive via a bridge). + */ + if (!bond_slave_has_mac(bond, arp->mac_src)) + return NULL; + if (arp->op_code == htons(ARPOP_REPLY)) { /* the arp must be sent on the selected * rx channel @@ -740,8 +778,9 @@ static void rlb_rebalance(struct bonding *bond) _lock_rx_hashtbl_bh(bond); ntt = 0; - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); assigned_slave = rlb_next_rx_slave(bond); if (assigned_slave && (client_info->slave != assigned_slave)) { @@ -759,11 +798,113 @@ static void rlb_rebalance(struct bonding *bond) } /* Caller must hold rx_hashtbl lock */ +static void rlb_init_table_entry_dst(struct rlb_client_info *entry) +{ + entry->used_next = RLB_NULL_INDEX; + entry->used_prev = RLB_NULL_INDEX; + entry->assigned = 0; + entry->slave = NULL; + entry->tag = 0; +} +static void rlb_init_table_entry_src(struct rlb_client_info *entry) +{ + entry->src_first = RLB_NULL_INDEX; + entry->src_prev = RLB_NULL_INDEX; + entry->src_next = RLB_NULL_INDEX; +} + static void rlb_init_table_entry(struct rlb_client_info *entry) { memset(entry, 0, sizeof(struct rlb_client_info)); - entry->next = RLB_NULL_INDEX; - entry->prev = RLB_NULL_INDEX; + rlb_init_table_entry_dst(entry); + rlb_init_table_entry_src(entry); +} + +static void rlb_delete_table_entry_dst(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx_hashtbl[index].used_next; + u32 prev_index = bond_info->rx_hashtbl[index].used_prev; + + if (index == bond_info->rx_hashtbl_used_head) + bond_info->rx_hashtbl_used_head = next_index; + if (prev_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[prev_index].used_next = next_index; + if (next_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next_index].used_prev = prev_index; +} + +/* unlink a rlb hash table entry from the src list */ +static void rlb_src_unlink(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next_index = bond_info->rx_hashtbl[index].src_next; + u32 prev_index = bond_info->rx_hashtbl[index].src_prev; + + bond_info->rx_hashtbl[index].src_next = RLB_NULL_INDEX; + bond_info->rx_hashtbl[index].src_prev = RLB_NULL_INDEX; + + if (next_index != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next_index].src_prev = prev_index; + + if (prev_index == RLB_NULL_INDEX) + return; + + /* is prev_index pointing to the head of this list? */ + if (bond_info->rx_hashtbl[prev_index].src_first == index) + bond_info->rx_hashtbl[prev_index].src_first = next_index; + else + bond_info->rx_hashtbl[prev_index].src_next = next_index; + +} + +static void rlb_delete_table_entry(struct bonding *bond, u32 index) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]); + + rlb_delete_table_entry_dst(bond, index); + rlb_init_table_entry_dst(entry); + + rlb_src_unlink(bond, index); +} + +/* add the rx_hashtbl[ip_dst_hash] entry to the list + * of entries with identical ip_src_hash + */ +static void rlb_src_link(struct bonding *bond, u32 ip_src_hash, u32 ip_dst_hash) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 next; + + bond_info->rx_hashtbl[ip_dst_hash].src_prev = ip_src_hash; + next = bond_info->rx_hashtbl[ip_src_hash].src_first; + bond_info->rx_hashtbl[ip_dst_hash].src_next = next; + if (next != RLB_NULL_INDEX) + bond_info->rx_hashtbl[next].src_prev = ip_dst_hash; + bond_info->rx_hashtbl[ip_src_hash].src_first = ip_dst_hash; +} + +/* deletes all rx_hashtbl entries with arp->ip_src if their mac_src does + * not match arp->mac_src */ +static void rlb_purge_src_ip(struct bonding *bond, struct arp_pkt *arp) +{ + struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); + u32 ip_src_hash = _simple_hash((u8*)&(arp->ip_src), sizeof(arp->ip_src)); + u32 index; + + _lock_rx_hashtbl_bh(bond); + + index = bond_info->rx_hashtbl[ip_src_hash].src_first; + while (index != RLB_NULL_INDEX) { + struct rlb_client_info *entry = &(bond_info->rx_hashtbl[index]); + u32 next_index = entry->src_next; + if (entry->ip_src == arp->ip_src && + !ether_addr_equal_64bits(arp->mac_src, entry->mac_src)) + rlb_delete_table_entry(bond, index); + index = next_index; + } + _unlock_rx_hashtbl_bh(bond); } static int rlb_initialize(struct bonding *bond) @@ -781,7 +922,7 @@ static int rlb_initialize(struct bonding *bond) bond_info->rx_hashtbl = new_hashtbl; - bond_info->rx_hashtbl_head = RLB_NULL_INDEX; + bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; for (i = 0; i < RLB_HASH_TABLE_SIZE; i++) { rlb_init_table_entry(bond_info->rx_hashtbl + i); @@ -803,7 +944,7 @@ static void rlb_deinitialize(struct bonding *bond) kfree(bond_info->rx_hashtbl); bond_info->rx_hashtbl = NULL; - bond_info->rx_hashtbl_head = RLB_NULL_INDEX; + bond_info->rx_hashtbl_used_head = RLB_NULL_INDEX; _unlock_rx_hashtbl_bh(bond); } @@ -815,25 +956,13 @@ static void rlb_clear_vlan(struct bonding *bond, unsigned short vlan_id) _lock_rx_hashtbl_bh(bond); - curr_index = bond_info->rx_hashtbl_head; + curr_index = bond_info->rx_hashtbl_used_head; while (curr_index != RLB_NULL_INDEX) { struct rlb_client_info *curr = &(bond_info->rx_hashtbl[curr_index]); - u32 next_index = bond_info->rx_hashtbl[curr_index].next; - u32 prev_index = bond_info->rx_hashtbl[curr_index].prev; - - if (curr->tag && (curr->vlan_id == vlan_id)) { - if (curr_index == bond_info->rx_hashtbl_head) { - bond_info->rx_hashtbl_head = next_index; - } - if (prev_index != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[prev_index].next = next_index; - } - if (next_index != RLB_NULL_INDEX) { - bond_info->rx_hashtbl[next_index].prev = prev_index; - } + u32 next_index = bond_info->rx_hashtbl[curr_index].used_next; - rlb_init_table_entry(curr); - } + if (curr->tag && (curr->vlan_id == vlan_id)) + rlb_delete_table_entry(bond, curr_index); curr_index = next_index; } diff --git a/drivers/net/bonding/bond_alb.h b/drivers/net/bonding/bond_alb.h index 90f140a2d19..e7a5b8b37ea 100644 --- a/drivers/net/bonding/bond_alb.h +++ b/drivers/net/bonding/bond_alb.h @@ -94,15 +94,35 @@ struct tlb_client_info { /* ------------------------------------------------------------------------- * struct rlb_client_info contains all info related to a specific rx client - * connection. This is the Clients Hash Table entry struct + * connection. This is the Clients Hash Table entry struct. + * Note that this is not a proper hash table; if a new client's IP address + * hash collides with an existing client entry, the old entry is replaced. + * + * There is a linked list (linked by the used_next and used_prev members) + * linking all the used entries of the hash table. This allows updating + * all the clients without walking over all the unused elements of the table. + * + * There are also linked lists of entries with identical hash(ip_src). These + * allow cleaning up the table from ip_src<->mac_src associations that have + * become outdated and would cause sending out invalid ARP updates to the + * network. These are linked by the (src_next and src_prev members). * ------------------------------------------------------------------------- */ struct rlb_client_info { __be32 ip_src; /* the server IP address */ __be32 ip_dst; /* the client IP address */ + u8 mac_src[ETH_ALEN]; /* the server MAC address */ u8 mac_dst[ETH_ALEN]; /* the client MAC address */ - u32 next; /* The next Hash table entry index */ - u32 prev; /* The previous Hash table entry index */ + + /* list of used hash table entries, starting at rx_hashtbl_used_head */ + u32 used_next; + u32 used_prev; + + /* ip_src based hashing */ + u32 src_next; /* next entry with same hash(ip_src) */ + u32 src_prev; /* prev entry with same hash(ip_src) */ + u32 src_first; /* first entry with hash(ip_src) == this entry's index */ + u8 assigned; /* checking whether this entry is assigned */ u8 ntt; /* flag - need to transmit client info */ struct slave *slave; /* the slave assigned to this client */ @@ -131,7 +151,7 @@ struct alb_bond_info { int rlb_enabled; struct rlb_client_info *rx_hashtbl; /* Receive hash table */ spinlock_t rx_hashtbl_lock; - u32 rx_hashtbl_head; + u32 rx_hashtbl_used_head; u8 rx_ntt; /* flag - need to transmit * to all rx clients */ diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c index 2cf084eb9d5..5fc4c235147 100644 --- a/drivers/net/bonding/bond_debugfs.c +++ b/drivers/net/bonding/bond_debugfs.c @@ -31,8 +31,9 @@ static int bond_debug_rlb_hash_show(struct seq_file *m, void *v) spin_lock_bh(&(BOND_ALB_INFO(bond).rx_hashtbl_lock)); - hash_index = bond_info->rx_hashtbl_head; - for (; hash_index != RLB_NULL_INDEX; hash_index = client_info->next) { + hash_index = bond_info->rx_hashtbl_used_head; + for (; hash_index != RLB_NULL_INDEX; + hash_index = client_info->used_next) { client_info = &(bond_info->rx_hashtbl[hash_index]); seq_printf(m, "%-15pI4 %-15pI4 %-17pM %s\n", &client_info->ip_src, diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index 5f5b69f37d2..c8bff3e83a5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -1838,7 +1838,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) * anyway (it holds no special properties of the bond device), * so we can change it without calling change_active_interface() */ - if (!bond->curr_active_slave) + if (!bond->curr_active_slave && new_slave->link == BOND_LINK_UP) bond->curr_active_slave = new_slave; break; diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index f8af2fcd3d1..6dded569b11 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -22,6 +22,7 @@ #include <linux/in6.h> #include <linux/netpoll.h> #include <linux/inetdevice.h> +#include <linux/etherdevice.h> #include "bond_3ad.h" #include "bond_alb.h" @@ -450,6 +451,18 @@ static inline void bond_destroy_proc_dir(struct bond_net *bn) } #endif +static inline struct slave *bond_slave_has_mac(struct bonding *bond, + const u8 *mac) +{ + int i = 0; + struct slave *tmp; + + bond_for_each_slave(bond, tmp, i) + if (ether_addr_equal_64bits(mac, tmp->dev->dev_addr)) + return tmp; + + return NULL; +} /* exported from bond_main.c */ extern int bond_net_id; diff --git a/drivers/net/can/at91_can.c b/drivers/net/can/at91_can.c index fcff73a73b1..14b166bdbea 100644 --- a/drivers/net/can/at91_can.c +++ b/drivers/net/can/at91_can.c @@ -1372,6 +1372,7 @@ static const struct platform_device_id at91_can_id_table[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(platform, at91_can_id_table); static struct platform_driver at91_can_driver = { .probe = at91_can_probe, diff --git a/drivers/net/can/bfin_can.c b/drivers/net/can/bfin_can.c index f2d6d258a28..a3f8de96225 100644 --- a/drivers/net/can/bfin_can.c +++ b/drivers/net/can/bfin_can.c @@ -691,3 +691,4 @@ module_platform_driver(bfin_can_driver); MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Blackfin on-chip CAN netdevice driver"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/net/can/c_can/c_can.c b/drivers/net/can/c_can/c_can.c index e5180dfddba..5233b8f58d7 100644 --- a/drivers/net/can/c_can/c_can.c +++ b/drivers/net/can/c_can/c_can.c @@ -233,6 +233,12 @@ static inline void c_can_pm_runtime_put_sync(const struct c_can_priv *priv) pm_runtime_put_sync(priv->device); } +static inline void c_can_reset_ram(const struct c_can_priv *priv, bool enable) +{ + if (priv->raminit) + priv->raminit(priv, enable); +} + static inline int get_tx_next_msg_obj(const struct c_can_priv *priv) { return (priv->tx_next & C_CAN_NEXT_MSG_OBJ_MASK) + @@ -1090,6 +1096,7 @@ static int c_can_open(struct net_device *dev) struct c_can_priv *priv = netdev_priv(dev); c_can_pm_runtime_get_sync(priv); + c_can_reset_ram(priv, true); /* open the can device */ err = open_candev(dev); @@ -1118,6 +1125,7 @@ static int c_can_open(struct net_device *dev) exit_irq_fail: close_candev(dev); exit_open_fail: + c_can_reset_ram(priv, false); c_can_pm_runtime_put_sync(priv); return err; } @@ -1131,6 +1139,8 @@ static int c_can_close(struct net_device *dev) c_can_stop(dev); free_irq(dev->irq, dev); close_candev(dev); + + c_can_reset_ram(priv, false); c_can_pm_runtime_put_sync(priv); return 0; @@ -1188,6 +1198,7 @@ int c_can_power_down(struct net_device *dev) c_can_stop(dev); + c_can_reset_ram(priv, false); c_can_pm_runtime_put_sync(priv); return 0; @@ -1206,6 +1217,7 @@ int c_can_power_up(struct net_device *dev) WARN_ON(priv->type != BOSCH_D_CAN); c_can_pm_runtime_get_sync(priv); + c_can_reset_ram(priv, true); /* Clear PDR and INIT bits */ val = priv->read_reg(priv, C_CAN_CTRL_EX_REG); diff --git a/drivers/net/can/c_can/c_can.h b/drivers/net/can/c_can/c_can.h index e5ed41dafa1..d2e1c21b143 100644 --- a/drivers/net/can/c_can/c_can.h +++ b/drivers/net/can/c_can/c_can.h @@ -169,6 +169,9 @@ struct c_can_priv { void *priv; /* for board-specific data */ u16 irqstatus; enum c_can_dev_id type; + u32 __iomem *raminit_ctrlreg; + unsigned int instance; + void (*raminit) (const struct c_can_priv *priv, bool enable); }; struct net_device *alloc_c_can_dev(void); diff --git a/drivers/net/can/c_can/c_can_platform.c b/drivers/net/can/c_can/c_can_platform.c index ee1416132ab..0044fd859db 100644 --- a/drivers/net/can/c_can/c_can_platform.c +++ b/drivers/net/can/c_can/c_can_platform.c @@ -38,6 +38,8 @@ #include "c_can.h" +#define CAN_RAMINIT_START_MASK(i) (1 << (i)) + /* * 16-bit c_can registers can be arranged differently in the memory * architecture of different implementations. For example: 16-bit @@ -68,6 +70,18 @@ static void c_can_plat_write_reg_aligned_to_32bit(struct c_can_priv *priv, writew(val, priv->base + 2 * priv->regs[index]); } +static void c_can_hw_raminit(const struct c_can_priv *priv, bool enable) +{ + u32 val; + + val = readl(priv->raminit_ctrlreg); + if (enable) + val |= CAN_RAMINIT_START_MASK(priv->instance); + else + val &= ~CAN_RAMINIT_START_MASK(priv->instance); + writel(val, priv->raminit_ctrlreg); +} + static struct platform_device_id c_can_id_table[] = { [BOSCH_C_CAN_PLATFORM] = { .name = KBUILD_MODNAME, @@ -83,12 +97,14 @@ static struct platform_device_id c_can_id_table[] = { }, { } }; +MODULE_DEVICE_TABLE(platform, c_can_id_table); static const struct of_device_id c_can_of_table[] = { { .compatible = "bosch,c_can", .data = &c_can_id_table[BOSCH_C_CAN] }, { .compatible = "bosch,d_can", .data = &c_can_id_table[BOSCH_D_CAN] }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, c_can_of_table); static int __devinit c_can_plat_probe(struct platform_device *pdev) { @@ -99,7 +115,7 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) const struct of_device_id *match; const struct platform_device_id *id; struct pinctrl *pinctrl; - struct resource *mem; + struct resource *mem, *res; int irq; struct clk *clk; @@ -178,6 +194,18 @@ static int __devinit c_can_plat_probe(struct platform_device *pdev) priv->can.ctrlmode_supported |= CAN_CTRLMODE_3_SAMPLES; priv->read_reg = c_can_plat_read_reg_aligned_to_16bit; priv->write_reg = c_can_plat_write_reg_aligned_to_16bit; + + if (pdev->dev.of_node) + priv->instance = of_alias_get_id(pdev->dev.of_node, "d_can"); + else + priv->instance = pdev->id; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->raminit_ctrlreg = devm_request_and_ioremap(&pdev->dev, res); + if (!priv->raminit_ctrlreg || priv->instance < 0) + dev_info(&pdev->dev, "control memory is not used for raminit\n"); + else + priv->raminit = c_can_hw_raminit; break; default: ret = -EINVAL; diff --git a/drivers/net/can/cc770/cc770_platform.c b/drivers/net/can/cc770/cc770_platform.c index 688371cda37..3da6cbb542a 100644 --- a/drivers/net/can/cc770/cc770_platform.c +++ b/drivers/net/can/cc770/cc770_platform.c @@ -60,6 +60,7 @@ MODULE_AUTHOR("Wolfgang Grandegger <wg@grandegger.com>"); MODULE_DESCRIPTION("Socket-CAN driver for CC770 on the platform bus"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); #define CC770_PLATFORM_CAN_CLOCK 16000000 @@ -258,6 +259,7 @@ static struct of_device_id __devinitdata cc770_platform_table[] = { {.compatible = "intc,82527"}, /* AN82527 from Intel CP */ {}, }; +MODULE_DEVICE_TABLE(of, cc770_platform_table); static struct platform_driver cc770_platform_driver = { .driver = { diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 963e2ccd10d..8233e5ed293 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -609,8 +609,7 @@ void close_candev(struct net_device *dev) { struct can_priv *priv = netdev_priv(dev); - if (del_timer_sync(&priv->restart_timer)) - dev_put(dev); + del_timer_sync(&priv->restart_timer); can_flush_echo_skb(dev); } EXPORT_SYMBOL_GPL(close_candev); diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index a412bf6d73e..9a17965e00f 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -979,11 +979,13 @@ static const struct of_device_id flexcan_of_match[] = { { .compatible = "fsl,imx6q-flexcan", .data = &fsl_imx6q_devtype_data, }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(of, flexcan_of_match); static const struct platform_device_id flexcan_id_table[] = { { .name = "flexcan", .driver_data = (kernel_ulong_t)&fsl_p1010_devtype_data, }, { /* sentinel */ }, }; +MODULE_DEVICE_TABLE(platform, flexcan_id_table); static int __devinit flexcan_probe(struct platform_device *pdev) { diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c index 799c354083c..514d0206425 100644 --- a/drivers/net/can/mscan/mpc5xxx_can.c +++ b/drivers/net/can/mscan/mpc5xxx_can.c @@ -396,6 +396,7 @@ static const struct of_device_id __devinitconst mpc5xxx_can_table[] = { { .compatible = "fsl,mpc5121-mscan", .data = &mpc5121_can_data, }, {}, }; +MODULE_DEVICE_TABLE(of, mpc5xxx_can_table); static struct platform_driver mpc5xxx_can_driver = { .driver = { diff --git a/drivers/net/can/mscan/mscan.c b/drivers/net/can/mscan/mscan.c index 2b104d5f422..e6b40954e20 100644 --- a/drivers/net/can/mscan/mscan.c +++ b/drivers/net/can/mscan/mscan.c @@ -517,12 +517,8 @@ static irqreturn_t mscan_isr(int irq, void *dev_id) static int mscan_do_set_mode(struct net_device *dev, enum can_mode mode) { - struct mscan_priv *priv = netdev_priv(dev); int ret = 0; - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: ret = mscan_restart(dev); @@ -590,8 +586,6 @@ static int mscan_open(struct net_device *dev) goto exit_napi_disable; } - priv->open_time = jiffies; - if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) setbits8(®s->canctl1, MSCAN_LISTEN); else @@ -606,7 +600,6 @@ static int mscan_open(struct net_device *dev) return 0; exit_free_irq: - priv->open_time = 0; free_irq(dev->irq, dev); exit_napi_disable: napi_disable(&priv->napi); @@ -627,7 +620,6 @@ static int mscan_close(struct net_device *dev) mscan_set_mode(dev, MSCAN_INIT_MODE); close_candev(dev); free_irq(dev->irq, dev); - priv->open_time = 0; return 0; } diff --git a/drivers/net/can/mscan/mscan.h b/drivers/net/can/mscan/mscan.h index b43e9f5d326..af2ed8baf0a 100644 --- a/drivers/net/can/mscan/mscan.h +++ b/drivers/net/can/mscan/mscan.h @@ -281,7 +281,6 @@ struct tx_queue_entry { struct mscan_priv { struct can_priv can; /* must be the first member */ unsigned int type; /* MSCAN type variants */ - long open_time; unsigned long flags; void __iomem *reg_base; /* ioremap'ed address to registers */ u8 shadow_statflg; diff --git a/drivers/net/can/sja1000/Kconfig b/drivers/net/can/sja1000/Kconfig index 559be87a090..92f73c708a3 100644 --- a/drivers/net/can/sja1000/Kconfig +++ b/drivers/net/can/sja1000/Kconfig @@ -21,7 +21,7 @@ config CAN_SJA1000_PLATFORM config CAN_SJA1000_OF_PLATFORM tristate "Generic OF Platform Bus based SJA1000 driver" - depends on PPC_OF + depends on OF ---help--- This driver adds support for the SJA1000 chips connected to the OpenFirmware "platform bus" found on embedded systems with diff --git a/drivers/net/can/sja1000/sja1000.c b/drivers/net/can/sja1000/sja1000.c index 25011dbe1b9..83ee11eca0e 100644 --- a/drivers/net/can/sja1000/sja1000.c +++ b/drivers/net/can/sja1000/sja1000.c @@ -188,11 +188,6 @@ static void sja1000_start(struct net_device *dev) static int sja1000_set_mode(struct net_device *dev, enum can_mode mode) { - struct sja1000_priv *priv = netdev_priv(dev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: sja1000_start(dev); @@ -579,7 +574,6 @@ static int sja1000_open(struct net_device *dev) /* init and start chi */ sja1000_start(dev); - priv->open_time = jiffies; netif_start_queue(dev); @@ -598,8 +592,6 @@ static int sja1000_close(struct net_device *dev) close_candev(dev); - priv->open_time = 0; - return 0; } diff --git a/drivers/net/can/sja1000/sja1000.h b/drivers/net/can/sja1000/sja1000.h index 23fff06875f..afa99847a51 100644 --- a/drivers/net/can/sja1000/sja1000.h +++ b/drivers/net/can/sja1000/sja1000.h @@ -152,7 +152,6 @@ */ struct sja1000_priv { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb; /* the lower-layer is responsible for appropriate locking */ diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index f2683eb6a3d..e45258d4369 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -42,6 +42,8 @@ #include <linux/can/dev.h> #include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> #include <asm/prom.h> #include "sja1000.h" @@ -59,13 +61,13 @@ MODULE_LICENSE("GPL v2"); static u8 sja1000_ofp_read_reg(const struct sja1000_priv *priv, int reg) { - return in_8(priv->reg_base + reg); + return ioread8(priv->reg_base + reg); } static void sja1000_ofp_write_reg(const struct sja1000_priv *priv, int reg, u8 val) { - out_8(priv->reg_base + reg, val); + iowrite8(val, priv->reg_base + reg); } static int __devexit sja1000_ofp_remove(struct platform_device *ofdev) diff --git a/drivers/net/can/sja1000/sja1000_platform.c b/drivers/net/can/sja1000/sja1000_platform.c index 662c5f7eb0c..21619bb5b86 100644 --- a/drivers/net/can/sja1000/sja1000_platform.c +++ b/drivers/net/can/sja1000/sja1000_platform.c @@ -34,6 +34,7 @@ MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>"); MODULE_DESCRIPTION("Socket-CAN driver for SJA1000 on the platform bus"); +MODULE_ALIAS("platform:" DRV_NAME); MODULE_LICENSE("GPL v2"); static u8 sp_read_reg8(const struct sja1000_priv *priv, int reg) diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index 9ded21e79db..1267b366dcf 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -1055,3 +1055,4 @@ module_platform_driver(ti_hecc_driver); MODULE_AUTHOR("Anant Gole <anantgole@ti.com>"); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION(DRV_DESC); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/net/can/usb/Kconfig b/drivers/net/can/usb/Kconfig index 0a6876841c2..a4e4bee3571 100644 --- a/drivers/net/can/usb/Kconfig +++ b/drivers/net/can/usb/Kconfig @@ -13,6 +13,35 @@ config CAN_ESD_USB2 This driver supports the CAN-USB/2 interface from esd electronic system design gmbh (http://www.esd.eu). +config CAN_KVASER_USB + tristate "Kvaser CAN/USB interface" + ---help--- + This driver adds support for Kvaser CAN/USB devices like Kvaser + Leaf Light. + + The driver gives support for the following devices: + - Kvaser Leaf Light + - Kvaser Leaf Professional HS + - Kvaser Leaf SemiPro HS + - Kvaser Leaf Professional LS + - Kvaser Leaf Professional SWC + - Kvaser Leaf Professional LIN + - Kvaser Leaf SemiPro LS + - Kvaser Leaf SemiPro SWC + - Kvaser Memorator II HS/HS + - Kvaser USBcan Professional HS/HS + - Kvaser Leaf Light GI + - Kvaser Leaf Professional HS (OBD-II connector) + - Kvaser Memorator Professional HS/LS + - Kvaser Leaf Light "China" + - Kvaser BlackBird SemiPro + - Kvaser USBcan R + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called kvaser_usb. + config CAN_PEAK_USB tristate "PEAK PCAN-USB/USB Pro interfaces" ---help--- diff --git a/drivers/net/can/usb/Makefile b/drivers/net/can/usb/Makefile index da6d1d3b296..80a2ee41fd6 100644 --- a/drivers/net/can/usb/Makefile +++ b/drivers/net/can/usb/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_CAN_EMS_USB) += ems_usb.o obj-$(CONFIG_CAN_ESD_USB2) += esd_usb2.o +obj-$(CONFIG_CAN_KVASER_USB) += kvaser_usb.o obj-$(CONFIG_CAN_PEAK_USB) += peak_usb/ ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG diff --git a/drivers/net/can/usb/ems_usb.c b/drivers/net/can/usb/ems_usb.c index 086fa321677..c69f0b72b35 100644 --- a/drivers/net/can/usb/ems_usb.c +++ b/drivers/net/can/usb/ems_usb.c @@ -245,7 +245,6 @@ struct ems_tx_urb_context { struct ems_usb { struct can_priv can; /* must be the first member */ - int open_time; struct sk_buff *echo_skb[MAX_TX_URBS]; @@ -728,7 +727,6 @@ static int ems_usb_open(struct net_device *netdev) return err; } - dev->open_time = jiffies; netif_start_queue(netdev); @@ -878,8 +876,6 @@ static int ems_usb_close(struct net_device *netdev) close_candev(netdev); - dev->open_time = 0; - return 0; } @@ -905,9 +901,6 @@ static int ems_usb_set_mode(struct net_device *netdev, enum can_mode mode) { struct ems_usb *dev = netdev_priv(netdev); - if (!dev->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: if (ems_usb_write_mode(dev, SJA1000_MOD_NORMAL)) diff --git a/drivers/net/can/usb/esd_usb2.c b/drivers/net/can/usb/esd_usb2.c index 124e0dd3490..9b74d1e3ad4 100644 --- a/drivers/net/can/usb/esd_usb2.c +++ b/drivers/net/can/usb/esd_usb2.c @@ -217,7 +217,6 @@ struct esd_usb2_net_priv { struct usb_anchor tx_submitted; struct esd_tx_urb_context tx_contexts[MAX_TX_URBS]; - int open_time; struct esd_usb2 *usb2; struct net_device *netdev; int index; @@ -695,8 +694,6 @@ static int esd_usb2_open(struct net_device *netdev) return err; } - priv->open_time = jiffies; - netif_start_queue(netdev); return 0; @@ -864,8 +861,6 @@ static int esd_usb2_close(struct net_device *netdev) close_candev(netdev); - priv->open_time = 0; - return 0; } @@ -941,11 +936,6 @@ static int esd_usb2_get_berr_counter(const struct net_device *netdev, static int esd_usb2_set_mode(struct net_device *netdev, enum can_mode mode) { - struct esd_usb2_net_priv *priv = netdev_priv(netdev); - - if (!priv->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: netif_wake_queue(netdev); diff --git a/drivers/net/can/usb/kvaser_usb.c b/drivers/net/can/usb/kvaser_usb.c new file mode 100644 index 00000000000..5b58a4d8739 --- /dev/null +++ b/drivers/net/can/usb/kvaser_usb.c @@ -0,0 +1,1627 @@ +/* + * 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 version 2. + * + * Parts of this driver are based on the following: + * - Kvaser linux leaf driver (version 4.78) + * - CAN driver for esd CAN-USB/2 + * + * Copyright (C) 2002-2006 KVASER AB, Sweden. All rights reserved. + * Copyright (C) 2010 Matthias Fuchs <matthias.fuchs@esd.eu>, esd gmbh + * Copyright (C) 2012 Olivier Sobrie <olivier@sobrie.be> + */ + +#include <linux/init.h> +#include <linux/completion.h> +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include <linux/can.h> +#include <linux/can/dev.h> +#include <linux/can/error.h> + +#define MAX_TX_URBS 16 +#define MAX_RX_URBS 4 +#define START_TIMEOUT 1000 /* msecs */ +#define STOP_TIMEOUT 1000 /* msecs */ +#define USB_SEND_TIMEOUT 1000 /* msecs */ +#define USB_RECV_TIMEOUT 1000 /* msecs */ +#define RX_BUFFER_SIZE 3072 +#define CAN_USB_CLOCK 8000000 +#define MAX_NET_DEVICES 3 + +/* Kvaser USB devices */ +#define KVASER_VENDOR_ID 0x0bfd +#define USB_LEAF_DEVEL_PRODUCT_ID 10 +#define USB_LEAF_LITE_PRODUCT_ID 11 +#define USB_LEAF_PRO_PRODUCT_ID 12 +#define USB_LEAF_SPRO_PRODUCT_ID 14 +#define USB_LEAF_PRO_LS_PRODUCT_ID 15 +#define USB_LEAF_PRO_SWC_PRODUCT_ID 16 +#define USB_LEAF_PRO_LIN_PRODUCT_ID 17 +#define USB_LEAF_SPRO_LS_PRODUCT_ID 18 +#define USB_LEAF_SPRO_SWC_PRODUCT_ID 19 +#define USB_MEMO2_DEVEL_PRODUCT_ID 22 +#define USB_MEMO2_HSHS_PRODUCT_ID 23 +#define USB_UPRO_HSHS_PRODUCT_ID 24 +#define USB_LEAF_LITE_GI_PRODUCT_ID 25 +#define USB_LEAF_PRO_OBDII_PRODUCT_ID 26 +#define USB_MEMO2_HSLS_PRODUCT_ID 27 +#define USB_LEAF_LITE_CH_PRODUCT_ID 28 +#define USB_BLACKBIRD_SPRO_PRODUCT_ID 29 +#define USB_OEM_MERCURY_PRODUCT_ID 34 +#define USB_OEM_LEAF_PRODUCT_ID 35 +#define USB_CAN_R_PRODUCT_ID 39 + +/* USB devices features */ +#define KVASER_HAS_SILENT_MODE BIT(0) +#define KVASER_HAS_TXRX_ERRORS BIT(1) + +/* Message header size */ +#define MSG_HEADER_LEN 2 + +/* Can message flags */ +#define MSG_FLAG_ERROR_FRAME BIT(0) +#define MSG_FLAG_OVERRUN BIT(1) +#define MSG_FLAG_NERR BIT(2) +#define MSG_FLAG_WAKEUP BIT(3) +#define MSG_FLAG_REMOTE_FRAME BIT(4) +#define MSG_FLAG_RESERVED BIT(5) +#define MSG_FLAG_TX_ACK BIT(6) +#define MSG_FLAG_TX_REQUEST BIT(7) + +/* Can states */ +#define M16C_STATE_BUS_RESET BIT(0) +#define M16C_STATE_BUS_ERROR BIT(4) +#define M16C_STATE_BUS_PASSIVE BIT(5) +#define M16C_STATE_BUS_OFF BIT(6) + +/* Can msg ids */ +#define CMD_RX_STD_MESSAGE 12 +#define CMD_TX_STD_MESSAGE 13 +#define CMD_RX_EXT_MESSAGE 14 +#define CMD_TX_EXT_MESSAGE 15 +#define CMD_SET_BUS_PARAMS 16 +#define CMD_GET_BUS_PARAMS 17 +#define CMD_GET_BUS_PARAMS_REPLY 18 +#define CMD_GET_CHIP_STATE 19 +#define CMD_CHIP_STATE_EVENT 20 +#define CMD_SET_CTRL_MODE 21 +#define CMD_GET_CTRL_MODE 22 +#define CMD_GET_CTRL_MODE_REPLY 23 +#define CMD_RESET_CHIP 24 +#define CMD_RESET_CARD 25 +#define CMD_START_CHIP 26 +#define CMD_START_CHIP_REPLY 27 +#define CMD_STOP_CHIP 28 +#define CMD_STOP_CHIP_REPLY 29 +#define CMD_GET_CARD_INFO2 32 +#define CMD_GET_CARD_INFO 34 +#define CMD_GET_CARD_INFO_REPLY 35 +#define CMD_GET_SOFTWARE_INFO 38 +#define CMD_GET_SOFTWARE_INFO_REPLY 39 +#define CMD_ERROR_EVENT 45 +#define CMD_FLUSH_QUEUE 48 +#define CMD_RESET_ERROR_COUNTER 49 +#define CMD_TX_ACKNOWLEDGE 50 +#define CMD_CAN_ERROR_EVENT 51 +#define CMD_USB_THROTTLE 77 +#define CMD_LOG_MESSAGE 106 + +/* error factors */ +#define M16C_EF_ACKE BIT(0) +#define M16C_EF_CRCE BIT(1) +#define M16C_EF_FORME BIT(2) +#define M16C_EF_STFE BIT(3) +#define M16C_EF_BITE0 BIT(4) +#define M16C_EF_BITE1 BIT(5) +#define M16C_EF_RCVE BIT(6) +#define M16C_EF_TRE BIT(7) + +/* bittiming parameters */ +#define KVASER_USB_TSEG1_MIN 1 +#define KVASER_USB_TSEG1_MAX 16 +#define KVASER_USB_TSEG2_MIN 1 +#define KVASER_USB_TSEG2_MAX 8 +#define KVASER_USB_SJW_MAX 4 +#define KVASER_USB_BRP_MIN 1 +#define KVASER_USB_BRP_MAX 64 +#define KVASER_USB_BRP_INC 1 + +/* ctrl modes */ +#define KVASER_CTRL_MODE_NORMAL 1 +#define KVASER_CTRL_MODE_SILENT 2 +#define KVASER_CTRL_MODE_SELFRECEPTION 3 +#define KVASER_CTRL_MODE_OFF 4 + +struct kvaser_msg_simple { + u8 tid; + u8 channel; +} __packed; + +struct kvaser_msg_cardinfo { + u8 tid; + u8 nchannels; + __le32 serial_number; + __le32 padding; + __le32 clock_resolution; + __le32 mfgdate; + u8 ean[8]; + u8 hw_revision; + u8 usb_hs_mode; + __le16 padding2; +} __packed; + +struct kvaser_msg_cardinfo2 { + u8 tid; + u8 channel; + u8 pcb_id[24]; + __le32 oem_unlock_code; +} __packed; + +struct kvaser_msg_softinfo { + u8 tid; + u8 channel; + __le32 sw_options; + __le32 fw_version; + __le16 max_outstanding_tx; + __le16 padding[9]; +} __packed; + +struct kvaser_msg_busparams { + u8 tid; + u8 channel; + __le32 bitrate; + u8 tseg1; + u8 tseg2; + u8 sjw; + u8 no_samp; +} __packed; + +struct kvaser_msg_tx_can { + u8 channel; + u8 tid; + u8 msg[14]; + u8 padding; + u8 flags; +} __packed; + +struct kvaser_msg_rx_can { + u8 channel; + u8 flag; + __le16 time[3]; + u8 msg[14]; +} __packed; + +struct kvaser_msg_chip_state_event { + u8 tid; + u8 channel; + __le16 time[3]; + u8 tx_errors_count; + u8 rx_errors_count; + u8 status; + u8 padding[3]; +} __packed; + +struct kvaser_msg_tx_acknowledge { + u8 channel; + u8 tid; + __le16 time[3]; + u8 flags; + u8 time_offset; +} __packed; + +struct kvaser_msg_error_event { + u8 tid; + u8 flags; + __le16 time[3]; + u8 channel; + u8 padding; + u8 tx_errors_count; + u8 rx_errors_count; + u8 status; + u8 error_factor; +} __packed; + +struct kvaser_msg_ctrl_mode { + u8 tid; + u8 channel; + u8 ctrl_mode; + u8 padding[3]; +} __packed; + +struct kvaser_msg_flush_queue { + u8 tid; + u8 channel; + u8 flags; + u8 padding[3]; +} __packed; + +struct kvaser_msg_log_message { + u8 channel; + u8 flags; + __le16 time[3]; + u8 dlc; + u8 time_offset; + __le32 id; + u8 data[8]; +} __packed; + +struct kvaser_msg { + u8 len; + u8 id; + union { + struct kvaser_msg_simple simple; + struct kvaser_msg_cardinfo cardinfo; + struct kvaser_msg_cardinfo2 cardinfo2; + struct kvaser_msg_softinfo softinfo; + struct kvaser_msg_busparams busparams; + struct kvaser_msg_tx_can tx_can; + struct kvaser_msg_rx_can rx_can; + struct kvaser_msg_chip_state_event chip_state_event; + struct kvaser_msg_tx_acknowledge tx_acknowledge; + struct kvaser_msg_error_event error_event; + struct kvaser_msg_ctrl_mode ctrl_mode; + struct kvaser_msg_flush_queue flush_queue; + struct kvaser_msg_log_message log_message; + } u; +} __packed; + +struct kvaser_usb_tx_urb_context { + struct kvaser_usb_net_priv *priv; + u32 echo_index; + int dlc; +}; + +struct kvaser_usb { + struct usb_device *udev; + struct kvaser_usb_net_priv *nets[MAX_NET_DEVICES]; + + struct usb_endpoint_descriptor *bulk_in, *bulk_out; + struct usb_anchor rx_submitted; + + u32 fw_version; + unsigned int nchannels; + + bool rxinitdone; + void *rxbuf[MAX_RX_URBS]; + dma_addr_t rxbuf_dma[MAX_RX_URBS]; +}; + +struct kvaser_usb_net_priv { + struct can_priv can; + + atomic_t active_tx_urbs; + struct usb_anchor tx_submitted; + struct kvaser_usb_tx_urb_context tx_contexts[MAX_TX_URBS]; + + struct completion start_comp, stop_comp; + + struct kvaser_usb *dev; + struct net_device *netdev; + int channel; + + struct can_berr_counter bec; +}; + +static const struct usb_device_id kvaser_usb_table[] = { + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_DEVEL_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_SWC_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_LIN_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_LS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_SPRO_SWC_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_DEVEL_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSHS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_UPRO_HSHS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_GI_PRODUCT_ID) }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_PRO_OBDII_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS | + KVASER_HAS_SILENT_MODE }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_MEMO2_HSLS_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_LEAF_LITE_CH_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_BLACKBIRD_SPRO_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_MERCURY_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_OEM_LEAF_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { USB_DEVICE(KVASER_VENDOR_ID, USB_CAN_R_PRODUCT_ID), + .driver_info = KVASER_HAS_TXRX_ERRORS }, + { } +}; +MODULE_DEVICE_TABLE(usb, kvaser_usb_table); + +static inline int kvaser_usb_send_msg(const struct kvaser_usb *dev, + struct kvaser_msg *msg) +{ + int actual_len; + + return usb_bulk_msg(dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + msg, msg->len, &actual_len, + USB_SEND_TIMEOUT); +} + +static int kvaser_usb_wait_msg(const struct kvaser_usb *dev, u8 id, + struct kvaser_msg *msg) +{ + struct kvaser_msg *tmp; + void *buf; + int actual_len; + int err; + int pos = 0; + + buf = kzalloc(RX_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + buf, RX_BUFFER_SIZE, &actual_len, + USB_RECV_TIMEOUT); + if (err < 0) + goto end; + + while (pos <= actual_len - MSG_HEADER_LEN) { + tmp = buf + pos; + + if (!tmp->len) + break; + + if (pos + tmp->len > actual_len) { + dev_err(dev->udev->dev.parent, "Format error\n"); + break; + } + + if (tmp->id == id) { + memcpy(msg, tmp, tmp->len); + goto end; + } + + pos += tmp->len; + } + + err = -EINVAL; + +end: + kfree(buf); + + return err; +} + +static int kvaser_usb_send_simple_msg(const struct kvaser_usb *dev, + u8 msg_id, int channel) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = msg_id; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple); + msg->u.simple.channel = channel; + msg->u.simple.tid = 0xff; + + rc = kvaser_usb_send_msg(dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_get_software_info(struct kvaser_usb *dev) +{ + struct kvaser_msg msg; + int err; + + err = kvaser_usb_send_simple_msg(dev, CMD_GET_SOFTWARE_INFO, 0); + if (err) + return err; + + err = kvaser_usb_wait_msg(dev, CMD_GET_SOFTWARE_INFO_REPLY, &msg); + if (err) + return err; + + dev->fw_version = le32_to_cpu(msg.u.softinfo.fw_version); + + return 0; +} + +static int kvaser_usb_get_card_info(struct kvaser_usb *dev) +{ + struct kvaser_msg msg; + int err; + + err = kvaser_usb_send_simple_msg(dev, CMD_GET_CARD_INFO, 0); + if (err) + return err; + + err = kvaser_usb_wait_msg(dev, CMD_GET_CARD_INFO_REPLY, &msg); + if (err) + return err; + + dev->nchannels = msg.u.cardinfo.nchannels; + + return 0; +} + +static void kvaser_usb_tx_acknowledge(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct net_device_stats *stats; + struct kvaser_usb_tx_urb_context *context; + struct kvaser_usb_net_priv *priv; + struct sk_buff *skb; + struct can_frame *cf; + u8 channel = msg->u.tx_acknowledge.channel; + u8 tid = msg->u.tx_acknowledge.tid; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (!netif_device_present(priv->netdev)) + return; + + stats = &priv->netdev->stats; + + context = &priv->tx_contexts[tid % MAX_TX_URBS]; + + /* Sometimes the state change doesn't come after a bus-off event */ + if (priv->can.restart_ms && + (priv->can.state >= CAN_STATE_BUS_OFF)) { + skb = alloc_can_err_skb(priv->netdev, &cf); + if (skb) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } else { + netdev_err(priv->netdev, + "No memory left for err_skb\n"); + } + + priv->can.can_stats.restarts++; + netif_carrier_on(priv->netdev); + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + } + + stats->tx_packets++; + stats->tx_bytes += context->dlc; + can_get_echo_skb(priv->netdev, context->echo_index); + + context->echo_index = MAX_TX_URBS; + atomic_dec(&priv->active_tx_urbs); + + netif_wake_queue(priv->netdev); +} + +static void kvaser_usb_simple_msg_callback(struct urb *urb) +{ + struct net_device *netdev = urb->context; + + kfree(urb->transfer_buffer); + + if (urb->status) + netdev_warn(netdev, "urb status received: %d\n", + urb->status); +} + +static int kvaser_usb_simple_msg_async(struct kvaser_usb_net_priv *priv, + u8 msg_id) +{ + struct kvaser_usb *dev = priv->dev; + struct net_device *netdev = priv->netdev; + struct kvaser_msg *msg; + struct urb *urb; + void *buf; + int err; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + return -ENOMEM; + } + + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + usb_free_urb(urb); + return -ENOMEM; + } + + msg = (struct kvaser_msg *)buf; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_simple); + msg->id = msg_id; + msg->u.simple.channel = priv->channel; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, msg->len, + kvaser_usb_simple_msg_callback, priv); + usb_anchor_urb(urb, &priv->tx_submitted); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err) { + netdev_err(netdev, "Error transmitting URB\n"); + usb_unanchor_urb(urb); + usb_free_urb(urb); + kfree(buf); + return err; + } + + usb_free_urb(urb); + + return 0; +} + +static void kvaser_usb_unlink_tx_urbs(struct kvaser_usb_net_priv *priv) +{ + int i; + + usb_kill_anchored_urbs(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < MAX_TX_URBS; i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; +} + +static void kvaser_usb_rx_error(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + struct kvaser_usb_net_priv *priv; + unsigned int new_state; + u8 channel, status, txerr, rxerr, error_factor; + + switch (msg->id) { + case CMD_CAN_ERROR_EVENT: + channel = msg->u.error_event.channel; + status = msg->u.error_event.status; + txerr = msg->u.error_event.tx_errors_count; + rxerr = msg->u.error_event.rx_errors_count; + error_factor = msg->u.error_event.error_factor; + break; + case CMD_LOG_MESSAGE: + channel = msg->u.log_message.channel; + status = msg->u.log_message.data[0]; + txerr = msg->u.log_message.data[2]; + rxerr = msg->u.log_message.data[3]; + error_factor = msg->u.log_message.data[1]; + break; + case CMD_CHIP_STATE_EVENT: + channel = msg->u.chip_state_event.channel; + status = msg->u.chip_state_event.status; + txerr = msg->u.chip_state_event.tx_errors_count; + rxerr = msg->u.chip_state_event.rx_errors_count; + error_factor = 0; + break; + default: + dev_err(dev->udev->dev.parent, "Invalid msg id (%d)\n", + msg->id); + return; + } + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if (status & M16C_STATE_BUS_RESET) { + kvaser_usb_unlink_tx_urbs(priv); + return; + } + + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + new_state = priv->can.state; + + netdev_dbg(priv->netdev, "Error status: 0x%02x\n", status); + + if (status & M16C_STATE_BUS_OFF) { + cf->can_id |= CAN_ERR_BUSOFF; + + priv->can.can_stats.bus_off++; + if (!priv->can.restart_ms) + kvaser_usb_simple_msg_async(priv, CMD_STOP_CHIP); + + netif_carrier_off(priv->netdev); + + new_state = CAN_STATE_BUS_OFF; + } else if (status & M16C_STATE_BUS_PASSIVE) { + if (priv->can.state != CAN_STATE_ERROR_PASSIVE) { + cf->can_id |= CAN_ERR_CRTL; + + if (txerr || rxerr) + cf->data[1] = (txerr > rxerr) + ? CAN_ERR_CRTL_TX_PASSIVE + : CAN_ERR_CRTL_RX_PASSIVE; + else + cf->data[1] = CAN_ERR_CRTL_TX_PASSIVE | + CAN_ERR_CRTL_RX_PASSIVE; + + priv->can.can_stats.error_passive++; + } + + new_state = CAN_STATE_ERROR_PASSIVE; + } + + if (status == M16C_STATE_BUS_ERROR) { + if ((priv->can.state < CAN_STATE_ERROR_WARNING) && + ((txerr >= 96) || (rxerr >= 96))) { + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = (txerr > rxerr) + ? CAN_ERR_CRTL_TX_WARNING + : CAN_ERR_CRTL_RX_WARNING; + + priv->can.can_stats.error_warning++; + new_state = CAN_STATE_ERROR_WARNING; + } else if (priv->can.state > CAN_STATE_ERROR_ACTIVE) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + + new_state = CAN_STATE_ERROR_ACTIVE; + } + } + + if (!status) { + cf->can_id |= CAN_ERR_PROT; + cf->data[2] = CAN_ERR_PROT_ACTIVE; + + new_state = CAN_STATE_ERROR_ACTIVE; + } + + if (priv->can.restart_ms && + (priv->can.state >= CAN_STATE_BUS_OFF) && + (new_state < CAN_STATE_BUS_OFF)) { + cf->can_id |= CAN_ERR_RESTARTED; + netif_carrier_on(priv->netdev); + + priv->can.can_stats.restarts++; + } + + if (error_factor) { + priv->can.can_stats.bus_error++; + stats->rx_errors++; + + cf->can_id |= CAN_ERR_BUSERROR | CAN_ERR_PROT; + + if (error_factor & M16C_EF_ACKE) + cf->data[3] |= (CAN_ERR_PROT_LOC_ACK); + if (error_factor & M16C_EF_CRCE) + cf->data[3] |= (CAN_ERR_PROT_LOC_CRC_SEQ | + CAN_ERR_PROT_LOC_CRC_DEL); + if (error_factor & M16C_EF_FORME) + cf->data[2] |= CAN_ERR_PROT_FORM; + if (error_factor & M16C_EF_STFE) + cf->data[2] |= CAN_ERR_PROT_STUFF; + if (error_factor & M16C_EF_BITE0) + cf->data[2] |= CAN_ERR_PROT_BIT0; + if (error_factor & M16C_EF_BITE1) + cf->data[2] |= CAN_ERR_PROT_BIT1; + if (error_factor & M16C_EF_TRE) + cf->data[2] |= CAN_ERR_PROT_TX; + } + + cf->data[6] = txerr; + cf->data[7] = rxerr; + + priv->bec.txerr = txerr; + priv->bec.rxerr = rxerr; + + priv->can.state = new_state; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void kvaser_usb_rx_can_err(const struct kvaser_usb_net_priv *priv, + const struct kvaser_msg *msg) +{ + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats = &priv->netdev->stats; + + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | + MSG_FLAG_NERR)) { + netdev_err(priv->netdev, "Unknow error (flags: 0x%02x)\n", + msg->u.rx_can.flag); + + stats->rx_errors++; + return; + } + + if (msg->u.rx_can.flag & MSG_FLAG_OVERRUN) { + skb = alloc_can_err_skb(priv->netdev, &cf); + if (!skb) { + stats->rx_dropped++; + return; + } + + cf->can_id |= CAN_ERR_CRTL; + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; + + stats->rx_over_errors++; + stats->rx_errors++; + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + } +} + +static void kvaser_usb_rx_can_msg(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + struct can_frame *cf; + struct sk_buff *skb; + struct net_device_stats *stats; + u8 channel = msg->u.rx_can.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + stats = &priv->netdev->stats; + + if (msg->u.rx_can.flag & (MSG_FLAG_ERROR_FRAME | MSG_FLAG_NERR | + MSG_FLAG_OVERRUN)) { + kvaser_usb_rx_can_err(priv, msg); + return; + } else if (msg->u.rx_can.flag & ~MSG_FLAG_REMOTE_FRAME) { + netdev_warn(priv->netdev, + "Unhandled frame (flags: 0x%02x)", + msg->u.rx_can.flag); + return; + } + + skb = alloc_can_skb(priv->netdev, &cf); + if (!skb) { + stats->tx_dropped++; + return; + } + + cf->can_id = ((msg->u.rx_can.msg[0] & 0x1f) << 6) | + (msg->u.rx_can.msg[1] & 0x3f); + cf->can_dlc = get_can_dlc(msg->u.rx_can.msg[5]); + + if (msg->id == CMD_RX_EXT_MESSAGE) { + cf->can_id <<= 18; + cf->can_id |= ((msg->u.rx_can.msg[2] & 0x0f) << 14) | + ((msg->u.rx_can.msg[3] & 0xff) << 6) | + (msg->u.rx_can.msg[4] & 0x3f); + cf->can_id |= CAN_EFF_FLAG; + } + + if (msg->u.rx_can.flag & MSG_FLAG_REMOTE_FRAME) + cf->can_id |= CAN_RTR_FLAG; + else + memcpy(cf->data, &msg->u.rx_can.msg[6], cf->can_dlc); + + netif_rx(skb); + + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; +} + +static void kvaser_usb_start_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = msg->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + if (completion_done(&priv->start_comp) && + netif_queue_stopped(priv->netdev)) { + netif_wake_queue(priv->netdev); + } else { + netif_start_queue(priv->netdev); + complete(&priv->start_comp); + } +} + +static void kvaser_usb_stop_chip_reply(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + struct kvaser_usb_net_priv *priv; + u8 channel = msg->u.simple.channel; + + if (channel >= dev->nchannels) { + dev_err(dev->udev->dev.parent, + "Invalid channel number (%d)\n", channel); + return; + } + + priv = dev->nets[channel]; + + complete(&priv->stop_comp); +} + +static void kvaser_usb_handle_message(const struct kvaser_usb *dev, + const struct kvaser_msg *msg) +{ + switch (msg->id) { + case CMD_START_CHIP_REPLY: + kvaser_usb_start_chip_reply(dev, msg); + break; + + case CMD_STOP_CHIP_REPLY: + kvaser_usb_stop_chip_reply(dev, msg); + break; + + case CMD_RX_STD_MESSAGE: + case CMD_RX_EXT_MESSAGE: + kvaser_usb_rx_can_msg(dev, msg); + break; + + case CMD_CHIP_STATE_EVENT: + case CMD_CAN_ERROR_EVENT: + kvaser_usb_rx_error(dev, msg); + break; + + case CMD_LOG_MESSAGE: + if (msg->u.log_message.flags & MSG_FLAG_ERROR_FRAME) + kvaser_usb_rx_error(dev, msg); + break; + + case CMD_TX_ACKNOWLEDGE: + kvaser_usb_tx_acknowledge(dev, msg); + break; + + default: + dev_warn(dev->udev->dev.parent, + "Unhandled message (%d)\n", msg->id); + break; + } +} + +static void kvaser_usb_read_bulk_callback(struct urb *urb) +{ + struct kvaser_usb *dev = urb->context; + struct kvaser_msg *msg; + int pos = 0; + int err, i; + + switch (urb->status) { + case 0: + break; + case -ENOENT: + case -ESHUTDOWN: + return; + default: + dev_info(dev->udev->dev.parent, "Rx URB aborted (%d)\n", + urb->status); + goto resubmit_urb; + } + + while (pos <= urb->actual_length - MSG_HEADER_LEN) { + msg = urb->transfer_buffer + pos; + + if (!msg->len) + break; + + if (pos + msg->len > urb->actual_length) { + dev_err(dev->udev->dev.parent, "Format error\n"); + break; + } + + kvaser_usb_handle_message(dev, msg); + + pos += msg->len; + } + +resubmit_urb: + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + urb->transfer_buffer, RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, dev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (err == -ENODEV) { + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + netif_device_detach(dev->nets[i]->netdev); + } + } else if (err) { + dev_err(dev->udev->dev.parent, + "Failed resubmitting read bulk urb: %d\n", err); + } + + return; +} + +static int kvaser_usb_setup_rx_urbs(struct kvaser_usb *dev) +{ + int i, err = 0; + + if (dev->rxinitdone) + return 0; + + for (i = 0; i < MAX_RX_URBS; i++) { + struct urb *urb = NULL; + u8 *buf = NULL; + dma_addr_t buf_dma; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + dev_warn(dev->udev->dev.parent, + "No memory left for URBs\n"); + err = -ENOMEM; + break; + } + + buf = usb_alloc_coherent(dev->udev, RX_BUFFER_SIZE, + GFP_KERNEL, &buf_dma); + if (!buf) { + dev_warn(dev->udev->dev.parent, + "No memory left for USB buffer\n"); + usb_free_urb(urb); + err = -ENOMEM; + break; + } + + usb_fill_bulk_urb(urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in->bEndpointAddress), + buf, RX_BUFFER_SIZE, + kvaser_usb_read_bulk_callback, + dev); + urb->transfer_dma = buf_dma; + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + usb_anchor_urb(urb, &dev->rx_submitted); + + err = usb_submit_urb(urb, GFP_KERNEL); + if (err) { + usb_unanchor_urb(urb); + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, buf, + buf_dma); + usb_free_urb(urb); + break; + } + + dev->rxbuf[i] = buf; + dev->rxbuf_dma[i] = buf_dma; + + usb_free_urb(urb); + } + + if (i == 0) { + dev_warn(dev->udev->dev.parent, + "Cannot setup read URBs, error %d\n", err); + return err; + } else if (i < MAX_RX_URBS) { + dev_warn(dev->udev->dev.parent, + "RX performances may be slow\n"); + } + + dev->rxinitdone = true; + + return 0; +} + +static int kvaser_usb_set_opt_mode(const struct kvaser_usb_net_priv *priv) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_SET_CTRL_MODE; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_ctrl_mode); + msg->u.ctrl_mode.tid = 0xff; + msg->u.ctrl_mode.channel = priv->channel; + + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_SILENT; + else + msg->u.ctrl_mode.ctrl_mode = KVASER_CTRL_MODE_NORMAL; + + rc = kvaser_usb_send_msg(priv->dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_start_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->start_comp); + + err = kvaser_usb_send_simple_msg(priv->dev, CMD_START_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->start_comp, + msecs_to_jiffies(START_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_open(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + int err; + + err = open_candev(netdev); + if (err) + return err; + + err = kvaser_usb_setup_rx_urbs(dev); + if (err) + goto error; + + err = kvaser_usb_set_opt_mode(priv); + if (err) + goto error; + + err = kvaser_usb_start_chip(priv); + if (err) { + netdev_warn(netdev, "Cannot start device, error %d\n", err); + goto error; + } + + priv->can.state = CAN_STATE_ERROR_ACTIVE; + + return 0; + +error: + close_candev(netdev); + return err; +} + +static void kvaser_usb_unlink_all_urbs(struct kvaser_usb *dev) +{ + int i; + + usb_kill_anchored_urbs(&dev->rx_submitted); + + for (i = 0; i < MAX_RX_URBS; i++) + usb_free_coherent(dev->udev, RX_BUFFER_SIZE, + dev->rxbuf[i], + dev->rxbuf_dma[i]); + + for (i = 0; i < MAX_NET_DEVICES; i++) { + struct kvaser_usb_net_priv *priv = dev->nets[i]; + + if (priv) + kvaser_usb_unlink_tx_urbs(priv); + } +} + +static int kvaser_usb_stop_chip(struct kvaser_usb_net_priv *priv) +{ + int err; + + init_completion(&priv->stop_comp); + + err = kvaser_usb_send_simple_msg(priv->dev, CMD_STOP_CHIP, + priv->channel); + if (err) + return err; + + if (!wait_for_completion_timeout(&priv->stop_comp, + msecs_to_jiffies(STOP_TIMEOUT))) + return -ETIMEDOUT; + + return 0; +} + +static int kvaser_usb_flush_queue(struct kvaser_usb_net_priv *priv) +{ + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_FLUSH_QUEUE; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_flush_queue); + msg->u.flush_queue.channel = priv->channel; + msg->u.flush_queue.flags = 0x00; + + rc = kvaser_usb_send_msg(priv->dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_close(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + int err; + + netif_stop_queue(netdev); + + err = kvaser_usb_flush_queue(priv); + if (err) + netdev_warn(netdev, "Cannot flush queue, error %d\n", err); + + if (kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, priv->channel)) + netdev_warn(netdev, "Cannot reset card, error %d\n", err); + + err = kvaser_usb_stop_chip(priv); + if (err) + netdev_warn(netdev, "Cannot stop device, error %d\n", err); + + priv->can.state = CAN_STATE_STOPPED; + close_candev(priv->netdev); + + return 0; +} + +static void kvaser_usb_write_bulk_callback(struct urb *urb) +{ + struct kvaser_usb_tx_urb_context *context = urb->context; + struct kvaser_usb_net_priv *priv; + struct net_device *netdev; + + if (WARN_ON(!context)) + return; + + priv = context->priv; + netdev = priv->netdev; + + kfree(urb->transfer_buffer); + + if (!netif_device_present(netdev)) + return; + + if (urb->status) + netdev_info(netdev, "Tx URB aborted (%d)\n", urb->status); +} + +static netdev_tx_t kvaser_usb_start_xmit(struct sk_buff *skb, + struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct kvaser_usb *dev = priv->dev; + struct net_device_stats *stats = &netdev->stats; + struct can_frame *cf = (struct can_frame *)skb->data; + struct kvaser_usb_tx_urb_context *context = NULL; + struct urb *urb; + void *buf; + struct kvaser_msg *msg; + int i, err; + int ret = NETDEV_TX_OK; + + if (can_dropped_invalid_skb(netdev, skb)) + return NETDEV_TX_OK; + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + netdev_err(netdev, "No memory left for URBs\n"); + stats->tx_dropped++; + goto nourbmem; + } + + buf = kmalloc(sizeof(struct kvaser_msg), GFP_ATOMIC); + if (!buf) { + netdev_err(netdev, "No memory left for USB buffer\n"); + stats->tx_dropped++; + goto nobufmem; + } + + msg = buf; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_tx_can); + msg->u.tx_can.flags = 0; + msg->u.tx_can.channel = priv->channel; + + if (cf->can_id & CAN_EFF_FLAG) { + msg->id = CMD_TX_EXT_MESSAGE; + msg->u.tx_can.msg[0] = (cf->can_id >> 24) & 0x1f; + msg->u.tx_can.msg[1] = (cf->can_id >> 18) & 0x3f; + msg->u.tx_can.msg[2] = (cf->can_id >> 14) & 0x0f; + msg->u.tx_can.msg[3] = (cf->can_id >> 6) & 0xff; + msg->u.tx_can.msg[4] = cf->can_id & 0x3f; + } else { + msg->id = CMD_TX_STD_MESSAGE; + msg->u.tx_can.msg[0] = (cf->can_id >> 6) & 0x1f; + msg->u.tx_can.msg[1] = cf->can_id & 0x3f; + } + + msg->u.tx_can.msg[5] = cf->can_dlc; + memcpy(&msg->u.tx_can.msg[6], cf->data, cf->can_dlc); + + if (cf->can_id & CAN_RTR_FLAG) + msg->u.tx_can.flags |= MSG_FLAG_REMOTE_FRAME; + + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) { + if (priv->tx_contexts[i].echo_index == MAX_TX_URBS) { + context = &priv->tx_contexts[i]; + break; + } + } + + if (!context) { + netdev_warn(netdev, "cannot find free context\n"); + ret = NETDEV_TX_BUSY; + goto releasebuf; + } + + context->priv = priv; + context->echo_index = i; + context->dlc = cf->can_dlc; + + msg->u.tx_can.tid = context->echo_index; + + usb_fill_bulk_urb(urb, dev->udev, + usb_sndbulkpipe(dev->udev, + dev->bulk_out->bEndpointAddress), + buf, msg->len, + kvaser_usb_write_bulk_callback, context); + usb_anchor_urb(urb, &priv->tx_submitted); + + can_put_echo_skb(skb, netdev, context->echo_index); + + atomic_inc(&priv->active_tx_urbs); + + if (atomic_read(&priv->active_tx_urbs) >= MAX_TX_URBS) + netif_stop_queue(netdev); + + err = usb_submit_urb(urb, GFP_ATOMIC); + if (unlikely(err)) { + can_free_echo_skb(netdev, context->echo_index); + + skb = NULL; /* set to NULL to avoid double free in + * dev_kfree_skb(skb) */ + + atomic_dec(&priv->active_tx_urbs); + usb_unanchor_urb(urb); + + stats->tx_dropped++; + + if (err == -ENODEV) + netif_device_detach(netdev); + else + netdev_warn(netdev, "Failed tx_urb %d\n", err); + + goto releasebuf; + } + + usb_free_urb(urb); + + return NETDEV_TX_OK; + +releasebuf: + kfree(buf); +nobufmem: + usb_free_urb(urb); +nourbmem: + dev_kfree_skb(skb); + return ret; +} + +static const struct net_device_ops kvaser_usb_netdev_ops = { + .ndo_open = kvaser_usb_open, + .ndo_stop = kvaser_usb_close, + .ndo_start_xmit = kvaser_usb_start_xmit, +}; + +static const struct can_bittiming_const kvaser_usb_bittiming_const = { + .name = "kvaser_usb", + .tseg1_min = KVASER_USB_TSEG1_MIN, + .tseg1_max = KVASER_USB_TSEG1_MAX, + .tseg2_min = KVASER_USB_TSEG2_MIN, + .tseg2_max = KVASER_USB_TSEG2_MAX, + .sjw_max = KVASER_USB_SJW_MAX, + .brp_min = KVASER_USB_BRP_MIN, + .brp_max = KVASER_USB_BRP_MAX, + .brp_inc = KVASER_USB_BRP_INC, +}; + +static int kvaser_usb_set_bittiming(struct net_device *netdev) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + struct can_bittiming *bt = &priv->can.bittiming; + struct kvaser_usb *dev = priv->dev; + struct kvaser_msg *msg; + int rc; + + msg = kmalloc(sizeof(*msg), GFP_KERNEL); + if (!msg) + return -ENOMEM; + + msg->id = CMD_SET_BUS_PARAMS; + msg->len = MSG_HEADER_LEN + sizeof(struct kvaser_msg_busparams); + msg->u.busparams.channel = priv->channel; + msg->u.busparams.tid = 0xff; + msg->u.busparams.bitrate = cpu_to_le32(bt->bitrate); + msg->u.busparams.sjw = bt->sjw; + msg->u.busparams.tseg1 = bt->prop_seg + bt->phase_seg1; + msg->u.busparams.tseg2 = bt->phase_seg2; + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) + msg->u.busparams.no_samp = 3; + else + msg->u.busparams.no_samp = 1; + + rc = kvaser_usb_send_msg(dev, msg); + + kfree(msg); + return rc; +} + +static int kvaser_usb_set_mode(struct net_device *netdev, + enum can_mode mode) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + int err; + + switch (mode) { + case CAN_MODE_START: + err = kvaser_usb_simple_msg_async(priv, CMD_START_CHIP); + if (err) + return err; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int kvaser_usb_get_berr_counter(const struct net_device *netdev, + struct can_berr_counter *bec) +{ + struct kvaser_usb_net_priv *priv = netdev_priv(netdev); + + *bec = priv->bec; + + return 0; +} + +static void kvaser_usb_remove_interfaces(struct kvaser_usb *dev) +{ + int i; + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + unregister_netdev(dev->nets[i]->netdev); + } + + kvaser_usb_unlink_all_urbs(dev); + + for (i = 0; i < dev->nchannels; i++) { + if (!dev->nets[i]) + continue; + + free_candev(dev->nets[i]->netdev); + } +} + +static int kvaser_usb_init_one(struct usb_interface *intf, + const struct usb_device_id *id, int channel) +{ + struct kvaser_usb *dev = usb_get_intfdata(intf); + struct net_device *netdev; + struct kvaser_usb_net_priv *priv; + int i, err; + + netdev = alloc_candev(sizeof(*priv), MAX_TX_URBS); + if (!netdev) { + dev_err(&intf->dev, "Cannot alloc candev\n"); + return -ENOMEM; + } + + priv = netdev_priv(netdev); + + init_completion(&priv->start_comp); + init_completion(&priv->stop_comp); + + init_usb_anchor(&priv->tx_submitted); + atomic_set(&priv->active_tx_urbs, 0); + + for (i = 0; i < ARRAY_SIZE(priv->tx_contexts); i++) + priv->tx_contexts[i].echo_index = MAX_TX_URBS; + + priv->dev = dev; + priv->netdev = netdev; + priv->channel = channel; + + priv->can.state = CAN_STATE_STOPPED; + priv->can.clock.freq = CAN_USB_CLOCK; + priv->can.bittiming_const = &kvaser_usb_bittiming_const; + priv->can.do_set_bittiming = kvaser_usb_set_bittiming; + priv->can.do_set_mode = kvaser_usb_set_mode; + if (id->driver_info & KVASER_HAS_TXRX_ERRORS) + priv->can.do_get_berr_counter = kvaser_usb_get_berr_counter; + priv->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES; + if (id->driver_info & KVASER_HAS_SILENT_MODE) + priv->can.ctrlmode_supported |= CAN_CTRLMODE_LISTENONLY; + + netdev->flags |= IFF_ECHO; + + netdev->netdev_ops = &kvaser_usb_netdev_ops; + + SET_NETDEV_DEV(netdev, &intf->dev); + + dev->nets[channel] = priv; + + err = register_candev(netdev); + if (err) { + dev_err(&intf->dev, "Failed to register can device\n"); + free_candev(netdev); + dev->nets[channel] = NULL; + return err; + } + + netdev_dbg(netdev, "device registered\n"); + + return 0; +} + +static void kvaser_usb_get_endpoints(const struct usb_interface *intf, + struct usb_endpoint_descriptor **in, + struct usb_endpoint_descriptor **out) +{ + const struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + iface_desc = &intf->altsetting[0]; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) + *in = endpoint; + + if (usb_endpoint_is_bulk_out(endpoint)) + *out = endpoint; + } +} + +static int kvaser_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct kvaser_usb *dev; + int err = -ENOMEM; + int i; + + dev = devm_kzalloc(&intf->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + kvaser_usb_get_endpoints(intf, &dev->bulk_in, &dev->bulk_out); + if (!dev->bulk_in || !dev->bulk_out) { + dev_err(&intf->dev, "Cannot get usb endpoint(s)"); + return err; + } + + dev->udev = interface_to_usbdev(intf); + + init_usb_anchor(&dev->rx_submitted); + + usb_set_intfdata(intf, dev); + + for (i = 0; i < MAX_NET_DEVICES; i++) + kvaser_usb_send_simple_msg(dev, CMD_RESET_CHIP, i); + + err = kvaser_usb_get_software_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get software infos, error %d\n", err); + return err; + } + + err = kvaser_usb_get_card_info(dev); + if (err) { + dev_err(&intf->dev, + "Cannot get card infos, error %d\n", err); + return err; + } + + dev_dbg(&intf->dev, "Firmware version: %d.%d.%d\n", + ((dev->fw_version >> 24) & 0xff), + ((dev->fw_version >> 16) & 0xff), + (dev->fw_version & 0xffff)); + + for (i = 0; i < dev->nchannels; i++) { + err = kvaser_usb_init_one(intf, id, i); + if (err) { + kvaser_usb_remove_interfaces(dev); + return err; + } + } + + return 0; +} + +static void kvaser_usb_disconnect(struct usb_interface *intf) +{ + struct kvaser_usb *dev = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + + if (!dev) + return; + + kvaser_usb_remove_interfaces(dev); +} + +static struct usb_driver kvaser_usb_driver = { + .name = "kvaser_usb", + .probe = kvaser_usb_probe, + .disconnect = kvaser_usb_disconnect, + .id_table = kvaser_usb_table, +}; + +module_usb_driver(kvaser_usb_driver); + +MODULE_AUTHOR("Olivier Sobrie <olivier@sobrie.be>"); +MODULE_DESCRIPTION("CAN driver for Kvaser CAN/USB devices"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 86f26a1ede4..25723d8ee20 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -519,8 +519,10 @@ static int pcan_usb_decode_error(struct pcan_usb_msg_context *mc, u8 n, mc->pdev->dev.can.state = new_state; if (status_len & PCAN_USB_STATUSLEN_TIMESTAMP) { + struct skb_shared_hwtstamps *hwts = skb_hwtstamps(skb); + peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); - skb->tstamp = timeval_to_ktime(tv); + hwts->hwtstamp = timeval_to_ktime(tv); } netif_rx(skb); @@ -605,6 +607,7 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) struct sk_buff *skb; struct can_frame *cf; struct timeval tv; + struct skb_shared_hwtstamps *hwts; skb = alloc_can_skb(mc->netdev, &cf); if (!skb) @@ -652,7 +655,8 @@ static int pcan_usb_decode_data(struct pcan_usb_msg_context *mc, u8 status_len) /* convert timestamp into kernel time */ peak_usb_get_ts_tv(&mc->pdev->time_ref, mc->ts16, &tv); - skb->tstamp = timeval_to_ktime(tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); /* push the skb */ netif_rx(skb); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index c4643c400d4..d9290ea788e 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -520,7 +520,6 @@ static int peak_usb_ndo_open(struct net_device *netdev) return err; } - dev->open_time = jiffies; netif_start_queue(netdev); return 0; @@ -576,7 +575,6 @@ static int peak_usb_ndo_stop(struct net_device *netdev) close_candev(netdev); - dev->open_time = 0; dev->can.state = CAN_STATE_STOPPED; /* can set bus off now */ @@ -661,9 +659,6 @@ static int peak_usb_set_mode(struct net_device *netdev, enum can_mode mode) struct peak_usb_device *dev = netdev_priv(netdev); int err = 0; - if (!dev->open_time) - return -EINVAL; - switch (mode) { case CAN_MODE_START: err = peak_usb_restart(dev); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index c8e5e91d7cb..073b47ff8ee 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -104,7 +104,6 @@ struct peak_usb_device { struct can_priv can; struct peak_usb_adapter *adapter; unsigned int ctrl_idx; - int open_time; u32 state; struct sk_buff *echo_skb[PCAN_USB_MAX_TX_URBS]; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index e1626d92511..30d79bfa5b1 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -532,6 +532,7 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, struct can_frame *can_frame; struct sk_buff *skb; struct timeval tv; + struct skb_shared_hwtstamps *hwts; skb = alloc_can_skb(netdev, &can_frame); if (!skb) @@ -549,7 +550,8 @@ static int pcan_usb_pro_handle_canmsg(struct pcan_usb_pro_interface *usb_if, memcpy(can_frame->data, rx->data, can_frame->can_dlc); peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(rx->ts32), &tv); - skb->tstamp = timeval_to_ktime(tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); netif_rx(skb); netdev->stats.rx_packets++; @@ -570,6 +572,7 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, u8 err_mask = 0; struct sk_buff *skb; struct timeval tv; + struct skb_shared_hwtstamps *hwts; /* nothing should be sent while in BUS_OFF state */ if (dev->can.state == CAN_STATE_BUS_OFF) @@ -664,7 +667,8 @@ static int pcan_usb_pro_handle_error(struct pcan_usb_pro_interface *usb_if, dev->can.state = new_state; peak_usb_get_ts_tv(&usb_if->time_ref, le32_to_cpu(er->ts32), &tv); - skb->tstamp = timeval_to_ktime(tv); + hwts = skb_hwtstamps(skb); + hwts->hwtstamp = timeval_to_ktime(tv); netif_rx(skb); netdev->stats.rx_packets++; netdev->stats.rx_bytes += can_frame->can_dlc; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h index 19517b34d14..641d8847c32 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h @@ -936,7 +936,6 @@ struct bnx2x_port { /* used to synchronize phy accesses */ struct mutex phy_mutex; - int need_hw_lock; u32 port_stx; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 54d522da1aa..8779ac1f89a 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -948,14 +948,12 @@ void bnx2x_acquire_phy_lock(struct bnx2x *bp) { mutex_lock(&bp->port.phy_mutex); - if (bp->port.need_hw_lock) - bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_MDIO); + bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_MDIO); } void bnx2x_release_phy_lock(struct bnx2x *bp) { - if (bp->port.need_hw_lock) - bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_MDIO); + bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_MDIO); mutex_unlock(&bp->port.phy_mutex); } @@ -2248,7 +2246,8 @@ int bnx2x_nic_load(struct bnx2x *bp, int load_mode) DRV_PULSE_SEQ_MASK); BNX2X_DEV_INFO("drv_pulse 0x%x\n", bp->fw_drv_pulse_wr_seq); - load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, 0); + load_code = bnx2x_fw_command(bp, DRV_MSG_CODE_LOAD_REQ, + DRV_MSG_CODE_LOAD_REQ_WITH_LFA); if (!load_code) { BNX2X_ERR("MCP response failure, aborting\n"); rc = -EBUSY; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index c40c0253e10..e05f981398b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2660,20 +2660,25 @@ static int bnx2x_set_phys_id(struct net_device *dev, return 1; /* cycle on/off once per second */ case ETHTOOL_ID_ON: + bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_ON, SPEED_1000); + bnx2x_release_phy_lock(bp); break; case ETHTOOL_ID_OFF: + bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_FRONT_PANEL_OFF, 0); - + bnx2x_release_phy_lock(bp); break; case ETHTOOL_ID_INACTIVE: + bnx2x_acquire_phy_lock(bp); bnx2x_set_led(&bp->link_params, &bp->link_vars, LED_MODE_OPER, bp->link_vars.line_speed); + bnx2x_release_phy_lock(bp); } return 0; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h index 7eaa74b78a5..1504e0a0f12 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_hsi.h @@ -695,6 +695,7 @@ struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */ #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54618SE 0x00000e00 #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM8722 0x00000f00 #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM54616 0x00001000 + #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_BCM84834 0x00001100 #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_FAILURE 0x0000fd00 #define PORT_HW_CFG_XGXS_EXT_PHY2_TYPE_NOT_CONN 0x0000ff00 @@ -751,6 +752,7 @@ struct port_hw_cfg { /* port 0: 0x12c port 1: 0x2bc */ #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE 0x00000e00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8722 0x00000f00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616 0x00001000 + #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834 0x00001100 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT_WC 0x0000fc00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_FAILURE 0x0000fd00 #define PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN 0x0000ff00 @@ -1246,6 +1248,7 @@ struct drv_func_mb { #define DRV_MSG_CODE_VRFY_AFEX_SUPPORTED 0xa2000000 #define REQ_BC_VER_4_VRFY_AFEX_SUPPORTED 0x00070002 #define REQ_BC_VER_4_SFP_TX_DISABLE_SUPPORTED 0x00070014 + #define REQ_BC_VER_4_MT_SUPPORTED 0x00070201 #define REQ_BC_VER_4_PFC_STATS_SUPPORTED 0x00070201 #define REQ_BC_VER_4_FCOE_FEATURES 0x00070209 @@ -2159,6 +2162,16 @@ struct shmem2_region { #define SHMEM_EEE_TIME_OUTPUT_BIT 0x80000000 u32 sizeof_port_stats; + + /* Link Flap Avoidance */ + u32 lfa_host_addr[PORT_MAX]; + u32 reserved1; + + u32 reserved2; /* Offset 0x148 */ + u32 reserved3; /* Offset 0x14C */ + u32 reserved4; /* Offset 0x150 */ + u32 link_attr_sync[PORT_MAX]; /* Offset 0x154 */ + #define LINK_ATTR_SYNC_KR2_ENABLE (1<<0) }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c index c98da25f22e..3e7d8246c50 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.c @@ -121,6 +121,7 @@ #define GP_STATUS_10G_XFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI #define GP_STATUS_20G_DXGXS MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS #define GP_STATUS_10G_SFI MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI +#define GP_STATUS_20G_KR2 MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_KR2 #define LINK_10THD LINK_STATUS_SPEED_AND_DUPLEX_10THD #define LINK_10TFD LINK_STATUS_SPEED_AND_DUPLEX_10TFD #define LINK_100TXHD LINK_STATUS_SPEED_AND_DUPLEX_100TXHD @@ -1440,30 +1441,47 @@ void bnx2x_pfc_statistic(struct link_params *params, struct link_vars *vars, /******************************************************************/ /* MAC/PBF section */ /******************************************************************/ -static void bnx2x_set_mdio_clk(struct bnx2x *bp, u32 chip_id, u8 port) +static void bnx2x_set_mdio_clk(struct bnx2x *bp, u32 chip_id, + u32 emac_base) { - u32 mode, emac_base; + u32 new_mode, cur_mode; + u32 clc_cnt; /* Set clause 45 mode, slow down the MDIO clock to 2.5MHz * (a value of 49==0x31) and make sure that the AUTO poll is off */ + cur_mode = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - if (CHIP_IS_E2(bp)) - emac_base = GRCBASE_EMAC0; - else - emac_base = (port) ? GRCBASE_EMAC1 : GRCBASE_EMAC0; - mode = REG_RD(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE); - mode &= ~(EMAC_MDIO_MODE_AUTO_POLL | - EMAC_MDIO_MODE_CLOCK_CNT); if (USES_WARPCORE(bp)) - mode |= (74L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); + clc_cnt = 74L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT; else - mode |= (49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT); + clc_cnt = 49L << EMAC_MDIO_MODE_CLOCK_CNT_BITSHIFT; - mode |= (EMAC_MDIO_MODE_CLAUSE_45); - REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE, mode); + if (((cur_mode & EMAC_MDIO_MODE_CLOCK_CNT) == clc_cnt) && + (cur_mode & (EMAC_MDIO_MODE_CLAUSE_45))) + return; + + new_mode = cur_mode & + ~(EMAC_MDIO_MODE_AUTO_POLL | EMAC_MDIO_MODE_CLOCK_CNT); + new_mode |= clc_cnt; + new_mode |= (EMAC_MDIO_MODE_CLAUSE_45); + DP(NETIF_MSG_LINK, "Changing emac_mode from 0x%x to 0x%x\n", + cur_mode, new_mode); + REG_WR(bp, emac_base + EMAC_REG_EMAC_MDIO_MODE, new_mode); udelay(40); } + +static void bnx2x_set_mdio_emac_per_phy(struct bnx2x *bp, + struct link_params *params) +{ + u8 phy_index; + /* Set mdio clock per phy */ + for (phy_index = INT_PHY; phy_index < params->num_phys; + phy_index++) + bnx2x_set_mdio_clk(bp, params->chip_id, + params->phy[phy_index].mdio_ctrl); +} + static u8 bnx2x_is_4_port_mode(struct bnx2x *bp) { u32 port4mode_ovwr_val; @@ -1508,7 +1526,8 @@ static void bnx2x_emac_init(struct link_params *params, } timeout--; } while (val & EMAC_MODE_RESET); - bnx2x_set_mdio_clk(bp, params->chip_id, port); + + bnx2x_set_mdio_emac_per_phy(bp, params); /* Set mac address */ val = ((params->mac_addr[0] << 8) | params->mac_addr[1]); @@ -1664,7 +1683,10 @@ static void bnx2x_xmac_init(struct link_params *params, u32 max_speed) * ports of the path */ - if ((CHIP_NUM(bp) == CHIP_NUM_57840_4_10) && + if (((CHIP_NUM(bp) == CHIP_NUM_57840_4_10) || + (CHIP_NUM(bp) == CHIP_NUM_57840_2_20) || + (CHIP_NUM(bp) == CHIP_NUM_57840_OBSOLETE)) && + is_port4mode && (REG_RD(bp, MISC_REG_RESET_REG_2) & MISC_REGISTERS_RESET_REG_2_XMAC)) { DP(NETIF_MSG_LINK, @@ -1760,6 +1782,18 @@ static int bnx2x_xmac_enable(struct link_params *params, */ REG_WR(bp, NIG_REG_EGRESS_EMAC0_PORT + params->port*4, 0); + /* When XMAC is in XLGMII mode, disable sending idles for fault + * detection. + */ + if (!(params->phy[INT_PHY].flags & FLAGS_TX_ERROR_CHECK)) { + REG_WR(bp, xmac_base + XMAC_REG_RX_LSS_CTRL, + (XMAC_RX_LSS_CTRL_REG_LOCAL_FAULT_DISABLE | + XMAC_RX_LSS_CTRL_REG_REMOTE_FAULT_DISABLE)); + REG_WR(bp, xmac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, 0); + REG_WR(bp, xmac_base + XMAC_REG_CLEAR_RX_LSS_STATUS, + XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_LOCAL_FAULT_STATUS | + XMAC_CLEAR_RX_LSS_STATUS_REG_CLEAR_REMOTE_FAULT_STATUS); + } /* Set Max packet size */ REG_WR(bp, xmac_base + XMAC_REG_RX_MAX_SIZE, 0x2710); @@ -1780,6 +1814,12 @@ static int bnx2x_xmac_enable(struct link_params *params, /* Enable TX and RX */ val = XMAC_CTRL_REG_TX_EN | XMAC_CTRL_REG_RX_EN; + /* Set MAC in XLGMII mode for dual-mode */ + if ((vars->line_speed == SPEED_20000) && + (params->phy[INT_PHY].supported & + SUPPORTED_20000baseKR2_Full)) + val |= XMAC_CTRL_REG_XLGMII_ALIGN_ENB; + /* Check loopback mode */ if (lb) val |= XMAC_CTRL_REG_LINE_LOCAL_LPBK; @@ -2096,6 +2136,16 @@ static void bnx2x_update_mng(struct link_params *params, u32 link_status) port_mb[params->port].link_status), link_status); } +static void bnx2x_update_link_attr(struct link_params *params, u32 link_attr) +{ + struct bnx2x *bp = params->bp; + + if (SHMEM2_HAS(bp, link_attr_sync)) + REG_WR(bp, params->shmem2_base + + offsetof(struct shmem2_region, + link_attr_sync[params->port]), link_attr); +} + static void bnx2x_update_pfc_nig(struct link_params *params, struct link_vars *vars, struct bnx2x_nig_brb_pfc_port_params *nig_params) @@ -2126,7 +2176,7 @@ static void bnx2x_update_pfc_nig(struct link_params *params, if (CHIP_IS_E3(bp)) ppp_enable = 0; else - ppp_enable = 1; + ppp_enable = 1; xcm_mask &= ~(port ? NIG_LLH1_XCM_MASK_REG_LLH1_XCM_MASK_BCN : NIG_LLH0_XCM_MASK_REG_LLH0_XCM_MASK_BCN); xcm_out_en = 0; @@ -2247,7 +2297,6 @@ int bnx2x_update_pfc(struct link_params *params, return bnx2x_status; } - static int bnx2x_bmac1_enable(struct link_params *params, struct link_vars *vars, u8 is_lb) @@ -2651,6 +2700,13 @@ static int bnx2x_cl45_read(struct bnx2x *bp, struct bnx2x_phy *phy, u32 val; u16 i; int rc = 0; + u32 chip_id; + if (phy->flags & FLAGS_MDC_MDIO_WA_G) { + chip_id = (REG_RD(bp, MISC_REG_CHIP_NUM) << 16) | + ((REG_RD(bp, MISC_REG_CHIP_REV) & 0xf) << 12); + bnx2x_set_mdio_clk(bp, chip_id, phy->mdio_ctrl); + } + if (phy->flags & FLAGS_MDC_MDIO_WA_B0) bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS, EMAC_MDIO_STATUS_10MB); @@ -2719,6 +2775,13 @@ static int bnx2x_cl45_write(struct bnx2x *bp, struct bnx2x_phy *phy, u32 tmp; u8 i; int rc = 0; + u32 chip_id; + if (phy->flags & FLAGS_MDC_MDIO_WA_G) { + chip_id = (REG_RD(bp, MISC_REG_CHIP_NUM) << 16) | + ((REG_RD(bp, MISC_REG_CHIP_REV) & 0xf) << 12); + bnx2x_set_mdio_clk(bp, chip_id, phy->mdio_ctrl); + } + if (phy->flags & FLAGS_MDC_MDIO_WA_B0) bnx2x_bits_en(bp, phy->mdio_ctrl + EMAC_REG_EMAC_MDIO_STATUS, EMAC_MDIO_STATUS_10MB); @@ -3147,6 +3210,15 @@ static void bnx2x_cl45_read_or_write(struct bnx2x *bp, struct bnx2x_phy *phy, bnx2x_cl45_write(bp, phy, devad, reg, val | or_val); } +static void bnx2x_cl45_read_and_write(struct bnx2x *bp, + struct bnx2x_phy *phy, + u8 devad, u16 reg, u16 and_val) +{ + u16 val; + bnx2x_cl45_read(bp, phy, devad, reg, &val); + bnx2x_cl45_write(bp, phy, devad, reg, val & and_val); +} + int bnx2x_phy_read(struct link_params *params, u8 phy_addr, u8 devad, u16 reg, u16 *ret_val) { @@ -3551,6 +3623,44 @@ static u8 bnx2x_ext_phy_resolve_fc(struct bnx2x_phy *phy, * init configuration, and set/clear SGMII flag. Internal * phy init is done purely in phy_init stage. */ +static void bnx2x_warpcore_enable_AN_KR2(struct bnx2x_phy *phy, + struct link_params *params, + struct link_vars *vars) +{ + struct bnx2x *bp = params->bp; + u16 i; + static struct bnx2x_reg_set reg_set[] = { + /* Step 1 - Program the TX/RX alignment markers */ + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL5, 0xa157}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL7, 0xcbe2}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL6, 0x7537}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL9, 0xa157}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL11, 0xcbe2}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL10, 0x7537}, + /* Step 2 - Configure the NP registers */ + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_USERB0_CTRL, 0x000a}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL1, 0x6400}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL3, 0x0620}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CODE_FIELD, 0x0157}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI1, 0x6464}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI2, 0x3150}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI3, 0x3150}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_BAM_CODE, 0x0157}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_UD_CODE, 0x0620} + }; + DP(NETIF_MSG_LINK, "Enabling 20G-KR2\n"); + + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_CL49_USERB0_CTRL, (3<<6)); + + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg, + reg_set[i].val); + + /* Start KR2 work-around timer which handles BCM8073 link-parner */ + vars->link_attr_sync |= LINK_ATTR_SYNC_KR2_ENABLE; + bnx2x_update_link_attr(params, vars->link_attr_sync); +} static void bnx2x_warpcore_set_lpi_passthrough(struct bnx2x_phy *phy, struct link_params *params) @@ -3564,6 +3674,21 @@ static void bnx2x_warpcore_set_lpi_passthrough(struct bnx2x_phy *phy, MDIO_WC_REG_DIGITAL4_MISC5, 0xc000); } +static void bnx2x_warpcore_restart_AN_KR(struct bnx2x_phy *phy, + struct link_params *params) +{ + /* Restart autoneg on the leading lane only */ + struct bnx2x *bp = params->bp; + u16 lane = bnx2x_get_warpcore_lane(phy, params); + CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, + MDIO_AER_BLOCK_AER_REG, lane); + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, + MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1200); + + /* Restore AER */ + bnx2x_set_aer_mmd(params, phy); +} + static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) { @@ -3576,7 +3701,9 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, {MDIO_WC_DEVAD, MDIO_WC_REG_RX66_CONTROL, 0x7415}, {MDIO_WC_DEVAD, MDIO_WC_REG_SERDESDIGITAL_MISC2, 0x6190}, /* Disable Autoneg: re-enable it after adv is done. */ - {MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0} + {MDIO_AN_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0}, + {MDIO_PMA_DEVAD, MDIO_WC_REG_PMD_KR_CONTROL, 0x2}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL72_USERB0_CL72_TX_FIR_TAP, 0}, }; DP(NETIF_MSG_LINK, "Enable Auto Negotiation for KR\n"); /* Set to default registers that may be overriden by 10G force */ @@ -3585,11 +3712,11 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, reg_set[i].val); bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, &cl72_ctrl); - cl72_ctrl &= 0xf8ff; + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, &cl72_ctrl); + cl72_ctrl &= 0x08ff; cl72_ctrl |= 0x3800; bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, cl72_ctrl); + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, cl72_ctrl); /* Check adding advertisement for 1G KX */ if (((vars->line_speed == SPEED_AUTO_NEG) && @@ -3624,6 +3751,16 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, ((0x02 << MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) | (0x06 << MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) | (0x09 << MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET))); + /* Configure the next lane if dual mode */ + if (phy->flags & FLAGS_WC_DUAL_MODE) + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_TX0_TX_DRIVER + 0x10*(lane+1), + ((0x02 << + MDIO_WC_REG_TX0_TX_DRIVER_POST2_COEFF_OFFSET) | + (0x06 << + MDIO_WC_REG_TX0_TX_DRIVER_IDRIVER_OFFSET) | + (0x09 << + MDIO_WC_REG_TX0_TX_DRIVER_IPRE_DRIVER_OFFSET))); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_CL72_USERB0_CL72_OS_DEF_CTRL, 0x03f0); @@ -3670,10 +3807,26 @@ static void bnx2x_warpcore_enable_AN_KR(struct bnx2x_phy *phy, bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL3_UP1, 0x1f); - /* Enable Autoneg */ - bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1200); + if (((phy->req_line_speed == SPEED_AUTO_NEG) && + (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G)) || + (phy->req_line_speed == SPEED_20000)) { + + CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, + MDIO_AER_BLOCK_AER_REG, lane); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_RX1_PCI_CTRL + (0x10*lane), + (1<<11)); + + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXS_X2_CONTROL3, 0x7); + bnx2x_set_aer_mmd(params, phy); + + bnx2x_warpcore_enable_AN_KR2(phy, params, vars); + } + + /* Enable Autoneg: only on the main lane */ + bnx2x_warpcore_restart_AN_KR(phy, params); } static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy, @@ -3692,9 +3845,7 @@ static void bnx2x_warpcore_set_10G_KR(struct bnx2x_phy *phy, {MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL3_UP1, 0x1}, {MDIO_WC_DEVAD, MDIO_WC_REG_DIGITAL5_MISC7, 0xa}, /* Leave cl72 training enable, needed for KR */ - {MDIO_PMA_DEVAD, - MDIO_WC_REG_PMD_IEEE9BLK_TENGBASE_KR_PMD_CONTROL_REGISTER_150, - 0x2} + {MDIO_PMA_DEVAD, MDIO_WC_REG_PMD_KR_CONTROL, 0x2} }; for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) @@ -3764,27 +3915,21 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0); /* Disable 100FX Enable and Auto-Detect */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL1, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_FX100_CTRL1, (val & 0xFFFA)); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_FX100_CTRL1, 0xFFFA); /* Disable 100FX Idle detect */ bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_FX100_CTRL3, 0x0080); /* Set Block address to Remote PHY & Clear forced_speed[5] */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC3, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DIGITAL4_MISC3, (val & 0xFF7F)); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL4_MISC3, 0xFF7F); /* Turn off auto-detect & fiber mode */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, &val); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, - (val & 0xFFEE)); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X1, + 0xFFEE); /* Set filter_force_link, disable_false_link and parallel_detect */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, @@ -3846,22 +3991,65 @@ static void bnx2x_warpcore_set_10G_XFI(struct bnx2x_phy *phy, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x100); /* Release tx_fifo_reset */ + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, + 0xFFFE); + /* Release rxSeqStart */ + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, 0x7FFF); +} + +static void bnx2x_warpcore_set_20G_force_KR2(struct bnx2x_phy *phy, + struct link_params *params) +{ + u16 val; + struct bnx2x *bp = params->bp; + /* Set global registers, so set AER lane to 0 */ + CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, + MDIO_AER_BLOCK_AER_REG, 0); + + /* Disable sequencer */ + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, ~(1<<13)); + + bnx2x_set_aer_mmd(params, phy); + + bnx2x_cl45_read_and_write(bp, phy, MDIO_PMA_DEVAD, + MDIO_WC_REG_PMD_KR_CONTROL, ~(1<<1)); + bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_CTRL, 0); + /* Turn off CL73 */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, &val); + MDIO_WC_REG_CL73_USERB0_CTRL, &val); + val &= ~(1<<5); + val |= (1<<6); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_SERDESDIGITAL_CONTROL1000X3, val & 0xFFFE); + MDIO_WC_REG_CL73_USERB0_CTRL, val); + + /* Set 20G KR2 force speed */ + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_SERDESDIGITAL_MISC1, 0x1f); + + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL4_MISC3, (1<<7)); - /* Release rxSeqStart */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, &val); + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, &val); + val &= ~(3<<14); + val |= (1<<15); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_DSC2B0_DSC_MISC_CTRL0, (val & 0x7FFF)); -} + MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL, val); + bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_CL72_USERB0_CL72_TX_FIR_TAP, 0x835A); -static void bnx2x_warpcore_set_20G_KR2(struct bnx2x *bp, - struct bnx2x_phy *phy) -{ - DP(NETIF_MSG_LINK, "KR2 still not supported !!!\n"); + /* Enable sequencer (over lane 0) */ + CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, + MDIO_AER_BLOCK_AER_REG, 0); + + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, (1<<13)); + + bnx2x_set_aer_mmd(params, phy); } static void bnx2x_warpcore_set_20G_DXGXS(struct bnx2x *bp, @@ -3931,20 +4119,16 @@ static void bnx2x_warpcore_set_sgmii_speed(struct bnx2x_phy *phy, u16 val16, digctrl_kx1, digctrl_kx2; /* Clear XFI clock comp in non-10G single lane mode. */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_RX66_CONTROL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_RX66_CONTROL, val16 & ~(3<<13)); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_RX66_CONTROL, ~(3<<13)); bnx2x_warpcore_set_lpi_passthrough(phy, params); if (always_autoneg || phy->req_line_speed == SPEED_AUTO_NEG) { /* SGMII Autoneg */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, - val16 | 0x1000); + bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_COMBO_IEEE0_MIICTRL, + 0x1000); DP(NETIF_MSG_LINK, "set SGMII AUTONEG\n"); } else { bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, @@ -4086,7 +4270,7 @@ static int bnx2x_get_mod_abs_int_cfg(struct bnx2x *bp, if ((cfg_pin < PIN_CFG_GPIO0_P0) || (cfg_pin > PIN_CFG_GPIO3_P1)) { DP(NETIF_MSG_LINK, - "ERROR: Invalid cfg pin %x for module detect indication\n", + "No cfg pin %x for module detect indication\n", cfg_pin); return -EINVAL; } @@ -4097,7 +4281,7 @@ static int bnx2x_get_mod_abs_int_cfg(struct bnx2x *bp, *gpio_num = MISC_REGISTERS_GPIO_3; *gpio_port = port; } - DP(NETIF_MSG_LINK, "MOD_ABS int GPIO%d_P%d\n", *gpio_num, *gpio_port); + return 0; } @@ -4120,7 +4304,7 @@ static int bnx2x_is_sfp_module_plugged(struct bnx2x_phy *phy, return 0; } static int bnx2x_warpcore_get_sigdet(struct bnx2x_phy *phy, - struct link_params *params) + struct link_params *params) { u16 gp2_status_reg0, lane; struct bnx2x *bp = params->bp; @@ -4134,8 +4318,8 @@ static int bnx2x_warpcore_get_sigdet(struct bnx2x_phy *phy, } static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy, - struct link_params *params, - struct link_vars *vars) + struct link_params *params, + struct link_vars *vars) { struct bnx2x *bp = params->bp; u32 serdes_net_if; @@ -4163,7 +4347,7 @@ static void bnx2x_warpcore_config_runtime(struct bnx2x_phy *phy, case PORT_HW_CFG_NET_SERDES_IF_KR: /* Do we get link yet? */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, 0x81d1, - &gp_status1); + &gp_status1); lnkup = (gp_status1 >> (8+lane)) & 0x1;/* 1G */ /*10G KR*/ lnkup_kr = (gp_status1 >> (12+lane)) & 0x1; @@ -4215,6 +4399,27 @@ static void bnx2x_warpcore_config_sfi(struct bnx2x_phy *phy, } } +static void bnx2x_sfp_e3_set_transmitter(struct link_params *params, + struct bnx2x_phy *phy, + u8 tx_en) +{ + struct bnx2x *bp = params->bp; + u32 cfg_pin; + u8 port = params->port; + + cfg_pin = REG_RD(bp, params->shmem_base + + offsetof(struct shmem_region, + dev_info.port_hw_config[port].e3_sfp_ctrl)) & + PORT_HW_CFG_E3_TX_LASER_MASK; + /* Set the !tx_en since this pin is DISABLE_TX_LASER */ + DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en); + + /* For 20G, the expected pin to be used is 3 pins after the current */ + bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1); + if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G) + bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1); +} + static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) @@ -4275,9 +4480,14 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, break; case PORT_HW_CFG_NET_SERDES_IF_SFI: - /* Issue Module detection */ + /* Issue Module detection if module is plugged, or + * enabled transmitter to avoid current leakage in case + * no module is connected + */ if (bnx2x_is_sfp_module_plugged(phy, params)) bnx2x_sfp_module_detection(phy, params); + else + bnx2x_sfp_e3_set_transmitter(params, phy, 1); bnx2x_warpcore_config_sfi(phy, params); break; @@ -4293,16 +4503,14 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, bnx2x_sfp_module_detection(phy, params); break; - case PORT_HW_CFG_NET_SERDES_IF_KR2: - if (vars->line_speed != SPEED_20000) { - DP(NETIF_MSG_LINK, "Speed not supported yet\n"); - return; + if (!params->loopback_mode) { + bnx2x_warpcore_enable_AN_KR(phy, params, vars); + } else { + DP(NETIF_MSG_LINK, "Setting KR 20G-Force\n"); + bnx2x_warpcore_set_20G_force_KR2(phy, params); } - DP(NETIF_MSG_LINK, "Setting 20G KR2\n"); - bnx2x_warpcore_set_20G_KR2(bp, phy); break; - default: DP(NETIF_MSG_LINK, "Unsupported Serdes Net Interface 0x%x\n", @@ -4316,68 +4524,35 @@ static void bnx2x_warpcore_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Exit config init\n"); } -static void bnx2x_sfp_e3_set_transmitter(struct link_params *params, - struct bnx2x_phy *phy, - u8 tx_en) -{ - struct bnx2x *bp = params->bp; - u32 cfg_pin; - u8 port = params->port; - - cfg_pin = REG_RD(bp, params->shmem_base + - offsetof(struct shmem_region, - dev_info.port_hw_config[port].e3_sfp_ctrl)) & - PORT_HW_CFG_TX_LASER_MASK; - /* Set the !tx_en since this pin is DISABLE_TX_LASER */ - DP(NETIF_MSG_LINK, "Setting WC TX to %d\n", tx_en); - /* For 20G, the expected pin to be used is 3 pins after the current */ - - bnx2x_set_cfg_pin(bp, cfg_pin, tx_en ^ 1); - if (phy->speed_cap_mask & PORT_HW_CFG_SPEED_CAPABILITY_D0_20G) - bnx2x_set_cfg_pin(bp, cfg_pin + 3, tx_en ^ 1); -} - static void bnx2x_warpcore_link_reset(struct bnx2x_phy *phy, struct link_params *params) { struct bnx2x *bp = params->bp; u16 val16, lane; bnx2x_sfp_e3_set_transmitter(params, phy, 0); - bnx2x_set_mdio_clk(bp, params->chip_id, params->port); + bnx2x_set_mdio_emac_per_phy(bp, params); bnx2x_set_aer_mmd(params, phy); /* Global register */ bnx2x_warpcore_reset_lane(bp, phy, 1); /* Clear loopback settings (if any) */ /* 10G & 20G */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_COMBO_IEEE0_MIICTRL, val16 & - 0xBFFF); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_COMBO_IEEE0_MIICTRL, 0xBFFF); - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_IEEE0BLK_MIICNTL, val16 & 0xfffe); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_IEEE0BLK_MIICNTL, 0xfffe); /* Update those 1-copy registers */ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, MDIO_AER_BLOCK_AER_REG, 0); /* Enable 1G MDIO (1-copy) */ - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, - &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, - val16 & ~0x10); - - bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL2, &val16); - bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL2, - val16 & 0xff00); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK0_XGXSCONTROL, + ~0x10); + bnx2x_cl45_read_and_write(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_XGXSBLK1_LANECTRL2, 0xff00); lane = bnx2x_get_warpcore_lane(phy, params); /* Disable CL36 PCS Tx */ bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, @@ -4413,8 +4588,9 @@ static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Setting Warpcore loopback type %x, speed %d\n", params->loopback_mode, phy->req_line_speed); - if (phy->req_line_speed < SPEED_10000) { - /* 10/100/1000 */ + if (phy->req_line_speed < SPEED_10000 || + phy->supported & SUPPORTED_20000baseKR2_Full) { + /* 10/100/1000/20G-KR2 */ /* Update those 1-copy registers */ CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, @@ -4427,18 +4603,20 @@ static void bnx2x_set_warpcore_loopback(struct bnx2x_phy *phy, lane = bnx2x_get_warpcore_lane(phy, params); bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_XGXSBLK1_LANECTRL2, &val16); + val16 |= (1<<lane); + if (phy->flags & FLAGS_WC_DUAL_MODE) + val16 |= (2<<lane); bnx2x_cl45_write(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_XGXSBLK1_LANECTRL2, - val16 | (1<<lane)); + MDIO_WC_REG_XGXSBLK1_LANECTRL2, + val16); /* Switch back to 4-copy registers */ bnx2x_set_aer_mmd(params, phy); } else { - /* 10G & 20G */ + /* 10G / 20G-DXGXS */ bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_COMBO_IEEE0_MIICTRL, 0x4000); - bnx2x_cl45_read_or_write(bp, phy, MDIO_WC_DEVAD, MDIO_WC_REG_IEEE0BLK_MIICNTL, 0x1); } @@ -4603,6 +4781,10 @@ void bnx2x_link_status_update(struct link_params *params, params->feature_config_flags &= ~FEATURE_CONFIG_PFC_ENABLED; + if (SHMEM2_HAS(bp, link_attr_sync)) + vars->link_attr_sync = SHMEM2_RD(bp, + link_attr_sync[params->port]); + DP(NETIF_MSG_LINK, "link_status 0x%x phy_link_up %x int_mask 0x%x\n", vars->link_status, vars->phy_link_up, vars->aeu_int_mask); DP(NETIF_MSG_LINK, "line_speed %x duplex %x flow_ctrl 0x%x\n", @@ -5332,6 +5514,7 @@ static int bnx2x_get_link_speed_duplex(struct bnx2x_phy *phy, vars->link_status |= LINK_10GTFD; break; case GP_STATUS_20G_DXGXS: + case GP_STATUS_20G_KR2: vars->line_speed = SPEED_20000; vars->link_status |= LINK_20GTFD; break; @@ -5439,7 +5622,15 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy, int rc = 0; lane = bnx2x_get_warpcore_lane(phy, params); /* Read gp_status */ - if (phy->req_line_speed > SPEED_10000) { + if ((params->loopback_mode) && + (phy->flags & FLAGS_WC_DUAL_MODE)) { + bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL5_LINK_STATUS, &link_up); + bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, + MDIO_WC_REG_DIGITAL5_LINK_STATUS, &link_up); + link_up &= 0x1; + } else if ((phy->req_line_speed > SPEED_10000) && + (phy->supported & SUPPORTED_20000baseMLD2_Full)) { u16 temp_link_up; bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, 1, &temp_link_up); @@ -5452,12 +5643,22 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy, bnx2x_ext_phy_resolve_fc(phy, params, vars); } else { bnx2x_cl45_read(bp, phy, MDIO_WC_DEVAD, - MDIO_WC_REG_GP2_STATUS_GP_2_1, &gp_status1); + MDIO_WC_REG_GP2_STATUS_GP_2_1, + &gp_status1); DP(NETIF_MSG_LINK, "0x81d1 = 0x%x\n", gp_status1); - /* Check for either KR or generic link up. */ - gp_status1 = ((gp_status1 >> 8) & 0xf) | - ((gp_status1 >> 12) & 0xf); - link_up = gp_status1 & (1 << lane); + /* Check for either KR, 1G, or AN up. */ + link_up = ((gp_status1 >> 8) | + (gp_status1 >> 12) | + (gp_status1)) & + (1 << lane); + if (phy->supported & SUPPORTED_20000baseKR2_Full) { + u16 an_link; + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_STATUS, &an_link); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_STATUS, &an_link); + link_up |= (an_link & (1<<2)); + } if (link_up && SINGLE_MEDIA_DIRECT(params)) { u16 pd, gp_status4; if (phy->req_line_speed == SPEED_AUTO_NEG) { @@ -5522,7 +5723,7 @@ static int bnx2x_warpcore_read_status(struct bnx2x_phy *phy, if ((lane & 1) == 0) gp_speed <<= 8; gp_speed &= 0x3f00; - + link_up = !!link_up; rc = bnx2x_get_link_speed_duplex(phy, params, vars, link_up, gp_speed, duplex); @@ -6683,7 +6884,7 @@ int bnx2x_link_update(struct link_params *params, struct link_vars *vars) } else if (prev_line_speed != vars->line_speed) { REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); - usleep_range(1000, 2000); + usleep_range(1000, 2000); } } @@ -6753,7 +6954,7 @@ void bnx2x_ext_phy_hw_reset(struct bnx2x *bp, u8 port) { bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); - usleep_range(1000, 2000); + usleep_range(1000, 2000); bnx2x_set_gpio(bp, MISC_REGISTERS_GPIO_1, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); } @@ -6894,7 +7095,7 @@ static int bnx2x_8073_8727_external_rom_boot(struct bnx2x *bp, MDIO_PMA_DEVAD, MDIO_PMA_REG_M8051_MSGOUT_REG, &fw_msgout); - usleep_range(1000, 2000); + usleep_range(1000, 2000); } while (fw_ver1 == 0 || fw_ver1 == 0x4321 || ((fw_msgout & 0xff) != 0x03 && (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073))); @@ -7604,13 +7805,12 @@ static int bnx2x_8726_read_sfp_module_eeprom(struct bnx2x_phy *phy, if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0; - usleep_range(1000, 2000); + usleep_range(1000, 2000); } return -EINVAL; } static void bnx2x_warpcore_power_module(struct link_params *params, - struct bnx2x_phy *phy, u8 power) { u32 pin_cfg; @@ -7652,10 +7852,10 @@ static int bnx2x_warpcore_read_sfp_module_eeprom(struct bnx2x_phy *phy, addr32 = addr & (~0x3); do { if ((!is_init) && (cnt == I2C_WA_PWR_ITER)) { - bnx2x_warpcore_power_module(params, phy, 0); + bnx2x_warpcore_power_module(params, 0); /* Note that 100us are not enough here */ usleep_range(1000, 2000); - bnx2x_warpcore_power_module(params, phy, 1); + bnx2x_warpcore_power_module(params, 1); } rc = bnx2x_bsc_read(params, phy, 0xa0, addr32, 0, byte_cnt, data_array); @@ -7715,7 +7915,7 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy, /* Wait appropriate time for two-wire command to finish before * polling the status register */ - usleep_range(1000, 2000); + usleep_range(1000, 2000); /* Wait up to 500us for command complete status */ for (i = 0; i < 100; i++) { @@ -7751,7 +7951,7 @@ static int bnx2x_8727_read_sfp_module_eeprom(struct bnx2x_phy *phy, if ((val & MDIO_PMA_REG_SFP_TWO_WIRE_CTRL_STATUS_MASK) == MDIO_PMA_REG_SFP_TWO_WIRE_STATUS_IDLE) return 0; - usleep_range(1000, 2000); + usleep_range(1000, 2000); } return -EINVAL; @@ -7786,9 +7986,8 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy, { struct bnx2x *bp = params->bp; u32 sync_offset = 0, phy_idx, media_types; - u8 val[2], check_limiting_mode = 0; + u8 gport, val[2], check_limiting_mode = 0; *edc_mode = EDC_MODE_LIMITING; - phy->media_type = ETH_PHY_UNSPECIFIED; /* First check for copper cable */ if (bnx2x_read_sfp_module_eeprom(phy, @@ -7843,8 +8042,15 @@ static int bnx2x_get_edc_mode(struct bnx2x_phy *phy, SFP_EEPROM_COMP_CODE_LR_MASK | SFP_EEPROM_COMP_CODE_LRM_MASK)) == 0) { DP(NETIF_MSG_LINK, "1G Optic module detected\n"); + gport = params->port; phy->media_type = ETH_PHY_SFP_1G_FIBER; phy->req_line_speed = SPEED_1000; + if (!CHIP_IS_E1x(bp)) + gport = BP_PATH(bp) + (params->port << 1); + netdev_err(bp->dev, "Warning: Link speed was forced to 1000Mbps." + " Current SFP module in port %d is not" + " compliant with 10G Ethernet\n", + gport); } else { int idx, cfg_idx = 0; DP(NETIF_MSG_LINK, "10G Optic module detected\n"); @@ -8241,7 +8447,7 @@ static void bnx2x_warpcore_hw_reset(struct bnx2x_phy *phy, struct link_params *params) { struct bnx2x *bp = params->bp; - bnx2x_warpcore_power_module(params, phy, 0); + bnx2x_warpcore_power_module(params, 0); /* Put Warpcore in low power mode */ REG_WR(bp, MISC_REG_WC0_RESET, 0x0c0e); @@ -8264,7 +8470,7 @@ static void bnx2x_power_sfp_module(struct link_params *params, bnx2x_8727_power_module(params->bp, phy, power); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT: - bnx2x_warpcore_power_module(params, phy, power); + bnx2x_warpcore_power_module(params, power); break; default: break; @@ -8337,7 +8543,8 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy, u32 val = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info. port_feature_config[params->port].config)); - + /* Enabled transmitter by default */ + bnx2x_sfp_set_transmitter(params, phy, 1); DP(NETIF_MSG_LINK, "SFP+ module plugged in/out detected on port %d\n", params->port); /* Power up module */ @@ -8370,14 +8577,12 @@ int bnx2x_sfp_module_detection(struct bnx2x_phy *phy, */ bnx2x_set_limiting_mode(params, phy, edc_mode); - /* Enable transmit for this module if the module is approved, or - * if unapproved modules should also enable the Tx laser + /* Disable transmit for this module if the module is not approved, and + * laser needs to be disabled. */ - if (rc == 0 || - (val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) != - PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) - bnx2x_sfp_set_transmitter(params, phy, 1); - else + if ((rc) && + ((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == + PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER)) bnx2x_sfp_set_transmitter(params, phy, 0); return rc; @@ -8389,11 +8594,13 @@ void bnx2x_handle_module_detect_int(struct link_params *params) struct bnx2x_phy *phy; u32 gpio_val; u8 gpio_num, gpio_port; - if (CHIP_IS_E3(bp)) + if (CHIP_IS_E3(bp)) { phy = ¶ms->phy[INT_PHY]; - else + /* Always enable TX laser,will be disabled in case of fault */ + bnx2x_sfp_set_transmitter(params, phy, 1); + } else { phy = ¶ms->phy[EXT_PHY1]; - + } if (bnx2x_get_mod_abs_int_cfg(bp, params->chip_id, params->shmem_base, params->port, &gpio_num, &gpio_port) == -EINVAL) { @@ -8409,7 +8616,7 @@ void bnx2x_handle_module_detect_int(struct link_params *params) /* Call the handling function in case module is detected */ if (gpio_val == 0) { - bnx2x_set_mdio_clk(bp, params->chip_id, params->port); + bnx2x_set_mdio_emac_per_phy(bp, params); bnx2x_set_aer_mmd(params, phy); bnx2x_power_sfp_module(params, phy, 1); @@ -8438,10 +8645,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params) DP(NETIF_MSG_LINK, "SFP+ module is not initialized\n"); } } else { - u32 val = REG_RD(bp, params->shmem_base + - offsetof(struct shmem_region, dev_info. - port_feature_config[params->port]. - config)); bnx2x_set_gpio_int(bp, gpio_num, MISC_REGISTERS_GPIO_INT_OUTPUT_SET, gpio_port); @@ -8449,10 +8652,6 @@ void bnx2x_handle_module_detect_int(struct link_params *params) * Disable transmit for this module */ phy->media_type = ETH_PHY_NOT_PRESENT; - if (((val & PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_MASK) == - PORT_FEAT_CFG_OPT_MDL_ENFRCMNT_DISABLE_TX_LASER) || - CHIP_IS_E3(bp)) - bnx2x_sfp_set_transmitter(params, phy, 0); } } @@ -9192,6 +9391,7 @@ static u8 bnx2x_8727_read_status(struct bnx2x_phy *phy, bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, MDIO_PMA_LASI_RXSTAT, &rx_alarm_status); + bnx2x_8727_power_module(params->bp, phy, 0); return 0; } } /* Over current check */ @@ -9296,20 +9496,28 @@ static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy, struct bnx2x *bp, u8 port) { - u16 val, fw_ver1, fw_ver2, cnt; + u16 val, fw_ver2, cnt, i; + static struct bnx2x_reg_set reg_set[] = { + {MDIO_PMA_DEVAD, 0xA819, 0x0014}, + {MDIO_PMA_DEVAD, 0xA81A, 0xc200}, + {MDIO_PMA_DEVAD, 0xA81B, 0x0000}, + {MDIO_PMA_DEVAD, 0xA81C, 0x0300}, + {MDIO_PMA_DEVAD, 0xA817, 0x0009} + }; + u16 fw_ver1; - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || + (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) { bnx2x_cl45_read(bp, phy, MDIO_CTL_DEVAD, 0x400f, &fw_ver1); bnx2x_save_spirom_version(bp, port, fw_ver1 & 0xfff, phy->ver_addr); } else { /* For 32-bit registers in 848xx, access via MDIO2ARM i/f. */ /* (1) set reg 0xc200_0014(SPI_BRIDGE_CTRL_2) to 0x03000000 */ - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA819, 0x0014); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81A, 0xc200); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81B, 0x0000); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA81C, 0x0300); - bnx2x_cl45_write(bp, phy, MDIO_PMA_DEVAD, 0xA817, 0x0009); + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); + i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, + reg_set[i].reg, reg_set[i].val); for (cnt = 0; cnt < 100; cnt++) { bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, 0xA818, &val); @@ -9357,8 +9565,16 @@ static void bnx2x_save_848xx_spirom_version(struct bnx2x_phy *phy, static void bnx2x_848xx_set_led(struct bnx2x *bp, struct bnx2x_phy *phy) { - u16 val, offset; - + u16 val, offset, i; + static struct bnx2x_reg_set reg_set[] = { + {MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED1_MASK, 0x0080}, + {MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED2_MASK, 0x0018}, + {MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED3_MASK, 0x0006}, + {MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LED3_BLINK, 0x0000}, + {MDIO_PMA_DEVAD, MDIO_PMA_REG_84823_CTL_SLOW_CLK_CNT_HIGH, + MDIO_PMA_REG_84823_BLINK_RATE_VAL_15P9HZ}, + {MDIO_AN_DEVAD, 0xFFFB, 0xFFFD} + }; /* PHYC_CTL_LED_CTL */ bnx2x_cl45_read(bp, phy, MDIO_PMA_DEVAD, @@ -9370,49 +9586,20 @@ static void bnx2x_848xx_set_led(struct bnx2x *bp, MDIO_PMA_DEVAD, MDIO_PMA_REG_8481_LINK_SIGNAL, val); - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LED1_MASK, - 0x80); - - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LED2_MASK, - 0x18); - - /* Select activity source by Tx and Rx, as suggested by PHY AE */ - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LED3_MASK, - 0x0006); - - /* Select the closest activity blink rate to that in 10/100/1000 */ - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_8481_LED3_BLINK, - 0); - - /* Configure the blink rate to ~15.9 Hz */ - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, - MDIO_PMA_REG_84823_CTL_SLOW_CLK_CNT_HIGH, - MDIO_PMA_REG_84823_BLINK_RATE_VAL_15P9HZ); + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg, + reg_set[i].val); - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) + if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || + (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) offset = MDIO_PMA_REG_84833_CTL_LED_CTL_1; else offset = MDIO_PMA_REG_84823_CTL_LED_CTL_1; - bnx2x_cl45_read(bp, phy, - MDIO_PMA_DEVAD, offset, &val); - val |= MDIO_PMA_REG_84823_LED3_STRETCH_EN; /* stretch_en for LED3*/ - bnx2x_cl45_write(bp, phy, - MDIO_PMA_DEVAD, offset, val); - - /* 'Interrupt Mask' */ - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, - 0xFFFB, 0xFFFD); + /* stretch_en for LED3*/ + bnx2x_cl45_read_or_write(bp, phy, + MDIO_PMA_DEVAD, offset, + MDIO_PMA_REG_84823_LED3_STRETCH_EN); } static void bnx2x_848xx_specific_func(struct bnx2x_phy *phy, @@ -9422,7 +9609,8 @@ static void bnx2x_848xx_specific_func(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; switch (action) { case PHY_INIT: - if (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) && + (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) { /* Save spirom version */ bnx2x_save_848xx_spirom_version(phy, bp, params->port); } @@ -9443,7 +9631,7 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, struct link_vars *vars) { struct bnx2x *bp = params->bp; - u16 autoneg_val, an_1000_val, an_10_100_val, an_10g_val; + u16 autoneg_val, an_1000_val, an_10_100_val; bnx2x_848xx_specific_func(phy, params, PHY_INIT); bnx2x_cl45_write(bp, phy, @@ -9542,11 +9730,12 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, if (phy->req_duplex == DUPLEX_FULL) autoneg_val |= (1<<8); - /* Always write this if this is not 84833. - * For 84833, write it only when it's a forced speed. + /* Always write this if this is not 84833/4. + * For 84833/4, write it only when it's a forced speed. */ - if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || - ((autoneg_val & (1<<12)) == 0)) + if (((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) && + (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) || + ((autoneg_val & (1<<12)) == 0)) bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_8481_LEGACY_MII_CTRL, autoneg_val); @@ -9558,14 +9747,11 @@ static int bnx2x_848xx_cmn_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Advertising 10G\n"); /* Restart autoneg for 10G*/ - bnx2x_cl45_read(bp, phy, - MDIO_AN_DEVAD, - MDIO_AN_REG_8481_10GBASE_T_AN_CTRL, - &an_10g_val); - bnx2x_cl45_write(bp, phy, - MDIO_AN_DEVAD, - MDIO_AN_REG_8481_10GBASE_T_AN_CTRL, - an_10g_val | 0x1000); + bnx2x_cl45_read_or_write( + bp, phy, + MDIO_AN_DEVAD, + MDIO_AN_REG_8481_10GBASE_T_AN_CTRL, + 0x1000); bnx2x_cl45_write(bp, phy, MDIO_AN_DEVAD, MDIO_AN_REG_CTRL, 0x3200); @@ -9598,9 +9784,8 @@ static int bnx2x_8481_config_init(struct bnx2x_phy *phy, #define PHY84833_CMDHDLR_WAIT 300 #define PHY84833_CMDHDLR_MAX_ARGS 5 static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, - struct link_params *params, - u16 fw_cmd, - u16 cmd_args[], int argc) + struct link_params *params, u16 fw_cmd, + u16 cmd_args[], int argc) { int idx; u16 val; @@ -9614,7 +9799,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, MDIO_84833_CMD_HDLR_STATUS, &val); if (val == PHY84833_STATUS_CMD_OPEN_FOR_CMDS) break; - usleep_range(1000, 2000); + usleep_range(1000, 2000); } if (idx >= PHY84833_CMDHDLR_WAIT) { DP(NETIF_MSG_LINK, "FW cmd: FW not ready.\n"); @@ -9635,7 +9820,7 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, if ((val == PHY84833_STATUS_CMD_COMPLETE_PASS) || (val == PHY84833_STATUS_CMD_COMPLETE_ERROR)) break; - usleep_range(1000, 2000); + usleep_range(1000, 2000); } if ((idx >= PHY84833_CMDHDLR_WAIT) || (val == PHY84833_STATUS_CMD_COMPLETE_ERROR)) { @@ -9654,7 +9839,6 @@ static int bnx2x_84833_cmd_hdlr(struct bnx2x_phy *phy, return 0; } - static int bnx2x_84833_pair_swap_cfg(struct bnx2x_phy *phy, struct link_params *params, struct link_vars *vars) @@ -9802,11 +9986,11 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, struct bnx2x *bp = params->bp; u8 port, initialize = 1; u16 val; - u32 actual_phy_selection, cms_enable; + u32 actual_phy_selection; u16 cmd_args[PHY84833_CMDHDLR_MAX_ARGS]; int rc = 0; - usleep_range(1000, 2000); + usleep_range(1000, 2000); if (!(CHIP_IS_E1x(bp))) port = BP_PATH(bp); @@ -9828,7 +10012,8 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, /* Wait for GPHY to come out of reset */ msleep(50); - if (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + if ((phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) && + (phy->type != PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) { /* BCM84823 requires that XGXS links up first @ 10G for normal * behavior. */ @@ -9884,7 +10069,8 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, DP(NETIF_MSG_LINK, "Multi_phy config = 0x%x, Media control = 0x%x\n", params->multi_phy_config, val); - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || + (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) { bnx2x_84833_pair_swap_cfg(phy, params, vars); /* Keep AutogrEEEn disabled. */ @@ -9904,7 +10090,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, bnx2x_save_848xx_spirom_version(phy, bp, params->port); /* 84833 PHY has a better feature and doesn't need to support this. */ if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823) { - cms_enable = REG_RD(bp, params->shmem_base + + u32 cms_enable = REG_RD(bp, params->shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[params->port].default_cfg)) & PORT_HW_CFG_ENABLE_CMS_MASK; @@ -9933,7 +10119,7 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, return rc; } - if ((params->req_duplex[actual_phy_selection] == DUPLEX_FULL) && + if ((phy->req_duplex == DUPLEX_FULL) && (params->eee_mode & EEE_MODE_ADV_LPI) && (bnx2x_eee_calc_timer(params) || !(params->eee_mode & EEE_MODE_ENABLE_LPI))) @@ -9948,15 +10134,13 @@ static int bnx2x_848x3_config_init(struct bnx2x_phy *phy, vars->eee_status &= ~SHMEM_EEE_SUPPORTED_MASK; } - if (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) { + if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || + (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) { /* Bring PHY out of super isolate mode as the final step. */ - bnx2x_cl45_read(bp, phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, &val); - val &= ~MDIO_84833_SUPER_ISOLATE; - bnx2x_cl45_write(bp, phy, - MDIO_CTL_DEVAD, - MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); + bnx2x_cl45_read_and_write(bp, phy, + MDIO_CTL_DEVAD, + MDIO_84833_TOP_CFG_XGPHY_STRAP1, + (u16)~MDIO_84833_SUPER_ISOLATE); } return rc; } @@ -10090,7 +10274,6 @@ static u8 bnx2x_848xx_read_status(struct bnx2x_phy *phy, return link_up; } - static int bnx2x_848xx_format_ver(u32 raw_ver, u8 *str, u16 *len) { int status = 0; @@ -10962,7 +11145,7 @@ static void bnx2x_7101_set_link_led(struct bnx2x_phy *phy, /* STATIC PHY DECLARATION */ /******************************************************************/ -static struct bnx2x_phy phy_null = { +static const struct bnx2x_phy phy_null = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_NOT_CONN, .addr = 0, .def_md_devad = 0, @@ -10988,7 +11171,7 @@ static struct bnx2x_phy phy_null = { .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_serdes = { +static const struct bnx2x_phy phy_serdes = { .type = PORT_HW_CFG_SERDES_EXT_PHY_TYPE_DIRECT, .addr = 0xff, .def_md_devad = 0, @@ -11023,7 +11206,7 @@ static struct bnx2x_phy phy_serdes = { .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_xgxs = { +static const struct bnx2x_phy phy_xgxs = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT, .addr = 0xff, .def_md_devad = 0, @@ -11058,12 +11241,11 @@ static struct bnx2x_phy phy_xgxs = { .set_link_led = (set_link_led_t)NULL, .phy_specific_func = (phy_specific_func_t)bnx2x_xgxs_specific_func }; -static struct bnx2x_phy phy_warpcore = { +static const struct bnx2x_phy phy_warpcore = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_DIRECT, .addr = 0xff, .def_md_devad = 0, - .flags = (FLAGS_HW_LOCK_REQUIRED | - FLAGS_TX_ERROR_CHECK), + .flags = FLAGS_TX_ERROR_CHECK, .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .mdio_ctrl = 0, @@ -11097,7 +11279,7 @@ static struct bnx2x_phy phy_warpcore = { }; -static struct bnx2x_phy phy_7101 = { +static const struct bnx2x_phy phy_7101 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_SFX7101, .addr = 0xff, .def_md_devad = 0, @@ -11126,11 +11308,11 @@ static struct bnx2x_phy phy_7101 = { .set_link_led = (set_link_led_t)bnx2x_7101_set_link_led, .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_8073 = { +static const struct bnx2x_phy phy_8073 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8073, .addr = 0xff, .def_md_devad = 0, - .flags = FLAGS_HW_LOCK_REQUIRED, + .flags = 0, .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .mdio_ctrl = 0, @@ -11157,7 +11339,7 @@ static struct bnx2x_phy phy_8073 = { .set_link_led = (set_link_led_t)NULL, .phy_specific_func = (phy_specific_func_t)bnx2x_8073_specific_func }; -static struct bnx2x_phy phy_8705 = { +static const struct bnx2x_phy phy_8705 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8705, .addr = 0xff, .def_md_devad = 0, @@ -11185,7 +11367,7 @@ static struct bnx2x_phy phy_8705 = { .set_link_led = (set_link_led_t)NULL, .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_8706 = { +static const struct bnx2x_phy phy_8706 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8706, .addr = 0xff, .def_md_devad = 0, @@ -11215,12 +11397,11 @@ static struct bnx2x_phy phy_8706 = { .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_8726 = { +static const struct bnx2x_phy phy_8726 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8726, .addr = 0xff, .def_md_devad = 0, - .flags = (FLAGS_HW_LOCK_REQUIRED | - FLAGS_INIT_XGXS_FIRST | + .flags = (FLAGS_INIT_XGXS_FIRST | FLAGS_TX_ERROR_CHECK), .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, @@ -11248,7 +11429,7 @@ static struct bnx2x_phy phy_8726 = { .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_8727 = { +static const struct bnx2x_phy phy_8727 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8727, .addr = 0xff, .def_md_devad = 0, @@ -11278,7 +11459,7 @@ static struct bnx2x_phy phy_8727 = { .set_link_led = (set_link_led_t)bnx2x_8727_set_link_led, .phy_specific_func = (phy_specific_func_t)bnx2x_8727_specific_func }; -static struct bnx2x_phy phy_8481 = { +static const struct bnx2x_phy phy_8481 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM8481, .addr = 0xff, .def_md_devad = 0, @@ -11314,7 +11495,7 @@ static struct bnx2x_phy phy_8481 = { .phy_specific_func = (phy_specific_func_t)NULL }; -static struct bnx2x_phy phy_84823 = { +static const struct bnx2x_phy phy_84823 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84823, .addr = 0xff, .def_md_devad = 0, @@ -11351,7 +11532,7 @@ static struct bnx2x_phy phy_84823 = { .phy_specific_func = (phy_specific_func_t)bnx2x_848xx_specific_func }; -static struct bnx2x_phy phy_84833 = { +static const struct bnx2x_phy phy_84833 = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833, .addr = 0xff, .def_md_devad = 0, @@ -11386,7 +11567,41 @@ static struct bnx2x_phy phy_84833 = { .phy_specific_func = (phy_specific_func_t)bnx2x_848xx_specific_func }; -static struct bnx2x_phy phy_54618se = { +static const struct bnx2x_phy phy_84834 = { + .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834, + .addr = 0xff, + .def_md_devad = 0, + .flags = FLAGS_FAN_FAILURE_DET_REQ | + FLAGS_REARM_LATCH_SIGNAL, + .rx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, + .tx_preemphasis = {0xffff, 0xffff, 0xffff, 0xffff}, + .mdio_ctrl = 0, + .supported = (SUPPORTED_100baseT_Half | + SUPPORTED_100baseT_Full | + SUPPORTED_1000baseT_Full | + SUPPORTED_10000baseT_Full | + SUPPORTED_TP | + SUPPORTED_Autoneg | + SUPPORTED_Pause | + SUPPORTED_Asym_Pause), + .media_type = ETH_PHY_BASE_T, + .ver_addr = 0, + .req_flow_ctrl = 0, + .req_line_speed = 0, + .speed_cap_mask = 0, + .req_duplex = 0, + .rsrv = 0, + .config_init = (config_init_t)bnx2x_848x3_config_init, + .read_status = (read_status_t)bnx2x_848xx_read_status, + .link_reset = (link_reset_t)bnx2x_848x3_link_reset, + .config_loopback = (config_loopback_t)NULL, + .format_fw_ver = (format_fw_ver_t)bnx2x_848xx_format_ver, + .hw_reset = (hw_reset_t)bnx2x_84833_hw_reset_phy, + .set_link_led = (set_link_led_t)bnx2x_848xx_set_link_led, + .phy_specific_func = (phy_specific_func_t)bnx2x_848xx_specific_func +}; + +static const struct bnx2x_phy phy_54618se = { .type = PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE, .addr = 0xff, .def_md_devad = 0, @@ -11564,9 +11779,11 @@ static int bnx2x_populate_int_phy(struct bnx2x *bp, u32 shmem_base, u8 port, phy->media_type = ETH_PHY_KR; phy->flags |= FLAGS_WC_DUAL_MODE; phy->supported &= (SUPPORTED_20000baseKR2_Full | + SUPPORTED_Autoneg | SUPPORTED_FIBRE | SUPPORTED_Pause | SUPPORTED_Asym_Pause); + phy->flags &= ~FLAGS_TX_ERROR_CHECK; break; default: DP(NETIF_MSG_LINK, "Unknown WC interface type 0x%x\n", @@ -11665,6 +11882,9 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp, case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833: *phy = phy_84833; break; + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834: + *phy = phy_84834; + break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54616: case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM54618SE: *phy = phy_54618se; @@ -11721,9 +11941,10 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp, } phy->mdio_ctrl = bnx2x_get_emac_base(bp, mdc_mdio_access, port); - if ((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) && + if (((phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833) || + (phy->type == PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834)) && (phy->ver_addr)) { - /* Remove 100Mb link supported for BCM84833 when phy fw + /* Remove 100Mb link supported for BCM84833/4 when phy fw * version lower than or equal to 1.39 */ u32 raw_ver = REG_RD(bp, phy->ver_addr); @@ -11733,12 +11954,6 @@ static int bnx2x_populate_ext_phy(struct bnx2x *bp, SUPPORTED_100baseT_Full); } - /* In case mdc/mdio_access of the external phy is different than the - * mdc/mdio access of the XGXS, a HW lock must be taken in each access - * to prevent one port interfere with another port's CL45 operations. - */ - if (mdc_mdio_access != SHARED_HW_CFG_MDC_MDIO_ACCESS1_BOTH) - phy->flags |= FLAGS_HW_LOCK_REQUIRED; DP(NETIF_MSG_LINK, "phy_type 0x%x port %d found in index %d\n", phy_type, port, phy_index); DP(NETIF_MSG_LINK, " addr=0x%x, mdio_ctl=0x%x\n", @@ -11863,7 +12078,6 @@ u32 bnx2x_phy_selection(struct link_params *params) return return_cfg; } - int bnx2x_phy_probe(struct link_params *params) { u8 phy_index, actual_phy_idx; @@ -11907,6 +12121,10 @@ int bnx2x_phy_probe(struct link_params *params) FEATURE_CONFIG_DISABLE_REMOTE_FAULT_DET) phy->flags &= ~FLAGS_TX_ERROR_CHECK; + if (!(params->feature_config_flags & + FEATURE_CONFIG_MT_SUPPORT)) + phy->flags |= FLAGS_MDC_MDIO_WA_G; + sync_offset = params->shmem_base + offsetof(struct shmem_region, dev_info.port_hw_config[params->port].media_type); @@ -12018,13 +12236,17 @@ static void bnx2x_init_xgxs_loopback(struct link_params *params, struct link_vars *vars) { struct bnx2x *bp = params->bp; - vars->link_up = 1; - vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; - vars->duplex = DUPLEX_FULL; + struct bnx2x_phy *int_phy = ¶ms->phy[INT_PHY]; + vars->link_up = 1; + vars->flow_ctrl = BNX2X_FLOW_CTRL_NONE; + vars->duplex = DUPLEX_FULL; if (params->req_line_speed[0] == SPEED_1000) - vars->line_speed = SPEED_1000; + vars->line_speed = SPEED_1000; + else if ((params->req_line_speed[0] == SPEED_20000) || + (int_phy->flags & FLAGS_WC_DUAL_MODE)) + vars->line_speed = SPEED_20000; else - vars->line_speed = SPEED_10000; + vars->line_speed = SPEED_10000; if (!USES_WARPCORE(bp)) bnx2x_xgxs_deassert(params); @@ -12044,24 +12266,20 @@ static void bnx2x_init_xgxs_loopback(struct link_params *params, bnx2x_bmac_enable(params, vars, 0, 1); } - if (params->loopback_mode == LOOPBACK_XGXS) { - /* set 10G XGXS loopback */ - params->phy[INT_PHY].config_loopback( - ¶ms->phy[INT_PHY], - params); - - } else { - /* set external phy loopback */ - u8 phy_index; - for (phy_index = EXT_PHY1; - phy_index < params->num_phys; phy_index++) { - if (params->phy[phy_index].config_loopback) - params->phy[phy_index].config_loopback( - ¶ms->phy[phy_index], - params); - } - } - REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); + if (params->loopback_mode == LOOPBACK_XGXS) { + /* Set 10G XGXS loopback */ + int_phy->config_loopback(int_phy, params); + } else { + /* Set external phy loopback */ + u8 phy_index; + for (phy_index = EXT_PHY1; + phy_index < params->num_phys; phy_index++) + if (params->phy[phy_index].config_loopback) + params->phy[phy_index].config_loopback( + ¶ms->phy[phy_index], + params); + } + REG_WR(bp, NIG_REG_EGRESS_DRAIN0_MODE + params->port*4, 0); bnx2x_set_led(params, vars, LED_MODE_OPER, vars->line_speed); } @@ -12071,7 +12289,7 @@ void bnx2x_set_rx_filter(struct link_params *params, u8 en) struct bnx2x *bp = params->bp; u8 val = en * 0x1F; - /* Open the gate between the NIG to the BRB */ + /* Open / close the gate between the NIG and the BRB */ if (!CHIP_IS_E1x(bp)) val |= en * 0x20; REG_WR(bp, NIG_REG_LLH0_BRB1_DRV_MASK + params->port*4, val); @@ -12345,7 +12563,7 @@ int bnx2x_link_reset(struct link_params *params, struct link_vars *vars, * Hold it as vars low */ /* Clear link led */ - bnx2x_set_mdio_clk(bp, params->chip_id, port); + bnx2x_set_mdio_emac_per_phy(bp, params); bnx2x_set_led(params, vars, LED_MODE_OFF, 0); if (reset_ext_phy) { @@ -12696,7 +12914,7 @@ static int bnx2x_8727_common_init_phy(struct bnx2x *bp, /* Initiate PHY reset*/ bnx2x_set_gpio(bp, reset_gpio, MISC_REGISTERS_GPIO_OUTPUT_LOW, port); - usleep_range(1000, 2000); + usleep_range(1000, 2000); bnx2x_set_gpio(bp, reset_gpio, MISC_REGISTERS_GPIO_OUTPUT_HIGH, port); @@ -12784,7 +13002,8 @@ static int bnx2x_84833_common_init_phy(struct bnx2x *bp, } static int bnx2x_84833_pre_init_phy(struct bnx2x *bp, - struct bnx2x_phy *phy) + struct bnx2x_phy *phy, + u8 port) { u16 val, cnt; /* Wait for FW completing its initialization. */ @@ -12794,7 +13013,7 @@ static int bnx2x_84833_pre_init_phy(struct bnx2x *bp, MDIO_PMA_REG_CTRL, &val); if (!(val & (1<<15))) break; - usleep_range(1000, 2000); + usleep_range(1000, 2000); } if (cnt >= 1500) { DP(NETIF_MSG_LINK, "84833 reset timeout\n"); @@ -12811,26 +13030,28 @@ static int bnx2x_84833_pre_init_phy(struct bnx2x *bp, MDIO_84833_TOP_CFG_XGPHY_STRAP1, val); /* Save spirom version */ - bnx2x_save_848xx_spirom_version(phy, bp, PORT_0); + bnx2x_save_848xx_spirom_version(phy, bp, port); return 0; } int bnx2x_pre_init_phy(struct bnx2x *bp, u32 shmem_base, u32 shmem2_base, - u32 chip_id) + u32 chip_id, + u8 port) { int rc = 0; struct bnx2x_phy phy; - bnx2x_set_mdio_clk(bp, chip_id, PORT_0); if (bnx2x_populate_phy(bp, EXT_PHY1, shmem_base, shmem2_base, - PORT_0, &phy)) { + port, &phy) != 0) { DP(NETIF_MSG_LINK, "populate_phy failed\n"); return -EINVAL; } + bnx2x_set_mdio_clk(bp, chip_id, phy.mdio_ctrl); switch (phy.type) { case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833: - rc = bnx2x_84833_pre_init_phy(bp, &phy); + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834: + rc = bnx2x_84833_pre_init_phy(bp, &phy, port); break; default: break; @@ -12867,6 +13088,7 @@ static int bnx2x_ext_phy_common_init(struct bnx2x *bp, u32 shmem_base_path[], phy_index, chip_id); break; case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84833: + case PORT_HW_CFG_XGXS_EXT_PHY_TYPE_BCM84834: /* GPIO3's are linked, and so both need to be toggled * to obtain required 2us pulse. */ @@ -12898,8 +13120,9 @@ int bnx2x_common_init_phy(struct bnx2x *bp, u32 shmem_base_path[], u32 phy_ver, val; u8 phy_index = 0; u32 ext_phy_type, ext_phy_config; - bnx2x_set_mdio_clk(bp, chip_id, PORT_0); - bnx2x_set_mdio_clk(bp, chip_id, PORT_1); + + bnx2x_set_mdio_clk(bp, chip_id, GRCBASE_EMAC0); + bnx2x_set_mdio_clk(bp, chip_id, GRCBASE_EMAC1); DP(NETIF_MSG_LINK, "Begin common phy init\n"); if (CHIP_IS_E3(bp)) { /* Enable EPIO */ @@ -12960,6 +13183,7 @@ static void bnx2x_check_over_curr(struct link_params *params, " error.\n", params->port); vars->phy_flags |= PHY_OVER_CURRENT_FLAG; + bnx2x_warpcore_power_module(params, 0); } } else vars->phy_flags &= ~PHY_OVER_CURRENT_FLAG; @@ -13139,6 +13363,108 @@ static void bnx2x_sfp_tx_fault_detection(struct bnx2x_phy *phy, } } } +static void bnx2x_disable_kr2(struct link_params *params, + struct link_vars *vars, + struct bnx2x_phy *phy) +{ + struct bnx2x *bp = params->bp; + int i; + static struct bnx2x_reg_set reg_set[] = { + /* Step 1 - Program the TX/RX alignment markers */ + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL5, 0x7690}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL7, 0xe647}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL6, 0xc4f0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_TX_CTRL9, 0x7690}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL11, 0xe647}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL82_USERB1_RX_CTRL10, 0xc4f0}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_USERB0_CTRL, 0x000c}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL1, 0x6000}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CTRL3, 0x0000}, + {MDIO_WC_DEVAD, MDIO_WC_REG_CL73_BAM_CODE_FIELD, 0x0002}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI1, 0x0000}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI2, 0x0af7}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_OUI3, 0x0af7}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_BAM_CODE, 0x0002}, + {MDIO_WC_DEVAD, MDIO_WC_REG_ETA_CL73_LD_UD_CODE, 0x0000} + }; + DP(NETIF_MSG_LINK, "Disabling 20G-KR2\n"); + + for (i = 0; i < sizeof(reg_set)/sizeof(struct bnx2x_reg_set); i++) + bnx2x_cl45_write(bp, phy, reg_set[i].devad, reg_set[i].reg, + reg_set[i].val); + vars->link_attr_sync &= ~LINK_ATTR_SYNC_KR2_ENABLE; + bnx2x_update_link_attr(params, vars->link_attr_sync); + + /* Restart AN on leading lane */ + bnx2x_warpcore_restart_AN_KR(phy, params); +} + +static void bnx2x_kr2_recovery(struct link_params *params, + struct link_vars *vars, + struct bnx2x_phy *phy) +{ + struct bnx2x *bp = params->bp; + DP(NETIF_MSG_LINK, "KR2 recovery\n"); + bnx2x_warpcore_enable_AN_KR2(phy, params, vars); + bnx2x_warpcore_restart_AN_KR(phy, params); +} + +static void bnx2x_check_kr2_wa(struct link_params *params, + struct link_vars *vars, + struct bnx2x_phy *phy) +{ + struct bnx2x *bp = params->bp; + u16 base_page, next_page, not_kr2_device, lane; + int sigdet = bnx2x_warpcore_get_sigdet(phy, params); + + if (!sigdet) { + if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE)) + bnx2x_kr2_recovery(params, vars, phy); + return; + } + + lane = bnx2x_get_warpcore_lane(phy, params); + CL22_WR_OVER_CL45(bp, phy, MDIO_REG_BANK_AER_BLOCK, + MDIO_AER_BLOCK_AER_REG, lane); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_LP_AUTO_NEG, &base_page); + bnx2x_cl45_read(bp, phy, MDIO_AN_DEVAD, + MDIO_AN_REG_LP_AUTO_NEG2, &next_page); + bnx2x_set_aer_mmd(params, phy); + + /* CL73 has not begun yet */ + if (base_page == 0) { + if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE)) + bnx2x_kr2_recovery(params, vars, phy); + return; + } + + /* In case NP bit is not set in the BasePage, or it is set, + * but only KX is advertised, declare this link partner as non-KR2 + * device. + */ + not_kr2_device = (((base_page & 0x8000) == 0) || + (((base_page & 0x8000) && + ((next_page & 0xe0) == 0x2)))); + + /* In case KR2 is already disabled, check if we need to re-enable it */ + if (!(vars->link_attr_sync & LINK_ATTR_SYNC_KR2_ENABLE)) { + if (!not_kr2_device) { + DP(NETIF_MSG_LINK, "BP=0x%x, NP=0x%x\n", base_page, + next_page); + bnx2x_kr2_recovery(params, vars, phy); + } + return; + } + /* KR2 is enabled, but not KR2 device */ + if (not_kr2_device) { + /* Disable KR2 on both lanes */ + DP(NETIF_MSG_LINK, "BP=0x%x, NP=0x%x\n", base_page, next_page); + bnx2x_disable_kr2(params, vars, phy); + return; + } +} + void bnx2x_period_func(struct link_params *params, struct link_vars *vars) { u16 phy_idx; @@ -13156,6 +13482,9 @@ void bnx2x_period_func(struct link_params *params, struct link_vars *vars) if (CHIP_IS_E3(bp)) { struct bnx2x_phy *phy = ¶ms->phy[INT_PHY]; bnx2x_set_aer_mmd(params, phy); + if ((phy->supported & SUPPORTED_20000baseKR2_Full) && + (phy->speed_cap_mask & SPEED_20000)) + bnx2x_check_kr2_wa(params, vars, phy); bnx2x_check_over_curr(params, vars); if (vars->rx_tx_asic_rst) bnx2x_warpcore_config_runtime(phy, params, vars); @@ -13176,27 +13505,7 @@ void bnx2x_period_func(struct link_params *params, struct link_vars *vars) bnx2x_update_mng(params, vars->link_status); } } - } - -} - -u8 bnx2x_hw_lock_required(struct bnx2x *bp, u32 shmem_base, u32 shmem2_base) -{ - u8 phy_index; - struct bnx2x_phy phy; - for (phy_index = INT_PHY; phy_index < MAX_PHYS; - phy_index++) { - if (bnx2x_populate_phy(bp, phy_index, shmem_base, shmem2_base, - 0, &phy) != 0) { - DP(NETIF_MSG_LINK, "populate phy failed\n"); - return 0; - } - - if (phy.flags & FLAGS_HW_LOCK_REQUIRED) - return 1; - } - return 0; } u8 bnx2x_fan_failure_det_req(struct bnx2x *bp, diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h index ba981ced628..181c5ce490b 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_link.h @@ -139,8 +139,6 @@ struct bnx2x_phy { u8 addr; u8 def_md_devad; u16 flags; - /* Require HW lock */ -#define FLAGS_HW_LOCK_REQUIRED (1<<0) /* No Over-Current detection */ #define FLAGS_NOC (1<<1) /* Fan failure detection required */ @@ -156,6 +154,7 @@ struct bnx2x_phy { #define FLAGS_MDC_MDIO_WA_B0 (1<<10) #define FLAGS_TX_ERROR_CHECK (1<<12) #define FLAGS_EEE (1<<13) +#define FLAGS_MDC_MDIO_WA_G (1<<15) /* preemphasis values for the rx side */ u16 rx_preemphasis[4]; @@ -267,6 +266,8 @@ struct link_params { #define FEATURE_CONFIG_AUTOGREEEN_ENABLED (1<<9) #define FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED (1<<10) #define FEATURE_CONFIG_DISABLE_REMOTE_FAULT_DET (1<<11) +#define FEATURE_CONFIG_MT_SUPPORT (1<<13) + /* Will be populated during common init */ struct bnx2x_phy phy[MAX_PHYS]; @@ -347,6 +348,8 @@ struct link_vars { u8 rx_tx_asic_rst; u8 turn_to_run_wc_rt; u16 rsrv2; + /* The same definitions as the shmem2 parameter */ + u32 link_attr_sync; }; /***********************************************************/ @@ -418,10 +421,6 @@ int bnx2x_read_sfp_module_eeprom(struct bnx2x_phy *phy, void bnx2x_hw_reset_phy(struct link_params *params); -/* Checks if HW lock is required for this phy/board type */ -u8 bnx2x_hw_lock_required(struct bnx2x *bp, u32 shmem_base, - u32 shmem2_base); - /* Check swap bit and adjust PHY order */ u32 bnx2x_phy_selection(struct link_params *params); @@ -460,9 +459,6 @@ struct bnx2x_nig_brb_pfc_port_params { u32 rx_cos_priority_mask[DCBX_MAX_NUM_COS]; u32 llfc_high_priority_classes; u32 llfc_low_priority_classes; - /* BRB */ - u32 cos0_pauseable; - u32 cos1_pauseable; }; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 54b8c1f19d3..b4659c4b6fb 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -6267,6 +6267,10 @@ void bnx2x_pf_disable(struct bnx2x *bp) static void bnx2x__common_init_phy(struct bnx2x *bp) { u32 shmem_base[2], shmem2_base[2]; + /* Avoid common init in case MFW supports LFA */ + if (SHMEM2_RD(bp, size) > + (u32)offsetof(struct shmem2_region, lfa_host_addr[BP_PORT(bp)])) + return; shmem_base[0] = bp->common.shmem_base; shmem2_base[0] = bp->common.shmem2_base; if (!CHIP_IS_E1x(bp)) { @@ -9862,6 +9866,14 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) bp->link_params.shmem_base = bp->common.shmem_base; bp->link_params.shmem2_base = bp->common.shmem2_base; + if (SHMEM2_RD(bp, size) > + (u32)offsetof(struct shmem2_region, lfa_host_addr[BP_PORT(bp)])) + bp->link_params.lfa_base = + REG_RD(bp, bp->common.shmem2_base + + (u32)offsetof(struct shmem2_region, + lfa_host_addr[BP_PORT(bp)])); + else + bp->link_params.lfa_base = 0; BNX2X_DEV_INFO("shmem offset 0x%x shmem2 offset 0x%x\n", bp->common.shmem_base, bp->common.shmem2_base); @@ -9909,6 +9921,11 @@ static void __devinit bnx2x_get_common_hwinfo(struct bnx2x *bp) bp->link_params.feature_config_flags |= (val >= REQ_BC_VER_4_SFP_TX_DISABLE_SUPPORTED) ? FEATURE_CONFIG_BC_SUPPORTS_SFP_TX_DISABLED : 0; + + bp->link_params.feature_config_flags |= + (val >= REQ_BC_VER_4_MT_SUPPORTED) ? + FEATURE_CONFIG_MT_SUPPORT : 0; + bp->flags |= (val >= REQ_BC_VER_4_PFC_STATS_SUPPORTED) ? BC_SUPPORTS_PFC_STATS : 0; @@ -10360,17 +10377,6 @@ static void __devinit bnx2x_get_port_hwinfo(struct bnx2x *bp) bp->mdio.prtad = XGXS_EXT_PHY_ADDR(ext_phy_config); - /* - * Check if hw lock is required to access MDC/MDIO bus to the PHY(s) - * In MF mode, it is set to cover self test cases - */ - if (IS_MF(bp)) - bp->port.need_hw_lock = 1; - else - bp->port.need_hw_lock = bnx2x_hw_lock_required(bp, - bp->common.shmem_base, - bp->common.shmem2_base); - /* Configure link feature according to nvram value */ eee_mode = (((SHMEM_RD(bp, dev_info. port_feature_config[port].eee_power_mode)) & diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h index 7d93adb57f3..f8d432af256 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_reg.h @@ -5498,6 +5498,7 @@ #define XMAC_CTRL_REG_RX_EN (0x1<<1) #define XMAC_CTRL_REG_SOFT_RESET (0x1<<6) #define XMAC_CTRL_REG_TX_EN (0x1<<0) +#define XMAC_CTRL_REG_XLGMII_ALIGN_ENB (0x1<<7) #define XMAC_PAUSE_CTRL_REG_RX_PAUSE_EN (0x1<<18) #define XMAC_PAUSE_CTRL_REG_TX_PAUSE_EN (0x1<<17) #define XMAC_PFC_CTRL_HI_REG_FORCE_PFC_XON (0x1<<1) @@ -5518,11 +5519,14 @@ #define XMAC_REG_PAUSE_CTRL 0x68 #define XMAC_REG_PFC_CTRL 0x70 #define XMAC_REG_PFC_CTRL_HI 0x74 +#define XMAC_REG_RX_LSS_CTRL 0x50 #define XMAC_REG_RX_LSS_STATUS 0x58 /* [RW 14] Maximum packet size in receive direction; exclusive of preamble & * CRC in strip mode */ #define XMAC_REG_RX_MAX_SIZE 0x40 #define XMAC_REG_TX_CTRL 0x20 +#define XMAC_RX_LSS_CTRL_REG_LOCAL_FAULT_DISABLE (0x1<<0) +#define XMAC_RX_LSS_CTRL_REG_REMOTE_FAULT_DISABLE (0x1<<1) /* [RW 16] Indirect access to the XX table of the XX protection mechanism. The fields are:[4:0] - tail pointer; 9:5] - Link List size; 14:10] - header pointer. */ @@ -6688,6 +6692,7 @@ #define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_XFI 0x1B00 #define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_DXGXS 0x1E00 #define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_10G_SFI 0x1F00 +#define MDIO_GP_STATUS_TOP_AN_STATUS1_ACTUAL_SPEED_20G_KR2 0x3900 #define MDIO_REG_BANK_10G_PARALLEL_DETECT 0x8130 @@ -7062,7 +7067,8 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_AN_IEEE1BLK_AN_ADVERTISEMENT2 0x12 #define MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_ABILITY 0x4000 #define MDIO_WC_REG_AN_IEEE1BLK_AN_ADV2_FEC_REQ 0x8000 -#define MDIO_WC_REG_PMD_IEEE9BLK_TENGBASE_KR_PMD_CONTROL_REGISTER_150 0x96 +#define MDIO_WC_REG_PCS_STATUS2 0x0021 +#define MDIO_WC_REG_PMD_KR_CONTROL 0x0096 #define MDIO_WC_REG_XGXSBLK0_XGXSCONTROL 0x8000 #define MDIO_WC_REG_XGXSBLK0_MISCCONTROL1 0x800e #define MDIO_WC_REG_XGXSBLK1_DESKEW 0x8010 @@ -7094,6 +7100,7 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_PAR_DET_10G_STATUS 0x8130 #define MDIO_WC_REG_PAR_DET_10G_CTRL 0x8131 #define MDIO_WC_REG_XGXS_X2_CONTROL2 0x8141 +#define MDIO_WC_REG_XGXS_X2_CONTROL3 0x8142 #define MDIO_WC_REG_XGXS_RX_LN_SWAP1 0x816B #define MDIO_WC_REG_XGXS_TX_LN_SWAP1 0x8169 #define MDIO_WC_REG_GP2_STATUS_GP_2_0 0x81d0 @@ -7128,6 +7135,7 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_TX_FIR_TAP_POST_TAP_OFFSET 0x0a #define MDIO_WC_REG_TX_FIR_TAP_POST_TAP_MASK 0x7c00 #define MDIO_WC_REG_TX_FIR_TAP_ENABLE 0x8000 +#define MDIO_WC_REG_CL72_USERB0_CL72_TX_FIR_TAP 0x82e2 #define MDIO_WC_REG_CL72_USERB0_CL72_MISC1_CONTROL 0x82e3 #define MDIO_WC_REG_CL72_USERB0_CL72_OS_DEF_CTRL 0x82e6 #define MDIO_WC_REG_CL72_USERB0_CL72_BR_DEF_CTRL 0x82e7 @@ -7145,9 +7153,16 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_DIGITAL4_MISC5 0x833e #define MDIO_WC_REG_DIGITAL5_MISC6 0x8345 #define MDIO_WC_REG_DIGITAL5_MISC7 0x8349 +#define MDIO_WC_REG_DIGITAL5_LINK_STATUS 0x834d #define MDIO_WC_REG_DIGITAL5_ACTUAL_SPEED 0x834e #define MDIO_WC_REG_DIGITAL6_MP5_NEXTPAGECTRL 0x8350 #define MDIO_WC_REG_CL49_USERB0_CTRL 0x8368 +#define MDIO_WC_REG_CL73_USERB0_CTRL 0x8370 +#define MDIO_WC_REG_CL73_USERB0_USTAT 0x8371 +#define MDIO_WC_REG_CL73_BAM_CTRL1 0x8372 +#define MDIO_WC_REG_CL73_BAM_CTRL2 0x8373 +#define MDIO_WC_REG_CL73_BAM_CTRL3 0x8374 +#define MDIO_WC_REG_CL73_BAM_CODE_FIELD 0x837b #define MDIO_WC_REG_EEE_COMBO_CONTROL0 0x8390 #define MDIO_WC_REG_TX66_CONTROL 0x83b0 #define MDIO_WC_REG_RX66_CONTROL 0x83c0 @@ -7161,7 +7176,17 @@ Theotherbitsarereservedandshouldbezero*/ #define MDIO_WC_REG_RX66_SCW3_MASK 0x83c9 #define MDIO_WC_REG_FX100_CTRL1 0x8400 #define MDIO_WC_REG_FX100_CTRL3 0x8402 - +#define MDIO_WC_REG_CL82_USERB1_TX_CTRL5 0x8436 +#define MDIO_WC_REG_CL82_USERB1_TX_CTRL6 0x8437 +#define MDIO_WC_REG_CL82_USERB1_TX_CTRL7 0x8438 +#define MDIO_WC_REG_CL82_USERB1_TX_CTRL9 0x8439 +#define MDIO_WC_REG_CL82_USERB1_RX_CTRL10 0x843a +#define MDIO_WC_REG_CL82_USERB1_RX_CTRL11 0x843b +#define MDIO_WC_REG_ETA_CL73_OUI1 0x8453 +#define MDIO_WC_REG_ETA_CL73_OUI2 0x8454 +#define MDIO_WC_REG_ETA_CL73_OUI3 0x8455 +#define MDIO_WC_REG_ETA_CL73_LD_BAM_CODE 0x8456 +#define MDIO_WC_REG_ETA_CL73_LD_UD_CODE 0x8457 #define MDIO_WC_REG_MICROBLK_CMD 0xffc2 #define MDIO_WC_REG_MICROBLK_DL_STATUS 0xffc5 #define MDIO_WC_REG_MICROBLK_CMD3 0xffcc diff --git a/drivers/net/ethernet/chelsio/Kconfig b/drivers/net/ethernet/chelsio/Kconfig index a71c0f317a9..d40c994a4f6 100644 --- a/drivers/net/ethernet/chelsio/Kconfig +++ b/drivers/net/ethernet/chelsio/Kconfig @@ -48,7 +48,7 @@ config CHELSIO_T1_1G config CHELSIO_T3 tristate "Chelsio Communications T3 10Gb Ethernet support" - depends on PCI + depends on PCI && INET select FW_LOADER select MDIO ---help--- diff --git a/drivers/net/ethernet/dec/ewrk3.c b/drivers/net/ethernet/dec/ewrk3.c index 17ae8c61968..9f992b95edd 100644 --- a/drivers/net/ethernet/dec/ewrk3.c +++ b/drivers/net/ethernet/dec/ewrk3.c @@ -1910,9 +1910,8 @@ static struct net_device *ewrk3_devs[MAX_NUM_EWRK3S]; static int ndevs; static int io[MAX_NUM_EWRK3S+1] = { 0x300, 0, }; -/* '21' below should really be 'MAX_NUM_EWRK3S' */ module_param_array(io, int, NULL, 0); -module_param_array(irq, int, NULL, 0); +module_param_array(irq, byte, NULL, 0); MODULE_PARM_DESC(io, "EtherWORKS 3 I/O base address(es)"); MODULE_PARM_DESC(irq, "EtherWORKS 3 IRQ number(s)"); diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index adef536c158..0661e937958 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1675,24 +1675,6 @@ static inline int events_get(struct be_eq_obj *eqo) return num; } -static int event_handle(struct be_eq_obj *eqo) -{ - bool rearm = false; - int num = events_get(eqo); - - /* Deal with any spurious interrupts that come without events */ - if (!num) - rearm = true; - - if (num || msix_enabled(eqo->adapter)) - be_eq_notify(eqo->adapter, eqo->q.id, rearm, true, num); - - if (num) - napi_schedule(&eqo->napi); - - return num; -} - /* Leaves the EQ is disarmed state */ static void be_eq_clean(struct be_eq_obj *eqo) { @@ -2014,15 +1996,23 @@ static int be_rx_cqs_create(struct be_adapter *adapter) static irqreturn_t be_intx(int irq, void *dev) { - struct be_adapter *adapter = dev; - int num_evts; + struct be_eq_obj *eqo = dev; + struct be_adapter *adapter = eqo->adapter; + int num_evts = 0; - /* With INTx only one EQ is used */ - num_evts = event_handle(&adapter->eq_obj[0]); - if (num_evts) - return IRQ_HANDLED; - else - return IRQ_NONE; + /* On Lancer, clear-intr bit of the EQ DB does not work. + * INTx is de-asserted only on notifying num evts. + */ + if (lancer_chip(adapter)) + num_evts = events_get(eqo); + + /* The EQ-notify may not de-assert INTx rightaway, causing + * the ISR to be invoked again. So, return HANDLED even when + * num_evts is zero. + */ + be_eq_notify(adapter, eqo->q.id, false, true, num_evts); + napi_schedule(&eqo->napi); + return IRQ_HANDLED; } static irqreturn_t be_msix(int irq, void *dev) @@ -2342,10 +2332,10 @@ static int be_irq_register(struct be_adapter *adapter) return status; } - /* INTx */ + /* INTx: only the first EQ is used */ netdev->irq = adapter->pdev->irq; status = request_irq(netdev->irq, be_intx, IRQF_SHARED, netdev->name, - adapter); + &adapter->eq_obj[0]); if (status) { dev_err(&adapter->pdev->dev, "INTx request IRQ failed - err %d\n", status); @@ -2367,7 +2357,7 @@ static void be_irq_unregister(struct be_adapter *adapter) /* INTx */ if (!msix_enabled(adapter)) { - free_irq(netdev->irq, adapter); + free_irq(netdev->irq, &adapter->eq_obj[0]); goto done; } @@ -3023,8 +3013,10 @@ static void be_netpoll(struct net_device *netdev) struct be_eq_obj *eqo; int i; - for_all_evt_queues(adapter, eqo, i) - event_handle(eqo); + for_all_evt_queues(adapter, eqo, i) { + be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0); + napi_schedule(&eqo->napi); + } return; } diff --git a/drivers/net/ethernet/intel/e1000e/80003es2lan.c b/drivers/net/ethernet/intel/e1000e/80003es2lan.c index 4dd18a1f45d..e73c2c35599 100644 --- a/drivers/net/ethernet/intel/e1000e/80003es2lan.c +++ b/drivers/net/ethernet/intel/e1000e/80003es2lan.c @@ -26,8 +26,7 @@ *******************************************************************************/ -/* - * 80003ES2LAN Gigabit Ethernet Controller (Copper) +/* 80003ES2LAN Gigabit Ethernet Controller (Copper) * 80003ES2LAN Gigabit Ethernet Controller (Serdes) */ @@ -80,7 +79,8 @@ 1 = 50-80M 2 = 80-110M 3 = 110-140M - 4 = >140M */ + 4 = >140M + */ /* Kumeran Mode Control Register (Page 193, Register 16) */ #define GG82563_KMCR_PASS_FALSE_CARRIER 0x0800 @@ -95,8 +95,7 @@ /* In-Band Control Register (Page 194, Register 18) */ #define GG82563_ICR_DIS_PADDING 0x0010 /* Disable Padding */ -/* - * A table for the GG82563 cable length where the range is defined +/* A table for the GG82563 cable length where the range is defined * with a lower bound at "index" and the upper bound at * "index + 5". */ @@ -183,8 +182,7 @@ static s32 e1000_init_nvm_params_80003es2lan(struct e1000_hw *hw) size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> E1000_EECD_SIZE_EX_SHIFT); - /* - * Added to a constant, "size" becomes the left-shift value + /* Added to a constant, "size" becomes the left-shift value * for setting word_size. */ size += NVM_WORD_SIZE_BASE_SHIFT; @@ -375,8 +373,7 @@ static s32 e1000_acquire_swfw_sync_80003es2lan(struct e1000_hw *hw, u16 mask) if (!(swfw_sync & (fwmask | swmask))) break; - /* - * Firmware currently using resource (fwmask) + /* Firmware currently using resource (fwmask) * or other software thread using resource (swmask) */ e1000e_put_hw_semaphore(hw); @@ -442,8 +439,7 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; } else { - /* - * Use Alternative Page Select register to access + /* Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; @@ -457,8 +453,7 @@ static s32 e1000_read_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, } if (hw->dev_spec.e80003es2lan.mdic_wa_enable) { - /* - * The "ready" bit in the MDIC register may be incorrectly set + /* The "ready" bit in the MDIC register may be incorrectly set * before the device has completed the "Page Select" MDI * transaction. So we wait 200us after each MDI command... */ @@ -513,8 +508,7 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, if ((offset & MAX_PHY_REG_ADDRESS) < GG82563_MIN_ALT_REG) { page_select = GG82563_PHY_PAGE_SELECT; } else { - /* - * Use Alternative Page Select register to access + /* Use Alternative Page Select register to access * registers 30 and 31 */ page_select = GG82563_PHY_PAGE_SELECT_ALT; @@ -528,8 +522,7 @@ static s32 e1000_write_phy_reg_gg82563_80003es2lan(struct e1000_hw *hw, } if (hw->dev_spec.e80003es2lan.mdic_wa_enable) { - /* - * The "ready" bit in the MDIC register may be incorrectly set + /* The "ready" bit in the MDIC register may be incorrectly set * before the device has completed the "Page Select" MDI * transaction. So we wait 200us after each MDI command... */ @@ -618,8 +611,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) u16 phy_data; bool link; - /* - * Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI * forced whenever speed and duplex are forced. */ ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); @@ -657,8 +649,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) return ret_val; if (!link) { - /* - * We didn't get link. + /* We didn't get link. * Reset the DSP and cross our fingers. */ ret_val = e1000e_phy_reset_dsp(hw); @@ -677,8 +668,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Resetting the phy means we need to verify the TX_CLK corresponds + /* Resetting the phy means we need to verify the TX_CLK corresponds * to the link speed. 10Mbps -> 2.5MHz, else 25MHz. */ phy_data &= ~GG82563_MSCR_TX_CLK_MASK; @@ -687,8 +677,7 @@ static s32 e1000_phy_force_speed_duplex_80003es2lan(struct e1000_hw *hw) else phy_data |= GG82563_MSCR_TX_CLK_100MBPS_25; - /* - * In addition, we must re-enable CRS on Tx for both half and full + /* In addition, we must re-enable CRS on Tx for both half and full * duplex. */ phy_data |= GG82563_MSCR_ASSERT_CRS_ON_TX; @@ -766,8 +755,7 @@ static s32 e1000_reset_hw_80003es2lan(struct e1000_hw *hw) s32 ret_val; u16 kum_reg_data; - /* - * Prevent the PCI-E bus from sticking if there is no TLP connection + /* Prevent the PCI-E bus from sticking if there is no TLP connection * on the last TLP read/write transaction when MAC is reset. */ ret_val = e1000e_disable_pcie_master(hw); @@ -899,8 +887,7 @@ static s32 e1000_init_hw_80003es2lan(struct e1000_hw *hw) hw->dev_spec.e80003es2lan.mdic_wa_enable = false; } - /* - * Clear all of the statistics registers (clear on read). It is + /* Clear all of the statistics registers (clear on read). It is * important that we do this after we have tried to establish link * because the symbol error count will increment wildly if there * is no link. @@ -945,8 +932,7 @@ static void e1000_initialize_hw_bits_80003es2lan(struct e1000_hw *hw) reg |= (1 << 28); ew32(TARC(1), reg); - /* - * Disable IPv6 extension header parsing because some malformed + /* Disable IPv6 extension header parsing because some malformed * IPv6 headers can hang the Rx. */ reg = er32(RFCTL); @@ -979,8 +965,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Options: + /* Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode @@ -1006,8 +991,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) break; } - /* - * Options: + /* Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled @@ -1065,8 +1049,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Do not init these registers when the HW is in IAMT mode, since the + /* Do not init these registers when the HW is in IAMT mode, since the * firmware will have already initialized them. We only initialize * them if the HW is not in IAMT mode. */ @@ -1087,8 +1070,7 @@ static s32 e1000_copper_link_setup_gg82563_80003es2lan(struct e1000_hw *hw) return ret_val; } - /* - * Workaround: Disable padding in Kumeran interface in the MAC + /* Workaround: Disable padding in Kumeran interface in the MAC * and in the PHY to avoid CRC errors. */ ret_val = e1e_rphy(hw, GG82563_PHY_INBAND_CTRL, &data); @@ -1121,8 +1103,7 @@ static s32 e1000_setup_copper_link_80003es2lan(struct e1000_hw *hw) ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); ew32(CTRL, ctrl); - /* - * Set the mac to wait the maximum time between each + /* Set the mac to wait the maximum time between each * iteration and increase the max iterations when * polling the phy; this fixes erroneous timeouts at 10Mbps. */ @@ -1352,8 +1333,7 @@ static s32 e1000_read_mac_addr_80003es2lan(struct e1000_hw *hw) { s32 ret_val = 0; - /* - * If there's an alternate MAC address place it in RAR0 + /* If there's an alternate MAC address place it in RAR0 * so that it will override the Si installed default perm * address. */ diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index c9858640800..c77d010d5c5 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -26,8 +26,7 @@ *******************************************************************************/ -/* - * 82571EB Gigabit Ethernet Controller +/* 82571EB Gigabit Ethernet Controller * 82571EB Gigabit Ethernet Controller (Copper) * 82571EB Gigabit Ethernet Controller (Fiber) * 82571EB Dual Port Gigabit Mezzanine Adapter @@ -191,8 +190,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) if (((eecd >> 15) & 0x3) == 0x3) { nvm->type = e1000_nvm_flash_hw; nvm->word_size = 2048; - /* - * Autonomous Flash update bit must be cleared due + /* Autonomous Flash update bit must be cleared due * to Flash update issue. */ eecd &= ~E1000_EECD_AUPDEN; @@ -204,8 +202,7 @@ static s32 e1000_init_nvm_params_82571(struct e1000_hw *hw) nvm->type = e1000_nvm_eeprom_spi; size = (u16)((eecd & E1000_EECD_SIZE_EX_MASK) >> E1000_EECD_SIZE_EX_SHIFT); - /* - * Added to a constant, "size" becomes the left-shift value + /* Added to a constant, "size" becomes the left-shift value * for setting word_size. */ size += NVM_WORD_SIZE_BASE_SHIFT; @@ -291,8 +288,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw) /* FWSM register */ mac->has_fwsm = true; - /* - * ARC supported; valid only if manageability features are + /* ARC supported; valid only if manageability features are * enabled. */ mac->arc_subsystem_valid = !!(er32(FWSM) & @@ -314,8 +310,7 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw) break; } - /* - * Ensure that the inter-port SWSM.SMBI lock bit is clear before + /* Ensure that the inter-port SWSM.SMBI lock bit is clear before * first NVM or PHY access. This should be done for single-port * devices, and for one port only on dual-port devices so that * for those devices we can still use the SMBI lock to synchronize @@ -352,11 +347,8 @@ static s32 e1000_init_mac_params_82571(struct e1000_hw *hw) ew32(SWSM, swsm & ~E1000_SWSM_SMBI); } - /* - * Initialize device specific counter of SMBI acquisition - * timeouts. - */ - hw->dev_spec.e82571.smb_counter = 0; + /* Initialize device specific counter of SMBI acquisition timeouts. */ + hw->dev_spec.e82571.smb_counter = 0; return 0; } @@ -445,8 +437,7 @@ static s32 e1000_get_phy_id_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82571: case e1000_82572: - /* - * The 82571 firmware may still be configuring the PHY. + /* The 82571 firmware may still be configuring the PHY. * In this case, we cannot access the PHY until the * configuration is done. So we explicitly set the * PHY ID. @@ -492,8 +483,7 @@ static s32 e1000_get_hw_semaphore_82571(struct e1000_hw *hw) s32 fw_timeout = hw->nvm.word_size + 1; s32 i = 0; - /* - * If we have timedout 3 times on trying to acquire + /* If we have timedout 3 times on trying to acquire * the inter-port SMBI semaphore, there is old code * operating on the other port, and it is not * releasing SMBI. Modify the number of times that @@ -787,8 +777,7 @@ static s32 e1000_update_nvm_checksum_82571(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * If our nvm is an EEPROM, then we're done + /* If our nvm is an EEPROM, then we're done * otherwise, commit the checksum to the flash NVM. */ if (hw->nvm.type != e1000_nvm_flash_hw) @@ -806,8 +795,7 @@ static s32 e1000_update_nvm_checksum_82571(struct e1000_hw *hw) /* Reset the firmware if using STM opcode. */ if ((er32(FLOP) & 0xFF00) == E1000_STM_OPCODE) { - /* - * The enabling of and the actual reset must be done + /* The enabling of and the actual reset must be done * in two write cycles. */ ew32(HICR, E1000_HICR_FW_RESET_ENABLE); @@ -867,8 +855,7 @@ static s32 e1000_write_nvm_eewr_82571(struct e1000_hw *hw, u16 offset, u32 i, eewr = 0; s32 ret_val = 0; - /* - * A check for invalid values: offset too large, too many words, + /* A check for invalid values: offset too large, too many words, * and not enough words. */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || @@ -957,8 +944,7 @@ static s32 e1000_set_d0_lplu_state_82571(struct e1000_hw *hw, bool active) } else { data &= ~IGP02E1000_PM_D0_LPLU; ret_val = e1e_wphy(hw, IGP02E1000_PHY_POWER_MGMT, data); - /* - * LPLU and SmartSpeed are mutually exclusive. LPLU is used + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used * during Dx states where the power conservation is most * important. During driver activity we should enable * SmartSpeed, so performance is maintained. @@ -1002,8 +988,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) u32 ctrl, ctrl_ext, eecd, tctl; s32 ret_val; - /* - * Prevent the PCI-E bus from sticking if there is no TLP connection + /* Prevent the PCI-E bus from sticking if there is no TLP connection * on the last TLP read/write transaction when MAC is reset. */ ret_val = e1000e_disable_pcie_master(hw); @@ -1021,8 +1006,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) usleep_range(10000, 20000); - /* - * Must acquire the MDIO ownership before MAC reset. + /* Must acquire the MDIO ownership before MAC reset. * Ownership defaults to firmware after a reset. */ switch (hw->mac.type) { @@ -1067,8 +1051,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) /* We don't want to continue accessing MAC registers. */ return ret_val; - /* - * Phy configuration from NVM just starts after EECD_AUTO_RD is set. + /* Phy configuration from NVM just starts after EECD_AUTO_RD is set. * Need to wait for Phy configuration completion before accessing * NVM and Phy. */ @@ -1076,8 +1059,7 @@ static s32 e1000_reset_hw_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82571: case e1000_82572: - /* - * REQ and GNT bits need to be cleared when using AUTO_RD + /* REQ and GNT bits need to be cleared when using AUTO_RD * to access the EEPROM. */ eecd = er32(EECD); @@ -1138,8 +1120,7 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) e_dbg("Initializing the IEEE VLAN\n"); mac->ops.clear_vfta(hw); - /* Setup the receive address. */ - /* + /* Setup the receive address. * If, however, a locally administered address was assigned to the * 82571, we must reserve a RAR for it to work around an issue where * resetting one port will reload the MAC on the other port. @@ -1183,8 +1164,7 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) break; } - /* - * Clear all of the statistics registers (clear on read). It is + /* Clear all of the statistics registers (clear on read). It is * important that we do this after we have tried to establish link * because the symbol error count will increment wildly if there * is no link. @@ -1281,8 +1261,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) ew32(PBA_ECC, reg); } - /* - * Workaround for hardware errata. + /* Workaround for hardware errata. * Ensure that DMA Dynamic Clock gating is disabled on 82571 and 82572 */ if ((hw->mac.type == e1000_82571) || (hw->mac.type == e1000_82572)) { @@ -1291,8 +1270,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) ew32(CTRL_EXT, reg); } - /* - * Disable IPv6 extension header parsing because some malformed + /* Disable IPv6 extension header parsing because some malformed * IPv6 headers can hang the Rx. */ if (hw->mac.type <= e1000_82573) { @@ -1309,8 +1287,7 @@ static void e1000_initialize_hw_bits_82571(struct e1000_hw *hw) reg |= (1 << 22); ew32(GCR, reg); - /* - * Workaround for hardware errata. + /* Workaround for hardware errata. * apply workaround for hardware errata documented in errata * docs Fixes issue where some error prone or unreliable PCIe * completions are occurring, particularly with ASPM enabled. @@ -1344,8 +1321,7 @@ static void e1000_clear_vfta_82571(struct e1000_hw *hw) case e1000_82574: case e1000_82583: if (hw->mng_cookie.vlan_id != 0) { - /* - * The VFTA is a 4096b bit-field, each identifying + /* The VFTA is a 4096b bit-field, each identifying * a single VLAN ID. The following operations * determine which 32b entry (i.e. offset) into the * array we want to set the VLAN ID (i.e. bit) of @@ -1362,8 +1338,7 @@ static void e1000_clear_vfta_82571(struct e1000_hw *hw) break; } for (offset = 0; offset < E1000_VLAN_FILTER_TBL_SIZE; offset++) { - /* - * If the offset we want to clear is the same offset of the + /* If the offset we want to clear is the same offset of the * manageability VLAN ID, then clear all bits except that of * the manageability unit. */ @@ -1401,8 +1376,7 @@ static s32 e1000_led_on_82574(struct e1000_hw *hw) ctrl = hw->mac.ledctl_mode2; if (!(E1000_STATUS_LU & er32(STATUS))) { - /* - * If no link, then turn LED on by setting the invert bit + /* If no link, then turn LED on by setting the invert bit * for each LED that's "on" (0x0E) in ledctl_mode2. */ for (i = 0; i < 4; i++) @@ -1427,8 +1401,7 @@ bool e1000_check_phy_82574(struct e1000_hw *hw) u16 receive_errors = 0; s32 ret_val = 0; - /* - * Read PHY Receive Error counter first, if its is max - all F's then + /* Read PHY Receive Error counter first, if its is max - all F's then * read the Base1000T status register If both are max then PHY is hung. */ ret_val = e1e_rphy(hw, E1000_RECEIVE_ERROR_COUNTER, &receive_errors); @@ -1458,8 +1431,7 @@ bool e1000_check_phy_82574(struct e1000_hw *hw) **/ static s32 e1000_setup_link_82571(struct e1000_hw *hw) { - /* - * 82573 does not have a word in the NVM to determine + /* 82573 does not have a word in the NVM to determine * the default flow control setting, so we explicitly * set it to full. */ @@ -1526,8 +1498,7 @@ static s32 e1000_setup_fiber_serdes_link_82571(struct e1000_hw *hw) switch (hw->mac.type) { case e1000_82571: case e1000_82572: - /* - * If SerDes loopback mode is entered, there is no form + /* If SerDes loopback mode is entered, there is no form * of reset to take the adapter out of that mode. So we * have to explicitly take the adapter out of loopback * mode. This prevents drivers from twiddling their thumbs @@ -1584,8 +1555,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) switch (mac->serdes_link_state) { case e1000_serdes_link_autoneg_complete: if (!(status & E1000_STATUS_LU)) { - /* - * We have lost link, retry autoneg before + /* We have lost link, retry autoneg before * reporting link failure */ mac->serdes_link_state = @@ -1598,8 +1568,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) break; case e1000_serdes_link_forced_up: - /* - * If we are receiving /C/ ordered sets, re-enable + /* If we are receiving /C/ ordered sets, re-enable * auto-negotiation in the TXCW register and disable * forced link in the Device Control register in an * attempt to auto-negotiate with our link partner. @@ -1619,8 +1588,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) case e1000_serdes_link_autoneg_progress: if (rxcw & E1000_RXCW_C) { - /* - * We received /C/ ordered sets, meaning the + /* We received /C/ ordered sets, meaning the * link partner has autonegotiated, and we can * trust the Link Up (LU) status bit. */ @@ -1636,8 +1604,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) e_dbg("AN_PROG -> DOWN\n"); } } else { - /* - * The link partner did not autoneg. + /* The link partner did not autoneg. * Force link up and full duplex, and change * state to forced. */ @@ -1660,8 +1627,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) case e1000_serdes_link_down: default: - /* - * The link was down but the receiver has now gained + /* The link was down but the receiver has now gained * valid sync, so lets see if we can bring the link * up. */ @@ -1679,8 +1645,7 @@ static s32 e1000_check_for_serdes_link_82571(struct e1000_hw *hw) mac->serdes_link_state = e1000_serdes_link_down; e_dbg("ANYSTATE -> DOWN\n"); } else { - /* - * Check several times, if SYNCH bit and CONFIG + /* Check several times, if SYNCH bit and CONFIG * bit both are consistently 1 then simply ignore * the IV bit and restart Autoneg */ @@ -1780,8 +1745,7 @@ void e1000e_set_laa_state_82571(struct e1000_hw *hw, bool state) /* If workaround is activated... */ if (state) - /* - * Hold a copy of the LAA in RAR[14] This is done so that + /* Hold a copy of the LAA in RAR[14] This is done so that * between the time RAR[0] gets clobbered and the time it * gets fixed, the actual LAA is in one of the RARs and no * incoming packets directed to this port are dropped. @@ -1810,8 +1774,7 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw) if (nvm->type != e1000_nvm_flash_hw) return 0; - /* - * Check bit 4 of word 10h. If it is 0, firmware is done updating + /* Check bit 4 of word 10h. If it is 0, firmware is done updating * 10h-12h. Checksum may need to be fixed. */ ret_val = e1000_read_nvm(hw, 0x10, 1, &data); @@ -1819,8 +1782,7 @@ static s32 e1000_fix_nvm_checksum_82571(struct e1000_hw *hw) return ret_val; if (!(data & 0x10)) { - /* - * Read 0x23 and check bit 15. This bit is a 1 + /* Read 0x23 and check bit 15. This bit is a 1 * when the checksum has already been fixed. If * the checksum is still wrong and this bit is a * 1, we need to return bad checksum. Otherwise, @@ -1852,8 +1814,7 @@ static s32 e1000_read_mac_addr_82571(struct e1000_hw *hw) if (hw->mac.type == e1000_82571) { s32 ret_val = 0; - /* - * If there's an alternate MAC address place it in RAR0 + /* If there's an alternate MAC address place it in RAR0 * so that it will override the Si installed default perm * address. */ diff --git a/drivers/net/ethernet/intel/e1000e/defines.h b/drivers/net/ethernet/intel/e1000e/defines.h index 76edbc1be33..02a12b69555 100644 --- a/drivers/net/ethernet/intel/e1000e/defines.h +++ b/drivers/net/ethernet/intel/e1000e/defines.h @@ -185,8 +185,7 @@ #define E1000_RCTL_BSEX 0x02000000 /* Buffer size extension */ #define E1000_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ -/* - * Use byte values for the following shift parameters +/* Use byte values for the following shift parameters * Usage: * psrctl |= (((ROUNDUP(value0, 128) >> E1000_PSRCTL_BSIZE0_SHIFT) & * E1000_PSRCTL_BSIZE0_MASK) | @@ -242,8 +241,7 @@ #define E1000_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */ #define E1000_CTRL_PHY_RST 0x80000000 /* PHY Reset */ -/* - * Bit definitions for the Management Data IO (MDIO) and Management Data +/* Bit definitions for the Management Data IO (MDIO) and Management Data * Clock (MDC) pins in the Device Control Register. */ @@ -424,8 +422,7 @@ #define E1000_PBA_ECC_STAT_CLR 0x00000002 /* Clear ECC error counter */ #define E1000_PBA_ECC_INT_EN 0x00000004 /* Enable ICR bit 5 for ECC */ -/* - * This defines the bits that are set in the Interrupt Mask +/* This defines the bits that are set in the Interrupt Mask * Set/Read Register. Each bit is documented below: * o RXT0 = Receiver Timer Interrupt (ring 0) * o TXDW = Transmit Descriptor Written Back @@ -475,8 +472,7 @@ /* 802.1q VLAN Packet Size */ #define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ -/* Receive Address */ -/* +/* Receive Address * Number of high/low register pairs in the RAR. The RAR (Receive Address * Registers) holds the directed and multicast addresses that we monitor. * Technically, we have 16 spots. However, we reserve one of these spots @@ -723,8 +719,7 @@ #define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */ #define MAX_PHY_MULTI_PAGE_REG 0xF -/* Bit definitions for valid PHY IDs. */ -/* +/* Bit definitions for valid PHY IDs. * I = Integrated * E = External */ @@ -762,8 +757,7 @@ #define M88E1000_PSCR_AUTO_X_1000T 0x0040 /* Auto crossover enabled all speeds */ #define M88E1000_PSCR_AUTO_X_MODE 0x0060 -/* - * 1=Enable Extended 10BASE-T distance (Lower 10BASE-T Rx Threshold) +/* 1=Enable Extended 10BASE-T distance (Lower 10BASE-T Rx Threshold) * 0=Normal 10BASE-T Rx Threshold */ #define M88E1000_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Transmit */ @@ -779,14 +773,12 @@ #define M88E1000_PSSR_CABLE_LENGTH_SHIFT 7 -/* - * Number of times we will attempt to autonegotiate before downshifting if we +/* Number of times we will attempt to autonegotiate before downshifting if we * are the master */ #define M88E1000_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00 #define M88E1000_EPSCR_MASTER_DOWNSHIFT_1X 0x0000 -/* - * Number of times we will attempt to autonegotiate before downshifting if we +/* Number of times we will attempt to autonegotiate before downshifting if we * are the slave */ #define M88E1000_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300 @@ -808,8 +800,7 @@ #define PHY_REG(page, reg) (((page) << PHY_PAGE_SHIFT) | \ ((reg) & MAX_PHY_REG_ADDRESS)) -/* - * Bits... +/* Bits... * 15-5: page * 4-0: register offset */ diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 04668b47a1d..6782a2eea1b 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -161,8 +161,7 @@ struct e1000_info; /* Time to wait before putting the device into D3 if there's no link (in ms). */ #define LINK_TIMEOUT 100 -/* - * Count for polling __E1000_RESET condition every 10-20msec. +/* Count for polling __E1000_RESET condition every 10-20msec. * Experimentation has shown the reset can take approximately 210msec. */ #define E1000_CHECK_RESET_COUNT 25 @@ -172,8 +171,7 @@ struct e1000_info; #define BURST_RDTR 0x20 #define BURST_RADV 0x20 -/* - * in the case of WTHRESH, it appears at least the 82571/2 hardware +/* in the case of WTHRESH, it appears at least the 82571/2 hardware * writes back 4 descriptors when WTHRESH=5, and 3 descriptors when * WTHRESH=4, so a setting of 5 gives the most efficient bus * utilization but to avoid possible Tx stalls, set it to 1 @@ -214,8 +212,7 @@ struct e1000_ps_page { u64 dma; /* must be u64 - written to hw */ }; -/* - * wrappers around a pointer to a socket buffer, +/* wrappers around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct e1000_buffer { @@ -305,9 +302,7 @@ struct e1000_adapter { u16 tx_itr; u16 rx_itr; - /* - * Tx - */ + /* Tx */ struct e1000_ring *tx_ring /* One per active queue */ ____cacheline_aligned_in_smp; u32 tx_fifo_limit; @@ -340,9 +335,7 @@ struct e1000_adapter { u32 tx_fifo_size; u32 tx_dma_failed; - /* - * Rx - */ + /* Rx */ bool (*clean_rx) (struct e1000_ring *ring, int *work_done, int work_to_do) ____cacheline_aligned_in_smp; void (*alloc_rx_buf) (struct e1000_ring *ring, int cleaned_count, diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index c11ac275666..f95bc6ee1c2 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -214,7 +214,8 @@ static int e1000_set_spd_dplx(struct e1000_adapter *adapter, u32 spd, u8 dplx) mac->autoneg = 0; /* Make sure dplx is at most 1 bit and lsb of speed is not set - * for the switch() below to work */ + * for the switch() below to work + */ if ((spd & 1) || (dplx & ~1)) goto err_inval; @@ -263,8 +264,7 @@ static int e1000_set_settings(struct net_device *netdev, struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; - /* - * When SoL/IDER sessions are active, autoneg/speed/duplex + /* When SoL/IDER sessions are active, autoneg/speed/duplex * cannot be changed */ if (hw->phy.ops.check_reset_block && @@ -273,8 +273,7 @@ static int e1000_set_settings(struct net_device *netdev, return -EINVAL; } - /* - * MDI setting is only allowed when autoneg enabled because + /* MDI setting is only allowed when autoneg enabled because * some hardware doesn't allow MDI setting when speed or * duplex is forced. */ @@ -316,8 +315,7 @@ static int e1000_set_settings(struct net_device *netdev, /* MDI-X => 2; MDI => 1; Auto => 3 */ if (ecmd->eth_tp_mdix_ctrl) { - /* - * fix up the value for auto (3 => 0) as zero is mapped + /* fix up the value for auto (3 => 0) as zero is mapped * internally to auto */ if (ecmd->eth_tp_mdix_ctrl == ETH_TP_MDI_AUTO) @@ -454,8 +452,8 @@ static void e1000_get_regs(struct net_device *netdev, regs_buff[12] = adapter->hw.phy.type; /* PHY type (IGP=1, M88=0) */ /* ethtool doesn't use anything past this point, so all this - * code is likely legacy junk for apps that may or may not - * exist */ + * code is likely legacy junk for apps that may or may not exist + */ if (hw->phy.type == e1000_phy_m88) { e1e_rphy(hw, M88E1000_PHY_SPEC_STATUS, &phy_data); regs_buff[13] = (u32)phy_data; /* cable length */ @@ -598,8 +596,7 @@ static int e1000_set_eeprom(struct net_device *netdev, if (ret_val) goto out; - /* - * Update the checksum over the first part of the EEPROM if needed + /* Update the checksum over the first part of the EEPROM if needed * and flush shadow RAM for applicable controllers */ if ((first_word <= NVM_CHECKSUM_REG) || @@ -623,8 +620,7 @@ static void e1000_get_drvinfo(struct net_device *netdev, strlcpy(drvinfo->version, e1000e_driver_version, sizeof(drvinfo->version)); - /* - * EEPROM image version # is reported as firmware version # for + /* EEPROM image version # is reported as firmware version # for * PCI-E controllers */ snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), @@ -708,8 +704,7 @@ static int e1000_set_ringparam(struct net_device *netdev, e1000e_down(adapter); - /* - * We can't just free everything and then setup again, because the + /* We can't just free everything and then setup again, because the * ISRs in MSI-X mode get passed pointers to the Tx and Rx ring * structs. First, attempt to allocate new resources... */ @@ -813,8 +808,7 @@ static int e1000_reg_test(struct e1000_adapter *adapter, u64 *data) u32 mask; u32 wlock_mac = 0; - /* - * The status register is Read Only, so a write should fail. + /* The status register is Read Only, so a write should fail. * Some bits that get toggled are ignored. */ switch (mac->type) { @@ -996,8 +990,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) } if (!shared_int) { - /* - * Disable the interrupt to be reported in + /* Disable the interrupt to be reported in * the cause register and then force the same * interrupt and see if one gets posted. If * an interrupt was posted to the bus, the @@ -1015,8 +1008,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) } } - /* - * Enable the interrupt to be reported in + /* Enable the interrupt to be reported in * the cause register and then force the same * interrupt and see if one gets posted. If * an interrupt was not posted to the bus, the @@ -1034,8 +1026,7 @@ static int e1000_intr_test(struct e1000_adapter *adapter, u64 *data) } if (!shared_int) { - /* - * Disable the other interrupts to be reported in + /* Disable the other interrupts to be reported in * the cause register and then force the other * interrupts and see if any get posted. If * an interrupt was posted to the bus, the @@ -1378,8 +1369,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) hw->phy.type == e1000_phy_m88) { ctrl_reg |= E1000_CTRL_ILOS; /* Invert Loss of Signal */ } else { - /* - * Set the ILOS bit on the fiber Nic if half duplex link is + /* Set the ILOS bit on the fiber Nic if half duplex link is * detected. */ if ((er32(STATUS) & E1000_STATUS_FD) == 0) @@ -1388,8 +1378,7 @@ static int e1000_integrated_phy_loopback(struct e1000_adapter *adapter) ew32(CTRL, ctrl_reg); - /* - * Disable the receiver on the PHY so when a cable is plugged in, the + /* Disable the receiver on the PHY so when a cable is plugged in, the * PHY does not begin to autoneg when a cable is reconnected to the NIC. */ if (hw->phy.type == e1000_phy_m88) @@ -1408,8 +1397,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter) /* special requirements for 82571/82572 fiber adapters */ - /* - * jump through hoops to make sure link is up because serdes + /* jump through hoops to make sure link is up because serdes * link is hardwired up */ ctrl |= E1000_CTRL_SLU; @@ -1429,8 +1417,7 @@ static int e1000_set_82571_fiber_loopback(struct e1000_adapter *adapter) ew32(CTRL, ctrl); } - /* - * special write to serdes control register to enable SerDes analog + /* special write to serdes control register to enable SerDes analog * loopback */ #define E1000_SERDES_LB_ON 0x410 @@ -1448,8 +1435,7 @@ static int e1000_set_es2lan_mac_loopback(struct e1000_adapter *adapter) u32 ctrlext = er32(CTRL_EXT); u32 ctrl = er32(CTRL); - /* - * save CTRL_EXT to restore later, reuse an empty variable (unused + /* save CTRL_EXT to restore later, reuse an empty variable (unused * on mac_type 80003es2lan) */ adapter->tx_fifo_head = ctrlext; @@ -1585,8 +1571,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) ew32(RDT(0), rx_ring->count - 1); - /* - * Calculate the loop count based on the largest descriptor ring + /* Calculate the loop count based on the largest descriptor ring * The idea is to wrap the largest ring a number of times using 64 * send/receive pairs during each loop */ @@ -1627,8 +1612,7 @@ static int e1000_run_loopback_test(struct e1000_adapter *adapter) l++; if (l == rx_ring->count) l = 0; - /* - * time + 20 msecs (200 msecs on 2.4) is more than + /* time + 20 msecs (200 msecs on 2.4) is more than * enough time to complete the receives, if it's * exceeded, break and error off */ @@ -1649,10 +1633,7 @@ static int e1000_loopback_test(struct e1000_adapter *adapter, u64 *data) { struct e1000_hw *hw = &adapter->hw; - /* - * PHY loopback cannot be performed if SoL/IDER - * sessions are active - */ + /* PHY loopback cannot be performed if SoL/IDER sessions are active */ if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) { e_err("Cannot do PHY loopback test when SoL/IDER is active.\n"); @@ -1686,8 +1667,7 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data) int i = 0; hw->mac.serdes_has_link = false; - /* - * On some blade server designs, link establishment + /* On some blade server designs, link establishment * could take as long as 2-3 minutes */ do { @@ -1701,8 +1681,7 @@ static int e1000_link_test(struct e1000_adapter *adapter, u64 *data) } else { hw->mac.ops.check_for_link(hw); if (hw->mac.autoneg) - /* - * On some Phy/switch combinations, link establishment + /* On some Phy/switch combinations, link establishment * can take a few seconds more than expected. */ msleep(5000); diff --git a/drivers/net/ethernet/intel/e1000e/hw.h b/drivers/net/ethernet/intel/e1000e/hw.h index d37bfd96c98..cf217777586 100644 --- a/drivers/net/ethernet/intel/e1000e/hw.h +++ b/drivers/net/ethernet/intel/e1000e/hw.h @@ -85,8 +85,7 @@ enum e1e_registers { E1000_FCRTL = 0x02160, /* Flow Control Receive Threshold Low - RW */ E1000_FCRTH = 0x02168, /* Flow Control Receive Threshold High - RW */ E1000_PSRCTL = 0x02170, /* Packet Split Receive Control - RW */ -/* - * Convenience macros +/* Convenience macros * * Note: "_n" is the queue number of the register to be written to. * @@ -800,8 +799,7 @@ struct e1000_mac_operations { s32 (*read_mac_addr)(struct e1000_hw *); }; -/* - * When to use various PHY register access functions: +/* When to use various PHY register access functions: * * Func Caller * Function Does Does When to use diff --git a/drivers/net/ethernet/intel/e1000e/ich8lan.c b/drivers/net/ethernet/intel/e1000e/ich8lan.c index e3a7b07df62..97633654760 100644 --- a/drivers/net/ethernet/intel/e1000e/ich8lan.c +++ b/drivers/net/ethernet/intel/e1000e/ich8lan.c @@ -26,8 +26,7 @@ *******************************************************************************/ -/* - * 82562G 10/100 Network Connection +/* 82562G 10/100 Network Connection * 82562G-2 10/100 Network Connection * 82562GT 10/100 Network Connection * 82562GT-2 10/100 Network Connection @@ -354,8 +353,7 @@ static bool e1000_phy_is_accessible_pchlan(struct e1000_hw *hw) return true; } - /* - * In case the PHY needs to be in mdio slow mode, + /* In case the PHY needs to be in mdio slow mode, * set slow mode and try to get the PHY id again. */ hw->phy.ops.release(hw); @@ -386,8 +384,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) return ret_val; } - /* - * The MAC-PHY interconnect may be in SMBus mode. If the PHY is + /* The MAC-PHY interconnect may be in SMBus mode. If the PHY is * inaccessible and resetting the PHY is not blocked, toggle the * LANPHYPC Value bit to force the interconnect to PCIe mode. */ @@ -396,8 +393,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) if (e1000_phy_is_accessible_pchlan(hw)) break; - /* - * Before toggling LANPHYPC, see if PHY is accessible by + /* Before toggling LANPHYPC, see if PHY is accessible by * forcing MAC to SMBus mode first. */ mac_reg = er32(CTRL_EXT); @@ -406,8 +402,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) /* fall-through */ case e1000_pch2lan: - /* - * Gate automatic PHY configuration by hardware on + /* Gate automatic PHY configuration by hardware on * non-managed 82579 */ if ((hw->mac.type == e1000_pch2lan) && @@ -474,8 +469,7 @@ static s32 e1000_init_phy_workarounds_pchlan(struct e1000_hw *hw) hw->phy.ops.release(hw); - /* - * Reset the PHY before any access to it. Doing so, ensures + /* Reset the PHY before any access to it. Doing so, ensures * that the PHY is in a known good state before we read/write * PHY registers. The generic reset is sufficient here, * because we haven't determined the PHY type yet. @@ -536,8 +530,7 @@ static s32 e1000_init_phy_params_pchlan(struct e1000_hw *hw) /* fall-through */ case e1000_pch2lan: case e1000_pch_lpt: - /* - * In case the PHY needs to be in mdio slow mode, + /* In case the PHY needs to be in mdio slow mode, * set slow mode and try to get the PHY id again. */ ret_val = e1000_set_mdio_slow_mode_hv(hw); @@ -593,8 +586,7 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw) phy->ops.power_up = e1000_power_up_phy_copper; phy->ops.power_down = e1000_power_down_phy_copper_ich8lan; - /* - * We may need to do this twice - once for IGP and if that fails, + /* We may need to do this twice - once for IGP and if that fails, * we'll set BM func pointers and try again */ ret_val = e1000e_determine_phy_address(hw); @@ -679,8 +671,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) gfpreg = er32flash(ICH_FLASH_GFPREG); - /* - * sector_X_addr is a "sector"-aligned address (4096 bytes) + /* sector_X_addr is a "sector"-aligned address (4096 bytes) * Add 1 to sector_end_addr since this sector is included in * the overall size. */ @@ -690,8 +681,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* flash_base_addr is byte-aligned */ nvm->flash_base_addr = sector_base_addr << FLASH_SECTOR_ADDR_SHIFT; - /* - * find total size of the NVM, then cut in half since the total + /* find total size of the NVM, then cut in half since the total * size represents two separate NVM banks. */ nvm->flash_bank_size = (sector_end_addr - sector_base_addr) @@ -788,8 +778,7 @@ static s32 e1000_init_mac_params_ich8lan(struct e1000_hw *hw) if (mac->type == e1000_ich8lan) e1000e_set_kmrn_lock_loss_workaround_ich8lan(hw, true); - /* - * Gate automatic PHY configuration by hardware on managed + /* Gate automatic PHY configuration by hardware on managed * 82579 and i217 */ if ((mac->type == e1000_pch2lan || mac->type == e1000_pch_lpt) && @@ -840,8 +829,7 @@ static s32 e1000_set_eee_pchlan(struct e1000_hw *hw) goto release; e1e_rphy_locked(hw, I82579_EMI_DATA, &dev_spec->eee_lp_ability); - /* - * EEE is not supported in 100Half, so ignore partner's EEE + /* EEE is not supported in 100Half, so ignore partner's EEE * in 100 ability if full-duplex is not advertised. */ e1e_rphy_locked(hw, PHY_LP_ABILITY, &phy_reg); @@ -869,8 +857,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) bool link; u16 phy_reg; - /* - * We only want to go out to the PHY registers to see if Auto-Neg + /* We only want to go out to the PHY registers to see if Auto-Neg * has completed and/or if our link status has changed. The * get_link_status flag is set upon receiving a Link Status * Change or Rx Sequence Error interrupt. @@ -878,8 +865,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (!mac->get_link_status) return 0; - /* - * First we want to see if the MII Status Register reports + /* First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex * of the PHY. */ @@ -914,8 +900,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) return ret_val; } - /* - * Workaround for PCHx parts in half-duplex: + /* Workaround for PCHx parts in half-duplex: * Set the number of preambles removed from the packet * when it is passed from the PHY to the MAC to prevent * the MAC from misinterpreting the packet type. @@ -932,8 +917,7 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) break; } - /* - * Check if there was DownShift, must be checked + /* Check if there was DownShift, must be checked * immediately after link-up */ e1000e_check_downshift(hw); @@ -943,22 +927,19 @@ static s32 e1000_check_for_copper_link_ich8lan(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * If we are forcing speed/duplex, then we simply return since + /* If we are forcing speed/duplex, then we simply return since * we have already determined whether we have link or not. */ if (!mac->autoneg) return -E1000_ERR_CONFIG; - /* - * Auto-Neg is enabled. Auto Speed Detection takes care + /* Auto-Neg is enabled. Auto Speed Detection takes care * of MAC speed/duplex configuration. So we only need to * configure Collision Distance in the MAC. */ mac->ops.config_collision_dist(hw); - /* - * Configure Flow Control now that Auto-Neg has completed. + /* Configure Flow Control now that Auto-Neg has completed. * First, we need to restore the desired flow control * settings because we may have had to re-autoneg with a * different link partner. @@ -1000,8 +981,7 @@ static s32 e1000_get_variants_ich8lan(struct e1000_adapter *adapter) if (rc) return rc; - /* - * Disable Jumbo Frame support on parts with Intel 10/100 PHY or + /* Disable Jumbo Frame support on parts with Intel 10/100 PHY or * on parts with MACsec enabled in NVM (reflected in CTRL_EXT). */ if ((adapter->hw.phy.type == e1000_phy_ife) || @@ -1191,8 +1171,7 @@ static void e1000_rar_set_pch2lan(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; - /* - * HW expects these in little endian so we reverse the byte order + /* 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] | @@ -1256,8 +1235,7 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) u32 rar_low, rar_high; u32 wlock_mac; - /* - * HW expects these in little endian so we reverse the byte order + /* 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) | @@ -1277,8 +1255,7 @@ static void e1000_rar_set_pch_lpt(struct e1000_hw *hw, u8 *addr, u32 index) return; } - /* - * The manageability engine (ME) can lock certain SHRAR registers that + /* The manageability engine (ME) can lock certain SHRAR registers that * it is using - those registers are unavailable for use. */ if (index < hw->mac.rar_entry_count) { @@ -1387,8 +1364,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) s32 ret_val = 0; u16 word_addr, reg_data, reg_addr, phy_page = 0; - /* - * Initialize the PHY from the NVM on ICH platforms. This + /* Initialize the PHY from the NVM on ICH platforms. This * is needed due to an issue where the NVM configuration is * not properly autoloaded after power transitions. * Therefore, after each PHY reset, we will load the @@ -1422,8 +1398,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) if (!(data & sw_cfg_mask)) goto release; - /* - * Make sure HW does not configure LCD from PHY + /* Make sure HW does not configure LCD from PHY * extended configuration before SW configuration */ data = er32(EXTCNF_CTRL); @@ -1443,8 +1418,7 @@ static s32 e1000_sw_lcd_config_ich8lan(struct e1000_hw *hw) if (((hw->mac.type == e1000_pchlan) && !(data & E1000_EXTCNF_CTRL_OEM_WRITE_ENABLE)) || (hw->mac.type > e1000_pchlan)) { - /* - * HW configures the SMBus address and LEDs when the + /* HW configures the SMBus address and LEDs when the * OEM and LCD Write Enable bits are set in the NVM. * When both NVM bits are cleared, SW will configure * them instead. @@ -1748,8 +1722,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) } if (hw->phy.type == e1000_phy_82578) { - /* - * Return registers to default by doing a soft reset then + /* Return registers to default by doing a soft reset then * writing 0x3140 to the control register. */ if (hw->phy.revision < 2) { @@ -1769,8 +1742,7 @@ static s32 e1000_hv_phy_workarounds_ich8lan(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Configure the K1 Si workaround during phy reset assuming there is + /* Configure the K1 Si workaround during phy reset assuming there is * link so that it disables K1 if link is in 1Gbps. */ ret_val = e1000_k1_gig_workaround_hv(hw, true); @@ -1853,8 +1825,7 @@ s32 e1000_lv_jumbo_workaround_ich8lan(struct e1000_hw *hw, bool enable) return ret_val; if (enable) { - /* - * Write Rx addresses (rar_entry_count for RAL/H, +4 for + /* Write Rx addresses (rar_entry_count for RAL/H, +4 for * SHRAL/H) and initial CRC values to the MAC */ for (i = 0; i < (hw->mac.rar_entry_count + 4); i++) { @@ -2131,8 +2102,7 @@ static void e1000_lan_init_done_ich8lan(struct e1000_hw *hw) udelay(100); } while ((!data) && --loop); - /* - * If basic configuration is incomplete before the above loop + /* If basic configuration is incomplete before the above loop * count reaches 0, loading the configuration from NVM will * leave the PHY in a bad state possibly resulting in no link. */ @@ -2299,8 +2269,7 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active) if (phy->type != e1000_phy_igp_3) return 0; - /* - * Call gig speed drop workaround on LPLU before accessing + /* Call gig speed drop workaround on LPLU before accessing * any PHY registers */ if (hw->mac.type == e1000_ich8lan) @@ -2319,8 +2288,7 @@ static s32 e1000_set_d0_lplu_state_ich8lan(struct e1000_hw *hw, bool active) if (phy->type != e1000_phy_igp_3) return 0; - /* - * LPLU and SmartSpeed are mutually exclusive. LPLU is used + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used * during Dx states where the power conservation is most * important. During driver activity we should enable * SmartSpeed, so performance is maintained. @@ -2382,8 +2350,7 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active) if (phy->type != e1000_phy_igp_3) return 0; - /* - * LPLU and SmartSpeed are mutually exclusive. LPLU is used + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used * during Dx states where the power conservation is most * important. During driver activity we should enable * SmartSpeed, so performance is maintained. @@ -2420,8 +2387,7 @@ static s32 e1000_set_d3_lplu_state_ich8lan(struct e1000_hw *hw, bool active) if (phy->type != e1000_phy_igp_3) return 0; - /* - * Call gig speed drop workaround on LPLU before accessing + /* Call gig speed drop workaround on LPLU before accessing * any PHY registers */ if (hw->mac.type == e1000_ich8lan) @@ -2589,8 +2555,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) ew16flash(ICH_FLASH_HSFSTS, hsfsts.regval); - /* - * Either we should have a hardware SPI cycle in progress + /* Either we should have a hardware SPI cycle in progress * bit to check against, in order to start a new cycle or * FDONE bit should be changed in the hardware so that it * is 1 after hardware reset, which can then be used as an @@ -2599,8 +2564,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) */ if (!hsfsts.hsf_status.flcinprog) { - /* - * There is no cycle running at present, + /* There is no cycle running at present, * so we can start a cycle. * Begin by setting Flash Cycle Done. */ @@ -2610,8 +2574,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) } else { s32 i; - /* - * Otherwise poll for sometime so the current + /* Otherwise poll for sometime so the current * cycle has a chance to end before giving up. */ for (i = 0; i < ICH_FLASH_READ_COMMAND_TIMEOUT; i++) { @@ -2623,8 +2586,7 @@ static s32 e1000_flash_cycle_init_ich8lan(struct e1000_hw *hw) udelay(1); } if (!ret_val) { - /* - * Successful in waiting for previous cycle to timeout, + /* Successful in waiting for previous cycle to timeout, * now set the Flash Cycle Done. */ hsfsts.hsf_status.flcdone = 1; @@ -2753,8 +2715,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, ret_val = e1000_flash_cycle_ich8lan(hw, ICH_FLASH_READ_COMMAND_TIMEOUT); - /* - * Check if FCERR is set to 1, if set to 1, clear it + /* Check if FCERR is set to 1, if set to 1, clear it * and try the whole sequence a few more times, else * read in (shift in) the Flash Data0, the order is * least significant byte first msb to lsb @@ -2767,8 +2728,7 @@ static s32 e1000_read_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, *data = (u16)(flash_data & 0x0000FFFF); break; } else { - /* - * If we've gotten here, then things are probably + /* If we've gotten here, then things are probably * completely hosed, but if the error condition is * detected, it won't hurt to give it another try... * ICH_FLASH_CYCLE_REPEAT_COUNT times. @@ -2849,8 +2809,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) nvm->ops.acquire(hw); - /* - * We're writing to the opposite bank so if we're on bank 1, + /* We're writing to the opposite bank so if we're on bank 1, * write to bank 0 etc. We also need to erase the segment that * is going to be written */ @@ -2875,8 +2834,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) } for (i = 0; i < E1000_ICH8_SHADOW_RAM_WORDS; i++) { - /* - * Determine whether to write the value stored + /* Determine whether to write the value stored * in the other NVM bank or a modified value stored * in the shadow RAM */ @@ -2890,8 +2848,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) break; } - /* - * If the word is 0x13, then make sure the signature bits + /* If the word is 0x13, then make sure the signature bits * (15:14) are 11b until the commit has completed. * This will allow us to write 10b which indicates the * signature is valid. We want to do this after the write @@ -2920,8 +2877,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) break; } - /* - * Don't bother writing the segment valid bits if sector + /* Don't bother writing the segment valid bits if sector * programming failed. */ if (ret_val) { @@ -2930,8 +2886,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) goto release; } - /* - * Finally validate the new segment by setting bit 15:14 + /* Finally validate the new segment by setting bit 15:14 * to 10b in word 0x13 , this can be done without an * erase as well since these bits are 11 to start with * and we need to change bit 14 to 0b @@ -2948,8 +2903,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) if (ret_val) goto release; - /* - * And invalidate the previously valid segment by setting + /* And invalidate the previously valid segment by setting * its signature word (0x13) high_byte to 0b. This can be * done without an erase because flash erase sets all bits * to 1's. We can write 1's to 0's without an erase @@ -2968,8 +2922,7 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) release: nvm->ops.release(hw); - /* - * Reload the EEPROM, or else modifications will not appear + /* Reload the EEPROM, or else modifications will not appear * until after the next adapter reset. */ if (!ret_val) { @@ -2997,8 +2950,7 @@ static s32 e1000_validate_nvm_checksum_ich8lan(struct e1000_hw *hw) s32 ret_val; u16 data; - /* - * Read 0x19 and check bit 6. If this bit is 0, the checksum + /* Read 0x19 and check bit 6. If this bit is 0, the checksum * needs to be fixed. This bit is an indication that the NVM * was prepared by OEM software and did not calculate the * checksum...a likely scenario. @@ -3048,8 +3000,7 @@ void e1000e_write_protect_nvm_ich8lan(struct e1000_hw *hw) pr0.range.wpe = true; ew32flash(ICH_FLASH_PR0, pr0.regval); - /* - * Lock down a subset of GbE Flash Control Registers, e.g. + /* Lock down a subset of GbE Flash Control Registers, e.g. * PR0 to prevent the write-protection from being lifted. * Once FLOCKDN is set, the registers protected by it cannot * be written until FLOCKDN is cleared by a hardware reset. @@ -3109,8 +3060,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, ew32flash(ICH_FLASH_FDATA0, flash_data); - /* - * check if FCERR is set to 1 , if set to 1, clear it + /* check if FCERR is set to 1 , if set to 1, clear it * and try the whole sequence a few more times else done */ ret_val = e1000_flash_cycle_ich8lan(hw, @@ -3118,8 +3068,7 @@ static s32 e1000_write_flash_data_ich8lan(struct e1000_hw *hw, u32 offset, if (!ret_val) break; - /* - * If we're here, then things are most likely + /* If we're here, then things are most likely * completely hosed, but if the error condition * is detected, it won't hurt to give it another * try...ICH_FLASH_CYCLE_REPEAT_COUNT times. @@ -3207,8 +3156,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) hsfsts.regval = er16flash(ICH_FLASH_HSFSTS); - /* - * Determine HW Sector size: Read BERASE bits of hw flash status + /* Determine HW Sector size: Read BERASE bits of hw flash status * register * 00: The Hw sector is 256 bytes, hence we need to erase 16 * consecutive sectors. The start index for the nth Hw sector @@ -3253,16 +3201,14 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) if (ret_val) return ret_val; - /* - * Write a value 11 (block Erase) in Flash + /* Write a value 11 (block Erase) in Flash * Cycle field in hw flash control */ hsflctl.regval = er16flash(ICH_FLASH_HSFCTL); hsflctl.hsf_ctrl.flcycle = ICH_CYCLE_ERASE; ew16flash(ICH_FLASH_HSFCTL, hsflctl.regval); - /* - * Write the last 24 bits of an index within the + /* Write the last 24 bits of an index within the * block into Flash Linear address field in Flash * Address. */ @@ -3274,8 +3220,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) if (!ret_val) break; - /* - * Check if FCERR is set to 1. If 1, + /* Check if FCERR is set to 1. If 1, * clear it and try the whole sequence * a few more times else Done */ @@ -3403,8 +3348,7 @@ static s32 e1000_get_bus_info_ich8lan(struct e1000_hw *hw) ret_val = e1000e_get_bus_info_pcie(hw); - /* - * ICH devices are "PCI Express"-ish. They have + /* ICH devices are "PCI Express"-ish. They have * a configuration space, but do not contain * PCI Express Capability registers, so bus width * must be hardcoded. @@ -3429,8 +3373,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) u32 ctrl, reg; s32 ret_val; - /* - * Prevent the PCI-E bus from sticking if there is no TLP connection + /* Prevent the PCI-E bus from sticking if there is no TLP connection * on the last TLP read/write transaction when MAC is reset. */ ret_val = e1000e_disable_pcie_master(hw); @@ -3440,8 +3383,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) e_dbg("Masking off all interrupts\n"); ew32(IMC, 0xffffffff); - /* - * Disable the Transmit and Receive units. Then delay to allow + /* Disable the Transmit and Receive units. Then delay to allow * any pending transactions to complete before we hit the MAC * with the global reset. */ @@ -3474,15 +3416,13 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) ctrl = er32(CTRL); if (!hw->phy.ops.check_reset_block(hw)) { - /* - * Full-chip reset requires MAC and PHY reset at the same + /* Full-chip reset requires MAC and PHY reset at the same * time to make sure the interface between MAC and the * external PHY is reset. */ ctrl |= E1000_CTRL_PHY_RST; - /* - * Gate automatic PHY configuration by hardware on + /* Gate automatic PHY configuration by hardware on * non-managed 82579 */ if ((hw->mac.type == e1000_pch2lan) && @@ -3516,8 +3456,7 @@ static s32 e1000_reset_hw_ich8lan(struct e1000_hw *hw) return ret_val; } - /* - * For PCH, this write will make sure that any noise + /* For PCH, this write will make sure that any noise * will be detected as a CRC error and be dropped rather than show up * as a bad packet to the DMA engine. */ @@ -3569,8 +3508,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) for (i = 0; i < mac->mta_reg_count; i++) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); - /* - * The 82578 Rx buffer will stall if wakeup is enabled in host and + /* The 82578 Rx buffer will stall if wakeup is enabled in host and * the ME. Disable wakeup by clearing the host wakeup bit. * Reset the phy after disabling host wakeup to reset the Rx buffer. */ @@ -3600,8 +3538,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) E1000_TXDCTL_MAX_TX_DESC_PREFETCH; ew32(TXDCTL(1), txdctl); - /* - * ICH8 has opposite polarity of no_snoop bits. + /* ICH8 has opposite polarity of no_snoop bits. * By default, we should use snoop behavior. */ if (mac->type == e1000_ich8lan) @@ -3614,8 +3551,7 @@ static s32 e1000_init_hw_ich8lan(struct e1000_hw *hw) ctrl_ext |= E1000_CTRL_EXT_RO_DIS; ew32(CTRL_EXT, ctrl_ext); - /* - * Clear all of the statistics registers (clear on read). It is + /* Clear all of the statistics registers (clear on read). It is * important that we do this after we have tried to establish link * because the symbol error count will increment wildly if there * is no link. @@ -3676,15 +3612,13 @@ static void e1000_initialize_hw_bits_ich8lan(struct e1000_hw *hw) ew32(STATUS, reg); } - /* - * work-around descriptor data corruption issue during nfs v2 udp + /* work-around descriptor data corruption issue during nfs v2 udp * traffic, just disable the nfs filtering capability */ reg = er32(RFCTL); reg |= (E1000_RFCTL_NFSW_DIS | E1000_RFCTL_NFSR_DIS); - /* - * Disable IPv6 extension header parsing because some malformed + /* Disable IPv6 extension header parsing because some malformed * IPv6 headers can hang the Rx. */ if (hw->mac.type == e1000_ich8lan) @@ -3709,8 +3643,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw) if (hw->phy.ops.check_reset_block(hw)) return 0; - /* - * ICH parts do not have a word in the NVM to determine + /* ICH parts do not have a word in the NVM to determine * the default flow control setting, so we explicitly * set it to full. */ @@ -3722,8 +3655,7 @@ static s32 e1000_setup_link_ich8lan(struct e1000_hw *hw) hw->fc.requested_mode = e1000_fc_full; } - /* - * Save off the requested flow control mode for use later. Depending + /* Save off the requested flow control mode for use later. Depending * on the link partner's capabilities, we may or may not use this mode. */ hw->fc.current_mode = hw->fc.requested_mode; @@ -3771,8 +3703,7 @@ static s32 e1000_setup_copper_link_ich8lan(struct e1000_hw *hw) ctrl &= ~(E1000_CTRL_FRCSPD | E1000_CTRL_FRCDPX); ew32(CTRL, ctrl); - /* - * Set the mac to wait the maximum time between each iteration + /* Set the mac to wait the maximum time between each iteration * and increase the max iterations when polling the phy; * this fixes erroneous timeouts at 10Mbps. */ @@ -3892,8 +3823,7 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw) if (!dev_spec->kmrn_lock_loss_workaround_enabled) return 0; - /* - * Make sure link is up before proceeding. If not just return. + /* Make sure link is up before proceeding. If not just return. * Attempting this while link is negotiating fouled up link * stability */ @@ -3925,8 +3855,7 @@ static s32 e1000_kmrn_lock_loss_workaround_ich8lan(struct e1000_hw *hw) E1000_PHY_CTRL_NOND0A_GBE_DISABLE); ew32(PHY_CTRL, phy_ctrl); - /* - * Call gig speed drop workaround on Gig disable before accessing + /* Call gig speed drop workaround on Gig disable before accessing * any PHY registers */ e1000e_gig_downshift_workaround_ich8lan(hw); @@ -3983,8 +3912,7 @@ void e1000e_igp3_phy_powerdown_workaround_ich8lan(struct e1000_hw *hw) E1000_PHY_CTRL_NOND0A_GBE_DISABLE); ew32(PHY_CTRL, reg); - /* - * Call gig speed drop workaround on Gig disable before + /* Call gig speed drop workaround on Gig disable before * accessing any PHY registers */ if (hw->mac.type == e1000_ich8lan) @@ -4078,8 +4006,7 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) goto release; e1e_rphy_locked(hw, I82579_EMI_DATA, &eee_advert); - /* - * Disable LPLU if both link partners support 100BaseT + /* Disable LPLU if both link partners support 100BaseT * EEE and 100Full is advertised on both ends of the * link. */ @@ -4091,8 +4018,7 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) E1000_PHY_CTRL_NOND0A_LPLU); } - /* - * For i217 Intel Rapid Start Technology support, + /* For i217 Intel Rapid Start Technology support, * when the system is going into Sx and no manageability engine * is present, the driver must configure proxy to reset only on * power good. LPI (Low Power Idle) state must also reset only @@ -4106,8 +4032,7 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) phy_reg |= I217_PROXY_CTRL_AUTO_DISABLE; e1e_wphy_locked(hw, I217_PROXY_CTRL, phy_reg); - /* - * Set bit enable LPI (EEE) to reset only on + /* Set bit enable LPI (EEE) to reset only on * power good. */ e1e_rphy_locked(hw, I217_SxCTRL, &phy_reg); @@ -4120,8 +4045,7 @@ void e1000_suspend_workarounds_ich8lan(struct e1000_hw *hw) e1e_wphy_locked(hw, I217_MEMPWR, phy_reg); } - /* - * Enable MTA to reset for Intel Rapid Start Technology + /* Enable MTA to reset for Intel Rapid Start Technology * Support */ e1e_rphy_locked(hw, I217_CGFREG, &phy_reg); @@ -4175,8 +4099,7 @@ void e1000_resume_workarounds_pchlan(struct e1000_hw *hw) return; } - /* - * For i217 Intel Rapid Start Technology support when the system + /* For i217 Intel Rapid Start Technology support when the system * is transitioning from Sx and no manageability engine is present * configure SMBus to restore on reset, disable proxy, and enable * the reset on MTA (Multicast table array). @@ -4191,8 +4114,7 @@ void e1000_resume_workarounds_pchlan(struct e1000_hw *hw) } if (!(er32(FWSM) & E1000_ICH_FWSM_FW_VALID)) { - /* - * Restore clear on SMB if no manageability engine + /* Restore clear on SMB if no manageability engine * is present */ ret_val = e1e_rphy_locked(hw, I217_MEMPWR, &phy_reg); @@ -4298,8 +4220,7 @@ static s32 e1000_led_on_pchlan(struct e1000_hw *hw) u16 data = (u16)hw->mac.ledctl_mode2; u32 i, led; - /* - * If no link, then turn LED on by setting the invert bit + /* If no link, then turn LED on by setting the invert bit * for each LED that's mode is "link_up" in ledctl_mode2. */ if (!(er32(STATUS) & E1000_STATUS_LU)) { @@ -4329,8 +4250,7 @@ static s32 e1000_led_off_pchlan(struct e1000_hw *hw) u16 data = (u16)hw->mac.ledctl_mode1; u32 i, led; - /* - * If no link, then turn LED off by clearing the invert bit + /* If no link, then turn LED off by clearing the invert bit * for each LED that's mode is "link_up" in ledctl_mode1. */ if (!(er32(STATUS) & E1000_STATUS_LU)) { @@ -4375,8 +4295,7 @@ static s32 e1000_get_cfg_done_ich8lan(struct e1000_hw *hw) } else { ret_val = e1000e_get_auto_rd_done(hw); if (ret_val) { - /* - * When auto config read does not complete, do not + /* When auto config read does not complete, do not * return with an error. This can happen in situations * where there is no eeprom and prevents getting link. */ diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index a1343992848..54d9dafaf12 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -73,8 +73,7 @@ void e1000_set_lan_id_multi_port_pcie(struct e1000_hw *hw) struct e1000_bus_info *bus = &hw->bus; u32 reg; - /* - * The status register reports the correct function number + /* The status register reports the correct function number * for the device regardless of function swap state. */ reg = er32(STATUS); @@ -210,8 +209,7 @@ s32 e1000_check_alt_mac_addr_generic(struct e1000_hw *hw) return 0; } - /* - * We have a valid alternate MAC address, and we want to treat it the + /* We have a valid alternate MAC address, and we want to treat it the * same as the normal permanent MAC address stored by the HW into the * RAR. Do this by mapping this address into RAR0. */ @@ -233,8 +231,7 @@ void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) { u32 rar_low, rar_high; - /* - * HW expects these in little endian so we reverse the byte order + /* 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) | @@ -246,8 +243,7 @@ void e1000e_rar_set_generic(struct e1000_hw *hw, u8 *addr, u32 index) if (rar_low || rar_high) rar_high |= E1000_RAH_AV; - /* - * Some bridges will combine consecutive 32-bit writes into + /* Some bridges will combine consecutive 32-bit writes into * a single burst write, which will malfunction on some parts. * The flushes avoid this. */ @@ -273,15 +269,13 @@ static u32 e1000_hash_mc_addr(struct e1000_hw *hw, u8 *mc_addr) /* Register count multiplied by bits per register */ hash_mask = (hw->mac.mta_reg_count * 32) - 1; - /* - * For a mc_filter_type of 0, bit_shift is the number of left-shifts + /* For a mc_filter_type of 0, bit_shift is the number of left-shifts * where 0xFF would still fall within the hash mask. */ while (hash_mask >> bit_shift != 0xFF) bit_shift++; - /* - * The portion of the address that is used for the hash table + /* The portion of the address that is used for the hash table * is determined by the mc_filter_type setting. * The algorithm is such that there is a total of 8 bits of shifting. * The bit_shift for a mc_filter_type of 0 represents the number of @@ -423,8 +417,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) s32 ret_val; bool link; - /* - * We only want to go out to the PHY registers to see if Auto-Neg + /* We only want to go out to the PHY registers to see if Auto-Neg * has completed and/or if our link status has changed. The * get_link_status flag is set upon receiving a Link Status * Change or Rx Sequence Error interrupt. @@ -432,8 +425,7 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) if (!mac->get_link_status) return 0; - /* - * First we want to see if the MII Status Register reports + /* First we want to see if the MII Status Register reports * link. If so, then we want to get the current speed/duplex * of the PHY. */ @@ -446,28 +438,24 @@ s32 e1000e_check_for_copper_link(struct e1000_hw *hw) mac->get_link_status = false; - /* - * Check if there was DownShift, must be checked + /* Check if there was DownShift, must be checked * immediately after link-up */ e1000e_check_downshift(hw); - /* - * If we are forcing speed/duplex, then we simply return since + /* If we are forcing speed/duplex, then we simply return since * we have already determined whether we have link or not. */ if (!mac->autoneg) return -E1000_ERR_CONFIG; - /* - * Auto-Neg is enabled. Auto Speed Detection takes care + /* Auto-Neg is enabled. Auto Speed Detection takes care * of MAC speed/duplex configuration. So we only need to * configure Collision Distance in the MAC. */ mac->ops.config_collision_dist(hw); - /* - * Configure Flow Control now that Auto-Neg has completed. + /* Configure Flow Control now that Auto-Neg has completed. * First, we need to restore the desired flow control * settings because we may have had to re-autoneg with a * different link partner. @@ -498,8 +486,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw) status = er32(STATUS); rxcw = er32(RXCW); - /* - * If we don't have link (auto-negotiation failed or link partner + /* If we don't have link (auto-negotiation failed or link partner * cannot auto-negotiate), the cable is plugged in (we have signal), * and our link partner is not trying to auto-negotiate with us (we * are receiving idles or data), we need to force link up. We also @@ -530,8 +517,7 @@ s32 e1000e_check_for_fiber_link(struct e1000_hw *hw) return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { - /* - * If we are forcing link and we are receiving /C/ ordered + /* If we are forcing link and we are receiving /C/ ordered * sets, re-enable auto-negotiation in the TXCW register * and disable forced link in the Device Control register * in an attempt to auto-negotiate with our link partner. @@ -565,8 +551,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) status = er32(STATUS); rxcw = er32(RXCW); - /* - * If we don't have link (auto-negotiation failed or link partner + /* If we don't have link (auto-negotiation failed or link partner * cannot auto-negotiate), and our link partner is not trying to * auto-negotiate with us (we are receiving idles or data), * we need to force link up. We also need to give auto-negotiation @@ -595,8 +580,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) return ret_val; } } else if ((ctrl & E1000_CTRL_SLU) && (rxcw & E1000_RXCW_C)) { - /* - * If we are forcing link and we are receiving /C/ ordered + /* If we are forcing link and we are receiving /C/ ordered * sets, re-enable auto-negotiation in the TXCW register * and disable forced link in the Device Control register * in an attempt to auto-negotiate with our link partner. @@ -607,8 +591,7 @@ s32 e1000e_check_for_serdes_link(struct e1000_hw *hw) mac->serdes_has_link = true; } else if (!(E1000_TXCW_ANE & er32(TXCW))) { - /* - * If we force link for non-auto-negotiation switch, check + /* If we force link for non-auto-negotiation switch, check * link status based on MAC synchronization for internal * serdes media type. */ @@ -665,8 +648,7 @@ static s32 e1000_set_default_fc_generic(struct e1000_hw *hw) s32 ret_val; u16 nvm_data; - /* - * Read and store word 0x0F of the EEPROM. This word contains bits + /* Read and store word 0x0F of the EEPROM. This word contains bits * that determine the hardware's default PAUSE (flow control) mode, * a bit that determines whether the HW defaults to enabling or * disabling auto-negotiation, and the direction of the @@ -705,15 +687,13 @@ s32 e1000e_setup_link_generic(struct e1000_hw *hw) { s32 ret_val; - /* - * In the case of the phy reset being blocked, we already have a link. + /* In the case of the phy reset being blocked, we already have a link. * We do not need to set it up again. */ if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw)) return 0; - /* - * If requested flow control is set to default, set flow control + /* If requested flow control is set to default, set flow control * based on the EEPROM flow control settings. */ if (hw->fc.requested_mode == e1000_fc_default) { @@ -722,8 +702,7 @@ s32 e1000e_setup_link_generic(struct e1000_hw *hw) return ret_val; } - /* - * Save off the requested flow control mode for use later. Depending + /* Save off the requested flow control mode for use later. Depending * on the link partner's capabilities, we may or may not use this mode. */ hw->fc.current_mode = hw->fc.requested_mode; @@ -735,8 +714,7 @@ s32 e1000e_setup_link_generic(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Initialize the flow control address, type, and PAUSE timer + /* Initialize the flow control address, type, and PAUSE timer * registers to their default values. This is done even if flow * control is disabled, because it does not hurt anything to * initialize these registers. @@ -763,8 +741,7 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) struct e1000_mac_info *mac = &hw->mac; u32 txcw; - /* - * Check for a software override of the flow control settings, and + /* Check for a software override of the flow control settings, and * setup the device accordingly. If auto-negotiation is enabled, then * software will have to set the "PAUSE" bits to the correct value in * the Transmit Config Word Register (TXCW) and re-start auto- @@ -786,8 +763,7 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) txcw = (E1000_TXCW_ANE | E1000_TXCW_FD); break; case e1000_fc_rx_pause: - /* - * Rx Flow control is enabled and Tx Flow control is disabled + /* Rx Flow control is enabled and Tx Flow control is disabled * by a software over-ride. Since there really isn't a way to * advertise that we are capable of Rx Pause ONLY, we will * advertise that we support both symmetric and asymmetric Rx @@ -797,15 +773,13 @@ static s32 e1000_commit_fc_settings_generic(struct e1000_hw *hw) txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); break; case e1000_fc_tx_pause: - /* - * Tx Flow control is enabled, and Rx Flow control is disabled, + /* Tx Flow control is enabled, and Rx Flow control is disabled, * by a software over-ride. */ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR); break; case e1000_fc_full: - /* - * Flow control (both Rx and Tx) is enabled by a software + /* Flow control (both Rx and Tx) is enabled by a software * over-ride. */ txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK); @@ -835,8 +809,7 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) u32 i, status; s32 ret_val; - /* - * If we have a signal (the cable is plugged in, or assumed true for + /* If we have a signal (the cable is plugged in, or assumed true for * serdes media) then poll for a "Link-Up" indication in the Device * Status Register. Time-out if a link isn't seen in 500 milliseconds * seconds (Auto-negotiation should complete in less than 500 @@ -851,8 +824,7 @@ static s32 e1000_poll_fiber_serdes_link_generic(struct e1000_hw *hw) if (i == FIBER_LINK_UP_LIMIT) { e_dbg("Never got a valid link from auto-neg!!!\n"); mac->autoneg_failed = true; - /* - * AutoNeg failed to achieve a link, so we'll call + /* AutoNeg failed to achieve a link, so we'll call * mac->check_for_link. This routine will force the * link up if we detect a signal. This will allow us to * communicate with non-autonegotiating link partners. @@ -894,8 +866,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Since auto-negotiation is enabled, take the link out of reset (the + /* Since auto-negotiation is enabled, take the link out of reset (the * link will be in reset, because we previously reset the chip). This * will restart auto-negotiation. If auto-negotiation is successful * then the link-up status bit will be set and the flow control enable @@ -907,8 +878,7 @@ s32 e1000e_setup_fiber_serdes_link(struct e1000_hw *hw) e1e_flush(); usleep_range(1000, 2000); - /* - * For these adapters, the SW definable pin 1 is set when the optics + /* For these adapters, the SW definable pin 1 is set when the optics * detect a signal. If we have a signal, then poll for a "Link-Up" * indication. */ @@ -954,16 +924,14 @@ s32 e1000e_set_fc_watermarks(struct e1000_hw *hw) { u32 fcrtl = 0, fcrth = 0; - /* - * Set the flow control receive threshold registers. Normally, + /* Set the flow control receive threshold registers. Normally, * these registers will be set to a default threshold that may be * adjusted later by the driver's runtime code. However, if the * ability to transmit pause frames is not enabled, then these * registers will be set to 0. */ if (hw->fc.current_mode & e1000_fc_tx_pause) { - /* - * We need to set up the Receive Threshold high and low water + /* We need to set up the Receive Threshold high and low water * marks as well as (optionally) enabling the transmission of * XON frames. */ @@ -995,8 +963,7 @@ s32 e1000e_force_mac_fc(struct e1000_hw *hw) ctrl = er32(CTRL); - /* - * Because we didn't get link via the internal auto-negotiation + /* Because we didn't get link via the internal auto-negotiation * mechanism (we either forced link or we got link via PHY * auto-neg), we have to manually enable/disable transmit an * receive flow control. @@ -1057,8 +1024,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; u16 speed, duplex; - /* - * Check for the case where we have fiber media and auto-neg failed + /* Check for the case where we have fiber media and auto-neg failed * so we had to force link. In this case, we need to force the * configuration of the MAC to match the "fc" parameter. */ @@ -1076,15 +1042,13 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) return ret_val; } - /* - * Check for the case where we have copper media and auto-neg is + /* Check for the case where we have copper media and auto-neg is * enabled. In this case, we need to check and see if Auto-Neg * has completed, and if so, how the PHY and link partner has * flow control configured. */ if ((hw->phy.media_type == e1000_media_type_copper) && mac->autoneg) { - /* - * Read the MII Status Register and check to see if AutoNeg + /* Read the MII Status Register and check to see if AutoNeg * has completed. We read this twice because this reg has * some "sticky" (latched) bits. */ @@ -1100,8 +1064,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) return ret_val; } - /* - * The AutoNeg process has completed, so we now need to + /* The AutoNeg process has completed, so we now need to * read both the Auto Negotiation Advertisement * Register (Address 4) and the Auto_Negotiation Base * Page Ability Register (Address 5) to determine how @@ -1115,8 +1078,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Two bits in the Auto Negotiation Advertisement Register + /* Two bits in the Auto Negotiation Advertisement Register * (Address 4) and two bits in the Auto Negotiation Base * Page Ability Register (Address 5) determine flow control * for both the PHY and the link partner. The following @@ -1151,8 +1113,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) */ if ((mii_nway_adv_reg & NWAY_AR_PAUSE) && (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) { - /* - * Now we need to check if the user selected Rx ONLY + /* Now we need to check if the user selected Rx ONLY * of pause frames. In this case, we had to advertise * FULL flow control because we could not advertise Rx * ONLY. Hence, we must now check to see if we need to @@ -1166,8 +1127,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) e_dbg("Flow Control = Rx PAUSE frames only.\n"); } } - /* - * For receiving PAUSE frames ONLY. + /* For receiving PAUSE frames ONLY. * * LOCAL DEVICE | LINK PARTNER * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result @@ -1181,8 +1141,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) hw->fc.current_mode = e1000_fc_tx_pause; e_dbg("Flow Control = Tx PAUSE frames only.\n"); } - /* - * For transmitting PAUSE frames ONLY. + /* For transmitting PAUSE frames ONLY. * * LOCAL DEVICE | LINK PARTNER * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result @@ -1196,16 +1155,14 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) hw->fc.current_mode = e1000_fc_rx_pause; e_dbg("Flow Control = Rx PAUSE frames only.\n"); } else { - /* - * Per the IEEE spec, at this point flow control + /* Per the IEEE spec, at this point flow control * should be disabled. */ hw->fc.current_mode = e1000_fc_none; e_dbg("Flow Control = NONE.\n"); } - /* - * Now we need to do one last check... If we auto- + /* Now we need to do one last check... If we auto- * negotiated to HALF DUPLEX, flow control should not be * enabled per IEEE 802.3 spec. */ @@ -1218,8 +1175,7 @@ s32 e1000e_config_fc_after_link_up(struct e1000_hw *hw) if (duplex == HALF_DUPLEX) hw->fc.current_mode = e1000_fc_none; - /* - * Now we call a subroutine to actually force the MAC + /* Now we call a subroutine to actually force the MAC * controller to use the correct flow control settings. */ ret_val = e1000e_force_mac_fc(hw); @@ -1520,8 +1476,7 @@ s32 e1000e_blink_led_generic(struct e1000_hw *hw) ledctl_blink = E1000_LEDCTL_LED0_BLINK | (E1000_LEDCTL_MODE_LED_ON << E1000_LEDCTL_LED0_MODE_SHIFT); } else { - /* - * set the blink bit for each LED that's "on" (0x0E) + /* set the blink bit for each LED that's "on" (0x0E) * in ledctl_mode2 */ ledctl_blink = hw->mac.ledctl_mode2; diff --git a/drivers/net/ethernet/intel/e1000e/manage.c b/drivers/net/ethernet/intel/e1000e/manage.c index bacc950fc68..6dc47beb3ad 100644 --- a/drivers/net/ethernet/intel/e1000e/manage.c +++ b/drivers/net/ethernet/intel/e1000e/manage.c @@ -143,8 +143,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw) return hw->mac.tx_pkt_filtering; } - /* - * If we can't read from the host interface for whatever + /* If we can't read from the host interface for whatever * reason, disable filtering. */ ret_val = e1000_mng_enable_host_if(hw); @@ -163,8 +162,7 @@ bool e1000e_enable_tx_pkt_filtering(struct e1000_hw *hw) hdr->checksum = 0; csum = e1000_calculate_checksum((u8 *)hdr, E1000_MNG_DHCP_COOKIE_LENGTH); - /* - * If either the checksums or signature don't match, then + /* If either the checksums or signature don't match, then * the cookie area isn't considered valid, in which case we * take the safe route of assuming Tx filtering is enabled. */ @@ -252,8 +250,7 @@ static s32 e1000_mng_host_if_write(struct e1000_hw *hw, u8 *buffer, /* Calculate length in DWORDs */ length >>= 2; - /* - * The device driver writes the relevant command block into the + /* The device driver writes the relevant command block into the * ram area. */ for (i = 0; i < length; i++) { diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index dadb13be479..6d06ed4e34b 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -146,9 +146,11 @@ static const struct e1000_reg_info e1000_reg_info_tbl[] = { {0, NULL} }; -/* +/** * e1000_regdump - register printout routine - */ + * @hw: pointer to the HW structure + * @reginfo: pointer to the register info table + **/ static void e1000_regdump(struct e1000_hw *hw, struct e1000_reg_info *reginfo) { int n = 0; @@ -196,9 +198,10 @@ static void e1000e_dump_ps_pages(struct e1000_adapter *adapter, } } -/* +/** * e1000e_dump - Print registers, Tx-ring and Rx-ring - */ + * @adapter: board private structure + **/ static void e1000e_dump(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -623,8 +626,7 @@ map_skb: rx_desc->read.buffer_addr = cpu_to_le64(buffer_info->dma); if (unlikely(!(i & (E1000_RX_BUFFER_WRITE - 1)))) { - /* - * Force memory writes to complete before letting h/w + /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). @@ -692,8 +694,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_ring *rx_ring, goto no_buffers; } } - /* - * Refresh the desc even if buffer_addrs + /* Refresh the desc even if buffer_addrs * didn't change because each write-back * erases this info. */ @@ -726,8 +727,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_ring *rx_ring, rx_desc->read.buffer_addr[0] = cpu_to_le64(buffer_info->dma); if (unlikely(!(i & (E1000_RX_BUFFER_WRITE - 1)))) { - /* - * Force memory writes to complete before letting h/w + /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). @@ -817,7 +817,8 @@ check_page: /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, - * such as IA-64). */ + * such as IA-64). + */ wmb(); if (adapter->flags2 & FLAG2_PCIM2PCI_ARBITER_WA) e1000e_update_rdt_wa(rx_ring, i); @@ -891,8 +892,7 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, length = le16_to_cpu(rx_desc->wb.upper.length); - /* - * !EOP means multiple descriptors were used to store a single + /* !EOP means multiple descriptors were used to store a single * packet, if that's the case we need to toss it. In fact, we * need to toss every packet with the EOP bit clear and the * next frame that _does_ have the EOP bit set, as it is by @@ -933,8 +933,7 @@ static bool e1000_clean_rx_irq(struct e1000_ring *rx_ring, int *work_done, total_rx_bytes += length; total_rx_packets++; - /* - * code added for copybreak, this should improve + /* code added for copybreak, this should improve * performance for small packets with large amounts * of reassembly being done in the stack */ @@ -1032,15 +1031,13 @@ static void e1000_print_hw_hang(struct work_struct *work) if (!adapter->tx_hang_recheck && (adapter->flags2 & FLAG2_DMA_BURST)) { - /* - * May be block on write-back, flush and detect again + /* May be block on write-back, flush and detect again * flush pending descriptor writebacks to memory */ ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); /* execute the writes immediately */ e1e_flush(); - /* - * Due to rare timing issues, write to TIDV again to ensure + /* Due to rare timing issues, write to TIDV again to ensure * the write is successful */ ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); @@ -1169,8 +1166,7 @@ static bool e1000_clean_tx_irq(struct e1000_ring *tx_ring) } if (adapter->detect_tx_hung) { - /* - * Detect a transmit hang in hardware, this serializes the + /* Detect a transmit hang in hardware, this serializes the * check with the clearing of time_stamp and movement of i */ adapter->detect_tx_hung = false; @@ -1270,14 +1266,12 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, skb_put(skb, length); { - /* - * this looks ugly, but it seems compiler issues make + /* this looks ugly, but it seems compiler issues make * it more efficient than reusing j */ int l1 = le16_to_cpu(rx_desc->wb.upper.length[0]); - /* - * page alloc/put takes too long and effects small + /* page alloc/put takes too long and effects small * packet throughput, so unsplit small packets and * save the alloc/put only valid in softirq (napi) * context to call kmap_* @@ -1288,8 +1282,7 @@ static bool e1000_clean_rx_irq_ps(struct e1000_ring *rx_ring, int *work_done, ps_page = &buffer_info->ps_pages[0]; - /* - * there is no documentation about how to call + /* there is no documentation about how to call * kmap_atomic, so we can't hold the mapping * very long */ @@ -1486,14 +1479,16 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, skb_shinfo(rxtop)->nr_frags, buffer_info->page, 0, length); /* re-use the current skb, we only consumed the - * page */ + * page + */ buffer_info->skb = skb; skb = rxtop; rxtop = NULL; e1000_consume_page(buffer_info, skb, length); } else { /* no chain, got EOP, this buf is the packet - * copybreak to save the put_page/alloc_page */ + * copybreak to save the put_page/alloc_page + */ if (length <= copybreak && skb_tailroom(skb) >= length) { u8 *vaddr; @@ -1502,7 +1497,8 @@ static bool e1000_clean_jumbo_rx_irq(struct e1000_ring *rx_ring, int *work_done, length); kunmap_atomic(vaddr); /* re-use the page, so don't erase - * buffer_info->page */ + * buffer_info->page + */ skb_put(skb, length); } else { skb_fill_page_desc(skb, 0, @@ -1656,22 +1652,17 @@ static irqreturn_t e1000_intr_msi(int irq, void *data) struct e1000_hw *hw = &adapter->hw; u32 icr = er32(ICR); - /* - * read ICR disables interrupts using IAM - */ - + /* read ICR disables interrupts using IAM */ if (icr & E1000_ICR_LSC) { hw->mac.get_link_status = true; - /* - * ICH8 workaround-- Call gig speed drop workaround on cable + /* ICH8 workaround-- Call gig speed drop workaround on cable * disconnect (LSC) before accessing any PHY registers */ if ((adapter->flags & FLAG_LSC_GIG_SPEED_DROP) && (!(er32(STATUS) & E1000_STATUS_LU))) schedule_work(&adapter->downshift_task); - /* - * 80003ES2LAN workaround-- For packet buffer work-around on + /* 80003ES2LAN workaround-- For packet buffer work-around on * link down event; disable receives here in the ISR and reset * adapter in watchdog */ @@ -1713,31 +1704,27 @@ static irqreturn_t e1000_intr(int irq, void *data) if (!icr || test_bit(__E1000_DOWN, &adapter->state)) return IRQ_NONE; /* Not our interrupt */ - /* - * IMS will not auto-mask if INT_ASSERTED is not set, and if it is + /* IMS will not auto-mask if INT_ASSERTED is not set, and if it is * not set, then the adapter didn't send an interrupt */ if (!(icr & E1000_ICR_INT_ASSERTED)) return IRQ_NONE; - /* - * Interrupt Auto-Mask...upon reading ICR, + /* Interrupt Auto-Mask...upon reading ICR, * interrupts are masked. No need for the * IMC write */ if (icr & E1000_ICR_LSC) { hw->mac.get_link_status = true; - /* - * ICH8 workaround-- Call gig speed drop workaround on cable + /* ICH8 workaround-- Call gig speed drop workaround on cable * disconnect (LSC) before accessing any PHY registers */ if ((adapter->flags & FLAG_LSC_GIG_SPEED_DROP) && (!(er32(STATUS) & E1000_STATUS_LU))) schedule_work(&adapter->downshift_task); - /* - * 80003ES2LAN workaround-- + /* 80003ES2LAN workaround-- * For packet buffer work-around on link down event; * disable receives here in the ISR and * reset adapter in watchdog @@ -2469,8 +2456,7 @@ static void e1000_set_itr(struct e1000_adapter *adapter) set_itr_now: if (new_itr != adapter->itr) { - /* - * this attempts to bias the interrupt rate towards Bulk + /* this attempts to bias the interrupt rate towards Bulk * by adding intermediate steps when interrupt rate is * increasing */ @@ -2740,8 +2726,7 @@ static void e1000_init_manageability_pt(struct e1000_adapter *adapter) manc = er32(MANC); - /* - * enable receiving management packets to the host. this will probably + /* enable receiving management packets to the host. this will probably * generate destination unreachable messages from the host OS, but * the packets will be handled on SMBUS */ @@ -2754,8 +2739,7 @@ static void e1000_init_manageability_pt(struct e1000_adapter *adapter) break; case e1000_82574: case e1000_82583: - /* - * Check if IPMI pass-through decision filter already exists; + /* Check if IPMI pass-through decision filter already exists; * if so, enable it. */ for (i = 0, j = 0; i < 8; i++) { @@ -2827,8 +2811,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) u32 txdctl = er32(TXDCTL(0)); txdctl &= ~(E1000_TXDCTL_PTHRESH | E1000_TXDCTL_HTHRESH | E1000_TXDCTL_WTHRESH); - /* - * set up some performance related parameters to encourage the + /* set up some performance related parameters to encourage the * hardware to use the bus more efficiently in bursts, depends * on the tx_int_delay to be enabled, * wthresh = 1 ==> burst write is disabled to avoid Tx stalls @@ -2845,8 +2828,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) if (adapter->flags & FLAG_TARC_SPEED_MODE_BIT) { tarc = er32(TARC(0)); - /* - * set the speed mode bit, we'll clear it if we're not at + /* set the speed mode bit, we'll clear it if we're not at * gigabit link later */ #define SPEED_MODE_BIT (1 << 21) @@ -2967,8 +2949,7 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) rfctl |= E1000_RFCTL_EXTEN; ew32(RFCTL, rfctl); - /* - * 82571 and greater support packet-split where the protocol + /* 82571 and greater support packet-split where the protocol * header is placed in skb->data and the packet data is * placed in pages hanging off of skb_shinfo(skb)->nr_frags. * In the case of a non-split, skb->data is linearly filled, @@ -3016,7 +2997,8 @@ static void e1000_setup_rctl(struct e1000_adapter *adapter) /* This is useful for sniffing bad packets. */ if (adapter->netdev->features & NETIF_F_RXALL) { /* UPE and MPE will be handled by normal PROMISC logic - * in e1000e_set_rx_mode */ + * in e1000e_set_rx_mode + */ rctl |= (E1000_RCTL_SBP | /* Receive bad packets */ E1000_RCTL_BAM | /* RX All Bcast Pkts */ E1000_RCTL_PMCF); /* RX All MAC Ctrl Pkts */ @@ -3071,8 +3053,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) usleep_range(10000, 20000); if (adapter->flags2 & FLAG2_DMA_BURST) { - /* - * set the writeback threshold (only takes effect if the RDTR + /* set the writeback threshold (only takes effect if the RDTR * is set). set GRAN=1 and write back up to 0x4 worth, and * enable prefetching of 0x20 Rx descriptors * granularity = 01 @@ -3083,8 +3064,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ew32(RXDCTL(0), E1000_RXDCTL_DMA_BURST_ENABLE); ew32(RXDCTL(1), E1000_RXDCTL_DMA_BURST_ENABLE); - /* - * override the delay timers for enabling bursting, only if + /* override the delay timers for enabling bursting, only if * the value was not set by the user via module options */ if (adapter->rx_int_delay == DEFAULT_RDTR) @@ -3108,8 +3088,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ew32(CTRL_EXT, ctrl_ext); e1e_flush(); - /* - * Setup the HW Rx Head and Tail Descriptor Pointers and + /* Setup the HW Rx Head and Tail Descriptor Pointers and * the Base and Length of the Rx Descriptor Ring */ rdba = rx_ring->dma; @@ -3130,8 +3109,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ew32(RXCSUM, rxcsum); if (adapter->hw.mac.type == e1000_pch2lan) { - /* - * With jumbo frames, excessive C-state transition + /* With jumbo frames, excessive C-state transition * latencies result in dropped transactions. */ if (adapter->netdev->mtu > ETH_DATA_LEN) { @@ -3216,8 +3194,7 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev) if (!netdev_uc_empty(netdev) && rar_entries) { struct netdev_hw_addr *ha; - /* - * write the addresses in reverse order to avoid write + /* write the addresses in reverse order to avoid write * combining */ netdev_for_each_uc_addr(ha, netdev) { @@ -3269,8 +3246,7 @@ static void e1000e_set_rx_mode(struct net_device *netdev) if (netdev->flags & IFF_ALLMULTI) { rctl |= E1000_RCTL_MPE; } else { - /* - * Write addresses to the MTA, if the attempt fails + /* Write addresses to the MTA, if the attempt fails * then we should just turn on promiscuous mode so * that we can at least receive multicast traffic */ @@ -3279,8 +3255,7 @@ static void e1000e_set_rx_mode(struct net_device *netdev) rctl |= E1000_RCTL_MPE; } e1000e_vlan_filter_enable(adapter); - /* - * Write addresses to available RAR registers, if there is not + /* Write addresses to available RAR registers, if there is not * sufficient space to store all the addresses then enable * unicast promiscuous mode */ @@ -3315,8 +3290,7 @@ static void e1000e_setup_rss_hash(struct e1000_adapter *adapter) for (i = 0; i < 32; i++) ew32(RETA(i), 0); - /* - * Disable raw packet checksumming so that RSS hash is placed in + /* Disable raw packet checksumming so that RSS hash is placed in * descriptor on writeback. */ rxcsum = er32(RXCSUM); @@ -3408,8 +3382,7 @@ void e1000e_reset(struct e1000_adapter *adapter) ew32(PBA, pba); if (adapter->max_frame_size > ETH_FRAME_LEN + ETH_FCS_LEN) { - /* - * To maintain wire speed transmits, the Tx FIFO should be + /* To maintain wire speed transmits, the Tx FIFO should be * large enough to accommodate two full transmit packets, * rounded up to the next 1KB and expressed in KB. Likewise, * the Rx FIFO should be large enough to accommodate at least @@ -3421,8 +3394,7 @@ void e1000e_reset(struct e1000_adapter *adapter) tx_space = pba >> 16; /* lower 16 bits has Rx packet buffer allocation size in KB */ pba &= 0xffff; - /* - * the Tx fifo also stores 16 bytes of information about the Tx + /* the Tx fifo also stores 16 bytes of information about the Tx * but don't include ethernet FCS because hardware appends it */ min_tx_space = (adapter->max_frame_size + @@ -3435,8 +3407,7 @@ void e1000e_reset(struct e1000_adapter *adapter) min_rx_space = ALIGN(min_rx_space, 1024); min_rx_space >>= 10; - /* - * If current Tx allocation is less than the min Tx FIFO size, + /* If current Tx allocation is less than the min Tx FIFO size, * and the min Tx FIFO size is less than the current Rx FIFO * allocation, take space away from current Rx allocation */ @@ -3444,8 +3415,7 @@ void e1000e_reset(struct e1000_adapter *adapter) ((min_tx_space - tx_space) < pba)) { pba -= min_tx_space - tx_space; - /* - * if short on Rx space, Rx wins and must trump Tx + /* if short on Rx space, Rx wins and must trump Tx * adjustment */ if (pba < min_rx_space) @@ -3455,8 +3425,7 @@ void e1000e_reset(struct e1000_adapter *adapter) ew32(PBA, pba); } - /* - * flow control settings + /* flow control settings * * The high water mark must be low enough to fit one full frame * (or the size used for early receive) above it in the Rx FIFO. @@ -3490,8 +3459,7 @@ void e1000e_reset(struct e1000_adapter *adapter) fc->low_water = fc->high_water - 8; break; case e1000_pchlan: - /* - * Workaround PCH LOM adapter hangs with certain network + /* Workaround PCH LOM adapter hangs with certain network * loads. If hangs persist, try disabling Tx flow control. */ if (adapter->netdev->mtu > ETH_DATA_LEN) { @@ -3516,8 +3484,7 @@ void e1000e_reset(struct e1000_adapter *adapter) break; } - /* - * Alignment of Tx data is on an arbitrary byte boundary with the + /* Alignment of Tx data is on an arbitrary byte boundary with the * maximum size per Tx descriptor limited only to the transmit * allocation of the packet buffer minus 96 bytes with an upper * limit of 24KB due to receive synchronization limitations. @@ -3525,8 +3492,7 @@ void e1000e_reset(struct e1000_adapter *adapter) adapter->tx_fifo_limit = min_t(u32, ((er32(PBA) >> 16) << 10) - 96, 24 << 10); - /* - * Disable Adaptive Interrupt Moderation if 2 full packets cannot + /* Disable Adaptive Interrupt Moderation if 2 full packets cannot * fit in receive buffer. */ if (adapter->itr_setting & 0x3) { @@ -3549,8 +3515,7 @@ void e1000e_reset(struct e1000_adapter *adapter) /* Allow time for pending master requests to run */ mac->ops.reset_hw(hw); - /* - * For parts with AMT enabled, let the firmware know + /* For parts with AMT enabled, let the firmware know * that the network interface is in control */ if (adapter->flags & FLAG_HAS_AMT) @@ -3579,8 +3544,7 @@ void e1000e_reset(struct e1000_adapter *adapter) if ((adapter->flags & FLAG_HAS_SMART_POWER_DOWN) && !(adapter->flags & FLAG_SMART_POWER_DOWN)) { u16 phy_data = 0; - /* - * speed up time to link by disabling smart power down, ignore + /* speed up time to link by disabling smart power down, ignore * the return value of this function because there is nothing * different we would do if it failed */ @@ -3628,8 +3592,7 @@ static void e1000e_flush_descriptors(struct e1000_adapter *adapter) /* execute the writes immediately */ e1e_flush(); - /* - * due to rare timing issues, write to TIDV/RDTR again to ensure the + /* due to rare timing issues, write to TIDV/RDTR again to ensure the * write is successful */ ew32(TIDV, adapter->tx_int_delay | E1000_TIDV_FPD); @@ -3647,8 +3610,7 @@ void e1000e_down(struct e1000_adapter *adapter) struct e1000_hw *hw = &adapter->hw; u32 tctl, rctl; - /* - * signal that we're down so the interrupt handler does not + /* signal that we're down so the interrupt handler does not * reschedule our watchdog timer */ set_bit(__E1000_DOWN, &adapter->state); @@ -3691,8 +3653,7 @@ void e1000e_down(struct e1000_adapter *adapter) if (!pci_channel_offline(adapter->pdev)) e1000e_reset(adapter); - /* - * TODO: for power management, we could drop the link and + /* TODO: for power management, we could drop the link and * pci_disable_device here. */ } @@ -3755,8 +3716,7 @@ static irqreturn_t e1000_intr_msi_test(int irq, void *data) e_dbg("icr is %08X\n", icr); if (icr & E1000_ICR_RXSEQ) { adapter->flags &= ~FLAG_MSI_TEST_FAILED; - /* - * Force memory writes to complete before acknowledging the + /* Force memory writes to complete before acknowledging the * interrupt is handled. */ wmb(); @@ -3786,7 +3746,8 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) e1000e_reset_interrupt_capability(adapter); /* Assume that the test fails, if it succeeds then the test - * MSI irq handler will unset this flag */ + * MSI irq handler will unset this flag + */ adapter->flags |= FLAG_MSI_TEST_FAILED; err = pci_enable_msi(adapter->pdev); @@ -3800,8 +3761,7 @@ static int e1000_test_msi_interrupt(struct e1000_adapter *adapter) goto msi_test_failed; } - /* - * Force memory writes to complete before enabling and firing an + /* Force memory writes to complete before enabling and firing an * interrupt. */ wmb(); @@ -3901,8 +3861,7 @@ static int e1000_open(struct net_device *netdev) if (err) goto err_setup_rx; - /* - * If AMT is enabled, let the firmware know that the network + /* If AMT is enabled, let the firmware know that the network * interface is now open and reset the part to a known state. */ if (adapter->flags & FLAG_HAS_AMT) { @@ -3923,8 +3882,7 @@ static int e1000_open(struct net_device *netdev) PM_QOS_CPU_DMA_LATENCY, PM_QOS_DEFAULT_VALUE); - /* - * before we allocate an interrupt, we must be ready to handle it. + /* before we allocate an interrupt, we must be ready to handle it. * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt * as soon as we call pci_request_irq, so we have to setup our * clean_rx handler before we do so. @@ -3935,8 +3893,7 @@ static int e1000_open(struct net_device *netdev) if (err) goto err_req_irq; - /* - * Work around PCIe errata with MSI interrupts causing some chipsets to + /* Work around PCIe errata with MSI interrupts causing some chipsets to * ignore e1000e MSI messages, which means we need to test our MSI * interrupt now */ @@ -4017,16 +3974,14 @@ static int e1000_close(struct net_device *netdev) e1000e_free_tx_resources(adapter->tx_ring); e1000e_free_rx_resources(adapter->rx_ring); - /* - * kill manageability vlan ID if supported, but not if a vlan with + /* kill manageability vlan ID if supported, but not if a vlan with * the same ID is registered on the host OS (let 8021q kill it) */ if (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN) e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); - /* - * If AMT is enabled, let the firmware know that the network + /* If AMT is enabled, let the firmware know that the network * interface is now closed */ if ((adapter->flags & FLAG_HAS_AMT) && @@ -4065,8 +4020,7 @@ static int e1000_set_mac(struct net_device *netdev, void *p) /* activate the work around */ e1000e_set_laa_state_82571(&adapter->hw, 1); - /* - * Hold a copy of the LAA in RAR[14] This is done so that + /* Hold a copy of the LAA in RAR[14] This is done so that * between the time RAR[0] gets clobbered and the time it * gets fixed (in e1000_watchdog), the actual LAA is in one * of the RARs and no incoming packets directed to this port @@ -4099,10 +4053,13 @@ static void e1000e_update_phy_task(struct work_struct *work) e1000_get_phy_info(&adapter->hw); } -/* +/** + * e1000_update_phy_info - timre call-back to update PHY info + * @data: pointer to adapter cast into an unsigned long + * * Need to wait a few seconds after link up to get diagnostic information from * the phy - */ + **/ static void e1000_update_phy_info(unsigned long data) { struct e1000_adapter *adapter = (struct e1000_adapter *) data; @@ -4129,8 +4086,7 @@ static void e1000e_update_phy_stats(struct e1000_adapter *adapter) if (ret_val) return; - /* - * A page set is expensive so check if already on desired page. + /* A page set is expensive so check if already on desired page. * If not, set to the page with the PHY status registers. */ hw->phy.addr = 1; @@ -4201,8 +4157,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) struct e1000_hw *hw = &adapter->hw; struct pci_dev *pdev = adapter->pdev; - /* - * Prevent stats update while adapter is being reset, or if the pci + /* Prevent stats update while adapter is being reset, or if the pci * connection is down. */ if (adapter->link_speed == 0) @@ -4270,8 +4225,7 @@ static void e1000e_update_stats(struct e1000_adapter *adapter) /* Rx Errors */ - /* - * RLEC on some newer hardware can be incorrect so build + /* RLEC on some newer hardware can be incorrect so build * our own version based on RUC and ROC */ netdev->stats.rx_errors = adapter->stats.rxerrc + @@ -4323,8 +4277,7 @@ static void e1000_phy_read_status(struct e1000_adapter *adapter) if (ret_val) e_warn("Error reading PHY register\n"); } else { - /* - * Do not read PHY registers if link is not up + /* Do not read PHY registers if link is not up * Set values to typical power-on defaults */ phy->bmcr = (BMCR_SPEED1000 | BMCR_ANENABLE | BMCR_FULLDPLX); @@ -4362,8 +4315,7 @@ static bool e1000e_has_link(struct e1000_adapter *adapter) bool link_active = false; s32 ret_val = 0; - /* - * get_link_status is set on LSC (link status) interrupt or + /* get_link_status is set on LSC (link status) interrupt or * Rx sequence error interrupt. get_link_status will stay * false until the check_for_link establishes link * for copper adapters ONLY @@ -4415,8 +4367,7 @@ static void e1000e_check_82574_phy_workaround(struct e1000_adapter *adapter) { struct e1000_hw *hw = &adapter->hw; - /* - * With 82574 controllers, PHY needs to be checked periodically + /* With 82574 controllers, PHY needs to be checked periodically * for hung state and reset, if two calls return true */ if (e1000_check_phy_82574(hw)) @@ -4484,8 +4435,7 @@ static void e1000_watchdog_task(struct work_struct *work) &adapter->link_speed, &adapter->link_duplex); e1000_print_link_info(adapter); - /* - * On supported PHYs, check for duplex mismatch only + /* On supported PHYs, check for duplex mismatch only * if link has autonegotiated at 10/100 half */ if ((hw->phy.type == e1000_phy_igp_3 || @@ -4515,8 +4465,7 @@ static void e1000_watchdog_task(struct work_struct *work) break; } - /* - * workaround: re-program speed mode bit after + /* workaround: re-program speed mode bit after * link-up event */ if ((adapter->flags & FLAG_TARC_SPEED_MODE_BIT) && @@ -4527,8 +4476,7 @@ static void e1000_watchdog_task(struct work_struct *work) ew32(TARC(0), tarc0); } - /* - * disable TSO for pcie and 10/100 speeds, to avoid + /* disable TSO for pcie and 10/100 speeds, to avoid * some hardware issues */ if (!(adapter->flags & FLAG_TSO_FORCE)) { @@ -4549,16 +4497,14 @@ static void e1000_watchdog_task(struct work_struct *work) } } - /* - * enable transmits in the hardware, need to do this + /* enable transmits in the hardware, need to do this * after setting TARC(0) */ tctl = er32(TCTL); tctl |= E1000_TCTL_EN; ew32(TCTL, tctl); - /* - * Perform any post-link-up configuration before + /* Perform any post-link-up configuration before * reporting link up. */ if (phy->ops.cfg_on_link_up) @@ -4609,8 +4555,7 @@ link_up: if (!netif_carrier_ok(netdev) && (e1000_desc_unused(tx_ring) + 1 < tx_ring->count)) { - /* - * We've lost link, so the controller stops DMA, + /* We've lost link, so the controller stops DMA, * but we've got queued Tx work that's never going * to get done, so reset controller to flush Tx. * (Do the reset outside of interrupt context). @@ -4622,8 +4567,7 @@ link_up: /* Simple mode for Interrupt Throttle Rate (ITR) */ if (adapter->itr_setting == 4) { - /* - * Symmetric Tx/Rx gets a reduced ITR=2000; + /* Symmetric Tx/Rx gets a reduced ITR=2000; * Total asymmetrical Tx or Rx gets ITR=8000; * everyone else is between 2000-8000. */ @@ -4648,8 +4592,7 @@ link_up: /* Force detection of hung controller every watchdog period */ adapter->detect_tx_hung = true; - /* - * With 82571 controllers, LAA may be overwritten due to controller + /* With 82571 controllers, LAA may be overwritten due to controller * reset from the other port. Set the appropriate LAA in RAR[0] */ if (e1000e_get_laa_state_82571(hw)) @@ -4948,8 +4891,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count) if (unlikely(tx_flags & E1000_TX_FLAGS_NO_FCS)) tx_desc->lower.data &= ~(cpu_to_le32(E1000_TXD_CMD_IFCS)); - /* - * Force memory writes to complete before letting h/w + /* Force memory writes to complete before letting h/w * know there are new descriptors to fetch. (Only * applicable for weak-ordered memory model archs, * such as IA-64). @@ -4963,8 +4905,7 @@ static void e1000_tx_queue(struct e1000_ring *tx_ring, int tx_flags, int count) else writel(i, tx_ring->tail); - /* - * we need this if more than one processor can write to our tail + /* we need this if more than one processor can write to our tail * at a time, it synchronizes IO on IA64/Altix systems */ mmiowb(); @@ -5014,15 +4955,13 @@ static int __e1000_maybe_stop_tx(struct e1000_ring *tx_ring, int size) struct e1000_adapter *adapter = tx_ring->adapter; netif_stop_queue(adapter->netdev); - /* - * Herbert's original patch had: + /* Herbert's original patch had: * smp_mb__after_netif_stop_queue(); * but since that doesn't exist yet, just open code it. */ smp_mb(); - /* - * We need to check again in a case another CPU has just + /* We need to check again in a case another CPU has just * made room available. */ if (e1000_desc_unused(tx_ring) < size) @@ -5067,8 +5006,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, return NETDEV_TX_OK; } - /* - * The minimum packet size with TCTL.PSP set is 17 bytes so + /* The minimum packet size with TCTL.PSP set is 17 bytes so * pad skb in order to meet this minimum size requirement */ if (unlikely(skb->len < 17)) { @@ -5082,14 +5020,12 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if (mss) { u8 hdr_len; - /* - * TSO Workaround for 82571/2/3 Controllers -- if skb->data + /* TSO Workaround for 82571/2/3 Controllers -- if skb->data * points to just header, pull a few bytes of payload from * frags into skb->data */ hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - /* - * we do this workaround for ES2LAN, but it is un-necessary, + /* we do this workaround for ES2LAN, but it is un-necessary, * avoiding it could save a lot of cycles */ if (skb->data_len && (hdr_len == len)) { @@ -5120,8 +5056,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, if (adapter->hw.mac.tx_pkt_filtering) e1000_transfer_dhcp_info(adapter, skb); - /* - * need: count + 2 desc gap to keep tail from touching + /* need: count + 2 desc gap to keep tail from touching * head, otherwise try next time */ if (e1000_maybe_stop_tx(tx_ring, count + 2)) @@ -5145,8 +5080,7 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb, else if (e1000_tx_csum(tx_ring, skb)) tx_flags |= E1000_TX_FLAGS_CSUM; - /* - * Old method was to assume IPv4 packet by default if TSO was enabled. + /* Old method was to assume IPv4 packet by default if TSO was enabled. * 82571 hardware supports TSO capabilities for IPv6 as well... * no longer assume, we must. */ @@ -5233,8 +5167,7 @@ struct rtnl_link_stats64 *e1000e_get_stats64(struct net_device *netdev, /* Rx Errors */ - /* - * RLEC on some newer hardware can be incorrect so build + /* RLEC on some newer hardware can be incorrect so build * our own version based on RUC and ROC */ stats->rx_errors = adapter->stats.rxerrc + @@ -5303,8 +5236,7 @@ static int e1000_change_mtu(struct net_device *netdev, int new_mtu) if (netif_running(netdev)) e1000e_down(adapter); - /* - * NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN + /* NOTE: netdev_alloc_skb reserves 16 bytes, and typically NET_IP_ALIGN * means we reserve 2 more, this pushes us to allocate from the next * larger slab size. * i.e. RXBUFFER_2048 --> size-4096 slab @@ -5566,8 +5498,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake, if (adapter->hw.phy.type == e1000_phy_igp_3) e1000e_igp3_phy_powerdown_workaround_ich8lan(&adapter->hw); - /* - * Release control of h/w to f/w. If f/w is AMT enabled, this + /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ e1000e_release_hw_control(adapter); @@ -5594,8 +5525,7 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep, struct net_device *netdev = pci_get_drvdata(pdev); struct e1000_adapter *adapter = netdev_priv(netdev); - /* - * The pci-e switch on some quad port adapters will report a + /* The pci-e switch on some quad port adapters will report a * correctable error when the MAC transitions from D0 to D3. To * prevent this we need to mask off the correctable errors on the * downstream port of the pci-e switch. @@ -5624,8 +5554,7 @@ static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) #else static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state) { - /* - * Both device and parent should have the same ASPM setting. + /* Both device and parent should have the same ASPM setting. * Disable ASPM in downstream component first and then upstream. */ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, state); @@ -5719,8 +5648,7 @@ static int __e1000_resume(struct pci_dev *pdev) netif_device_attach(netdev); - /* - * If the controller has AMT, do not set DRV_LOAD until the interface + /* If the controller has AMT, do not set DRV_LOAD until the interface * is up. For all other cases, let the f/w know that the h/w is now * under the control of the driver. */ @@ -5848,7 +5776,10 @@ static irqreturn_t e1000_intr_msix(int irq, void *data) return IRQ_HANDLED; } -/* +/** + * e1000_netpoll + * @netdev: network interface device structure + * * Polling 'interrupt' - used by things like netconsole to send skbs * without having to re-enable interrupts. It's not called while * the interrupt routine is executing. @@ -5973,8 +5904,7 @@ static void e1000_io_resume(struct pci_dev *pdev) netif_device_attach(netdev); - /* - * If the controller has AMT, do not set DRV_LOAD until the interface + /* If the controller has AMT, do not set DRV_LOAD until the interface * is up. For all other cases, let the f/w know that the h/w is now * under the control of the driver. */ @@ -6273,14 +6203,12 @@ static int __devinit e1000_probe(struct pci_dev *pdev, if (e1000e_enable_mng_pass_thru(&adapter->hw)) adapter->flags |= FLAG_MNG_PT_ENABLED; - /* - * before reading the NVM, reset the controller to + /* before reading the NVM, reset the controller to * put the device in a known good starting state */ adapter->hw.mac.ops.reset_hw(&adapter->hw); - /* - * systems with ASPM and others may see the checksum fail on the first + /* systems with ASPM and others may see the checksum fail on the first * attempt. Let's give it a few tries */ for (i = 0;; i++) { @@ -6335,8 +6263,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, adapter->rx_ring->count = E1000_DEFAULT_RXD; adapter->tx_ring->count = E1000_DEFAULT_TXD; - /* - * Initial Wake on LAN setting - If APM wake is enabled in + /* Initial Wake on LAN setting - If APM wake is enabled in * the EEPROM, enable the ACPI Magic Packet filter */ if (adapter->flags & FLAG_APME_IN_WUC) { @@ -6360,8 +6287,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, if (eeprom_data & eeprom_apme_mask) adapter->eeprom_wol |= E1000_WUFC_MAG; - /* - * now that we have the eeprom settings, apply the special cases + /* now that we have the eeprom settings, apply the special cases * where the eeprom may be wrong or the board simply won't support * wake on lan on a particular port */ @@ -6378,8 +6304,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, /* reset the hardware with the new settings */ e1000e_reset(adapter); - /* - * If the controller has AMT, do not set DRV_LOAD until the interface + /* If the controller has AMT, do not set DRV_LOAD until the interface * is up. For all other cases, let the f/w know that the h/w is now * under the control of the driver. */ @@ -6442,8 +6367,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev) struct e1000_adapter *adapter = netdev_priv(netdev); bool down = test_bit(__E1000_DOWN, &adapter->state); - /* - * The timers may be rescheduled, so explicitly disable them + /* The timers may be rescheduled, so explicitly disable them * from being rescheduled. */ if (!down) @@ -6468,8 +6392,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev) if (pci_dev_run_wake(pdev)) pm_runtime_get_noresume(&pdev->dev); - /* - * Release control of h/w to f/w. If f/w is AMT enabled, this + /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. */ e1000e_release_hw_control(adapter); diff --git a/drivers/net/ethernet/intel/e1000e/nvm.c b/drivers/net/ethernet/intel/e1000e/nvm.c index a969f1af1b4..b6468804cb2 100644 --- a/drivers/net/ethernet/intel/e1000e/nvm.c +++ b/drivers/net/ethernet/intel/e1000e/nvm.c @@ -279,8 +279,7 @@ static s32 e1000_ready_nvm_eeprom(struct e1000_hw *hw) e1e_flush(); udelay(1); - /* - * Read "Status Register" repeatedly until the LSB is cleared. + /* Read "Status Register" repeatedly until the LSB is cleared. * The EEPROM will signal that the command has been completed * by clearing bit 0 of the internal status register. If it's * not cleared within 'timeout', then error out. @@ -321,8 +320,7 @@ s32 e1000e_read_nvm_eerd(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) u32 i, eerd = 0; s32 ret_val = 0; - /* - * A check for invalid values: offset too large, too many words, + /* A check for invalid values: offset too large, too many words, * too many words for the offset, and not enough words. */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || @@ -364,8 +362,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) s32 ret_val; u16 widx = 0; - /* - * A check for invalid values: offset too large, too many words, + /* A check for invalid values: offset too large, too many words, * and not enough words. */ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || @@ -393,8 +390,7 @@ s32 e1000e_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) e1000_standby_nvm(hw); - /* - * Some SPI eeproms use the 8th address bit embedded in the + /* Some SPI eeproms use the 8th address bit embedded in the * opcode */ if ((nvm->address_bits == 8) && (offset >= 128)) @@ -461,8 +457,7 @@ s32 e1000_read_pba_string_generic(struct e1000_hw *hw, u8 *pba_num, return ret_val; } - /* - * if nvm_data is not ptr guard the PBA must be in legacy format which + /* if nvm_data is not ptr guard the PBA must be in legacy format which * means pba_ptr is actually our second data word for the PBA number * and we can decode it into an ascii string */ diff --git a/drivers/net/ethernet/intel/e1000e/param.c b/drivers/net/ethernet/intel/e1000e/param.c index dfbfa7fd98c..1fbb31554e4 100644 --- a/drivers/net/ethernet/intel/e1000e/param.c +++ b/drivers/net/ethernet/intel/e1000e/param.c @@ -32,11 +32,9 @@ #include "e1000.h" -/* - * This is the only thing that needs to be changed to adjust the +/* This is the only thing that needs to be changed to adjust the * maximum number of ports that the driver can manage. */ - #define E1000_MAX_NIC 32 #define OPTION_UNSET -1 @@ -49,12 +47,10 @@ module_param(copybreak, uint, 0644); MODULE_PARM_DESC(copybreak, "Maximum size of packet that is copied to a new buffer on receive"); -/* - * All parameters are treated the same, as an integer array of values. +/* All parameters are treated the same, as an integer array of values. * This macro just reduces the need to repeat the same declaration code * over and over (plus this helps to avoid typo bugs). */ - #define E1000_PARAM_INIT { [0 ... E1000_MAX_NIC] = OPTION_UNSET } #define E1000_PARAM(X, desc) \ static int __devinitdata X[E1000_MAX_NIC+1] \ @@ -63,8 +59,7 @@ MODULE_PARM_DESC(copybreak, module_param_array_named(X, X, int, &num_##X, 0); \ MODULE_PARM_DESC(X, desc); -/* - * Transmit Interrupt Delay in units of 1.024 microseconds +/* Transmit Interrupt Delay in units of 1.024 microseconds * Tx interrupt delay needs to typically be set to something non-zero * * Valid Range: 0-65535 @@ -74,8 +69,7 @@ E1000_PARAM(TxIntDelay, "Transmit Interrupt Delay"); #define MAX_TXDELAY 0xFFFF #define MIN_TXDELAY 0 -/* - * Transmit Absolute Interrupt Delay in units of 1.024 microseconds +/* Transmit Absolute Interrupt Delay in units of 1.024 microseconds * * Valid Range: 0-65535 */ @@ -84,8 +78,7 @@ E1000_PARAM(TxAbsIntDelay, "Transmit Absolute Interrupt Delay"); #define MAX_TXABSDELAY 0xFFFF #define MIN_TXABSDELAY 0 -/* - * Receive Interrupt Delay in units of 1.024 microseconds +/* Receive Interrupt Delay in units of 1.024 microseconds * hardware will likely hang if you set this to anything but zero. * * Valid Range: 0-65535 @@ -94,8 +87,7 @@ E1000_PARAM(RxIntDelay, "Receive Interrupt Delay"); #define MAX_RXDELAY 0xFFFF #define MIN_RXDELAY 0 -/* - * Receive Absolute Interrupt Delay in units of 1.024 microseconds +/* Receive Absolute Interrupt Delay in units of 1.024 microseconds * * Valid Range: 0-65535 */ @@ -103,8 +95,7 @@ E1000_PARAM(RxAbsIntDelay, "Receive Absolute Interrupt Delay"); #define MAX_RXABSDELAY 0xFFFF #define MIN_RXABSDELAY 0 -/* - * Interrupt Throttle Rate (interrupts/sec) +/* Interrupt Throttle Rate (interrupts/sec) * * Valid Range: 100-100000 or one of: 0=off, 1=dynamic, 3=dynamic conservative */ @@ -113,8 +104,7 @@ E1000_PARAM(InterruptThrottleRate, "Interrupt Throttling Rate"); #define MAX_ITR 100000 #define MIN_ITR 100 -/* - * IntMode (Interrupt Mode) +/* IntMode (Interrupt Mode) * * Valid Range: varies depending on kernel configuration & hardware support * @@ -132,8 +122,7 @@ E1000_PARAM(IntMode, "Interrupt Mode"); #define MAX_INTMODE 2 #define MIN_INTMODE 0 -/* - * Enable Smart Power Down of the PHY +/* Enable Smart Power Down of the PHY * * Valid Range: 0, 1 * @@ -141,8 +130,7 @@ E1000_PARAM(IntMode, "Interrupt Mode"); */ E1000_PARAM(SmartPowerDownEnable, "Enable PHY smart power down"); -/* - * Enable Kumeran Lock Loss workaround +/* Enable Kumeran Lock Loss workaround * * Valid Range: 0, 1 * @@ -150,8 +138,7 @@ E1000_PARAM(SmartPowerDownEnable, "Enable PHY smart power down"); */ E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround"); -/* - * Write Protect NVM +/* Write Protect NVM * * Valid Range: 0, 1 * @@ -159,8 +146,7 @@ E1000_PARAM(KumeranLockLoss, "Enable Kumeran lock loss workaround"); */ E1000_PARAM(WriteProtectNVM, "Write-protect NVM [WARNING: disabling this can lead to corrupted NVM]"); -/* - * Enable CRC Stripping +/* Enable CRC Stripping * * Valid Range: 0, 1 * @@ -351,8 +337,7 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) if (num_InterruptThrottleRate > bd) { adapter->itr = InterruptThrottleRate[bd]; - /* - * Make sure a message is printed for non-special + /* Make sure a message is printed for non-special * values. And in case of an invalid option, display * warning, use default and go through itr/itr_setting * adjustment logic below @@ -361,14 +346,12 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) e1000_validate_option(&adapter->itr, &opt, adapter)) adapter->itr = opt.def; } else { - /* - * If no option specified, use default value and go + /* If no option specified, use default value and go * through the logic below to adjust itr/itr_setting */ adapter->itr = opt.def; - /* - * Make sure a message is printed for non-special + /* Make sure a message is printed for non-special * default values */ if (adapter->itr > 4) @@ -400,8 +383,7 @@ void __devinit e1000e_check_options(struct e1000_adapter *adapter) opt.name); break; default: - /* - * Save the setting, because the dynamic bits + /* Save the setting, because the dynamic bits * change itr. * * Clear the lower two bits because diff --git a/drivers/net/ethernet/intel/e1000e/phy.c b/drivers/net/ethernet/intel/e1000e/phy.c index fc62a3f3a5b..28b38ff37e8 100644 --- a/drivers/net/ethernet/intel/e1000e/phy.c +++ b/drivers/net/ethernet/intel/e1000e/phy.c @@ -193,8 +193,7 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) return -E1000_ERR_PARAM; } - /* - * Set up Op-code, Phy Address, and register offset in the MDI + /* Set up Op-code, Phy Address, and register offset in the MDI * Control register. The MAC will take care of interfacing with the * PHY to retrieve the desired data. */ @@ -204,8 +203,7 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) ew32(MDIC, mdic); - /* - * Poll the ready bit to see if the MDI read completed + /* Poll the ready bit to see if the MDI read completed * Increasing the time out as testing showed failures with * the lower time out */ @@ -225,8 +223,7 @@ s32 e1000e_read_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 *data) } *data = (u16) mdic; - /* - * Allow some time after each MDIC transaction to avoid + /* Allow some time after each MDIC transaction to avoid * reading duplicate data in the next MDIC transaction. */ if (hw->mac.type == e1000_pch2lan) @@ -253,8 +250,7 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) return -E1000_ERR_PARAM; } - /* - * Set up Op-code, Phy Address, and register offset in the MDI + /* Set up Op-code, Phy Address, and register offset in the MDI * Control register. The MAC will take care of interfacing with the * PHY to retrieve the desired data. */ @@ -265,8 +261,7 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) ew32(MDIC, mdic); - /* - * Poll the ready bit to see if the MDI read completed + /* Poll the ready bit to see if the MDI read completed * Increasing the time out as testing showed failures with * the lower time out */ @@ -285,8 +280,7 @@ s32 e1000e_write_phy_reg_mdic(struct e1000_hw *hw, u32 offset, u16 data) return -E1000_ERR_PHY; } - /* - * Allow some time after each MDIC transaction to avoid + /* Allow some time after each MDIC transaction to avoid * reading duplicate data in the next MDIC transaction. */ if (hw->mac.type == e1000_pch2lan) @@ -708,8 +702,7 @@ s32 e1000_copper_link_setup_82577(struct e1000_hw *hw) if (ret_val) return ret_val; phy_data &= ~I82577_PHY_CTRL2_MDIX_CFG_MASK; - /* - * Options: + /* Options: * 0 - Auto (default) * 1 - MDI mode * 2 - MDI-X mode @@ -754,8 +747,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) if (phy->type != e1000_phy_bm) phy_data |= M88E1000_PSCR_ASSERT_CRS_ON_TX; - /* - * Options: + /* Options: * MDI/MDI-X = 0 (default) * 0 - Auto for all speeds * 1 - MDI mode @@ -780,8 +772,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) break; } - /* - * Options: + /* Options: * disable_polarity_correction = 0 (default) * Automatic Correction for Reversed Cable Polarity * 0 - Disabled @@ -818,8 +809,7 @@ s32 e1000e_copper_link_setup_m88(struct e1000_hw *hw) if ((phy->type == e1000_phy_m88) && (phy->revision < E1000_REVISION_4) && (phy->id != BME1000_E_PHY_ID_R2)) { - /* - * Force TX_CLK in the Extended PHY Specific Control Register + /* Force TX_CLK in the Extended PHY Specific Control Register * to 25MHz clock. */ ret_val = e1e_rphy(hw, M88E1000_EXT_PHY_SPEC_CTRL, &phy_data); @@ -899,8 +889,7 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw) return ret_val; } - /* - * Wait 100ms for MAC to configure PHY from NVM settings, to avoid + /* Wait 100ms for MAC to configure PHY from NVM settings, to avoid * timeout issues when LFS is enabled. */ msleep(100); @@ -936,8 +925,7 @@ s32 e1000e_copper_link_setup_igp(struct e1000_hw *hw) /* set auto-master slave resolution settings */ if (hw->mac.autoneg) { - /* - * when autonegotiation advertisement is only 1000Mbps then we + /* when autonegotiation advertisement is only 1000Mbps then we * should disable SmartSpeed and enable Auto MasterSlave * resolution as hardware default. */ @@ -1001,16 +989,14 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) return ret_val; } - /* - * Need to parse both autoneg_advertised and fc and set up + /* Need to parse both autoneg_advertised and fc and set up * the appropriate PHY registers. First we will parse for * autoneg_advertised software override. Since we can advertise * a plethora of combinations, we need to check each bit * individually. */ - /* - * First we clear all the 10/100 mb speed bits in the Auto-Neg + /* First we clear all the 10/100 mb speed bits in the Auto-Neg * Advertisement Register (Address 4) and the 1000 mb speed bits in * the 1000Base-T Control Register (Address 9). */ @@ -1056,8 +1042,7 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS; } - /* - * Check for a software override of the flow control settings, and + /* Check for a software override of the flow control settings, and * setup the PHY advertisement registers accordingly. If * auto-negotiation is enabled, then software will have to set the * "PAUSE" bits to the correct value in the Auto-Negotiation @@ -1076,15 +1061,13 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) */ switch (hw->fc.current_mode) { case e1000_fc_none: - /* - * Flow control (Rx & Tx) is completely disabled by a + /* Flow control (Rx & Tx) is completely disabled by a * software over-ride. */ mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); break; case e1000_fc_rx_pause: - /* - * Rx Flow control is enabled, and Tx Flow control is + /* Rx Flow control is enabled, and Tx Flow control is * disabled, by a software over-ride. * * Since there really isn't a way to advertise that we are @@ -1096,16 +1079,14 @@ static s32 e1000_phy_setup_autoneg(struct e1000_hw *hw) mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); break; case e1000_fc_tx_pause: - /* - * Tx Flow control is enabled, and Rx Flow control is + /* Tx Flow control is enabled, and Rx Flow control is * disabled, by a software over-ride. */ mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR; mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE; break; case e1000_fc_full: - /* - * Flow control (both Rx and Tx) is enabled by a software + /* Flow control (both Rx and Tx) is enabled by a software * over-ride. */ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE); @@ -1142,14 +1123,12 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) s32 ret_val; u16 phy_ctrl; - /* - * Perform some bounds checking on the autoneg advertisement + /* Perform some bounds checking on the autoneg advertisement * parameter. */ phy->autoneg_advertised &= phy->autoneg_mask; - /* - * If autoneg_advertised is zero, we assume it was not defaulted + /* If autoneg_advertised is zero, we assume it was not defaulted * by the calling code so we set to advertise full capability. */ if (!phy->autoneg_advertised) @@ -1163,8 +1142,7 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) } e_dbg("Restarting Auto-Neg\n"); - /* - * Restart auto-negotiation by setting the Auto Neg Enable bit and + /* Restart auto-negotiation by setting the Auto Neg Enable bit and * the Auto Neg Restart bit in the PHY control register. */ ret_val = e1e_rphy(hw, PHY_CONTROL, &phy_ctrl); @@ -1176,8 +1154,7 @@ static s32 e1000_copper_link_autoneg(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Does the user want to wait for Auto-Neg to complete here, or + /* Does the user want to wait for Auto-Neg to complete here, or * check at a later time (for example, callback routine). */ if (phy->autoneg_wait_to_complete) { @@ -1208,16 +1185,14 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw) bool link; if (hw->mac.autoneg) { - /* - * Setup autoneg and flow control advertisement and perform + /* Setup autoneg and flow control advertisement and perform * autonegotiation. */ ret_val = e1000_copper_link_autoneg(hw); if (ret_val) return ret_val; } else { - /* - * PHY will be set to 10H, 10F, 100H or 100F + /* PHY will be set to 10H, 10F, 100H or 100F * depending on user settings. */ e_dbg("Forcing Speed and Duplex\n"); @@ -1228,8 +1203,7 @@ s32 e1000e_setup_copper_link(struct e1000_hw *hw) } } - /* - * Check link status. Wait up to 100 microseconds for link to become + /* Check link status. Wait up to 100 microseconds for link to become * valid. */ ret_val = e1000e_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10, @@ -1273,8 +1247,7 @@ s32 e1000e_phy_force_speed_duplex_igp(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Clear Auto-Crossover to force MDI manually. IGP requires MDI + /* Clear Auto-Crossover to force MDI manually. IGP requires MDI * forced whenever speed and duplex are forced. */ ret_val = e1e_rphy(hw, IGP01E1000_PHY_PORT_CTRL, &phy_data); @@ -1328,8 +1301,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) u16 phy_data; bool link; - /* - * Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI + /* Clear Auto-Crossover to force MDI manually. M88E1000 requires MDI * forced whenever speed and duplex are forced. */ ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); @@ -1370,8 +1342,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) if (hw->phy.type != e1000_phy_m88) { e_dbg("Link taking longer than expected.\n"); } else { - /* - * We didn't get link. + /* We didn't get link. * Reset the DSP and cross our fingers. */ ret_val = e1e_wphy(hw, M88E1000_PHY_PAGE_SELECT, @@ -1398,8 +1369,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Resetting the phy means we need to re-force TX_CLK in the + /* Resetting the phy means we need to re-force TX_CLK in the * Extended PHY Specific Control Register to 25MHz clock from * the reset value of 2.5MHz. */ @@ -1408,8 +1378,7 @@ s32 e1000e_phy_force_speed_duplex_m88(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * In addition, we must re-enable CRS on Tx for both half and full + /* In addition, we must re-enable CRS on Tx for both half and full * duplex. */ ret_val = e1e_rphy(hw, M88E1000_PHY_SPEC_CTRL, &phy_data); @@ -1573,8 +1542,7 @@ s32 e1000e_set_d3_lplu_state(struct e1000_hw *hw, bool active) ret_val = e1e_wphy(hw, IGP02E1000_PHY_POWER_MGMT, data); if (ret_val) return ret_val; - /* - * LPLU and SmartSpeed are mutually exclusive. LPLU is used + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used * during Dx states where the power conservation is most * important. During driver activity we should enable * SmartSpeed, so performance is maintained. @@ -1702,8 +1670,7 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) s32 ret_val; u16 data, offset, mask; - /* - * Polarity is determined based on the speed of + /* Polarity is determined based on the speed of * our connection. */ ret_val = e1e_rphy(hw, IGP01E1000_PHY_PORT_STATUS, &data); @@ -1715,8 +1682,7 @@ s32 e1000_check_polarity_igp(struct e1000_hw *hw) offset = IGP01E1000_PHY_PCS_INIT_REG; mask = IGP01E1000_PHY_POLARITY_MASK; } else { - /* - * This really only applies to 10Mbps since + /* This really only applies to 10Mbps since * there is no polarity for 100Mbps (always 0). */ offset = IGP01E1000_PHY_PORT_STATUS; @@ -1745,8 +1711,7 @@ s32 e1000_check_polarity_ife(struct e1000_hw *hw) s32 ret_val; u16 phy_data, offset, mask; - /* - * Polarity is determined based on the reversal feature being enabled. + /* Polarity is determined based on the reversal feature being enabled. */ if (phy->polarity_correction) { offset = IFE_PHY_EXTENDED_STATUS_CONTROL; @@ -1791,8 +1756,7 @@ static s32 e1000_wait_autoneg(struct e1000_hw *hw) msleep(100); } - /* - * PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation + /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation * has completed. */ return ret_val; @@ -1814,15 +1778,13 @@ s32 e1000e_phy_has_link_generic(struct e1000_hw *hw, u32 iterations, u16 i, phy_status; for (i = 0; i < iterations; i++) { - /* - * Some PHYs require the PHY_STATUS register to be read + /* Some PHYs require the PHY_STATUS register to be read * twice due to the link bit being sticky. No harm doing * it across the board. */ ret_val = e1e_rphy(hw, PHY_STATUS, &phy_status); if (ret_val) - /* - * If the first read fails, another entity may have + /* If the first read fails, another entity may have * ownership of the resources, wait and try again to * see if they have relinquished the resources yet. */ @@ -1913,8 +1875,7 @@ s32 e1000e_get_cable_length_igp_2(struct e1000_hw *hw) if (ret_val) return ret_val; - /* - * Getting bits 15:9, which represent the combination of + /* Getting bits 15:9, which represent the combination of * coarse and fine gain values. The result is a number * that can be put into the lookup table to obtain the * approximate cable length. @@ -2285,15 +2246,13 @@ s32 e1000e_phy_init_script_igp3(struct e1000_hw *hw) e1e_wphy(hw, 0x1796, 0x0008); /* Change cg_icount + enable integbp for channels BCD */ e1e_wphy(hw, 0x1798, 0xD008); - /* - * Change cg_icount + enable integbp + change prop_factor_master + /* Change cg_icount + enable integbp + change prop_factor_master * to 8 for channel A */ e1e_wphy(hw, 0x1898, 0xD918); /* Disable AHT in Slave mode on channel A */ e1e_wphy(hw, 0x187A, 0x0800); - /* - * Enable LPLU and disable AN to 1000 in non-D0a states, + /* Enable LPLU and disable AN to 1000 in non-D0a states, * Enable SPD+B2B */ e1e_wphy(hw, 0x0019, 0x008D); @@ -2417,8 +2376,7 @@ s32 e1000e_determine_phy_address(struct e1000_hw *hw) e1000e_get_phy_id(hw); phy_type = e1000e_get_phy_type_from_id(hw->phy.id); - /* - * If phy_type is valid, break - we found our + /* If phy_type is valid, break - we found our * PHY address */ if (phy_type != e1000_phy_unknown) @@ -2478,8 +2436,7 @@ s32 e1000e_write_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 data) if (offset > MAX_PHY_MULTI_PAGE_REG) { u32 page_shift, page_select; - /* - * Page select is register 31 for phy address 1 and 22 for + /* Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for * phy address 1. */ @@ -2537,8 +2494,7 @@ s32 e1000e_read_phy_reg_bm(struct e1000_hw *hw, u32 offset, u16 *data) if (offset > MAX_PHY_MULTI_PAGE_REG) { u32 page_shift, page_select; - /* - * Page select is register 31 for phy address 1 and 22 for + /* Page select is register 31 for phy address 1 and 22 for * phy address 2 and 3. Page select is shifted only for * phy address 1. */ @@ -2683,8 +2639,7 @@ s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) return ret_val; } - /* - * Enable both PHY wakeup mode and Wakeup register page writes. + /* Enable both PHY wakeup mode and Wakeup register page writes. * Prevent a power state change by disabling ME and Host PHY wakeup. */ temp = *phy_reg; @@ -2698,8 +2653,7 @@ s32 e1000_enable_phy_wakeup_reg_access_bm(struct e1000_hw *hw, u16 *phy_reg) return ret_val; } - /* - * Select Host Wakeup Registers page - caller now able to write + /* Select Host Wakeup Registers page - caller now able to write * registers on the Wakeup registers page */ return e1000_set_page_igp(hw, (BM_WUC_PAGE << IGP_PAGE_SHIFT)); @@ -3038,8 +2992,7 @@ static s32 __e1000_write_phy_reg_hv(struct e1000_hw *hw, u32 offset, u16 data, if (page == HV_INTC_FC_PAGE_START) page = 0; - /* - * Workaround MDIO accesses being disabled after entering IEEE + /* Workaround MDIO accesses being disabled after entering IEEE * Power Down (when bit 11 of the PHY Control register is set) */ if ((hw->phy.type == e1000_phy_82578) && diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c index 8c12dbd0d6c..fdaaf2709d0 100644 --- a/drivers/net/ethernet/intel/igb/e1000_82575.c +++ b/drivers/net/ethernet/intel/igb/e1000_82575.c @@ -1028,6 +1028,15 @@ static s32 igb_check_for_link_82575(struct e1000_hw *hw) * continue to check for link. */ hw->mac.get_link_status = !hw->mac.serdes_has_link; + + /* Configure Flow Control now that Auto-Neg has completed. + * First, we need to restore the desired flow control + * settings because we may have had to re-autoneg with a + * different link partner. + */ + ret_val = igb_config_fc_after_link_up(hw); + if (ret_val) + hw_dbg("Error configuring flow control\n"); } else { ret_val = igb_check_for_copper_link(hw); } @@ -1345,7 +1354,7 @@ out: **/ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) { - u32 ctrl_ext, ctrl_reg, reg; + u32 ctrl_ext, ctrl_reg, reg, anadv_reg; bool pcs_autoneg; s32 ret_val = E1000_SUCCESS; u16 data; @@ -1433,27 +1442,45 @@ static s32 igb_setup_serdes_link_82575(struct e1000_hw *hw) reg &= ~(E1000_PCS_LCTL_AN_ENABLE | E1000_PCS_LCTL_FLV_LINK_UP | E1000_PCS_LCTL_FSD | E1000_PCS_LCTL_FORCE_LINK); - /* - * We force flow control to prevent the CTRL register values from being - * overwritten by the autonegotiated flow control values - */ - reg |= E1000_PCS_LCTL_FORCE_FCTRL; - if (pcs_autoneg) { /* Set PCS register for autoneg */ reg |= E1000_PCS_LCTL_AN_ENABLE | /* Enable Autoneg */ E1000_PCS_LCTL_AN_RESTART; /* Restart autoneg */ + + /* Disable force flow control for autoneg */ + reg &= ~E1000_PCS_LCTL_FORCE_FCTRL; + + /* Configure flow control advertisement for autoneg */ + anadv_reg = rd32(E1000_PCS_ANADV); + anadv_reg &= ~(E1000_TXCW_ASM_DIR | E1000_TXCW_PAUSE); + switch (hw->fc.requested_mode) { + case e1000_fc_full: + case e1000_fc_rx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + anadv_reg |= E1000_TXCW_PAUSE; + break; + case e1000_fc_tx_pause: + anadv_reg |= E1000_TXCW_ASM_DIR; + break; + default: + break; + } + wr32(E1000_PCS_ANADV, anadv_reg); + hw_dbg("Configuring Autoneg:PCS_LCTL=0x%08X\n", reg); } else { /* Set PCS register for forced link */ reg |= E1000_PCS_LCTL_FSD; /* Force Speed */ + /* Force flow control for forced link */ + reg |= E1000_PCS_LCTL_FORCE_FCTRL; + hw_dbg("Configuring Forced Link:PCS_LCTL=0x%08X\n", reg); } wr32(E1000_PCS_LCTL, reg); - if (!igb_sgmii_active_82575(hw)) + if (!pcs_autoneg && !igb_sgmii_active_82575(hw)) igb_force_mac_fc(hw); return ret_val; @@ -1927,6 +1954,12 @@ static s32 igb_reset_hw_82580(struct e1000_hw *hw) hw->dev_spec._82575.global_device_reset = false; + /* due to hw errata, global device reset doesn't always + * work on 82580 + */ + if (hw->mac.type == e1000_82580) + global_device_reset = false; + /* Get current control state. */ ctrl = rd32(E1000_CTRL); diff --git a/drivers/net/ethernet/intel/igb/e1000_defines.h b/drivers/net/ethernet/intel/igb/e1000_defines.h index 198d1484882..45dce06eff2 100644 --- a/drivers/net/ethernet/intel/igb/e1000_defines.h +++ b/drivers/net/ethernet/intel/igb/e1000_defines.h @@ -431,6 +431,10 @@ #define FLOW_CONTROL_ADDRESS_HIGH 0x00000100 #define FLOW_CONTROL_TYPE 0x8808 +/* Transmit Config Word */ +#define E1000_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */ +#define E1000_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */ + /* 802.1q VLAN Packet Size */ #define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */ #define E1000_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */ @@ -539,6 +543,9 @@ /* mPHY Near End Digital Loopback Override Bit */ #define E1000_MPHY_PCS_CLK_REG_DIGINELBEN 0x10 +#define E1000_PCS_LCTL_FORCE_FCTRL 0x80 +#define E1000_PCS_LSTS_AN_COMPLETE 0x10000 + /* PHY Control Register */ #define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */ #define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */ diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.c b/drivers/net/ethernet/intel/igb/e1000_i210.c index 41474298d36..fbcdbebb0b5 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.c +++ b/drivers/net/ethernet/intel/igb/e1000_i210.c @@ -35,11 +35,42 @@ #include "e1000_hw.h" #include "e1000_i210.h" -static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw); -static void igb_put_hw_semaphore_i210(struct e1000_hw *hw); -static s32 igb_write_nvm_srwr(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data); -static s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw); +/** + * igb_get_hw_semaphore_i210 - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + */ +static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + s32 ret_val = E1000_SUCCESS; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + /* Get the FW semaphore. */ + for (i = 0; i < timeout; i++) { + swsm = rd32(E1000_SWSM); + wr32(E1000_SWSM, swsm | E1000_SWSM_SWESMBI); + + /* Semaphore acquired if bit latched */ + if (rd32(E1000_SWSM) & E1000_SWSM_SWESMBI) + break; + + udelay(50); + } + + if (i == timeout) { + /* Release semaphores */ + igb_put_hw_semaphore(hw); + hw_dbg("Driver can't access the NVM\n"); + ret_val = -E1000_ERR_NVM; + goto out; + } + +out: + return ret_val; +} /** * igb_acquire_nvm_i210 - Request for access to EEPROM @@ -68,6 +99,23 @@ void igb_release_nvm_i210(struct e1000_hw *hw) } /** + * igb_put_hw_semaphore_i210 - Release hardware semaphore + * @hw: pointer to the HW structure + * + * Release hardware semaphore used to access the PHY or NVM + */ +static void igb_put_hw_semaphore_i210(struct e1000_hw *hw) +{ + u32 swsm; + + swsm = rd32(E1000_SWSM); + + swsm &= ~E1000_SWSM_SWESMBI; + + wr32(E1000_SWSM, swsm); +} + +/** * igb_acquire_swfw_sync_i210 - Acquire SW/FW semaphore * @hw: pointer to the HW structure * @mask: specifies which semaphore to acquire @@ -138,60 +186,6 @@ void igb_release_swfw_sync_i210(struct e1000_hw *hw, u16 mask) } /** - * igb_get_hw_semaphore_i210 - Acquire hardware semaphore - * @hw: pointer to the HW structure - * - * Acquire the HW semaphore to access the PHY or NVM - **/ -static s32 igb_get_hw_semaphore_i210(struct e1000_hw *hw) -{ - u32 swsm; - s32 ret_val = E1000_SUCCESS; - s32 timeout = hw->nvm.word_size + 1; - s32 i = 0; - - /* Get the FW semaphore. */ - for (i = 0; i < timeout; i++) { - swsm = rd32(E1000_SWSM); - wr32(E1000_SWSM, swsm | E1000_SWSM_SWESMBI); - - /* Semaphore acquired if bit latched */ - if (rd32(E1000_SWSM) & E1000_SWSM_SWESMBI) - break; - - udelay(50); - } - - if (i == timeout) { - /* Release semaphores */ - igb_put_hw_semaphore(hw); - hw_dbg("Driver can't access the NVM\n"); - ret_val = -E1000_ERR_NVM; - goto out; - } - -out: - return ret_val; -} - -/** - * igb_put_hw_semaphore_i210 - Release hardware semaphore - * @hw: pointer to the HW structure - * - * Release hardware semaphore used to access the PHY or NVM - **/ -static void igb_put_hw_semaphore_i210(struct e1000_hw *hw) -{ - u32 swsm; - - swsm = rd32(E1000_SWSM); - - swsm &= ~E1000_SWSM_SWESMBI; - - wr32(E1000_SWSM, swsm); -} - -/** * igb_read_nvm_srrd_i210 - Reads Shadow Ram using EERD register * @hw: pointer to the HW structure * @offset: offset of word in the Shadow Ram to read @@ -229,49 +223,6 @@ s32 igb_read_nvm_srrd_i210(struct e1000_hw *hw, u16 offset, u16 words, } /** - * igb_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR - * @hw: pointer to the HW structure - * @offset: offset within the Shadow RAM to be written to - * @words: number of words to write - * @data: 16 bit word(s) to be written to the Shadow RAM - * - * Writes data to Shadow RAM at offset using EEWR register. - * - * If e1000_update_nvm_checksum is not called after this function , the - * data will not be committed to FLASH and also Shadow RAM will most likely - * contain an invalid checksum. - * - * If error code is returned, data and Shadow RAM may be inconsistent - buffer - * partially written. - **/ -s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, - u16 *data) -{ - s32 status = E1000_SUCCESS; - u16 i, count; - - /* We cannot hold synchronization semaphores for too long, - * because of forceful takeover procedure. However it is more efficient - * to write in bursts than synchronizing access for each word. */ - for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { - count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? - E1000_EERD_EEWR_MAX_COUNT : (words - i); - if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { - status = igb_write_nvm_srwr(hw, offset, count, - data + i); - hw->nvm.ops.release(hw); - } else { - status = E1000_ERR_SWFW_SYNC; - } - - if (status != E1000_SUCCESS) - break; - } - - return status; -} - -/** * igb_write_nvm_srwr - Write to Shadow Ram using EEWR * @hw: pointer to the HW structure * @offset: offset within the Shadow Ram to be written to @@ -329,6 +280,50 @@ out: } /** + * igb_write_nvm_srwr_i210 - Write to Shadow RAM using EEWR + * @hw: pointer to the HW structure + * @offset: offset within the Shadow RAM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the Shadow RAM + * + * Writes data to Shadow RAM at offset using EEWR register. + * + * If e1000_update_nvm_checksum is not called after this function , the + * data will not be committed to FLASH and also Shadow RAM will most likely + * contain an invalid checksum. + * + * If error code is returned, data and Shadow RAM may be inconsistent - buffer + * partially written. + */ +s32 igb_write_nvm_srwr_i210(struct e1000_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = E1000_SUCCESS; + u16 i, count; + + /* We cannot hold synchronization semaphores for too long, + * because of forceful takeover procedure. However it is more efficient + * to write in bursts than synchronizing access for each word. + */ + for (i = 0; i < words; i += E1000_EERD_EEWR_MAX_COUNT) { + count = (words - i) / E1000_EERD_EEWR_MAX_COUNT > 0 ? + E1000_EERD_EEWR_MAX_COUNT : (words - i); + if (hw->nvm.ops.acquire(hw) == E1000_SUCCESS) { + status = igb_write_nvm_srwr(hw, offset, count, + data + i); + hw->nvm.ops.release(hw); + } else { + status = E1000_ERR_SWFW_SYNC; + } + + if (status != E1000_SUCCESS) + break; + } + + return status; +} + +/** * igb_read_nvm_i211 - Read NVM wrapper function for I211 * @hw: pointer to the HW structure * @address: the word address (aka eeprom offset) to read @@ -350,16 +345,40 @@ s32 igb_read_nvm_i211(struct e1000_hw *hw, u16 offset, u16 words, if (ret_val != E1000_SUCCESS) hw_dbg("MAC Addr not found in iNVM\n"); break; - case NVM_ID_LED_SETTINGS: case NVM_INIT_CTRL_2: + ret_val = igb_read_invm_i211(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_2_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; case NVM_INIT_CTRL_4: + ret_val = igb_read_invm_i211(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_INIT_CTRL_4_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; case NVM_LED_1_CFG: + ret_val = igb_read_invm_i211(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_1_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } + break; case NVM_LED_0_2_CFG: igb_read_invm_i211(hw, offset, data); + if (ret_val != E1000_SUCCESS) { + *data = NVM_LED_0_2_CFG_DEFAULT_I211; + ret_val = E1000_SUCCESS; + } break; - case NVM_COMPAT: - *data = ID_LED_DEFAULT_I210; - break; + case NVM_ID_LED_SETTINGS: + ret_val = igb_read_invm_i211(hw, (u8)offset, data); + if (ret_val != E1000_SUCCESS) { + *data = ID_LED_RESERVED_FFFF; + ret_val = E1000_SUCCESS; + } case NVM_SUB_DEV_ID: *data = hw->subsystem_device_id; break; @@ -613,6 +632,28 @@ out: } /** + * igb_pool_flash_update_done_i210 - Pool FLUDONE status. + * @hw: pointer to the HW structure + * + */ +static s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw) +{ + s32 ret_val = -E1000_ERR_NVM; + u32 i, reg; + + for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { + reg = rd32(E1000_EECD); + if (reg & E1000_EECD_FLUDONE_I210) { + ret_val = E1000_SUCCESS; + break; + } + udelay(5); + } + + return ret_val; +} + +/** * igb_update_flash_i210 - Commit EEPROM to the flash * @hw: pointer to the HW structure * @@ -642,28 +683,6 @@ out: } /** - * igb_pool_flash_update_done_i210 - Pool FLUDONE status. - * @hw: pointer to the HW structure - * - **/ -s32 igb_pool_flash_update_done_i210(struct e1000_hw *hw) -{ - s32 ret_val = -E1000_ERR_NVM; - u32 i, reg; - - for (i = 0; i < E1000_FLUDONE_ATTEMPTS; i++) { - reg = rd32(E1000_EECD); - if (reg & E1000_EECD_FLUDONE_I210) { - ret_val = E1000_SUCCESS; - break; - } - udelay(5); - } - - return ret_val; -} - -/** * igb_valid_led_default_i210 - Verify a valid default LED config * @hw: pointer to the HW structure * @data: pointer to the NVM (EEPROM) diff --git a/drivers/net/ethernet/intel/igb/e1000_i210.h b/drivers/net/ethernet/intel/igb/e1000_i210.h index 974d23584d7..1c89358a99a 100644 --- a/drivers/net/ethernet/intel/igb/e1000_i210.h +++ b/drivers/net/ethernet/intel/igb/e1000_i210.h @@ -84,4 +84,10 @@ enum E1000_INVM_STRUCTURE_TYPE { (ID_LED_DEF1_DEF2 << 4) | \ (ID_LED_DEF1_DEF2)) +/* NVM offset defaults for i211 device */ +#define NVM_INIT_CTRL_2_DEFAULT_I211 0X7243 +#define NVM_INIT_CTRL_4_DEFAULT_I211 0x00C1 +#define NVM_LED_1_CFG_DEFAULT_I211 0x0184 +#define NVM_LED_0_2_CFG_DEFAULT_I211 0x200C + #endif diff --git a/drivers/net/ethernet/intel/igb/e1000_mac.c b/drivers/net/ethernet/intel/igb/e1000_mac.c index 7acddfe9e6d..101e6e4da97 100644 --- a/drivers/net/ethernet/intel/igb/e1000_mac.c +++ b/drivers/net/ethernet/intel/igb/e1000_mac.c @@ -839,6 +839,7 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw) { struct e1000_mac_info *mac = &hw->mac; s32 ret_val = 0; + u32 pcs_status_reg, pcs_adv_reg, pcs_lp_ability_reg, pcs_ctrl_reg; u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; u16 speed, duplex; @@ -1040,6 +1041,129 @@ s32 igb_config_fc_after_link_up(struct e1000_hw *hw) goto out; } } + /* Check for the case where we have SerDes media and auto-neg is + * enabled. In this case, we need to check and see if Auto-Neg + * has completed, and if so, how the PHY and link partner has + * flow control configured. + */ + if ((hw->phy.media_type == e1000_media_type_internal_serdes) + && mac->autoneg) { + /* Read the PCS_LSTS and check to see if AutoNeg + * has completed. + */ + pcs_status_reg = rd32(E1000_PCS_LSTAT); + + if (!(pcs_status_reg & E1000_PCS_LSTS_AN_COMPLETE)) { + hw_dbg("PCS Auto Neg has not completed.\n"); + return ret_val; + } + + /* The AutoNeg process has completed, so we now need to + * read both the Auto Negotiation Advertisement + * Register (PCS_ANADV) and the Auto_Negotiation Base + * Page Ability Register (PCS_LPAB) to determine how + * flow control was negotiated. + */ + pcs_adv_reg = rd32(E1000_PCS_ANADV); + pcs_lp_ability_reg = rd32(E1000_PCS_LPAB); + + /* Two bits in the Auto Negotiation Advertisement Register + * (PCS_ANADV) and two bits in the Auto Negotiation Base + * Page Ability Register (PCS_LPAB) determine flow control + * for both the PHY and the link partner. The following + * table, taken out of the IEEE 802.3ab/D6.0 dated March 25, + * 1999, describes these PAUSE resolution bits and how flow + * control is determined based upon these settings. + * NOTE: DC = Don't Care + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution + *-------|---------|-------|---------|-------------------- + * 0 | 0 | DC | DC | e1000_fc_none + * 0 | 1 | 0 | DC | e1000_fc_none + * 0 | 1 | 1 | 0 | e1000_fc_none + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + * 1 | 0 | 0 | DC | e1000_fc_none + * 1 | DC | 1 | DC | e1000_fc_full + * 1 | 1 | 0 | 0 | e1000_fc_none + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + * + * Are both PAUSE bits set to 1? If so, this implies + * Symmetric Flow Control is enabled at both ends. The + * ASM_DIR bits are irrelevant per the spec. + * + * For Symmetric Flow Control: + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | DC | 1 | DC | e1000_fc_full + * + */ + if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE)) { + /* Now we need to check if the user selected Rx ONLY + * of pause frames. In this case, we had to advertise + * FULL flow control because we could not advertise Rx + * ONLY. Hence, we must now check to see if we need to + * turn OFF the TRANSMISSION of PAUSE frames. + */ + if (hw->fc.requested_mode == e1000_fc_full) { + hw->fc.current_mode = e1000_fc_full; + hw_dbg("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = e1000_fc_rx_pause; + hw_dbg("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* For receiving PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 0 | 1 | 1 | 1 | e1000_fc_tx_pause + */ + else if (!(pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + (pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_tx_pause; + hw_dbg("Flow Control = Tx PAUSE frames only.\n"); + } + /* For transmitting PAUSE frames ONLY. + * + * LOCAL DEVICE | LINK PARTNER + * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result + *-------|---------|-------|---------|-------------------- + * 1 | 1 | 0 | 1 | e1000_fc_rx_pause + */ + else if ((pcs_adv_reg & E1000_TXCW_PAUSE) && + (pcs_adv_reg & E1000_TXCW_ASM_DIR) && + !(pcs_lp_ability_reg & E1000_TXCW_PAUSE) && + (pcs_lp_ability_reg & E1000_TXCW_ASM_DIR)) { + hw->fc.current_mode = e1000_fc_rx_pause; + hw_dbg("Flow Control = Rx PAUSE frames only.\n"); + } else { + /* Per the IEEE spec, at this point flow control + * should be disabled. + */ + hw->fc.current_mode = e1000_fc_none; + hw_dbg("Flow Control = NONE.\n"); + } + + /* Now we call a subroutine to actually force the MAC + * controller to use the correct flow control settings. + */ + pcs_ctrl_reg = rd32(E1000_PCS_LCTL); + pcs_ctrl_reg |= E1000_PCS_LCTL_FORCE_FCTRL; + wr32(E1000_PCS_LCTL, pcs_ctrl_reg); + + ret_val = igb_force_mac_fc(hw); + if (ret_val) { + hw_dbg("Error forcing flow control settings\n"); + return ret_val; + } + } out: return ret_val; diff --git a/drivers/net/ethernet/intel/igb/e1000_nvm.c b/drivers/net/ethernet/intel/igb/e1000_nvm.c index 7db3f80bcd5..fbb7604db36 100644 --- a/drivers/net/ethernet/intel/igb/e1000_nvm.c +++ b/drivers/net/ethernet/intel/igb/e1000_nvm.c @@ -438,7 +438,7 @@ out: s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) { struct e1000_nvm_info *nvm = &hw->nvm; - s32 ret_val; + s32 ret_val = -E1000_ERR_NVM; u16 widx = 0; /* @@ -448,22 +448,21 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) || (words == 0)) { hw_dbg("nvm parameter(s) out of bounds\n"); - ret_val = -E1000_ERR_NVM; - goto out; + return ret_val; } - ret_val = hw->nvm.ops.acquire(hw); - if (ret_val) - goto out; - - msleep(10); - while (widx < words) { u8 write_opcode = NVM_WRITE_OPCODE_SPI; - ret_val = igb_ready_nvm_eeprom(hw); + ret_val = nvm->ops.acquire(hw); if (ret_val) - goto release; + return ret_val; + + ret_val = igb_ready_nvm_eeprom(hw); + if (ret_val) { + nvm->ops.release(hw); + return ret_val; + } igb_standby_nvm(hw); @@ -497,13 +496,10 @@ s32 igb_write_nvm_spi(struct e1000_hw *hw, u16 offset, u16 words, u16 *data) break; } } + usleep_range(1000, 2000); + nvm->ops.release(hw); } - msleep(10); -release: - hw->nvm.ops.release(hw); - -out: return ret_val; } diff --git a/drivers/net/ethernet/intel/igb/igb.h b/drivers/net/ethernet/intel/igb/igb.h index c15a4811b47..17f1686ee41 100644 --- a/drivers/net/ethernet/intel/igb/igb.h +++ b/drivers/net/ethernet/intel/igb/igb.h @@ -42,6 +42,8 @@ struct igb_adapter; +#define E1000_PCS_CFG_IGN_SD 1 + /* Interrupt defines */ #define IGB_START_ITR 648 /* ~6000 ints/sec */ #define IGB_4K_ITR 980 diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index e2288b5a9ca..bfe9208c4b1 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -1624,6 +1624,20 @@ static int igb_setup_loopback_test(struct igb_adapter *adapter) reg &= ~E1000_CONNSW_ENRGSRC; wr32(E1000_CONNSW, reg); + /* Unset sigdetect for SERDES loopback on + * 82580 and i350 devices. + */ + switch (hw->mac.type) { + case e1000_82580: + case e1000_i350: + reg = rd32(E1000_PCS_CFG0); + reg |= E1000_PCS_CFG_IGN_SD; + wr32(E1000_PCS_CFG0, reg); + break; + default: + break; + } + /* Set PCS register for forced speed */ reg = rd32(E1000_PCS_LCTL); reg &= ~E1000_PCS_LCTL_AN_ENABLE; /* Disable Autoneg*/ diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 0ce145e9354..b85b15a8898 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -1589,8 +1589,7 @@ void igb_reset(struct igb_adapter *adapter) struct e1000_hw *hw = &adapter->hw; struct e1000_mac_info *mac = &hw->mac; struct e1000_fc_info *fc = &hw->fc; - u32 pba = 0, tx_space, min_tx_space, min_rx_space; - u16 hwm; + u32 pba = 0, tx_space, min_tx_space, min_rx_space, hwm; /* Repartition Pba for greater than 9k mtu * To take effect CTRL.RST is required. @@ -1665,7 +1664,7 @@ void igb_reset(struct igb_adapter *adapter) hwm = min(((pba << 10) * 9 / 10), ((pba << 10) - 2 * adapter->max_frame_size)); - fc->high_water = hwm & 0xFFF0; /* 16-byte granularity */ + fc->high_water = hwm & 0xFFFFFFF0; /* 16-byte granularity */ fc->low_water = fc->high_water - 16; fc->pause_time = 0xFFFF; fc->send_xon = 1; diff --git a/drivers/net/ethernet/intel/igbvf/defines.h b/drivers/net/ethernet/intel/igbvf/defines.h index 3e18045d8f8..d9fa999b168 100644 --- a/drivers/net/ethernet/intel/igbvf/defines.h +++ b/drivers/net/ethernet/intel/igbvf/defines.h @@ -46,6 +46,7 @@ #define E1000_RXD_ERR_SE 0x02 /* Symbol Error */ #define E1000_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */ +#define E1000_RXDEXT_STATERR_LB 0x00040000 #define E1000_RXDEXT_STATERR_CE 0x01000000 #define E1000_RXDEXT_STATERR_SE 0x02000000 #define E1000_RXDEXT_STATERR_SEQ 0x04000000 diff --git a/drivers/net/ethernet/intel/igbvf/igbvf.h b/drivers/net/ethernet/intel/igbvf/igbvf.h index a895e2f7b34..fdca7b67277 100644 --- a/drivers/net/ethernet/intel/igbvf/igbvf.h +++ b/drivers/net/ethernet/intel/igbvf/igbvf.h @@ -295,7 +295,7 @@ struct igbvf_info { /* hardware capability, feature, and workaround flags */ #define IGBVF_FLAG_RX_CSUM_DISABLED (1 << 0) - +#define IGBVF_FLAG_RX_LB_VLAN_BSWAP (1 << 1) #define IGBVF_RX_DESC_ADV(R, i) \ (&((((R).desc))[i].rx_desc)) #define IGBVF_TX_DESC_ADV(R, i) \ diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c index 4051ec40461..3d92ad8cdca 100644 --- a/drivers/net/ethernet/intel/igbvf/netdev.c +++ b/drivers/net/ethernet/intel/igbvf/netdev.c @@ -47,7 +47,7 @@ #include "igbvf.h" -#define DRV_VERSION "2.0.1-k" +#define DRV_VERSION "2.0.2-k" char igbvf_driver_name[] = "igbvf"; const char igbvf_driver_version[] = DRV_VERSION; static const char igbvf_driver_string[] = @@ -107,12 +107,19 @@ static void igbvf_receive_skb(struct igbvf_adapter *adapter, struct sk_buff *skb, u32 status, u16 vlan) { + u16 vid; + if (status & E1000_RXD_STAT_VP) { - u16 vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK; + if ((adapter->flags & IGBVF_FLAG_RX_LB_VLAN_BSWAP) && + (status & E1000_RXDEXT_STATERR_LB)) + vid = be16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK; + else + vid = le16_to_cpu(vlan) & E1000_RXD_SPC_VLAN_MASK; if (test_bit(vid, adapter->active_vlans)) __vlan_hwaccel_put_tag(skb, vid); } - netif_receive_skb(skb); + + napi_gro_receive(&adapter->rx_ring->napi, skb); } static inline void igbvf_rx_checksum_adv(struct igbvf_adapter *adapter, @@ -2767,6 +2774,10 @@ static int __devinit igbvf_probe(struct pci_dev *pdev, /* reset the hardware with the new settings */ igbvf_reset(adapter); + /* set hardware-specific flags */ + if (adapter->hw.mac.type == e1000_vfadapt_i350) + adapter->flags |= IGBVF_FLAG_RX_LB_VLAN_BSWAP; + strcpy(netdev->name, "eth%d"); err = register_netdev(netdev); if (err) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h index 7ff4c4fdcb0..8e786764c60 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h @@ -483,6 +483,7 @@ struct ixgbe_adapter { #define IXGBE_FLAG2_RSS_FIELD_IPV6_UDP (u32)(1 << 9) #define IXGBE_FLAG2_PTP_ENABLED (u32)(1 << 10) #define IXGBE_FLAG2_PTP_PPS_ENABLED (u32)(1 << 11) +#define IXGBE_FLAG2_BRIDGE_MODE_VEB (u32)(1 << 12) /* Tx fast path data */ int num_tx_queues; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c index efaf9a73cc7..50aa546b8c7 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_debugfs.c @@ -47,23 +47,27 @@ static ssize_t ixgbe_dbg_reg_ops_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { struct ixgbe_adapter *adapter = filp->private_data; - char buf[256]; - int bytes_not_copied; + char *buf; int len; /* don't allow partial reads */ if (*ppos != 0) return 0; - len = snprintf(buf, sizeof(buf), "%s: %s\n", - adapter->netdev->name, ixgbe_dbg_reg_ops_buf); - if (count < len) + buf = kasprintf(GFP_KERNEL, "%s: %s\n", + adapter->netdev->name, + ixgbe_dbg_reg_ops_buf); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); return -ENOSPC; - bytes_not_copied = copy_to_user(buffer, buf, len); - if (bytes_not_copied < 0) - return bytes_not_copied; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); - *ppos = len; + kfree(buf); return len; } @@ -79,7 +83,7 @@ static ssize_t ixgbe_dbg_reg_ops_write(struct file *filp, size_t count, loff_t *ppos) { struct ixgbe_adapter *adapter = filp->private_data; - int bytes_not_copied; + int len; /* don't allow partial writes */ if (*ppos != 0) @@ -87,14 +91,15 @@ static ssize_t ixgbe_dbg_reg_ops_write(struct file *filp, if (count >= sizeof(ixgbe_dbg_reg_ops_buf)) return -ENOSPC; - bytes_not_copied = copy_from_user(ixgbe_dbg_reg_ops_buf, buffer, count); - if (bytes_not_copied < 0) - return bytes_not_copied; - else if (bytes_not_copied < count) - count -= bytes_not_copied; - else - return -ENOSPC; - ixgbe_dbg_reg_ops_buf[count] = '\0'; + len = simple_write_to_buffer(ixgbe_dbg_reg_ops_buf, + sizeof(ixgbe_dbg_reg_ops_buf)-1, + ppos, + buffer, + count); + if (len < 0) + return len; + + ixgbe_dbg_reg_ops_buf[len] = '\0'; if (strncmp(ixgbe_dbg_reg_ops_buf, "write", 5) == 0) { u32 reg, value; @@ -147,23 +152,27 @@ static ssize_t ixgbe_dbg_netdev_ops_read(struct file *filp, size_t count, loff_t *ppos) { struct ixgbe_adapter *adapter = filp->private_data; - char buf[256]; - int bytes_not_copied; + char *buf; int len; /* don't allow partial reads */ if (*ppos != 0) return 0; - len = snprintf(buf, sizeof(buf), "%s: %s\n", - adapter->netdev->name, ixgbe_dbg_netdev_ops_buf); - if (count < len) + buf = kasprintf(GFP_KERNEL, "%s: %s\n", + adapter->netdev->name, + ixgbe_dbg_netdev_ops_buf); + if (!buf) + return -ENOMEM; + + if (count < strlen(buf)) { + kfree(buf); return -ENOSPC; - bytes_not_copied = copy_to_user(buffer, buf, len); - if (bytes_not_copied < 0) - return bytes_not_copied; + } + + len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf)); - *ppos = len; + kfree(buf); return len; } @@ -179,7 +188,7 @@ static ssize_t ixgbe_dbg_netdev_ops_write(struct file *filp, size_t count, loff_t *ppos) { struct ixgbe_adapter *adapter = filp->private_data; - int bytes_not_copied; + int len; /* don't allow partial writes */ if (*ppos != 0) @@ -187,15 +196,15 @@ static ssize_t ixgbe_dbg_netdev_ops_write(struct file *filp, if (count >= sizeof(ixgbe_dbg_netdev_ops_buf)) return -ENOSPC; - bytes_not_copied = copy_from_user(ixgbe_dbg_netdev_ops_buf, - buffer, count); - if (bytes_not_copied < 0) - return bytes_not_copied; - else if (bytes_not_copied < count) - count -= bytes_not_copied; - else - return -ENOSPC; - ixgbe_dbg_netdev_ops_buf[count] = '\0'; + len = simple_write_to_buffer(ixgbe_dbg_netdev_ops_buf, + sizeof(ixgbe_dbg_netdev_ops_buf)-1, + ppos, + buffer, + count); + if (len < 0) + return len; + + ixgbe_dbg_netdev_ops_buf[len] = '\0'; if (strncmp(ixgbe_dbg_netdev_ops_buf, "tx_timeout", 10) == 0) { adapter->netdev->netdev_ops->ndo_tx_timeout(adapter->netdev); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 80e3cb7c39e..79b83458318 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -63,11 +63,7 @@ char ixgbe_default_device_descr[] = static char ixgbe_default_device_descr[] = "Intel(R) 10 Gigabit Network Connection"; #endif -#define MAJ 3 -#define MIN 9 -#define BUILD 15 -#define DRV_VERSION __stringify(MAJ) "." __stringify(MIN) "." \ - __stringify(BUILD) "-k" +#define DRV_VERSION "3.11.33-k" const char ixgbe_driver_version[] = DRV_VERSION; static const char ixgbe_copyright[] = "Copyright (c) 1999-2012 Intel Corporation."; @@ -703,6 +699,7 @@ static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter) struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_hw_stats *hwstats = &adapter->stats; u32 xoff[8] = {0}; + u8 tc; int i; bool pfc_en = adapter->dcb_cfg.pfc_mode_enable; @@ -716,21 +713,26 @@ static void ixgbe_update_xoff_received(struct ixgbe_adapter *adapter) /* update stats for each tc, only valid with PFC enabled */ for (i = 0; i < MAX_TX_PACKET_BUFFERS; i++) { + u32 pxoffrxc; + switch (hw->mac.type) { case ixgbe_mac_82598EB: - xoff[i] = IXGBE_READ_REG(hw, IXGBE_PXOFFRXC(i)); + pxoffrxc = IXGBE_READ_REG(hw, IXGBE_PXOFFRXC(i)); break; default: - xoff[i] = IXGBE_READ_REG(hw, IXGBE_PXOFFRXCNT(i)); + pxoffrxc = IXGBE_READ_REG(hw, IXGBE_PXOFFRXCNT(i)); } - hwstats->pxoffrxc[i] += xoff[i]; + hwstats->pxoffrxc[i] += pxoffrxc; + /* Get the TC for given UP */ + tc = netdev_get_prio_tc_map(adapter->netdev, i); + xoff[tc] += pxoffrxc; } /* disarm tx queues that have received xoff frames */ for (i = 0; i < adapter->num_tx_queues; i++) { struct ixgbe_ring *tx_ring = adapter->tx_ring[i]; - u8 tc = tx_ring->dcb_tc; + tc = tx_ring->dcb_tc; if (xoff[tc]) clear_bit(__IXGBE_HANG_CHECK_ARMED, &tx_ring->state); } @@ -1336,26 +1338,29 @@ static unsigned int ixgbe_get_headlen(unsigned char *data, if (hlen < sizeof(struct iphdr)) return hdr.network - data; - /* record next protocol */ - nexthdr = hdr.ipv4->protocol; - hdr.network += hlen; + /* record next protocol if header is present */ + if (!hdr.ipv4->frag_off) + nexthdr = hdr.ipv4->protocol; } else if (protocol == __constant_htons(ETH_P_IPV6)) { if ((hdr.network - data) > (max_len - sizeof(struct ipv6hdr))) return max_len; /* record next protocol */ nexthdr = hdr.ipv6->nexthdr; - hdr.network += sizeof(struct ipv6hdr); + hlen = sizeof(struct ipv6hdr); #ifdef IXGBE_FCOE } else if (protocol == __constant_htons(ETH_P_FCOE)) { if ((hdr.network - data) > (max_len - FCOE_HEADER_LEN)) return max_len; - hdr.network += FCOE_HEADER_LEN; + hlen = FCOE_HEADER_LEN; #endif } else { return hdr.network - data; } + /* relocate pointer to start of L4 header */ + hdr.network += hlen; + /* finally sort out TCP/UDP */ if (nexthdr == IPPROTO_TCP) { if ((hdr.network - data) > (max_len - sizeof(struct tcphdr))) @@ -3170,14 +3175,6 @@ void ixgbe_configure_rx_ring(struct ixgbe_adapter *adapter, ixgbe_configure_srrctl(adapter, ring); ixgbe_configure_rscctl(adapter, ring); - /* If operating in IOV mode set RLPML for X540 */ - if ((adapter->flags & IXGBE_FLAG_SRIOV_ENABLED) && - hw->mac.type == ixgbe_mac_X540) { - rxdctl &= ~IXGBE_RXDCTL_RLPMLMASK; - rxdctl |= ((ring->netdev->mtu + ETH_HLEN + - ETH_FCS_LEN + VLAN_HLEN) | IXGBE_RXDCTL_RLPML_EN); - } - if (hw->mac.type == ixgbe_mac_82598EB) { /* * enable cache line friendly hardware writes: @@ -3249,6 +3246,8 @@ static void ixgbe_configure_virtualization(struct ixgbe_adapter *adapter) IXGBE_WRITE_REG(hw, IXGBE_VFRE(reg_offset ^ 1), reg_offset - 1); IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset), (~0) << vf_shift); IXGBE_WRITE_REG(hw, IXGBE_VFTE(reg_offset ^ 1), reg_offset - 1); + if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB) + IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); /* Map PF MAC address in RAR Entry 0 to first pool following VFs */ hw->mac.ops.set_vmdq(hw, 0, VMDQ_P(0)); @@ -7041,11 +7040,13 @@ static int ixgbe_ndo_bridge_setlink(struct net_device *dev, continue; mode = nla_get_u16(attr); - if (mode == BRIDGE_MODE_VEPA) + if (mode == BRIDGE_MODE_VEPA) { reg = 0; - else if (mode == BRIDGE_MODE_VEB) + adapter->flags2 &= ~IXGBE_FLAG2_BRIDGE_MODE_VEB; + } else if (mode == BRIDGE_MODE_VEB) { reg = IXGBE_PFDTXGSWC_VT_LBEN; - else + adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB; + } else return -EINVAL; IXGBE_WRITE_REG(&adapter->hw, IXGBE_PFDTXGSWC, reg); @@ -7066,7 +7067,7 @@ static int ixgbe_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, if (!(adapter->flags & IXGBE_FLAG_SRIOV_ENABLED)) return 0; - if (IXGBE_READ_REG(&adapter->hw, IXGBE_PFDTXGSWC) & 1) + if (adapter->flags2 & IXGBE_FLAG2_BRIDGE_MODE_VEB) mode = BRIDGE_MODE_VEB; else mode = BRIDGE_MODE_VEPA; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c index 4993642d1ce..85cddac673e 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_sriov.c @@ -119,6 +119,7 @@ void ixgbe_enable_sriov(struct ixgbe_adapter *adapter, /* Initialize default switching mode VEB */ IXGBE_WRITE_REG(hw, IXGBE_PFDTXGSWC, IXGBE_PFDTXGSWC_VT_LBEN); + adapter->flags2 |= IXGBE_FLAG2_BRIDGE_MODE_VEB; /* If call to enable VFs succeeded then allocate memory * for per VF control structures. diff --git a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c index 5d36795877c..b799ab12a29 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_dcb_nl.c @@ -237,7 +237,7 @@ static int mlx4_en_dcbnl_ieee_setmaxrate(struct net_device *dev, if (err) return err; - memcpy(priv->maxrate, tmp, sizeof(*priv->maxrate)); + memcpy(priv->maxrate, tmp, sizeof(priv->maxrate)); return 0; } diff --git a/drivers/net/ethernet/myricom/Kconfig b/drivers/net/ethernet/myricom/Kconfig index 540f0c6fc16..3932d081fa2 100644 --- a/drivers/net/ethernet/myricom/Kconfig +++ b/drivers/net/ethernet/myricom/Kconfig @@ -23,7 +23,6 @@ config MYRI10GE depends on PCI && INET select FW_LOADER select CRC32 - select INET_LRO ---help--- This driver supports Myricom Myri-10G Dual Protocol interface in Ethernet mode. If the eeprom on your board is not recent enough, diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index 83516e3369c..a40234ea2fb 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -50,7 +50,6 @@ #include <linux/etherdevice.h> #include <linux/if_ether.h> #include <linux/if_vlan.h> -#include <linux/inet_lro.h> #include <linux/dca.h> #include <linux/ip.h> #include <linux/inet.h> @@ -96,8 +95,6 @@ MODULE_LICENSE("Dual BSD/GPL"); #define MYRI10GE_EEPROM_STRINGS_SIZE 256 #define MYRI10GE_MAX_SEND_DESC_TSO ((65536 / 2048) * 2) -#define MYRI10GE_MAX_LRO_DESCRIPTORS 8 -#define MYRI10GE_LRO_MAX_PKTS 64 #define MYRI10GE_NO_CONFIRM_DATA htonl(0xffffffff) #define MYRI10GE_NO_RESPONSE_RESULT 0xffffffff @@ -165,8 +162,6 @@ struct myri10ge_rx_done { dma_addr_t bus; int cnt; int idx; - struct net_lro_mgr lro_mgr; - struct net_lro_desc lro_desc[MYRI10GE_MAX_LRO_DESCRIPTORS]; }; struct myri10ge_slice_netstats { @@ -338,11 +333,6 @@ static int myri10ge_debug = -1; /* defaults above */ module_param(myri10ge_debug, int, 0); MODULE_PARM_DESC(myri10ge_debug, "Debug level (0=none,...,16=all)"); -static int myri10ge_lro_max_pkts = MYRI10GE_LRO_MAX_PKTS; -module_param(myri10ge_lro_max_pkts, int, S_IRUGO); -MODULE_PARM_DESC(myri10ge_lro_max_pkts, - "Number of LRO packets to be aggregated"); - static int myri10ge_fill_thresh = 256; module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed"); @@ -1197,36 +1187,6 @@ static inline void myri10ge_vlan_ip_csum(struct sk_buff *skb, __wsum hw_csum) } } -static inline void -myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va, - struct skb_frag_struct *rx_frags, int len, int hlen) -{ - struct skb_frag_struct *skb_frags; - - skb->len = skb->data_len = len; - /* attach the page(s) */ - - skb_frags = skb_shinfo(skb)->frags; - while (len > 0) { - memcpy(skb_frags, rx_frags, sizeof(*skb_frags)); - len -= skb_frag_size(rx_frags); - skb_frags++; - rx_frags++; - skb_shinfo(skb)->nr_frags++; - } - - /* pskb_may_pull is not available in irq context, but - * skb_pull() (for ether_pad and eth_type_trans()) requires - * the beginning of the packet in skb_headlen(), move it - * manually */ - skb_copy_to_linear_data(skb, va, hlen); - skb_shinfo(skb)->frags[0].page_offset += hlen; - skb_frag_size_sub(&skb_shinfo(skb)->frags[0], hlen); - skb->data_len -= hlen; - skb->tail += hlen; - skb_pull(skb, MXGEFW_PAD); -} - static void myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, int bytes, int watchdog) @@ -1304,18 +1264,50 @@ myri10ge_unmap_rx_page(struct pci_dev *pdev, } } -#define MYRI10GE_HLEN 64 /* The number of bytes to copy from a - * page into an skb */ +/* + * GRO does not support acceleration of tagged vlan frames, and + * this NIC does not support vlan tag offload, so we must pop + * the tag ourselves to be able to achieve GRO performance that + * is comparable to LRO. + */ + +static inline void +myri10ge_vlan_rx(struct net_device *dev, void *addr, struct sk_buff *skb) +{ + u8 *va; + struct vlan_ethhdr *veh; + struct skb_frag_struct *frag; + __wsum vsum; + + va = addr; + va += MXGEFW_PAD; + veh = (struct vlan_ethhdr *)va; + if ((dev->features & NETIF_F_HW_VLAN_RX) == NETIF_F_HW_VLAN_RX && + veh->h_vlan_proto == htons(ETH_P_8021Q)) { + /* fixup csum if needed */ + if (skb->ip_summed == CHECKSUM_COMPLETE) { + vsum = csum_partial(va + ETH_HLEN, VLAN_HLEN, 0); + skb->csum = csum_sub(skb->csum, vsum); + } + /* pop tag */ + __vlan_hwaccel_put_tag(skb, ntohs(veh->h_vlan_TCI)); + memmove(va + VLAN_HLEN, va, 2 * ETH_ALEN); + skb->len -= VLAN_HLEN; + skb->data_len -= VLAN_HLEN; + frag = skb_shinfo(skb)->frags; + frag->page_offset += VLAN_HLEN; + skb_frag_size_set(frag, skb_frag_size(frag) - VLAN_HLEN); + } +} static inline int -myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum, - bool lro_enabled) +myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum) { struct myri10ge_priv *mgp = ss->mgp; struct sk_buff *skb; - struct skb_frag_struct rx_frags[MYRI10GE_MAX_FRAGS_PER_FRAME]; + struct skb_frag_struct *rx_frags; struct myri10ge_rx_buf *rx; - int i, idx, hlen, remainder, bytes; + int i, idx, remainder, bytes; struct pci_dev *pdev = mgp->pdev; struct net_device *dev = mgp->dev; u8 *va; @@ -1332,67 +1324,48 @@ myri10ge_rx_done(struct myri10ge_slice_state *ss, int len, __wsum csum, idx = rx->cnt & rx->mask; va = page_address(rx->info[idx].page) + rx->info[idx].page_offset; prefetch(va); + + skb = napi_get_frags(&ss->napi); + if (unlikely(skb == NULL)) { + ss->stats.rx_dropped++; + for (i = 0, remainder = len; remainder > 0; i++) { + myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes); + put_page(rx->info[idx].page); + rx->cnt++; + idx = rx->cnt & rx->mask; + remainder -= MYRI10GE_ALLOC_SIZE; + } + return 0; + } + rx_frags = skb_shinfo(skb)->frags; /* Fill skb_frag_struct(s) with data from our receive */ for (i = 0, remainder = len; remainder > 0; i++) { myri10ge_unmap_rx_page(pdev, &rx->info[idx], bytes); - __skb_frag_set_page(&rx_frags[i], rx->info[idx].page); - rx_frags[i].page_offset = rx->info[idx].page_offset; - if (remainder < MYRI10GE_ALLOC_SIZE) - skb_frag_size_set(&rx_frags[i], remainder); - else - skb_frag_size_set(&rx_frags[i], MYRI10GE_ALLOC_SIZE); + skb_fill_page_desc(skb, i, rx->info[idx].page, + rx->info[idx].page_offset, + remainder < MYRI10GE_ALLOC_SIZE ? + remainder : MYRI10GE_ALLOC_SIZE); rx->cnt++; idx = rx->cnt & rx->mask; remainder -= MYRI10GE_ALLOC_SIZE; } - if (lro_enabled) { - rx_frags[0].page_offset += MXGEFW_PAD; - skb_frag_size_sub(&rx_frags[0], MXGEFW_PAD); - len -= MXGEFW_PAD; - lro_receive_frags(&ss->rx_done.lro_mgr, rx_frags, - /* opaque, will come back in get_frag_header */ - len, len, - (void *)(__force unsigned long)csum, csum); - - return 1; - } - - hlen = MYRI10GE_HLEN > len ? len : MYRI10GE_HLEN; - - /* allocate an skb to attach the page(s) to. This is done - * after trying LRO, so as to avoid skb allocation overheads */ - - skb = netdev_alloc_skb(dev, MYRI10GE_HLEN + 16); - if (unlikely(skb == NULL)) { - ss->stats.rx_dropped++; - do { - i--; - __skb_frag_unref(&rx_frags[i]); - } while (i != 0); - return 0; - } + /* remove padding */ + rx_frags[0].page_offset += MXGEFW_PAD; + rx_frags[0].size -= MXGEFW_PAD; + len -= MXGEFW_PAD; - /* Attach the pages to the skb, and trim off any padding */ - myri10ge_rx_skb_build(skb, va, rx_frags, len, hlen); - if (skb_frag_size(&skb_shinfo(skb)->frags[0]) <= 0) { - skb_frag_unref(skb, 0); - skb_shinfo(skb)->nr_frags = 0; - } else { - skb->truesize += bytes * skb_shinfo(skb)->nr_frags; + skb->len = len; + skb->data_len = len; + skb->truesize += len; + if (dev->features & NETIF_F_RXCSUM) { + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum; } - skb->protocol = eth_type_trans(skb, dev); + myri10ge_vlan_rx(mgp->dev, va, skb); skb_record_rx_queue(skb, ss - &mgp->ss[0]); - if (dev->features & NETIF_F_RXCSUM) { - if ((skb->protocol == htons(ETH_P_IP)) || - (skb->protocol == htons(ETH_P_IPV6))) { - skb->csum = csum; - skb->ip_summed = CHECKSUM_COMPLETE; - } else - myri10ge_vlan_ip_csum(skb, csum); - } - netif_receive_skb(skb); + napi_gro_frags(&ss->napi); return 1; } @@ -1480,18 +1453,11 @@ myri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget) u16 length; __wsum checksum; - /* - * Prevent compiler from generating more than one ->features memory - * access to avoid theoretical race condition with functions that - * change NETIF_F_LRO flag at runtime. - */ - bool lro_enabled = !!(ACCESS_ONCE(mgp->dev->features) & NETIF_F_LRO); - while (rx_done->entry[idx].length != 0 && work_done < budget) { length = ntohs(rx_done->entry[idx].length); rx_done->entry[idx].length = 0; checksum = csum_unfold(rx_done->entry[idx].checksum); - rx_ok = myri10ge_rx_done(ss, length, checksum, lro_enabled); + rx_ok = myri10ge_rx_done(ss, length, checksum); rx_packets += rx_ok; rx_bytes += rx_ok * (unsigned long)length; cnt++; @@ -1503,9 +1469,6 @@ myri10ge_clean_rx_done(struct myri10ge_slice_state *ss, int budget) ss->stats.rx_packets += rx_packets; ss->stats.rx_bytes += rx_bytes; - if (lro_enabled) - lro_flush_all(&rx_done->lro_mgr); - /* restock receive rings if needed */ if (ss->rx_small.fill_cnt - ss->rx_small.cnt < myri10ge_fill_thresh) myri10ge_alloc_rx_pages(mgp, &ss->rx_small, @@ -1779,7 +1742,6 @@ static const char myri10ge_gstrings_slice_stats[][ETH_GSTRING_LEN] = { "tx_pkt_start", "tx_pkt_done", "tx_req", "tx_done", "rx_small_cnt", "rx_big_cnt", "wake_queue", "stop_queue", "tx_linearized", - "LRO aggregated", "LRO flushed", "LRO avg aggr", "LRO no_desc", }; #define MYRI10GE_NET_STATS_LEN 21 @@ -1880,14 +1842,6 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, data[i++] = (unsigned int)ss->tx.wake_queue; data[i++] = (unsigned int)ss->tx.stop_queue; data[i++] = (unsigned int)ss->tx.linearized; - data[i++] = ss->rx_done.lro_mgr.stats.aggregated; - data[i++] = ss->rx_done.lro_mgr.stats.flushed; - if (ss->rx_done.lro_mgr.stats.flushed) - data[i++] = ss->rx_done.lro_mgr.stats.aggregated / - ss->rx_done.lro_mgr.stats.flushed; - else - data[i++] = 0; - data[i++] = ss->rx_done.lro_mgr.stats.no_desc; } } @@ -2271,67 +2225,6 @@ static void myri10ge_free_irq(struct myri10ge_priv *mgp) pci_disable_msix(pdev); } -static int -myri10ge_get_frag_header(struct skb_frag_struct *frag, void **mac_hdr, - void **ip_hdr, void **tcpudp_hdr, - u64 * hdr_flags, void *priv) -{ - struct ethhdr *eh; - struct vlan_ethhdr *veh; - struct iphdr *iph; - u8 *va = skb_frag_address(frag); - unsigned long ll_hlen; - /* passed opaque through lro_receive_frags() */ - __wsum csum = (__force __wsum) (unsigned long)priv; - - /* find the mac header, aborting if not IPv4 */ - - eh = (struct ethhdr *)va; - *mac_hdr = eh; - ll_hlen = ETH_HLEN; - if (eh->h_proto != htons(ETH_P_IP)) { - if (eh->h_proto == htons(ETH_P_8021Q)) { - veh = (struct vlan_ethhdr *)va; - if (veh->h_vlan_encapsulated_proto != htons(ETH_P_IP)) - return -1; - - ll_hlen += VLAN_HLEN; - - /* - * HW checksum starts ETH_HLEN bytes into - * frame, so we must subtract off the VLAN - * header's checksum before csum can be used - */ - csum = csum_sub(csum, csum_partial(va + ETH_HLEN, - VLAN_HLEN, 0)); - } else { - return -1; - } - } - *hdr_flags = LRO_IPV4; - - iph = (struct iphdr *)(va + ll_hlen); - *ip_hdr = iph; - if (iph->protocol != IPPROTO_TCP) - return -1; - if (ip_is_fragment(iph)) - return -1; - *hdr_flags |= LRO_TCP; - *tcpudp_hdr = (u8 *) (*ip_hdr) + (iph->ihl << 2); - - /* verify the IP checksum */ - if (unlikely(ip_fast_csum((u8 *) iph, iph->ihl))) - return -1; - - /* verify the checksum */ - if (unlikely(csum_tcpudp_magic(iph->saddr, iph->daddr, - ntohs(iph->tot_len) - (iph->ihl << 2), - IPPROTO_TCP, csum))) - return -1; - - return 0; -} - static int myri10ge_get_txrx(struct myri10ge_priv *mgp, int slice) { struct myri10ge_cmd cmd; @@ -2402,7 +2295,6 @@ static int myri10ge_open(struct net_device *dev) struct myri10ge_cmd cmd; int i, status, big_pow2, slice; u8 *itable; - struct net_lro_mgr *lro_mgr; if (mgp->running != MYRI10GE_ETH_STOPPED) return -EBUSY; @@ -2513,19 +2405,6 @@ static int myri10ge_open(struct net_device *dev) goto abort_with_rings; } - lro_mgr = &ss->rx_done.lro_mgr; - lro_mgr->dev = dev; - lro_mgr->features = LRO_F_NAPI; - lro_mgr->ip_summed = CHECKSUM_COMPLETE; - lro_mgr->ip_summed_aggr = CHECKSUM_UNNECESSARY; - lro_mgr->max_desc = MYRI10GE_MAX_LRO_DESCRIPTORS; - lro_mgr->lro_arr = ss->rx_done.lro_desc; - lro_mgr->get_frag_header = myri10ge_get_frag_header; - lro_mgr->max_aggr = myri10ge_lro_max_pkts; - lro_mgr->frag_align_pad = 2; - if (lro_mgr->max_aggr > MAX_SKB_FRAGS) - lro_mgr->max_aggr = MAX_SKB_FRAGS; - /* must happen prior to any irq */ napi_enable(&(ss)->napi); } @@ -3143,15 +3022,6 @@ static int myri10ge_set_mac_address(struct net_device *dev, void *addr) return 0; } -static netdev_features_t myri10ge_fix_features(struct net_device *dev, - netdev_features_t features) -{ - if (!(features & NETIF_F_RXCSUM)) - features &= ~NETIF_F_LRO; - - return features; -} - static int myri10ge_change_mtu(struct net_device *dev, int new_mtu) { struct myri10ge_priv *mgp = netdev_priv(dev); @@ -3878,7 +3748,6 @@ static const struct net_device_ops myri10ge_netdev_ops = { .ndo_get_stats64 = myri10ge_get_stats, .ndo_validate_addr = eth_validate_addr, .ndo_change_mtu = myri10ge_change_mtu, - .ndo_fix_features = myri10ge_fix_features, .ndo_set_rx_mode = myri10ge_set_multicast_list, .ndo_set_mac_address = myri10ge_set_mac_address, }; @@ -4018,7 +3887,11 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->netdev_ops = &myri10ge_netdev_ops; netdev->mtu = myri10ge_initial_mtu; - netdev->hw_features = mgp->features | NETIF_F_LRO | NETIF_F_RXCSUM; + netdev->hw_features = mgp->features | NETIF_F_RXCSUM; + + /* fake NETIF_F_HW_VLAN_RX for good GRO performance */ + netdev->hw_features |= NETIF_F_HW_VLAN_RX; + netdev->features = netdev->hw_features; if (dac_enabled) diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile index ddba83ef3f4..c4b8ced8382 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/Makefile +++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile @@ -5,4 +5,5 @@ obj-$(CONFIG_QLCNIC) := qlcnic.o qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \ - qlcnic_ethtool.o qlcnic_ctx.o + qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \ + qlcnic_sysfs.o qlcnic_minidump.o diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index ec29f7988d4..082eecbf414 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -89,16 +89,6 @@ #define QLCNIC_CT_DEFAULT_RX_BUF_LEN 2048 #define QLCNIC_LRO_BUFFER_EXTRA 2048 -/* Opcodes to be used with the commands */ -#define TX_ETHER_PKT 0x01 -#define TX_TCP_PKT 0x02 -#define TX_UDP_PKT 0x03 -#define TX_IP_PKT 0x04 -#define TX_TCP_LSO 0x05 -#define TX_TCP_LSO6 0x06 -#define TX_TCPV6_PKT 0x0b -#define TX_UDPV6_PKT 0x0c - /* Tx defines */ #define QLCNIC_MAX_FRAGS_PER_TX 14 #define MAX_TSO_HEADER_DESC 2 @@ -147,28 +137,6 @@ * Added fileds of tcpHdrSize and ipHdrSize, The driver needs to do it only when * we are doing LSO (above the 1500 size packet) only. */ - -#define FLAGS_VLAN_TAGGED 0x10 -#define FLAGS_VLAN_OOB 0x40 - -#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \ - (cmd_desc)->vlan_TCI = cpu_to_le16(v); -#define qlcnic_set_cmd_desc_port(cmd_desc, var) \ - ((cmd_desc)->port_ctxid |= ((var) & 0x0F)) -#define qlcnic_set_cmd_desc_ctxid(cmd_desc, var) \ - ((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0)) - -#define qlcnic_set_tx_port(_desc, _port) \ - ((_desc)->port_ctxid = ((_port) & 0xf) | (((_port) << 4) & 0xf0)) - -#define qlcnic_set_tx_flags_opcode(_desc, _flags, _opcode) \ - ((_desc)->flags_opcode |= \ - cpu_to_le16(((_flags) & 0x7f) | (((_opcode) & 0x3f) << 7))) - -#define qlcnic_set_tx_frags_len(_desc, _frags, _len) \ - ((_desc)->nfrags__length = \ - cpu_to_le32(((_frags) & 0xff) | (((_len) & 0xffffff) << 8))) - struct cmd_desc_type0 { u8 tcp_hdr_offset; /* For LSO only */ u8 ip_hdr_offset; /* For LSO only */ @@ -203,65 +171,6 @@ struct rcv_desc { __le64 addr_buffer; } __packed; -/* opcode field in status_desc */ -#define QLCNIC_SYN_OFFLOAD 0x03 -#define QLCNIC_RXPKT_DESC 0x04 -#define QLCNIC_OLD_RXPKT_DESC 0x3f -#define QLCNIC_RESPONSE_DESC 0x05 -#define QLCNIC_LRO_DESC 0x12 - -/* for status field in status_desc */ -#define STATUS_CKSUM_LOOP 0 -#define STATUS_CKSUM_OK 2 - -/* owner bits of status_desc */ -#define STATUS_OWNER_HOST (0x1ULL << 56) -#define STATUS_OWNER_PHANTOM (0x2ULL << 56) - -/* Status descriptor: - 0-3 port, 4-7 status, 8-11 type, 12-27 total_length - 28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset - 53-55 desc_cnt, 56-57 owner, 58-63 opcode - */ -#define qlcnic_get_sts_port(sts_data) \ - ((sts_data) & 0x0F) -#define qlcnic_get_sts_status(sts_data) \ - (((sts_data) >> 4) & 0x0F) -#define qlcnic_get_sts_type(sts_data) \ - (((sts_data) >> 8) & 0x0F) -#define qlcnic_get_sts_totallength(sts_data) \ - (((sts_data) >> 12) & 0xFFFF) -#define qlcnic_get_sts_refhandle(sts_data) \ - (((sts_data) >> 28) & 0xFFFF) -#define qlcnic_get_sts_prot(sts_data) \ - (((sts_data) >> 44) & 0x0F) -#define qlcnic_get_sts_pkt_offset(sts_data) \ - (((sts_data) >> 48) & 0x1F) -#define qlcnic_get_sts_desc_cnt(sts_data) \ - (((sts_data) >> 53) & 0x7) -#define qlcnic_get_sts_opcode(sts_data) \ - (((sts_data) >> 58) & 0x03F) - -#define qlcnic_get_lro_sts_refhandle(sts_data) \ - ((sts_data) & 0x0FFFF) -#define qlcnic_get_lro_sts_length(sts_data) \ - (((sts_data) >> 16) & 0x0FFFF) -#define qlcnic_get_lro_sts_l2_hdr_offset(sts_data) \ - (((sts_data) >> 32) & 0x0FF) -#define qlcnic_get_lro_sts_l4_hdr_offset(sts_data) \ - (((sts_data) >> 40) & 0x0FF) -#define qlcnic_get_lro_sts_timestamp(sts_data) \ - (((sts_data) >> 48) & 0x1) -#define qlcnic_get_lro_sts_type(sts_data) \ - (((sts_data) >> 49) & 0x7) -#define qlcnic_get_lro_sts_push_flag(sts_data) \ - (((sts_data) >> 52) & 0x1) -#define qlcnic_get_lro_sts_seq_number(sts_data) \ - ((sts_data) & 0x0FFFFFFFF) -#define qlcnic_get_lro_sts_mss(sts_data1) \ - ((sts_data1 >> 32) & 0x0FFFF) - - struct status_desc { __le64 status_desc_data[2]; } __attribute__ ((aligned(16))); @@ -1346,142 +1255,7 @@ struct qlcnic_esw_statistics { struct __qlcnic_esw_statistics tx; }; -struct qlcnic_common_entry_hdr { - u32 type; - u32 offset; - u32 cap_size; - u8 mask; - u8 rsvd[2]; - u8 flags; -} __packed; - -struct __crb { - u32 addr; - u8 stride; - u8 rsvd1[3]; - u32 data_size; - u32 no_ops; - u32 rsvd2[4]; -} __packed; - -struct __ctrl { - u32 addr; - u8 stride; - u8 index_a; - u16 timeout; - u32 data_size; - u32 no_ops; - u8 opcode; - u8 index_v; - u8 shl_val; - u8 shr_val; - u32 val1; - u32 val2; - u32 val3; -} __packed; - -struct __cache { - u32 addr; - u16 stride; - u16 init_tag_val; - u32 size; - u32 no_ops; - u32 ctrl_addr; - u32 ctrl_val; - u32 read_addr; - u8 read_addr_stride; - u8 read_addr_num; - u8 rsvd1[2]; -} __packed; - -struct __ocm { - u8 rsvd[8]; - u32 size; - u32 no_ops; - u8 rsvd1[8]; - u32 read_addr; - u32 read_addr_stride; -} __packed; - -struct __mem { - u8 rsvd[24]; - u32 addr; - u32 size; -} __packed; - -struct __mux { - u32 addr; - u8 rsvd[4]; - u32 size; - u32 no_ops; - u32 val; - u32 val_stride; - u32 read_addr; - u8 rsvd2[4]; -} __packed; - -struct __queue { - u32 sel_addr; - u16 stride; - u8 rsvd[2]; - u32 size; - u32 no_ops; - u8 rsvd2[8]; - u32 read_addr; - u8 read_addr_stride; - u8 read_addr_cnt; - u8 rsvd3[2]; -} __packed; - -struct qlcnic_dump_entry { - struct qlcnic_common_entry_hdr hdr; - union { - struct __crb crb; - struct __cache cache; - struct __ocm ocm; - struct __mem mem; - struct __mux mux; - struct __queue que; - struct __ctrl ctrl; - } region; -} __packed; - -enum op_codes { - QLCNIC_DUMP_NOP = 0, - QLCNIC_DUMP_READ_CRB = 1, - QLCNIC_DUMP_READ_MUX = 2, - QLCNIC_DUMP_QUEUE = 3, - QLCNIC_DUMP_BRD_CONFIG = 4, - QLCNIC_DUMP_READ_OCM = 6, - QLCNIC_DUMP_PEG_REG = 7, - QLCNIC_DUMP_L1_DTAG = 8, - QLCNIC_DUMP_L1_ITAG = 9, - QLCNIC_DUMP_L1_DATA = 11, - QLCNIC_DUMP_L1_INST = 12, - QLCNIC_DUMP_L2_DTAG = 21, - QLCNIC_DUMP_L2_ITAG = 22, - QLCNIC_DUMP_L2_DATA = 23, - QLCNIC_DUMP_L2_INST = 24, - QLCNIC_DUMP_READ_ROM = 71, - QLCNIC_DUMP_READ_MEM = 72, - QLCNIC_DUMP_READ_CTRL = 98, - QLCNIC_DUMP_TLHDR = 99, - QLCNIC_DUMP_RDEND = 255 -}; - -#define QLCNIC_DUMP_WCRB BIT_0 -#define QLCNIC_DUMP_RWCRB BIT_1 -#define QLCNIC_DUMP_ANDCRB BIT_2 -#define QLCNIC_DUMP_ORCRB BIT_3 -#define QLCNIC_DUMP_POLLCRB BIT_4 -#define QLCNIC_DUMP_RD_SAVE BIT_5 -#define QLCNIC_DUMP_WRT_SAVED BIT_6 -#define QLCNIC_DUMP_MOD_SAVE_ST BIT_7 -#define QLCNIC_DUMP_SKIP BIT_7 - -#define QLCNIC_DUMP_MASK_MIN 3 #define QLCNIC_DUMP_MASK_DEF 0x1f -#define QLCNIC_DUMP_MASK_MAX 0xff #define QLCNIC_FORCE_FW_DUMP_KEY 0xdeadfeed #define QLCNIC_ENABLE_FW_DUMP 0xaddfeed #define QLCNIC_DISABLE_FW_DUMP 0xbadfeed @@ -1489,12 +1263,6 @@ enum op_codes { #define QLCNIC_SET_QUIESCENT 0xadd00010 #define QLCNIC_RESET_QUIESCENT 0xadd00020 -struct qlcnic_dump_operations { - enum op_codes opcode; - u32 (*handler)(struct qlcnic_adapter *, struct qlcnic_dump_entry *, - __le32 *); -}; - struct _cdrp_cmd { u32 cmd; u32 arg1; @@ -1552,6 +1320,8 @@ void qlcnic_pcie_sem_unlock(struct qlcnic_adapter *, int); #define __QLCNIC_MAX_LED_RATE 0xf #define __QLCNIC_MAX_LED_STATE 0x2 +#define MAX_CTL_CHECK 1000 + int qlcnic_get_board_info(struct qlcnic_adapter *adapter); int qlcnic_wol_supported(struct qlcnic_adapter *adapter); int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate); @@ -1648,6 +1418,26 @@ int qlcnic_clear_esw_stats(struct qlcnic_adapter *adapter, u8, u8, u8); int qlcnic_get_mac_stats(struct qlcnic_adapter *, struct qlcnic_mac_statistics *); extern int qlcnic_config_tso; +int qlcnic_napi_add(struct qlcnic_adapter *, struct net_device *); +void qlcnic_napi_del(struct qlcnic_adapter *adapter); +void qlcnic_napi_enable(struct qlcnic_adapter *adapter); +void qlcnic_napi_disable(struct qlcnic_adapter *adapter); +int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *, int); +void qlcnic_free_sds_rings(struct qlcnic_recv_context *); +void qlcnic_free_tx_rings(struct qlcnic_adapter *); +int qlcnic_alloc_tx_rings(struct qlcnic_adapter *, struct net_device *); + +void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter); +void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter); +void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter); +void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter); +int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32); +int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); +void qlcnic_set_vlan_config(struct qlcnic_adapter *, + struct qlcnic_esw_func_cfg *); +void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *, + struct qlcnic_esw_func_cfg *); + /* * QLOGIC Board information */ @@ -1694,6 +1484,21 @@ static inline u32 qlcnic_tx_avail(struct qlcnic_host_tx_ring *tx_ring) tx_ring->producer; } +static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring) +{ + writel(0, sds_ring->crb_intr_mask); +} + +static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring) +{ + struct qlcnic_adapter *adapter = sds_ring->adapter; + + writel(0x1, sds_ring->crb_intr_mask); + + if (!QLCNIC_IS_MSI_FAMILY(adapter)) + writel(0xfbff, adapter->tgt_mask_reg); +} + extern const struct ethtool_ops qlcnic_ethtool_ops; extern const struct ethtool_ops qlcnic_ethtool_failed_ops; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index aeacf1deea4..ff879cd2925 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -266,33 +266,6 @@ static const unsigned crb_hub_agt[64] = { 0, }; -static void qlcnic_read_dump_reg(u32 addr, void __iomem *bar0, u32 *data) -{ - u32 dest; - void __iomem *window_reg; - - dest = addr & 0xFFFF0000; - window_reg = bar0 + QLCNIC_FW_DUMP_REG1; - writel(dest, window_reg); - readl(window_reg); - window_reg = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr); - *data = readl(window_reg); -} - -static void qlcnic_write_dump_reg(u32 addr, void __iomem *bar0, u32 data) -{ - u32 dest; - void __iomem *window_reg; - - dest = addr & 0xFFFF0000; - window_reg = bar0 + QLCNIC_FW_DUMP_REG1; - writel(dest, window_reg); - readl(window_reg); - window_reg = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr); - writel(data, window_reg); - readl(window_reg); -} - /* PCI Windowing for DDR regions. */ #define QLCNIC_PCIE_SEM_TIMEOUT 10000 @@ -1088,8 +1061,6 @@ qlcnic_pci_camqm_write_2M(struct qlcnic_adapter *adapter, u64 off, u64 data) mutex_unlock(&adapter->ahw->mem_lock); } -#define MAX_CTL_CHECK 1000 - int qlcnic_pci_mem_write_2M(struct qlcnic_adapter *adapter, u64 off, u64 data) @@ -1347,460 +1318,3 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) return rv; } - -/* FW dump related functions */ -static u32 qlcnic_dump_crb(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i; - u32 addr, data; - struct __crb *crb = &entry->region.crb; - void __iomem *base = adapter->ahw->pci_base0; - - addr = crb->addr; - - for (i = 0; i < crb->no_ops; i++) { - qlcnic_read_dump_reg(addr, base, &data); - *buffer++ = cpu_to_le32(addr); - *buffer++ = cpu_to_le32(data); - addr += crb->stride; - } - return crb->no_ops * 2 * sizeof(u32); -} - -static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i, k, timeout = 0; - void __iomem *base = adapter->ahw->pci_base0; - u32 addr, data; - u8 opcode, no_ops; - struct __ctrl *ctr = &entry->region.ctrl; - struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr; - - addr = ctr->addr; - no_ops = ctr->no_ops; - - for (i = 0; i < no_ops; i++) { - k = 0; - opcode = 0; - for (k = 0; k < 8; k++) { - if (!(ctr->opcode & (1 << k))) - continue; - switch (1 << k) { - case QLCNIC_DUMP_WCRB: - qlcnic_write_dump_reg(addr, base, ctr->val1); - break; - case QLCNIC_DUMP_RWCRB: - qlcnic_read_dump_reg(addr, base, &data); - qlcnic_write_dump_reg(addr, base, data); - break; - case QLCNIC_DUMP_ANDCRB: - qlcnic_read_dump_reg(addr, base, &data); - qlcnic_write_dump_reg(addr, base, - data & ctr->val2); - break; - case QLCNIC_DUMP_ORCRB: - qlcnic_read_dump_reg(addr, base, &data); - qlcnic_write_dump_reg(addr, base, - data | ctr->val3); - break; - case QLCNIC_DUMP_POLLCRB: - while (timeout <= ctr->timeout) { - qlcnic_read_dump_reg(addr, base, &data); - if ((data & ctr->val2) == ctr->val1) - break; - msleep(1); - timeout++; - } - if (timeout > ctr->timeout) { - dev_info(&adapter->pdev->dev, - "Timed out, aborting poll CRB\n"); - return -EINVAL; - } - break; - case QLCNIC_DUMP_RD_SAVE: - if (ctr->index_a) - addr = t_hdr->saved_state[ctr->index_a]; - qlcnic_read_dump_reg(addr, base, &data); - t_hdr->saved_state[ctr->index_v] = data; - break; - case QLCNIC_DUMP_WRT_SAVED: - if (ctr->index_v) - data = t_hdr->saved_state[ctr->index_v]; - else - data = ctr->val1; - if (ctr->index_a) - addr = t_hdr->saved_state[ctr->index_a]; - qlcnic_write_dump_reg(addr, base, data); - break; - case QLCNIC_DUMP_MOD_SAVE_ST: - data = t_hdr->saved_state[ctr->index_v]; - data <<= ctr->shl_val; - data >>= ctr->shr_val; - if (ctr->val2) - data &= ctr->val2; - data |= ctr->val3; - data += ctr->val1; - t_hdr->saved_state[ctr->index_v] = data; - break; - default: - dev_info(&adapter->pdev->dev, - "Unknown opcode\n"); - break; - } - } - addr += ctr->stride; - } - return 0; -} - -static u32 qlcnic_dump_mux(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int loop; - u32 val, data = 0; - struct __mux *mux = &entry->region.mux; - void __iomem *base = adapter->ahw->pci_base0; - - val = mux->val; - for (loop = 0; loop < mux->no_ops; loop++) { - qlcnic_write_dump_reg(mux->addr, base, val); - qlcnic_read_dump_reg(mux->read_addr, base, &data); - *buffer++ = cpu_to_le32(val); - *buffer++ = cpu_to_le32(data); - val += mux->val_stride; - } - return 2 * mux->no_ops * sizeof(u32); -} - -static u32 qlcnic_dump_que(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i, loop; - u32 cnt, addr, data, que_id = 0; - void __iomem *base = adapter->ahw->pci_base0; - struct __queue *que = &entry->region.que; - - addr = que->read_addr; - cnt = que->read_addr_cnt; - - for (loop = 0; loop < que->no_ops; loop++) { - qlcnic_write_dump_reg(que->sel_addr, base, que_id); - addr = que->read_addr; - for (i = 0; i < cnt; i++) { - qlcnic_read_dump_reg(addr, base, &data); - *buffer++ = cpu_to_le32(data); - addr += que->read_addr_stride; - } - que_id += que->stride; - } - return que->no_ops * cnt * sizeof(u32); -} - -static u32 qlcnic_dump_ocm(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i; - u32 data; - void __iomem *addr; - struct __ocm *ocm = &entry->region.ocm; - - addr = adapter->ahw->pci_base0 + ocm->read_addr; - for (i = 0; i < ocm->no_ops; i++) { - data = readl(addr); - *buffer++ = cpu_to_le32(data); - addr += ocm->read_addr_stride; - } - return ocm->no_ops * sizeof(u32); -} - -static u32 qlcnic_read_rom(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i, count = 0; - u32 fl_addr, size, val, lck_val, addr; - struct __mem *rom = &entry->region.mem; - void __iomem *base = adapter->ahw->pci_base0; - - fl_addr = rom->addr; - size = rom->size/4; -lock_try: - lck_val = readl(base + QLCNIC_FLASH_SEM2_LK); - if (!lck_val && count < MAX_CTL_CHECK) { - msleep(10); - count++; - goto lock_try; - } - writel(adapter->ahw->pci_func, (base + QLCNIC_FLASH_LOCK_ID)); - for (i = 0; i < size; i++) { - addr = fl_addr & 0xFFFF0000; - qlcnic_write_dump_reg(FLASH_ROM_WINDOW, base, addr); - addr = LSW(fl_addr) + FLASH_ROM_DATA; - qlcnic_read_dump_reg(addr, base, &val); - fl_addr += 4; - *buffer++ = cpu_to_le32(val); - } - readl(base + QLCNIC_FLASH_SEM2_ULK); - return rom->size; -} - -static u32 qlcnic_dump_l1_cache(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i; - u32 cnt, val, data, addr; - void __iomem *base = adapter->ahw->pci_base0; - struct __cache *l1 = &entry->region.cache; - - val = l1->init_tag_val; - - for (i = 0; i < l1->no_ops; i++) { - qlcnic_write_dump_reg(l1->addr, base, val); - qlcnic_write_dump_reg(l1->ctrl_addr, base, LSW(l1->ctrl_val)); - addr = l1->read_addr; - cnt = l1->read_addr_num; - while (cnt) { - qlcnic_read_dump_reg(addr, base, &data); - *buffer++ = cpu_to_le32(data); - addr += l1->read_addr_stride; - cnt--; - } - val += l1->stride; - } - return l1->no_ops * l1->read_addr_num * sizeof(u32); -} - -static u32 qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - int i; - u32 cnt, val, data, addr; - u8 poll_mask, poll_to, time_out = 0; - void __iomem *base = adapter->ahw->pci_base0; - struct __cache *l2 = &entry->region.cache; - - val = l2->init_tag_val; - poll_mask = LSB(MSW(l2->ctrl_val)); - poll_to = MSB(MSW(l2->ctrl_val)); - - for (i = 0; i < l2->no_ops; i++) { - qlcnic_write_dump_reg(l2->addr, base, val); - if (LSW(l2->ctrl_val)) - qlcnic_write_dump_reg(l2->ctrl_addr, base, - LSW(l2->ctrl_val)); - if (!poll_mask) - goto skip_poll; - do { - qlcnic_read_dump_reg(l2->ctrl_addr, base, &data); - if (!(data & poll_mask)) - break; - msleep(1); - time_out++; - } while (time_out <= poll_to); - - if (time_out > poll_to) { - dev_err(&adapter->pdev->dev, - "Timeout exceeded in %s, aborting dump\n", - __func__); - return -EINVAL; - } -skip_poll: - addr = l2->read_addr; - cnt = l2->read_addr_num; - while (cnt) { - qlcnic_read_dump_reg(addr, base, &data); - *buffer++ = cpu_to_le32(data); - addr += l2->read_addr_stride; - cnt--; - } - val += l2->stride; - } - return l2->no_ops * l2->read_addr_num * sizeof(u32); -} - -static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - u32 addr, data, test, ret = 0; - int i, reg_read; - struct __mem *mem = &entry->region.mem; - void __iomem *base = adapter->ahw->pci_base0; - - reg_read = mem->size; - addr = mem->addr; - /* check for data size of multiple of 16 and 16 byte alignment */ - if ((addr & 0xf) || (reg_read%16)) { - dev_info(&adapter->pdev->dev, - "Unaligned memory addr:0x%x size:0x%x\n", - addr, reg_read); - return -EINVAL; - } - - mutex_lock(&adapter->ahw->mem_lock); - - while (reg_read != 0) { - qlcnic_write_dump_reg(MIU_TEST_ADDR_LO, base, addr); - qlcnic_write_dump_reg(MIU_TEST_ADDR_HI, base, 0); - qlcnic_write_dump_reg(MIU_TEST_CTR, base, - TA_CTL_ENABLE | TA_CTL_START); - - for (i = 0; i < MAX_CTL_CHECK; i++) { - qlcnic_read_dump_reg(MIU_TEST_CTR, base, &test); - if (!(test & TA_CTL_BUSY)) - break; - } - if (i == MAX_CTL_CHECK) { - if (printk_ratelimit()) { - dev_err(&adapter->pdev->dev, - "failed to read through agent\n"); - ret = -EINVAL; - goto out; - } - } - for (i = 0; i < 4; i++) { - qlcnic_read_dump_reg(MIU_TEST_READ_DATA[i], base, - &data); - *buffer++ = cpu_to_le32(data); - } - addr += 16; - reg_read -= 16; - ret += 16; - } -out: - mutex_unlock(&adapter->ahw->mem_lock); - return mem->size; -} - -static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter, - struct qlcnic_dump_entry *entry, __le32 *buffer) -{ - entry->hdr.flags |= QLCNIC_DUMP_SKIP; - return 0; -} - -static const struct qlcnic_dump_operations fw_dump_ops[] = { - { QLCNIC_DUMP_NOP, qlcnic_dump_nop }, - { QLCNIC_DUMP_READ_CRB, qlcnic_dump_crb }, - { QLCNIC_DUMP_READ_MUX, qlcnic_dump_mux }, - { QLCNIC_DUMP_QUEUE, qlcnic_dump_que }, - { QLCNIC_DUMP_BRD_CONFIG, qlcnic_read_rom }, - { QLCNIC_DUMP_READ_OCM, qlcnic_dump_ocm }, - { QLCNIC_DUMP_PEG_REG, qlcnic_dump_ctrl }, - { QLCNIC_DUMP_L1_DTAG, qlcnic_dump_l1_cache }, - { QLCNIC_DUMP_L1_ITAG, qlcnic_dump_l1_cache }, - { QLCNIC_DUMP_L1_DATA, qlcnic_dump_l1_cache }, - { QLCNIC_DUMP_L1_INST, qlcnic_dump_l1_cache }, - { QLCNIC_DUMP_L2_DTAG, qlcnic_dump_l2_cache }, - { QLCNIC_DUMP_L2_ITAG, qlcnic_dump_l2_cache }, - { QLCNIC_DUMP_L2_DATA, qlcnic_dump_l2_cache }, - { QLCNIC_DUMP_L2_INST, qlcnic_dump_l2_cache }, - { QLCNIC_DUMP_READ_ROM, qlcnic_read_rom }, - { QLCNIC_DUMP_READ_MEM, qlcnic_read_memory }, - { QLCNIC_DUMP_READ_CTRL, qlcnic_dump_ctrl }, - { QLCNIC_DUMP_TLHDR, qlcnic_dump_nop }, - { QLCNIC_DUMP_RDEND, qlcnic_dump_nop }, -}; - -/* Walk the template and collect dump for each entry in the dump template */ -static int -qlcnic_valid_dump_entry(struct device *dev, struct qlcnic_dump_entry *entry, - u32 size) -{ - int ret = 1; - if (size != entry->hdr.cap_size) { - dev_info(dev, - "Invalidate dump, Type:%d\tMask:%d\tSize:%dCap_size:%d\n", - entry->hdr.type, entry->hdr.mask, size, entry->hdr.cap_size); - dev_info(dev, "Aborting further dump capture\n"); - ret = 0; - } - return ret; -} - -int qlcnic_dump_fw(struct qlcnic_adapter *adapter) -{ - __le32 *buffer; - char mesg[64]; - char *msg[] = {mesg, NULL}; - int i, k, ops_cnt, ops_index, dump_size = 0; - u32 entry_offset, dump, no_entries, buf_offset = 0; - struct qlcnic_dump_entry *entry; - struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; - struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr; - - if (fw_dump->clr) { - dev_info(&adapter->pdev->dev, - "Previous dump not cleared, not capturing dump\n"); - return -EIO; - } - /* Calculate the size for dump data area only */ - for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++) - if (i & tmpl_hdr->drv_cap_mask) - dump_size += tmpl_hdr->cap_sizes[k]; - if (!dump_size) - return -EIO; - - fw_dump->data = vzalloc(dump_size); - if (!fw_dump->data) { - dev_info(&adapter->pdev->dev, - "Unable to allocate (%d KB) for fw dump\n", - dump_size/1024); - return -ENOMEM; - } - buffer = fw_dump->data; - fw_dump->size = dump_size; - no_entries = tmpl_hdr->num_entries; - ops_cnt = ARRAY_SIZE(fw_dump_ops); - entry_offset = tmpl_hdr->offset; - tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION; - tmpl_hdr->sys_info[1] = adapter->fw_version; - - for (i = 0; i < no_entries; i++) { - entry = (void *)tmpl_hdr + entry_offset; - if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) { - entry->hdr.flags |= QLCNIC_DUMP_SKIP; - entry_offset += entry->hdr.offset; - continue; - } - /* Find the handler for this entry */ - ops_index = 0; - while (ops_index < ops_cnt) { - if (entry->hdr.type == fw_dump_ops[ops_index].opcode) - break; - ops_index++; - } - if (ops_index == ops_cnt) { - dev_info(&adapter->pdev->dev, - "Invalid entry type %d, exiting dump\n", - entry->hdr.type); - goto error; - } - /* Collect dump for this entry */ - dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer); - if (dump && !qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, - dump)) - entry->hdr.flags |= QLCNIC_DUMP_SKIP; - buf_offset += entry->hdr.cap_size; - entry_offset += entry->hdr.offset; - buffer = fw_dump->data + buf_offset; - } - if (dump_size != buf_offset) { - dev_info(&adapter->pdev->dev, - "Captured(%d) and expected size(%d) do not match\n", - buf_offset, dump_size); - goto error; - } else { - fw_dump->clr = 1; - snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", - adapter->netdev->name); - dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n", - fw_dump->size); - /* Send a udev event to notify availability of FW dump */ - kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg); - return 0; - } -error: - vfree(fw_dump->data); - return -EINVAL; -} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c index ccbef8491d2..d8610ea56a4 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_init.c @@ -25,10 +25,6 @@ static unsigned int crb_addr_xform[QLCNIC_MAX_CRB_XFORM]; #define QLCNIC_ADDR_ERROR (0xffffffff) -static void -qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, - struct qlcnic_host_rds_ring *rds_ring); - static int qlcnic_check_fw_hearbeat(struct qlcnic_adapter *adapter); @@ -1332,629 +1328,3 @@ qlcnic_release_firmware(struct qlcnic_adapter *adapter) release_firmware(adapter->fw); adapter->fw = NULL; } - -static void -qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, - struct qlcnic_fw_msg *msg) -{ - u32 cable_OUI; - u16 cable_len; - u16 link_speed; - u8 link_status, module, duplex, autoneg; - u8 lb_status = 0; - struct net_device *netdev = adapter->netdev; - - adapter->has_link_events = 1; - - cable_OUI = msg->body[1] & 0xffffffff; - cable_len = (msg->body[1] >> 32) & 0xffff; - link_speed = (msg->body[1] >> 48) & 0xffff; - - link_status = msg->body[2] & 0xff; - duplex = (msg->body[2] >> 16) & 0xff; - autoneg = (msg->body[2] >> 24) & 0xff; - lb_status = (msg->body[2] >> 32) & 0x3; - - module = (msg->body[2] >> 8) & 0xff; - if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) - dev_info(&netdev->dev, "unsupported cable: OUI 0x%x, " - "length %d\n", cable_OUI, cable_len); - else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN) - dev_info(&netdev->dev, "unsupported cable length %d\n", - cable_len); - - if (!link_status && (lb_status == QLCNIC_ILB_MODE || - lb_status == QLCNIC_ELB_MODE)) - adapter->ahw->loopback_state |= QLCNIC_LINKEVENT; - - qlcnic_advert_link_change(adapter, link_status); - - if (duplex == LINKEVENT_FULL_DUPLEX) - adapter->link_duplex = DUPLEX_FULL; - else - adapter->link_duplex = DUPLEX_HALF; - - adapter->module_type = module; - adapter->link_autoneg = autoneg; - - if (link_status) { - adapter->link_speed = link_speed; - } else { - adapter->link_speed = SPEED_UNKNOWN; - adapter->link_duplex = DUPLEX_UNKNOWN; - } -} - -static void -qlcnic_handle_fw_message(int desc_cnt, int index, - struct qlcnic_host_sds_ring *sds_ring) -{ - struct qlcnic_fw_msg msg; - struct status_desc *desc; - struct qlcnic_adapter *adapter; - struct device *dev; - int i = 0, opcode, ret; - - while (desc_cnt > 0 && i < 8) { - desc = &sds_ring->desc_head[index]; - msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]); - msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]); - - index = get_next_index(index, sds_ring->num_desc); - desc_cnt--; - } - - adapter = sds_ring->adapter; - dev = &adapter->pdev->dev; - opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); - - switch (opcode) { - case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: - qlcnic_handle_linkevent(adapter, &msg); - break; - case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK: - ret = (u32)(msg.body[1]); - switch (ret) { - case 0: - adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE; - break; - case 1: - dev_info(dev, "loopback already in progress\n"); - adapter->diag_cnt = -QLCNIC_TEST_IN_PROGRESS; - break; - case 2: - dev_info(dev, "loopback cable is not connected\n"); - adapter->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN; - break; - default: - dev_info(dev, "loopback configure request failed," - " ret %x\n", ret); - adapter->diag_cnt = -QLCNIC_UNDEFINED_ERROR; - break; - } - break; - default: - break; - } -} - -static int -qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter, - struct qlcnic_host_rds_ring *rds_ring, - struct qlcnic_rx_buffer *buffer) -{ - struct sk_buff *skb; - dma_addr_t dma; - struct pci_dev *pdev = adapter->pdev; - - skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size); - if (!skb) { - adapter->stats.skb_alloc_failure++; - return -ENOMEM; - } - - skb_reserve(skb, NET_IP_ALIGN); - - dma = pci_map_single(pdev, skb->data, - rds_ring->dma_size, PCI_DMA_FROMDEVICE); - - if (pci_dma_mapping_error(pdev, dma)) { - adapter->stats.rx_dma_map_error++; - dev_kfree_skb_any(skb); - return -ENOMEM; - } - - buffer->skb = skb; - buffer->dma = dma; - - return 0; -} - -static struct sk_buff *qlcnic_process_rxbuf(struct qlcnic_adapter *adapter, - struct qlcnic_host_rds_ring *rds_ring, u16 index, u16 cksum) -{ - struct qlcnic_rx_buffer *buffer; - struct sk_buff *skb; - - buffer = &rds_ring->rx_buf_arr[index]; - - if (unlikely(buffer->skb == NULL)) { - WARN_ON(1); - return NULL; - } - - pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size, - PCI_DMA_FROMDEVICE); - - skb = buffer->skb; - - if (likely((adapter->netdev->features & NETIF_F_RXCSUM) && - (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) { - adapter->stats.csummed++; - skb->ip_summed = CHECKSUM_UNNECESSARY; - } else { - skb_checksum_none_assert(skb); - } - - buffer->skb = NULL; - - return skb; -} - -static inline int -qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter, struct sk_buff *skb, - u16 *vlan_tag) -{ - struct ethhdr *eth_hdr; - - if (!__vlan_get_tag(skb, vlan_tag)) { - eth_hdr = (struct ethhdr *) skb->data; - memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2); - skb_pull(skb, VLAN_HLEN); - } - if (!adapter->pvid) - return 0; - - if (*vlan_tag == adapter->pvid) { - /* Outer vlan tag. Packet should follow non-vlan path */ - *vlan_tag = 0xffff; - return 0; - } - if (adapter->flags & QLCNIC_TAGGING_ENABLED) - return 0; - - return -EINVAL; -} - -static struct qlcnic_rx_buffer * -qlcnic_process_rcv(struct qlcnic_adapter *adapter, - struct qlcnic_host_sds_ring *sds_ring, - int ring, u64 sts_data0) -{ - struct net_device *netdev = adapter->netdev; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - struct qlcnic_rx_buffer *buffer; - struct sk_buff *skb; - struct qlcnic_host_rds_ring *rds_ring; - int index, length, cksum, pkt_offset; - u16 vid = 0xffff; - - if (unlikely(ring >= adapter->max_rds_rings)) - return NULL; - - rds_ring = &recv_ctx->rds_rings[ring]; - - index = qlcnic_get_sts_refhandle(sts_data0); - if (unlikely(index >= rds_ring->num_desc)) - return NULL; - - buffer = &rds_ring->rx_buf_arr[index]; - - length = qlcnic_get_sts_totallength(sts_data0); - cksum = qlcnic_get_sts_status(sts_data0); - pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); - - skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); - if (!skb) - return buffer; - - if (length > rds_ring->skb_size) - skb_put(skb, rds_ring->skb_size); - else - skb_put(skb, length); - - if (pkt_offset) - skb_pull(skb, pkt_offset); - - if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { - adapter->stats.rxdropped++; - dev_kfree_skb(skb); - return buffer; - } - - skb->protocol = eth_type_trans(skb, netdev); - - if (vid != 0xffff) - __vlan_hwaccel_put_tag(skb, vid); - - napi_gro_receive(&sds_ring->napi, skb); - - adapter->stats.rx_pkts++; - adapter->stats.rxbytes += length; - - return buffer; -} - -#define QLC_TCP_HDR_SIZE 20 -#define QLC_TCP_TS_OPTION_SIZE 12 -#define QLC_TCP_TS_HDR_SIZE (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE) - -static struct qlcnic_rx_buffer * -qlcnic_process_lro(struct qlcnic_adapter *adapter, - int ring, u64 sts_data0, u64 sts_data1) -{ - struct net_device *netdev = adapter->netdev; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - struct qlcnic_rx_buffer *buffer; - struct sk_buff *skb; - struct qlcnic_host_rds_ring *rds_ring; - struct iphdr *iph; - struct tcphdr *th; - bool push, timestamp; - int l2_hdr_offset, l4_hdr_offset; - int index; - u16 lro_length, length, data_offset; - u32 seq_number; - u16 vid = 0xffff; - - if (unlikely(ring > adapter->max_rds_rings)) - return NULL; - - rds_ring = &recv_ctx->rds_rings[ring]; - - index = qlcnic_get_lro_sts_refhandle(sts_data0); - if (unlikely(index > rds_ring->num_desc)) - return NULL; - - buffer = &rds_ring->rx_buf_arr[index]; - - timestamp = qlcnic_get_lro_sts_timestamp(sts_data0); - lro_length = qlcnic_get_lro_sts_length(sts_data0); - l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0); - l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0); - push = qlcnic_get_lro_sts_push_flag(sts_data0); - seq_number = qlcnic_get_lro_sts_seq_number(sts_data1); - - skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); - if (!skb) - return buffer; - - if (timestamp) - data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE; - else - data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE; - - skb_put(skb, lro_length + data_offset); - - skb_pull(skb, l2_hdr_offset); - - if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { - adapter->stats.rxdropped++; - dev_kfree_skb(skb); - return buffer; - } - - skb->protocol = eth_type_trans(skb, netdev); - - iph = (struct iphdr *)skb->data; - th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); - - length = (iph->ihl << 2) + (th->doff << 2) + lro_length; - iph->tot_len = htons(length); - iph->check = 0; - iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); - th->psh = push; - th->seq = htonl(seq_number); - - length = skb->len; - - if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) - skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1); - - if (vid != 0xffff) - __vlan_hwaccel_put_tag(skb, vid); - netif_receive_skb(skb); - - adapter->stats.lro_pkts++; - adapter->stats.lrobytes += length; - - return buffer; -} - -int -qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max) -{ - struct qlcnic_adapter *adapter = sds_ring->adapter; - struct list_head *cur; - struct status_desc *desc; - struct qlcnic_rx_buffer *rxbuf; - u64 sts_data0, sts_data1; - - int count = 0; - int opcode, ring, desc_cnt; - u32 consumer = sds_ring->consumer; - - while (count < max) { - desc = &sds_ring->desc_head[consumer]; - sts_data0 = le64_to_cpu(desc->status_desc_data[0]); - - if (!(sts_data0 & STATUS_OWNER_HOST)) - break; - - desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); - opcode = qlcnic_get_sts_opcode(sts_data0); - - switch (opcode) { - case QLCNIC_RXPKT_DESC: - case QLCNIC_OLD_RXPKT_DESC: - case QLCNIC_SYN_OFFLOAD: - ring = qlcnic_get_sts_type(sts_data0); - rxbuf = qlcnic_process_rcv(adapter, sds_ring, - ring, sts_data0); - break; - case QLCNIC_LRO_DESC: - ring = qlcnic_get_lro_sts_type(sts_data0); - sts_data1 = le64_to_cpu(desc->status_desc_data[1]); - rxbuf = qlcnic_process_lro(adapter, ring, sts_data0, - sts_data1); - break; - case QLCNIC_RESPONSE_DESC: - qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); - default: - goto skip; - } - - WARN_ON(desc_cnt > 1); - - if (likely(rxbuf)) - list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]); - else - adapter->stats.null_rxbuf++; - -skip: - for (; desc_cnt > 0; desc_cnt--) { - desc = &sds_ring->desc_head[consumer]; - desc->status_desc_data[0] = - cpu_to_le64(STATUS_OWNER_PHANTOM); - consumer = get_next_index(consumer, sds_ring->num_desc); - } - count++; - } - - for (ring = 0; ring < adapter->max_rds_rings; ring++) { - struct qlcnic_host_rds_ring *rds_ring = - &adapter->recv_ctx->rds_rings[ring]; - - if (!list_empty(&sds_ring->free_list[ring])) { - list_for_each(cur, &sds_ring->free_list[ring]) { - rxbuf = list_entry(cur, - struct qlcnic_rx_buffer, list); - qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf); - } - spin_lock(&rds_ring->lock); - list_splice_tail_init(&sds_ring->free_list[ring], - &rds_ring->free_list); - spin_unlock(&rds_ring->lock); - } - - qlcnic_post_rx_buffers_nodb(adapter, rds_ring); - } - - if (count) { - sds_ring->consumer = consumer; - writel(consumer, sds_ring->crb_sts_consumer); - } - - return count; -} - -void -qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, - struct qlcnic_host_rds_ring *rds_ring) -{ - struct rcv_desc *pdesc; - struct qlcnic_rx_buffer *buffer; - int count = 0; - u32 producer; - struct list_head *head; - - producer = rds_ring->producer; - - head = &rds_ring->free_list; - while (!list_empty(head)) { - - buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); - - if (!buffer->skb) { - if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) - break; - } - - count++; - list_del(&buffer->list); - - /* make a rcv descriptor */ - pdesc = &rds_ring->desc_head[producer]; - pdesc->addr_buffer = cpu_to_le64(buffer->dma); - pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); - pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); - - producer = get_next_index(producer, rds_ring->num_desc); - } - - if (count) { - rds_ring->producer = producer; - writel((producer-1) & (rds_ring->num_desc-1), - rds_ring->crb_rcv_producer); - } -} - -static void -qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, - struct qlcnic_host_rds_ring *rds_ring) -{ - struct rcv_desc *pdesc; - struct qlcnic_rx_buffer *buffer; - int count = 0; - uint32_t producer; - struct list_head *head; - - if (!spin_trylock(&rds_ring->lock)) - return; - - producer = rds_ring->producer; - - head = &rds_ring->free_list; - while (!list_empty(head)) { - - buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); - - if (!buffer->skb) { - if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) - break; - } - - count++; - list_del(&buffer->list); - - /* make a rcv descriptor */ - pdesc = &rds_ring->desc_head[producer]; - pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); - pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); - pdesc->addr_buffer = cpu_to_le64(buffer->dma); - - producer = get_next_index(producer, rds_ring->num_desc); - } - - if (count) { - rds_ring->producer = producer; - writel((producer - 1) & (rds_ring->num_desc - 1), - rds_ring->crb_rcv_producer); - } - spin_unlock(&rds_ring->lock); -} - -static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) -{ - int i; - unsigned char *data = skb->data; - - printk(KERN_INFO "\n"); - for (i = 0; i < skb->len; i++) { - QLCDB(adapter, DRV, "%02x ", data[i]); - if ((i & 0x0f) == 8) - printk(KERN_INFO "\n"); - } -} - -static void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, int ring, - u64 sts_data0) -{ - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - struct sk_buff *skb; - struct qlcnic_host_rds_ring *rds_ring; - int index, length, cksum, pkt_offset; - - if (unlikely(ring >= adapter->max_rds_rings)) - return; - - rds_ring = &recv_ctx->rds_rings[ring]; - - index = qlcnic_get_sts_refhandle(sts_data0); - length = qlcnic_get_sts_totallength(sts_data0); - if (unlikely(index >= rds_ring->num_desc)) - return; - - cksum = qlcnic_get_sts_status(sts_data0); - pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); - - skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); - if (!skb) - return; - - if (length > rds_ring->skb_size) - skb_put(skb, rds_ring->skb_size); - else - skb_put(skb, length); - - if (pkt_offset) - skb_pull(skb, pkt_offset); - - if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) - adapter->diag_cnt++; - else - dump_skb(skb, adapter); - - dev_kfree_skb_any(skb); - adapter->stats.rx_pkts++; - adapter->stats.rxbytes += length; - - return; -} - -void -qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) -{ - struct qlcnic_adapter *adapter = sds_ring->adapter; - struct status_desc *desc; - u64 sts_data0; - int ring, opcode, desc_cnt; - - u32 consumer = sds_ring->consumer; - - desc = &sds_ring->desc_head[consumer]; - sts_data0 = le64_to_cpu(desc->status_desc_data[0]); - - if (!(sts_data0 & STATUS_OWNER_HOST)) - return; - - desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); - opcode = qlcnic_get_sts_opcode(sts_data0); - switch (opcode) { - case QLCNIC_RESPONSE_DESC: - qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); - break; - default: - ring = qlcnic_get_sts_type(sts_data0); - qlcnic_process_rcv_diag(adapter, ring, sts_data0); - break; - } - - for (; desc_cnt > 0; desc_cnt--) { - desc = &sds_ring->desc_head[consumer]; - desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); - consumer = get_next_index(consumer, sds_ring->num_desc); - } - - sds_ring->consumer = consumer; - writel(consumer, sds_ring->crb_sts_consumer); -} - -void qlcnic_fetch_mac(u32 off1, u32 off2, u8 alt_mac, u8 *mac) -{ - u32 mac_low, mac_high; - int i; - - mac_low = off1; - mac_high = off2; - - if (alt_mac) { - mac_low |= (mac_low >> 16) | (mac_high << 16); - mac_high >>= 16; - } - - for (i = 0; i < 2; i++) - mac[i] = (u8)(mac_high >> ((1 - i) * 8)); - for (i = 2; i < 6; i++) - mac[i] = (u8)(mac_low >> ((5 - i) * 8)); -} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c new file mode 100644 index 00000000000..ba352c18c35 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -0,0 +1,1309 @@ +#include <linux/netdevice.h> +#include <linux/if_vlan.h> +#include <net/ip.h> +#include <linux/ipv6.h> + +#include "qlcnic.h" + +#define QLCNIC_MAC_HASH(MAC)\ + ((((MAC) & 0x70000) >> 0x10) | (((MAC) & 0x70000000000ULL) >> 0x25)) + +#define TX_ETHER_PKT 0x01 +#define TX_TCP_PKT 0x02 +#define TX_UDP_PKT 0x03 +#define TX_IP_PKT 0x04 +#define TX_TCP_LSO 0x05 +#define TX_TCP_LSO6 0x06 +#define TX_TCPV6_PKT 0x0b +#define TX_UDPV6_PKT 0x0c +#define FLAGS_VLAN_TAGGED 0x10 +#define FLAGS_VLAN_OOB 0x40 + +#define qlcnic_set_tx_vlan_tci(cmd_desc, v) \ + (cmd_desc)->vlan_TCI = cpu_to_le16(v); +#define qlcnic_set_cmd_desc_port(cmd_desc, var) \ + ((cmd_desc)->port_ctxid |= ((var) & 0x0F)) +#define qlcnic_set_cmd_desc_ctxid(cmd_desc, var) \ + ((cmd_desc)->port_ctxid |= ((var) << 4 & 0xF0)) + +#define qlcnic_set_tx_port(_desc, _port) \ + ((_desc)->port_ctxid = ((_port) & 0xf) | (((_port) << 4) & 0xf0)) + +#define qlcnic_set_tx_flags_opcode(_desc, _flags, _opcode) \ + ((_desc)->flags_opcode |= \ + cpu_to_le16(((_flags) & 0x7f) | (((_opcode) & 0x3f) << 7))) + +#define qlcnic_set_tx_frags_len(_desc, _frags, _len) \ + ((_desc)->nfrags__length = \ + cpu_to_le32(((_frags) & 0xff) | (((_len) & 0xffffff) << 8))) + +/* owner bits of status_desc */ +#define STATUS_OWNER_HOST (0x1ULL << 56) +#define STATUS_OWNER_PHANTOM (0x2ULL << 56) + +/* Status descriptor: + 0-3 port, 4-7 status, 8-11 type, 12-27 total_length + 28-43 reference_handle, 44-47 protocol, 48-52 pkt_offset + 53-55 desc_cnt, 56-57 owner, 58-63 opcode + */ +#define qlcnic_get_sts_port(sts_data) \ + ((sts_data) & 0x0F) +#define qlcnic_get_sts_status(sts_data) \ + (((sts_data) >> 4) & 0x0F) +#define qlcnic_get_sts_type(sts_data) \ + (((sts_data) >> 8) & 0x0F) +#define qlcnic_get_sts_totallength(sts_data) \ + (((sts_data) >> 12) & 0xFFFF) +#define qlcnic_get_sts_refhandle(sts_data) \ + (((sts_data) >> 28) & 0xFFFF) +#define qlcnic_get_sts_prot(sts_data) \ + (((sts_data) >> 44) & 0x0F) +#define qlcnic_get_sts_pkt_offset(sts_data) \ + (((sts_data) >> 48) & 0x1F) +#define qlcnic_get_sts_desc_cnt(sts_data) \ + (((sts_data) >> 53) & 0x7) +#define qlcnic_get_sts_opcode(sts_data) \ + (((sts_data) >> 58) & 0x03F) + +#define qlcnic_get_lro_sts_refhandle(sts_data) \ + ((sts_data) & 0x0FFFF) +#define qlcnic_get_lro_sts_length(sts_data) \ + (((sts_data) >> 16) & 0x0FFFF) +#define qlcnic_get_lro_sts_l2_hdr_offset(sts_data) \ + (((sts_data) >> 32) & 0x0FF) +#define qlcnic_get_lro_sts_l4_hdr_offset(sts_data) \ + (((sts_data) >> 40) & 0x0FF) +#define qlcnic_get_lro_sts_timestamp(sts_data) \ + (((sts_data) >> 48) & 0x1) +#define qlcnic_get_lro_sts_type(sts_data) \ + (((sts_data) >> 49) & 0x7) +#define qlcnic_get_lro_sts_push_flag(sts_data) \ + (((sts_data) >> 52) & 0x1) +#define qlcnic_get_lro_sts_seq_number(sts_data) \ + ((sts_data) & 0x0FFFFFFFF) +#define qlcnic_get_lro_sts_mss(sts_data1) \ + ((sts_data1 >> 32) & 0x0FFFF) + +/* opcode field in status_desc */ +#define QLCNIC_SYN_OFFLOAD 0x03 +#define QLCNIC_RXPKT_DESC 0x04 +#define QLCNIC_OLD_RXPKT_DESC 0x3f +#define QLCNIC_RESPONSE_DESC 0x05 +#define QLCNIC_LRO_DESC 0x12 + +/* for status field in status_desc */ +#define STATUS_CKSUM_LOOP 0 +#define STATUS_CKSUM_OK 2 + +static void qlcnic_change_filter(struct qlcnic_adapter *adapter, + u64 uaddr, __le16 vlan_id, + struct qlcnic_host_tx_ring *tx_ring) +{ + struct cmd_desc_type0 *hwdesc; + struct qlcnic_nic_req *req; + struct qlcnic_mac_req *mac_req; + struct qlcnic_vlan_req *vlan_req; + u32 producer; + u64 word; + + producer = tx_ring->producer; + hwdesc = &tx_ring->desc_head[tx_ring->producer]; + + req = (struct qlcnic_nic_req *)hwdesc; + memset(req, 0, sizeof(struct qlcnic_nic_req)); + req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23); + + word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16); + req->req_hdr = cpu_to_le64(word); + + mac_req = (struct qlcnic_mac_req *)&(req->words[0]); + mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD; + memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN); + + vlan_req = (struct qlcnic_vlan_req *)&req->words[1]; + vlan_req->vlan_id = vlan_id; + + tx_ring->producer = get_next_index(producer, tx_ring->num_desc); + smp_mb(); +} + +static void qlcnic_send_filter(struct qlcnic_adapter *adapter, + struct qlcnic_host_tx_ring *tx_ring, + struct cmd_desc_type0 *first_desc, + struct sk_buff *skb) +{ + struct ethhdr *phdr = (struct ethhdr *)(skb->data); + struct qlcnic_filter *fil, *tmp_fil; + struct hlist_node *tmp_hnode, *n; + struct hlist_head *head; + u64 src_addr = 0; + __le16 vlan_id = 0; + u8 hindex; + + if (ether_addr_equal(phdr->h_source, adapter->mac_addr)) + return; + + if (adapter->fhash.fnum >= adapter->fhash.fmax) + return; + + /* Only NPAR capable devices support vlan based learning*/ + if (adapter->flags & QLCNIC_ESWITCH_ENABLED) + vlan_id = first_desc->vlan_TCI; + memcpy(&src_addr, phdr->h_source, ETH_ALEN); + hindex = QLCNIC_MAC_HASH(src_addr) & (QLCNIC_LB_MAX_FILTERS - 1); + head = &(adapter->fhash.fhead[hindex]); + + hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { + if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) && + tmp_fil->vlan_id == vlan_id) { + + if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) + qlcnic_change_filter(adapter, src_addr, vlan_id, + tx_ring); + tmp_fil->ftime = jiffies; + return; + } + } + + fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC); + if (!fil) + return; + + qlcnic_change_filter(adapter, src_addr, vlan_id, tx_ring); + + fil->ftime = jiffies; + fil->vlan_id = vlan_id; + memcpy(fil->faddr, &src_addr, ETH_ALEN); + + spin_lock(&adapter->mac_learn_lock); + + hlist_add_head(&(fil->fnode), head); + adapter->fhash.fnum++; + + spin_unlock(&adapter->mac_learn_lock); +} + +static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter, + struct cmd_desc_type0 *first_desc, struct sk_buff *skb) +{ + u8 l4proto, opcode = 0, hdr_len = 0; + u16 flags = 0, vlan_tci = 0; + int copied, offset, copy_len, size; + struct cmd_desc_type0 *hwdesc; + struct vlan_ethhdr *vh; + struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; + u16 protocol = ntohs(skb->protocol); + u32 producer = tx_ring->producer; + + if (protocol == ETH_P_8021Q) { + vh = (struct vlan_ethhdr *)skb->data; + flags = FLAGS_VLAN_TAGGED; + vlan_tci = ntohs(vh->h_vlan_TCI); + protocol = ntohs(vh->h_vlan_encapsulated_proto); + } else if (vlan_tx_tag_present(skb)) { + flags = FLAGS_VLAN_OOB; + vlan_tci = vlan_tx_tag_get(skb); + } + if (unlikely(adapter->pvid)) { + if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED)) + return -EIO; + if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED)) + goto set_flags; + + flags = FLAGS_VLAN_OOB; + vlan_tci = adapter->pvid; + } +set_flags: + qlcnic_set_tx_vlan_tci(first_desc, vlan_tci); + qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); + + if (*(skb->data) & BIT_0) { + flags |= BIT_0; + memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN); + } + opcode = TX_ETHER_PKT; + if ((adapter->netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) && + skb_shinfo(skb)->gso_size > 0) { + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); + first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); + first_desc->total_hdr_length = hdr_len; + opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO; + + /* For LSO, we need to copy the MAC/IP/TCP headers into + * the descriptor ring */ + copied = 0; + offset = 2; + + if (flags & FLAGS_VLAN_OOB) { + first_desc->total_hdr_length += VLAN_HLEN; + first_desc->tcp_hdr_offset = VLAN_HLEN; + first_desc->ip_hdr_offset = VLAN_HLEN; + + /* Only in case of TSO on vlan device */ + flags |= FLAGS_VLAN_TAGGED; + + /* Create a TSO vlan header template for firmware */ + hwdesc = &tx_ring->desc_head[producer]; + tx_ring->cmd_buf_arr[producer].skb = NULL; + + copy_len = min((int)sizeof(struct cmd_desc_type0) - + offset, hdr_len + VLAN_HLEN); + + vh = (struct vlan_ethhdr *)((char *) hwdesc + 2); + skb_copy_from_linear_data(skb, vh, 12); + vh->h_vlan_proto = htons(ETH_P_8021Q); + vh->h_vlan_TCI = htons(vlan_tci); + + skb_copy_from_linear_data_offset(skb, 12, + (char *)vh + 16, + copy_len - 16); + copied = copy_len - VLAN_HLEN; + offset = 0; + producer = get_next_index(producer, tx_ring->num_desc); + } + + while (copied < hdr_len) { + size = (int)sizeof(struct cmd_desc_type0) - offset; + copy_len = min(size, (hdr_len - copied)); + hwdesc = &tx_ring->desc_head[producer]; + tx_ring->cmd_buf_arr[producer].skb = NULL; + skb_copy_from_linear_data_offset(skb, copied, + (char *)hwdesc + + offset, copy_len); + copied += copy_len; + offset = 0; + producer = get_next_index(producer, tx_ring->num_desc); + } + + tx_ring->producer = producer; + smp_mb(); + adapter->stats.lso_frames++; + + } else if (skb->ip_summed == CHECKSUM_PARTIAL) { + if (protocol == ETH_P_IP) { + l4proto = ip_hdr(skb)->protocol; + + if (l4proto == IPPROTO_TCP) + opcode = TX_TCP_PKT; + else if (l4proto == IPPROTO_UDP) + opcode = TX_UDP_PKT; + } else if (protocol == ETH_P_IPV6) { + l4proto = ipv6_hdr(skb)->nexthdr; + + if (l4proto == IPPROTO_TCP) + opcode = TX_TCPV6_PKT; + else if (l4proto == IPPROTO_UDP) + opcode = TX_UDPV6_PKT; + } + } + first_desc->tcp_hdr_offset += skb_transport_offset(skb); + first_desc->ip_hdr_offset += skb_network_offset(skb); + qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); + + return 0; +} + +static int qlcnic_map_tx_skb(struct pci_dev *pdev, struct sk_buff *skb, + struct qlcnic_cmd_buffer *pbuf) +{ + struct qlcnic_skb_frag *nf; + struct skb_frag_struct *frag; + int i, nr_frags; + dma_addr_t map; + + nr_frags = skb_shinfo(skb)->nr_frags; + nf = &pbuf->frag_array[0]; + + map = pci_map_single(pdev, skb->data, skb_headlen(skb), + PCI_DMA_TODEVICE); + if (pci_dma_mapping_error(pdev, map)) + goto out_err; + + nf->dma = map; + nf->length = skb_headlen(skb); + + for (i = 0; i < nr_frags; i++) { + frag = &skb_shinfo(skb)->frags[i]; + nf = &pbuf->frag_array[i+1]; + map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), + DMA_TO_DEVICE); + if (dma_mapping_error(&pdev->dev, map)) + goto unwind; + + nf->dma = map; + nf->length = skb_frag_size(frag); + } + + return 0; + +unwind: + while (--i >= 0) { + nf = &pbuf->frag_array[i+1]; + pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); + } + + nf = &pbuf->frag_array[0]; + pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); + +out_err: + return -ENOMEM; +} + +static void qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb, + struct qlcnic_cmd_buffer *pbuf) +{ + struct qlcnic_skb_frag *nf = &pbuf->frag_array[0]; + int i, nr_frags = skb_shinfo(skb)->nr_frags; + + for (i = 0; i < nr_frags; i++) { + nf = &pbuf->frag_array[i+1]; + pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); + } + + nf = &pbuf->frag_array[0]; + pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); + pbuf->skb = NULL; +} + +static inline void qlcnic_clear_cmddesc(u64 *desc) +{ + desc[0] = 0ULL; + desc[2] = 0ULL; + desc[7] = 0ULL; +} + +netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) +{ + struct qlcnic_adapter *adapter = netdev_priv(netdev); + struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; + struct qlcnic_cmd_buffer *pbuf; + struct qlcnic_skb_frag *buffrag; + struct cmd_desc_type0 *hwdesc, *first_desc; + struct pci_dev *pdev; + struct ethhdr *phdr; + int i, k, frag_count, delta = 0; + u32 producer, num_txd; + + num_txd = tx_ring->num_desc; + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { + netif_stop_queue(netdev); + return NETDEV_TX_BUSY; + } + + if (adapter->flags & QLCNIC_MACSPOOF) { + phdr = (struct ethhdr *)skb->data; + if (!ether_addr_equal(phdr->h_source, adapter->mac_addr)) + goto drop_packet; + } + + frag_count = skb_shinfo(skb)->nr_frags + 1; + /* 14 frags supported for normal packet and + * 32 frags supported for TSO packet + */ + if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) { + for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++) + delta += skb_frag_size(&skb_shinfo(skb)->frags[i]); + + if (!__pskb_pull_tail(skb, delta)) + goto drop_packet; + + frag_count = 1 + skb_shinfo(skb)->nr_frags; + } + + if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) { + netif_stop_queue(netdev); + if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { + netif_start_queue(netdev); + } else { + adapter->stats.xmit_off++; + return NETDEV_TX_BUSY; + } + } + + producer = tx_ring->producer; + pbuf = &tx_ring->cmd_buf_arr[producer]; + pdev = adapter->pdev; + first_desc = &tx_ring->desc_head[producer]; + hwdesc = &tx_ring->desc_head[producer]; + qlcnic_clear_cmddesc((u64 *)hwdesc); + + if (qlcnic_map_tx_skb(pdev, skb, pbuf)) { + adapter->stats.tx_dma_map_error++; + goto drop_packet; + } + + pbuf->skb = skb; + pbuf->frag_count = frag_count; + + qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len); + qlcnic_set_tx_port(first_desc, adapter->portnum); + + for (i = 0; i < frag_count; i++) { + k = i % 4; + + if ((k == 0) && (i > 0)) { + /* move to next desc.*/ + producer = get_next_index(producer, num_txd); + hwdesc = &tx_ring->desc_head[producer]; + qlcnic_clear_cmddesc((u64 *)hwdesc); + tx_ring->cmd_buf_arr[producer].skb = NULL; + } + + buffrag = &pbuf->frag_array[i]; + hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length); + switch (k) { + case 0: + hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma); + break; + case 1: + hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma); + break; + case 2: + hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma); + break; + case 3: + hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma); + break; + } + } + + tx_ring->producer = get_next_index(producer, num_txd); + smp_mb(); + + if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb))) + goto unwind_buff; + + if (adapter->mac_learn) + qlcnic_send_filter(adapter, tx_ring, first_desc, skb); + + adapter->stats.txbytes += skb->len; + adapter->stats.xmitcalled++; + + qlcnic_update_cmd_producer(tx_ring); + + return NETDEV_TX_OK; + +unwind_buff: + qlcnic_unmap_buffers(pdev, skb, pbuf); +drop_packet: + adapter->stats.txdropped++; + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup) +{ + struct net_device *netdev = adapter->netdev; + + if (adapter->ahw->linkup && !linkup) { + netdev_info(netdev, "NIC Link is down\n"); + adapter->ahw->linkup = 0; + if (netif_running(netdev)) { + netif_carrier_off(netdev); + netif_stop_queue(netdev); + } + } else if (!adapter->ahw->linkup && linkup) { + netdev_info(netdev, "NIC Link is up\n"); + adapter->ahw->linkup = 1; + if (netif_running(netdev)) { + netif_carrier_on(netdev); + netif_wake_queue(netdev); + } + } +} + +static int qlcnic_alloc_rx_skb(struct qlcnic_adapter *adapter, + struct qlcnic_host_rds_ring *rds_ring, + struct qlcnic_rx_buffer *buffer) +{ + struct sk_buff *skb; + dma_addr_t dma; + struct pci_dev *pdev = adapter->pdev; + + skb = netdev_alloc_skb(adapter->netdev, rds_ring->skb_size); + if (!skb) { + adapter->stats.skb_alloc_failure++; + return -ENOMEM; + } + + skb_reserve(skb, NET_IP_ALIGN); + dma = pci_map_single(pdev, skb->data, rds_ring->dma_size, + PCI_DMA_FROMDEVICE); + + if (pci_dma_mapping_error(pdev, dma)) { + adapter->stats.rx_dma_map_error++; + dev_kfree_skb_any(skb); + return -ENOMEM; + } + + buffer->skb = skb; + buffer->dma = dma; + + return 0; +} + +static void qlcnic_post_rx_buffers_nodb(struct qlcnic_adapter *adapter, + struct qlcnic_host_rds_ring *rds_ring) +{ + struct rcv_desc *pdesc; + struct qlcnic_rx_buffer *buffer; + int count = 0; + uint32_t producer; + struct list_head *head; + + if (!spin_trylock(&rds_ring->lock)) + return; + + producer = rds_ring->producer; + head = &rds_ring->free_list; + + while (!list_empty(head)) { + buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); + + if (!buffer->skb) { + if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) + break; + } + + count++; + list_del(&buffer->list); + + /* make a rcv descriptor */ + pdesc = &rds_ring->desc_head[producer]; + pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); + pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); + pdesc->addr_buffer = cpu_to_le64(buffer->dma); + producer = get_next_index(producer, rds_ring->num_desc); + } + + if (count) { + rds_ring->producer = producer; + writel((producer - 1) & (rds_ring->num_desc - 1), + rds_ring->crb_rcv_producer); + } + + spin_unlock(&rds_ring->lock); +} + +static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter) +{ + u32 sw_consumer, hw_consumer; + int i, done, count = 0; + struct qlcnic_cmd_buffer *buffer; + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; + struct qlcnic_skb_frag *frag; + struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; + + if (!spin_trylock(&adapter->tx_clean_lock)) + return 1; + + sw_consumer = tx_ring->sw_consumer; + hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); + + while (sw_consumer != hw_consumer) { + buffer = &tx_ring->cmd_buf_arr[sw_consumer]; + if (buffer->skb) { + frag = &buffer->frag_array[0]; + pci_unmap_single(pdev, frag->dma, frag->length, + PCI_DMA_TODEVICE); + frag->dma = 0ULL; + for (i = 1; i < buffer->frag_count; i++) { + frag++; + pci_unmap_page(pdev, frag->dma, frag->length, + PCI_DMA_TODEVICE); + frag->dma = 0ULL; + } + + adapter->stats.xmitfinished++; + dev_kfree_skb_any(buffer->skb); + buffer->skb = NULL; + } + + sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc); + if (++count >= MAX_STATUS_HANDLE) + break; + } + + if (count && netif_running(netdev)) { + tx_ring->sw_consumer = sw_consumer; + + smp_mb(); + + if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) { + if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { + netif_wake_queue(netdev); + adapter->stats.xmit_on++; + } + } + adapter->tx_timeo_cnt = 0; + } + /* + * If everything is freed up to consumer then check if the ring is full + * If the ring is full then check if more needs to be freed and + * schedule the call back again. + * + * This happens when there are 2 CPUs. One could be freeing and the + * other filling it. If the ring is full when we get out of here and + * the card has already interrupted the host then the host can miss the + * interrupt. + * + * There is still a possible race condition and the host could miss an + * interrupt. The card has to take care of this. + */ + hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); + done = (sw_consumer == hw_consumer); + + spin_unlock(&adapter->tx_clean_lock); + + return done; +} + +static int qlcnic_poll(struct napi_struct *napi, int budget) +{ + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_adapter *adapter; + int tx_complete, work_done; + + sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); + adapter = sds_ring->adapter; + + tx_complete = qlcnic_process_cmd_ring(adapter); + work_done = qlcnic_process_rcv_ring(sds_ring, budget); + + if ((work_done < budget) && tx_complete) { + napi_complete(&sds_ring->napi); + if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) + qlcnic_enable_int(sds_ring); + } + + return work_done; +} + +static int qlcnic_rx_poll(struct napi_struct *napi, int budget) +{ + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_adapter *adapter; + int work_done; + + sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); + adapter = sds_ring->adapter; + + work_done = qlcnic_process_rcv_ring(sds_ring, budget); + + if (work_done < budget) { + napi_complete(&sds_ring->napi); + if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) + qlcnic_enable_int(sds_ring); + } + + return work_done; +} + +static void qlcnic_handle_linkevent(struct qlcnic_adapter *adapter, + struct qlcnic_fw_msg *msg) +{ + u32 cable_OUI; + u16 cable_len, link_speed; + u8 link_status, module, duplex, autoneg, lb_status = 0; + struct net_device *netdev = adapter->netdev; + + adapter->has_link_events = 1; + + cable_OUI = msg->body[1] & 0xffffffff; + cable_len = (msg->body[1] >> 32) & 0xffff; + link_speed = (msg->body[1] >> 48) & 0xffff; + + link_status = msg->body[2] & 0xff; + duplex = (msg->body[2] >> 16) & 0xff; + autoneg = (msg->body[2] >> 24) & 0xff; + lb_status = (msg->body[2] >> 32) & 0x3; + + module = (msg->body[2] >> 8) & 0xff; + if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLE) + dev_info(&netdev->dev, + "unsupported cable: OUI 0x%x, length %d\n", + cable_OUI, cable_len); + else if (module == LINKEVENT_MODULE_TWINAX_UNSUPPORTED_CABLELEN) + dev_info(&netdev->dev, "unsupported cable length %d\n", + cable_len); + + if (!link_status && (lb_status == QLCNIC_ILB_MODE || + lb_status == QLCNIC_ELB_MODE)) + adapter->ahw->loopback_state |= QLCNIC_LINKEVENT; + + qlcnic_advert_link_change(adapter, link_status); + + if (duplex == LINKEVENT_FULL_DUPLEX) + adapter->link_duplex = DUPLEX_FULL; + else + adapter->link_duplex = DUPLEX_HALF; + + adapter->module_type = module; + adapter->link_autoneg = autoneg; + + if (link_status) { + adapter->link_speed = link_speed; + } else { + adapter->link_speed = SPEED_UNKNOWN; + adapter->link_duplex = DUPLEX_UNKNOWN; + } +} + +static void qlcnic_handle_fw_message(int desc_cnt, int index, + struct qlcnic_host_sds_ring *sds_ring) +{ + struct qlcnic_fw_msg msg; + struct status_desc *desc; + struct qlcnic_adapter *adapter; + struct device *dev; + int i = 0, opcode, ret; + + while (desc_cnt > 0 && i < 8) { + desc = &sds_ring->desc_head[index]; + msg.words[i++] = le64_to_cpu(desc->status_desc_data[0]); + msg.words[i++] = le64_to_cpu(desc->status_desc_data[1]); + + index = get_next_index(index, sds_ring->num_desc); + desc_cnt--; + } + + adapter = sds_ring->adapter; + dev = &adapter->pdev->dev; + opcode = qlcnic_get_nic_msg_opcode(msg.body[0]); + + switch (opcode) { + case QLCNIC_C2H_OPCODE_GET_LINKEVENT_RESPONSE: + qlcnic_handle_linkevent(adapter, &msg); + break; + case QLCNIC_C2H_OPCODE_CONFIG_LOOPBACK: + ret = (u32)(msg.body[1]); + switch (ret) { + case 0: + adapter->ahw->loopback_state |= QLCNIC_LB_RESPONSE; + break; + case 1: + dev_info(dev, "loopback already in progress\n"); + adapter->diag_cnt = -QLCNIC_TEST_IN_PROGRESS; + break; + case 2: + dev_info(dev, "loopback cable is not connected\n"); + adapter->diag_cnt = -QLCNIC_LB_CABLE_NOT_CONN; + break; + default: + dev_info(dev, + "loopback configure request failed, err %x\n", + ret); + adapter->diag_cnt = -QLCNIC_UNDEFINED_ERROR; + break; + } + break; + default: + break; + } +} + +static struct sk_buff * +qlcnic_process_rxbuf(struct qlcnic_adapter *adapter, + struct qlcnic_host_rds_ring *rds_ring, u16 index, + u16 cksum) +{ + struct qlcnic_rx_buffer *buffer; + struct sk_buff *skb; + + buffer = &rds_ring->rx_buf_arr[index]; + + if (unlikely(buffer->skb == NULL)) { + WARN_ON(1); + return NULL; + } + + pci_unmap_single(adapter->pdev, buffer->dma, rds_ring->dma_size, + PCI_DMA_FROMDEVICE); + + skb = buffer->skb; + + if (likely((adapter->netdev->features & NETIF_F_RXCSUM) && + (cksum == STATUS_CKSUM_OK || cksum == STATUS_CKSUM_LOOP))) { + adapter->stats.csummed++; + skb->ip_summed = CHECKSUM_UNNECESSARY; + } else { + skb_checksum_none_assert(skb); + } + + buffer->skb = NULL; + + return skb; +} + +static inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter, + struct sk_buff *skb, u16 *vlan_tag) +{ + struct ethhdr *eth_hdr; + + if (!__vlan_get_tag(skb, vlan_tag)) { + eth_hdr = (struct ethhdr *)skb->data; + memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2); + skb_pull(skb, VLAN_HLEN); + } + if (!adapter->pvid) + return 0; + + if (*vlan_tag == adapter->pvid) { + /* Outer vlan tag. Packet should follow non-vlan path */ + *vlan_tag = 0xffff; + return 0; + } + if (adapter->flags & QLCNIC_TAGGING_ENABLED) + return 0; + + return -EINVAL; +} + +static struct qlcnic_rx_buffer * +qlcnic_process_rcv(struct qlcnic_adapter *adapter, + struct qlcnic_host_sds_ring *sds_ring, int ring, + u64 sts_data0) +{ + struct net_device *netdev = adapter->netdev; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + struct qlcnic_rx_buffer *buffer; + struct sk_buff *skb; + struct qlcnic_host_rds_ring *rds_ring; + int index, length, cksum, pkt_offset; + u16 vid = 0xffff; + + if (unlikely(ring >= adapter->max_rds_rings)) + return NULL; + + rds_ring = &recv_ctx->rds_rings[ring]; + + index = qlcnic_get_sts_refhandle(sts_data0); + if (unlikely(index >= rds_ring->num_desc)) + return NULL; + + buffer = &rds_ring->rx_buf_arr[index]; + length = qlcnic_get_sts_totallength(sts_data0); + cksum = qlcnic_get_sts_status(sts_data0); + pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); + + skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); + if (!skb) + return buffer; + + if (length > rds_ring->skb_size) + skb_put(skb, rds_ring->skb_size); + else + skb_put(skb, length); + + if (pkt_offset) + skb_pull(skb, pkt_offset); + + if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { + adapter->stats.rxdropped++; + dev_kfree_skb(skb); + return buffer; + } + + skb->protocol = eth_type_trans(skb, netdev); + + if (vid != 0xffff) + __vlan_hwaccel_put_tag(skb, vid); + + napi_gro_receive(&sds_ring->napi, skb); + + adapter->stats.rx_pkts++; + adapter->stats.rxbytes += length; + + return buffer; +} + +#define QLC_TCP_HDR_SIZE 20 +#define QLC_TCP_TS_OPTION_SIZE 12 +#define QLC_TCP_TS_HDR_SIZE (QLC_TCP_HDR_SIZE + QLC_TCP_TS_OPTION_SIZE) + +static struct qlcnic_rx_buffer * +qlcnic_process_lro(struct qlcnic_adapter *adapter, + int ring, u64 sts_data0, u64 sts_data1) +{ + struct net_device *netdev = adapter->netdev; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + struct qlcnic_rx_buffer *buffer; + struct sk_buff *skb; + struct qlcnic_host_rds_ring *rds_ring; + struct iphdr *iph; + struct tcphdr *th; + bool push, timestamp; + int index, l2_hdr_offset, l4_hdr_offset; + u16 lro_length, length, data_offset, vid = 0xffff; + u32 seq_number; + + if (unlikely(ring > adapter->max_rds_rings)) + return NULL; + + rds_ring = &recv_ctx->rds_rings[ring]; + + index = qlcnic_get_lro_sts_refhandle(sts_data0); + if (unlikely(index > rds_ring->num_desc)) + return NULL; + + buffer = &rds_ring->rx_buf_arr[index]; + + timestamp = qlcnic_get_lro_sts_timestamp(sts_data0); + lro_length = qlcnic_get_lro_sts_length(sts_data0); + l2_hdr_offset = qlcnic_get_lro_sts_l2_hdr_offset(sts_data0); + l4_hdr_offset = qlcnic_get_lro_sts_l4_hdr_offset(sts_data0); + push = qlcnic_get_lro_sts_push_flag(sts_data0); + seq_number = qlcnic_get_lro_sts_seq_number(sts_data1); + + skb = qlcnic_process_rxbuf(adapter, rds_ring, index, STATUS_CKSUM_OK); + if (!skb) + return buffer; + + if (timestamp) + data_offset = l4_hdr_offset + QLC_TCP_TS_HDR_SIZE; + else + data_offset = l4_hdr_offset + QLC_TCP_HDR_SIZE; + + skb_put(skb, lro_length + data_offset); + skb_pull(skb, l2_hdr_offset); + + if (unlikely(qlcnic_check_rx_tagging(adapter, skb, &vid))) { + adapter->stats.rxdropped++; + dev_kfree_skb(skb); + return buffer; + } + + skb->protocol = eth_type_trans(skb, netdev); + iph = (struct iphdr *)skb->data; + th = (struct tcphdr *)(skb->data + (iph->ihl << 2)); + length = (iph->ihl << 2) + (th->doff << 2) + lro_length; + iph->tot_len = htons(length); + iph->check = 0; + iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); + th->psh = push; + th->seq = htonl(seq_number); + length = skb->len; + + if (adapter->flags & QLCNIC_FW_LRO_MSS_CAP) + skb_shinfo(skb)->gso_size = qlcnic_get_lro_sts_mss(sts_data1); + + if (vid != 0xffff) + __vlan_hwaccel_put_tag(skb, vid); + netif_receive_skb(skb); + + adapter->stats.lro_pkts++; + adapter->stats.lrobytes += length; + + return buffer; +} + +int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max) +{ + struct qlcnic_host_rds_ring *rds_ring; + struct qlcnic_adapter *adapter = sds_ring->adapter; + struct list_head *cur; + struct status_desc *desc; + struct qlcnic_rx_buffer *rxbuf; + u64 sts_data0, sts_data1; + __le64 owner_phantom = cpu_to_le64(STATUS_OWNER_PHANTOM); + int opcode, ring, desc_cnt, count = 0; + u32 consumer = sds_ring->consumer; + + while (count < max) { + desc = &sds_ring->desc_head[consumer]; + sts_data0 = le64_to_cpu(desc->status_desc_data[0]); + + if (!(sts_data0 & STATUS_OWNER_HOST)) + break; + + desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); + opcode = qlcnic_get_sts_opcode(sts_data0); + + switch (opcode) { + case QLCNIC_RXPKT_DESC: + case QLCNIC_OLD_RXPKT_DESC: + case QLCNIC_SYN_OFFLOAD: + ring = qlcnic_get_sts_type(sts_data0); + rxbuf = qlcnic_process_rcv(adapter, sds_ring, ring, + sts_data0); + break; + case QLCNIC_LRO_DESC: + ring = qlcnic_get_lro_sts_type(sts_data0); + sts_data1 = le64_to_cpu(desc->status_desc_data[1]); + rxbuf = qlcnic_process_lro(adapter, ring, sts_data0, + sts_data1); + break; + case QLCNIC_RESPONSE_DESC: + qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); + default: + goto skip; + } + + WARN_ON(desc_cnt > 1); + + if (likely(rxbuf)) + list_add_tail(&rxbuf->list, &sds_ring->free_list[ring]); + else + adapter->stats.null_rxbuf++; + +skip: + for (; desc_cnt > 0; desc_cnt--) { + desc = &sds_ring->desc_head[consumer]; + desc->status_desc_data[0] = owner_phantom; + consumer = get_next_index(consumer, sds_ring->num_desc); + } + count++; + } + + for (ring = 0; ring < adapter->max_rds_rings; ring++) { + rds_ring = &adapter->recv_ctx->rds_rings[ring]; + + if (!list_empty(&sds_ring->free_list[ring])) { + list_for_each(cur, &sds_ring->free_list[ring]) { + rxbuf = list_entry(cur, struct qlcnic_rx_buffer, + list); + qlcnic_alloc_rx_skb(adapter, rds_ring, rxbuf); + } + spin_lock(&rds_ring->lock); + list_splice_tail_init(&sds_ring->free_list[ring], + &rds_ring->free_list); + spin_unlock(&rds_ring->lock); + } + + qlcnic_post_rx_buffers_nodb(adapter, rds_ring); + } + + if (count) { + sds_ring->consumer = consumer; + writel(consumer, sds_ring->crb_sts_consumer); + } + + return count; +} + +void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter, + struct qlcnic_host_rds_ring *rds_ring) +{ + struct rcv_desc *pdesc; + struct qlcnic_rx_buffer *buffer; + int count = 0; + u32 producer; + struct list_head *head; + + producer = rds_ring->producer; + head = &rds_ring->free_list; + + while (!list_empty(head)) { + + buffer = list_entry(head->next, struct qlcnic_rx_buffer, list); + + if (!buffer->skb) { + if (qlcnic_alloc_rx_skb(adapter, rds_ring, buffer)) + break; + } + + count++; + list_del(&buffer->list); + + /* make a rcv descriptor */ + pdesc = &rds_ring->desc_head[producer]; + pdesc->addr_buffer = cpu_to_le64(buffer->dma); + pdesc->reference_handle = cpu_to_le16(buffer->ref_handle); + pdesc->buffer_length = cpu_to_le32(rds_ring->dma_size); + producer = get_next_index(producer, rds_ring->num_desc); + } + + if (count) { + rds_ring->producer = producer; + writel((producer-1) & (rds_ring->num_desc-1), + rds_ring->crb_rcv_producer); + } +} + +static void dump_skb(struct sk_buff *skb, struct qlcnic_adapter *adapter) +{ + int i; + unsigned char *data = skb->data; + + pr_info(KERN_INFO "\n"); + for (i = 0; i < skb->len; i++) { + QLCDB(adapter, DRV, "%02x ", data[i]); + if ((i & 0x0f) == 8) + pr_info(KERN_INFO "\n"); + } +} + +static void qlcnic_process_rcv_diag(struct qlcnic_adapter *adapter, int ring, + u64 sts_data0) +{ + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + struct sk_buff *skb; + struct qlcnic_host_rds_ring *rds_ring; + int index, length, cksum, pkt_offset; + + if (unlikely(ring >= adapter->max_rds_rings)) + return; + + rds_ring = &recv_ctx->rds_rings[ring]; + + index = qlcnic_get_sts_refhandle(sts_data0); + length = qlcnic_get_sts_totallength(sts_data0); + if (unlikely(index >= rds_ring->num_desc)) + return; + + cksum = qlcnic_get_sts_status(sts_data0); + pkt_offset = qlcnic_get_sts_pkt_offset(sts_data0); + + skb = qlcnic_process_rxbuf(adapter, rds_ring, index, cksum); + if (!skb) + return; + + if (length > rds_ring->skb_size) + skb_put(skb, rds_ring->skb_size); + else + skb_put(skb, length); + + if (pkt_offset) + skb_pull(skb, pkt_offset); + + if (!qlcnic_check_loopback_buff(skb->data, adapter->mac_addr)) + adapter->diag_cnt++; + else + dump_skb(skb, adapter); + + dev_kfree_skb_any(skb); + adapter->stats.rx_pkts++; + adapter->stats.rxbytes += length; + + return; +} + +void qlcnic_process_rcv_ring_diag(struct qlcnic_host_sds_ring *sds_ring) +{ + struct qlcnic_adapter *adapter = sds_ring->adapter; + struct status_desc *desc; + u64 sts_data0; + int ring, opcode, desc_cnt; + + u32 consumer = sds_ring->consumer; + + desc = &sds_ring->desc_head[consumer]; + sts_data0 = le64_to_cpu(desc->status_desc_data[0]); + + if (!(sts_data0 & STATUS_OWNER_HOST)) + return; + + desc_cnt = qlcnic_get_sts_desc_cnt(sts_data0); + opcode = qlcnic_get_sts_opcode(sts_data0); + switch (opcode) { + case QLCNIC_RESPONSE_DESC: + qlcnic_handle_fw_message(desc_cnt, consumer, sds_ring); + break; + default: + ring = qlcnic_get_sts_type(sts_data0); + qlcnic_process_rcv_diag(adapter, ring, sts_data0); + break; + } + + for (; desc_cnt > 0; desc_cnt--) { + desc = &sds_ring->desc_head[consumer]; + desc->status_desc_data[0] = cpu_to_le64(STATUS_OWNER_PHANTOM); + consumer = get_next_index(consumer, sds_ring->num_desc); + } + + sds_ring->consumer = consumer; + writel(consumer, sds_ring->crb_sts_consumer); +} + +void qlcnic_fetch_mac(u32 off1, u32 off2, u8 alt_mac, u8 *mac) +{ + u32 mac_low, mac_high; + int i; + + mac_low = off1; + mac_high = off2; + + if (alt_mac) { + mac_low |= (mac_low >> 16) | (mac_high << 16); + mac_high >>= 16; + } + + for (i = 0; i < 2; i++) + mac[i] = (u8)(mac_high >> ((1 - i) * 8)); + for (i = 2; i < 6; i++) + mac[i] = (u8)(mac_low >> ((5 - i) * 8)); +} + +int qlcnic_napi_add(struct qlcnic_adapter *adapter, struct net_device *netdev) +{ + int ring, max_sds_rings; + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + + if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings)) + return -ENOMEM; + + max_sds_rings = adapter->max_sds_rings; + + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &recv_ctx->sds_rings[ring]; + + if (ring == max_sds_rings - 1) + netif_napi_add(netdev, &sds_ring->napi, qlcnic_poll, + QLCNIC_NETDEV_WEIGHT / max_sds_rings); + else + netif_napi_add(netdev, &sds_ring->napi, qlcnic_rx_poll, + QLCNIC_NETDEV_WEIGHT*2); + } + + return 0; +} + +void qlcnic_napi_del(struct qlcnic_adapter *adapter) +{ + int ring; + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &recv_ctx->sds_rings[ring]; + netif_napi_del(&sds_ring->napi); + } + + qlcnic_free_sds_rings(adapter->recv_ctx); +} + +void qlcnic_napi_enable(struct qlcnic_adapter *adapter) +{ + int ring; + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + + if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) + return; + + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &recv_ctx->sds_rings[ring]; + napi_enable(&sds_ring->napi); + qlcnic_enable_int(sds_ring); + } +} + +void qlcnic_napi_disable(struct qlcnic_adapter *adapter) +{ + int ring; + struct qlcnic_host_sds_ring *sds_ring; + struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + + if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) + return; + + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &recv_ctx->sds_rings[ring]; + qlcnic_disable_int(sds_ring); + napi_synchronize(&sds_ring->napi); + napi_disable(&sds_ring->napi); + } +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 67159d6e9a3..1eef0bf36aa 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -66,17 +66,10 @@ static void qlcnic_fw_poll_work(struct work_struct *work); static void qlcnic_schedule_work(struct qlcnic_adapter *adapter, work_func_t func, int delay); static void qlcnic_cancel_fw_work(struct qlcnic_adapter *adapter); -static int qlcnic_poll(struct napi_struct *napi, int budget); -static int qlcnic_rx_poll(struct napi_struct *napi, int budget); #ifdef CONFIG_NET_POLL_CONTROLLER static void qlcnic_poll_controller(struct net_device *netdev); #endif -static void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter); -static void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter); -static void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter); -static void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter); - static void qlcnic_idc_debug_info(struct qlcnic_adapter *adapter, u8 encoding); static void qlcnic_clr_all_drv_state(struct qlcnic_adapter *adapter, u8); static int qlcnic_can_start_firmware(struct qlcnic_adapter *adapter); @@ -92,8 +85,6 @@ static int qlcnic_start_firmware(struct qlcnic_adapter *); static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter); static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *); -static int qlcnicvf_config_led(struct qlcnic_adapter *, u32, u32); -static int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *, u32); static int qlcnicvf_start_firmware(struct qlcnic_adapter *); static void qlcnic_set_netdev_features(struct qlcnic_adapter *, struct qlcnic_esw_func_cfg *); @@ -130,23 +121,7 @@ static const u32 msi_tgt_status[8] = { static const struct qlcnic_legacy_intr_set legacy_intr[] = QLCNIC_LEGACY_INTR_CONFIG; -static inline void qlcnic_disable_int(struct qlcnic_host_sds_ring *sds_ring) -{ - writel(0, sds_ring->crb_intr_mask); -} - -static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring) -{ - struct qlcnic_adapter *adapter = sds_ring->adapter; - - writel(0x1, sds_ring->crb_intr_mask); - - if (!QLCNIC_IS_MSI_FAMILY(adapter)) - writel(0xfbff, adapter->tgt_mask_reg); -} - -static int -qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count) +int qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count) { int size = sizeof(struct qlcnic_host_sds_ring) * count; @@ -155,8 +130,7 @@ qlcnic_alloc_sds_rings(struct qlcnic_recv_context *recv_ctx, int count) return recv_ctx->sds_rings == NULL; } -static void -qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx) +void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx) { if (recv_ctx->sds_rings != NULL) kfree(recv_ctx->sds_rings); @@ -164,80 +138,6 @@ qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx) recv_ctx->sds_rings = NULL; } -static int -qlcnic_napi_add(struct qlcnic_adapter *adapter, struct net_device *netdev) -{ - int ring; - struct qlcnic_host_sds_ring *sds_ring; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - - if (qlcnic_alloc_sds_rings(recv_ctx, adapter->max_sds_rings)) - return -ENOMEM; - - for (ring = 0; ring < adapter->max_sds_rings; ring++) { - sds_ring = &recv_ctx->sds_rings[ring]; - - if (ring == adapter->max_sds_rings - 1) - netif_napi_add(netdev, &sds_ring->napi, qlcnic_poll, - QLCNIC_NETDEV_WEIGHT/adapter->max_sds_rings); - else - netif_napi_add(netdev, &sds_ring->napi, - qlcnic_rx_poll, QLCNIC_NETDEV_WEIGHT*2); - } - - return 0; -} - -static void -qlcnic_napi_del(struct qlcnic_adapter *adapter) -{ - int ring; - struct qlcnic_host_sds_ring *sds_ring; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - - for (ring = 0; ring < adapter->max_sds_rings; ring++) { - sds_ring = &recv_ctx->sds_rings[ring]; - netif_napi_del(&sds_ring->napi); - } - - qlcnic_free_sds_rings(adapter->recv_ctx); -} - -static void -qlcnic_napi_enable(struct qlcnic_adapter *adapter) -{ - int ring; - struct qlcnic_host_sds_ring *sds_ring; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - - if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) - return; - - for (ring = 0; ring < adapter->max_sds_rings; ring++) { - sds_ring = &recv_ctx->sds_rings[ring]; - napi_enable(&sds_ring->napi); - qlcnic_enable_int(sds_ring); - } -} - -static void -qlcnic_napi_disable(struct qlcnic_adapter *adapter) -{ - int ring; - struct qlcnic_host_sds_ring *sds_ring; - struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; - - if (adapter->is_up != QLCNIC_ADAPTER_UP_MAGIC) - return; - - for (ring = 0; ring < adapter->max_sds_rings; ring++) { - sds_ring = &recv_ctx->sds_rings[ring]; - qlcnic_disable_int(sds_ring); - napi_synchronize(&sds_ring->napi); - napi_disable(&sds_ring->napi); - } -} - static void qlcnic_clear_stats(struct qlcnic_adapter *adapter) { memset(&adapter->stats, 0, sizeof(adapter->stats)); @@ -722,9 +622,8 @@ qlcnic_initialize_nic(struct qlcnic_adapter *adapter) return err; } -static void -qlcnic_set_vlan_config(struct qlcnic_adapter *adapter, - struct qlcnic_esw_func_cfg *esw_cfg) +void qlcnic_set_vlan_config(struct qlcnic_adapter *adapter, + struct qlcnic_esw_func_cfg *esw_cfg) { if (esw_cfg->discard_tagged) adapter->flags &= ~QLCNIC_TAGGING_ENABLED; @@ -755,9 +654,8 @@ qlcnic_vlan_rx_del(struct net_device *netdev, u16 vid) return 0; } -static void -qlcnic_set_eswitch_port_features(struct qlcnic_adapter *adapter, - struct qlcnic_esw_func_cfg *esw_cfg) +void qlcnic_set_eswitch_port_features(struct qlcnic_adapter *adapter, + struct qlcnic_esw_func_cfg *esw_cfg) { adapter->flags &= ~(QLCNIC_MACSPOOF | QLCNIC_MAC_OVERRIDE_DISABLED | QLCNIC_PROMISC_DISABLED); @@ -774,8 +672,7 @@ qlcnic_set_eswitch_port_features(struct qlcnic_adapter *adapter, qlcnic_set_netdev_features(adapter, esw_cfg); } -static int -qlcnic_set_eswitch_port_config(struct qlcnic_adapter *adapter) +static int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *adapter) { struct qlcnic_esw_func_cfg esw_cfg; @@ -1924,428 +1821,6 @@ static void qlcnic_free_lb_filters_mem(struct qlcnic_adapter *adapter) adapter->fhash.fmax = 0; } -static void qlcnic_change_filter(struct qlcnic_adapter *adapter, - u64 uaddr, __le16 vlan_id, struct qlcnic_host_tx_ring *tx_ring) -{ - struct cmd_desc_type0 *hwdesc; - struct qlcnic_nic_req *req; - struct qlcnic_mac_req *mac_req; - struct qlcnic_vlan_req *vlan_req; - u32 producer; - u64 word; - - producer = tx_ring->producer; - hwdesc = &tx_ring->desc_head[tx_ring->producer]; - - req = (struct qlcnic_nic_req *)hwdesc; - memset(req, 0, sizeof(struct qlcnic_nic_req)); - req->qhdr = cpu_to_le64(QLCNIC_REQUEST << 23); - - word = QLCNIC_MAC_EVENT | ((u64)(adapter->portnum) << 16); - req->req_hdr = cpu_to_le64(word); - - mac_req = (struct qlcnic_mac_req *)&(req->words[0]); - mac_req->op = vlan_id ? QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_ADD; - memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN); - - vlan_req = (struct qlcnic_vlan_req *)&req->words[1]; - vlan_req->vlan_id = vlan_id; - - tx_ring->producer = get_next_index(producer, tx_ring->num_desc); - smp_mb(); -} - -#define QLCNIC_MAC_HASH(MAC)\ - ((((MAC) & 0x70000) >> 0x10) | (((MAC) & 0x70000000000ULL) >> 0x25)) - -static void -qlcnic_send_filter(struct qlcnic_adapter *adapter, - struct qlcnic_host_tx_ring *tx_ring, - struct cmd_desc_type0 *first_desc, - struct sk_buff *skb) -{ - struct ethhdr *phdr = (struct ethhdr *)(skb->data); - struct qlcnic_filter *fil, *tmp_fil; - struct hlist_node *tmp_hnode, *n; - struct hlist_head *head; - u64 src_addr = 0; - __le16 vlan_id = 0; - u8 hindex; - - if (ether_addr_equal(phdr->h_source, adapter->mac_addr)) - return; - - if (adapter->fhash.fnum >= adapter->fhash.fmax) - return; - - /* Only NPAR capable devices support vlan based learning*/ - if (adapter->flags & QLCNIC_ESWITCH_ENABLED) - vlan_id = first_desc->vlan_TCI; - memcpy(&src_addr, phdr->h_source, ETH_ALEN); - hindex = QLCNIC_MAC_HASH(src_addr) & (QLCNIC_LB_MAX_FILTERS - 1); - head = &(adapter->fhash.fhead[hindex]); - - hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { - if (!memcmp(tmp_fil->faddr, &src_addr, ETH_ALEN) && - tmp_fil->vlan_id == vlan_id) { - - if (jiffies > - (QLCNIC_READD_AGE * HZ + tmp_fil->ftime)) - qlcnic_change_filter(adapter, src_addr, vlan_id, - tx_ring); - tmp_fil->ftime = jiffies; - return; - } - } - - fil = kzalloc(sizeof(struct qlcnic_filter), GFP_ATOMIC); - if (!fil) - return; - - qlcnic_change_filter(adapter, src_addr, vlan_id, tx_ring); - - fil->ftime = jiffies; - fil->vlan_id = vlan_id; - memcpy(fil->faddr, &src_addr, ETH_ALEN); - spin_lock(&adapter->mac_learn_lock); - hlist_add_head(&(fil->fnode), head); - adapter->fhash.fnum++; - spin_unlock(&adapter->mac_learn_lock); -} - -static int -qlcnic_tx_pkt(struct qlcnic_adapter *adapter, - struct cmd_desc_type0 *first_desc, - struct sk_buff *skb) -{ - u8 opcode = 0, hdr_len = 0; - u16 flags = 0, vlan_tci = 0; - int copied, offset, copy_len; - struct cmd_desc_type0 *hwdesc; - struct vlan_ethhdr *vh; - struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; - u16 protocol = ntohs(skb->protocol); - u32 producer = tx_ring->producer; - - if (protocol == ETH_P_8021Q) { - vh = (struct vlan_ethhdr *)skb->data; - flags = FLAGS_VLAN_TAGGED; - vlan_tci = ntohs(vh->h_vlan_TCI); - protocol = ntohs(vh->h_vlan_encapsulated_proto); - } else if (vlan_tx_tag_present(skb)) { - flags = FLAGS_VLAN_OOB; - vlan_tci = vlan_tx_tag_get(skb); - } - if (unlikely(adapter->pvid)) { - if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED)) - return -EIO; - if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED)) - goto set_flags; - - flags = FLAGS_VLAN_OOB; - vlan_tci = adapter->pvid; - } -set_flags: - qlcnic_set_tx_vlan_tci(first_desc, vlan_tci); - qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); - - if (*(skb->data) & BIT_0) { - flags |= BIT_0; - memcpy(&first_desc->eth_addr, skb->data, ETH_ALEN); - } - opcode = TX_ETHER_PKT; - if ((adapter->netdev->features & (NETIF_F_TSO | NETIF_F_TSO6)) && - skb_shinfo(skb)->gso_size > 0) { - - hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); - - first_desc->mss = cpu_to_le16(skb_shinfo(skb)->gso_size); - first_desc->total_hdr_length = hdr_len; - - opcode = (protocol == ETH_P_IPV6) ? TX_TCP_LSO6 : TX_TCP_LSO; - - /* For LSO, we need to copy the MAC/IP/TCP headers into - * the descriptor ring */ - copied = 0; - offset = 2; - - if (flags & FLAGS_VLAN_OOB) { - first_desc->total_hdr_length += VLAN_HLEN; - first_desc->tcp_hdr_offset = VLAN_HLEN; - first_desc->ip_hdr_offset = VLAN_HLEN; - /* Only in case of TSO on vlan device */ - flags |= FLAGS_VLAN_TAGGED; - - /* Create a TSO vlan header template for firmware */ - - hwdesc = &tx_ring->desc_head[producer]; - tx_ring->cmd_buf_arr[producer].skb = NULL; - - copy_len = min((int)sizeof(struct cmd_desc_type0) - - offset, hdr_len + VLAN_HLEN); - - vh = (struct vlan_ethhdr *)((char *) hwdesc + 2); - skb_copy_from_linear_data(skb, vh, 12); - vh->h_vlan_proto = htons(ETH_P_8021Q); - vh->h_vlan_TCI = htons(vlan_tci); - - skb_copy_from_linear_data_offset(skb, 12, - (char *)vh + 16, copy_len - 16); - - copied = copy_len - VLAN_HLEN; - offset = 0; - - producer = get_next_index(producer, tx_ring->num_desc); - } - - while (copied < hdr_len) { - - copy_len = min((int)sizeof(struct cmd_desc_type0) - - offset, (hdr_len - copied)); - - hwdesc = &tx_ring->desc_head[producer]; - tx_ring->cmd_buf_arr[producer].skb = NULL; - - skb_copy_from_linear_data_offset(skb, copied, - (char *) hwdesc + offset, copy_len); - - copied += copy_len; - offset = 0; - - producer = get_next_index(producer, tx_ring->num_desc); - } - - tx_ring->producer = producer; - smp_mb(); - adapter->stats.lso_frames++; - - } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - u8 l4proto; - - if (protocol == ETH_P_IP) { - l4proto = ip_hdr(skb)->protocol; - - if (l4proto == IPPROTO_TCP) - opcode = TX_TCP_PKT; - else if (l4proto == IPPROTO_UDP) - opcode = TX_UDP_PKT; - } else if (protocol == ETH_P_IPV6) { - l4proto = ipv6_hdr(skb)->nexthdr; - - if (l4proto == IPPROTO_TCP) - opcode = TX_TCPV6_PKT; - else if (l4proto == IPPROTO_UDP) - opcode = TX_UDPV6_PKT; - } - } - first_desc->tcp_hdr_offset += skb_transport_offset(skb); - first_desc->ip_hdr_offset += skb_network_offset(skb); - qlcnic_set_tx_flags_opcode(first_desc, flags, opcode); - - return 0; -} - -static int -qlcnic_map_tx_skb(struct pci_dev *pdev, - struct sk_buff *skb, struct qlcnic_cmd_buffer *pbuf) -{ - struct qlcnic_skb_frag *nf; - struct skb_frag_struct *frag; - int i, nr_frags; - dma_addr_t map; - - nr_frags = skb_shinfo(skb)->nr_frags; - nf = &pbuf->frag_array[0]; - - map = pci_map_single(pdev, skb->data, - skb_headlen(skb), PCI_DMA_TODEVICE); - if (pci_dma_mapping_error(pdev, map)) - goto out_err; - - nf->dma = map; - nf->length = skb_headlen(skb); - - for (i = 0; i < nr_frags; i++) { - frag = &skb_shinfo(skb)->frags[i]; - nf = &pbuf->frag_array[i+1]; - - map = skb_frag_dma_map(&pdev->dev, frag, 0, skb_frag_size(frag), - DMA_TO_DEVICE); - if (dma_mapping_error(&pdev->dev, map)) - goto unwind; - - nf->dma = map; - nf->length = skb_frag_size(frag); - } - - return 0; - -unwind: - while (--i >= 0) { - nf = &pbuf->frag_array[i+1]; - pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); - } - - nf = &pbuf->frag_array[0]; - pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); - -out_err: - return -ENOMEM; -} - -static void -qlcnic_unmap_buffers(struct pci_dev *pdev, struct sk_buff *skb, - struct qlcnic_cmd_buffer *pbuf) -{ - struct qlcnic_skb_frag *nf = &pbuf->frag_array[0]; - int nr_frags = skb_shinfo(skb)->nr_frags; - int i; - - for (i = 0; i < nr_frags; i++) { - nf = &pbuf->frag_array[i+1]; - pci_unmap_page(pdev, nf->dma, nf->length, PCI_DMA_TODEVICE); - } - - nf = &pbuf->frag_array[0]; - pci_unmap_single(pdev, nf->dma, skb_headlen(skb), PCI_DMA_TODEVICE); - pbuf->skb = NULL; -} - -static inline void -qlcnic_clear_cmddesc(u64 *desc) -{ - desc[0] = 0ULL; - desc[2] = 0ULL; - desc[7] = 0ULL; -} - -netdev_tx_t -qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) -{ - struct qlcnic_adapter *adapter = netdev_priv(netdev); - struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; - struct qlcnic_cmd_buffer *pbuf; - struct qlcnic_skb_frag *buffrag; - struct cmd_desc_type0 *hwdesc, *first_desc; - struct pci_dev *pdev; - struct ethhdr *phdr; - int delta = 0; - int i, k; - - u32 producer; - int frag_count; - u32 num_txd = tx_ring->num_desc; - - if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { - netif_stop_queue(netdev); - return NETDEV_TX_BUSY; - } - - if (adapter->flags & QLCNIC_MACSPOOF) { - phdr = (struct ethhdr *)skb->data; - if (!ether_addr_equal(phdr->h_source, adapter->mac_addr)) - goto drop_packet; - } - - frag_count = skb_shinfo(skb)->nr_frags + 1; - /* 14 frags supported for normal packet and - * 32 frags supported for TSO packet - */ - if (!skb_is_gso(skb) && frag_count > QLCNIC_MAX_FRAGS_PER_TX) { - - for (i = 0; i < (frag_count - QLCNIC_MAX_FRAGS_PER_TX); i++) - delta += skb_frag_size(&skb_shinfo(skb)->frags[i]); - - if (!__pskb_pull_tail(skb, delta)) - goto drop_packet; - - frag_count = 1 + skb_shinfo(skb)->nr_frags; - } - - if (unlikely(qlcnic_tx_avail(tx_ring) <= TX_STOP_THRESH)) { - netif_stop_queue(netdev); - if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) - netif_start_queue(netdev); - else { - adapter->stats.xmit_off++; - return NETDEV_TX_BUSY; - } - } - - producer = tx_ring->producer; - pbuf = &tx_ring->cmd_buf_arr[producer]; - - pdev = adapter->pdev; - - first_desc = hwdesc = &tx_ring->desc_head[producer]; - qlcnic_clear_cmddesc((u64 *)hwdesc); - - if (qlcnic_map_tx_skb(pdev, skb, pbuf)) { - adapter->stats.tx_dma_map_error++; - goto drop_packet; - } - - pbuf->skb = skb; - pbuf->frag_count = frag_count; - - qlcnic_set_tx_frags_len(first_desc, frag_count, skb->len); - qlcnic_set_tx_port(first_desc, adapter->portnum); - - for (i = 0; i < frag_count; i++) { - - k = i % 4; - - if ((k == 0) && (i > 0)) { - /* move to next desc.*/ - producer = get_next_index(producer, num_txd); - hwdesc = &tx_ring->desc_head[producer]; - qlcnic_clear_cmddesc((u64 *)hwdesc); - tx_ring->cmd_buf_arr[producer].skb = NULL; - } - - buffrag = &pbuf->frag_array[i]; - - hwdesc->buffer_length[k] = cpu_to_le16(buffrag->length); - switch (k) { - case 0: - hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma); - break; - case 1: - hwdesc->addr_buffer2 = cpu_to_le64(buffrag->dma); - break; - case 2: - hwdesc->addr_buffer3 = cpu_to_le64(buffrag->dma); - break; - case 3: - hwdesc->addr_buffer4 = cpu_to_le64(buffrag->dma); - break; - } - } - - tx_ring->producer = get_next_index(producer, num_txd); - smp_mb(); - - if (unlikely(qlcnic_tx_pkt(adapter, first_desc, skb))) - goto unwind_buff; - - if (adapter->mac_learn) - qlcnic_send_filter(adapter, tx_ring, first_desc, skb); - - adapter->stats.txbytes += skb->len; - adapter->stats.xmitcalled++; - - qlcnic_update_cmd_producer(tx_ring); - - return NETDEV_TX_OK; - -unwind_buff: - qlcnic_unmap_buffers(pdev, skb, pbuf); -drop_packet: - adapter->stats.txdropped++; - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; -} - static int qlcnic_check_temp(struct qlcnic_adapter *adapter) { struct net_device *netdev = adapter->netdev; @@ -2382,27 +1857,6 @@ static int qlcnic_check_temp(struct qlcnic_adapter *adapter) return rv; } -void qlcnic_advert_link_change(struct qlcnic_adapter *adapter, int linkup) -{ - struct net_device *netdev = adapter->netdev; - - if (adapter->ahw->linkup && !linkup) { - netdev_info(netdev, "NIC Link is down\n"); - adapter->ahw->linkup = 0; - if (netif_running(netdev)) { - netif_carrier_off(netdev); - netif_stop_queue(netdev); - } - } else if (!adapter->ahw->linkup && linkup) { - netdev_info(netdev, "NIC Link is up\n"); - adapter->ahw->linkup = 1; - if (netif_running(netdev)) { - netif_carrier_on(netdev); - netif_wake_queue(netdev); - } - } -} - static void qlcnic_tx_timeout(struct net_device *netdev) { struct qlcnic_adapter *adapter = netdev_priv(netdev); @@ -2509,122 +1963,6 @@ static irqreturn_t qlcnic_msix_intr(int irq, void *data) return IRQ_HANDLED; } -static int qlcnic_process_cmd_ring(struct qlcnic_adapter *adapter) -{ - u32 sw_consumer, hw_consumer; - int count = 0, i; - struct qlcnic_cmd_buffer *buffer; - struct pci_dev *pdev = adapter->pdev; - struct net_device *netdev = adapter->netdev; - struct qlcnic_skb_frag *frag; - int done; - struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring; - - if (!spin_trylock(&adapter->tx_clean_lock)) - return 1; - - sw_consumer = tx_ring->sw_consumer; - hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); - - while (sw_consumer != hw_consumer) { - buffer = &tx_ring->cmd_buf_arr[sw_consumer]; - if (buffer->skb) { - frag = &buffer->frag_array[0]; - pci_unmap_single(pdev, frag->dma, frag->length, - PCI_DMA_TODEVICE); - frag->dma = 0ULL; - for (i = 1; i < buffer->frag_count; i++) { - frag++; - pci_unmap_page(pdev, frag->dma, frag->length, - PCI_DMA_TODEVICE); - frag->dma = 0ULL; - } - - adapter->stats.xmitfinished++; - dev_kfree_skb_any(buffer->skb); - buffer->skb = NULL; - } - - sw_consumer = get_next_index(sw_consumer, tx_ring->num_desc); - if (++count >= MAX_STATUS_HANDLE) - break; - } - - if (count && netif_running(netdev)) { - tx_ring->sw_consumer = sw_consumer; - - smp_mb(); - - if (netif_queue_stopped(netdev) && netif_carrier_ok(netdev)) { - if (qlcnic_tx_avail(tx_ring) > TX_STOP_THRESH) { - netif_wake_queue(netdev); - adapter->stats.xmit_on++; - } - } - adapter->tx_timeo_cnt = 0; - } - /* - * If everything is freed up to consumer then check if the ring is full - * If the ring is full then check if more needs to be freed and - * schedule the call back again. - * - * This happens when there are 2 CPUs. One could be freeing and the - * other filling it. If the ring is full when we get out of here and - * the card has already interrupted the host then the host can miss the - * interrupt. - * - * There is still a possible race condition and the host could miss an - * interrupt. The card has to take care of this. - */ - hw_consumer = le32_to_cpu(*(tx_ring->hw_consumer)); - done = (sw_consumer == hw_consumer); - spin_unlock(&adapter->tx_clean_lock); - - return done; -} - -static int qlcnic_poll(struct napi_struct *napi, int budget) -{ - struct qlcnic_host_sds_ring *sds_ring = - container_of(napi, struct qlcnic_host_sds_ring, napi); - - struct qlcnic_adapter *adapter = sds_ring->adapter; - - int tx_complete; - int work_done; - - tx_complete = qlcnic_process_cmd_ring(adapter); - - work_done = qlcnic_process_rcv_ring(sds_ring, budget); - - if ((work_done < budget) && tx_complete) { - napi_complete(&sds_ring->napi); - if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) - qlcnic_enable_int(sds_ring); - } - - return work_done; -} - -static int qlcnic_rx_poll(struct napi_struct *napi, int budget) -{ - struct qlcnic_host_sds_ring *sds_ring = - container_of(napi, struct qlcnic_host_sds_ring, napi); - - struct qlcnic_adapter *adapter = sds_ring->adapter; - int work_done; - - work_done = qlcnic_process_rcv_ring(sds_ring, budget); - - if (work_done < budget) { - napi_complete(&sds_ring->napi); - if (test_bit(__QLCNIC_DEV_UP, &adapter->state)) - qlcnic_enable_int(sds_ring); - } - - return work_done; -} - #ifdef CONFIG_NET_POLL_CONTROLLER static void qlcnic_poll_controller(struct net_device *netdev) { @@ -3392,93 +2730,6 @@ qlcnicvf_start_firmware(struct qlcnic_adapter *adapter) return err; } -static int -qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable) -{ - return -EOPNOTSUPP; -} - -static int -qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) -{ - return -EOPNOTSUPP; -} - -static ssize_t -qlcnic_store_bridged_mode(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - unsigned long new; - int ret = -EINVAL; - - if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG)) - goto err_out; - - if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) - goto err_out; - - if (strict_strtoul(buf, 2, &new)) - goto err_out; - - if (!adapter->nic_ops->config_bridged_mode(adapter, !!new)) - ret = len; - -err_out: - return ret; -} - -static ssize_t -qlcnic_show_bridged_mode(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - int bridged_mode = 0; - - if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) - bridged_mode = !!(adapter->flags & QLCNIC_BRIDGE_ENABLED); - - return sprintf(buf, "%d\n", bridged_mode); -} - -static struct device_attribute dev_attr_bridged_mode = { - .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, - .show = qlcnic_show_bridged_mode, - .store = qlcnic_store_bridged_mode, -}; - -static ssize_t -qlcnic_store_diag_mode(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - unsigned long new; - - if (strict_strtoul(buf, 2, &new)) - return -EINVAL; - - if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED)) - adapter->flags ^= QLCNIC_DIAG_ENABLED; - - return len; -} - -static ssize_t -qlcnic_show_diag_mode(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", - !!(adapter->flags & QLCNIC_DIAG_ENABLED)); -} - -static struct device_attribute dev_attr_diag_mode = { - .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, - .show = qlcnic_show_diag_mode, - .store = qlcnic_store_diag_mode, -}; - int qlcnic_validate_max_rss(struct net_device *netdev, u8 max_hw, u8 val) { if (!use_msi_x && !use_msi) { @@ -3529,859 +2780,6 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data) return err; } -static int -qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, u8 *state, - u8 *rate) -{ - *rate = LSB(beacon); - *state = MSB(beacon); - - QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state); - - if (!*state) { - *rate = __QLCNIC_MAX_LED_RATE; - return 0; - } else if (*state > __QLCNIC_MAX_LED_STATE) - return -EINVAL; - - if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE)) - return -EINVAL; - - return 0; -} - -static ssize_t -qlcnic_store_beacon(struct device *dev, - struct device_attribute *attr, const char *buf, size_t len) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - int max_sds_rings = adapter->max_sds_rings; - u16 beacon; - u8 b_state, b_rate; - int err; - - if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { - dev_warn(dev, "LED test not supported for non " - "privilege function\n"); - return -EOPNOTSUPP; - } - - if (len != sizeof(u16)) - return QL_STATUS_INVALID_PARAM; - - memcpy(&beacon, buf, sizeof(u16)); - err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate); - if (err) - return err; - - if (adapter->ahw->beacon_state == b_state) - return len; - - rtnl_lock(); - - if (!adapter->ahw->beacon_state) - if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { - rtnl_unlock(); - return -EBUSY; - } - - if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { - err = -EIO; - goto out; - } - - if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { - err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST); - if (err) - goto out; - set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); - } - - err = qlcnic_config_led(adapter, b_state, b_rate); - - if (!err) { - err = len; - adapter->ahw->beacon_state = b_state; - } - - if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) - qlcnic_diag_free_res(adapter->netdev, max_sds_rings); - - out: - if (!adapter->ahw->beacon_state) - clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); - rtnl_unlock(); - - return err; -} - -static ssize_t -qlcnic_show_beacon(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - - return sprintf(buf, "%d\n", adapter->ahw->beacon_state); -} - -static struct device_attribute dev_attr_beacon = { - .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, - .show = qlcnic_show_beacon, - .store = qlcnic_store_beacon, -}; - -static int -qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, - loff_t offset, size_t size) -{ - size_t crb_size = 4; - - if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) - return -EIO; - - if (offset < QLCNIC_PCI_CRBSPACE) { - if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, - QLCNIC_PCI_CAMQM_END)) - crb_size = 8; - else - return -EINVAL; - } - - if ((size != crb_size) || (offset & (crb_size-1))) - return -EINVAL; - - return 0; -} - -static ssize_t -qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - u32 data; - u64 qmdata; - int ret; - - ret = qlcnic_sysfs_validate_crb(adapter, offset, size); - if (ret != 0) - return ret; - - if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { - qlcnic_pci_camqm_read_2M(adapter, offset, &qmdata); - memcpy(buf, &qmdata, size); - } else { - data = QLCRD32(adapter, offset); - memcpy(buf, &data, size); - } - return size; -} - -static ssize_t -qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - u32 data; - u64 qmdata; - int ret; - - ret = qlcnic_sysfs_validate_crb(adapter, offset, size); - if (ret != 0) - return ret; - - if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { - memcpy(&qmdata, buf, size); - qlcnic_pci_camqm_write_2M(adapter, offset, qmdata); - } else { - memcpy(&data, buf, size); - QLCWR32(adapter, offset, data); - } - return size; -} - -static int -qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter, - loff_t offset, size_t size) -{ - if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) - return -EIO; - - if ((size != 8) || (offset & 0x7)) - return -EIO; - - return 0; -} - -static ssize_t -qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - u64 data; - int ret; - - ret = qlcnic_sysfs_validate_mem(adapter, offset, size); - if (ret != 0) - return ret; - - if (qlcnic_pci_mem_read_2M(adapter, offset, &data)) - return -EIO; - - memcpy(buf, &data, size); - - return size; -} - -static ssize_t -qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, - char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - u64 data; - int ret; - - ret = qlcnic_sysfs_validate_mem(adapter, offset, size); - if (ret != 0) - return ret; - - memcpy(&data, buf, size); - - if (qlcnic_pci_mem_write_2M(adapter, offset, data)) - return -EIO; - - return size; -} - -static struct bin_attribute bin_attr_crb = { - .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_crb, - .write = qlcnic_sysfs_write_crb, -}; - -static struct bin_attribute bin_attr_mem = { - .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_mem, - .write = qlcnic_sysfs_write_mem, -}; - -static int -validate_pm_config(struct qlcnic_adapter *adapter, - struct qlcnic_pm_func_cfg *pm_cfg, int count) -{ - - u8 src_pci_func, s_esw_id, d_esw_id; - u8 dest_pci_func; - int i; - - for (i = 0; i < count; i++) { - src_pci_func = pm_cfg[i].pci_func; - dest_pci_func = pm_cfg[i].dest_npar; - if (src_pci_func >= QLCNIC_MAX_PCI_FUNC - || dest_pci_func >= QLCNIC_MAX_PCI_FUNC) - return QL_STATUS_INVALID_PARAM; - - if (adapter->npars[src_pci_func].type != QLCNIC_TYPE_NIC) - return QL_STATUS_INVALID_PARAM; - - if (adapter->npars[dest_pci_func].type != QLCNIC_TYPE_NIC) - return QL_STATUS_INVALID_PARAM; - - s_esw_id = adapter->npars[src_pci_func].phy_port; - d_esw_id = adapter->npars[dest_pci_func].phy_port; - - if (s_esw_id != d_esw_id) - return QL_STATUS_INVALID_PARAM; - - } - return 0; - -} - -static ssize_t -qlcnic_sysfs_write_pm_config(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_pm_func_cfg *pm_cfg; - u32 id, action, pci_func; - int count, rem, i, ret; - - count = size / sizeof(struct qlcnic_pm_func_cfg); - rem = size % sizeof(struct qlcnic_pm_func_cfg); - if (rem) - return QL_STATUS_INVALID_PARAM; - - pm_cfg = (struct qlcnic_pm_func_cfg *) buf; - - ret = validate_pm_config(adapter, pm_cfg, count); - if (ret) - return ret; - for (i = 0; i < count; i++) { - pci_func = pm_cfg[i].pci_func; - action = !!pm_cfg[i].action; - id = adapter->npars[pci_func].phy_port; - ret = qlcnic_config_port_mirroring(adapter, id, - action, pci_func); - if (ret) - return ret; - } - - for (i = 0; i < count; i++) { - pci_func = pm_cfg[i].pci_func; - id = adapter->npars[pci_func].phy_port; - adapter->npars[pci_func].enable_pm = !!pm_cfg[i].action; - adapter->npars[pci_func].dest_npar = id; - } - return size; -} - -static ssize_t -qlcnic_sysfs_read_pm_config(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_pm_func_cfg pm_cfg[QLCNIC_MAX_PCI_FUNC]; - int i; - - if (size != sizeof(pm_cfg)) - return QL_STATUS_INVALID_PARAM; - - for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { - if (adapter->npars[i].type != QLCNIC_TYPE_NIC) - continue; - pm_cfg[i].action = adapter->npars[i].enable_pm; - pm_cfg[i].dest_npar = 0; - pm_cfg[i].pci_func = i; - } - memcpy(buf, &pm_cfg, size); - - return size; -} - -static int -validate_esw_config(struct qlcnic_adapter *adapter, - struct qlcnic_esw_func_cfg *esw_cfg, int count) -{ - u32 op_mode; - u8 pci_func; - int i; - - op_mode = readl(adapter->ahw->pci_base0 + QLCNIC_DRV_OP_MODE); - - for (i = 0; i < count; i++) { - pci_func = esw_cfg[i].pci_func; - if (pci_func >= QLCNIC_MAX_PCI_FUNC) - return QL_STATUS_INVALID_PARAM; - - if (adapter->op_mode == QLCNIC_MGMT_FUNC) - if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC) - return QL_STATUS_INVALID_PARAM; - - switch (esw_cfg[i].op_mode) { - case QLCNIC_PORT_DEFAULTS: - if (QLC_DEV_GET_DRV(op_mode, pci_func) != - QLCNIC_NON_PRIV_FUNC) { - if (esw_cfg[i].mac_anti_spoof != 0) - return QL_STATUS_INVALID_PARAM; - if (esw_cfg[i].mac_override != 1) - return QL_STATUS_INVALID_PARAM; - if (esw_cfg[i].promisc_mode != 1) - return QL_STATUS_INVALID_PARAM; - } - break; - case QLCNIC_ADD_VLAN: - if (!IS_VALID_VLAN(esw_cfg[i].vlan_id)) - return QL_STATUS_INVALID_PARAM; - if (!esw_cfg[i].op_type) - return QL_STATUS_INVALID_PARAM; - break; - case QLCNIC_DEL_VLAN: - if (!esw_cfg[i].op_type) - return QL_STATUS_INVALID_PARAM; - break; - default: - return QL_STATUS_INVALID_PARAM; - } - } - return 0; -} - -static ssize_t -qlcnic_sysfs_write_esw_config(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_esw_func_cfg *esw_cfg; - struct qlcnic_npar_info *npar; - int count, rem, i, ret; - u8 pci_func, op_mode = 0; - - count = size / sizeof(struct qlcnic_esw_func_cfg); - rem = size % sizeof(struct qlcnic_esw_func_cfg); - if (rem) - return QL_STATUS_INVALID_PARAM; - - esw_cfg = (struct qlcnic_esw_func_cfg *) buf; - ret = validate_esw_config(adapter, esw_cfg, count); - if (ret) - return ret; - - for (i = 0; i < count; i++) { - if (adapter->op_mode == QLCNIC_MGMT_FUNC) - if (qlcnic_config_switch_port(adapter, &esw_cfg[i])) - return QL_STATUS_INVALID_PARAM; - - if (adapter->ahw->pci_func != esw_cfg[i].pci_func) - continue; - - op_mode = esw_cfg[i].op_mode; - qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i]); - esw_cfg[i].op_mode = op_mode; - esw_cfg[i].pci_func = adapter->ahw->pci_func; - - switch (esw_cfg[i].op_mode) { - case QLCNIC_PORT_DEFAULTS: - qlcnic_set_eswitch_port_features(adapter, &esw_cfg[i]); - break; - case QLCNIC_ADD_VLAN: - qlcnic_set_vlan_config(adapter, &esw_cfg[i]); - break; - case QLCNIC_DEL_VLAN: - esw_cfg[i].vlan_id = 0; - qlcnic_set_vlan_config(adapter, &esw_cfg[i]); - break; - } - } - - if (adapter->op_mode != QLCNIC_MGMT_FUNC) - goto out; - - for (i = 0; i < count; i++) { - pci_func = esw_cfg[i].pci_func; - npar = &adapter->npars[pci_func]; - switch (esw_cfg[i].op_mode) { - case QLCNIC_PORT_DEFAULTS: - npar->promisc_mode = esw_cfg[i].promisc_mode; - npar->mac_override = esw_cfg[i].mac_override; - npar->offload_flags = esw_cfg[i].offload_flags; - npar->mac_anti_spoof = esw_cfg[i].mac_anti_spoof; - npar->discard_tagged = esw_cfg[i].discard_tagged; - break; - case QLCNIC_ADD_VLAN: - npar->pvid = esw_cfg[i].vlan_id; - break; - case QLCNIC_DEL_VLAN: - npar->pvid = 0; - break; - } - } -out: - return size; -} - -static ssize_t -qlcnic_sysfs_read_esw_config(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_esw_func_cfg esw_cfg[QLCNIC_MAX_PCI_FUNC]; - u8 i; - - if (size != sizeof(esw_cfg)) - return QL_STATUS_INVALID_PARAM; - - for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { - if (adapter->npars[i].type != QLCNIC_TYPE_NIC) - continue; - esw_cfg[i].pci_func = i; - if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i])) - return QL_STATUS_INVALID_PARAM; - } - memcpy(buf, &esw_cfg, size); - - return size; -} - -static int -validate_npar_config(struct qlcnic_adapter *adapter, - struct qlcnic_npar_func_cfg *np_cfg, int count) -{ - u8 pci_func, i; - - for (i = 0; i < count; i++) { - pci_func = np_cfg[i].pci_func; - if (pci_func >= QLCNIC_MAX_PCI_FUNC) - return QL_STATUS_INVALID_PARAM; - - if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC) - return QL_STATUS_INVALID_PARAM; - - if (!IS_VALID_BW(np_cfg[i].min_bw) || - !IS_VALID_BW(np_cfg[i].max_bw)) - return QL_STATUS_INVALID_PARAM; - } - return 0; -} - -static ssize_t -qlcnic_sysfs_write_npar_config(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_info nic_info; - struct qlcnic_npar_func_cfg *np_cfg; - int i, count, rem, ret; - u8 pci_func; - - count = size / sizeof(struct qlcnic_npar_func_cfg); - rem = size % sizeof(struct qlcnic_npar_func_cfg); - if (rem) - return QL_STATUS_INVALID_PARAM; - - np_cfg = (struct qlcnic_npar_func_cfg *) buf; - ret = validate_npar_config(adapter, np_cfg, count); - if (ret) - return ret; - - for (i = 0; i < count ; i++) { - pci_func = np_cfg[i].pci_func; - ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func); - if (ret) - return ret; - nic_info.pci_func = pci_func; - nic_info.min_tx_bw = np_cfg[i].min_bw; - nic_info.max_tx_bw = np_cfg[i].max_bw; - ret = qlcnic_set_nic_info(adapter, &nic_info); - if (ret) - return ret; - adapter->npars[i].min_bw = nic_info.min_tx_bw; - adapter->npars[i].max_bw = nic_info.max_tx_bw; - } - - return size; - -} -static ssize_t -qlcnic_sysfs_read_npar_config(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_info nic_info; - struct qlcnic_npar_func_cfg np_cfg[QLCNIC_MAX_PCI_FUNC]; - int i, ret; - - if (size != sizeof(np_cfg)) - return QL_STATUS_INVALID_PARAM; - - for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) { - if (adapter->npars[i].type != QLCNIC_TYPE_NIC) - continue; - ret = qlcnic_get_nic_info(adapter, &nic_info, i); - if (ret) - return ret; - - np_cfg[i].pci_func = i; - np_cfg[i].op_mode = (u8)nic_info.op_mode; - np_cfg[i].port_num = nic_info.phys_port; - np_cfg[i].fw_capab = nic_info.capabilities; - np_cfg[i].min_bw = nic_info.min_tx_bw ; - np_cfg[i].max_bw = nic_info.max_tx_bw; - np_cfg[i].max_tx_queues = nic_info.max_tx_ques; - np_cfg[i].max_rx_queues = nic_info.max_rx_ques; - } - memcpy(buf, &np_cfg, size); - return size; -} - -static ssize_t -qlcnic_sysfs_get_port_stats(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_esw_statistics port_stats; - int ret; - - if (size != sizeof(struct qlcnic_esw_statistics)) - return QL_STATUS_INVALID_PARAM; - - if (offset >= QLCNIC_MAX_PCI_FUNC) - return QL_STATUS_INVALID_PARAM; - - memset(&port_stats, 0, size); - ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, - &port_stats.rx); - if (ret) - return ret; - - ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, - &port_stats.tx); - if (ret) - return ret; - - memcpy(buf, &port_stats, size); - return size; -} - -static ssize_t -qlcnic_sysfs_get_esw_stats(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_esw_statistics esw_stats; - int ret; - - if (size != sizeof(struct qlcnic_esw_statistics)) - return QL_STATUS_INVALID_PARAM; - - if (offset >= QLCNIC_NIU_MAX_XG_PORTS) - return QL_STATUS_INVALID_PARAM; - - memset(&esw_stats, 0, size); - ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, - &esw_stats.rx); - if (ret) - return ret; - - ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, - &esw_stats.tx); - if (ret) - return ret; - - memcpy(buf, &esw_stats, size); - return size; -} - -static ssize_t -qlcnic_sysfs_clear_esw_stats(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - int ret; - - if (offset >= QLCNIC_NIU_MAX_XG_PORTS) - return QL_STATUS_INVALID_PARAM; - - ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, - QLCNIC_QUERY_RX_COUNTER); - if (ret) - return ret; - - ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, - QLCNIC_QUERY_TX_COUNTER); - if (ret) - return ret; - - return size; -} - -static ssize_t -qlcnic_sysfs_clear_port_stats(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - int ret; - - if (offset >= QLCNIC_MAX_PCI_FUNC) - return QL_STATUS_INVALID_PARAM; - - ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, - QLCNIC_QUERY_RX_COUNTER); - if (ret) - return ret; - - ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, - QLCNIC_QUERY_TX_COUNTER); - if (ret) - return ret; - - return size; -} - -static ssize_t -qlcnic_sysfs_read_pci_config(struct file *file, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t offset, size_t size) -{ - struct device *dev = container_of(kobj, struct device, kobj); - struct qlcnic_adapter *adapter = dev_get_drvdata(dev); - struct qlcnic_pci_func_cfg pci_cfg[QLCNIC_MAX_PCI_FUNC]; - struct qlcnic_pci_info *pci_info; - int i, ret; - - if (size != sizeof(pci_cfg)) - return QL_STATUS_INVALID_PARAM; - - pci_info = kcalloc(QLCNIC_MAX_PCI_FUNC, sizeof(*pci_info), GFP_KERNEL); - if (!pci_info) - return -ENOMEM; - - ret = qlcnic_get_pci_info(adapter, pci_info); - if (ret) { - kfree(pci_info); - return ret; - } - - for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) { - pci_cfg[i].pci_func = pci_info[i].id; - pci_cfg[i].func_type = pci_info[i].type; - pci_cfg[i].port_num = pci_info[i].default_port; - pci_cfg[i].min_bw = pci_info[i].tx_min_bw; - pci_cfg[i].max_bw = pci_info[i].tx_max_bw; - memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN); - } - memcpy(buf, &pci_cfg, size); - kfree(pci_info); - return size; -} -static struct bin_attribute bin_attr_npar_config = { - .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_npar_config, - .write = qlcnic_sysfs_write_npar_config, -}; - -static struct bin_attribute bin_attr_pci_config = { - .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_pci_config, - .write = NULL, -}; - -static struct bin_attribute bin_attr_port_stats = { - .attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_get_port_stats, - .write = qlcnic_sysfs_clear_port_stats, -}; - -static struct bin_attribute bin_attr_esw_stats = { - .attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_get_esw_stats, - .write = qlcnic_sysfs_clear_esw_stats, -}; - -static struct bin_attribute bin_attr_esw_config = { - .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_esw_config, - .write = qlcnic_sysfs_write_esw_config, -}; - -static struct bin_attribute bin_attr_pm_config = { - .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)}, - .size = 0, - .read = qlcnic_sysfs_read_pm_config, - .write = qlcnic_sysfs_write_pm_config, -}; - -static void -qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter) -{ - struct device *dev = &adapter->pdev->dev; - - if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) - if (device_create_file(dev, &dev_attr_bridged_mode)) - dev_warn(dev, - "failed to create bridged_mode sysfs entry\n"); -} - -static void -qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter) -{ - struct device *dev = &adapter->pdev->dev; - - if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) - device_remove_file(dev, &dev_attr_bridged_mode); -} - -static void -qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) -{ - struct device *dev = &adapter->pdev->dev; - u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); - - if (device_create_bin_file(dev, &bin_attr_port_stats)) - dev_info(dev, "failed to create port stats sysfs entry"); - - if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) - return; - if (device_create_file(dev, &dev_attr_diag_mode)) - dev_info(dev, "failed to create diag_mode sysfs entry\n"); - if (device_create_bin_file(dev, &bin_attr_crb)) - dev_info(dev, "failed to create crb sysfs entry\n"); - if (device_create_bin_file(dev, &bin_attr_mem)) - dev_info(dev, "failed to create mem sysfs entry\n"); - - if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) - return; - - if (device_create_bin_file(dev, &bin_attr_pci_config)) - dev_info(dev, "failed to create pci config sysfs entry"); - if (device_create_file(dev, &dev_attr_beacon)) - dev_info(dev, "failed to create beacon sysfs entry"); - - if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) - return; - if (device_create_bin_file(dev, &bin_attr_esw_config)) - dev_info(dev, "failed to create esw config sysfs entry"); - if (adapter->op_mode != QLCNIC_MGMT_FUNC) - return; - if (device_create_bin_file(dev, &bin_attr_npar_config)) - dev_info(dev, "failed to create npar config sysfs entry"); - if (device_create_bin_file(dev, &bin_attr_pm_config)) - dev_info(dev, "failed to create pm config sysfs entry"); - if (device_create_bin_file(dev, &bin_attr_esw_stats)) - dev_info(dev, "failed to create eswitch stats sysfs entry"); -} - -static void -qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) -{ - struct device *dev = &adapter->pdev->dev; - u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); - - device_remove_bin_file(dev, &bin_attr_port_stats); - - if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) - return; - device_remove_file(dev, &dev_attr_diag_mode); - device_remove_bin_file(dev, &bin_attr_crb); - device_remove_bin_file(dev, &bin_attr_mem); - if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) - return; - device_remove_bin_file(dev, &bin_attr_pci_config); - device_remove_file(dev, &dev_attr_beacon); - if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) - return; - device_remove_bin_file(dev, &bin_attr_esw_config); - if (adapter->op_mode != QLCNIC_MGMT_FUNC) - return; - device_remove_bin_file(dev, &bin_attr_npar_config); - device_remove_bin_file(dev, &bin_attr_pm_config); - device_remove_bin_file(dev, &bin_attr_esw_stats); -} - #ifdef CONFIG_INET #define is_qlcnic_netdev(dev) (dev->netdev_ops == &qlcnic_netdev_ops) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c new file mode 100644 index 00000000000..12ff2927074 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -0,0 +1,629 @@ +#include "qlcnic.h" +#include "qlcnic_hdr.h" + +#include <net/ip.h> + +#define QLCNIC_DUMP_WCRB BIT_0 +#define QLCNIC_DUMP_RWCRB BIT_1 +#define QLCNIC_DUMP_ANDCRB BIT_2 +#define QLCNIC_DUMP_ORCRB BIT_3 +#define QLCNIC_DUMP_POLLCRB BIT_4 +#define QLCNIC_DUMP_RD_SAVE BIT_5 +#define QLCNIC_DUMP_WRT_SAVED BIT_6 +#define QLCNIC_DUMP_MOD_SAVE_ST BIT_7 +#define QLCNIC_DUMP_SKIP BIT_7 + +#define QLCNIC_DUMP_MASK_MAX 0xff + +struct qlcnic_common_entry_hdr { + u32 type; + u32 offset; + u32 cap_size; + u8 mask; + u8 rsvd[2]; + u8 flags; +} __packed; + +struct __crb { + u32 addr; + u8 stride; + u8 rsvd1[3]; + u32 data_size; + u32 no_ops; + u32 rsvd2[4]; +} __packed; + +struct __ctrl { + u32 addr; + u8 stride; + u8 index_a; + u16 timeout; + u32 data_size; + u32 no_ops; + u8 opcode; + u8 index_v; + u8 shl_val; + u8 shr_val; + u32 val1; + u32 val2; + u32 val3; +} __packed; + +struct __cache { + u32 addr; + u16 stride; + u16 init_tag_val; + u32 size; + u32 no_ops; + u32 ctrl_addr; + u32 ctrl_val; + u32 read_addr; + u8 read_addr_stride; + u8 read_addr_num; + u8 rsvd1[2]; +} __packed; + +struct __ocm { + u8 rsvd[8]; + u32 size; + u32 no_ops; + u8 rsvd1[8]; + u32 read_addr; + u32 read_addr_stride; +} __packed; + +struct __mem { + u8 rsvd[24]; + u32 addr; + u32 size; +} __packed; + +struct __mux { + u32 addr; + u8 rsvd[4]; + u32 size; + u32 no_ops; + u32 val; + u32 val_stride; + u32 read_addr; + u8 rsvd2[4]; +} __packed; + +struct __queue { + u32 sel_addr; + u16 stride; + u8 rsvd[2]; + u32 size; + u32 no_ops; + u8 rsvd2[8]; + u32 read_addr; + u8 read_addr_stride; + u8 read_addr_cnt; + u8 rsvd3[2]; +} __packed; + +struct qlcnic_dump_entry { + struct qlcnic_common_entry_hdr hdr; + union { + struct __crb crb; + struct __cache cache; + struct __ocm ocm; + struct __mem mem; + struct __mux mux; + struct __queue que; + struct __ctrl ctrl; + } region; +} __packed; + +enum qlcnic_minidump_opcode { + QLCNIC_DUMP_NOP = 0, + QLCNIC_DUMP_READ_CRB = 1, + QLCNIC_DUMP_READ_MUX = 2, + QLCNIC_DUMP_QUEUE = 3, + QLCNIC_DUMP_BRD_CONFIG = 4, + QLCNIC_DUMP_READ_OCM = 6, + QLCNIC_DUMP_PEG_REG = 7, + QLCNIC_DUMP_L1_DTAG = 8, + QLCNIC_DUMP_L1_ITAG = 9, + QLCNIC_DUMP_L1_DATA = 11, + QLCNIC_DUMP_L1_INST = 12, + QLCNIC_DUMP_L2_DTAG = 21, + QLCNIC_DUMP_L2_ITAG = 22, + QLCNIC_DUMP_L2_DATA = 23, + QLCNIC_DUMP_L2_INST = 24, + QLCNIC_DUMP_READ_ROM = 71, + QLCNIC_DUMP_READ_MEM = 72, + QLCNIC_DUMP_READ_CTRL = 98, + QLCNIC_DUMP_TLHDR = 99, + QLCNIC_DUMP_RDEND = 255 +}; + +struct qlcnic_dump_operations { + enum qlcnic_minidump_opcode opcode; + u32 (*handler)(struct qlcnic_adapter *, struct qlcnic_dump_entry *, + __le32 *); +}; + +static void qlcnic_read_dump_reg(u32 addr, void __iomem *bar0, u32 *data) +{ + u32 dest; + void __iomem *window_reg; + + dest = addr & 0xFFFF0000; + window_reg = bar0 + QLCNIC_FW_DUMP_REG1; + writel(dest, window_reg); + readl(window_reg); + window_reg = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr); + *data = readl(window_reg); +} + +static void qlcnic_write_dump_reg(u32 addr, void __iomem *bar0, u32 data) +{ + u32 dest; + void __iomem *window_reg; + + dest = addr & 0xFFFF0000; + window_reg = bar0 + QLCNIC_FW_DUMP_REG1; + writel(dest, window_reg); + readl(window_reg); + window_reg = bar0 + QLCNIC_FW_DUMP_REG2 + LSW(addr); + writel(data, window_reg); + readl(window_reg); +} + +/* FW dump related functions */ +static u32 qlcnic_dump_crb(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i; + u32 addr, data; + struct __crb *crb = &entry->region.crb; + void __iomem *base = adapter->ahw->pci_base0; + + addr = crb->addr; + + for (i = 0; i < crb->no_ops; i++) { + qlcnic_read_dump_reg(addr, base, &data); + *buffer++ = cpu_to_le32(addr); + *buffer++ = cpu_to_le32(data); + addr += crb->stride; + } + return crb->no_ops * 2 * sizeof(u32); +} + +static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i, k, timeout = 0; + void __iomem *base = adapter->ahw->pci_base0; + u32 addr, data; + u8 opcode, no_ops; + struct __ctrl *ctr = &entry->region.ctrl; + struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr; + + addr = ctr->addr; + no_ops = ctr->no_ops; + + for (i = 0; i < no_ops; i++) { + k = 0; + opcode = 0; + for (k = 0; k < 8; k++) { + if (!(ctr->opcode & (1 << k))) + continue; + switch (1 << k) { + case QLCNIC_DUMP_WCRB: + qlcnic_write_dump_reg(addr, base, ctr->val1); + break; + case QLCNIC_DUMP_RWCRB: + qlcnic_read_dump_reg(addr, base, &data); + qlcnic_write_dump_reg(addr, base, data); + break; + case QLCNIC_DUMP_ANDCRB: + qlcnic_read_dump_reg(addr, base, &data); + qlcnic_write_dump_reg(addr, base, + data & ctr->val2); + break; + case QLCNIC_DUMP_ORCRB: + qlcnic_read_dump_reg(addr, base, &data); + qlcnic_write_dump_reg(addr, base, + data | ctr->val3); + break; + case QLCNIC_DUMP_POLLCRB: + while (timeout <= ctr->timeout) { + qlcnic_read_dump_reg(addr, base, &data); + if ((data & ctr->val2) == ctr->val1) + break; + msleep(1); + timeout++; + } + if (timeout > ctr->timeout) { + dev_info(&adapter->pdev->dev, + "Timed out, aborting poll CRB\n"); + return -EINVAL; + } + break; + case QLCNIC_DUMP_RD_SAVE: + if (ctr->index_a) + addr = t_hdr->saved_state[ctr->index_a]; + qlcnic_read_dump_reg(addr, base, &data); + t_hdr->saved_state[ctr->index_v] = data; + break; + case QLCNIC_DUMP_WRT_SAVED: + if (ctr->index_v) + data = t_hdr->saved_state[ctr->index_v]; + else + data = ctr->val1; + if (ctr->index_a) + addr = t_hdr->saved_state[ctr->index_a]; + qlcnic_write_dump_reg(addr, base, data); + break; + case QLCNIC_DUMP_MOD_SAVE_ST: + data = t_hdr->saved_state[ctr->index_v]; + data <<= ctr->shl_val; + data >>= ctr->shr_val; + if (ctr->val2) + data &= ctr->val2; + data |= ctr->val3; + data += ctr->val1; + t_hdr->saved_state[ctr->index_v] = data; + break; + default: + dev_info(&adapter->pdev->dev, + "Unknown opcode\n"); + break; + } + } + addr += ctr->stride; + } + return 0; +} + +static u32 qlcnic_dump_mux(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int loop; + u32 val, data = 0; + struct __mux *mux = &entry->region.mux; + void __iomem *base = adapter->ahw->pci_base0; + + val = mux->val; + for (loop = 0; loop < mux->no_ops; loop++) { + qlcnic_write_dump_reg(mux->addr, base, val); + qlcnic_read_dump_reg(mux->read_addr, base, &data); + *buffer++ = cpu_to_le32(val); + *buffer++ = cpu_to_le32(data); + val += mux->val_stride; + } + return 2 * mux->no_ops * sizeof(u32); +} + +static u32 qlcnic_dump_que(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i, loop; + u32 cnt, addr, data, que_id = 0; + void __iomem *base = adapter->ahw->pci_base0; + struct __queue *que = &entry->region.que; + + addr = que->read_addr; + cnt = que->read_addr_cnt; + + for (loop = 0; loop < que->no_ops; loop++) { + qlcnic_write_dump_reg(que->sel_addr, base, que_id); + addr = que->read_addr; + for (i = 0; i < cnt; i++) { + qlcnic_read_dump_reg(addr, base, &data); + *buffer++ = cpu_to_le32(data); + addr += que->read_addr_stride; + } + que_id += que->stride; + } + return que->no_ops * cnt * sizeof(u32); +} + +static u32 qlcnic_dump_ocm(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i; + u32 data; + void __iomem *addr; + struct __ocm *ocm = &entry->region.ocm; + + addr = adapter->ahw->pci_base0 + ocm->read_addr; + for (i = 0; i < ocm->no_ops; i++) { + data = readl(addr); + *buffer++ = cpu_to_le32(data); + addr += ocm->read_addr_stride; + } + return ocm->no_ops * sizeof(u32); +} + +static u32 qlcnic_read_rom(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i, count = 0; + u32 fl_addr, size, val, lck_val, addr; + struct __mem *rom = &entry->region.mem; + void __iomem *base = adapter->ahw->pci_base0; + + fl_addr = rom->addr; + size = rom->size/4; +lock_try: + lck_val = readl(base + QLCNIC_FLASH_SEM2_LK); + if (!lck_val && count < MAX_CTL_CHECK) { + msleep(10); + count++; + goto lock_try; + } + writel(adapter->ahw->pci_func, (base + QLCNIC_FLASH_LOCK_ID)); + for (i = 0; i < size; i++) { + addr = fl_addr & 0xFFFF0000; + qlcnic_write_dump_reg(FLASH_ROM_WINDOW, base, addr); + addr = LSW(fl_addr) + FLASH_ROM_DATA; + qlcnic_read_dump_reg(addr, base, &val); + fl_addr += 4; + *buffer++ = cpu_to_le32(val); + } + readl(base + QLCNIC_FLASH_SEM2_ULK); + return rom->size; +} + +static u32 qlcnic_dump_l1_cache(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i; + u32 cnt, val, data, addr; + void __iomem *base = adapter->ahw->pci_base0; + struct __cache *l1 = &entry->region.cache; + + val = l1->init_tag_val; + + for (i = 0; i < l1->no_ops; i++) { + qlcnic_write_dump_reg(l1->addr, base, val); + qlcnic_write_dump_reg(l1->ctrl_addr, base, LSW(l1->ctrl_val)); + addr = l1->read_addr; + cnt = l1->read_addr_num; + while (cnt) { + qlcnic_read_dump_reg(addr, base, &data); + *buffer++ = cpu_to_le32(data); + addr += l1->read_addr_stride; + cnt--; + } + val += l1->stride; + } + return l1->no_ops * l1->read_addr_num * sizeof(u32); +} + +static u32 qlcnic_dump_l2_cache(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + int i; + u32 cnt, val, data, addr; + u8 poll_mask, poll_to, time_out = 0; + void __iomem *base = adapter->ahw->pci_base0; + struct __cache *l2 = &entry->region.cache; + + val = l2->init_tag_val; + poll_mask = LSB(MSW(l2->ctrl_val)); + poll_to = MSB(MSW(l2->ctrl_val)); + + for (i = 0; i < l2->no_ops; i++) { + qlcnic_write_dump_reg(l2->addr, base, val); + if (LSW(l2->ctrl_val)) + qlcnic_write_dump_reg(l2->ctrl_addr, base, + LSW(l2->ctrl_val)); + if (!poll_mask) + goto skip_poll; + do { + qlcnic_read_dump_reg(l2->ctrl_addr, base, &data); + if (!(data & poll_mask)) + break; + msleep(1); + time_out++; + } while (time_out <= poll_to); + + if (time_out > poll_to) { + dev_err(&adapter->pdev->dev, + "Timeout exceeded in %s, aborting dump\n", + __func__); + return -EINVAL; + } +skip_poll: + addr = l2->read_addr; + cnt = l2->read_addr_num; + while (cnt) { + qlcnic_read_dump_reg(addr, base, &data); + *buffer++ = cpu_to_le32(data); + addr += l2->read_addr_stride; + cnt--; + } + val += l2->stride; + } + return l2->no_ops * l2->read_addr_num * sizeof(u32); +} + +static u32 qlcnic_read_memory(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + u32 addr, data, test, ret = 0; + int i, reg_read; + struct __mem *mem = &entry->region.mem; + void __iomem *base = adapter->ahw->pci_base0; + + reg_read = mem->size; + addr = mem->addr; + /* check for data size of multiple of 16 and 16 byte alignment */ + if ((addr & 0xf) || (reg_read%16)) { + dev_info(&adapter->pdev->dev, + "Unaligned memory addr:0x%x size:0x%x\n", + addr, reg_read); + return -EINVAL; + } + + mutex_lock(&adapter->ahw->mem_lock); + + while (reg_read != 0) { + qlcnic_write_dump_reg(MIU_TEST_ADDR_LO, base, addr); + qlcnic_write_dump_reg(MIU_TEST_ADDR_HI, base, 0); + qlcnic_write_dump_reg(MIU_TEST_CTR, base, + TA_CTL_ENABLE | TA_CTL_START); + + for (i = 0; i < MAX_CTL_CHECK; i++) { + qlcnic_read_dump_reg(MIU_TEST_CTR, base, &test); + if (!(test & TA_CTL_BUSY)) + break; + } + if (i == MAX_CTL_CHECK) { + if (printk_ratelimit()) { + dev_err(&adapter->pdev->dev, + "failed to read through agent\n"); + ret = -EINVAL; + goto out; + } + } + for (i = 0; i < 4; i++) { + qlcnic_read_dump_reg(MIU_TEST_READ_DATA[i], base, + &data); + *buffer++ = cpu_to_le32(data); + } + addr += 16; + reg_read -= 16; + ret += 16; + } +out: + mutex_unlock(&adapter->ahw->mem_lock); + return mem->size; +} + +static u32 qlcnic_dump_nop(struct qlcnic_adapter *adapter, + struct qlcnic_dump_entry *entry, __le32 *buffer) +{ + entry->hdr.flags |= QLCNIC_DUMP_SKIP; + return 0; +} + +static const struct qlcnic_dump_operations fw_dump_ops[] = { + { QLCNIC_DUMP_NOP, qlcnic_dump_nop }, + { QLCNIC_DUMP_READ_CRB, qlcnic_dump_crb }, + { QLCNIC_DUMP_READ_MUX, qlcnic_dump_mux }, + { QLCNIC_DUMP_QUEUE, qlcnic_dump_que }, + { QLCNIC_DUMP_BRD_CONFIG, qlcnic_read_rom }, + { QLCNIC_DUMP_READ_OCM, qlcnic_dump_ocm }, + { QLCNIC_DUMP_PEG_REG, qlcnic_dump_ctrl }, + { QLCNIC_DUMP_L1_DTAG, qlcnic_dump_l1_cache }, + { QLCNIC_DUMP_L1_ITAG, qlcnic_dump_l1_cache }, + { QLCNIC_DUMP_L1_DATA, qlcnic_dump_l1_cache }, + { QLCNIC_DUMP_L1_INST, qlcnic_dump_l1_cache }, + { QLCNIC_DUMP_L2_DTAG, qlcnic_dump_l2_cache }, + { QLCNIC_DUMP_L2_ITAG, qlcnic_dump_l2_cache }, + { QLCNIC_DUMP_L2_DATA, qlcnic_dump_l2_cache }, + { QLCNIC_DUMP_L2_INST, qlcnic_dump_l2_cache }, + { QLCNIC_DUMP_READ_ROM, qlcnic_read_rom }, + { QLCNIC_DUMP_READ_MEM, qlcnic_read_memory }, + { QLCNIC_DUMP_READ_CTRL, qlcnic_dump_ctrl }, + { QLCNIC_DUMP_TLHDR, qlcnic_dump_nop }, + { QLCNIC_DUMP_RDEND, qlcnic_dump_nop }, +}; + +/* Walk the template and collect dump for each entry in the dump template */ +static int +qlcnic_valid_dump_entry(struct device *dev, struct qlcnic_dump_entry *entry, + u32 size) +{ + int ret = 1; + if (size != entry->hdr.cap_size) { + dev_info(dev, + "Invalid dump, Type:%d\tMask:%d\tSize:%dCap_size:%d\n", + entry->hdr.type, entry->hdr.mask, size, entry->hdr.cap_size); + dev_info(dev, "Aborting further dump capture\n"); + ret = 0; + } + return ret; +} + +int qlcnic_dump_fw(struct qlcnic_adapter *adapter) +{ + __le32 *buffer; + char mesg[64]; + char *msg[] = {mesg, NULL}; + int i, k, ops_cnt, ops_index, dump_size = 0; + u32 entry_offset, dump, no_entries, buf_offset = 0; + struct qlcnic_dump_entry *entry; + struct qlcnic_fw_dump *fw_dump = &adapter->ahw->fw_dump; + struct qlcnic_dump_template_hdr *tmpl_hdr = fw_dump->tmpl_hdr; + + if (fw_dump->clr) { + dev_info(&adapter->pdev->dev, + "Previous dump not cleared, not capturing dump\n"); + return -EIO; + } + /* Calculate the size for dump data area only */ + for (i = 2, k = 1; (i & QLCNIC_DUMP_MASK_MAX); i <<= 1, k++) + if (i & tmpl_hdr->drv_cap_mask) + dump_size += tmpl_hdr->cap_sizes[k]; + if (!dump_size) + return -EIO; + + fw_dump->data = vzalloc(dump_size); + if (!fw_dump->data) { + dev_info(&adapter->pdev->dev, + "Unable to allocate (%d KB) for fw dump\n", + dump_size / 1024); + return -ENOMEM; + } + buffer = fw_dump->data; + fw_dump->size = dump_size; + no_entries = tmpl_hdr->num_entries; + ops_cnt = ARRAY_SIZE(fw_dump_ops); + entry_offset = tmpl_hdr->offset; + tmpl_hdr->sys_info[0] = QLCNIC_DRIVER_VERSION; + tmpl_hdr->sys_info[1] = adapter->fw_version; + + for (i = 0; i < no_entries; i++) { + entry = (void *)tmpl_hdr + entry_offset; + if (!(entry->hdr.mask & tmpl_hdr->drv_cap_mask)) { + entry->hdr.flags |= QLCNIC_DUMP_SKIP; + entry_offset += entry->hdr.offset; + continue; + } + /* Find the handler for this entry */ + ops_index = 0; + while (ops_index < ops_cnt) { + if (entry->hdr.type == fw_dump_ops[ops_index].opcode) + break; + ops_index++; + } + if (ops_index == ops_cnt) { + dev_info(&adapter->pdev->dev, + "Invalid entry type %d, exiting dump\n", + entry->hdr.type); + goto error; + } + /* Collect dump for this entry */ + dump = fw_dump_ops[ops_index].handler(adapter, entry, buffer); + if (dump && !qlcnic_valid_dump_entry(&adapter->pdev->dev, entry, + dump)) + entry->hdr.flags |= QLCNIC_DUMP_SKIP; + buf_offset += entry->hdr.cap_size; + entry_offset += entry->hdr.offset; + buffer = fw_dump->data + buf_offset; + } + if (dump_size != buf_offset) { + dev_info(&adapter->pdev->dev, + "Captured(%d) and expected size(%d) do not match\n", + buf_offset, dump_size); + goto error; + } else { + fw_dump->clr = 1; + snprintf(mesg, sizeof(mesg), "FW_DUMP=%s", + adapter->netdev->name); + dev_info(&adapter->pdev->dev, "Dump data, %d bytes captured\n", + fw_dump->size); + /* Send a udev event to notify availability of FW dump */ + kobject_uevent_env(&adapter->pdev->dev.kobj, KOBJ_CHANGE, msg); + return 0; + } +error: + vfree(fw_dump->data); + return -EINVAL; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c new file mode 100644 index 00000000000..bdaa8cea349 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -0,0 +1,960 @@ +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> + +#include "qlcnic.h" + +#include <linux/swab.h> +#include <linux/dma-mapping.h> +#include <net/ip.h> +#include <linux/ipv6.h> +#include <linux/inetdevice.h> +#include <linux/sysfs.h> +#include <linux/aer.h> +#include <linux/log2.h> + +int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable) +{ + return -EOPNOTSUPP; +} + +int qlcnicvf_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) +{ + return -EOPNOTSUPP; +} + +static ssize_t qlcnic_store_bridged_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + unsigned long new; + int ret = -EINVAL; + + if (!(adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG)) + goto err_out; + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) + goto err_out; + + if (strict_strtoul(buf, 2, &new)) + goto err_out; + + if (!adapter->nic_ops->config_bridged_mode(adapter, !!new)) + ret = len; + +err_out: + return ret; +} + +static ssize_t qlcnic_show_bridged_mode(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int bridged_mode = 0; + + if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) + bridged_mode = !!(adapter->flags & QLCNIC_BRIDGE_ENABLED); + + return sprintf(buf, "%d\n", bridged_mode); +} + +static ssize_t qlcnic_store_diag_mode(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + unsigned long new; + + if (strict_strtoul(buf, 2, &new)) + return -EINVAL; + + if (!!new != !!(adapter->flags & QLCNIC_DIAG_ENABLED)) + adapter->flags ^= QLCNIC_DIAG_ENABLED; + + return len; +} + +static ssize_t qlcnic_show_diag_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", + !!(adapter->flags & QLCNIC_DIAG_ENABLED)); +} + +static int qlcnic_validate_beacon(struct qlcnic_adapter *adapter, u16 beacon, + u8 *state, u8 *rate) +{ + *rate = LSB(beacon); + *state = MSB(beacon); + + QLCDB(adapter, DRV, "rate %x state %x\n", *rate, *state); + + if (!*state) { + *rate = __QLCNIC_MAX_LED_RATE; + return 0; + } else if (*state > __QLCNIC_MAX_LED_STATE) { + return -EINVAL; + } + + if ((!*rate) || (*rate > __QLCNIC_MAX_LED_RATE)) + return -EINVAL; + + return 0; +} + +static ssize_t qlcnic_store_beacon(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int max_sds_rings = adapter->max_sds_rings; + u16 beacon; + u8 b_state, b_rate; + int err; + + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) { + dev_warn(dev, + "LED test not supported in non privileged mode\n"); + return -EOPNOTSUPP; + } + + if (len != sizeof(u16)) + return QL_STATUS_INVALID_PARAM; + + memcpy(&beacon, buf, sizeof(u16)); + err = qlcnic_validate_beacon(adapter, beacon, &b_state, &b_rate); + if (err) + return err; + + if (adapter->ahw->beacon_state == b_state) + return len; + + rtnl_lock(); + + if (!adapter->ahw->beacon_state) + if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) { + rtnl_unlock(); + return -EBUSY; + } + + if (test_bit(__QLCNIC_RESETTING, &adapter->state)) { + err = -EIO; + goto out; + } + + if (!test_bit(__QLCNIC_DEV_UP, &adapter->state)) { + err = qlcnic_diag_alloc_res(adapter->netdev, QLCNIC_LED_TEST); + if (err) + goto out; + set_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state); + } + + err = qlcnic_config_led(adapter, b_state, b_rate); + + if (!err) { + err = len; + adapter->ahw->beacon_state = b_state; + } + + if (test_and_clear_bit(__QLCNIC_DIAG_RES_ALLOC, &adapter->state)) + qlcnic_diag_free_res(adapter->netdev, max_sds_rings); + + out: + if (!adapter->ahw->beacon_state) + clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + rtnl_unlock(); + + return err; +} + +static ssize_t qlcnic_show_beacon(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", adapter->ahw->beacon_state); +} + +static int qlcnic_sysfs_validate_crb(struct qlcnic_adapter *adapter, + loff_t offset, size_t size) +{ + size_t crb_size = 4; + + if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) + return -EIO; + + if (offset < QLCNIC_PCI_CRBSPACE) { + if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, + QLCNIC_PCI_CAMQM_END)) + crb_size = 8; + else + return -EINVAL; + } + + if ((size != crb_size) || (offset & (crb_size-1))) + return -EINVAL; + + return 0; +} + +static ssize_t qlcnic_sysfs_read_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + u32 data; + u64 qmdata; + int ret; + + ret = qlcnic_sysfs_validate_crb(adapter, offset, size); + if (ret != 0) + return ret; + + if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { + qlcnic_pci_camqm_read_2M(adapter, offset, &qmdata); + memcpy(buf, &qmdata, size); + } else { + data = QLCRD32(adapter, offset); + memcpy(buf, &data, size); + } + return size; +} + +static ssize_t qlcnic_sysfs_write_crb(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + u32 data; + u64 qmdata; + int ret; + + ret = qlcnic_sysfs_validate_crb(adapter, offset, size); + if (ret != 0) + return ret; + + if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { + memcpy(&qmdata, buf, size); + qlcnic_pci_camqm_write_2M(adapter, offset, qmdata); + } else { + memcpy(&data, buf, size); + QLCWR32(adapter, offset, data); + } + return size; +} + +static int qlcnic_sysfs_validate_mem(struct qlcnic_adapter *adapter, + loff_t offset, size_t size) +{ + if (!(adapter->flags & QLCNIC_DIAG_ENABLED)) + return -EIO; + + if ((size != 8) || (offset & 0x7)) + return -EIO; + + return 0; +} + +static ssize_t qlcnic_sysfs_read_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + u64 data; + int ret; + + ret = qlcnic_sysfs_validate_mem(adapter, offset, size); + if (ret != 0) + return ret; + + if (qlcnic_pci_mem_read_2M(adapter, offset, &data)) + return -EIO; + + memcpy(buf, &data, size); + + return size; +} + +static ssize_t qlcnic_sysfs_write_mem(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, char *buf, + loff_t offset, size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + u64 data; + int ret; + + ret = qlcnic_sysfs_validate_mem(adapter, offset, size); + if (ret != 0) + return ret; + + memcpy(&data, buf, size); + + if (qlcnic_pci_mem_write_2M(adapter, offset, data)) + return -EIO; + + return size; +} + +static int validate_pm_config(struct qlcnic_adapter *adapter, + struct qlcnic_pm_func_cfg *pm_cfg, int count) +{ + u8 src_pci_func, s_esw_id, d_esw_id, dest_pci_func; + int i; + + for (i = 0; i < count; i++) { + src_pci_func = pm_cfg[i].pci_func; + dest_pci_func = pm_cfg[i].dest_npar; + if (src_pci_func >= QLCNIC_MAX_PCI_FUNC || + dest_pci_func >= QLCNIC_MAX_PCI_FUNC) + return QL_STATUS_INVALID_PARAM; + + if (adapter->npars[src_pci_func].type != QLCNIC_TYPE_NIC) + return QL_STATUS_INVALID_PARAM; + + if (adapter->npars[dest_pci_func].type != QLCNIC_TYPE_NIC) + return QL_STATUS_INVALID_PARAM; + + s_esw_id = adapter->npars[src_pci_func].phy_port; + d_esw_id = adapter->npars[dest_pci_func].phy_port; + + if (s_esw_id != d_esw_id) + return QL_STATUS_INVALID_PARAM; + } + return 0; + +} + +static ssize_t qlcnic_sysfs_write_pm_config(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_pm_func_cfg *pm_cfg; + u32 id, action, pci_func; + int count, rem, i, ret; + + count = size / sizeof(struct qlcnic_pm_func_cfg); + rem = size % sizeof(struct qlcnic_pm_func_cfg); + if (rem) + return QL_STATUS_INVALID_PARAM; + + pm_cfg = (struct qlcnic_pm_func_cfg *)buf; + + ret = validate_pm_config(adapter, pm_cfg, count); + if (ret) + return ret; + for (i = 0; i < count; i++) { + pci_func = pm_cfg[i].pci_func; + action = !!pm_cfg[i].action; + id = adapter->npars[pci_func].phy_port; + ret = qlcnic_config_port_mirroring(adapter, id, action, + pci_func); + if (ret) + return ret; + } + + for (i = 0; i < count; i++) { + pci_func = pm_cfg[i].pci_func; + id = adapter->npars[pci_func].phy_port; + adapter->npars[pci_func].enable_pm = !!pm_cfg[i].action; + adapter->npars[pci_func].dest_npar = id; + } + return size; +} + +static ssize_t qlcnic_sysfs_read_pm_config(struct file *filp, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_pm_func_cfg pm_cfg[QLCNIC_MAX_PCI_FUNC]; + int i; + + if (size != sizeof(pm_cfg)) + return QL_STATUS_INVALID_PARAM; + + for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { + if (adapter->npars[i].type != QLCNIC_TYPE_NIC) + continue; + pm_cfg[i].action = adapter->npars[i].enable_pm; + pm_cfg[i].dest_npar = 0; + pm_cfg[i].pci_func = i; + } + memcpy(buf, &pm_cfg, size); + + return size; +} + +static int validate_esw_config(struct qlcnic_adapter *adapter, + struct qlcnic_esw_func_cfg *esw_cfg, int count) +{ + u32 op_mode; + u8 pci_func; + int i; + + op_mode = readl(adapter->ahw->pci_base0 + QLCNIC_DRV_OP_MODE); + + for (i = 0; i < count; i++) { + pci_func = esw_cfg[i].pci_func; + if (pci_func >= QLCNIC_MAX_PCI_FUNC) + return QL_STATUS_INVALID_PARAM; + + if (adapter->op_mode == QLCNIC_MGMT_FUNC) { + if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC) + return QL_STATUS_INVALID_PARAM; + } + + switch (esw_cfg[i].op_mode) { + case QLCNIC_PORT_DEFAULTS: + if (QLC_DEV_GET_DRV(op_mode, pci_func) != + QLCNIC_NON_PRIV_FUNC) { + if (esw_cfg[i].mac_anti_spoof != 0) + return QL_STATUS_INVALID_PARAM; + if (esw_cfg[i].mac_override != 1) + return QL_STATUS_INVALID_PARAM; + if (esw_cfg[i].promisc_mode != 1) + return QL_STATUS_INVALID_PARAM; + } + break; + case QLCNIC_ADD_VLAN: + if (!IS_VALID_VLAN(esw_cfg[i].vlan_id)) + return QL_STATUS_INVALID_PARAM; + if (!esw_cfg[i].op_type) + return QL_STATUS_INVALID_PARAM; + break; + case QLCNIC_DEL_VLAN: + if (!esw_cfg[i].op_type) + return QL_STATUS_INVALID_PARAM; + break; + default: + return QL_STATUS_INVALID_PARAM; + } + } + return 0; +} + +static ssize_t qlcnic_sysfs_write_esw_config(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_esw_func_cfg *esw_cfg; + struct qlcnic_npar_info *npar; + int count, rem, i, ret; + u8 pci_func, op_mode = 0; + + count = size / sizeof(struct qlcnic_esw_func_cfg); + rem = size % sizeof(struct qlcnic_esw_func_cfg); + if (rem) + return QL_STATUS_INVALID_PARAM; + + esw_cfg = (struct qlcnic_esw_func_cfg *)buf; + ret = validate_esw_config(adapter, esw_cfg, count); + if (ret) + return ret; + + for (i = 0; i < count; i++) { + if (adapter->op_mode == QLCNIC_MGMT_FUNC) { + if (qlcnic_config_switch_port(adapter, &esw_cfg[i])) + return QL_STATUS_INVALID_PARAM; + } + + if (adapter->ahw->pci_func != esw_cfg[i].pci_func) + continue; + + op_mode = esw_cfg[i].op_mode; + qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i]); + esw_cfg[i].op_mode = op_mode; + esw_cfg[i].pci_func = adapter->ahw->pci_func; + + switch (esw_cfg[i].op_mode) { + case QLCNIC_PORT_DEFAULTS: + qlcnic_set_eswitch_port_features(adapter, &esw_cfg[i]); + break; + case QLCNIC_ADD_VLAN: + qlcnic_set_vlan_config(adapter, &esw_cfg[i]); + break; + case QLCNIC_DEL_VLAN: + esw_cfg[i].vlan_id = 0; + qlcnic_set_vlan_config(adapter, &esw_cfg[i]); + break; + } + } + + if (adapter->op_mode != QLCNIC_MGMT_FUNC) + goto out; + + for (i = 0; i < count; i++) { + pci_func = esw_cfg[i].pci_func; + npar = &adapter->npars[pci_func]; + switch (esw_cfg[i].op_mode) { + case QLCNIC_PORT_DEFAULTS: + npar->promisc_mode = esw_cfg[i].promisc_mode; + npar->mac_override = esw_cfg[i].mac_override; + npar->offload_flags = esw_cfg[i].offload_flags; + npar->mac_anti_spoof = esw_cfg[i].mac_anti_spoof; + npar->discard_tagged = esw_cfg[i].discard_tagged; + break; + case QLCNIC_ADD_VLAN: + npar->pvid = esw_cfg[i].vlan_id; + break; + case QLCNIC_DEL_VLAN: + npar->pvid = 0; + break; + } + } +out: + return size; +} + +static ssize_t qlcnic_sysfs_read_esw_config(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_esw_func_cfg esw_cfg[QLCNIC_MAX_PCI_FUNC]; + u8 i; + + if (size != sizeof(esw_cfg)) + return QL_STATUS_INVALID_PARAM; + + for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { + if (adapter->npars[i].type != QLCNIC_TYPE_NIC) + continue; + esw_cfg[i].pci_func = i; + if (qlcnic_get_eswitch_port_config(adapter, &esw_cfg[i])) + return QL_STATUS_INVALID_PARAM; + } + memcpy(buf, &esw_cfg, size); + + return size; +} + +static int validate_npar_config(struct qlcnic_adapter *adapter, + struct qlcnic_npar_func_cfg *np_cfg, + int count) +{ + u8 pci_func, i; + + for (i = 0; i < count; i++) { + pci_func = np_cfg[i].pci_func; + if (pci_func >= QLCNIC_MAX_PCI_FUNC) + return QL_STATUS_INVALID_PARAM; + + if (adapter->npars[pci_func].type != QLCNIC_TYPE_NIC) + return QL_STATUS_INVALID_PARAM; + + if (!IS_VALID_BW(np_cfg[i].min_bw) || + !IS_VALID_BW(np_cfg[i].max_bw)) + return QL_STATUS_INVALID_PARAM; + } + return 0; +} + +static ssize_t qlcnic_sysfs_write_npar_config(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_info nic_info; + struct qlcnic_npar_func_cfg *np_cfg; + int i, count, rem, ret; + u8 pci_func; + + count = size / sizeof(struct qlcnic_npar_func_cfg); + rem = size % sizeof(struct qlcnic_npar_func_cfg); + if (rem) + return QL_STATUS_INVALID_PARAM; + + np_cfg = (struct qlcnic_npar_func_cfg *)buf; + ret = validate_npar_config(adapter, np_cfg, count); + if (ret) + return ret; + + for (i = 0; i < count ; i++) { + pci_func = np_cfg[i].pci_func; + ret = qlcnic_get_nic_info(adapter, &nic_info, pci_func); + if (ret) + return ret; + nic_info.pci_func = pci_func; + nic_info.min_tx_bw = np_cfg[i].min_bw; + nic_info.max_tx_bw = np_cfg[i].max_bw; + ret = qlcnic_set_nic_info(adapter, &nic_info); + if (ret) + return ret; + adapter->npars[i].min_bw = nic_info.min_tx_bw; + adapter->npars[i].max_bw = nic_info.max_tx_bw; + } + + return size; + +} + +static ssize_t qlcnic_sysfs_read_npar_config(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_info nic_info; + struct qlcnic_npar_func_cfg np_cfg[QLCNIC_MAX_PCI_FUNC]; + int i, ret; + + if (size != sizeof(np_cfg)) + return QL_STATUS_INVALID_PARAM; + + for (i = 0; i < QLCNIC_MAX_PCI_FUNC; i++) { + if (adapter->npars[i].type != QLCNIC_TYPE_NIC) + continue; + ret = qlcnic_get_nic_info(adapter, &nic_info, i); + if (ret) + return ret; + + np_cfg[i].pci_func = i; + np_cfg[i].op_mode = (u8)nic_info.op_mode; + np_cfg[i].port_num = nic_info.phys_port; + np_cfg[i].fw_capab = nic_info.capabilities; + np_cfg[i].min_bw = nic_info.min_tx_bw; + np_cfg[i].max_bw = nic_info.max_tx_bw; + np_cfg[i].max_tx_queues = nic_info.max_tx_ques; + np_cfg[i].max_rx_queues = nic_info.max_rx_ques; + } + memcpy(buf, &np_cfg, size); + return size; +} + +static ssize_t qlcnic_sysfs_get_port_stats(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_esw_statistics port_stats; + int ret; + + if (size != sizeof(struct qlcnic_esw_statistics)) + return QL_STATUS_INVALID_PARAM; + + if (offset >= QLCNIC_MAX_PCI_FUNC) + return QL_STATUS_INVALID_PARAM; + + memset(&port_stats, 0, size); + ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, + &port_stats.rx); + if (ret) + return ret; + + ret = qlcnic_get_port_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, + &port_stats.tx); + if (ret) + return ret; + + memcpy(buf, &port_stats, size); + return size; +} + +static ssize_t qlcnic_sysfs_get_esw_stats(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_esw_statistics esw_stats; + int ret; + + if (size != sizeof(struct qlcnic_esw_statistics)) + return QL_STATUS_INVALID_PARAM; + + if (offset >= QLCNIC_NIU_MAX_XG_PORTS) + return QL_STATUS_INVALID_PARAM; + + memset(&esw_stats, 0, size); + ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_RX_COUNTER, + &esw_stats.rx); + if (ret) + return ret; + + ret = qlcnic_get_eswitch_stats(adapter, offset, QLCNIC_QUERY_TX_COUNTER, + &esw_stats.tx); + if (ret) + return ret; + + memcpy(buf, &esw_stats, size); + return size; +} + +static ssize_t qlcnic_sysfs_clear_esw_stats(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int ret; + + if (offset >= QLCNIC_NIU_MAX_XG_PORTS) + return QL_STATUS_INVALID_PARAM; + + ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, + QLCNIC_QUERY_RX_COUNTER); + if (ret) + return ret; + + ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_ESWITCH, offset, + QLCNIC_QUERY_TX_COUNTER); + if (ret) + return ret; + + return size; +} + +static ssize_t qlcnic_sysfs_clear_port_stats(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + int ret; + + if (offset >= QLCNIC_MAX_PCI_FUNC) + return QL_STATUS_INVALID_PARAM; + + ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, + QLCNIC_QUERY_RX_COUNTER); + if (ret) + return ret; + + ret = qlcnic_clear_esw_stats(adapter, QLCNIC_STATS_PORT, offset, + QLCNIC_QUERY_TX_COUNTER); + if (ret) + return ret; + + return size; +} + +static ssize_t qlcnic_sysfs_read_pci_config(struct file *file, + struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t offset, + size_t size) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + struct qlcnic_pci_func_cfg pci_cfg[QLCNIC_MAX_PCI_FUNC]; + struct qlcnic_pci_info *pci_info; + int i, ret; + + if (size != sizeof(pci_cfg)) + return QL_STATUS_INVALID_PARAM; + + pci_info = kcalloc(QLCNIC_MAX_PCI_FUNC, sizeof(*pci_info), GFP_KERNEL); + if (!pci_info) + return -ENOMEM; + + ret = qlcnic_get_pci_info(adapter, pci_info); + if (ret) { + kfree(pci_info); + return ret; + } + + for (i = 0; i < QLCNIC_MAX_PCI_FUNC ; i++) { + pci_cfg[i].pci_func = pci_info[i].id; + pci_cfg[i].func_type = pci_info[i].type; + pci_cfg[i].port_num = pci_info[i].default_port; + pci_cfg[i].min_bw = pci_info[i].tx_min_bw; + pci_cfg[i].max_bw = pci_info[i].tx_max_bw; + memcpy(&pci_cfg[i].def_mac_addr, &pci_info[i].mac, ETH_ALEN); + } + memcpy(buf, &pci_cfg, size); + kfree(pci_info); + return size; +} + +static struct device_attribute dev_attr_bridged_mode = { + .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)}, + .show = qlcnic_show_bridged_mode, + .store = qlcnic_store_bridged_mode, +}; + +static struct device_attribute dev_attr_diag_mode = { + .attr = {.name = "diag_mode", .mode = (S_IRUGO | S_IWUSR)}, + .show = qlcnic_show_diag_mode, + .store = qlcnic_store_diag_mode, +}; + +static struct device_attribute dev_attr_beacon = { + .attr = {.name = "beacon", .mode = (S_IRUGO | S_IWUSR)}, + .show = qlcnic_show_beacon, + .store = qlcnic_store_beacon, +}; + +static struct bin_attribute bin_attr_crb = { + .attr = {.name = "crb", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_crb, + .write = qlcnic_sysfs_write_crb, +}; + +static struct bin_attribute bin_attr_mem = { + .attr = {.name = "mem", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_mem, + .write = qlcnic_sysfs_write_mem, +}; + +static struct bin_attribute bin_attr_npar_config = { + .attr = {.name = "npar_config", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_npar_config, + .write = qlcnic_sysfs_write_npar_config, +}; + +static struct bin_attribute bin_attr_pci_config = { + .attr = {.name = "pci_config", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_pci_config, + .write = NULL, +}; + +static struct bin_attribute bin_attr_port_stats = { + .attr = {.name = "port_stats", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_get_port_stats, + .write = qlcnic_sysfs_clear_port_stats, +}; + +static struct bin_attribute bin_attr_esw_stats = { + .attr = {.name = "esw_stats", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_get_esw_stats, + .write = qlcnic_sysfs_clear_esw_stats, +}; + +static struct bin_attribute bin_attr_esw_config = { + .attr = {.name = "esw_config", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_esw_config, + .write = qlcnic_sysfs_write_esw_config, +}; + +static struct bin_attribute bin_attr_pm_config = { + .attr = {.name = "pm_config", .mode = (S_IRUGO | S_IWUSR)}, + .size = 0, + .read = qlcnic_sysfs_read_pm_config, + .write = qlcnic_sysfs_write_pm_config, +}; + +void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + + if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) + if (device_create_file(dev, &dev_attr_bridged_mode)) + dev_warn(dev, + "failed to create bridged_mode sysfs entry\n"); +} + +void qlcnic_remove_sysfs_entries(struct qlcnic_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + + if (adapter->capabilities & QLCNIC_FW_CAPABILITY_BDG) + device_remove_file(dev, &dev_attr_bridged_mode); +} + +void qlcnic_create_diag_entries(struct qlcnic_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); + + if (device_create_bin_file(dev, &bin_attr_port_stats)) + dev_info(dev, "failed to create port stats sysfs entry"); + + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) + return; + if (device_create_file(dev, &dev_attr_diag_mode)) + dev_info(dev, "failed to create diag_mode sysfs entry\n"); + if (device_create_bin_file(dev, &bin_attr_crb)) + dev_info(dev, "failed to create crb sysfs entry\n"); + if (device_create_bin_file(dev, &bin_attr_mem)) + dev_info(dev, "failed to create mem sysfs entry\n"); + + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; + + if (device_create_bin_file(dev, &bin_attr_pci_config)) + dev_info(dev, "failed to create pci config sysfs entry"); + if (device_create_file(dev, &dev_attr_beacon)) + dev_info(dev, "failed to create beacon sysfs entry"); + + if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) + return; + if (device_create_bin_file(dev, &bin_attr_esw_config)) + dev_info(dev, "failed to create esw config sysfs entry"); + if (adapter->op_mode != QLCNIC_MGMT_FUNC) + return; + if (device_create_bin_file(dev, &bin_attr_npar_config)) + dev_info(dev, "failed to create npar config sysfs entry"); + if (device_create_bin_file(dev, &bin_attr_pm_config)) + dev_info(dev, "failed to create pm config sysfs entry"); + if (device_create_bin_file(dev, &bin_attr_esw_stats)) + dev_info(dev, "failed to create eswitch stats sysfs entry"); +} + +void qlcnic_remove_diag_entries(struct qlcnic_adapter *adapter) +{ + struct device *dev = &adapter->pdev->dev; + u32 state = QLCRD32(adapter, QLCNIC_CRB_DEV_STATE); + + device_remove_bin_file(dev, &bin_attr_port_stats); + + if (adapter->op_mode == QLCNIC_NON_PRIV_FUNC) + return; + device_remove_file(dev, &dev_attr_diag_mode); + device_remove_bin_file(dev, &bin_attr_crb); + device_remove_bin_file(dev, &bin_attr_mem); + if (state == QLCNIC_DEV_FAILED || (state == QLCNIC_DEV_BADBAD)) + return; + device_remove_bin_file(dev, &bin_attr_pci_config); + device_remove_file(dev, &dev_attr_beacon); + if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) + return; + device_remove_bin_file(dev, &bin_attr_esw_config); + if (adapter->op_mode != QLCNIC_MGMT_FUNC) + return; + device_remove_bin_file(dev, &bin_attr_npar_config); + device_remove_bin_file(dev, &bin_attr_pm_config); + device_remove_bin_file(dev, &bin_attr_esw_stats); +} diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c index 4f86d0cd516..d858f310b2b 100644 --- a/drivers/net/ethernet/sfc/efx.c +++ b/drivers/net/ethernet/sfc/efx.c @@ -106,8 +106,8 @@ static struct workqueue_struct *reset_workqueue; * * This is only used in MSI-X interrupt mode */ -static unsigned int separate_tx_channels; -module_param(separate_tx_channels, uint, 0444); +static bool separate_tx_channels; +module_param(separate_tx_channels, bool, 0444); MODULE_PARM_DESC(separate_tx_channels, "Use separate channels for TX and RX"); @@ -160,8 +160,8 @@ static unsigned int rss_cpus; module_param(rss_cpus, uint, 0444); MODULE_PARM_DESC(rss_cpus, "Number of CPUs to use for Receive-Side Scaling"); -static int phy_flash_cfg; -module_param(phy_flash_cfg, int, 0644); +static bool phy_flash_cfg; +module_param(phy_flash_cfg, bool, 0644); MODULE_PARM_DESC(phy_flash_cfg, "Set PHYs into reflash mode initially"); static unsigned irq_adapt_low_thresh = 8000; @@ -2279,7 +2279,7 @@ int efx_reset(struct efx_nic *efx, enum reset_type method) netif_info(efx, drv, efx->net_dev, "resetting (%s)\n", RESET_TYPE(method)); - netif_device_detach(efx->net_dev); + efx_device_detach_sync(efx); efx_reset_down(efx, method); rc = efx->type->reset(efx, method); @@ -2758,7 +2758,7 @@ static int efx_pm_freeze(struct device *dev) if (efx->state != STATE_DISABLED) { efx->state = STATE_UNINIT; - netif_device_detach(efx->net_dev); + efx_device_detach_sync(efx); efx_stop_all(efx); efx_stop_interrupts(efx, false); diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index f11170bc48b..50247dfe8f5 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -163,4 +163,17 @@ extern void efx_link_status_changed(struct efx_nic *efx); extern void efx_link_set_advertising(struct efx_nic *efx, u32); extern void efx_link_set_wanted_fc(struct efx_nic *efx, u8); +static inline void efx_device_detach_sync(struct efx_nic *efx) +{ + struct net_device *dev = efx->net_dev; + + /* Lock/freeze all TX queues so that we can be sure the + * TX scheduler is stopped when we're done and before + * netif_device_present() becomes false. + */ + netif_tx_lock(dev); + netif_device_detach(dev); + netif_tx_unlock(dev); +} + #endif /* EFX_EFX_H */ diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 90f078eff8e..8e61cd06f66 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -816,6 +816,9 @@ static int efx_ethtool_reset(struct net_device *net_dev, u32 *flags) /* MAC address mask including only MC flag */ static const u8 mac_addr_mc_mask[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; +#define IP4_ADDR_FULL_MASK ((__force __be32)~0) +#define PORT_FULL_MASK ((__force __be16)~0) + static int efx_ethtool_get_class_rule(struct efx_nic *efx, struct ethtool_rx_flow_spec *rule) { @@ -865,12 +868,12 @@ static int efx_ethtool_get_class_rule(struct efx_nic *efx, &spec, &proto, &ip_entry->ip4dst, &ip_entry->pdst, &ip_entry->ip4src, &ip_entry->psrc); EFX_WARN_ON_PARANOID(rc); - ip_mask->ip4src = ~0; - ip_mask->psrc = ~0; + ip_mask->ip4src = IP4_ADDR_FULL_MASK; + ip_mask->psrc = PORT_FULL_MASK; } rule->flow_type = (proto == IPPROTO_TCP) ? TCP_V4_FLOW : UDP_V4_FLOW; - ip_mask->ip4dst = ~0; - ip_mask->pdst = ~0; + ip_mask->ip4dst = IP4_ADDR_FULL_MASK; + ip_mask->pdst = PORT_FULL_MASK; return rc; } @@ -971,7 +974,7 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx, /* Check for unsupported extensions */ if ((rule->flow_type & FLOW_EXT) && - (rule->m_ext.vlan_etype | rule->m_ext.data[0] | + (rule->m_ext.vlan_etype || rule->m_ext.data[0] || rule->m_ext.data[1])) return -EINVAL; @@ -986,16 +989,16 @@ static int efx_ethtool_set_class_rule(struct efx_nic *efx, IPPROTO_TCP : IPPROTO_UDP); /* Must match all of destination, */ - if ((__force u32)~ip_mask->ip4dst | - (__force u16)~ip_mask->pdst) + if (!(ip_mask->ip4dst == IP4_ADDR_FULL_MASK && + ip_mask->pdst == PORT_FULL_MASK)) return -EINVAL; /* all or none of source, */ - if ((ip_mask->ip4src | ip_mask->psrc) && - ((__force u32)~ip_mask->ip4src | - (__force u16)~ip_mask->psrc)) + if ((ip_mask->ip4src || ip_mask->psrc) && + !(ip_mask->ip4src == IP4_ADDR_FULL_MASK && + ip_mask->psrc == PORT_FULL_MASK)) return -EINVAL; /* and nothing else */ - if (ip_mask->tos | rule->m_ext.vlan_tci) + if (ip_mask->tos || rule->m_ext.vlan_tci) return -EINVAL; if (ip_mask->ip4src) diff --git a/drivers/net/ethernet/sfc/falcon.c b/drivers/net/ethernet/sfc/falcon.c index 12b573a8e82..49bcd196e10 100644 --- a/drivers/net/ethernet/sfc/falcon.c +++ b/drivers/net/ethernet/sfc/falcon.c @@ -1792,6 +1792,7 @@ const struct efx_nic_type falcon_a1_nic_type = { .remove_port = falcon_remove_port, .handle_global_event = falcon_handle_global_event, .prepare_flush = falcon_prepare_flush, + .finish_flush = efx_port_dummy_op_void, .update_stats = falcon_update_nic_stats, .start_stats = falcon_start_nic_stats, .stop_stats = falcon_stop_nic_stats, @@ -1834,6 +1835,7 @@ const struct efx_nic_type falcon_b0_nic_type = { .remove_port = falcon_remove_port, .handle_global_event = falcon_handle_global_event, .prepare_flush = falcon_prepare_flush, + .finish_flush = efx_port_dummy_op_void, .update_stats = falcon_update_nic_stats, .start_stats = falcon_start_nic_stats, .stop_stats = falcon_stop_nic_stats, diff --git a/drivers/net/ethernet/sfc/io.h b/drivers/net/ethernet/sfc/io.h index 751d1ec112c..96759aee1c6 100644 --- a/drivers/net/ethernet/sfc/io.h +++ b/drivers/net/ethernet/sfc/io.h @@ -22,22 +22,21 @@ * * Notes on locking strategy: * - * Most CSRs are 128-bit (oword) and therefore cannot be read or - * written atomically. Access from the host is buffered by the Bus - * Interface Unit (BIU). Whenever the host reads from the lowest - * address of such a register, or from the address of a different such - * register, the BIU latches the register's value. Subsequent reads - * from higher addresses of the same register will read the latched - * value. Whenever the host writes part of such a register, the BIU - * collects the written value and does not write to the underlying - * register until all 4 dwords have been written. A similar buffering - * scheme applies to host access to the NIC's 64-bit SRAM. + * Many CSRs are very wide and cannot be read or written atomically. + * Writes from the host are buffered by the Bus Interface Unit (BIU) + * up to 128 bits. Whenever the host writes part of such a register, + * the BIU collects the written value and does not write to the + * underlying register until all 4 dwords have been written. A + * similar buffering scheme applies to host access to the NIC's 64-bit + * SRAM. * - * Access to different CSRs and 64-bit SRAM words must be serialised, - * since interleaved access can result in lost writes or lost - * information from read-to-clear fields. We use efx_nic::biu_lock - * for this. (We could use separate locks for read and write, but - * this is not normally a performance bottleneck.) + * Writes to different CSRs and 64-bit SRAM words must be serialised, + * since interleaved access can result in lost writes. We use + * efx_nic::biu_lock for this. + * + * We also serialise reads from 128-bit CSRs and SRAM with the same + * spinlock. This may not be necessary, but it doesn't really matter + * as there are no such reads on the fast path. * * The DMA descriptor pointers (RX_DESC_UPD and TX_DESC_UPD) are * 128-bit but are special-cased in the BIU to avoid the need for @@ -204,20 +203,6 @@ static inline void efx_reado_table(struct efx_nic *efx, efx_oword_t *value, efx_reado(efx, value, reg + index * sizeof(efx_oword_t)); } -/* Write a 32-bit CSR forming part of a table, or 32-bit SRAM */ -static inline void efx_writed_table(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg, unsigned int index) -{ - efx_writed(efx, value, reg + index * sizeof(efx_oword_t)); -} - -/* Read a 32-bit CSR forming part of a table, or 32-bit SRAM */ -static inline void efx_readd_table(struct efx_nic *efx, efx_dword_t *value, - unsigned int reg, unsigned int index) -{ - efx_readd(efx, value, reg + index * sizeof(efx_dword_t)); -} - /* Page-mapped register block size */ #define EFX_PAGE_BLOCK_SIZE 0x2000 diff --git a/drivers/net/ethernet/sfc/mcdi.c b/drivers/net/ethernet/sfc/mcdi.c index aea43cbd052..0095ce95150 100644 --- a/drivers/net/ethernet/sfc/mcdi.c +++ b/drivers/net/ethernet/sfc/mcdi.c @@ -22,7 +22,7 @@ ************************************************************************** */ -#define MCDI_RPC_TIMEOUT 10 /*seconds */ +#define MCDI_RPC_TIMEOUT (10 * HZ) #define MCDI_PDU(efx) \ (efx_port_num(efx) ? MC_SMEM_P1_PDU_OFST : MC_SMEM_P0_PDU_OFST) @@ -120,7 +120,7 @@ static void efx_mcdi_copyout(struct efx_nic *efx, u8 *outbuf, size_t outlen) static int efx_mcdi_poll(struct efx_nic *efx) { struct efx_mcdi_iface *mcdi = efx_mcdi(efx); - unsigned int time, finish; + unsigned long time, finish; unsigned int respseq, respcmd, error; unsigned int pdu = FR_CZ_MC_TREG_SMEM + MCDI_PDU(efx); unsigned int rc, spins; @@ -136,7 +136,7 @@ static int efx_mcdi_poll(struct efx_nic *efx) * and poll once a jiffy (approximately) */ spins = TICK_USEC; - finish = get_seconds() + MCDI_RPC_TIMEOUT; + finish = jiffies + MCDI_RPC_TIMEOUT; while (1) { if (spins != 0) { @@ -146,7 +146,7 @@ static int efx_mcdi_poll(struct efx_nic *efx) schedule_timeout_uninterruptible(1); } - time = get_seconds(); + time = jiffies; rmb(); efx_readd(efx, ®, pdu); @@ -158,7 +158,7 @@ static int efx_mcdi_poll(struct efx_nic *efx) EFX_DWORD_FIELD(reg, MCDI_HEADER_RESPONSE)) break; - if (time >= finish) + if (time_after(time, finish)) return -ETIMEDOUT; } @@ -207,7 +207,9 @@ out: return 0; } -/* Test and clear MC-rebooted flag for this port/function */ +/* Test and clear MC-rebooted flag for this port/function; reset + * software state as necessary. + */ int efx_mcdi_poll_reboot(struct efx_nic *efx) { unsigned int addr = FR_CZ_MC_TREG_SMEM + MCDI_STATUS(efx); @@ -223,6 +225,11 @@ int efx_mcdi_poll_reboot(struct efx_nic *efx) if (value == 0) return 0; + /* MAC statistics have been cleared on the NIC; clear our copy + * so that efx_update_diff_stat() can continue to work. + */ + memset(&efx->mac_stats, 0, sizeof(efx->mac_stats)); + EFX_ZERO_DWORD(reg); efx_writed(efx, ®, addr); @@ -250,7 +257,7 @@ static int efx_mcdi_await_completion(struct efx_nic *efx) if (wait_event_timeout( mcdi->wq, atomic_read(&mcdi->state) == MCDI_STATE_COMPLETED, - msecs_to_jiffies(MCDI_RPC_TIMEOUT * 1000)) == 0) + MCDI_RPC_TIMEOUT) == 0) return -ETIMEDOUT; /* Check if efx_mcdi_set_mode() switched us back to polled completions. @@ -1216,7 +1223,7 @@ int efx_mcdi_flush_rxqs(struct efx_nic *efx) rc = efx_mcdi_rpc(efx, MC_CMD_FLUSH_RX_QUEUES, (u8 *)qid, count * sizeof(*qid), NULL, 0, NULL); - WARN_ON(rc > 0); + WARN_ON(rc < 0); kfree(qid); diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h index 2487f582ab0..2d756c1d714 100644 --- a/drivers/net/ethernet/sfc/net_driver.h +++ b/drivers/net/ethernet/sfc/net_driver.h @@ -200,6 +200,7 @@ struct efx_tx_queue { /* Members shared between paths and sometimes updated */ unsigned int empty_read_count ____cacheline_aligned_in_smp; #define EFX_EMPTY_COUNT_VALID 0x80000000 + atomic_t flush_outstanding; }; /** @@ -907,6 +908,7 @@ static inline unsigned int efx_port_num(struct efx_nic *efx) * @remove_port: Free resources allocated by probe_port() * @handle_global_event: Handle a "global" event (may be %NULL) * @prepare_flush: Prepare the hardware for flushing the DMA queues + * @finish_flush: Clean up after flushing the DMA queues * @update_stats: Update statistics not provided by event handling * @start_stats: Start the regular fetching of statistics * @stop_stats: Stop the regular fetching of statistics @@ -954,6 +956,7 @@ struct efx_nic_type { void (*remove_port)(struct efx_nic *efx); bool (*handle_global_event)(struct efx_channel *channel, efx_qword_t *); void (*prepare_flush)(struct efx_nic *efx); + void (*finish_flush)(struct efx_nic *efx); void (*update_stats)(struct efx_nic *efx); void (*start_stats)(struct efx_nic *efx); void (*stop_stats)(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/nic.c b/drivers/net/ethernet/sfc/nic.c index aab7cacb2e3..0ad790cc473 100644 --- a/drivers/net/ethernet/sfc/nic.c +++ b/drivers/net/ethernet/sfc/nic.c @@ -73,6 +73,8 @@ _EFX_CHANNEL_MAGIC(_EFX_CHANNEL_MAGIC_TX_DRAIN, \ (_tx_queue)->queue) +static void efx_magic_event(struct efx_channel *channel, u32 magic); + /************************************************************************** * * Solarstorm hardware access @@ -255,9 +257,6 @@ static int efx_alloc_special_buffer(struct efx_nic *efx, buffer->entries = len / EFX_BUF_SIZE; BUG_ON(buffer->dma_addr & (EFX_BUF_SIZE - 1)); - /* All zeros is a potentially valid event so memset to 0xff */ - memset(buffer->addr, 0xff, len); - /* Select new buffer ID */ buffer->index = efx->next_buffer_table; efx->next_buffer_table += buffer->entries; @@ -494,6 +493,9 @@ static void efx_flush_tx_queue(struct efx_tx_queue *tx_queue) struct efx_nic *efx = tx_queue->efx; efx_oword_t tx_flush_descq; + WARN_ON(atomic_read(&tx_queue->flush_outstanding)); + atomic_set(&tx_queue->flush_outstanding, 1); + EFX_POPULATE_OWORD_2(tx_flush_descq, FRF_AZ_TX_FLUSH_DESCQ_CMD, 1, FRF_AZ_TX_FLUSH_DESCQ, tx_queue->queue); @@ -669,6 +671,47 @@ static bool efx_flush_wake(struct efx_nic *efx) && atomic_read(&efx->rxq_flush_pending) > 0)); } +static bool efx_check_tx_flush_complete(struct efx_nic *efx) +{ + bool i = true; + efx_oword_t txd_ptr_tbl; + struct efx_channel *channel; + struct efx_tx_queue *tx_queue; + + efx_for_each_channel(channel, efx) { + efx_for_each_channel_tx_queue(tx_queue, channel) { + efx_reado_table(efx, &txd_ptr_tbl, + FR_BZ_TX_DESC_PTR_TBL, tx_queue->queue); + if (EFX_OWORD_FIELD(txd_ptr_tbl, + FRF_AZ_TX_DESCQ_FLUSH) || + EFX_OWORD_FIELD(txd_ptr_tbl, + FRF_AZ_TX_DESCQ_EN)) { + netif_dbg(efx, hw, efx->net_dev, + "flush did not complete on TXQ %d\n", + tx_queue->queue); + i = false; + } else if (atomic_cmpxchg(&tx_queue->flush_outstanding, + 1, 0)) { + /* The flush is complete, but we didn't + * receive a flush completion event + */ + netif_dbg(efx, hw, efx->net_dev, + "flush complete on TXQ %d, so drain " + "the queue\n", tx_queue->queue); + /* Don't need to increment drain_pending as it + * has already been incremented for the queues + * which did not drain + */ + efx_magic_event(channel, + EFX_CHANNEL_MAGIC_TX_DRAIN( + tx_queue)); + } + } + } + + return i; +} + /* Flush all the transmit queues, and continue flushing receive queues until * they're all flushed. Wait for the DRAIN events to be recieved so that there * are no more RX and TX events left on any channel. */ @@ -680,7 +723,6 @@ int efx_nic_flush_queues(struct efx_nic *efx) struct efx_tx_queue *tx_queue; int rc = 0; - efx->fc_disable++; efx->type->prepare_flush(efx); efx_for_each_channel(channel, efx) { @@ -730,7 +772,8 @@ int efx_nic_flush_queues(struct efx_nic *efx) timeout); } - if (atomic_read(&efx->drain_pending)) { + if (atomic_read(&efx->drain_pending) && + !efx_check_tx_flush_complete(efx)) { netif_err(efx, hw, efx->net_dev, "failed to flush %d queues " "(rx %d+%d)\n", atomic_read(&efx->drain_pending), atomic_read(&efx->rxq_flush_outstanding), @@ -742,7 +785,7 @@ int efx_nic_flush_queues(struct efx_nic *efx) atomic_set(&efx->rxq_flush_outstanding, 0); } - efx->fc_disable--; + efx->type->finish_flush(efx); return rc; } @@ -766,8 +809,13 @@ void efx_nic_eventq_read_ack(struct efx_channel *channel) EFX_POPULATE_DWORD_1(reg, FRF_AZ_EVQ_RPTR, channel->eventq_read_ptr & channel->eventq_mask); - efx_writed_table(efx, ®, efx->type->evq_rptr_tbl_base, - channel->channel); + + /* For Falcon A1, EVQ_RPTR_KER is documented as having a step size + * of 4 bytes, but it is really 16 bytes just like later revisions. + */ + efx_writed(efx, ®, + efx->type->evq_rptr_tbl_base + + FR_BZ_EVQ_RPTR_STEP * channel->channel); } /* Use HW to insert a SW defined event */ @@ -1017,9 +1065,10 @@ efx_handle_tx_flush_done(struct efx_nic *efx, efx_qword_t *event) if (qid < EFX_TXQ_TYPES * efx->n_tx_channels) { tx_queue = efx_get_tx_queue(efx, qid / EFX_TXQ_TYPES, qid % EFX_TXQ_TYPES); - - efx_magic_event(tx_queue->channel, - EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); + if (atomic_cmpxchg(&tx_queue->flush_outstanding, 1, 0)) { + efx_magic_event(tx_queue->channel, + EFX_CHANNEL_MAGIC_TX_DRAIN(tx_queue)); + } } } @@ -1565,7 +1614,9 @@ void efx_nic_push_rx_indir_table(struct efx_nic *efx) for (i = 0; i < FR_BZ_RX_INDIRECTION_TBL_ROWS; i++) { EFX_POPULATE_DWORD_1(dword, FRF_BZ_IT_QUEUE, efx->rx_indir_table[i]); - efx_writed_table(efx, &dword, FR_BZ_RX_INDIRECTION_TBL, i); + efx_writed(efx, &dword, + FR_BZ_RX_INDIRECTION_TBL + + FR_BZ_RX_INDIRECTION_TBL_STEP * i); } } @@ -2029,15 +2080,15 @@ void efx_nic_get_regs(struct efx_nic *efx, void *buf) for (i = 0; i < table->rows; i++) { switch (table->step) { - case 4: /* 32-bit register or SRAM */ - efx_readd_table(efx, buf, table->offset, i); + case 4: /* 32-bit SRAM */ + efx_readd(efx, buf, table->offset + 4 * i); break; case 8: /* 64-bit SRAM */ efx_sram_readq(efx, efx->membase + table->offset, buf, i); break; - case 16: /* 128-bit register */ + case 16: /* 128-bit-readable register */ efx_reado_table(efx, buf, table->offset, i); break; case 32: /* 128-bit register, interleaved */ diff --git a/drivers/net/ethernet/sfc/nic.h b/drivers/net/ethernet/sfc/nic.h index 7a9647a3c56..1b000332349 100644 --- a/drivers/net/ethernet/sfc/nic.h +++ b/drivers/net/ethernet/sfc/nic.h @@ -344,6 +344,8 @@ static inline int efx_nic_irq_test_irq_cpu(struct efx_nic *efx) /* Global Resources */ extern int efx_nic_flush_queues(struct efx_nic *efx); +extern void siena_prepare_flush(struct efx_nic *efx); +extern void siena_finish_flush(struct efx_nic *efx); extern void falcon_start_nic_stats(struct efx_nic *efx); extern void falcon_stop_nic_stats(struct efx_nic *efx); extern void falcon_setup_xaui(struct efx_nic *efx); diff --git a/drivers/net/ethernet/sfc/rx.c b/drivers/net/ethernet/sfc/rx.c index 9e0ad1b75c3..d780a0d096b 100644 --- a/drivers/net/ethernet/sfc/rx.c +++ b/drivers/net/ethernet/sfc/rx.c @@ -187,7 +187,6 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue) struct efx_nic *efx = rx_queue->efx; struct efx_rx_buffer *rx_buf; struct page *page; - void *page_addr; struct efx_rx_page_state *state; dma_addr_t dma_addr; unsigned index, count; @@ -207,12 +206,10 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue) __free_pages(page, efx->rx_buffer_order); return -EIO; } - page_addr = page_address(page); - state = page_addr; + state = page_address(page); state->refcnt = 0; state->dma_addr = dma_addr; - page_addr += sizeof(struct efx_rx_page_state); dma_addr += sizeof(struct efx_rx_page_state); split: @@ -230,7 +227,6 @@ static int efx_init_rx_buffers_page(struct efx_rx_queue *rx_queue) /* Use the second half of the page */ get_page(page); dma_addr += (PAGE_SIZE >> 1); - page_addr += (PAGE_SIZE >> 1); ++count; goto split; } diff --git a/drivers/net/ethernet/sfc/selftest.c b/drivers/net/ethernet/sfc/selftest.c index ce72ae4f399..2069f51b2aa 100644 --- a/drivers/net/ethernet/sfc/selftest.c +++ b/drivers/net/ethernet/sfc/selftest.c @@ -373,7 +373,7 @@ static void efx_iterate_state(struct efx_nic *efx) /* saddr set later and used as incrementing count */ payload->ip.daddr = htonl(INADDR_LOOPBACK); payload->ip.ihl = 5; - payload->ip.check = htons(0xdead); + payload->ip.check = (__force __sum16) htons(0xdead); payload->ip.tot_len = htons(sizeof(*payload) - sizeof(struct ethhdr)); payload->ip.version = IPVERSION; payload->ip.protocol = IPPROTO_UDP; @@ -722,7 +722,7 @@ int efx_selftest(struct efx_nic *efx, struct efx_self_tests *tests, /* Detach the device so the kernel doesn't transmit during the * loopback test and the watchdog timeout doesn't fire. */ - netif_device_detach(efx->net_dev); + efx_device_detach_sync(efx); if (efx->type->test_chip) { rc_reset = efx->type->test_chip(efx, tests); diff --git a/drivers/net/ethernet/sfc/siena.c b/drivers/net/ethernet/sfc/siena.c index 84b41bf08a3..ba40f67e4f0 100644 --- a/drivers/net/ethernet/sfc/siena.c +++ b/drivers/net/ethernet/sfc/siena.c @@ -127,6 +127,18 @@ static void siena_remove_port(struct efx_nic *efx) efx_nic_free_buffer(efx, &efx->stats_buffer); } +void siena_prepare_flush(struct efx_nic *efx) +{ + if (efx->fc_disable++ == 0) + efx_mcdi_set_mac(efx); +} + +void siena_finish_flush(struct efx_nic *efx) +{ + if (--efx->fc_disable == 0) + efx_mcdi_set_mac(efx); +} + static const struct efx_nic_register_test siena_register_tests[] = { { FR_AZ_ADR_REGION, EFX_OWORD32(0x0003FFFF, 0x0003FFFF, 0x0003FFFF, 0x0003FFFF) }, @@ -158,7 +170,7 @@ static const struct efx_nic_register_test siena_register_tests[] = { static int siena_test_chip(struct efx_nic *efx, struct efx_self_tests *tests) { - enum reset_type reset_method = reset_method; + enum reset_type reset_method = RESET_TYPE_ALL; int rc, rc2; efx_reset_down(efx, reset_method); @@ -659,7 +671,8 @@ const struct efx_nic_type siena_a0_nic_type = { .reset = siena_reset_hw, .probe_port = siena_probe_port, .remove_port = siena_remove_port, - .prepare_flush = efx_port_dummy_op_void, + .prepare_flush = siena_prepare_flush, + .finish_flush = siena_finish_flush, .update_stats = siena_update_nic_stats, .start_stats = siena_start_nic_stats, .stop_stats = siena_stop_nic_stats, diff --git a/drivers/net/ethernet/sfc/siena_sriov.c b/drivers/net/ethernet/sfc/siena_sriov.c index d49b53dc2a5..90f8d1604f5 100644 --- a/drivers/net/ethernet/sfc/siena_sriov.c +++ b/drivers/net/ethernet/sfc/siena_sriov.c @@ -695,8 +695,7 @@ static int efx_vfdi_fini_all_queues(struct efx_vf *vf) return VFDI_RC_ENOMEM; rtnl_lock(); - if (efx->fc_disable++ == 0) - efx_mcdi_set_mac(efx); + siena_prepare_flush(efx); rtnl_unlock(); /* Flush all the initialized queues */ @@ -733,8 +732,7 @@ static int efx_vfdi_fini_all_queues(struct efx_vf *vf) } rtnl_lock(); - if (--efx->fc_disable == 0) - efx_mcdi_set_mac(efx); + siena_finish_flush(efx); rtnl_unlock(); /* Irrespective of success/failure, fini the queues */ @@ -995,7 +993,7 @@ static void efx_sriov_reset_vf(struct efx_vf *vf, struct efx_buffer *buffer) FRF_AZ_EVQ_BUF_BASE_ID, buftbl); efx_writeo_table(efx, ®, FR_BZ_EVQ_PTR_TBL, abs_evq); EFX_POPULATE_DWORD_1(ptr, FRF_AZ_EVQ_RPTR, 0); - efx_writed_table(efx, &ptr, FR_BZ_EVQ_RPTR, abs_evq); + efx_writed(efx, &ptr, FR_BZ_EVQ_RPTR + FR_BZ_EVQ_RPTR_STEP * abs_evq); mutex_unlock(&vf->status_lock); } diff --git a/drivers/net/ethernet/stmicro/stmmac/Kconfig b/drivers/net/ethernet/stmicro/stmmac/Kconfig index 9f448279e12..1164930a40a 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Kconfig +++ b/drivers/net/ethernet/stmicro/stmmac/Kconfig @@ -54,31 +54,6 @@ config STMMAC_DA By default, the DMA arbitration scheme is based on Round-robin (rx:tx priority is 1:1). -config STMMAC_TIMER - bool "STMMAC Timer optimisation" - default n - depends on RTC_HCTOSYS_DEVICE - ---help--- - Use an external timer for mitigating the number of network - interrupts. Currently, for SH architectures, it is possible - to use the TMU channel 2 and the SH-RTC device. - -choice - prompt "Select Timer device" - depends on STMMAC_TIMER - -config STMMAC_TMU_TIMER - bool "TMU channel 2" - depends on CPU_SH4 - ---help--- - -config STMMAC_RTC_TIMER - bool "Real time clock" - depends on RTC_CLASS - ---help--- - -endchoice - choice prompt "Select the DMA TX/RX descriptor operating modes" depends on STMMAC_ETH diff --git a/drivers/net/ethernet/stmicro/stmmac/Makefile b/drivers/net/ethernet/stmicro/stmmac/Makefile index bc965ac9e02..c8e8ea60ac1 100644 --- a/drivers/net/ethernet/stmicro/stmmac/Makefile +++ b/drivers/net/ethernet/stmicro/stmmac/Makefile @@ -1,5 +1,4 @@ obj-$(CONFIG_STMMAC_ETH) += stmmac.o -stmmac-$(CONFIG_STMMAC_TIMER) += stmmac_timer.o stmmac-$(CONFIG_STMMAC_RING) += ring_mode.o stmmac-$(CONFIG_STMMAC_CHAINED) += chain_mode.o stmmac-$(CONFIG_STMMAC_PLATFORM) += stmmac_platform.o diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 719be3912aa..186d1480612 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -48,6 +48,10 @@ #define CHIP_DBG(fmt, args...) do { } while (0) #endif +/* Synopsys Core versions */ +#define DWMAC_CORE_3_40 0x34 +#define DWMAC_CORE_3_50 0x35 + #undef FRAME_FILTER_DEBUG /* #define FRAME_FILTER_DEBUG */ @@ -81,7 +85,7 @@ struct stmmac_extra_stats { unsigned long rx_missed_cntr; unsigned long rx_overflow_cntr; unsigned long rx_vlan; - /* Tx/Rx IRQ errors */ + /* Tx/Rx IRQ error info */ unsigned long tx_undeflow_irq; unsigned long tx_process_stopped_irq; unsigned long tx_jabber_irq; @@ -91,18 +95,23 @@ struct stmmac_extra_stats { unsigned long rx_watchdog_irq; unsigned long tx_early_irq; unsigned long fatal_bus_error_irq; - /* Extra info */ + /* Tx/Rx IRQ Events */ + unsigned long rx_early_irq; unsigned long threshold; unsigned long tx_pkt_n; unsigned long rx_pkt_n; - unsigned long poll_n; - unsigned long sched_timer_n; unsigned long normal_irq_n; + unsigned long rx_normal_irq_n; + unsigned long napi_poll; + unsigned long tx_normal_irq_n; + unsigned long tx_clean; + unsigned long tx_reset_ic_bit; + unsigned long irq_receive_pmt_irq_n; + /* MMC info */ unsigned long mmc_tx_irq_n; unsigned long mmc_rx_irq_n; unsigned long mmc_rx_csum_offload_irq_n; /* EEE */ - unsigned long irq_receive_pmt_irq_n; unsigned long irq_tx_path_in_lpi_mode_n; unsigned long irq_tx_path_exit_lpi_mode_n; unsigned long irq_rx_path_in_lpi_mode_n; @@ -162,6 +171,15 @@ struct stmmac_extra_stats { #define DMA_HW_FEAT_ACTPHYIF 0x70000000 /* Active/selected PHY interface */ #define DEFAULT_DMA_PBL 8 +/* Max/Min RI Watchdog Timer count value */ +#define MAX_DMA_RIWT 0xff +#define MIN_DMA_RIWT 0x20 +/* Tx coalesce parameters */ +#define STMMAC_COAL_TX_TIMER 40000 +#define STMMAC_MAX_COAL_TX_TICK 100000 +#define STMMAC_TX_MAX_FRAMES 256 +#define STMMAC_TX_FRAMES 64 + enum rx_frame_status { /* IPC status */ good_frame = 0, discard_frame = 1, @@ -169,10 +187,11 @@ enum rx_frame_status { /* IPC status */ llc_snap = 4, }; -enum tx_dma_irq_status { - tx_hard_error = 1, - tx_hard_error_bump_tc = 2, - handle_tx_rx = 3, +enum dma_irq_status { + tx_hard_error = 0x1, + tx_hard_error_bump_tc = 0x2, + handle_rx = 0x4, + handle_tx = 0x8, }; enum core_specific_irq_mask { @@ -296,6 +315,8 @@ struct stmmac_dma_ops { struct stmmac_extra_stats *x); /* If supported then get the optional core features */ unsigned int (*get_hw_feature) (void __iomem *ioaddr); + /* Program the HW RX Watchdog */ + void (*rx_watchdog) (void __iomem *ioaddr, u32 riwt); }; struct stmmac_ops { diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h index 0e4cacedc1f..7ad56afd632 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000.h @@ -230,8 +230,5 @@ enum rtc_control { #define GMAC_MMC_TX_INTR 0x108 #define GMAC_MMC_RX_CSUM_OFFLOAD 0x208 -/* Synopsys Core versions */ -#define DWMAC_CORE_3_40 0x34 - extern const struct stmmac_dma_ops dwmac1000_dma_ops; #endif /* __DWMAC1000_H__ */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c index 033500090f5..bf83c03bfd0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac1000_dma.c @@ -174,6 +174,11 @@ static unsigned int dwmac1000_get_hw_feature(void __iomem *ioaddr) return readl(ioaddr + DMA_HW_FEATURE); } +static void dwmac1000_rx_watchdog(void __iomem *ioaddr, u32 riwt) +{ + writel(riwt, ioaddr + DMA_RX_WATCHDOG); +} + const struct stmmac_dma_ops dwmac1000_dma_ops = { .init = dwmac1000_dma_init, .dump_regs = dwmac1000_dump_dma_regs, @@ -187,4 +192,5 @@ const struct stmmac_dma_ops dwmac1000_dma_ops = { .stop_rx = dwmac_dma_stop_rx, .dma_interrupt = dwmac_dma_interrupt, .get_hw_feature = dwmac1000_get_hw_feature, + .rx_watchdog = dwmac1000_rx_watchdog, }; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h index e49c9a0fd6f..807f30371e4 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_dma.h @@ -35,7 +35,10 @@ #define DMA_CONTROL 0x00001018 /* Ctrl (Operational Mode) */ #define DMA_INTR_ENA 0x0000101c /* Interrupt Enable */ #define DMA_MISSED_FRAME_CTR 0x00001020 /* Missed Frame Counter */ -#define DMA_AXI_BUS_MODE 0x00001028 /* AXI Bus Mode */ +/* Rx watchdog register */ +#define DMA_RX_WATCHDOG 0x00001024 +/* AXI Bus Mode */ +#define DMA_AXI_BUS_MODE 0x00001028 #define DMA_CUR_TX_BUF_ADDR 0x00001050 /* Current Host Tx Buffer */ #define DMA_CUR_RX_BUF_ADDR 0x00001054 /* Current Host Rx Buffer */ #define DMA_HW_FEATURE 0x00001058 /* HW Feature Register */ diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c index 4e0e18a44fc..491d7e93060 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac_lib.c @@ -204,16 +204,28 @@ int dwmac_dma_interrupt(void __iomem *ioaddr, } } /* TX/RX NORMAL interrupts */ - if (intr_status & DMA_STATUS_NIS) { + if (likely(intr_status & DMA_STATUS_NIS)) { x->normal_irq_n++; - if (likely((intr_status & DMA_STATUS_RI) || - (intr_status & (DMA_STATUS_TI)))) - ret = handle_tx_rx; + if (likely(intr_status & DMA_STATUS_RI)) { + u32 value = readl(ioaddr + DMA_INTR_ENA); + /* to schedule NAPI on real RIE event. */ + if (likely(value & DMA_INTR_ENA_RIE)) { + x->rx_normal_irq_n++; + ret |= handle_rx; + } + } + if (likely(intr_status & DMA_STATUS_TI)) { + x->tx_normal_irq_n++; + ret |= handle_tx; + } + if (unlikely(intr_status & DMA_STATUS_ERI)) + x->rx_early_irq++; } /* Optional hardware blocks, interrupts should be disabled */ if (unlikely(intr_status & (DMA_STATUS_GPI | DMA_STATUS_GMI | DMA_STATUS_GLI))) pr_info("%s: unexpected status %08x\n", __func__, intr_status); + /* Clear the interrupt by writing a logic 1 to the CSR5[15-0] */ writel((intr_status & 0x1ffff), ioaddr + DMA_STATUS); diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 7d51a65ab09..023a4fb4efa 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -24,16 +24,13 @@ #define __STMMAC_H__ #define STMMAC_RESOURCE_NAME "stmmaceth" -#define DRV_MODULE_VERSION "March_2012" +#define DRV_MODULE_VERSION "Nov_2012" #include <linux/clk.h> #include <linux/stmmac.h> #include <linux/phy.h> #include <linux/pci.h> #include "common.h" -#ifdef CONFIG_STMMAC_TIMER -#include "stmmac_timer.h" -#endif struct stmmac_priv { /* Frequently used values are kept adjacent for cache effect */ @@ -77,9 +74,6 @@ struct stmmac_priv { spinlock_t tx_lock; int wolopts; int wol_irq; -#ifdef CONFIG_STMMAC_TIMER - struct stmmac_timer *tm; -#endif struct plat_stmmacenet_data *plat; struct stmmac_counters mmc; struct dma_features dma_cap; @@ -93,6 +87,12 @@ struct stmmac_priv { int eee_enabled; int eee_active; int tx_lpi_timer; + struct timer_list txtimer; + u32 tx_count_frames; + u32 tx_coal_frames; + u32 tx_coal_timer; + int use_riwt; + u32 rx_riwt; }; extern int phyaddr; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 76fd61aa005..1372ce210b5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -76,7 +76,7 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(rx_missed_cntr), STMMAC_STAT(rx_overflow_cntr), STMMAC_STAT(rx_vlan), - /* Tx/Rx IRQ errors */ + /* Tx/Rx IRQ error info */ STMMAC_STAT(tx_undeflow_irq), STMMAC_STAT(tx_process_stopped_irq), STMMAC_STAT(tx_jabber_irq), @@ -86,18 +86,23 @@ static const struct stmmac_stats stmmac_gstrings_stats[] = { STMMAC_STAT(rx_watchdog_irq), STMMAC_STAT(tx_early_irq), STMMAC_STAT(fatal_bus_error_irq), - /* Extra info */ + /* Tx/Rx IRQ Events */ + STMMAC_STAT(rx_early_irq), STMMAC_STAT(threshold), STMMAC_STAT(tx_pkt_n), STMMAC_STAT(rx_pkt_n), - STMMAC_STAT(poll_n), - STMMAC_STAT(sched_timer_n), - STMMAC_STAT(normal_irq_n), STMMAC_STAT(normal_irq_n), + STMMAC_STAT(rx_normal_irq_n), + STMMAC_STAT(napi_poll), + STMMAC_STAT(tx_normal_irq_n), + STMMAC_STAT(tx_clean), + STMMAC_STAT(tx_reset_ic_bit), + STMMAC_STAT(irq_receive_pmt_irq_n), + /* MMC info */ STMMAC_STAT(mmc_tx_irq_n), STMMAC_STAT(mmc_rx_irq_n), STMMAC_STAT(mmc_rx_csum_offload_irq_n), - STMMAC_STAT(irq_receive_pmt_irq_n), + /* EEE */ STMMAC_STAT(irq_tx_path_in_lpi_mode_n), STMMAC_STAT(irq_tx_path_exit_lpi_mode_n), STMMAC_STAT(irq_rx_path_in_lpi_mode_n), @@ -519,6 +524,87 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, return phy_ethtool_set_eee(priv->phydev, edata); } +static u32 stmmac_usec2riwt(u32 usec, struct stmmac_priv *priv) +{ + unsigned long clk = clk_get_rate(priv->stmmac_clk); + + if (!clk) + return 0; + + return (usec * (clk / 1000000)) / 256; +} + +static u32 stmmac_riwt2usec(u32 riwt, struct stmmac_priv *priv) +{ + unsigned long clk = clk_get_rate(priv->stmmac_clk); + + if (!clk) + return 0; + + return (riwt * 256) / (clk / 1000000); +} + +static int stmmac_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec) +{ + struct stmmac_priv *priv = netdev_priv(dev); + + ec->tx_coalesce_usecs = priv->tx_coal_timer; + ec->tx_max_coalesced_frames = priv->tx_coal_frames; + + if (priv->use_riwt) + ec->rx_coalesce_usecs = stmmac_riwt2usec(priv->rx_riwt, priv); + + return 0; +} + +static int stmmac_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec) +{ + struct stmmac_priv *priv = netdev_priv(dev); + unsigned int rx_riwt; + + /* Check not supported parameters */ + if ((ec->rx_max_coalesced_frames) || (ec->rx_coalesce_usecs_irq) || + (ec->rx_max_coalesced_frames_irq) || (ec->tx_coalesce_usecs_irq) || + (ec->use_adaptive_rx_coalesce) || (ec->use_adaptive_tx_coalesce) || + (ec->pkt_rate_low) || (ec->rx_coalesce_usecs_low) || + (ec->rx_max_coalesced_frames_low) || (ec->tx_coalesce_usecs_high) || + (ec->tx_max_coalesced_frames_low) || (ec->pkt_rate_high) || + (ec->tx_coalesce_usecs_low) || (ec->rx_coalesce_usecs_high) || + (ec->rx_max_coalesced_frames_high) || + (ec->tx_max_coalesced_frames_irq) || + (ec->stats_block_coalesce_usecs) || + (ec->tx_max_coalesced_frames_high) || (ec->rate_sample_interval)) + return -EOPNOTSUPP; + + if (ec->rx_coalesce_usecs == 0) + return -EINVAL; + + if ((ec->tx_coalesce_usecs == 0) && + (ec->tx_max_coalesced_frames == 0)) + return -EINVAL; + + if ((ec->tx_coalesce_usecs > STMMAC_COAL_TX_TIMER) || + (ec->tx_max_coalesced_frames > STMMAC_TX_MAX_FRAMES)) + return -EINVAL; + + rx_riwt = stmmac_usec2riwt(ec->rx_coalesce_usecs, priv); + + if ((rx_riwt > MAX_DMA_RIWT) || (rx_riwt < MIN_DMA_RIWT)) + return -EINVAL; + else if (!priv->use_riwt) + return -EOPNOTSUPP; + + /* Only copy relevant parameters, ignore all others. */ + priv->tx_coal_frames = ec->tx_max_coalesced_frames; + priv->tx_coal_timer = ec->tx_coalesce_usecs; + priv->rx_riwt = rx_riwt; + priv->hw->dma->rx_watchdog(priv->ioaddr, priv->rx_riwt); + + return 0; +} + static const struct ethtool_ops stmmac_ethtool_ops = { .begin = stmmac_check_if_running, .get_drvinfo = stmmac_ethtool_getdrvinfo, @@ -539,6 +625,8 @@ static const struct ethtool_ops stmmac_ethtool_ops = { .set_eee = stmmac_ethtool_op_set_eee, .get_sset_count = stmmac_get_sset_count, .get_ts_info = ethtool_op_get_ts_info, + .get_coalesce = stmmac_get_coalesce, + .set_coalesce = stmmac_set_coalesce, }; void stmmac_set_ethtool_ops(struct net_device *netdev) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c6cdbc4eb05..542edbcd92c 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -115,16 +115,6 @@ static int tc = TC_DEFAULT; module_param(tc, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(tc, "DMA threshold control value"); -/* Pay attention to tune this parameter; take care of both - * hardware capability and network stabitily/performance impact. - * Many tests showed that ~4ms latency seems to be good enough. */ -#ifdef CONFIG_STMMAC_TIMER -#define DEFAULT_PERIODIC_RATE 256 -static int tmrate = DEFAULT_PERIODIC_RATE; -module_param(tmrate, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(tmrate, "External timer freq. (default: 256Hz)"); -#endif - #define DMA_BUFFER_SIZE BUF_SIZE_2KiB static int buf_sz = DMA_BUFFER_SIZE; module_param(buf_sz, int, S_IRUGO | S_IWUSR); @@ -147,6 +137,8 @@ static int stmmac_init_fs(struct net_device *dev); static void stmmac_exit_fs(void); #endif +#define STMMAC_COAL_TIMER(x) (jiffies + usecs_to_jiffies(x)) + /** * stmmac_verify_args - verify the driver parameters. * Description: it verifies if some wrong parameter is passed to the driver. @@ -536,12 +528,6 @@ static void init_dma_desc_rings(struct net_device *dev) else bfsize = stmmac_set_bfsize(dev->mtu, priv->dma_buf_sz); -#ifdef CONFIG_STMMAC_TIMER - /* Disable interrupts on completion for the reception if timer is on */ - if (likely(priv->tm->enable)) - dis_ic = 1; -#endif - DBG(probe, INFO, "stmmac: txsize %d, rxsize %d, bfsize %d\n", txsize, rxsize, bfsize); @@ -617,6 +603,8 @@ static void init_dma_desc_rings(struct net_device *dev) priv->dirty_tx = 0; priv->cur_tx = 0; + if (priv->use_riwt) + dis_ic = 1; /* Clear the Rx/Tx descriptors */ priv->hw->desc->init_rx_desc(priv->dma_rx, rxsize, dis_ic); priv->hw->desc->init_tx_desc(priv->dma_tx, txsize); @@ -704,16 +692,18 @@ static void stmmac_dma_operation_mode(struct stmmac_priv *priv) } /** - * stmmac_tx: - * @priv: private driver structure + * stmmac_tx_clean: + * @priv: private data pointer * Description: it reclaims resources after transmission completes. */ -static void stmmac_tx(struct stmmac_priv *priv) +static void stmmac_tx_clean(struct stmmac_priv *priv) { unsigned int txsize = priv->dma_tx_size; spin_lock(&priv->tx_lock); + priv->xstats.tx_clean++; + while (priv->dirty_tx != priv->cur_tx) { int last; unsigned int entry = priv->dirty_tx % txsize; @@ -773,69 +763,16 @@ static void stmmac_tx(struct stmmac_priv *priv) spin_unlock(&priv->tx_lock); } -static inline void stmmac_enable_irq(struct stmmac_priv *priv) -{ -#ifdef CONFIG_STMMAC_TIMER - if (likely(priv->tm->enable)) - priv->tm->timer_start(tmrate); - else -#endif - priv->hw->dma->enable_dma_irq(priv->ioaddr); -} - -static inline void stmmac_disable_irq(struct stmmac_priv *priv) -{ -#ifdef CONFIG_STMMAC_TIMER - if (likely(priv->tm->enable)) - priv->tm->timer_stop(); - else -#endif - priv->hw->dma->disable_dma_irq(priv->ioaddr); -} - -static int stmmac_has_work(struct stmmac_priv *priv) -{ - unsigned int has_work = 0; - int rxret, tx_work = 0; - - rxret = priv->hw->desc->get_rx_owner(priv->dma_rx + - (priv->cur_rx % priv->dma_rx_size)); - - if (priv->dirty_tx != priv->cur_tx) - tx_work = 1; - - if (likely(!rxret || tx_work)) - has_work = 1; - - return has_work; -} - -static inline void _stmmac_schedule(struct stmmac_priv *priv) +static inline void stmmac_enable_dma_irq(struct stmmac_priv *priv) { - if (likely(stmmac_has_work(priv))) { - stmmac_disable_irq(priv); - napi_schedule(&priv->napi); - } + priv->hw->dma->enable_dma_irq(priv->ioaddr); } -#ifdef CONFIG_STMMAC_TIMER -void stmmac_schedule(struct net_device *dev) +static inline void stmmac_disable_dma_irq(struct stmmac_priv *priv) { - struct stmmac_priv *priv = netdev_priv(dev); - - priv->xstats.sched_timer_n++; - - _stmmac_schedule(priv); + priv->hw->dma->disable_dma_irq(priv->ioaddr); } -static void stmmac_no_timer_started(unsigned int x) -{; -}; - -static void stmmac_no_timer_stopped(void) -{; -}; -#endif /** * stmmac_tx_err: @@ -858,16 +795,18 @@ static void stmmac_tx_err(struct stmmac_priv *priv) netif_wake_queue(priv->dev); } - static void stmmac_dma_interrupt(struct stmmac_priv *priv) { int status; status = priv->hw->dma->dma_interrupt(priv->ioaddr, &priv->xstats); - if (likely(status == handle_tx_rx)) - _stmmac_schedule(priv); - - else if (unlikely(status == tx_hard_error_bump_tc)) { + if (likely((status & handle_rx)) || (status & handle_tx)) { + if (likely(napi_schedule_prep(&priv->napi))) { + stmmac_disable_dma_irq(priv); + __napi_schedule(&priv->napi); + } + } + if (unlikely(status & tx_hard_error_bump_tc)) { /* Try to bump up the dma threshold on this failure */ if (unlikely(tc != SF_DMA_MODE) && (tc <= 256)) { tc += 64; @@ -983,7 +922,6 @@ static int stmmac_get_hw_features(struct stmmac_priv *priv) /* Alternate (enhanced) DESC mode*/ priv->dma_cap.enh_desc = (hw_cap & DMA_HW_FEAT_ENHDESSEL) >> 24; - } return hw_cap; @@ -1025,6 +963,38 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) } /** + * stmmac_tx_timer: + * @data: data pointer + * Description: + * This is the timer handler to directly invoke the stmmac_tx_clean. + */ +static void stmmac_tx_timer(unsigned long data) +{ + struct stmmac_priv *priv = (struct stmmac_priv *)data; + + stmmac_tx_clean(priv); +} + +/** + * stmmac_tx_timer: + * @priv: private data structure + * Description: + * This inits the transmit coalesce parameters: i.e. timer rate, + * timer handler and default threshold used for enabling the + * interrupt on completion bit. + */ +static void stmmac_init_tx_coalesce(struct stmmac_priv *priv) +{ + priv->tx_coal_frames = STMMAC_TX_FRAMES; + priv->tx_coal_timer = STMMAC_COAL_TX_TIMER; + init_timer(&priv->txtimer); + priv->txtimer.expires = STMMAC_COAL_TIMER(priv->tx_coal_timer); + priv->txtimer.data = (unsigned long)priv; + priv->txtimer.function = stmmac_tx_timer; + add_timer(&priv->txtimer); +} + +/** * stmmac_open - open entry point of the driver * @dev : pointer to the device structure. * Description: @@ -1038,23 +1008,6 @@ static int stmmac_open(struct net_device *dev) struct stmmac_priv *priv = netdev_priv(dev); int ret; -#ifdef CONFIG_STMMAC_TIMER - priv->tm = kzalloc(sizeof(struct stmmac_timer *), GFP_KERNEL); - if (unlikely(priv->tm == NULL)) - return -ENOMEM; - - priv->tm->freq = tmrate; - - /* Test if the external timer can be actually used. - * In case of failure continue without timer. */ - if (unlikely((stmmac_open_ext_timer(dev, priv->tm)) < 0)) { - pr_warning("stmmaceth: cannot attach the external timer.\n"); - priv->tm->freq = 0; - priv->tm->timer_start = stmmac_no_timer_started; - priv->tm->timer_stop = stmmac_no_timer_stopped; - } else - priv->tm->enable = 1; -#endif clk_prepare_enable(priv->stmmac_clk); stmmac_check_ether_addr(priv); @@ -1141,10 +1094,6 @@ static int stmmac_open(struct net_device *dev) priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); -#ifdef CONFIG_STMMAC_TIMER - priv->tm->timer_start(tmrate); -#endif - /* Dump DMA/MAC registers */ if (netif_msg_hw(priv)) { priv->hw->mac->dump_regs(priv->ioaddr); @@ -1157,6 +1106,13 @@ static int stmmac_open(struct net_device *dev) priv->tx_lpi_timer = STMMAC_DEFAULT_TWT_LS_TIMER; priv->eee_enabled = stmmac_eee_init(priv); + stmmac_init_tx_coalesce(priv); + + if ((priv->use_riwt) && (priv->hw->dma->rx_watchdog)) { + priv->rx_riwt = MAX_DMA_RIWT; + priv->hw->dma->rx_watchdog(priv->ioaddr, MAX_DMA_RIWT); + } + napi_enable(&priv->napi); netif_start_queue(dev); @@ -1170,9 +1126,6 @@ open_error_wolirq: free_irq(dev->irq, dev); open_error: -#ifdef CONFIG_STMMAC_TIMER - kfree(priv->tm); -#endif if (priv->phydev) phy_disconnect(priv->phydev); @@ -1203,14 +1156,10 @@ static int stmmac_release(struct net_device *dev) netif_stop_queue(dev); -#ifdef CONFIG_STMMAC_TIMER - /* Stop and release the timer */ - stmmac_close_ext_timer(); - if (priv->tm != NULL) - kfree(priv->tm); -#endif napi_disable(&priv->napi); + del_timer_sync(&priv->txtimer); + /* Free the IRQ lines */ free_irq(dev->irq, dev); if (priv->wol_irq != dev->irq) @@ -1273,11 +1222,13 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef STMMAC_XMIT_DEBUG if ((skb->len > ETH_FRAME_LEN) || nfrags) - pr_info("stmmac xmit:\n" - "\tskb addr %p - len: %d - nopaged_len: %d\n" - "\tn_frags: %d - ip_summed: %d - %s gso\n", - skb, skb->len, nopaged_len, nfrags, skb->ip_summed, - !skb_is_gso(skb) ? "isn't" : "is"); + pr_debug("stmmac xmit: [entry %d]\n" + "\tskb addr %p - len: %d - nopaged_len: %d\n" + "\tn_frags: %d - ip_summed: %d - %s gso\n" + "\ttx_count_frames %d\n", entry, + skb, skb->len, nopaged_len, nfrags, skb->ip_summed, + !skb_is_gso(skb) ? "isn't" : "is", + priv->tx_count_frames); #endif csum_insertion = (skb->ip_summed == CHECKSUM_PARTIAL); @@ -1287,9 +1238,9 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef STMMAC_XMIT_DEBUG if ((nfrags > 0) || (skb->len > ETH_FRAME_LEN)) - pr_debug("stmmac xmit: skb len: %d, nopaged_len: %d,\n" - "\t\tn_frags: %d, ip_summed: %d\n", - skb->len, nopaged_len, nfrags, skb->ip_summed); + pr_debug("\tskb len: %d, nopaged_len: %d,\n" + "\t\tn_frags: %d, ip_summed: %d\n", + skb->len, nopaged_len, nfrags, skb->ip_summed); #endif priv->tx_skbuff[entry] = skb; @@ -1320,16 +1271,24 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev) wmb(); } - /* Interrupt on completition only for the latest segment */ + /* Finalize the latest segment. */ priv->hw->desc->close_tx_desc(desc); -#ifdef CONFIG_STMMAC_TIMER - /* Clean IC while using timer */ - if (likely(priv->tm->enable)) - priv->hw->desc->clear_tx_ic(desc); -#endif - wmb(); + /* According to the coalesce parameter the IC bit for the latest + * segment could be reset and the timer re-started to invoke the + * stmmac_tx function. This approach takes care about the fragments. + */ + priv->tx_count_frames += nfrags + 1; + if (priv->tx_coal_frames > priv->tx_count_frames) { + priv->hw->desc->clear_tx_ic(desc); + priv->xstats.tx_reset_ic_bit++; + TX_DBG("\t[entry %d]: tx_count_frames %d\n", entry, + priv->tx_count_frames); + mod_timer(&priv->txtimer, + STMMAC_COAL_TIMER(priv->tx_coal_timer)); + } else + priv->tx_count_frames = 0; /* To avoid raise condition */ priv->hw->desc->set_tx_owner(first); @@ -1471,14 +1430,12 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) #endif skb->protocol = eth_type_trans(skb, priv->dev); - if (unlikely(!priv->plat->rx_coe)) { - /* No RX COE for old mac10/100 devices */ + if (unlikely(!priv->plat->rx_coe)) skb_checksum_none_assert(skb); - netif_receive_skb(skb); - } else { + else skb->ip_summed = CHECKSUM_UNNECESSARY; - napi_gro_receive(&priv->napi, skb); - } + + napi_gro_receive(&priv->napi, skb); priv->dev->stats.rx_packets++; priv->dev->stats.rx_bytes += frame_len; @@ -1500,21 +1457,20 @@ static int stmmac_rx(struct stmmac_priv *priv, int limit) * @budget : maximum number of packets that the current CPU can receive from * all interfaces. * Description : - * This function implements the the reception process. - * Also it runs the TX completion thread + * To look at the incoming frames and clear the tx resources. */ static int stmmac_poll(struct napi_struct *napi, int budget) { struct stmmac_priv *priv = container_of(napi, struct stmmac_priv, napi); int work_done = 0; - priv->xstats.poll_n++; - stmmac_tx(priv); - work_done = stmmac_rx(priv, budget); + priv->xstats.napi_poll++; + stmmac_tx_clean(priv); + work_done = stmmac_rx(priv, budget); if (work_done < budget) { napi_complete(napi); - stmmac_enable_irq(priv); + stmmac_enable_dma_irq(priv); } return work_done; } @@ -1523,7 +1479,7 @@ static int stmmac_poll(struct napi_struct *napi, int budget) * stmmac_tx_timeout * @dev : Pointer to net device structure * Description: this function is called when a packet transmission fails to - * complete within a reasonable tmrate. The driver will mark the error in the + * complete within a reasonable time. The driver will mark the error in the * netdev structure and arrange for the device to be reset to a sane state * in order to transmit a new packet. */ @@ -2050,6 +2006,16 @@ struct stmmac_priv *stmmac_dvr_probe(struct device *device, if (flow_ctrl) priv->flow_ctrl = FLOW_AUTO; /* RX/TX pause on */ + /* Rx Watchdog is available in the COREs newer than the 3.40. + * In some case, for example on bugged HW this feature + * has to be disable and this can be done by passing the + * riwt_off field from the platform. + */ + if ((priv->synopsys_id >= DWMAC_CORE_3_50) && (!priv->plat->riwt_off)) { + priv->use_riwt = 1; + pr_info(" Enable RX Mitigation via HW Watchdog Timer\n"); + } + netif_napi_add(ndev, &priv->napi, stmmac_poll, 64); spin_lock_init(&priv->lock); @@ -2141,11 +2107,9 @@ int stmmac_suspend(struct net_device *ndev) netif_device_detach(ndev); netif_stop_queue(ndev); -#ifdef CONFIG_STMMAC_TIMER - priv->tm->timer_stop(); - if (likely(priv->tm->enable)) + if (priv->use_riwt) dis_ic = 1; -#endif + napi_disable(&priv->napi); /* Stop TX/RX DMA */ @@ -2196,10 +2160,6 @@ int stmmac_resume(struct net_device *ndev) priv->hw->dma->start_tx(priv->ioaddr); priv->hw->dma->start_rx(priv->ioaddr); -#ifdef CONFIG_STMMAC_TIMER - if (likely(priv->tm->enable)) - priv->tm->timer_start(tmrate); -#endif napi_enable(&priv->napi); netif_start_queue(ndev); @@ -2295,11 +2255,6 @@ static int __init stmmac_cmdline_opt(char *str) } else if (!strncmp(opt, "eee_timer:", 6)) { if (kstrtoint(opt + 10, 0, &eee_timer)) goto err; -#ifdef CONFIG_STMMAC_TIMER - } else if (!strncmp(opt, "tmrate:", 7)) { - if (kstrtoint(opt + 7, 0, &tmrate)) - goto err; -#endif } } return 0; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c deleted file mode 100644 index 4ccd4e2977b..00000000000 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************* - STMMAC external timer support. - - Copyright (C) 2007-2009 STMicroelectronics Ltd - - This program is free software; you can redistribute it and/or modify it - under the terms and conditions of the GNU General Public License, - version 2, as published by the Free Software Foundation. - - This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. - - The full GNU General Public License is included in this distribution in - the file called "COPYING". - - Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> -*******************************************************************************/ - -#include <linux/kernel.h> -#include <linux/etherdevice.h> -#include "stmmac_timer.h" - -static void stmmac_timer_handler(void *data) -{ - struct net_device *dev = (struct net_device *)data; - - stmmac_schedule(dev); -} - -#define STMMAC_TIMER_MSG(timer, freq) \ -printk(KERN_INFO "stmmac_timer: %s Timer ON (freq %dHz)\n", timer, freq); - -#if defined(CONFIG_STMMAC_RTC_TIMER) -#include <linux/rtc.h> -static struct rtc_device *stmmac_rtc; -static rtc_task_t stmmac_task; - -static void stmmac_rtc_start(unsigned int new_freq) -{ - rtc_irq_set_freq(stmmac_rtc, &stmmac_task, new_freq); - rtc_irq_set_state(stmmac_rtc, &stmmac_task, 1); -} - -static void stmmac_rtc_stop(void) -{ - rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0); -} - -int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm) -{ - stmmac_task.private_data = dev; - stmmac_task.func = stmmac_timer_handler; - - stmmac_rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE); - if (stmmac_rtc == NULL) { - pr_err("open rtc device failed\n"); - return -ENODEV; - } - - rtc_irq_register(stmmac_rtc, &stmmac_task); - - /* Periodic mode is not supported */ - if ((rtc_irq_set_freq(stmmac_rtc, &stmmac_task, tm->freq) < 0)) { - pr_err("set periodic failed\n"); - rtc_irq_unregister(stmmac_rtc, &stmmac_task); - rtc_class_close(stmmac_rtc); - return -1; - } - - STMMAC_TIMER_MSG(CONFIG_RTC_HCTOSYS_DEVICE, tm->freq); - - tm->timer_start = stmmac_rtc_start; - tm->timer_stop = stmmac_rtc_stop; - - return 0; -} - -int stmmac_close_ext_timer(void) -{ - rtc_irq_set_state(stmmac_rtc, &stmmac_task, 0); - rtc_irq_unregister(stmmac_rtc, &stmmac_task); - rtc_class_close(stmmac_rtc); - return 0; -} - -#elif defined(CONFIG_STMMAC_TMU_TIMER) -#include <linux/clk.h> -#define TMU_CHANNEL "tmu2_clk" -static struct clk *timer_clock; - -static void stmmac_tmu_start(unsigned int new_freq) -{ - clk_set_rate(timer_clock, new_freq); - clk_prepare_enable(timer_clock); -} - -static void stmmac_tmu_stop(void) -{ - clk_disable_unprepare(timer_clock); -} - -int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm) -{ - timer_clock = clk_get(NULL, TMU_CHANNEL); - - if (IS_ERR(timer_clock)) - return -1; - - if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) { - timer_clock = NULL; - return -1; - } - - STMMAC_TIMER_MSG("TMU2", tm->freq); - tm->timer_start = stmmac_tmu_start; - tm->timer_stop = stmmac_tmu_stop; - - return 0; -} - -int stmmac_close_ext_timer(void) -{ - clk_disable_unprepare(timer_clock); - tmu2_unregister_user(); - clk_put(timer_clock); - return 0; -} -#endif diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h deleted file mode 100644 index aea9b14cdfb..00000000000 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - STMMAC external timer Header File. - - Copyright (C) 2007-2009 STMicroelectronics Ltd - - This program is free software; you can redistribute it and/or modify it - under the terms and conditions of the GNU General Public License, - version 2, as published by the Free Software Foundation. - - This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. - - The full GNU General Public License is included in this distribution in - the file called "COPYING". - - Author: Giuseppe Cavallaro <peppe.cavallaro@st.com> -*******************************************************************************/ -#ifndef __STMMAC_TIMER_H__ -#define __STMMAC_TIMER_H__ - -struct stmmac_timer { - void (*timer_start) (unsigned int new_freq); - void (*timer_stop) (void); - unsigned int freq; - unsigned int enable; -}; - -/* Open the HW timer device and return 0 in case of success */ -int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm); -/* Stop the timer and release it */ -int stmmac_close_ext_timer(void); -/* Function used for scheduling task within the stmmac */ -void stmmac_schedule(struct net_device *dev); - -#if defined(CONFIG_STMMAC_TMU_TIMER) -extern int tmu2_register_user(void *fnt, void *data); -extern void tmu2_unregister_user(void); -#endif - -#endif /* __STMMAC_TIMER_H__ */ diff --git a/drivers/net/ethernet/ti/Kconfig b/drivers/net/ethernet/ti/Kconfig index 48fcb5e3bd3..4426151d4ac 100644 --- a/drivers/net/ethernet/ti/Kconfig +++ b/drivers/net/ethernet/ti/Kconfig @@ -62,6 +62,7 @@ config TI_CPSW config TI_CPTS boolean "TI Common Platform Time Sync (CPTS) Support" + depends on TI_CPSW select PTP_1588_CLOCK ---help--- This driver supports the Common Platform Time Sync unit of diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index 02c24777e23..c9714e1c164 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -812,8 +812,9 @@ static void cpsw_hwtstamp_v2(struct cpsw_priv *priv) __raw_writel(ETH_P_1588, &priv->regs->ts_ltype); } -static int cpsw_hwtstamp_ioctl(struct cpsw_priv *priv, struct ifreq *ifr) +static int cpsw_hwtstamp_ioctl(struct net_device *dev, struct ifreq *ifr) { + struct cpsw_priv *priv = netdev_priv(dev); struct cpts *cpts = &priv->cpts; struct hwtstamp_config cfg; @@ -878,14 +879,12 @@ static int cpsw_hwtstamp_ioctl(struct cpsw_priv *priv, struct ifreq *ifr) static int cpsw_ndo_ioctl(struct net_device *dev, struct ifreq *req, int cmd) { - struct cpsw_priv *priv = netdev_priv(dev); - if (!netif_running(dev)) return -EINVAL; #ifdef CONFIG_TI_CPTS if (cmd == SIOCSHWTSTAMP) - return cpsw_hwtstamp_ioctl(priv, req); + return cpsw_hwtstamp_ioctl(dev, req); #endif return -ENOTSUPP; } diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 7fdeb528133..2b657d4d63a 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -605,8 +605,11 @@ int rndis_filter_set_device_mac(struct hv_device *hdev, char *mac) return -EBUSY; } else { set_complete = &request->response_msg.msg.set_complete; - if (set_complete->status != RNDIS_STATUS_SUCCESS) + if (set_complete->status != RNDIS_STATUS_SUCCESS) { + netdev_err(ndev, "Fail to set MAC on host side:0x%x\n", + set_complete->status); ret = -EINVAL; + } } cleanup: diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index d44cca32758..ad86660fb8f 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1794,10 +1794,12 @@ static void team_setup(struct net_device *dev) dev->features |= NETIF_F_LLTX; dev->features |= NETIF_F_GRO; - dev->hw_features = NETIF_F_HW_VLAN_TX | + dev->hw_features = TEAM_VLAN_FEATURES | + NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; + dev->hw_features &= ~(NETIF_F_ALL_CSUM & ~NETIF_F_HW_CSUM); dev->features |= dev->hw_features; } diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3bd9932a8cd..607a3a5981c 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -121,11 +121,11 @@ struct tap_filter { * also contains all socket related strctures (except sock_fprog and tap_filter) * to serve as one transmit queue for tuntap device. The sock_fprog and * tap_filter were kept in tun_struct since they were used for filtering for the - * netdevice not for a specific queue (at least I didn't see the reqirement for + * netdevice not for a specific queue (at least I didn't see the requirement for * this). * * RCU usage: - * The tun_file and tun_struct are loosely coupled, the pointer from on to the + * The tun_file and tun_struct are loosely coupled, the pointer from one to the * other can only be read while rcu_read_lock or rtnl_lock is held. */ struct tun_file { @@ -153,7 +153,7 @@ struct tun_flow_entry { #define TUN_NUM_FLOW_ENTRIES 1024 /* Since the socket were moved to tun_file, to preserve the behavior of persist - * device, socket fileter, sndbuf and vnet header size were restore when the + * device, socket filter, sndbuf and vnet header size were restore when the * file were attached to a persist device. */ struct tun_struct { @@ -689,7 +689,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) sk_filter(tfile->socket.sk, skb)) goto drop; - /* Limit the number of packets queued by divining txq length with the + /* Limit the number of packets queued by dividing txq length with the * number of queues. */ if (skb_queue_len(&tfile->socket.sk->sk_receive_queue) @@ -1296,7 +1296,7 @@ static ssize_t tun_do_read(struct tun_struct *tun, struct tun_file *tfile, struct sk_buff *skb; ssize_t ret = 0; - tun_debug(KERN_INFO, tun, "tun_chr_read\n"); + tun_debug(KERN_INFO, tun, "tun_do_read\n"); if (unlikely(!noblock)) add_wait_queue(&tfile->wq.wait, &wait); diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 3b566fa0f8e..1ea91f4237f 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -385,6 +385,7 @@ static const struct usb_device_id products[] = { }, /* 3. Combined interface devices matching on interface number */ + {QMI_FIXED_INTF(0x12d1, 0x140c, 1)}, /* Huawei E173 */ {QMI_FIXED_INTF(0x19d2, 0x0002, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0012, 1)}, {QMI_FIXED_INTF(0x19d2, 0x0017, 3)}, diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c index 953c4f44d50..1cbd936bb02 100644 --- a/drivers/net/usb/smsc75xx.c +++ b/drivers/net/usb/smsc75xx.c @@ -54,17 +54,15 @@ #define USB_PRODUCT_ID_LAN7500 (0x7500) #define USB_PRODUCT_ID_LAN7505 (0x7505) #define RXW_PADDING 2 -#define SUPPORTED_WAKE (WAKE_UCAST | WAKE_BCAST | \ +#define SUPPORTED_WAKE (WAKE_PHY | WAKE_UCAST | WAKE_BCAST | \ WAKE_MCAST | WAKE_ARP | WAKE_MAGIC) -#define check_warn(ret, fmt, args...) \ - ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) - -#define check_warn_return(ret, fmt, args...) \ - ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); return ret; } }) - -#define check_warn_goto_done(ret, fmt, args...) \ - ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); goto done; } }) +#define SUSPEND_SUSPEND0 (0x01) +#define SUSPEND_SUSPEND1 (0x02) +#define SUSPEND_SUSPEND2 (0x04) +#define SUSPEND_SUSPEND3 (0x08) +#define SUSPEND_ALLMODES (SUSPEND_SUSPEND0 | SUSPEND_SUSPEND1 | \ + SUSPEND_SUSPEND2 | SUSPEND_SUSPEND3) struct smsc75xx_priv { struct usbnet *dev; @@ -74,6 +72,7 @@ struct smsc75xx_priv { struct mutex dataport_mutex; spinlock_t rfe_ctl_lock; struct work_struct set_multicast; + u8 suspend_flags; }; struct usb_context { @@ -163,37 +162,21 @@ static int __must_check smsc75xx_write_reg(struct usbnet *dev, u32 index, return __smsc75xx_write_reg(dev, index, data, 0); } -static int smsc75xx_set_feature(struct usbnet *dev, u32 feature) -{ - if (WARN_ON_ONCE(!dev)) - return -EINVAL; - - return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE, - USB_DIR_OUT | USB_RECIP_DEVICE, - feature, 0, NULL, 0); -} - -static int smsc75xx_clear_feature(struct usbnet *dev, u32 feature) -{ - if (WARN_ON_ONCE(!dev)) - return -EINVAL; - - return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE, - USB_DIR_OUT | USB_RECIP_DEVICE, - feature, 0, NULL, 0); -} - /* Loop until the read is completed with timeout * called with phy_mutex held */ -static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) +static __must_check int __smsc75xx_phy_wait_not_busy(struct usbnet *dev, + int in_pm) { unsigned long start_time = jiffies; u32 val; int ret; do { - ret = smsc75xx_read_reg(dev, MII_ACCESS, &val); - check_warn_return(ret, "Error reading MII_ACCESS\n"); + ret = __smsc75xx_read_reg(dev, MII_ACCESS, &val, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_ACCESS\n"); + return ret; + } if (!(val & MII_ACCESS_BUSY)) return 0; @@ -202,7 +185,8 @@ static int smsc75xx_phy_wait_not_busy(struct usbnet *dev) return -EIO; } -static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +static int __smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx, + int in_pm) { struct usbnet *dev = netdev_priv(netdev); u32 val, addr; @@ -211,8 +195,11 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) mutex_lock(&dev->phy_mutex); /* confirm MII not busy */ - ret = smsc75xx_phy_wait_not_busy(dev); - check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_read\n"); + ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_read\n"); + goto done; + } /* set the address, index & direction (read from PHY) */ phy_id &= dev->mii.phy_id_mask; @@ -220,14 +207,23 @@ static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) | MII_ACCESS_READ | MII_ACCESS_BUSY; - ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); - check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); + ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_ACCESS\n"); + goto done; + } - ret = smsc75xx_phy_wait_not_busy(dev); - check_warn_goto_done(ret, "Timed out reading MII reg %02X\n", idx); + ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); + goto done; + } - ret = smsc75xx_read_reg(dev, MII_DATA, &val); - check_warn_goto_done(ret, "Error reading MII_DATA\n"); + ret = __smsc75xx_read_reg(dev, MII_DATA, &val, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_DATA\n"); + goto done; + } ret = (u16)(val & 0xFFFF); @@ -236,8 +232,8 @@ done: return ret; } -static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, - int regval) +static void __smsc75xx_mdio_write(struct net_device *netdev, int phy_id, + int idx, int regval, int in_pm) { struct usbnet *dev = netdev_priv(netdev); u32 val, addr; @@ -246,12 +242,18 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, mutex_lock(&dev->phy_mutex); /* confirm MII not busy */ - ret = smsc75xx_phy_wait_not_busy(dev); - check_warn_goto_done(ret, "MII is busy in smsc75xx_mdio_write\n"); + ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "MII is busy in smsc75xx_mdio_write\n"); + goto done; + } val = regval; - ret = smsc75xx_write_reg(dev, MII_DATA, val); - check_warn_goto_done(ret, "Error writing MII_DATA\n"); + ret = __smsc75xx_write_reg(dev, MII_DATA, val, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_DATA\n"); + goto done; + } /* set the address, index & direction (write to PHY) */ phy_id &= dev->mii.phy_id_mask; @@ -259,16 +261,45 @@ static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, addr = ((phy_id << MII_ACCESS_PHY_ADDR_SHIFT) & MII_ACCESS_PHY_ADDR) | ((idx << MII_ACCESS_REG_ADDR_SHIFT) & MII_ACCESS_REG_ADDR) | MII_ACCESS_WRITE | MII_ACCESS_BUSY; - ret = smsc75xx_write_reg(dev, MII_ACCESS, addr); - check_warn_goto_done(ret, "Error writing MII_ACCESS\n"); + ret = __smsc75xx_write_reg(dev, MII_ACCESS, addr, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_ACCESS\n"); + goto done; + } - ret = smsc75xx_phy_wait_not_busy(dev); - check_warn_goto_done(ret, "Timed out writing MII reg %02X\n", idx); + ret = __smsc75xx_phy_wait_not_busy(dev, in_pm); + if (ret < 0) { + netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); + goto done; + } done: mutex_unlock(&dev->phy_mutex); } +static int smsc75xx_mdio_read_nopm(struct net_device *netdev, int phy_id, + int idx) +{ + return __smsc75xx_mdio_read(netdev, phy_id, idx, 1); +} + +static void smsc75xx_mdio_write_nopm(struct net_device *netdev, int phy_id, + int idx, int regval) +{ + __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 1); +} + +static int smsc75xx_mdio_read(struct net_device *netdev, int phy_id, int idx) +{ + return __smsc75xx_mdio_read(netdev, phy_id, idx, 0); +} + +static void smsc75xx_mdio_write(struct net_device *netdev, int phy_id, int idx, + int regval) +{ + __smsc75xx_mdio_write(netdev, phy_id, idx, regval, 0); +} + static int smsc75xx_wait_eeprom(struct usbnet *dev) { unsigned long start_time = jiffies; @@ -277,7 +308,10 @@ static int smsc75xx_wait_eeprom(struct usbnet *dev) do { ret = smsc75xx_read_reg(dev, E2P_CMD, &val); - check_warn_return(ret, "Error reading E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_CMD\n"); + return ret; + } if (!(val & E2P_CMD_BUSY) || (val & E2P_CMD_TIMEOUT)) break; @@ -300,7 +334,10 @@ static int smsc75xx_eeprom_confirm_not_busy(struct usbnet *dev) do { ret = smsc75xx_read_reg(dev, E2P_CMD, &val); - check_warn_return(ret, "Error reading E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_CMD\n"); + return ret; + } if (!(val & E2P_CMD_BUSY)) return 0; @@ -328,14 +365,20 @@ static int smsc75xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, for (i = 0; i < length; i++) { val = E2P_CMD_BUSY | E2P_CMD_READ | (offset & E2P_CMD_ADDR); ret = smsc75xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_CMD\n"); + return ret; + } ret = smsc75xx_wait_eeprom(dev); if (ret < 0) return ret; ret = smsc75xx_read_reg(dev, E2P_DATA, &val); - check_warn_return(ret, "Error reading E2P_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_DATA\n"); + return ret; + } data[i] = val & 0xFF; offset++; @@ -360,7 +403,10 @@ static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, /* Issue write/erase enable command */ val = E2P_CMD_BUSY | E2P_CMD_EWEN; ret = smsc75xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_CMD\n"); + return ret; + } ret = smsc75xx_wait_eeprom(dev); if (ret < 0) @@ -371,12 +417,18 @@ static int smsc75xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, /* Fill data register */ val = data[i]; ret = smsc75xx_write_reg(dev, E2P_DATA, val); - check_warn_return(ret, "Error writing E2P_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_DATA\n"); + return ret; + } /* Send "write" command */ val = E2P_CMD_BUSY | E2P_CMD_WRITE | (offset & E2P_CMD_ADDR); ret = smsc75xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_CMD\n"); + return ret; + } ret = smsc75xx_wait_eeprom(dev); if (ret < 0) @@ -395,7 +447,10 @@ static int smsc75xx_dataport_wait_not_busy(struct usbnet *dev) for (i = 0; i < 100; i++) { u32 dp_sel; ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); - check_warn_return(ret, "Error reading DP_SEL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading DP_SEL\n"); + return ret; + } if (dp_sel & DP_SEL_DPRDY) return 0; @@ -418,28 +473,49 @@ static int smsc75xx_dataport_write(struct usbnet *dev, u32 ram_select, u32 addr, mutex_lock(&pdata->dataport_mutex); ret = smsc75xx_dataport_wait_not_busy(dev); - check_warn_goto_done(ret, "smsc75xx_dataport_write busy on entry\n"); + if (ret < 0) { + netdev_warn(dev->net, "smsc75xx_dataport_write busy on entry\n"); + goto done; + } ret = smsc75xx_read_reg(dev, DP_SEL, &dp_sel); - check_warn_goto_done(ret, "Error reading DP_SEL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading DP_SEL\n"); + goto done; + } dp_sel &= ~DP_SEL_RSEL; dp_sel |= ram_select; ret = smsc75xx_write_reg(dev, DP_SEL, dp_sel); - check_warn_goto_done(ret, "Error writing DP_SEL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing DP_SEL\n"); + goto done; + } for (i = 0; i < length; i++) { ret = smsc75xx_write_reg(dev, DP_ADDR, addr + i); - check_warn_goto_done(ret, "Error writing DP_ADDR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing DP_ADDR\n"); + goto done; + } ret = smsc75xx_write_reg(dev, DP_DATA, buf[i]); - check_warn_goto_done(ret, "Error writing DP_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing DP_DATA\n"); + goto done; + } ret = smsc75xx_write_reg(dev, DP_CMD, DP_CMD_WRITE); - check_warn_goto_done(ret, "Error writing DP_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing DP_CMD\n"); + goto done; + } ret = smsc75xx_dataport_wait_not_busy(dev); - check_warn_goto_done(ret, "smsc75xx_dataport_write timeout\n"); + if (ret < 0) { + netdev_warn(dev->net, "smsc75xx_dataport_write timeout\n"); + goto done; + } } done: @@ -467,7 +543,8 @@ static void smsc75xx_deferred_multicast_write(struct work_struct *param) DP_SEL_VHF_HASH_LEN, pdata->multicast_hash_table); ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); - check_warn(ret, "Error writing RFE_CRL\n"); + if (ret < 0) + netdev_warn(dev->net, "Error writing RFE_CRL\n"); } static void smsc75xx_set_multicast(struct net_device *netdev) @@ -541,10 +618,16 @@ static int smsc75xx_update_flowcontrol(struct usbnet *dev, u8 duplex, } ret = smsc75xx_write_reg(dev, FLOW, flow); - check_warn_return(ret, "Error writing FLOW\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing FLOW\n"); + return ret; + } ret = smsc75xx_write_reg(dev, FCT_FLOW, fct_flow); - check_warn_return(ret, "Error writing FCT_FLOW\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing FCT_FLOW\n"); + return ret; + } return 0; } @@ -561,7 +644,10 @@ static int smsc75xx_link_reset(struct usbnet *dev) PHY_INT_SRC_CLEAR_ALL); ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); - check_warn_return(ret, "Error writing INT_STS\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing INT_STS\n"); + return ret; + } mii_check_media(mii, 1, 1); mii_ethtool_gset(&dev->mii, &ecmd); @@ -640,9 +726,15 @@ static int smsc75xx_ethtool_set_wol(struct net_device *net, { struct usbnet *dev = netdev_priv(net); struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + int ret; pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; - return 0; + + ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); + if (ret < 0) + netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); + + return ret; } static const struct ethtool_ops smsc75xx_ethtool_ops = { @@ -695,19 +787,29 @@ static int smsc75xx_set_mac_address(struct usbnet *dev) u32 addr_hi = dev->net->dev_addr[4] | dev->net->dev_addr[5] << 8; int ret = smsc75xx_write_reg(dev, RX_ADDRH, addr_hi); - check_warn_return(ret, "Failed to write RX_ADDRH: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write RX_ADDRH: %d\n", ret); + return ret; + } ret = smsc75xx_write_reg(dev, RX_ADDRL, addr_lo); - check_warn_return(ret, "Failed to write RX_ADDRL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write RX_ADDRL: %d\n", ret); + return ret; + } addr_hi |= ADDR_FILTX_FB_VALID; ret = smsc75xx_write_reg(dev, ADDR_FILTX, addr_hi); - check_warn_return(ret, "Failed to write ADDR_FILTX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write ADDR_FILTX: %d\n", ret); + return ret; + } ret = smsc75xx_write_reg(dev, ADDR_FILTX + 4, addr_lo); - check_warn_return(ret, "Failed to write ADDR_FILTX+4: %d\n", ret); + if (ret < 0) + netdev_warn(dev->net, "Failed to write ADDR_FILTX+4: %d\n", ret); - return 0; + return ret; } static int smsc75xx_phy_initialize(struct usbnet *dev) @@ -729,7 +831,10 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) do { msleep(10); bmcr = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, MII_BMCR); - check_warn_return(bmcr, "Error reading MII_BMCR\n"); + if (bmcr < 0) { + netdev_warn(dev->net, "Error reading MII_BMCR\n"); + return bmcr; + } timeout++; } while ((bmcr & BMCR_RESET) && (timeout < 100)); @@ -746,7 +851,11 @@ static int smsc75xx_phy_initialize(struct usbnet *dev) /* read and write to clear phy interrupt status */ ret = smsc75xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); - check_warn_return(ret, "Error reading PHY_INT_SRC\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); + return ret; + } + smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_SRC, 0xffff); smsc75xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, @@ -764,14 +873,20 @@ static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) bool rxenabled; ret = smsc75xx_read_reg(dev, MAC_RX, &buf); - check_warn_return(ret, "Failed to read MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); + return ret; + } rxenabled = ((buf & MAC_RX_RXEN) != 0); if (rxenabled) { buf &= ~MAC_RX_RXEN; ret = smsc75xx_write_reg(dev, MAC_RX, buf); - check_warn_return(ret, "Failed to write MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); + return ret; + } } /* add 4 to size for FCS */ @@ -779,12 +894,18 @@ static int smsc75xx_set_rx_max_frame_length(struct usbnet *dev, int size) buf |= (((size + 4) << MAC_RX_MAX_SIZE_SHIFT) & MAC_RX_MAX_SIZE); ret = smsc75xx_write_reg(dev, MAC_RX, buf); - check_warn_return(ret, "Failed to write MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); + return ret; + } if (rxenabled) { buf |= MAC_RX_RXEN; ret = smsc75xx_write_reg(dev, MAC_RX, buf); - check_warn_return(ret, "Failed to write MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); + return ret; + } } return 0; @@ -795,7 +916,10 @@ static int smsc75xx_change_mtu(struct net_device *netdev, int new_mtu) struct usbnet *dev = netdev_priv(netdev); int ret = smsc75xx_set_rx_max_frame_length(dev, new_mtu); - check_warn_return(ret, "Failed to set mac rx frame length\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to set mac rx frame length\n"); + return ret; + } return usbnet_change_mtu(netdev, new_mtu); } @@ -820,9 +944,10 @@ static int smsc75xx_set_features(struct net_device *netdev, /* it's racing here! */ ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); - check_warn_return(ret, "Error writing RFE_CTL\n"); + if (ret < 0) + netdev_warn(dev->net, "Error writing RFE_CTL\n"); - return 0; + return ret; } static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) @@ -835,7 +960,10 @@ static int smsc75xx_wait_ready(struct usbnet *dev, int in_pm) ret = __smsc75xx_read_reg(dev, PMT_CTL, &buf, in_pm); - check_warn_return(ret, "Failed to read PMT_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); + return ret; + } if (buf & PMT_CTL_DEV_RDY) return 0; @@ -857,21 +985,33 @@ static int smsc75xx_reset(struct usbnet *dev) netif_dbg(dev, ifup, dev->net, "entering smsc75xx_reset\n"); ret = smsc75xx_wait_ready(dev, 0); - check_warn_return(ret, "device not ready in smsc75xx_reset\n"); + if (ret < 0) { + netdev_warn(dev->net, "device not ready in smsc75xx_reset\n"); + return ret; + } ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } buf |= HW_CFG_LRST; ret = smsc75xx_write_reg(dev, HW_CFG, buf); - check_warn_return(ret, "Failed to write HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); + return ret; + } timeout = 0; do { msleep(10); ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } timeout++; } while ((buf & HW_CFG_LRST) && (timeout < 100)); @@ -883,18 +1023,27 @@ static int smsc75xx_reset(struct usbnet *dev) netif_dbg(dev, ifup, dev->net, "Lite reset complete, resetting PHY\n"); ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); - check_warn_return(ret, "Failed to read PMT_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); + return ret; + } buf |= PMT_CTL_PHY_RST; ret = smsc75xx_write_reg(dev, PMT_CTL, buf); - check_warn_return(ret, "Failed to write PMT_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write PMT_CTL: %d\n", ret); + return ret; + } timeout = 0; do { msleep(10); ret = smsc75xx_read_reg(dev, PMT_CTL, &buf); - check_warn_return(ret, "Failed to read PMT_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PMT_CTL: %d\n", ret); + return ret; + } timeout++; } while ((buf & PMT_CTL_PHY_RST) && (timeout < 100)); @@ -908,13 +1057,19 @@ static int smsc75xx_reset(struct usbnet *dev) smsc75xx_init_mac_address(dev); ret = smsc75xx_set_mac_address(dev); - check_warn_return(ret, "Failed to set mac address\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to set mac address\n"); + return ret; + } netif_dbg(dev, ifup, dev->net, "MAC Address: %pM\n", dev->net->dev_addr); ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", buf); @@ -922,10 +1077,16 @@ static int smsc75xx_reset(struct usbnet *dev) buf |= HW_CFG_BIR; ret = smsc75xx_write_reg(dev, HW_CFG, buf); - check_warn_return(ret, "Failed to write HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR: 0x%08x\n", buf); @@ -945,36 +1106,57 @@ static int smsc75xx_reset(struct usbnet *dev) (ulong)dev->rx_urb_size); ret = smsc75xx_write_reg(dev, BURST_CAP, buf); - check_warn_return(ret, "Failed to write BURST_CAP: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, BURST_CAP, &buf); - check_warn_return(ret, "Failed to read BURST_CAP: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from BURST_CAP after writing: 0x%08x\n", buf); ret = smsc75xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); - check_warn_return(ret, "Failed to write BULK_IN_DLY: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, BULK_IN_DLY, &buf); - check_warn_return(ret, "Failed to read BULK_IN_DLY: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from BULK_IN_DLY after writing: 0x%08x\n", buf); if (turbo_mode) { ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); buf |= (HW_CFG_MEF | HW_CFG_BCE); ret = smsc75xx_write_reg(dev, HW_CFG, buf); - check_warn_return(ret, "Failed to write HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, HW_CFG, &buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "HW_CFG: 0x%08x\n", buf); } @@ -982,58 +1164,92 @@ static int smsc75xx_reset(struct usbnet *dev) /* set FIFO sizes */ buf = (MAX_RX_FIFO_SIZE - 512) / 512; ret = smsc75xx_write_reg(dev, FCT_RX_FIFO_END, buf); - check_warn_return(ret, "Failed to write FCT_RX_FIFO_END: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FCT_RX_FIFO_END: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "FCT_RX_FIFO_END set to 0x%08x\n", buf); buf = (MAX_TX_FIFO_SIZE - 512) / 512; ret = smsc75xx_write_reg(dev, FCT_TX_FIFO_END, buf); - check_warn_return(ret, "Failed to write FCT_TX_FIFO_END: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FCT_TX_FIFO_END: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "FCT_TX_FIFO_END set to 0x%08x\n", buf); ret = smsc75xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL); - check_warn_return(ret, "Failed to write INT_STS: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, ID_REV, &buf); - check_warn_return(ret, "Failed to read ID_REV: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", buf); ret = smsc75xx_read_reg(dev, E2P_CMD, &buf); - check_warn_return(ret, "Failed to read E2P_CMD: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read E2P_CMD: %d\n", ret); + return ret; + } /* only set default GPIO/LED settings if no EEPROM is detected */ if (!(buf & E2P_CMD_LOADED)) { ret = smsc75xx_read_reg(dev, LED_GPIO_CFG, &buf); - check_warn_return(ret, "Failed to read LED_GPIO_CFG: %d\n", - ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read LED_GPIO_CFG: %d\n", ret); + return ret; + } buf &= ~(LED_GPIO_CFG_LED2_FUN_SEL | LED_GPIO_CFG_LED10_FUN_SEL); buf |= LED_GPIO_CFG_LEDGPIO_EN | LED_GPIO_CFG_LED2_FUN_SEL; ret = smsc75xx_write_reg(dev, LED_GPIO_CFG, buf); - check_warn_return(ret, "Failed to write LED_GPIO_CFG: %d\n", - ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret); + return ret; + } } ret = smsc75xx_write_reg(dev, FLOW, 0); - check_warn_return(ret, "Failed to write FLOW: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); + return ret; + } ret = smsc75xx_write_reg(dev, FCT_FLOW, 0); - check_warn_return(ret, "Failed to write FCT_FLOW: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FCT_FLOW: %d\n", ret); + return ret; + } /* Don't need rfe_ctl_lock during initialisation */ ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); - check_warn_return(ret, "Failed to read RFE_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); + return ret; + } pdata->rfe_ctl |= RFE_CTL_AB | RFE_CTL_DPF; ret = smsc75xx_write_reg(dev, RFE_CTL, pdata->rfe_ctl); - check_warn_return(ret, "Failed to write RFE_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write RFE_CTL: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, RFE_CTL, &pdata->rfe_ctl); - check_warn_return(ret, "Failed to read RFE_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read RFE_CTL: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "RFE_CTL set to 0x%08x\n", pdata->rfe_ctl); @@ -1044,65 +1260,107 @@ static int smsc75xx_reset(struct usbnet *dev) smsc75xx_set_multicast(dev->net); ret = smsc75xx_phy_initialize(dev); - check_warn_return(ret, "Failed to initialize PHY: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to initialize PHY: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, INT_EP_CTL, &buf); - check_warn_return(ret, "Failed to read INT_EP_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); + return ret; + } /* enable PHY interrupts */ buf |= INT_ENP_PHY_INT; ret = smsc75xx_write_reg(dev, INT_EP_CTL, buf); - check_warn_return(ret, "Failed to write INT_EP_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); + return ret; + } /* allow mac to detect speed and duplex from phy */ ret = smsc75xx_read_reg(dev, MAC_CR, &buf); - check_warn_return(ret, "Failed to read MAC_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); + return ret; + } buf |= (MAC_CR_ADD | MAC_CR_ASD); ret = smsc75xx_write_reg(dev, MAC_CR, buf); - check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); + return ret; + } ret = smsc75xx_read_reg(dev, MAC_TX, &buf); - check_warn_return(ret, "Failed to read MAC_TX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_TX: %d\n", ret); + return ret; + } buf |= MAC_TX_TXEN; ret = smsc75xx_write_reg(dev, MAC_TX, buf); - check_warn_return(ret, "Failed to write MAC_TX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_TX: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "MAC_TX set to 0x%08x\n", buf); ret = smsc75xx_read_reg(dev, FCT_TX_CTL, &buf); - check_warn_return(ret, "Failed to read FCT_TX_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read FCT_TX_CTL: %d\n", ret); + return ret; + } buf |= FCT_TX_CTL_EN; ret = smsc75xx_write_reg(dev, FCT_TX_CTL, buf); - check_warn_return(ret, "Failed to write FCT_TX_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FCT_TX_CTL: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "FCT_TX_CTL set to 0x%08x\n", buf); ret = smsc75xx_set_rx_max_frame_length(dev, 1514); - check_warn_return(ret, "Failed to set max rx frame length\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to set max rx frame length\n"); + return ret; + } ret = smsc75xx_read_reg(dev, MAC_RX, &buf); - check_warn_return(ret, "Failed to read MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); + return ret; + } buf |= MAC_RX_RXEN; ret = smsc75xx_write_reg(dev, MAC_RX, buf); - check_warn_return(ret, "Failed to write MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "MAC_RX set to 0x%08x\n", buf); ret = smsc75xx_read_reg(dev, FCT_RX_CTL, &buf); - check_warn_return(ret, "Failed to read FCT_RX_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read FCT_RX_CTL: %d\n", ret); + return ret; + } buf |= FCT_RX_CTL_EN; ret = smsc75xx_write_reg(dev, FCT_RX_CTL, buf); - check_warn_return(ret, "Failed to write FCT_RX_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FCT_RX_CTL: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "FCT_RX_CTL set to 0x%08x\n", buf); @@ -1131,7 +1389,10 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); ret = usbnet_get_endpoints(dev, intf); - check_warn_return(ret, "usbnet_get_endpoints failed: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); + return ret; + } dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc75xx_priv), GFP_KERNEL); @@ -1163,6 +1424,10 @@ static int smsc75xx_bind(struct usbnet *dev, struct usb_interface *intf) /* Init all registers */ ret = smsc75xx_reset(dev); + if (ret < 0) { + netdev_warn(dev->net, "smsc75xx_reset error %d\n", ret); + return ret; + } dev->net->netdev_ops = &smsc75xx_netdev_ops; dev->net->ethtool_ops = &smsc75xx_ethtool_ops; @@ -1196,65 +1461,359 @@ static int smsc75xx_write_wuff(struct usbnet *dev, int filter, u32 wuf_cfg, int ret; ret = smsc75xx_write_reg(dev, cfg_base, wuf_cfg); - check_warn_return(ret, "Error writing WUF_CFGX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_CFGX\n"); + return ret; + } ret = smsc75xx_write_reg(dev, mask_base, wuf_mask1); - check_warn_return(ret, "Error writing WUF_MASKX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_MASKX\n"); + return ret; + } ret = smsc75xx_write_reg(dev, mask_base + 4, 0); - check_warn_return(ret, "Error writing WUF_MASKX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_MASKX\n"); + return ret; + } ret = smsc75xx_write_reg(dev, mask_base + 8, 0); - check_warn_return(ret, "Error writing WUF_MASKX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_MASKX\n"); + return ret; + } ret = smsc75xx_write_reg(dev, mask_base + 12, 0); - check_warn_return(ret, "Error writing WUF_MASKX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_MASKX\n"); + return ret; + } return 0; } +static int smsc75xx_enter_suspend0(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 val; + int ret; + + ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } + + val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); + val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + pdata->suspend_flags |= SUSPEND_SUSPEND0; + + return 0; +} + +static int smsc75xx_enter_suspend1(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 val; + int ret; + + ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } + + val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); + val |= PMT_CTL_SUS_MODE_1; + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + /* clear wol status, enable energy detection */ + val &= ~PMT_CTL_WUPS; + val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + pdata->suspend_flags |= SUSPEND_SUSPEND1; + + return 0; +} + +static int smsc75xx_enter_suspend2(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 val; + int ret; + + ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } + + val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); + val |= PMT_CTL_SUS_MODE_2; + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + pdata->suspend_flags |= SUSPEND_SUSPEND2; + + return 0; +} + +static int smsc75xx_enter_suspend3(struct usbnet *dev) +{ + struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 val; + int ret; + + ret = smsc75xx_read_reg_nopm(dev, FCT_RX_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading FCT_RX_CTL\n"); + return ret; + } + + if (val & FCT_RX_CTL_RXUSED) { + netdev_dbg(dev->net, "rx fifo not empty in autosuspend\n"); + return -EBUSY; + } + + ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } + + val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); + val |= PMT_CTL_SUS_MODE_3 | PMT_CTL_RES_CLR_WKP_EN; + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + /* clear wol status */ + val &= ~PMT_CTL_WUPS; + val |= PMT_CTL_WUPS_WOL; + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + + pdata->suspend_flags |= SUSPEND_SUSPEND3; + + return 0; +} + +static int smsc75xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) +{ + struct mii_if_info *mii = &dev->mii; + int ret; + + netdev_dbg(dev->net, "enabling PHY wakeup interrupts\n"); + + /* read to clear */ + ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); + return ret; + } + + /* enable interrupt source */ + ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_MASK\n"); + return ret; + } + + ret |= mask; + + smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, PHY_INT_MASK, ret); + + return 0; +} + +static int smsc75xx_link_ok_nopm(struct usbnet *dev) +{ + struct mii_if_info *mii = &dev->mii; + int ret; + + /* first, a dummy read, needed to latch some MII phys */ + ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_BMSR\n"); + return ret; + } + + ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_BMSR\n"); + return ret; + } + + return !!(ret & BMSR_LSTATUS); +} + +static int smsc75xx_autosuspend(struct usbnet *dev, u32 link_up) +{ + int ret; + + if (!netif_running(dev->net)) { + /* interface is ifconfig down so fully power down hw */ + netdev_dbg(dev->net, "autosuspend entering SUSPEND2\n"); + return smsc75xx_enter_suspend2(dev); + } + + if (!link_up) { + /* link is down so enter EDPD mode */ + netdev_dbg(dev->net, "autosuspend entering SUSPEND1\n"); + + /* enable PHY wakeup events for if cable is attached */ + ret = smsc75xx_enable_phy_wakeup_interrupts(dev, + PHY_INT_MASK_ANEG_COMP); + if (ret < 0) { + netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); + return ret; + } + + netdev_info(dev->net, "entering SUSPEND1 mode\n"); + return smsc75xx_enter_suspend1(dev); + } + + /* enable PHY wakeup events so we remote wakeup if cable is pulled */ + ret = smsc75xx_enable_phy_wakeup_interrupts(dev, + PHY_INT_MASK_LINK_DOWN); + if (ret < 0) { + netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); + return ret; + } + + netdev_dbg(dev->net, "autosuspend entering SUSPEND3\n"); + return smsc75xx_enter_suspend3(dev); +} + static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u32 val, link_up; int ret; - u32 val; ret = usbnet_suspend(intf, message); - check_warn_return(ret, "usbnet_suspend error\n"); + if (ret < 0) { + netdev_warn(dev->net, "usbnet_suspend error\n"); + return ret; + } - /* if no wol options set, enter lowest power SUSPEND2 mode */ - if (!(pdata->wolopts & SUPPORTED_WAKE)) { + if (pdata->suspend_flags) { + netdev_warn(dev->net, "error during last resume\n"); + pdata->suspend_flags = 0; + } + + /* determine if link is up using only _nopm functions */ + link_up = smsc75xx_link_ok_nopm(dev); + + if (message.event == PM_EVENT_AUTO_SUSPEND) { + ret = smsc75xx_autosuspend(dev, link_up); + goto done; + } + + /* if we get this far we're not autosuspending */ + /* if no wol options set, or if link is down and we're not waking on + * PHY activity, enter lowest power SUSPEND2 mode + */ + if (!(pdata->wolopts & SUPPORTED_WAKE) || + !(link_up || (pdata->wolopts & WAKE_PHY))) { netdev_info(dev->net, "entering SUSPEND2 mode\n"); /* disable energy detect (link up) & wake up events */ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val &= ~(WUCSR_MPEN | WUCSR_WUEN); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); - check_warn_return(ret, "Error reading PMT_CTL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + goto done; + } val &= ~(PMT_CTL_ED_EN | PMT_CTL_WOL_EN); ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); - check_warn_return(ret, "Error writing PMT_CTL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + goto done; + } - /* enter suspend2 mode */ - ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); - check_warn_return(ret, "Error reading PMT_CTL\n"); + ret = smsc75xx_enter_suspend2(dev); + goto done; + } - val &= ~(PMT_CTL_SUS_MODE | PMT_CTL_WUPS | PMT_CTL_PHY_RST); - val |= PMT_CTL_SUS_MODE_2; + if (pdata->wolopts & WAKE_PHY) { + ret = smsc75xx_enable_phy_wakeup_interrupts(dev, + (PHY_INT_MASK_ANEG_COMP | PHY_INT_MASK_LINK_DOWN)); + if (ret < 0) { + netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); + goto done; + } - ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); - check_warn_return(ret, "Error writing PMT_CTL\n"); + /* if link is down then configure EDPD and enter SUSPEND1, + * otherwise enter SUSPEND0 below + */ + if (!link_up) { + struct mii_if_info *mii = &dev->mii; + netdev_info(dev->net, "entering SUSPEND1 mode\n"); + + /* enable energy detect power-down mode */ + ret = smsc75xx_mdio_read_nopm(dev->net, mii->phy_id, + PHY_MODE_CTRL_STS); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); + goto done; + } - return 0; + ret |= MODE_CTRL_STS_EDPWRDOWN; + + smsc75xx_mdio_write_nopm(dev->net, mii->phy_id, + PHY_MODE_CTRL_STS, ret); + + /* enter SUSPEND1 mode */ + ret = smsc75xx_enter_suspend1(dev); + goto done; + } } if (pdata->wolopts & (WAKE_MCAST | WAKE_ARP)) { @@ -1263,7 +1822,10 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) /* disable all filters */ for (i = 0; i < WUF_NUM; i++) { ret = smsc75xx_write_reg_nopm(dev, WUF_CFGX + i * 4, 0); - check_warn_return(ret, "Error writing WUF_CFGX\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUF_CFGX\n"); + goto done; + } } if (pdata->wolopts & WAKE_MCAST) { @@ -1273,7 +1835,10 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) val = WUF_CFGX_EN | WUF_CFGX_ATYPE_MULTICAST | smsc_crc(mcast, 3); ret = smsc75xx_write_wuff(dev, filter++, val, 0x0007); - check_warn_return(ret, "Error writing wakeup filter\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing wakeup filter\n"); + goto done; + } } if (pdata->wolopts & WAKE_ARP) { @@ -1283,151 +1848,240 @@ static int smsc75xx_suspend(struct usb_interface *intf, pm_message_t message) val = WUF_CFGX_EN | WUF_CFGX_ATYPE_ALL | (0x0C << 16) | smsc_crc(arp, 2); ret = smsc75xx_write_wuff(dev, filter++, val, 0x0003); - check_warn_return(ret, "Error writing wakeup filter\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing wakeup filter\n"); + goto done; + } } /* clear any pending pattern match packet status */ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_WUFR; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } netdev_info(dev->net, "enabling packet match detection\n"); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_WUEN; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } else { netdev_info(dev->net, "disabling packet match detection\n"); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val &= ~WUCSR_WUEN; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } /* disable magic, bcast & unicast wakeup sources */ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val &= ~(WUCSR_MPEN | WUCSR_BCST_EN | WUCSR_PFDA_EN); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } + + if (pdata->wolopts & WAKE_PHY) { + netdev_info(dev->net, "enabling PHY wakeup\n"); + + ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + goto done; + } + + /* clear wol status, enable energy detection */ + val &= ~PMT_CTL_WUPS; + val |= (PMT_CTL_WUPS_ED | PMT_CTL_ED_EN); + + ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + goto done; + } + } if (pdata->wolopts & WAKE_MAGIC) { netdev_info(dev->net, "enabling magic packet wakeup\n"); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } /* clear any pending magic packet status */ val |= WUCSR_MPR | WUCSR_MPEN; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } if (pdata->wolopts & WAKE_BCAST) { netdev_info(dev->net, "enabling broadcast detection\n"); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_BCAST_FR | WUCSR_BCST_EN; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } if (pdata->wolopts & WAKE_UCAST) { netdev_info(dev->net, "enabling unicast detection\n"); ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_WUFR | WUCSR_PFDA_EN; ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } /* enable receiver to enable frame reception */ ret = smsc75xx_read_reg_nopm(dev, MAC_RX, &val); - check_warn_return(ret, "Failed to read MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_RX: %d\n", ret); + goto done; + } val |= MAC_RX_RXEN; ret = smsc75xx_write_reg_nopm(dev, MAC_RX, val); - check_warn_return(ret, "Failed to write MAC_RX: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_RX: %d\n", ret); + goto done; + } /* some wol options are enabled, so enter SUSPEND0 */ netdev_info(dev->net, "entering SUSPEND0 mode\n"); + ret = smsc75xx_enter_suspend0(dev); - ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); - check_warn_return(ret, "Error reading PMT_CTL\n"); - - val &= (~(PMT_CTL_SUS_MODE | PMT_CTL_PHY_RST)); - val |= PMT_CTL_SUS_MODE_0 | PMT_CTL_WOL_EN | PMT_CTL_WUPS; - - ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); - check_warn_return(ret, "Error writing PMT_CTL\n"); - - smsc75xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); - - return 0; +done: + if (ret) + usbnet_resume(intf); + return ret; } static int smsc75xx_resume(struct usb_interface *intf) { struct usbnet *dev = usb_get_intfdata(intf); struct smsc75xx_priv *pdata = (struct smsc75xx_priv *)(dev->data[0]); + u8 suspend_flags = pdata->suspend_flags; int ret; u32 val; - if (pdata->wolopts) { - netdev_info(dev->net, "resuming from SUSPEND0\n"); + netdev_dbg(dev->net, "resume suspend_flags=0x%02x\n", suspend_flags); - smsc75xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); + /* do this first to ensure it's cleared even in error case */ + pdata->suspend_flags = 0; + if (suspend_flags & SUSPEND_ALLMODES) { /* Disable wakeup sources */ ret = smsc75xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + return ret; + } val &= ~(WUCSR_WUEN | WUCSR_MPEN | WUCSR_PFDA_EN | WUCSR_BCST_EN); ret = smsc75xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + return ret; + } /* clear wake-up status */ ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); - check_warn_return(ret, "Error reading PMT_CTL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } val &= ~PMT_CTL_WOL_EN; val |= PMT_CTL_WUPS; ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); - check_warn_return(ret, "Error writing PMT_CTL\n"); - } else { + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } + } + + if (suspend_flags & SUSPEND_SUSPEND2) { netdev_info(dev->net, "resuming from SUSPEND2\n"); ret = smsc75xx_read_reg_nopm(dev, PMT_CTL, &val); - check_warn_return(ret, "Error reading PMT_CTL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PMT_CTL\n"); + return ret; + } val |= PMT_CTL_PHY_PWRUP; ret = smsc75xx_write_reg_nopm(dev, PMT_CTL, val); - check_warn_return(ret, "Error writing PMT_CTL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PMT_CTL\n"); + return ret; + } } ret = smsc75xx_wait_ready(dev, 1); - check_warn_return(ret, "device not ready in smsc75xx_resume\n"); + if (ret < 0) { + netdev_warn(dev->net, "device not ready in smsc75xx_resume\n"); + return ret; + } return usbnet_resume(intf); } @@ -1570,6 +2224,12 @@ static struct sk_buff *smsc75xx_tx_fixup(struct usbnet *dev, return skb; } +static int smsc75xx_manage_power(struct usbnet *dev, int on) +{ + dev->intf->needs_remote_wakeup = on; + return 0; +} + static const struct driver_info smsc75xx_info = { .description = "smsc75xx USB 2.0 Gigabit Ethernet", .bind = smsc75xx_bind, @@ -1579,6 +2239,7 @@ static const struct driver_info smsc75xx_info = { .rx_fixup = smsc75xx_rx_fixup, .tx_fixup = smsc75xx_tx_fixup, .status = smsc75xx_status, + .manage_power = smsc75xx_manage_power, .flags = FLAG_ETHER | FLAG_SEND_ZLP | FLAG_LINK_INTR, }; @@ -1606,6 +2267,7 @@ static struct usb_driver smsc75xx_driver = { .reset_resume = smsc75xx_resume, .disconnect = usbnet_disconnect, .disable_hub_initiated_lpm = 1, + .supports_autosuspend = 1, }; module_usb_driver(smsc75xx_driver); diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c index 79d495d1554..f7e1e189fa4 100644 --- a/drivers/net/usb/smsc95xx.c +++ b/drivers/net/usb/smsc95xx.c @@ -55,15 +55,6 @@ #define FEATURE_PHY_NLP_CROSSOVER (0x02) #define FEATURE_AUTOSUSPEND (0x04) -#define check_warn(ret, fmt, args...) \ - ({ if (ret < 0) netdev_warn(dev->net, fmt, ##args); }) - -#define check_warn_return(ret, fmt, args...) \ - ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); return ret; } }) - -#define check_warn_goto_done(ret, fmt, args...) \ - ({ if (ret < 0) { netdev_warn(dev->net, fmt, ##args); goto done; } }) - struct smsc95xx_priv { u32 mac_cr; u32 hash_hi; @@ -154,25 +145,6 @@ static int __must_check smsc95xx_write_reg(struct usbnet *dev, u32 index, { return __smsc95xx_write_reg(dev, index, data, 0); } -static int smsc95xx_set_feature(struct usbnet *dev, u32 feature) -{ - if (WARN_ON_ONCE(!dev)) - return -EINVAL; - - return usbnet_write_cmd_nopm(dev, USB_REQ_SET_FEATURE, - USB_RECIP_DEVICE, feature, 0, - NULL, 0); -} - -static int smsc95xx_clear_feature(struct usbnet *dev, u32 feature) -{ - if (WARN_ON_ONCE(!dev)) - return -EINVAL; - - return usbnet_write_cmd_nopm(dev, USB_REQ_CLEAR_FEATURE, - USB_RECIP_DEVICE, feature, - 0, NULL, 0); -} /* Loop until the read is completed with timeout * called with phy_mutex held */ @@ -185,7 +157,11 @@ static int __must_check __smsc95xx_phy_wait_not_busy(struct usbnet *dev, do { ret = __smsc95xx_read_reg(dev, MII_ADDR, &val, in_pm); - check_warn_return(ret, "Error reading MII_ACCESS\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_ACCESS\n"); + return ret; + } + if (!(val & MII_BUSY_)) return 0; } while (!time_after(jiffies, start_time + HZ)); @@ -204,20 +180,32 @@ static int __smsc95xx_mdio_read(struct net_device *netdev, int phy_id, int idx, /* confirm MII not busy */ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); - check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_read\n"); + if (ret < 0) { + netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_read\n"); + goto done; + } /* set the address, index & direction (read from PHY) */ phy_id &= dev->mii.phy_id_mask; idx &= dev->mii.reg_num_mask; addr = (phy_id << 11) | (idx << 6) | MII_READ_ | MII_BUSY_; ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); - check_warn_goto_done(ret, "Error writing MII_ADDR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_ADDR\n"); + goto done; + } ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); - check_warn_goto_done(ret, "Timed out reading MII reg %02X\n", idx); + if (ret < 0) { + netdev_warn(dev->net, "Timed out reading MII reg %02X\n", idx); + goto done; + } ret = __smsc95xx_read_reg(dev, MII_DATA, &val, in_pm); - check_warn_goto_done(ret, "Error reading MII_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_DATA\n"); + goto done; + } ret = (u16)(val & 0xFFFF); @@ -237,21 +225,33 @@ static void __smsc95xx_mdio_write(struct net_device *netdev, int phy_id, /* confirm MII not busy */ ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); - check_warn_goto_done(ret, "MII is busy in smsc95xx_mdio_write\n"); + if (ret < 0) { + netdev_warn(dev->net, "MII is busy in smsc95xx_mdio_write\n"); + goto done; + } val = regval; ret = __smsc95xx_write_reg(dev, MII_DATA, val, in_pm); - check_warn_goto_done(ret, "Error writing MII_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_DATA\n"); + goto done; + } /* set the address, index & direction (write to PHY) */ phy_id &= dev->mii.phy_id_mask; idx &= dev->mii.reg_num_mask; addr = (phy_id << 11) | (idx << 6) | MII_WRITE_ | MII_BUSY_; ret = __smsc95xx_write_reg(dev, MII_ADDR, addr, in_pm); - check_warn_goto_done(ret, "Error writing MII_ADDR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MII_ADDR\n"); + goto done; + } ret = __smsc95xx_phy_wait_not_busy(dev, in_pm); - check_warn_goto_done(ret, "Timed out writing MII reg %02X\n", idx); + if (ret < 0) { + netdev_warn(dev->net, "Timed out writing MII reg %02X\n", idx); + goto done; + } done: mutex_unlock(&dev->phy_mutex); @@ -288,7 +288,11 @@ static int __must_check smsc95xx_wait_eeprom(struct usbnet *dev) do { ret = smsc95xx_read_reg(dev, E2P_CMD, &val); - check_warn_return(ret, "Error reading E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_CMD\n"); + return ret; + } + if (!(val & E2P_CMD_BUSY_) || (val & E2P_CMD_TIMEOUT_)) break; udelay(40); @@ -310,7 +314,10 @@ static int __must_check smsc95xx_eeprom_confirm_not_busy(struct usbnet *dev) do { ret = smsc95xx_read_reg(dev, E2P_CMD, &val); - check_warn_return(ret, "Error reading E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_CMD\n"); + return ret; + } if (!(val & E2P_CMD_BUSY_)) return 0; @@ -338,14 +345,20 @@ static int smsc95xx_read_eeprom(struct usbnet *dev, u32 offset, u32 length, for (i = 0; i < length; i++) { val = E2P_CMD_BUSY_ | E2P_CMD_READ_ | (offset & E2P_CMD_ADDR_); ret = smsc95xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_CMD\n"); + return ret; + } ret = smsc95xx_wait_eeprom(dev); if (ret < 0) return ret; ret = smsc95xx_read_reg(dev, E2P_DATA, &val); - check_warn_return(ret, "Error reading E2P_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading E2P_DATA\n"); + return ret; + } data[i] = val & 0xFF; offset++; @@ -370,7 +383,10 @@ static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, /* Issue write/erase enable command */ val = E2P_CMD_BUSY_ | E2P_CMD_EWEN_; ret = smsc95xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_DATA\n"); + return ret; + } ret = smsc95xx_wait_eeprom(dev); if (ret < 0) @@ -381,12 +397,18 @@ static int smsc95xx_write_eeprom(struct usbnet *dev, u32 offset, u32 length, /* Fill data register */ val = data[i]; ret = smsc95xx_write_reg(dev, E2P_DATA, val); - check_warn_return(ret, "Error writing E2P_DATA\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_DATA\n"); + return ret; + } /* Send "write" command */ val = E2P_CMD_BUSY_ | E2P_CMD_WRITE_ | (offset & E2P_CMD_ADDR_); ret = smsc95xx_write_reg(dev, E2P_CMD, val); - check_warn_return(ret, "Error writing E2P_CMD\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing E2P_CMD\n"); + return ret; + } ret = smsc95xx_wait_eeprom(dev); if (ret < 0) @@ -469,13 +491,16 @@ static void smsc95xx_set_multicast(struct net_device *netdev) /* Initiate async writes, as we can't wait for completion here */ ret = smsc95xx_write_reg_async(dev, HASHH, &pdata->hash_hi); - check_warn(ret, "failed to initiate async write to HASHH\n"); + if (ret < 0) + netdev_warn(dev->net, "failed to initiate async write to HASHH\n"); ret = smsc95xx_write_reg_async(dev, HASHL, &pdata->hash_lo); - check_warn(ret, "failed to initiate async write to HASHL\n"); + if (ret < 0) + netdev_warn(dev->net, "failed to initiate async write to HASHL\n"); ret = smsc95xx_write_reg_async(dev, MAC_CR, &pdata->mac_cr); - check_warn(ret, "failed to initiate async write to MAC_CR\n"); + if (ret < 0) + netdev_warn(dev->net, "failed to initiate async write to MAC_CR\n"); } static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, @@ -484,7 +509,10 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, u32 flow, afc_cfg = 0; int ret = smsc95xx_read_reg(dev, AFC_CFG, &afc_cfg); - check_warn_return(ret, "Error reading AFC_CFG\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading AFC_CFG\n"); + return ret; + } if (duplex == DUPLEX_FULL) { u8 cap = mii_resolve_flowctrl_fdx(lcladv, rmtadv); @@ -509,12 +537,16 @@ static int smsc95xx_phy_update_flowcontrol(struct usbnet *dev, u8 duplex, } ret = smsc95xx_write_reg(dev, FLOW, flow); - check_warn_return(ret, "Error writing FLOW\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing FLOW\n"); + return ret; + } ret = smsc95xx_write_reg(dev, AFC_CFG, afc_cfg); - check_warn_return(ret, "Error writing AFC_CFG\n"); + if (ret < 0) + netdev_warn(dev->net, "Error writing AFC_CFG\n"); - return 0; + return ret; } static int smsc95xx_link_reset(struct usbnet *dev) @@ -528,10 +560,16 @@ static int smsc95xx_link_reset(struct usbnet *dev) /* clear interrupt status */ ret = smsc95xx_mdio_read(dev->net, mii->phy_id, PHY_INT_SRC); - check_warn_return(ret, "Error reading PHY_INT_SRC\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); + return ret; + } ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); - check_warn_return(ret, "Error writing INT_STS\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing INT_STS\n"); + return ret; + } mii_check_media(mii, 1, 1); mii_ethtool_gset(&dev->mii, &ecmd); @@ -553,12 +591,16 @@ static int smsc95xx_link_reset(struct usbnet *dev) spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); - check_warn_return(ret, "Error writing MAC_CR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing MAC_CR\n"); + return ret; + } ret = smsc95xx_phy_update_flowcontrol(dev, ecmd.duplex, lcladv, rmtadv); - check_warn_return(ret, "Error updating PHY flow control\n"); + if (ret < 0) + netdev_warn(dev->net, "Error updating PHY flow control\n"); - return 0; + return ret; } static void smsc95xx_status(struct usbnet *dev, struct urb *urb) @@ -592,7 +634,10 @@ static int smsc95xx_set_features(struct net_device *netdev, int ret; ret = smsc95xx_read_reg(dev, COE_CR, &read_buf); - check_warn_return(ret, "Failed to read COE_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read COE_CR: %d\n", ret); + return ret; + } if (features & NETIF_F_HW_CSUM) read_buf |= Tx_COE_EN_; @@ -605,7 +650,10 @@ static int smsc95xx_set_features(struct net_device *netdev, read_buf &= ~Rx_COE_EN_; ret = smsc95xx_write_reg(dev, COE_CR, read_buf); - check_warn_return(ret, "Failed to write COE_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write COE_CR: %d\n", ret); + return ret; + } netif_dbg(dev, hw, dev->net, "COE_CR = 0x%08x\n", read_buf); return 0; @@ -685,9 +733,15 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net, { struct usbnet *dev = netdev_priv(net); struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]); + int ret; pdata->wolopts = wolinfo->wolopts & SUPPORTED_WAKE; - return 0; + + ret = device_set_wakeup_enable(&dev->udev->dev, pdata->wolopts); + if (ret < 0) + netdev_warn(dev->net, "device_set_wakeup_enable error %d\n", ret); + + return ret; } static const struct ethtool_ops smsc95xx_ethtool_ops = { @@ -742,12 +796,16 @@ static int smsc95xx_set_mac_address(struct usbnet *dev) int ret; ret = smsc95xx_write_reg(dev, ADDRL, addr_lo); - check_warn_return(ret, "Failed to write ADDRL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write ADDRL: %d\n", ret); + return ret; + } ret = smsc95xx_write_reg(dev, ADDRH, addr_hi); - check_warn_return(ret, "Failed to write ADDRH: %d\n", ret); + if (ret < 0) + netdev_warn(dev->net, "Failed to write ADDRH: %d\n", ret); - return 0; + return ret; } /* starts the TX path */ @@ -763,13 +821,17 @@ static int smsc95xx_start_tx_path(struct usbnet *dev) spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ret = smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr); - check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); + return ret; + } /* Enable Tx at SCSRs */ ret = smsc95xx_write_reg(dev, TX_CFG, TX_CFG_ON_); - check_warn_return(ret, "Failed to write TX_CFG: %d\n", ret); + if (ret < 0) + netdev_warn(dev->net, "Failed to write TX_CFG: %d\n", ret); - return 0; + return ret; } /* Starts the Receive path */ @@ -784,9 +846,10 @@ static int smsc95xx_start_rx_path(struct usbnet *dev, int in_pm) spin_unlock_irqrestore(&pdata->mac_cr_lock, flags); ret = __smsc95xx_write_reg(dev, MAC_CR, pdata->mac_cr, in_pm); - check_warn_return(ret, "Failed to write MAC_CR: %d\n", ret); + if (ret < 0) + netdev_warn(dev->net, "Failed to write MAC_CR: %d\n", ret); - return 0; + return ret; } static int smsc95xx_phy_initialize(struct usbnet *dev) @@ -821,7 +884,10 @@ static int smsc95xx_phy_initialize(struct usbnet *dev) /* read to clear */ ret = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, PHY_INT_SRC); - check_warn_return(ret, "Failed to read PHY_INT_SRC during init\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PHY_INT_SRC during init\n"); + return ret; + } smsc95xx_mdio_write(dev->net, dev->mii.phy_id, PHY_INT_MASK, PHY_INT_MASK_DEFAULT_); @@ -840,13 +906,19 @@ static int smsc95xx_reset(struct usbnet *dev) netif_dbg(dev, ifup, dev->net, "entering smsc95xx_reset\n"); ret = smsc95xx_write_reg(dev, HW_CFG, HW_CFG_LRST_); - check_warn_return(ret, "Failed to write HW_CFG_LRST_ bit in HW_CFG\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG_LRST_ bit in HW_CFG\n"); + return ret; + } timeout = 0; do { msleep(10); ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } timeout++; } while ((read_buf & HW_CFG_LRST_) && (timeout < 100)); @@ -856,13 +928,19 @@ static int smsc95xx_reset(struct usbnet *dev) } ret = smsc95xx_write_reg(dev, PM_CTRL, PM_CTL_PHY_RST_); - check_warn_return(ret, "Failed to write PM_CTRL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write PM_CTRL: %d\n", ret); + return ret; + } timeout = 0; do { msleep(10); ret = smsc95xx_read_reg(dev, PM_CTRL, &read_buf); - check_warn_return(ret, "Failed to read PM_CTRL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read PM_CTRL: %d\n", ret); + return ret; + } timeout++; } while ((read_buf & PM_CTL_PHY_RST_) && (timeout < 100)); @@ -879,7 +957,10 @@ static int smsc95xx_reset(struct usbnet *dev) dev->net->dev_addr); ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG : 0x%08x\n", read_buf); @@ -887,10 +968,17 @@ static int smsc95xx_reset(struct usbnet *dev) read_buf |= HW_CFG_BIR_; ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); - check_warn_return(ret, "Failed to write HW_CFG_BIR_ bit in HW_CFG\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG_BIR_ bit in HW_CFG\n"); + return ret; + } ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } + netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing HW_CFG_BIR_: 0x%08x\n", read_buf); @@ -910,27 +998,42 @@ static int smsc95xx_reset(struct usbnet *dev) (ulong)dev->rx_urb_size); ret = smsc95xx_write_reg(dev, BURST_CAP, burst_cap); - check_warn_return(ret, "Failed to write BURST_CAP: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write BURST_CAP: %d\n", ret); + return ret; + } ret = smsc95xx_read_reg(dev, BURST_CAP, &read_buf); - check_warn_return(ret, "Failed to read BURST_CAP: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BURST_CAP: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from BURST_CAP after writing: 0x%08x\n", read_buf); ret = smsc95xx_write_reg(dev, BULK_IN_DLY, DEFAULT_BULK_IN_DELAY); - check_warn_return(ret, "Failed to write BULK_IN_DLY: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write BULK_IN_DLY: %d\n", ret); + return ret; + } ret = smsc95xx_read_reg(dev, BULK_IN_DLY, &read_buf); - check_warn_return(ret, "Failed to read BULK_IN_DLY: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read BULK_IN_DLY: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from BULK_IN_DLY after writing: 0x%08x\n", read_buf); ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG: 0x%08x\n", read_buf); @@ -944,66 +1047,111 @@ static int smsc95xx_reset(struct usbnet *dev) read_buf |= NET_IP_ALIGN << 9; ret = smsc95xx_write_reg(dev, HW_CFG, read_buf); - check_warn_return(ret, "Failed to write HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write HW_CFG: %d\n", ret); + return ret; + } ret = smsc95xx_read_reg(dev, HW_CFG, &read_buf); - check_warn_return(ret, "Failed to read HW_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read HW_CFG: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "Read Value from HW_CFG after writing: 0x%08x\n", read_buf); ret = smsc95xx_write_reg(dev, INT_STS, INT_STS_CLEAR_ALL_); - check_warn_return(ret, "Failed to write INT_STS: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_STS: %d\n", ret); + return ret; + } ret = smsc95xx_read_reg(dev, ID_REV, &read_buf); - check_warn_return(ret, "Failed to read ID_REV: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); + return ret; + } netif_dbg(dev, ifup, dev->net, "ID_REV = 0x%08x\n", read_buf); /* Configure GPIO pins as LED outputs */ write_buf = LED_GPIO_CFG_SPD_LED | LED_GPIO_CFG_LNK_LED | LED_GPIO_CFG_FDX_LED; ret = smsc95xx_write_reg(dev, LED_GPIO_CFG, write_buf); - check_warn_return(ret, "Failed to write LED_GPIO_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write LED_GPIO_CFG: %d\n", ret); + return ret; + } /* Init Tx */ ret = smsc95xx_write_reg(dev, FLOW, 0); - check_warn_return(ret, "Failed to write FLOW: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write FLOW: %d\n", ret); + return ret; + } ret = smsc95xx_write_reg(dev, AFC_CFG, AFC_CFG_DEFAULT); - check_warn_return(ret, "Failed to write AFC_CFG: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write AFC_CFG: %d\n", ret); + return ret; + } /* Don't need mac_cr_lock during initialisation */ ret = smsc95xx_read_reg(dev, MAC_CR, &pdata->mac_cr); - check_warn_return(ret, "Failed to read MAC_CR: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read MAC_CR: %d\n", ret); + return ret; + } /* Init Rx */ /* Set Vlan */ ret = smsc95xx_write_reg(dev, VLAN1, (u32)ETH_P_8021Q); - check_warn_return(ret, "Failed to write VLAN1: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write VLAN1: %d\n", ret); + return ret; + } /* Enable or disable checksum offload engines */ ret = smsc95xx_set_features(dev->net, dev->net->features); - check_warn_return(ret, "Failed to set checksum offload features\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to set checksum offload features\n"); + return ret; + } smsc95xx_set_multicast(dev->net); ret = smsc95xx_phy_initialize(dev); - check_warn_return(ret, "Failed to init PHY\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to init PHY\n"); + return ret; + } ret = smsc95xx_read_reg(dev, INT_EP_CTL, &read_buf); - check_warn_return(ret, "Failed to read INT_EP_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read INT_EP_CTL: %d\n", ret); + return ret; + } /* enable PHY interrupts */ read_buf |= INT_EP_CTL_PHY_INT_; ret = smsc95xx_write_reg(dev, INT_EP_CTL, read_buf); - check_warn_return(ret, "Failed to write INT_EP_CTL: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to write INT_EP_CTL: %d\n", ret); + return ret; + } ret = smsc95xx_start_tx_path(dev); - check_warn_return(ret, "Failed to start TX path\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to start TX path\n"); + return ret; + } ret = smsc95xx_start_rx_path(dev, 0); - check_warn_return(ret, "Failed to start RX path\n"); + if (ret < 0) { + netdev_warn(dev->net, "Failed to start RX path\n"); + return ret; + } netif_dbg(dev, ifup, dev->net, "smsc95xx_reset, return 0\n"); return 0; @@ -1031,7 +1179,10 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) printk(KERN_INFO SMSC_CHIPNAME " v" SMSC_DRIVER_VERSION "\n"); ret = usbnet_get_endpoints(dev, intf); - check_warn_return(ret, "usbnet_get_endpoints failed: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "usbnet_get_endpoints failed: %d\n", ret); + return ret; + } dev->data[0] = (unsigned long)kzalloc(sizeof(struct smsc95xx_priv), GFP_KERNEL); @@ -1058,7 +1209,10 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf) /* detect device revision as different features may be available */ ret = smsc95xx_read_reg(dev, ID_REV, &val); - check_warn_return(ret, "Failed to read ID_REV: %d\n", ret); + if (ret < 0) { + netdev_warn(dev->net, "Failed to read ID_REV: %d\n", ret); + return ret; + } val >>= 16; if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) || @@ -1088,9 +1242,10 @@ static void smsc95xx_unbind(struct usbnet *dev, struct usb_interface *intf) } } -static u16 smsc_crc(const u8 *buffer, size_t len, int filter) +static u32 smsc_crc(const u8 *buffer, size_t len, int filter) { - return bitrev16(crc16(0xFFFF, buffer, len)) << ((filter % 2) * 16); + u32 crc = bitrev16(crc16(0xFFFF, buffer, len)); + return crc << ((filter % 2) * 16); } static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) @@ -1102,11 +1257,17 @@ static int smsc95xx_enable_phy_wakeup_interrupts(struct usbnet *dev, u16 mask) /* read to clear */ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_SRC); - check_warn_return(ret, "Error reading PHY_INT_SRC\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_SRC\n"); + return ret; + } /* enable interrupt source */ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_INT_MASK); - check_warn_return(ret, "Error reading PHY_INT_MASK\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_INT_MASK\n"); + return ret; + } ret |= mask; @@ -1122,10 +1283,16 @@ static int smsc95xx_link_ok_nopm(struct usbnet *dev) /* first, a dummy read, needed to latch some MII phys */ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); - check_warn_return(ret, "Error reading MII_BMSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_BMSR\n"); + return ret; + } ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, MII_BMSR); - check_warn_return(ret, "Error reading MII_BMSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading MII_BMSR\n"); + return ret; + } return !!(ret & BMSR_LSTATUS); } @@ -1137,13 +1304,19 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev) int ret; ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + return ret; + } val &= (~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_)); val |= PM_CTL_SUS_MODE_0; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + return ret; + } /* clear wol status */ val &= ~PM_CTL_WUPS_; @@ -1154,15 +1327,17 @@ static int smsc95xx_enter_suspend0(struct usbnet *dev) val |= PM_CTL_WUPS_ED_; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + return ret; + } /* read back PM_CTRL */ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); - - smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); + if (ret < 0) + netdev_warn(dev->net, "Error reading PM_CTRL\n"); - return 0; + return ret; } static int smsc95xx_enter_suspend1(struct usbnet *dev) @@ -1181,7 +1356,10 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev) /* enable energy detect power-down mode */ ret = smsc95xx_mdio_read_nopm(dev->net, mii->phy_id, PHY_MODE_CTRL_STS); - check_warn_return(ret, "Error reading PHY_MODE_CTRL_STS\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PHY_MODE_CTRL_STS\n"); + return ret; + } ret |= MODE_CTRL_STS_EDPWRDOWN_; @@ -1189,24 +1367,29 @@ static int smsc95xx_enter_suspend1(struct usbnet *dev) /* enter SUSPEND1 mode */ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + return ret; + } val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); val |= PM_CTL_SUS_MODE_1; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + return ret; + } /* clear wol status, enable energy detection */ val &= ~PM_CTL_WUPS_; val |= (PM_CTL_WUPS_ED_ | PM_CTL_ED_EN_); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); - - smsc95xx_set_feature(dev, USB_DEVICE_REMOTE_WAKEUP); + if (ret < 0) + netdev_warn(dev->net, "Error writing PM_CTRL\n"); - return 0; + return ret; } static int smsc95xx_enter_suspend2(struct usbnet *dev) @@ -1215,15 +1398,19 @@ static int smsc95xx_enter_suspend2(struct usbnet *dev) int ret; ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + return ret; + } val &= ~(PM_CTL_SUS_MODE_ | PM_CTL_WUPS_ | PM_CTL_PHY_RST_); val |= PM_CTL_SUS_MODE_2; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) + netdev_warn(dev->net, "Error writing PM_CTRL\n"); - return 0; + return ret; } static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) @@ -1234,7 +1421,10 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) int ret; ret = usbnet_suspend(intf, message); - check_warn_return(ret, "usbnet_suspend error\n"); + if (ret < 0) { + netdev_warn(dev->net, "usbnet_suspend error\n"); + return ret; + } /* determine if link is up using only _nopm functions */ link_up = smsc95xx_link_ok_nopm(dev); @@ -1248,40 +1438,57 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) /* disable energy detect (link up) & wake up events */ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val &= ~(WUCSR_MPEN_ | WUCSR_WAKE_EN_); ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + goto done; + } val &= ~(PM_CTL_ED_EN_ | PM_CTL_WOL_EN_); ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + goto done; + } - return smsc95xx_enter_suspend2(dev); + ret = smsc95xx_enter_suspend2(dev); + goto done; } if (pdata->wolopts & WAKE_PHY) { ret = smsc95xx_enable_phy_wakeup_interrupts(dev, (PHY_INT_MASK_ANEG_COMP_ | PHY_INT_MASK_LINK_DOWN_)); - check_warn_return(ret, "error enabling PHY wakeup ints\n"); + if (ret < 0) { + netdev_warn(dev->net, "error enabling PHY wakeup ints\n"); + goto done; + } /* if link is down then configure EDPD and enter SUSPEND1, * otherwise enter SUSPEND0 below */ if (!link_up) { netdev_info(dev->net, "entering SUSPEND1 mode\n"); - return smsc95xx_enter_suspend1(dev); + ret = smsc95xx_enter_suspend1(dev); + goto done; } } if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { - u32 *filter_mask = kzalloc(32, GFP_KERNEL); + u32 *filter_mask = kzalloc(sizeof(u32) * 32, GFP_KERNEL); u32 command[2]; u32 offset[2]; u32 crc[4]; @@ -1290,6 +1497,12 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) LAN9500A_WUFF_NUM : LAN9500_WUFF_NUM; int i, filter = 0; + if (!filter_mask) { + netdev_warn(dev->net, "Unable to allocate filter_mask\n"); + ret = -ENOMEM; + goto done; + } + memset(command, 0, sizeof(command)); memset(offset, 0, sizeof(offset)); memset(crc, 0, sizeof(crc)); @@ -1347,51 +1560,77 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) for (i = 0; i < (wuff_filter_count * 4); i++) { ret = smsc95xx_write_reg_nopm(dev, WUFF, filter_mask[i]); - if (ret < 0) + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUFF\n"); kfree(filter_mask); - check_warn_return(ret, "Error writing WUFF\n"); + goto done; + } } kfree(filter_mask); for (i = 0; i < (wuff_filter_count / 4); i++) { ret = smsc95xx_write_reg_nopm(dev, WUFF, command[i]); - check_warn_return(ret, "Error writing WUFF\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUFF\n"); + goto done; + } } for (i = 0; i < (wuff_filter_count / 4); i++) { ret = smsc95xx_write_reg_nopm(dev, WUFF, offset[i]); - check_warn_return(ret, "Error writing WUFF\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUFF\n"); + goto done; + } } for (i = 0; i < (wuff_filter_count / 2); i++) { ret = smsc95xx_write_reg_nopm(dev, WUFF, crc[i]); - check_warn_return(ret, "Error writing WUFF\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUFF\n"); + goto done; + } } /* clear any pending pattern match packet status */ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_WUFR_; ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } if (pdata->wolopts & WAKE_MAGIC) { /* clear any pending magic packet status */ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } val |= WUCSR_MPR_; ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } } /* enable/disable wakeup sources */ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + goto done; + } if (pdata->wolopts & (WAKE_BCAST | WAKE_MCAST | WAKE_ARP | WAKE_UCAST)) { netdev_info(dev->net, "enabling pattern match wakeup\n"); @@ -1410,11 +1649,17 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) } ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + goto done; + } /* enable wol wakeup source */ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + goto done; + } val |= PM_CTL_WOL_EN_; @@ -1423,14 +1668,22 @@ static int smsc95xx_suspend(struct usb_interface *intf, pm_message_t message) val |= PM_CTL_ED_EN_; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + goto done; + } /* enable receiver to enable frame reception */ smsc95xx_start_rx_path(dev, 1); /* some wol options are enabled, so enter SUSPEND0 */ netdev_info(dev->net, "entering SUSPEND0 mode\n"); - return smsc95xx_enter_suspend0(dev); + ret = smsc95xx_enter_suspend0(dev); + +done: + if (ret) + usbnet_resume(intf); + return ret; } static int smsc95xx_resume(struct usb_interface *intf) @@ -1443,32 +1696,43 @@ static int smsc95xx_resume(struct usb_interface *intf) BUG_ON(!dev); if (pdata->wolopts) { - smsc95xx_clear_feature(dev, USB_DEVICE_REMOTE_WAKEUP); - /* clear wake-up sources */ ret = smsc95xx_read_reg_nopm(dev, WUCSR, &val); - check_warn_return(ret, "Error reading WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading WUCSR\n"); + return ret; + } val &= ~(WUCSR_WAKE_EN_ | WUCSR_MPEN_); ret = smsc95xx_write_reg_nopm(dev, WUCSR, val); - check_warn_return(ret, "Error writing WUCSR\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing WUCSR\n"); + return ret; + } /* clear wake-up status */ ret = smsc95xx_read_reg_nopm(dev, PM_CTRL, &val); - check_warn_return(ret, "Error reading PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error reading PM_CTRL\n"); + return ret; + } val &= ~PM_CTL_WOL_EN_; val |= PM_CTL_WUPS_; ret = smsc95xx_write_reg_nopm(dev, PM_CTRL, val); - check_warn_return(ret, "Error writing PM_CTRL\n"); + if (ret < 0) { + netdev_warn(dev->net, "Error writing PM_CTRL\n"); + return ret; + } } ret = usbnet_resume(intf); - check_warn_return(ret, "usbnet_resume error\n"); + if (ret < 0) + netdev_warn(dev->net, "usbnet_resume error\n"); - return 0; + return ret; } static void smsc95xx_rx_csum_offload(struct sk_buff *skb) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 24f6b27e169..95814d9747e 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -340,7 +340,7 @@ static int veth_newlink(struct net *src_net, struct net_device *dev, if (IS_ERR(net)) return PTR_ERR(net); - peer = rtnl_create_link(src_net, net, ifname, &veth_link_ops, tbp); + peer = rtnl_create_link(net, ifname, &veth_link_ops, tbp); if (IS_ERR(peer)) { put_net(net); return PTR_ERR(peer); diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c index 99b9ddf2127..77fa4286e5e 100644 --- a/drivers/net/wireless/at76c50x-usb.c +++ b/drivers/net/wireless/at76c50x-usb.c @@ -379,7 +379,7 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, manifest_sync_timeout); if (!size) { - dev_printk(KERN_ERR, &udev->dev, "FW buffer length invalid!\n"); + dev_err(&udev->dev, "FW buffer length invalid!\n"); return -EINVAL; } @@ -391,8 +391,8 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, if (need_dfu_state) { ret = at76_dfu_get_state(udev, &dfu_state); if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, - "cannot get DFU state: %d\n", ret); + dev_err(&udev->dev, + "cannot get DFU state: %d\n", ret); goto exit; } need_dfu_state = 0; @@ -407,9 +407,9 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, dfu_timeout = at76_get_timeout(&dfu_stat_buf); need_dfu_state = 0; } else - dev_printk(KERN_ERR, &udev->dev, - "at76_dfu_get_status returned %d\n", - ret); + dev_err(&udev->dev, + "at76_dfu_get_status returned %d\n", + ret); break; case STATE_DFU_DOWNLOAD_BUSY: @@ -438,9 +438,9 @@ static int at76_usbdfu_download(struct usb_device *udev, u8 *buf, u32 size, blockno++; if (ret != bsize) - dev_printk(KERN_ERR, &udev->dev, - "at76_load_int_fw_block " - "returned %d\n", ret); + dev_err(&udev->dev, + "at76_load_int_fw_block returned %d\n", + ret); need_dfu_state = 1; break; @@ -1255,8 +1255,7 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) at76_dbg(DBG_DEVSTART, "opmode %d", op_mode); if (op_mode != OPMODE_NORMAL_NIC_WITHOUT_FLASH) { - dev_printk(KERN_ERR, &udev->dev, "unexpected opmode %d\n", - op_mode); + dev_err(&udev->dev, "unexpected opmode %d\n", op_mode); return -EINVAL; } @@ -1275,9 +1274,9 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) size, bsize, blockno); ret = at76_load_ext_fw_block(udev, blockno, block, bsize); if (ret != bsize) { - dev_printk(KERN_ERR, &udev->dev, - "loading %dth firmware block failed: %d\n", - blockno, ret); + dev_err(&udev->dev, + "loading %dth firmware block failed: %d\n", + blockno, ret); goto exit; } buf += bsize; @@ -1293,8 +1292,8 @@ static int at76_load_external_fw(struct usb_device *udev, struct fwentry *fwe) exit: kfree(block); if (ret < 0) - dev_printk(KERN_ERR, &udev->dev, - "downloading external firmware failed: %d\n", ret); + dev_err(&udev->dev, + "downloading external firmware failed: %d\n", ret); return ret; } @@ -1308,8 +1307,8 @@ static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) need_remap ? 0 : 2 * HZ); if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, - "downloading internal fw failed with %d\n", ret); + dev_err(&udev->dev, + "downloading internal fw failed with %d\n", ret); goto exit; } @@ -1319,8 +1318,8 @@ static int at76_load_internal_fw(struct usb_device *udev, struct fwentry *fwe) if (need_remap) { ret = at76_remap(udev); if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, - "sending REMAP failed with %d\n", ret); + dev_err(&udev->dev, + "sending REMAP failed with %d\n", ret); goto exit; } } @@ -1555,11 +1554,10 @@ static struct fwentry *at76_load_firmware(struct usb_device *udev, at76_dbg(DBG_FW, "downloading firmware %s", fwe->fwname); ret = request_firmware(&fwe->fw, fwe->fwname, &udev->dev); if (ret < 0) { - dev_printk(KERN_ERR, &udev->dev, "firmware %s not found!\n", - fwe->fwname); - dev_printk(KERN_ERR, &udev->dev, - "you may need to download the firmware from " - "http://developer.berlios.de/projects/at76c503a/\n"); + dev_err(&udev->dev, "firmware %s not found!\n", + fwe->fwname); + dev_err(&udev->dev, + "you may need to download the firmware from http://developer.berlios.de/projects/at76c503a/\n"); goto exit; } @@ -1567,17 +1565,17 @@ static struct fwentry *at76_load_firmware(struct usb_device *udev, fwh = (struct at76_fw_header *)(fwe->fw->data); if (fwe->fw->size <= sizeof(*fwh)) { - dev_printk(KERN_ERR, &udev->dev, - "firmware is too short (0x%zx)\n", fwe->fw->size); + dev_err(&udev->dev, + "firmware is too short (0x%zx)\n", fwe->fw->size); goto exit; } /* CRC currently not checked */ fwe->board_type = le32_to_cpu(fwh->board_type); if (fwe->board_type != board_type) { - dev_printk(KERN_ERR, &udev->dev, - "board type mismatch, requested %u, got %u\n", - board_type, fwe->board_type); + dev_err(&udev->dev, + "board type mismatch, requested %u, got %u\n", + board_type, fwe->board_type); goto exit; } @@ -2150,8 +2148,7 @@ static int at76_alloc_urbs(struct at76_priv *priv, } if (!ep_in || !ep_out) { - dev_printk(KERN_ERR, &interface->dev, - "bulk endpoints missing\n"); + dev_err(&interface->dev, "bulk endpoints missing\n"); return -ENXIO; } @@ -2161,15 +2158,14 @@ static int at76_alloc_urbs(struct at76_priv *priv, priv->rx_urb = usb_alloc_urb(0, GFP_KERNEL); priv->tx_urb = usb_alloc_urb(0, GFP_KERNEL); if (!priv->rx_urb || !priv->tx_urb) { - dev_printk(KERN_ERR, &interface->dev, "cannot allocate URB\n"); + dev_err(&interface->dev, "cannot allocate URB\n"); return -ENOMEM; } buffer_size = sizeof(struct at76_tx_buffer) + MAX_PADDING_SIZE; priv->bulk_out_buffer = kmalloc(buffer_size, GFP_KERNEL); if (!priv->bulk_out_buffer) { - dev_printk(KERN_ERR, &interface->dev, - "cannot allocate output buffer\n"); + dev_err(&interface->dev, "cannot allocate output buffer\n"); return -ENOMEM; } @@ -2230,8 +2226,7 @@ static int at76_init_new_device(struct at76_priv *priv, /* MAC address */ ret = at76_get_hw_config(priv); if (ret < 0) { - dev_printk(KERN_ERR, &interface->dev, - "cannot get MAC address\n"); + dev_err(&interface->dev, "cannot get MAC address\n"); goto exit; } @@ -2358,8 +2353,8 @@ static int at76_probe(struct usb_interface *interface, we get 204 with 2.4.23, Fiberline FL-WL240u (505A+RFMD2958) ??? */ if (op_mode == OPMODE_HW_CONFIG_MODE) { - dev_printk(KERN_ERR, &interface->dev, - "cannot handle a device in HW_CONFIG_MODE\n"); + dev_err(&interface->dev, + "cannot handle a device in HW_CONFIG_MODE\n"); ret = -EBUSY; goto error; } @@ -2371,9 +2366,9 @@ static int at76_probe(struct usb_interface *interface, "downloading internal firmware\n"); ret = at76_load_internal_fw(udev, fwe); if (ret < 0) { - dev_printk(KERN_ERR, &interface->dev, - "error %d downloading internal firmware\n", - ret); + dev_err(&interface->dev, + "error %d downloading internal firmware\n", + ret); goto error; } usb_put_dev(udev); @@ -2408,8 +2403,8 @@ static int at76_probe(struct usb_interface *interface, /* Re-check firmware version */ ret = at76_get_mib(udev, MIB_FW_VERSION, &fwv, sizeof(fwv)); if (ret < 0) { - dev_printk(KERN_ERR, &interface->dev, - "error %d getting firmware version\n", ret); + dev_err(&interface->dev, + "error %d getting firmware version\n", ret); goto error; } } @@ -2449,7 +2444,7 @@ static void at76_disconnect(struct usb_interface *interface) wiphy_info(priv->hw->wiphy, "disconnecting\n"); at76_delete_device(priv); - dev_printk(KERN_INFO, &interface->dev, "disconnected\n"); + dev_info(&interface->dev, "disconnected\n"); } /* Structure for registering this driver with the USB subsystem */ diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index f782b6e502b..7157f7d311c 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -50,18 +50,19 @@ static void ar5523_read_reply(struct ar5523 *ar, struct ar5523_cmd_hdr *hdr, struct ar5523_tx_cmd *cmd) { int dlen, olen; - u32 *rp; + __be32 *rp; - dlen = hdr->len - sizeof(*hdr); + dlen = be32_to_cpu(hdr->len) - sizeof(*hdr); if (dlen < 0) { WARN_ON(1); goto out; } - ar5523_dbg(ar, "Code = %d len = %d\n", hdr->code & 0xff, dlen); + ar5523_dbg(ar, "Code = %d len = %d\n", be32_to_cpu(hdr->code) & 0xff, + dlen); - rp = (u32 *)(hdr + 1); + rp = (__be32 *)(hdr + 1); if (dlen >= sizeof(u32)) { olen = be32_to_cpu(rp[0]); dlen -= sizeof(u32); @@ -95,6 +96,7 @@ static void ar5523_cmd_rx_cb(struct urb *urb) struct ar5523_tx_cmd *cmd = &ar->tx_cmd; struct ar5523_cmd_hdr *hdr = ar->rx_cmd_buf; int dlen; + u32 code, hdrlen; if (urb->status) { if (urb->status != -ESHUTDOWN) @@ -110,15 +112,15 @@ static void ar5523_cmd_rx_cb(struct urb *urb) ar5523_dbg(ar, "%s code %02x priv %d\n", __func__, be32_to_cpu(hdr->code) & 0xff, hdr->priv); - hdr->code = be32_to_cpu(hdr->code); - hdr->len = be32_to_cpu(hdr->len); + code = be32_to_cpu(hdr->code); + hdrlen = be32_to_cpu(hdr->len); - switch (hdr->code & 0xff) { + switch (code & 0xff) { default: /* reply to a read command */ if (hdr->priv != AR5523_CMD_ID) { ar5523_err(ar, "Unexpected command id: %02x\n", - hdr->code & 0xff); + code & 0xff); goto skip; } ar5523_read_reply(ar, hdr, cmd); @@ -147,7 +149,7 @@ static void ar5523_cmd_rx_cb(struct urb *urb) case WDCMSG_TARGET_START: /* This command returns a bogus id so it needs special handling */ - dlen = hdr->len - sizeof(*hdr); + dlen = hdrlen - sizeof(*hdr); if (dlen != (int)sizeof(u32)) { ar5523_err(ar, "Invalid reply to WDCMSG_TARGET_START"); return; @@ -303,7 +305,7 @@ static int ar5523_config(struct ar5523 *ar, u32 reg, u32 val) write.reg = cpu_to_be32(reg); write.len = cpu_to_be32(0); /* 0 = single write */ - *(u32 *)write.data = cpu_to_be32(val); + *(__be32 *)write.data = cpu_to_be32(val); error = ar5523_cmd_write(ar, WDCMSG_TARGET_SET_CONFIG, &write, 3 * sizeof(u32), 0); @@ -335,29 +337,30 @@ static int ar5523_get_status(struct ar5523 *ar, u32 which, void *odata, int olen) { int error; + __be32 which_be; - which = cpu_to_be32(which); + which_be = cpu_to_be32(which); error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_STATUS, - &which, sizeof(which), odata, olen, AR5523_CMD_FLAG_MAGIC); + &which_be, sizeof(which_be), odata, olen, AR5523_CMD_FLAG_MAGIC); if (error != 0) - ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", - be32_to_cpu(which)); + ar5523_err(ar, "could not read EEPROM offset 0x%02x\n", which); return error; } static int ar5523_get_capability(struct ar5523 *ar, u32 cap, u32 *val) { int error; + __be32 cap_be, val_be; - cap = cpu_to_be32(cap); - error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, - &cap, sizeof(cap), val, sizeof(u32), AR5523_CMD_FLAG_MAGIC); + cap_be = cpu_to_be32(cap); + error = ar5523_cmd_read(ar, WDCMSG_TARGET_GET_CAPABILITY, &cap_be, + sizeof(cap_be), &val_be, sizeof(__be32), + AR5523_CMD_FLAG_MAGIC); if (error != 0) { - ar5523_err(ar, "could not read capability %u\n", - be32_to_cpu(cap)); + ar5523_err(ar, "could not read capability %u\n", cap); return error; } - *val = be32_to_cpu(*val); + *val = be32_to_cpu(val_be); return error; } @@ -1193,8 +1196,8 @@ static void ar5523_create_rateset(struct ar5523 *ar, if (!sta) { ar5523_info(ar, "STA not found. Cannot set rates\n"); sta_rate_set = bss_conf->basic_rates; - } - sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; + } else + sta_rate_set = sta->supp_rates[ar->hw->conf.channel->band]; ar5523_dbg(ar, "sta rate_set = %08x\n", sta_rate_set); @@ -1789,18 +1792,7 @@ static struct usb_driver ar5523_driver = { .disconnect = ar5523_disconnect, }; -static int __init ar5523_init(void) -{ - return usb_register(&ar5523_driver); -} - -static void __exit ar5523_exit(void) -{ - usb_deregister(&ar5523_driver); -} +module_usb_driver(ar5523_driver); MODULE_LICENSE("Dual BSD/GPL"); MODULE_FIRMWARE(AR5523_FIRMWARE_FILE); - -module_init(ar5523_init); -module_exit(ar5523_exit); diff --git a/drivers/net/wireless/ath/ar5523/ar5523_hw.h b/drivers/net/wireless/ath/ar5523/ar5523_hw.h index a0e8bf46031..0fe2c803f48 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523_hw.h +++ b/drivers/net/wireless/ath/ar5523/ar5523_hw.h @@ -161,7 +161,7 @@ struct ar5523_rx_desc { struct ar5523_tx_desc { __be32 msglen; - __be32 msgid; /* msg id (supplied by host) */ + u32 msgid; /* msg id (supplied by host) */ __be32 type; /* opcode: WDMSG_SEND or WDCMSG_FLUSH */ __be32 txqid; /* tx queue id and flags */ #define UATH_TXQID_MASK 0x0f diff --git a/drivers/net/wireless/ath/ath5k/ahb.c b/drivers/net/wireless/ath/ath5k/ahb.c index aec33cc207f..8e8bcc7a480 100644 --- a/drivers/net/wireless/ath/ath5k/ahb.c +++ b/drivers/net/wireless/ath/ath5k/ahb.c @@ -236,17 +236,4 @@ static struct platform_driver ath_ahb_driver = { }, }; -static int __init -ath5k_ahb_init(void) -{ - return platform_driver_register(&ath_ahb_driver); -} - -static void __exit -ath5k_ahb_exit(void) -{ - platform_driver_unregister(&ath_ahb_driver); -} - -module_init(ath5k_ahb_init); -module_exit(ath5k_ahb_exit); +module_platform_driver(ath_ahb_driver); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 9f31cfa56cc..2fd5bab2e22 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -511,8 +511,9 @@ ath5k_update_bssid_mask_and_opmode(struct ath5k_hw *ah, ath5k_vif_iter(&iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); memcpy(ah->bssidmask, iter_data.mask, ETH_ALEN); ah->opmode = iter_data.opmode; @@ -1348,7 +1349,7 @@ ath5k_receive_frame(struct ath5k_hw *ah, struct sk_buff *skb, * right now, so it's not too bad... */ rxs->mactime = ath5k_extend_tsf(ah, rs->rs_tstamp); - rxs->flag |= RX_FLAG_MACTIME_MPDU; + rxs->flag |= RX_FLAG_MACTIME_START; rxs->freq = ah->curchan->center_freq; rxs->band = ah->curchan->band; @@ -3045,8 +3046,9 @@ ath5k_any_vif_assoc(struct ath5k_hw *ah) iter_data.need_set_hw_addr = false; iter_data.found_active = true; - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); return iter_data.any_assoc; } diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c index 7a28538e6e0..1ea8c8795c8 100644 --- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c +++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c @@ -452,8 +452,9 @@ ath5k_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, iter_data.hw_macaddr = NULL; iter_data.n_stas = 0; iter_data.need_set_hw_addr = false; - ieee80211_iterate_active_interfaces_atomic(ah->hw, ath5k_vif_iter, - &iter_data); + ieee80211_iterate_active_interfaces_atomic( + ah->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath5k_vif_iter, &iter_data); /* Set up RX Filter */ if (iter_data.n_stas > 1) { diff --git a/drivers/net/wireless/ath/ath5k/reset.c b/drivers/net/wireless/ath/ath5k/reset.c index 0c2dd4771c3..4084b107628 100644 --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c @@ -789,9 +789,9 @@ ath5k_hw_nic_wakeup(struct ath5k_hw *ah, struct ieee80211_channel *channel) * (I don't think it supports 44MHz) */ /* On 2425 initvals TURBO_SHORT is not present */ if (ah->ah_bwmode == AR5K_BWMODE_40MHZ) { - turbo = AR5K_PHY_TURBO_MODE | - (ah->ah_radio == AR5K_RF2425) ? 0 : - AR5K_PHY_TURBO_SHORT; + turbo = AR5K_PHY_TURBO_MODE; + if (ah->ah_radio != AR5K_RF2425) + turbo |= AR5K_PHY_TURBO_SHORT; } else if (ah->ah_bwmode != AR5K_BWMODE_DEFAULT) { if (ah->ah_radio == AR5K_RF5413) { mode |= (ah->ah_bwmode == AR5K_BWMODE_10MHZ) ? diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index d755a5e7ed2..26c4b722085 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -30,3 +30,12 @@ config ATH6KL_DEBUG depends on ATH6KL ---help--- Enables debug support + +config ATH6KL_REGDOMAIN + bool "Atheros ath6kl regdomain support" + depends on ATH6KL + depends on CFG80211_CERTIFICATION_ONUS + ---help--- + Enabling this makes it possible to change the regdomain in + the firmware. This can be only enabled if regulatory requirements + are taken into account. diff --git a/drivers/net/wireless/ath/ath6kl/Makefile b/drivers/net/wireless/ath/ath6kl/Makefile index 8cae8886f17..cab0ec0d538 100644 --- a/drivers/net/wireless/ath/ath6kl/Makefile +++ b/drivers/net/wireless/ath/ath6kl/Makefile @@ -34,6 +34,7 @@ ath6kl_core-y += main.o ath6kl_core-y += txrx.o ath6kl_core-y += wmi.o ath6kl_core-y += core.o +ath6kl_core-y += recovery.o ath6kl_core-$(CONFIG_NL80211_TESTMODE) += testmode.o obj-$(CONFIG_ATH6KL_SDIO) += ath6kl_sdio.o diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 277089963eb..5516a8ccc3c 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -147,15 +147,15 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) { struct ath6kl *ar = vif->ar; - if (ar->state != ATH6KL_STATE_SCHED_SCAN) + if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags)) return false; del_timer_sync(&vif->sched_scan_timer); - ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); + if (ar->state == ATH6KL_STATE_RECOVERY) + return true; - ar->state = ATH6KL_STATE_ON; + ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, false); return true; } @@ -369,17 +369,13 @@ static int ath6kl_nliftype_to_drv_iftype(enum nl80211_iftype type, u8 *nw_type) { switch (type) { case NL80211_IFTYPE_STATION: + case NL80211_IFTYPE_P2P_CLIENT: *nw_type = INFRA_NETWORK; break; case NL80211_IFTYPE_ADHOC: *nw_type = ADHOC_NETWORK; break; case NL80211_IFTYPE_AP: - *nw_type = AP_NETWORK; - break; - case NL80211_IFTYPE_P2P_CLIENT: - *nw_type = INFRA_NETWORK; - break; case NL80211_IFTYPE_P2P_GO: *nw_type = AP_NETWORK; break; @@ -1031,30 +1027,15 @@ static int ath6kl_cfg80211_scan(struct wiphy *wiphy, vif->scan_req = request; - if (test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, - ar->fw_capabilities)) { - /* - * If capable of doing P2P mgmt operations using - * station interface, send additional information like - * supported rates to advertise and xmit rates for - * probe requests - */ - ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels, - request->no_cck, - request->rates); - } else { - ret = ath6kl_wmi_startscan_cmd(ar->wmi, vif->fw_vif_idx, - WMI_LONG_SCAN, force_fg_scan, - false, 0, - ATH6KL_FG_SCAN_INTERVAL, - n_channels, channels); - } + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, vif->fw_vif_idx, + WMI_LONG_SCAN, force_fg_scan, + false, 0, + ATH6KL_FG_SCAN_INTERVAL, + n_channels, channels, + request->no_cck, + request->rates); if (ret) { - ath6kl_err("wmi_startscan_cmd failed\n"); + ath6kl_err("failed to start scan: %d\n", ret); vif->scan_req = NULL; } @@ -1093,15 +1074,18 @@ out: void ath6kl_cfg80211_ch_switch_notify(struct ath6kl_vif *vif, int freq, enum wmi_phy_mode mode) { - enum nl80211_channel_type type; + struct cfg80211_chan_def chandef; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "channel switch notify nw_type %d freq %d mode %d\n", vif->nw_type, freq, mode); - type = (mode == WMI_11G_HT20) ? NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT; + cfg80211_chandef_create(&chandef, + ieee80211_get_channel(vif->ar->wiphy, freq), + (mode == WMI_11G_HT20) ? + NL80211_CHAN_HT20 : NL80211_CHAN_NO_HT); - cfg80211_ch_switch_notify(vif->ndev, freq, type); + cfg80211_ch_switch_notify(vif->ndev, &chandef); } static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, @@ -1384,11 +1368,8 @@ static int ath6kl_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return 0; } -/* - * The type nl80211_tx_power_setting replaces the following - * data type from 2.6.36 onwards -*/ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { @@ -1423,7 +1404,9 @@ static int ath6kl_cfg80211_set_txpower(struct wiphy *wiphy, return 0; } -static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, int *dbm) +static int ath6kl_cfg80211_get_txpower(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct ath6kl *ar = (struct ath6kl *)wiphy_priv(wiphy); struct ath6kl_vif *vif; @@ -1614,8 +1597,8 @@ static int ath6kl_cfg80211_join_ibss(struct wiphy *wiphy, vif->ssid_len = ibss_param->ssid_len; memcpy(vif->ssid, ibss_param->ssid, vif->ssid_len); - if (ibss_param->channel) - vif->ch_hint = ibss_param->channel->center_freq; + if (ibss_param->chandef.chan) + vif->ch_hint = ibss_param->chandef.chan->center_freq; if (ibss_param->channel_fixed) { /* @@ -1889,7 +1872,7 @@ static int ath6kl_wow_usr(struct ath6kl *ar, struct ath6kl_vif *vif, struct cfg80211_wowlan *wow, u32 *filter) { int ret, pos; - u8 mask[WOW_MASK_SIZE]; + u8 mask[WOW_PATTERN_SIZE]; u16 i; /* Configure the patterns that we received from the user. */ @@ -2107,33 +2090,16 @@ static int ath6kl_cfg80211_host_sleep(struct ath6kl *ar, struct ath6kl_vif *vif) return ret; } -static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +static int ath6kl_wow_suspend_vif(struct ath6kl_vif *vif, + struct cfg80211_wowlan *wow, u32 *filter) { + struct ath6kl *ar = vif->ar; struct in_device *in_dev; struct in_ifaddr *ifa; - struct ath6kl_vif *vif; int ret; - u32 filter = 0; u16 i, bmiss_time; - u8 index = 0; __be32 ips[MAX_IP_ADDRS]; - - /* The FW currently can't support multi-vif WoW properly. */ - if (ar->num_vif > 1) - return -EIO; - - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; - - if (!ath6kl_cfg80211_ready(vif)) - return -EIO; - - if (!test_bit(CONNECTED, &vif->flags)) - return -ENOTCONN; - - if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) - return -EINVAL; + u8 index = 0; if (!test_bit(NETDEV_MCAST_ALL_ON, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, @@ -2155,7 +2121,7 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) * the user. */ if (wow) - ret = ath6kl_wow_usr(ar, vif, wow, &filter); + ret = ath6kl_wow_usr(ar, vif, wow, filter); else if (vif->nw_type == AP_NETWORK) ret = ath6kl_wow_ap(ar, vif); else @@ -2190,12 +2156,10 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } - ar->state = ATH6KL_STATE_SUSPENDING; - /* Setup own IP addr for ARP agent. */ in_dev = __in_dev_get_rtnl(vif->ndev); if (!in_dev) - goto skip_arp; + return 0; ifa = in_dev->ifa_list; memset(&ips, 0, sizeof(ips)); @@ -2218,41 +2182,61 @@ static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) return ret; } -skip_arp: - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, + return ret; +} + +static int ath6kl_wow_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) +{ + struct ath6kl_vif *first_vif, *vif; + int ret = 0; + u32 filter = 0; + bool connected = false; + + /* enter / leave wow suspend on first vif always */ + first_vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!first_vif)) || + !ath6kl_cfg80211_ready(first_vif)) + return -EIO; + + if (wow && (wow->n_patterns > WOW_MAX_FILTERS_PER_LIST)) + return -EINVAL; + + /* install filters for each connected vif */ + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) + continue; + connected = true; + + ret = ath6kl_wow_suspend_vif(vif, wow, &filter); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); + + if (!connected) + return -ENOTCONN; + else if (ret) + return ret; + + ar->state = ATH6KL_STATE_SUSPENDING; + + ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, first_vif->fw_vif_idx, ATH6KL_WOW_MODE_ENABLE, filter, WOW_HOST_REQ_DELAY); if (ret) return ret; - ret = ath6kl_cfg80211_host_sleep(ar, vif); - if (ret) - return ret; - - return 0; + return ath6kl_cfg80211_host_sleep(ar, first_vif); } -static int ath6kl_wow_resume(struct ath6kl *ar) +static int ath6kl_wow_resume_vif(struct ath6kl_vif *vif) { - struct ath6kl_vif *vif; + struct ath6kl *ar = vif->ar; int ret; - vif = ath6kl_vif_first(ar); - if (!vif) - return -EIO; - - ar->state = ATH6KL_STATE_RESUMING; - - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_AWAKE); - if (ret) { - ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", - ret); - ar->state = ATH6KL_STATE_WOW; - return ret; - } - if (vif->nw_type != AP_NETWORK) { ret = ath6kl_wmi_scanparams_cmd(ar->wmi, vif->fw_vif_idx, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0); @@ -2270,13 +2254,11 @@ static int ath6kl_wow_resume(struct ath6kl *ar) return ret; } - ar->state = ATH6KL_STATE_ON; - if (!test_bit(NETDEV_MCAST_ALL_OFF, &vif->flags) && test_bit(ATH6KL_FW_CAPABILITY_WOW_MULTICAST_FILTER, ar->fw_capabilities)) { ret = ath6kl_wmi_mcast_filter_cmd(vif->ar->wmi, - vif->fw_vif_idx, true); + vif->fw_vif_idx, true); if (ret) return ret; } @@ -2286,6 +2268,48 @@ static int ath6kl_wow_resume(struct ath6kl *ar) return 0; } +static int ath6kl_wow_resume(struct ath6kl *ar) +{ + struct ath6kl_vif *vif; + int ret; + + vif = ath6kl_vif_first(ar); + if (WARN_ON(unlikely(!vif)) || + !ath6kl_cfg80211_ready(vif)) + return -EIO; + + ar->state = ATH6KL_STATE_RESUMING; + + ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, + ATH6KL_HOST_MODE_AWAKE); + if (ret) { + ath6kl_warn("Failed to configure host sleep mode for wow resume: %d\n", + ret); + goto cleanup; + } + + spin_lock_bh(&ar->list_lock); + list_for_each_entry(vif, &ar->vif_list, list) { + if (!test_bit(CONNECTED, &vif->flags) || + !ath6kl_cfg80211_ready(vif)) + continue; + ret = ath6kl_wow_resume_vif(vif); + if (ret) + break; + } + spin_unlock_bh(&ar->list_lock); + + if (ret) + goto cleanup; + + ar->state = ATH6KL_STATE_ON; + return 0; + +cleanup: + ar->state = ATH6KL_STATE_WOW; + return ret; +} + static int ath6kl_cfg80211_deepsleep_suspend(struct ath6kl *ar) { struct ath6kl_vif *vif; @@ -2422,13 +2446,6 @@ int ath6kl_cfg80211_suspend(struct ath6kl *ar, break; - case ATH6KL_CFG_SUSPEND_SCHED_SCAN: - /* - * Nothing needed for schedule scan, firmware is already in - * wow mode and sleeping most of the time. - */ - break; - default: break; } @@ -2476,9 +2493,6 @@ int ath6kl_cfg80211_resume(struct ath6kl *ar) } break; - case ATH6KL_STATE_SCHED_SCAN: - break; - default: break; } @@ -2495,14 +2509,23 @@ static int __ath6kl_cfg80211_suspend(struct wiphy *wiphy, { struct ath6kl *ar = wiphy_priv(wiphy); + ath6kl_recovery_suspend(ar); + return ath6kl_hif_suspend(ar, wow); } static int __ath6kl_cfg80211_resume(struct wiphy *wiphy) { struct ath6kl *ar = wiphy_priv(wiphy); + int err; + + err = ath6kl_hif_resume(ar); + if (err) + return err; - return ath6kl_hif_resume(ar); + ath6kl_recovery_resume(ar); + + return 0; } /* @@ -2739,6 +2762,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, int res; int i, ret; u16 rsn_capab = 0; + int inactivity_timeout = 0; ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s:\n", __func__); @@ -2857,7 +2881,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, p.ssid_len = vif->ssid_len; memcpy(p.ssid, vif->ssid, vif->ssid_len); p.dot11_auth_mode = vif->dot11_auth_mode; - p.ch = cpu_to_le16(info->channel->center_freq); + p.ch = cpu_to_le16(info->chandef.chan->center_freq); /* Enable uAPSD support by default */ res = ath6kl_wmi_ap_set_apsd(ar->wmi, vif->fw_vif_idx, true); @@ -2875,14 +2899,22 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, } if (info->inactivity_timeout) { + + inactivity_timeout = info->inactivity_timeout; + + if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS) + inactivity_timeout = DIV_ROUND_UP(inactivity_timeout, + 60); + res = ath6kl_wmi_set_inact_period(ar->wmi, vif->fw_vif_idx, - info->inactivity_timeout); + inactivity_timeout); if (res < 0) return res; } - if (ath6kl_set_htcap(vif, info->channel->band, - info->channel_type != NL80211_CHAN_NO_HT)) + if (ath6kl_set_htcap(vif, info->chandef.chan->band, + cfg80211_get_chandef_type(&info->chandef) + != NL80211_CHAN_NO_HT)) return -EIO; /* @@ -2898,6 +2930,7 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, WLAN_EID_RSN, WMI_RSN_IE_CAPB, (const u8 *) &rsn_capab, sizeof(rsn_capab)); + vif->rsn_capab = rsn_capab; if (res < 0) return res; } @@ -2977,7 +3010,6 @@ static int ath6kl_change_station(struct wiphy *wiphy, struct net_device *dev, static int ath6kl_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { @@ -3136,10 +3168,8 @@ static bool ath6kl_is_p2p_go_ssid(const u8 *buf, size_t len) static int ath6kl_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ath6kl_vif *vif = ath6kl_vif_from_wdev(wdev); struct ath6kl *ar = ath6kl_priv(vif->ndev); @@ -3211,7 +3241,7 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, struct ath6kl *ar = ath6kl_priv(dev); struct ath6kl_vif *vif = netdev_priv(dev); u16 interval; - int ret; + int ret, rssi_thold; if (ar->state != ATH6KL_STATE_ON) return -EIO; @@ -3219,10 +3249,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, if (vif->sme_state != SME_DISCONNECTED) return -EBUSY; - /* The FW currently can't support multi-vif WoW properly. */ - if (ar->num_vif > 1) - return -EIO; - ath6kl_cfg80211_scan_complete_event(vif, true); ret = ath6kl_set_probed_ssids(ar, vif, request->ssids, @@ -3244,6 +3270,23 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, return ret; } + if (test_bit(ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + ar->fw_capabilities)) { + if (request->rssi_thold <= NL80211_SCAN_RSSI_THOLD_OFF) + rssi_thold = 0; + else if (request->rssi_thold < -127) + rssi_thold = -127; + else + rssi_thold = request->rssi_thold; + + ret = ath6kl_wmi_set_rssi_filter_cmd(ar->wmi, vif->fw_vif_idx, + rssi_thold); + if (ret) { + ath6kl_err("failed to set RSSI threshold for scan\n"); + return ret; + } + } + /* fw uses seconds, also make sure that it's >0 */ interval = max_t(u16, 1, request->interval / 1000); @@ -3251,15 +3294,6 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, interval, interval, vif->bg_scan_period, 0, 0, 0, 3, 0, 0, 0); - ret = ath6kl_wmi_set_wow_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_WOW_MODE_ENABLE, - WOW_FILTER_SSID, - WOW_HOST_REQ_DELAY); - if (ret) { - ath6kl_warn("Failed to enable wow with ssid filter: %d\n", ret); - return ret; - } - /* this also clears IE in fw if it's not set */ ret = ath6kl_wmi_set_appie_cmd(ar->wmi, vif->fw_vif_idx, WMI_FRAME_PROBE_REQ, @@ -3270,17 +3304,13 @@ static int ath6kl_cfg80211_sscan_start(struct wiphy *wiphy, return ret; } - ret = ath6kl_wmi_set_host_sleep_mode_cmd(ar->wmi, vif->fw_vif_idx, - ATH6KL_HOST_MODE_ASLEEP); - if (ret) { - ath6kl_warn("Failed to enable host sleep mode for sched scan: %d\n", - ret); + ret = ath6kl_wmi_enable_sched_scan_cmd(ar->wmi, vif->fw_vif_idx, true); + if (ret) return ret; - } - ar->state = ATH6KL_STATE_SCHED_SCAN; + set_bit(SCHED_SCANNING, &vif->flags); - return ret; + return 0; } static int ath6kl_cfg80211_sscan_stop(struct wiphy *wiphy, @@ -3309,6 +3339,27 @@ static int ath6kl_cfg80211_set_bitrate(struct wiphy *wiphy, mask); } +static int ath6kl_cfg80211_set_txe_config(struct wiphy *wiphy, + struct net_device *dev, + u32 rate, u32 pkts, u32 intvl) +{ + struct ath6kl *ar = ath6kl_priv(dev); + struct ath6kl_vif *vif = netdev_priv(dev); + + if (vif->nw_type != INFRA_NETWORK || + !test_bit(ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, ar->fw_capabilities)) + return -EOPNOTSUPP; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + /* save this since the firmware won't report the interval */ + vif->txe_intvl = intvl; + + return ath6kl_wmi_set_txe_notify(ar->wmi, vif->fw_vif_idx, + rate, pkts, intvl); +} + static const struct ieee80211_txrx_stypes ath6kl_mgmt_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { @@ -3375,6 +3426,7 @@ static struct cfg80211_ops ath6kl_cfg80211_ops = { .sched_scan_start = ath6kl_cfg80211_sscan_start, .sched_scan_stop = ath6kl_cfg80211_sscan_stop, .set_bitrate_mask = ath6kl_cfg80211_set_bitrate, + .set_cqm_txe_config = ath6kl_cfg80211_set_txe_config, }; void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) @@ -3395,16 +3447,22 @@ void ath6kl_cfg80211_stop(struct ath6kl_vif *vif) break; } - if (test_bit(CONNECTED, &vif->flags) || - test_bit(CONNECT_PEND, &vif->flags)) + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + (test_bit(CONNECTED, &vif->flags) || + test_bit(CONNECT_PEND, &vif->flags))) ath6kl_wmi_disconnect_cmd(vif->ar->wmi, vif->fw_vif_idx); vif->sme_state = SME_DISCONNECTED; clear_bit(CONNECTED, &vif->flags); clear_bit(CONNECT_PEND, &vif->flags); + /* Stop netdev queues, needed during recovery */ + netif_stop_queue(vif->ndev); + netif_carrier_off(vif->ndev); + /* disable scanning */ - if (ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, + if (vif->ar->state != ATH6KL_STATE_RECOVERY && + ath6kl_wmi_scanparams_cmd(vif->ar->wmi, vif->fw_vif_idx, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0) ath6kl_warn("failed to disable scan during stop\n"); @@ -3416,7 +3474,7 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) struct ath6kl_vif *vif; vif = ath6kl_vif_first(ar); - if (!vif) { + if (!vif && ar->state != ATH6KL_STATE_RECOVERY) { /* save the current power mode before enabling power save */ ar->wmi->saved_pwr_mode = ar->wmi->pwr_mode; @@ -3434,6 +3492,56 @@ void ath6kl_cfg80211_stop_all(struct ath6kl *ar) ath6kl_cfg80211_stop(vif); } +static int ath6kl_cfg80211_reg_notify(struct wiphy *wiphy, + struct regulatory_request *request) +{ + struct ath6kl *ar = wiphy_priv(wiphy); + u32 rates[IEEE80211_NUM_BANDS]; + int ret, i; + + ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, + "cfg reg_notify %c%c%s%s initiator %d hint_type %d\n", + request->alpha2[0], request->alpha2[1], + request->intersect ? " intersect" : "", + request->processed ? " processed" : "", + request->initiator, request->user_reg_hint_type); + + /* + * As firmware is not able intersect regdoms, we can only listen to + * cellular hints. + */ + if (request->user_reg_hint_type != NL80211_USER_REG_HINT_CELL_BASE) + return -EOPNOTSUPP; + + ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, request->alpha2); + if (ret) { + ath6kl_err("failed to set regdomain: %d\n", ret); + return ret; + } + + /* + * Firmware will apply the regdomain change only after a scan is + * issued and it will send a WMI_REGDOMAIN_EVENTID when it has been + * changed. + */ + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) + if (wiphy->bands[i]) + rates[i] = (1 << wiphy->bands[i]->n_bitrates) - 1; + + + ret = ath6kl_wmi_beginscan_cmd(ar->wmi, 0, WMI_LONG_SCAN, false, + false, 0, ATH6KL_FG_SCAN_INTERVAL, + 0, NULL, false, rates); + if (ret) { + ath6kl_err("failed to start scan for a regdomain change: %d\n", + ret); + return ret; + } + + return 0; +} + static int ath6kl_cfg80211_vif_init(struct ath6kl_vif *vif) { vif->aggr_cntxt = aggr_init(vif); @@ -3506,9 +3614,13 @@ struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, vif->htcap[IEEE80211_BAND_5GHZ].ht_enable = true; memcpy(ndev->dev_addr, ar->mac_addr, ETH_ALEN); - if (fw_vif_idx != 0) + if (fw_vif_idx != 0) { ndev->dev_addr[0] = (ndev->dev_addr[0] ^ (1 << fw_vif_idx)) | 0x2; + if (test_bit(ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + ar->fw_capabilities)) + ndev->dev_addr[4] ^= 0x80; + } init_netdev(ndev); @@ -3562,6 +3674,12 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) BIT(NL80211_IFTYPE_P2P_CLIENT); } + if (config_enabled(CONFIG_ATH6KL_REGDOMAIN) && + test_bit(ATH6KL_FW_CAPABILITY_REGDOMAIN, ar->fw_capabilities)) { + wiphy->reg_notifier = ath6kl_cfg80211_reg_notify; + ar->wiphy->features |= NL80211_FEATURE_CELL_BASE_REG_HINTS; + } + /* max num of ssids that can be probed during scanning */ wiphy->max_scan_ssids = MAX_PROBED_SSIDS; @@ -3607,7 +3725,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.ht_supported = false; } - if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) { + if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; @@ -3646,7 +3764,7 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; - if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN, ar->fw_capabilities)) + if (test_bit(ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, ar->fw_capabilities)) ar->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; if (test_bit(ATH6KL_FW_CAPABILITY_INACTIVITY_TIMEOUT, diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.h b/drivers/net/wireless/ath/ath6kl/cfg80211.h index 780f77775a9..e5e70f3a8ca 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.h +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.h @@ -22,7 +22,6 @@ enum ath6kl_cfg_suspend_mode { ATH6KL_CFG_SUSPEND_DEEPSLEEP, ATH6KL_CFG_SUSPEND_CUTPOWER, ATH6KL_CFG_SUSPEND_WOW, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, }; struct wireless_dev *ath6kl_interface_add(struct ath6kl *ar, const char *name, diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 82c4dd2a960..4b46adbe8c9 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -33,6 +33,8 @@ static unsigned int wow_mode; static unsigned int uart_debug; static unsigned int ath6kl_p2p; static unsigned int testmode; +static unsigned int recovery_enable; +static unsigned int heart_beat_poll; module_param(debug_mask, uint, 0644); module_param(suspend_mode, uint, 0644); @@ -40,6 +42,12 @@ module_param(wow_mode, uint, 0644); module_param(uart_debug, uint, 0644); module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); +module_param(recovery_enable, uint, 0644); +module_param(heart_beat_poll, uint, 0644); +MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error"); +MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic" \ + "polling. This also specifies the polling interval in" \ + "msecs. Set reocvery_enable for this to be effective"); void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { @@ -202,6 +210,17 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ath6kl_dbg(ATH6KL_DBG_TRC, "%s: name=%s dev=0x%p, ar=0x%p\n", __func__, wdev->netdev->name, wdev->netdev, ar); + ar->fw_recovery.enable = !!recovery_enable; + if (!ar->fw_recovery.enable) + return ret; + + if (heart_beat_poll && + test_bit(ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + ar->fw_capabilities)) + ar->fw_recovery.hb_poll = heart_beat_poll; + + ath6kl_recovery_init(ar); + return ret; err_rxbuf_cleanup: @@ -291,6 +310,8 @@ void ath6kl_core_cleanup(struct ath6kl *ar) { ath6kl_hif_power_off(ar); + ath6kl_recovery_cleanup(ar); + destroy_workqueue(ar->ath6kl_wq); if (ar->htc_target) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index cec49a31029..189d8faf8c8 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -115,6 +115,27 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_SCHED_SCAN_MATCH_LIST, + /* Firmware supports filtering BSS results by RSSI */ + ATH6KL_FW_CAPABILITY_RSSI_SCAN_THOLD, + + /* FW sets mac_addr[4] ^= 0x80 for newly created interfaces */ + ATH6KL_FW_CAPABILITY_CUSTOM_MAC_ADDR, + + /* Firmware supports TX error rate notification */ + ATH6KL_FW_CAPABILITY_TX_ERR_NOTIFY, + + /* supports WMI_SET_REGDOMAIN_CMDID command */ + ATH6KL_FW_CAPABILITY_REGDOMAIN, + + /* Firmware supports sched scan decoupled from host sleep */ + ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, + + /* + * Firmware capability for hang detection through heart beat + * challenge messages. + */ + ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -128,11 +149,15 @@ struct ath6kl_fw_ie { }; enum ath6kl_hw_flags { - ATH6KL_HW_FLAG_64BIT_RATES = BIT(0), + ATH6KL_HW_64BIT_RATES = BIT(0), + ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1), + ATH6KL_HW_MAP_LP_ENDPOINT = BIT(2), + ATH6KL_HW_SDIO_CRC_ERROR_WAR = BIT(3), }; #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" +#define ATH6KL_FW_API4_FILE "fw-4.bin" /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba @@ -186,6 +211,13 @@ enum ath6kl_hw_flags { #define AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE \ AR6004_HW_1_2_FW_DIR "/bdata.bin" +/* AR6004 1.3 definitions */ +#define AR6004_HW_1_3_VERSION 0x31c8088a +#define AR6004_HW_1_3_FW_DIR "ath6k/AR6004/hw1.3" +#define AR6004_HW_1_3_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_1_3_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" +#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" + /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) #define STA_PS_SLEEP BIT(1) @@ -536,6 +568,7 @@ enum ath6kl_vif_state { HOST_SLEEP_MODE_CMD_PROCESSED, NETDEV_MCAST_ALL_ON, NETDEV_MCAST_ALL_OFF, + SCHED_SCANNING, }; struct ath6kl_vif { @@ -580,11 +613,13 @@ struct ath6kl_vif { u16 assoc_bss_beacon_int; u16 listen_intvl_t; u16 bmiss_time_t; + u32 txe_intvl; u16 bg_scan_period; u8 assoc_bss_dtim_period; struct net_device_stats net_stats; struct target_stats target_stats; struct wmi_connect_cmd profile; + u16 rsn_capab; struct list_head mc_filter; }; @@ -609,6 +644,7 @@ enum ath6kl_dev_state { SKIP_SCAN, ROAM_TBL_PEND, FIRST_BOOT, + RECOVERY_CLEANUP, }; enum ath6kl_state { @@ -619,7 +655,16 @@ enum ath6kl_state { ATH6KL_STATE_DEEPSLEEP, ATH6KL_STATE_CUTPOWER, ATH6KL_STATE_WOW, - ATH6KL_STATE_SCHED_SCAN, + ATH6KL_STATE_RECOVERY, +}; + +/* Fw error recovery */ +#define ATH6KL_HB_RESP_MISS_THRES 5 + +enum ath6kl_fw_err { + ATH6KL_FW_ASSERT, + ATH6KL_FW_HB_RESP_FAILURE, + ATH6KL_FW_EP_FULL, }; struct ath6kl { @@ -679,6 +724,7 @@ struct ath6kl { struct ath6kl_req_key ap_mode_bkey; struct sk_buff_head mcastpsq; u32 want_ch_switch; + u16 last_ch; /* * FIXME: protects access to mcastpsq but is actually useless as @@ -764,6 +810,17 @@ struct ath6kl { bool wiphy_registered; + struct ath6kl_fw_recovery { + struct work_struct recovery_work; + unsigned long err_reason; + unsigned long hb_poll; + struct timer_list hb_timer; + u32 seq_num; + bool hb_pending; + u8 hb_misscnt; + bool enable; + } fw_recovery; + #ifdef CONFIG_ATH6KL_DEBUG struct { struct sk_buff_head fwlog_queue; @@ -899,4 +956,12 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type); void ath6kl_core_cleanup(struct ath6kl *ar); void ath6kl_core_destroy(struct ath6kl *ar); +/* Fw error recovery */ +void ath6kl_init_hw_restart(struct ath6kl *ar); +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason); +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie); +void ath6kl_recovery_init(struct ath6kl *ar); +void ath6kl_recovery_cleanup(struct ath6kl *ar); +void ath6kl_recovery_suspend(struct ath6kl *ar); +void ath6kl_recovery_resume(struct ath6kl *ar); #endif /* CORE_H */ diff --git a/drivers/net/wireless/ath/ath6kl/debug.h b/drivers/net/wireless/ath/ath6kl/debug.h index 49639d8266c..f97cd4ead54 100644 --- a/drivers/net/wireless/ath/ath6kl/debug.h +++ b/drivers/net/wireless/ath/ath6kl/debug.h @@ -44,6 +44,7 @@ enum ATH6K_DEBUG_MASK { ATH6KL_DBG_SUSPEND = BIT(20), ATH6KL_DBG_USB = BIT(21), ATH6KL_DBG_USB_BULK = BIT(22), + ATH6KL_DBG_RECOVERY = BIT(23), ATH6KL_DBG_ANY = 0xffffffff /* enable all logs */ }; diff --git a/drivers/net/wireless/ath/ath6kl/hif.c b/drivers/net/wireless/ath/ath6kl/hif.c index 68ed6c2665b..a6b614421fa 100644 --- a/drivers/net/wireless/ath/ath6kl/hif.c +++ b/drivers/net/wireless/ath/ath6kl/hif.c @@ -136,6 +136,7 @@ static int ath6kl_hif_proc_dbg_intr(struct ath6kl_device *dev) ath6kl_hif_dump_fw_crash(dev->ar); ath6kl_read_fwlogs(dev->ar); + ath6kl_recovery_err_notify(dev->ar, ATH6KL_FW_ASSERT); return ret; } @@ -338,8 +339,7 @@ static int ath6kl_hif_proc_err_intr(struct ath6kl_device *dev) status = hif_read_write_sync(dev->ar, ERROR_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - if (status) - WARN_ON(1); + WARN_ON(status); return status; } @@ -383,8 +383,7 @@ static int ath6kl_hif_proc_cpu_intr(struct ath6kl_device *dev) status = hif_read_write_sync(dev->ar, CPU_INT_STATUS_ADDRESS, reg_buf, 4, HIF_WR_SYNC_BYTE_FIX); - if (status) - WARN_ON(1); + WARN_ON(status); return status; } @@ -695,11 +694,6 @@ int ath6kl_hif_setup(struct ath6kl_device *dev) ath6kl_dbg(ATH6KL_DBG_HIF, "hif block size %d mbox addr 0x%x\n", dev->htc_cnxt->block_sz, dev->ar->mbox_info.htc_addr); - /* usb doesn't support enabling interrupts */ - /* FIXME: remove check once USB support is implemented */ - if (dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) - return 0; - status = ath6kl_hif_disable_intrs(dev); fail_setup: diff --git a/drivers/net/wireless/ath/ath6kl/htc_mbox.c b/drivers/net/wireless/ath/ath6kl/htc_mbox.c index cd0e1ba410d..fbb78dfe078 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_mbox.c +++ b/drivers/net/wireless/ath/ath6kl/htc_mbox.c @@ -2492,7 +2492,8 @@ static int ath6kl_htc_mbox_conn_service(struct htc_target *target, max_msg_sz = le16_to_cpu(resp_msg->max_msg_sz); } - if (assigned_ep >= ENDPOINT_MAX || !max_msg_sz) { + if (WARN_ON_ONCE(assigned_ep == ENDPOINT_UNUSED || + assigned_ep >= ENDPOINT_MAX || !max_msg_sz)) { status = -ENOMEM; goto fail_tx; } @@ -2655,12 +2656,6 @@ static int ath6kl_htc_mbox_wait_target(struct htc_target *target) struct htc_service_connect_resp resp; int status; - /* FIXME: remove once USB support is implemented */ - if (target->dev->ar->hif_type == ATH6KL_HIF_TYPE_USB) { - ath6kl_err("HTC doesn't support USB yet. Patience!\n"); - return -EOPNOTSUPP; - } - /* we should be getting 1 control message that the target is ready */ packet = htc_wait_for_ctrl_msg(target); @@ -2890,9 +2885,7 @@ static void ath6kl_htc_mbox_cleanup(struct htc_target *target) { struct htc_packet *packet, *tmp_packet; - /* FIXME: remove check once USB support is implemented */ - if (target->dev->ar->hif_type != ATH6KL_HIF_TYPE_USB) - ath6kl_hif_cleanup_scatter(target->dev->ar); + ath6kl_hif_cleanup_scatter(target->dev->ar); list_for_each_entry_safe(packet, tmp_packet, &target->free_ctrl_txbuf, list) { diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index f9626c72369..ba6bd497b78 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -374,9 +374,8 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, packet = list_first_entry(txq, struct htc_packet, list); - list_del(&packet->list); - /* insert into local queue */ - list_add_tail(&packet->list, &send_queue); + /* move to local queue */ + list_move_tail(&packet->list, &send_queue); } /* @@ -399,11 +398,10 @@ static enum htc_send_queue_result htc_try_send(struct htc_target *target, * for cleanup */ } else { /* callback wants to keep this packet, - * remove from caller's queue */ - list_del(&packet->list); - /* put it in the send queue */ - list_add_tail(&packet->list, - &send_queue); + * move from caller's queue to the send + * queue */ + list_move_tail(&packet->list, + &send_queue); } } diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index f90b5db741c..f21fa322e5c 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -42,7 +42,7 @@ static const struct ath6kl_hw hw_list[] = { .reserved_ram_size = 6912, .refclk_hz = 26000000, .uarttx_pin = 8, - .flags = 0, + .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, /* hw2.0 needs override address hardcoded */ .app_start_override_addr = 0x944C00, @@ -68,7 +68,7 @@ static const struct ath6kl_hw hw_list[] = { .refclk_hz = 26000000, .uarttx_pin = 8, .testscript_addr = 0x57ef74, - .flags = 0, + .flags = ATH6KL_HW_SDIO_CRC_ERROR_WAR, .fw = { .dir = AR6003_HW_2_1_1_FW_DIR, @@ -93,7 +93,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -113,8 +114,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, - + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_1_FW_DIR, .fw = AR6004_HW_1_1_FIRMWARE_FILE, @@ -133,7 +134,8 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_FLAG_64BIT_RATES, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS, .fw = { .dir = AR6004_HW_1_2_FW_DIR, @@ -142,6 +144,28 @@ static const struct ath6kl_hw hw_list[] = { .fw_board = AR6004_HW_1_2_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE, }, + { + .id = AR6004_HW_1_3_VERSION, + .name = "ar6004 hw 1.3", + .dataset_patch_addr = 0x437860, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0x437000, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .refclk_hz = 40000000, + .uarttx_pin = 11, + .flags = ATH6KL_HW_64BIT_RATES | + ATH6KL_HW_AP_INACTIVITY_MINS | + ATH6KL_HW_MAP_LP_ENDPOINT, + + .fw = { + .dir = AR6004_HW_1_3_FW_DIR, + .fw = AR6004_HW_1_3_FIRMWARE_FILE, + }, + + .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, + }, }; /* @@ -337,7 +361,7 @@ static int ath6kl_init_service_ep(struct ath6kl *ar) if (ath6kl_connectservice(ar, &connect, "WMI DATA BK")) return -EIO; - /* connect to Video service, map this to to HI PRI */ + /* connect to Video service, map this to HI PRI */ connect.svc_id = WMI_DATA_VI_SVC; if (ath6kl_connectservice(ar, &connect, "WMI DATA VI")) return -EIO; @@ -1088,6 +1112,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar) if (ret) return ret; + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE); + if (ret == 0) { + ar->fw_api = 4; + goto out; + } + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API3_FILE); if (ret == 0) { ar->fw_api = 3; @@ -1401,8 +1431,7 @@ static int ath6kl_init_upload(struct ath6kl *ar) return status; /* WAR to avoid SDIO CRC err */ - if (ar->version.target_ver == AR6003_HW_2_0_VERSION || - ar->version.target_ver == AR6003_HW_2_1_1_VERSION) { + if (ar->hw.flags & ATH6KL_HW_SDIO_CRC_ERROR_WAR) { ath6kl_err("temporary war to avoid sdio crc error\n"); param = 0x28; @@ -1520,7 +1549,7 @@ static const char *ath6kl_init_get_hif_name(enum ath6kl_hif_type type) return NULL; } -int ath6kl_init_hw_start(struct ath6kl *ar) +static int __ath6kl_init_hw_start(struct ath6kl *ar) { long timeleft; int ret, i; @@ -1616,8 +1645,6 @@ int ath6kl_init_hw_start(struct ath6kl *ar) goto err_htc_stop; } - ar->state = ATH6KL_STATE_ON; - return 0; err_htc_stop: @@ -1630,7 +1657,18 @@ err_power_off: return ret; } -int ath6kl_init_hw_stop(struct ath6kl *ar) +int ath6kl_init_hw_start(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_start(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_ON; + return 0; +} + +static int __ath6kl_init_hw_stop(struct ath6kl *ar) { int ret; @@ -1646,11 +1684,37 @@ int ath6kl_init_hw_stop(struct ath6kl *ar) if (ret) ath6kl_warn("failed to power off hif: %d\n", ret); - ar->state = ATH6KL_STATE_OFF; + return 0; +} +int ath6kl_init_hw_stop(struct ath6kl *ar) +{ + int err; + + err = __ath6kl_init_hw_stop(ar); + if (err) + return err; + ar->state = ATH6KL_STATE_OFF; return 0; } +void ath6kl_init_hw_restart(struct ath6kl *ar) +{ + clear_bit(WMI_READY, &ar->flag); + + ath6kl_cfg80211_stop_all(ar); + + if (__ath6kl_init_hw_stop(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to stop during fw error recovery\n"); + return; + } + + if (__ath6kl_init_hw_start(ar)) { + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Failed to restart during fw error recovery\n"); + return; + } +} + /* FIXME: move this to cfg80211.c and rename to ath6kl_cfg80211_vif_stop() */ void ath6kl_cleanup_vif(struct ath6kl_vif *vif, bool wmi_ready) { diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index c189e28e86a..bd50b6b7b49 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -293,13 +293,17 @@ int ath6kl_read_fwlogs(struct ath6kl *ar) } address = TARG_VTOP(ar->target_type, debug_hdr_addr); - ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + ret = ath6kl_diag_read(ar, address, &debug_hdr, sizeof(debug_hdr)); + if (ret) + goto out; address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_hdr.dbuf_addr)); firstbuf = address; dropped = le32_to_cpu(debug_hdr.dropped); - ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + ret = ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + if (ret) + goto out; loop = 100; @@ -322,7 +326,8 @@ int ath6kl_read_fwlogs(struct ath6kl *ar) address = TARG_VTOP(ar->target_type, le32_to_cpu(debug_buf.next)); - ath6kl_diag_read(ar, address, &debug_buf, sizeof(debug_buf)); + ret = ath6kl_diag_read(ar, address, &debug_buf, + sizeof(debug_buf)); if (ret) goto out; @@ -436,12 +441,9 @@ void ath6kl_connect_ap_mode_bss(struct ath6kl_vif *vif, u16 channel) break; } - if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) { - ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + if (ar->last_ch != channel) /* we actually don't know the phymode, default to HT20 */ - ath6kl_cfg80211_ch_switch_notify(vif, channel, - WMI_11G_HT20); - } + ath6kl_cfg80211_ch_switch_notify(vif, channel, WMI_11G_HT20); ath6kl_wmi_bssfilter_cmd(ar->wmi, vif->fw_vif_idx, NONE_BSS_FILTER, 0); set_bit(CONNECTED, &vif->flags); @@ -606,6 +608,18 @@ static int ath6kl_commit_ch_switch(struct ath6kl_vif *vif, u16 channel) switch (vif->nw_type) { case AP_NETWORK: + /* + * reconfigure any saved RSN IE capabilites in the beacon / + * probe response to stay in sync with the supplicant. + */ + if (vif->rsn_capab && + test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) + ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, WMI_RSN_IE_CAPB, + (const u8 *) &vif->rsn_capab, + sizeof(vif->rsn_capab)); + return ath6kl_wmi_ap_profile_commit(ar->wmi, vif->fw_vif_idx, &vif->profile); default: @@ -628,6 +642,9 @@ static void ath6kl_check_ch_switch(struct ath6kl *ar, u16 channel) if (ar->want_ch_switch & (1 << vif->fw_vif_idx)) res = ath6kl_commit_ch_switch(vif, channel); + /* if channel switch failed, oh well we tried */ + ar->want_ch_switch &= ~(1 << vif->fw_vif_idx); + if (res) ath6kl_err("channel switch failed nw_type %d res %d\n", vif->nw_type, res); @@ -981,8 +998,25 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, if (vif->nw_type == AP_NETWORK) { /* disconnect due to other STA vif switching channels */ if (reason == BSS_DISCONNECTED && - prot_reason_status == WMI_AP_REASON_STA_ROAM) + prot_reason_status == WMI_AP_REASON_STA_ROAM) { ar->want_ch_switch |= 1 << vif->fw_vif_idx; + /* bail back to this channel if STA vif fails connect */ + ar->last_ch = le16_to_cpu(vif->profile.ch); + } + + if (prot_reason_status == WMI_AP_REASON_MAX_STA) { + /* send max client reached notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_MAX_CLIENTS, + GFP_KERNEL); + } + + if (prot_reason_status == WMI_AP_REASON_ACL) { + /* send blocked client notification to user space */ + cfg80211_conn_failed(vif->ndev, bssid, + NL80211_CONN_FAIL_BLOCKED_CLIENT, + GFP_KERNEL); + } if (!ath6kl_remove_sta(ar, bssid, prot_reason_status)) return; @@ -1041,6 +1075,9 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, } } + /* restart disconnected concurrent vifs waiting for new channel */ + ath6kl_check_ch_switch(ar, ar->last_ch); + /* update connect & link status atomically */ spin_lock_bh(&vif->if_lock); clear_bit(CONNECTED, &vif->flags); diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c new file mode 100644 index 00000000000..3a8d5e97dc8 --- /dev/null +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "core.h" +#include "cfg80211.h" +#include "debug.h" + +static void ath6kl_recovery_work(struct work_struct *work) +{ + struct ath6kl *ar = container_of(work, struct ath6kl, + fw_recovery.recovery_work); + + ar->state = ATH6KL_STATE_RECOVERY; + + del_timer_sync(&ar->fw_recovery.hb_timer); + + ath6kl_init_hw_restart(ar); + + ar->state = ATH6KL_STATE_ON; + clear_bit(WMI_CTRL_EP_FULL, &ar->flag); + + ar->fw_recovery.err_reason = 0; + + if (ar->fw_recovery.hb_poll) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_err_notify(struct ath6kl *ar, enum ath6kl_fw_err reason) +{ + if (!ar->fw_recovery.enable) + return; + + ath6kl_dbg(ATH6KL_DBG_RECOVERY, "Fw error detected, reason:%d\n", + reason); + + set_bit(reason, &ar->fw_recovery.err_reason); + + if (!test_bit(RECOVERY_CLEANUP, &ar->flag) && + ar->state != ATH6KL_STATE_RECOVERY) + queue_work(ar->ath6kl_wq, &ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_hb_event(struct ath6kl *ar, u32 cookie) +{ + if (cookie == ar->fw_recovery.seq_num) + ar->fw_recovery.hb_pending = false; +} + +static void ath6kl_recovery_hb_timer(unsigned long data) +{ + struct ath6kl *ar = (struct ath6kl *) data; + int err; + + if (test_bit(RECOVERY_CLEANUP, &ar->flag) || + (ar->state == ATH6KL_STATE_RECOVERY)) + return; + + if (ar->fw_recovery.hb_pending) + ar->fw_recovery.hb_misscnt++; + else + ar->fw_recovery.hb_misscnt = 0; + + if (ar->fw_recovery.hb_misscnt > ATH6KL_HB_RESP_MISS_THRES) { + ar->fw_recovery.hb_misscnt = 0; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_pending = false; + ath6kl_recovery_err_notify(ar, ATH6KL_FW_HB_RESP_FAILURE); + return; + } + + ar->fw_recovery.seq_num++; + ar->fw_recovery.hb_pending = true; + + err = ath6kl_wmi_get_challenge_resp_cmd(ar->wmi, + ar->fw_recovery.seq_num, 0); + if (err) + ath6kl_warn("Failed to send hb challenge request, err:%d\n", + err); + + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_init(struct ath6kl *ar) +{ + struct ath6kl_fw_recovery *recovery = &ar->fw_recovery; + + clear_bit(RECOVERY_CLEANUP, &ar->flag); + INIT_WORK(&recovery->recovery_work, ath6kl_recovery_work); + recovery->seq_num = 0; + recovery->hb_misscnt = 0; + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.hb_timer.function = ath6kl_recovery_hb_timer; + ar->fw_recovery.hb_timer.data = (unsigned long) ar; + init_timer_deferrable(&ar->fw_recovery.hb_timer); + + if (ar->fw_recovery.hb_poll) + mod_timer(&ar->fw_recovery.hb_timer, jiffies + + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} + +void ath6kl_recovery_cleanup(struct ath6kl *ar) +{ + if (!ar->fw_recovery.enable) + return; + + set_bit(RECOVERY_CLEANUP, &ar->flag); + + del_timer_sync(&ar->fw_recovery.hb_timer); + cancel_work_sync(&ar->fw_recovery.recovery_work); +} + +void ath6kl_recovery_suspend(struct ath6kl *ar) +{ + if (!ar->fw_recovery.enable) + return; + + ath6kl_recovery_cleanup(ar); + + if (!ar->fw_recovery.err_reason) + return; + + /* Process pending fw error detection */ + ar->fw_recovery.err_reason = 0; + WARN_ON(ar->state != ATH6KL_STATE_ON); + ar->state = ATH6KL_STATE_RECOVERY; + ath6kl_init_hw_restart(ar); + ar->state = ATH6KL_STATE_ON; +} + +void ath6kl_recovery_resume(struct ath6kl *ar) +{ + if (!ar->fw_recovery.enable) + return; + + clear_bit(RECOVERY_CLEANUP, &ar->flag); + + if (!ar->fw_recovery.hb_poll) + return; + + ar->fw_recovery.hb_pending = false; + ar->fw_recovery.seq_num = 0; + ar->fw_recovery.hb_misscnt = 0; + mod_timer(&ar->fw_recovery.hb_timer, + jiffies + msecs_to_jiffies(ar->fw_recovery.hb_poll)); +} diff --git a/drivers/net/wireless/ath/ath6kl/sdio.c b/drivers/net/wireless/ath/ath6kl/sdio.c index 05b95405f7b..d111980d44c 100644 --- a/drivers/net/wireless/ath/ath6kl/sdio.c +++ b/drivers/net/wireless/ath/ath6kl/sdio.c @@ -709,7 +709,7 @@ static int ath6kl_sdio_enable_scatter(struct ath6kl *ar) { struct ath6kl_sdio *ar_sdio = ath6kl_sdio_priv(ar); struct htc_target *target = ar->htc_target; - int ret; + int ret = 0; bool virt_scat = false; if (ar_sdio->scatter_enabled) @@ -844,22 +844,6 @@ static int ath6kl_sdio_suspend(struct ath6kl *ar, struct cfg80211_wowlan *wow) bool try_deepsleep = false; int ret; - if (ar->state == ATH6KL_STATE_SCHED_SCAN) { - ath6kl_dbg(ATH6KL_DBG_SUSPEND, "sched scan is in progress\n"); - - ret = ath6kl_set_sdio_pm_caps(ar); - if (ret) - goto cut_pwr; - - ret = ath6kl_cfg80211_suspend(ar, - ATH6KL_CFG_SUSPEND_SCHED_SCAN, - NULL); - if (ret) - goto cut_pwr; - - return 0; - } - if (ar->suspend_mode == WLAN_POWER_STATE_WOW || (!ar->suspend_mode && wow)) { @@ -942,14 +926,14 @@ static int ath6kl_sdio_resume(struct ath6kl *ar) case ATH6KL_STATE_WOW: break; - case ATH6KL_STATE_SCHED_SCAN: - break; - case ATH6KL_STATE_SUSPENDING: break; case ATH6KL_STATE_RESUMING: break; + + case ATH6KL_STATE_RECOVERY: + break; } ath6kl_cfg80211_resume(ar); @@ -1462,3 +1446,6 @@ MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FW_DIR "/" AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 7dfa0fd86d7..78b36928657 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -288,8 +288,16 @@ int ath6kl_control_tx(void *devt, struct sk_buff *skb, int status = 0; struct ath6kl_cookie *cookie = NULL; - if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) + if (WARN_ON_ONCE(ar->state == ATH6KL_STATE_WOW)) { + dev_kfree_skb(skb); return -EACCES; + } + + if (WARN_ON_ONCE(eid == ENDPOINT_UNUSED || + eid >= ENDPOINT_MAX)) { + status = -EINVAL; + goto fail_ctrl_tx; + } spin_lock_bh(&ar->lock); @@ -591,6 +599,7 @@ enum htc_send_full_action ath6kl_tx_queue_full(struct htc_target *target, */ set_bit(WMI_CTRL_EP_FULL, &ar->flag); ath6kl_err("wmi ctrl ep is full\n"); + ath6kl_recovery_err_notify(ar, ATH6KL_FW_EP_FULL); return action; } @@ -695,22 +704,31 @@ void ath6kl_tx_complete(struct htc_target *target, list); list_del(&packet->list); + if (WARN_ON_ONCE(packet->endpoint == ENDPOINT_UNUSED || + packet->endpoint >= ENDPOINT_MAX)) + continue; + ath6kl_cookie = (struct ath6kl_cookie *)packet->pkt_cntxt; - if (!ath6kl_cookie) - goto fatal; + if (WARN_ON_ONCE(!ath6kl_cookie)) + continue; status = packet->status; skb = ath6kl_cookie->skb; eid = packet->endpoint; map_no = ath6kl_cookie->map_no; - if (!skb || !skb->data) - goto fatal; + if (WARN_ON_ONCE(!skb || !skb->data)) { + dev_kfree_skb(skb); + ath6kl_free_cookie(ar, ath6kl_cookie); + continue; + } __skb_queue_tail(&skb_queue, skb); - if (!status && (packet->act_len != skb->len)) - goto fatal; + if (WARN_ON_ONCE(!status && (packet->act_len != skb->len))) { + ath6kl_free_cookie(ar, ath6kl_cookie); + continue; + } ar->tx_pending[eid]--; @@ -792,11 +810,6 @@ void ath6kl_tx_complete(struct htc_target *target, wake_up(&ar->event_wq); return; - -fatal: - WARN_ON(1); - spin_unlock_bh(&ar->lock); - return; } void ath6kl_tx_data_cleanup(struct ath6kl *ar) @@ -885,8 +898,11 @@ void ath6kl_rx_refill(struct htc_target *target, enum htc_endpoint_id endpoint) break; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_BUFFER_SIZE, endpoint); packet->skb = skb; @@ -908,8 +924,11 @@ void ath6kl_refill_amsdu_rxbufs(struct ath6kl *ar, int count) return; packet = (struct htc_packet *) skb->head; - if (!IS_ALIGNED((unsigned long) skb->data, 4)) + if (!IS_ALIGNED((unsigned long) skb->data, 4)) { + size_t len = skb_headlen(skb); skb->data = PTR_ALIGN(skb->data - 4, 4); + skb_set_tail_pointer(skb, len); + } set_htc_rxpkt_info(packet, skb, skb->data, ATH6KL_AMSDU_BUFFER_SIZE, 0); packet->skb = skb; diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 3740c3d6ab8..62bcc0d5bc2 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -185,9 +185,10 @@ static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, for (i = 0; i < urb_cnt; i++) { urb_context = kzalloc(sizeof(struct ath6kl_urb_context), GFP_KERNEL); - if (urb_context == NULL) - /* FIXME: set status to -ENOMEM */ - break; + if (urb_context == NULL) { + status = -ENOMEM; + goto fail_alloc_pipe_resources; + } urb_context->pipe = pipe; @@ -204,6 +205,7 @@ static int ath6kl_usb_alloc_pipe_resources(struct ath6kl_usb_pipe *pipe, pipe->logical_pipe_num, pipe->usb_pipe_handle, pipe->urb_alloc); +fail_alloc_pipe_resources: return status; } @@ -803,7 +805,11 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VI_SVC: - *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; + + if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 @@ -811,7 +817,11 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, *dl_pipe = ATH6KL_USB_PIPE_RX_DATA; break; case WMI_DATA_VO_SVC: - *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_HP; + + if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; + else + *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; /* * Disable rxdata2 directly, it will be enabled * if FW enable rxdata2 @@ -1196,7 +1206,14 @@ static struct usb_driver ath6kl_usb_driver = { static int ath6kl_usb_init(void) { - usb_register(&ath6kl_usb_driver); + int ret; + + ret = usb_register(&ath6kl_usb_driver); + if (ret) { + ath6kl_err("usb registration failed: %d\n", ret); + return ret; + } + return 0; } @@ -1220,3 +1237,6 @@ MODULE_FIRMWARE(AR6004_HW_1_1_DEFAULT_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_FIRMWARE_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_BOARD_DATA_FILE); MODULE_FIRMWARE(AR6004_HW_1_2_DEFAULT_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_FW_DIR "/" AR6004_HW_1_3_FIRMWARE_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_BOARD_DATA_FILE); +MODULE_FIRMWARE(AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index c30ab4b11d6..998f8b0f62f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -474,7 +474,7 @@ static int ath6kl_wmi_remain_on_chnl_event_rx(struct wmi *wmi, u8 *datap, return -EINVAL; } id = vif->last_roc_id; - cfg80211_ready_on_channel(&vif->wdev, id, chan, NL80211_CHAN_NO_HT, + cfg80211_ready_on_channel(&vif->wdev, id, chan, dur, GFP_ATOMIC); return 0; @@ -513,8 +513,7 @@ static int ath6kl_wmi_cancel_remain_on_chnl_event_rx(struct wmi *wmi, else id = vif->last_roc_id; /* timeout on uncanceled r-o-c */ vif->last_cancel_roc_id = 0; - cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, - NL80211_CHAN_NO_HT, GFP_ATOMIC); + cfg80211_remain_on_channel_expired(&vif->wdev, id, chan, GFP_ATOMIC); return 0; } @@ -936,8 +935,12 @@ static void ath6kl_wmi_regdomain_event(struct wmi *wmi, u8 *datap, int len) regpair = ath6kl_get_regpair((u16) reg_code); country = ath6kl_regd_find_country_by_rd((u16) reg_code); - ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", - regpair->regDmnEnum); + if (regpair) + ath6kl_dbg(ATH6KL_DBG_WMI, "Regpair used: 0x%0x\n", + regpair->regDmnEnum); + else + ath6kl_warn("Regpair not found reg_code 0x%0x\n", + reg_code); } if (country && wmi->parent_dev->wiphy_registered) { @@ -1116,7 +1119,7 @@ static int ath6kl_wmi_bssinfo_event_rx(struct wmi *wmi, u8 *datap, int len, * the timer would not ever fire if the scan interval is short * enough. */ - if (ar->state == ATH6KL_STATE_SCHED_SCAN && + if (test_bit(SCHED_SCANNING, &vif->flags) && !timer_pending(&vif->sched_scan_timer)) { mod_timer(&vif->sched_scan_timer, jiffies + msecs_to_jiffies(ATH6KL_SCHED_SCAN_RESULT_DELAY)); @@ -1170,6 +1173,9 @@ static int ath6kl_wmi_bitrate_reply_rx(struct wmi *wmi, u8 *datap, int len) rate = RATE_AUTO; } else { index = reply->rate_index & 0x7f; + if (WARN_ON_ONCE(index > (RATE_MCS_7_40 + 1))) + return -EINVAL; + sgi = (reply->rate_index & 0x80) ? 1 : 0; rate = wmi_rate_tbl[index][sgi]; } @@ -1531,6 +1537,68 @@ static int ath6kl_wmi_cac_event_rx(struct wmi *wmi, u8 *datap, int len, return 0; } +static int ath6kl_wmi_txe_notify_event_rx(struct wmi *wmi, u8 *datap, int len, + struct ath6kl_vif *vif) +{ + struct wmi_txe_notify_event *ev; + u32 rate, pkts; + + if (len < sizeof(*ev)) + return -EINVAL; + + if (vif->sme_state != SME_CONNECTED) + return -ENOTCONN; + + ev = (struct wmi_txe_notify_event *) datap; + rate = le32_to_cpu(ev->rate); + pkts = le32_to_cpu(ev->pkts); + + ath6kl_dbg(ATH6KL_DBG_WMI, "TXE notify event: peer %pM rate %d% pkts %d intvl %ds\n", + vif->bssid, rate, pkts, vif->txe_intvl); + + cfg80211_cqm_txe_notify(vif->ndev, vif->bssid, pkts, + rate, vif->txe_intvl, GFP_KERNEL); + + return 0; +} + +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl) +{ + struct sk_buff *skb; + struct wmi_txe_notify_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_txe_notify_cmd *) skb->data; + cmd->rate = cpu_to_le32(rate); + cmd->pkts = cpu_to_le32(pkts); + cmd->intvl = cpu_to_le32(intvl); + + return ath6kl_wmi_cmd_send(wmi, idx, skb, WMI_SET_TXE_NOTIFY_CMDID, + NO_SYNC_WMIFLAG); +} + +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi) +{ + struct sk_buff *skb; + struct wmi_set_rssi_filter_cmd *cmd; + int ret; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_rssi_filter_cmd *) skb->data; + cmd->rssi = rssi; + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSSI_FILTER_CMDID, + NO_SYNC_WMIFLAG); + return ret; +} + static int ath6kl_wmi_send_snr_threshold_params(struct wmi *wmi, struct wmi_snr_threshold_params_cmd *snr_cmd) { @@ -1677,8 +1745,11 @@ int ath6kl_wmi_cmd_send(struct wmi *wmi, u8 if_idx, struct sk_buff *skb, int ret; u16 info1; - if (WARN_ON(skb == NULL || (if_idx > (wmi->parent_dev->vif_max - 1)))) + if (WARN_ON(skb == NULL || + (if_idx > (wmi->parent_dev->vif_max - 1)))) { + dev_kfree_skb(skb); return -EINVAL; + } ath6kl_dbg(ATH6KL_DBG_WMI, "wmi tx id %d len %d flag %d\n", cmd_id, skb->len, sync_flag); @@ -1833,6 +1904,59 @@ int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx) return ret; } +/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use + * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P + * mgmt operations using station interface. + */ +static int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, + enum wmi_scan_type scan_type, + u32 force_fgscan, u32 is_legacy, + u32 home_dwell_time, + u32 force_scan_interval, + s8 num_chan, u16 *ch_list) +{ + struct sk_buff *skb; + struct wmi_start_scan_cmd *sc; + s8 size; + int i, ret; + + size = sizeof(struct wmi_start_scan_cmd); + + if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) + return -EINVAL; + + if (num_chan > WMI_MAX_CHANNELS) + return -EINVAL; + + if (num_chan) + size += sizeof(u16) * (num_chan - 1); + + skb = ath6kl_wmi_get_new_buf(size); + if (!skb) + return -ENOMEM; + + sc = (struct wmi_start_scan_cmd *) skb->data; + sc->scan_type = scan_type; + sc->force_fg_scan = cpu_to_le32(force_fgscan); + sc->is_legacy = cpu_to_le32(is_legacy); + sc->home_dwell_time = cpu_to_le32(home_dwell_time); + sc->force_scan_intvl = cpu_to_le32(force_scan_interval); + sc->num_ch = num_chan; + + for (i = 0; i < num_chan; i++) + sc->ch_list[i] = cpu_to_le16(ch_list[i]); + + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, + NO_SYNC_WMIFLAG); + + return ret; +} + +/* + * beginscan supports (compared to old startscan) P2P mgmt operations using + * station interface, send additional information like supported rates to + * advertise and xmit rates for probe requests + */ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, u32 force_fgscan, u32 is_legacy, @@ -1848,6 +1972,15 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, int num_rates; u32 ratemask; + if (!test_bit(ATH6KL_FW_CAPABILITY_STA_P2PDEV_DUPLEX, + ar->fw_capabilities)) { + return ath6kl_wmi_startscan_cmd(wmi, if_idx, + scan_type, force_fgscan, + is_legacy, home_dwell_time, + force_scan_interval, + num_chan, ch_list); + } + size = sizeof(struct wmi_begin_scan_cmd); if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) @@ -1900,50 +2033,24 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, return ret; } -/* ath6kl_wmi_start_scan_cmd is to be deprecated. Use - * ath6kl_wmi_begin_scan_cmd instead. The new function supports P2P - * mgmt operations using station interface. - */ -int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, - enum wmi_scan_type scan_type, - u32 force_fgscan, u32 is_legacy, - u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list) +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable) { struct sk_buff *skb; - struct wmi_start_scan_cmd *sc; - s8 size; - int i, ret; - - size = sizeof(struct wmi_start_scan_cmd); - - if ((scan_type != WMI_LONG_SCAN) && (scan_type != WMI_SHORT_SCAN)) - return -EINVAL; - - if (num_chan > WMI_MAX_CHANNELS) - return -EINVAL; - - if (num_chan) - size += sizeof(u16) * (num_chan - 1); + struct wmi_enable_sched_scan_cmd *sc; + int ret; - skb = ath6kl_wmi_get_new_buf(size); + skb = ath6kl_wmi_get_new_buf(sizeof(*sc)); if (!skb) return -ENOMEM; - sc = (struct wmi_start_scan_cmd *) skb->data; - sc->scan_type = scan_type; - sc->force_fg_scan = cpu_to_le32(force_fgscan); - sc->is_legacy = cpu_to_le32(is_legacy); - sc->home_dwell_time = cpu_to_le32(home_dwell_time); - sc->force_scan_intvl = cpu_to_le32(force_scan_interval); - sc->num_ch = num_chan; - - for (i = 0; i < num_chan; i++) - sc->ch_list[i] = cpu_to_le16(ch_list[i]); + ath6kl_dbg(ATH6KL_DBG_WMI, "%s scheduled scan on vif %d\n", + enable ? "enabling" : "disabling", if_idx); + sc = (struct wmi_enable_sched_scan_cmd *) skb->data; + sc->enable = enable ? 1 : 0; - ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_START_SCAN_CMDID, + ret = ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_ENABLE_SCHED_SCAN_CMDID, NO_SYNC_WMIFLAG); - return ret; } @@ -2275,8 +2382,10 @@ static int ath6kl_wmi_data_sync_send(struct wmi *wmi, struct sk_buff *skb, struct wmi_data_hdr *data_hdr; int ret; - if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) + if (WARN_ON(skb == NULL || ep_id == wmi->ep_id)) { + dev_kfree_skb(skb); return -EINVAL; + } skb_push(skb, sizeof(struct wmi_data_hdr)); @@ -2313,10 +2422,8 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) spin_unlock_bh(&wmi->lock); skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); - if (!skb) { - ret = -ENOMEM; - goto free_skb; - } + if (!skb) + return -ENOMEM; cmd = (struct wmi_sync_cmd *) skb->data; @@ -2339,7 +2446,7 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) * then do not send the Synchronize cmd on the control ep */ if (ret) - goto free_skb; + goto free_cmd_skb; /* * Send sync cmd followed by sync data messages on all @@ -2349,15 +2456,12 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) NO_SYNC_WMIFLAG); if (ret) - goto free_skb; - - /* cmd buffer sent, we no longer own it */ - skb = NULL; + goto free_data_skb; for (index = 0; index < num_pri_streams; index++) { if (WARN_ON(!data_sync_bufs[index].skb)) - break; + goto free_data_skb; ep_id = ath6kl_ac2_endpoint_id(wmi->parent_dev, data_sync_bufs[index]. @@ -2366,17 +2470,20 @@ static int ath6kl_wmi_sync_point(struct wmi *wmi, u8 if_idx) ath6kl_wmi_data_sync_send(wmi, data_sync_bufs[index].skb, ep_id, if_idx); - if (ret) - break; - data_sync_bufs[index].skb = NULL; + + if (ret) + goto free_data_skb; } -free_skb: + return 0; + +free_cmd_skb: /* free up any resources left over (possibly due to an error) */ if (skb) dev_kfree_skb(skb); +free_data_skb: for (index = 0; index < num_pri_streams; index++) { if (data_sync_bufs[index].skb != NULL) { dev_kfree_skb((struct sk_buff *)data_sync_bufs[index]. @@ -2618,11 +2725,13 @@ static int ath6kl_set_bitrate_mask64(struct wmi *wmi, u8 if_idx, { struct sk_buff *skb; int ret, mode, band; - u64 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + u64 mcsrate, ratemask[ATH6KL_NUM_BANDS]; struct wmi_set_tx_select_rates64_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + + /* only check 2.4 and 5 GHz bands, skip the rest */ + for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) @@ -2668,11 +2777,13 @@ static int ath6kl_set_bitrate_mask32(struct wmi *wmi, u8 if_idx, { struct sk_buff *skb; int ret, mode, band; - u32 mcsrate, ratemask[IEEE80211_NUM_BANDS]; + u32 mcsrate, ratemask[ATH6KL_NUM_BANDS]; struct wmi_set_tx_select_rates32_cmd *cmd; memset(&ratemask, 0, sizeof(ratemask)); - for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + + /* only check 2.4 and 5 GHz bands, skip the rest */ + for (band = 0; band <= IEEE80211_BAND_5GHZ; band++) { /* copy legacy rate mask */ ratemask[band] = mask->control[band].legacy; if (band == IEEE80211_BAND_5GHZ) @@ -2716,7 +2827,7 @@ int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, { struct ath6kl *ar = wmi->parent_dev; - if (ar->hw.flags & ATH6KL_HW_FLAG_64BIT_RATES) + if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); else return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); @@ -3139,12 +3250,40 @@ int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enhance) return ret; } +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) +{ + struct sk_buff *skb; + struct wmi_set_regdomain_cmd *cmd; + + skb = ath6kl_wmi_get_new_buf(sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_regdomain_cmd *) skb->data; + memcpy(cmd->iso_name, alpha2, 2); + + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_REGDOMAIN_CMDID, + NO_SYNC_WMIFLAG); +} + s32 ath6kl_wmi_get_rate(s8 rate_index) { + u8 sgi = 0; + if (rate_index == RATE_AUTO) return 0; - return wmi_rate_tbl[(u32) rate_index][0]; + /* SGI is stored as the MSB of the rate_index */ + if (rate_index & RATE_INDEX_MSB) { + rate_index &= RATE_INDEX_WITHOUT_SGI_MASK; + sgi = 1; + } + + if (WARN_ON(rate_index > RATE_MCS_7_40)) + rate_index = RATE_MCS_7_40; + + return wmi_rate_tbl[(u32) rate_index][sgi]; } static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, @@ -3634,6 +3773,19 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout) NO_SYNC_WMIFLAG); } +static void ath6kl_wmi_hb_challenge_resp_event(struct wmi *wmi, u8 *datap, + int len) +{ + struct wmix_hb_challenge_resp_cmd *cmd; + + if (len < sizeof(struct wmix_hb_challenge_resp_cmd)) + return; + + cmd = (struct wmix_hb_challenge_resp_cmd *) datap; + ath6kl_recovery_hb_event(wmi->parent_dev, + le32_to_cpu(cmd->cookie)); +} + static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) { struct wmix_cmd_hdr *cmd; @@ -3658,6 +3810,7 @@ static int ath6kl_wmi_control_rx_xtnd(struct wmi *wmi, struct sk_buff *skb) switch (id) { case WMIX_HB_CHALLENGE_RESP_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event hb challenge resp\n"); + ath6kl_wmi_hb_challenge_resp_event(wmi, datap, len); break; case WMIX_DBGLOG_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "wmi event dbglog len %d\n", len); @@ -3750,6 +3903,9 @@ static int ath6kl_wmi_proc_events_vif(struct wmi *wmi, u16 if_idx, u16 cmd_id, case WMI_RX_ACTION_EVENTID: ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_RX_ACTION_EVENTID\n"); return ath6kl_wmi_rx_action_event_rx(wmi, datap, len, vif); + case WMI_TXE_NOTIFY_EVENTID: + ath6kl_dbg(ATH6KL_DBG_WMI, "WMI_TXE_NOTIFY_EVENTID\n"); + return ath6kl_wmi_txe_notify_event_rx(wmi, datap, len, vif); default: ath6kl_dbg(ATH6KL_DBG_WMI, "unknown cmd id 0x%x\n", cmd_id); return -EINVAL; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 43339aca585..98b1755e67f 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -48,7 +48,7 @@ #define A_BAND_24GHZ 0 #define A_BAND_5GHZ 1 -#define A_NUM_BANDS 2 +#define ATH6KL_NUM_BANDS 2 /* in ms */ #define WMI_IMPLICIT_PSTREAM_INACTIVITY_INT 5000 @@ -628,6 +628,20 @@ enum wmi_cmd_id { WMI_SET_MCASTRATE, WMI_STA_BMISS_ENHANCE_CMDID, + + WMI_SET_REGDOMAIN_CMDID, + + WMI_SET_RSSI_FILTER_CMDID, + + WMI_SET_KEEP_ALIVE_EXT, + + WMI_VOICE_DETECTION_ENABLE_CMDID, + + WMI_SET_TXE_NOTIFY_CMDID, + + WMI_SET_RECOVERY_TEST_PARAMETER_CMDID, /*0xf094*/ + + WMI_ENABLE_SCHED_SCAN_CMDID, }; enum wmi_mgmt_frame_type { @@ -843,7 +857,7 @@ struct wmi_begin_scan_cmd { u8 scan_type; /* Supported rates to advertise in the probe request frames */ - struct wmi_supp_rates supp_rates[IEEE80211_NUM_BANDS]; + struct wmi_supp_rates supp_rates[ATH6KL_NUM_BANDS]; /* how many channels follow */ u8 num_ch; @@ -941,6 +955,11 @@ struct wmi_scan_params_cmd { __le32 max_dfsch_act_time; } __packed; +/* WMI_ENABLE_SCHED_SCAN_CMDID */ +struct wmi_enable_sched_scan_cmd { + u8 enable; +} __packed; + /* WMI_SET_BSS_FILTER_CMDID */ enum wmi_bss_filter { /* no beacons forwarded */ @@ -1032,6 +1051,11 @@ struct wmi_sta_bmiss_enhance_cmd { u8 enable; } __packed; +struct wmi_set_regdomain_cmd { + u8 length; + u8 iso_name[2]; +} __packed; + /* WMI_SET_POWER_MODE_CMDID */ enum wmi_power_mode { REC_POWER = 0x01, @@ -1276,6 +1300,11 @@ struct wmi_snr_threshold_params_cmd { u8 reserved[3]; } __packed; +/* Don't report BSSs with signal (RSSI) below this threshold */ +struct wmi_set_rssi_filter_cmd { + s8 rssi; +} __packed; + enum wmi_preamble_policy { WMI_IGNORE_BARKER_IN_ERP = 0, WMI_FOLLOW_BARKER_IN_ERP, @@ -1455,6 +1484,20 @@ enum wmi_event_id { WMI_P2P_CAPABILITIES_EVENTID, WMI_RX_ACTION_EVENTID, WMI_P2P_INFO_EVENTID, + + /* WPS Events */ + WMI_WPS_GET_STATUS_EVENTID, + WMI_WPS_PROFILE_EVENTID, + + /* more P2P events */ + WMI_NOA_INFO_EVENTID, + WMI_OPPPS_INFO_EVENTID, + WMI_PORT_STATUS_EVENTID, + + /* 802.11w */ + WMI_GET_RSN_CAP_EVENTID, + + WMI_TXE_NOTIFY_EVENTID, }; struct wmi_ready_event_2 { @@ -1749,6 +1792,9 @@ struct rx_stats { a_sle32 ucast_rate; } __packed; +#define RATE_INDEX_WITHOUT_SGI_MASK 0x7f +#define RATE_INDEX_MSB 0x80 + struct tkip_ccmp_stats { __le32 tkip_local_mic_fail; __le32 tkip_cnter_measures_invoked; @@ -2019,7 +2065,6 @@ struct wmi_set_ie_cmd { #define WOW_MAX_FILTERS_PER_LIST 4 #define WOW_PATTERN_SIZE 64 -#define WOW_MASK_SIZE 64 #define MAC_MAX_FILTERS_PER_LIST 4 @@ -2028,7 +2073,7 @@ struct wow_filter { u8 wow_filter_id; u8 wow_filter_size; u8 wow_filter_offset; - u8 wow_filter_mask[WOW_MASK_SIZE]; + u8 wow_filter_mask[WOW_PATTERN_SIZE]; u8 wow_filter_pattern[WOW_PATTERN_SIZE]; } __packed; @@ -2087,6 +2132,19 @@ struct wmi_del_wow_pattern_cmd { __le16 filter_id; } __packed; +/* WMI_SET_TXE_NOTIFY_CMDID */ +struct wmi_txe_notify_cmd { + __le32 rate; + __le32 pkts; + __le32 intvl; +} __packed; + +/* WMI_TXE_NOTIFY_EVENTID */ +struct wmi_txe_notify_event { + __le32 rate; + __le32 pkts; +} __packed; + /* WMI_SET_AKMP_PARAMS_CMD */ struct wmi_pmkid { @@ -2505,11 +2563,6 @@ int ath6kl_wmi_connect_cmd(struct wmi *wmi, u8 if_idx, int ath6kl_wmi_reconnect_cmd(struct wmi *wmi, u8 if_idx, u8 *bssid, u16 channel); int ath6kl_wmi_disconnect_cmd(struct wmi *wmi, u8 if_idx); -int ath6kl_wmi_startscan_cmd(struct wmi *wmi, u8 if_idx, - enum wmi_scan_type scan_type, - u32 force_fgscan, u32 is_legacy, - u32 home_dwell_time, u32 force_scan_interval, - s8 num_chan, u16 *ch_list); int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, enum wmi_scan_type scan_type, @@ -2517,6 +2570,7 @@ int ath6kl_wmi_beginscan_cmd(struct wmi *wmi, u8 if_idx, u32 home_dwell_time, u32 force_scan_interval, s8 num_chan, u16 *ch_list, u32 no_cck, u32 *rates); +int ath6kl_wmi_enable_sched_scan_cmd(struct wmi *wmi, u8 if_idx, bool enable); int ath6kl_wmi_scanparams_cmd(struct wmi *wmi, u8 if_idx, u16 fg_start_sec, u16 fg_end_sec, u16 bg_sec, @@ -2592,6 +2646,7 @@ int ath6kl_wmi_add_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, const u8 *mask); int ath6kl_wmi_del_wow_pattern_cmd(struct wmi *wmi, u8 if_idx, u16 list_id, u16 filter_id); +int ath6kl_wmi_set_rssi_filter_cmd(struct wmi *wmi, u8 if_idx, s8 rssi); int ath6kl_wmi_set_roam_lrssi_cmd(struct wmi *wmi, u8 lrssi); int ath6kl_wmi_ap_set_dtim_cmd(struct wmi *wmi, u8 if_idx, u32 dtim_period); int ath6kl_wmi_force_roam_cmd(struct wmi *wmi, const u8 *bssid); @@ -2600,6 +2655,9 @@ int ath6kl_wmi_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, bool mc_all_on); int ath6kl_wmi_add_del_mcast_filter_cmd(struct wmi *wmi, u8 if_idx, u8 *filter, bool add_filter); int ath6kl_wmi_sta_bmiss_enhance_cmd(struct wmi *wmi, u8 if_idx, bool enable); +int ath6kl_wmi_set_txe_notify(struct wmi *wmi, u8 idx, + u32 rate, u32 pkts, u32 intvl); +int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2); /* AP mode uAPSD */ int ath6kl_wmi_ap_set_apsd(struct wmi *wmi, u8 if_idx, u8 enable); @@ -2658,6 +2716,8 @@ int ath6kl_wmi_set_inact_period(struct wmi *wmi, u8 if_idx, int inact_timeout); void ath6kl_wmi_sscan_timer(unsigned long ptr); +int ath6kl_wmi_get_challenge_resp_cmd(struct wmi *wmi, u32 cookie, u32 source); + struct ath6kl_vif *ath6kl_get_vif_by_index(struct ath6kl *ar, u8 if_idx); void *ath6kl_wmi_init(struct ath6kl *devt); void ath6kl_wmi_shutdown(struct wmi *wmi); diff --git a/drivers/net/wireless/ath/ath9k/ar9003_calib.c b/drivers/net/wireless/ath/ath9k/ar9003_calib.c index 162401f22f8..8b0d8dcd762 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_calib.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_calib.c @@ -891,6 +891,74 @@ static void ar9003_hw_tx_iq_cal_reload(struct ath_hw *ah) AR_PHY_RX_IQCAL_CORR_B0_LOOPBACK_IQCORR_EN, 0x1); } +static void ar9003_hw_manual_peak_cal(struct ath_hw *ah, u8 chain, bool is_2g) +{ + int offset[8], total = 0, test; + int agc_out, i; + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC, 0x0); + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR, 0x0); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON_OVR, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON, 0x0); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR, 0x1); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0x1); + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR, 0x0); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR, 0x0); + + for (i = 6; i > 0; i--) { + offset[i] = BIT(i - 1); + test = total + offset[i]; + + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, + test); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, + test); + udelay(100); + agc_out = REG_READ_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_OUT); + offset[i] = (agc_out) ? 0 : 1; + total += (offset[i] << (i - 1)); + } + + if (is_2g) + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR, total); + else + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR, total); + + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_GAINSTAGES(chain), + AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE, 0); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXTX2(chain), + AR_PHY_65NM_RXTX2_RXON_OVR, 0); + REG_RMW_FIELD(ah, AR_PHY_65NM_RXRF_AGC(chain), + AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR, 0); +} + static bool ar9003_hw_init_cal(struct ath_hw *ah, struct ath9k_channel *chan) { @@ -989,6 +1057,14 @@ skip_tx_iqcal: status = ath9k_hw_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0, AH_WAIT_TIMEOUT); + if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) { + for (i = 0; i < AR9300_MAX_CHAINS; i++) { + if (!(ah->rxchainmask & (1 << i))) + continue; + ar9003_hw_manual_peak_cal(ah, i, + IS_CHAN_2GHZ(chan)); + } + } } if (ath9k_hw_mci_is_enabled(ah) && IS_CHAN_2GHZ(chan) && run_agc_cal) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 0693cd95b74..74fd3977fee 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -35,12 +35,6 @@ */ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) { -#define AR9462_BB_CTX_COEFJ(x) \ - ar9462_##x##_baseband_core_txfir_coeff_japan_2484 - -#define AR9462_BBC_TXIFR_COEFFJ \ - ar9462_2p0_baseband_core_txfir_coeff_japan_2484 - if (AR_SREV_9330_11(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], @@ -70,6 +64,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p1); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9331_1p1_baseband_core_txfir_coeff_japan_2484); + /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, @@ -106,6 +104,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9331_modes_lowest_ob_db_tx_gain_1p2); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9331_1p2_baseband_core_txfir_coeff_japan_2484); + /* additional clock settings */ if (ah->is_clk_25mhz) INIT_INI_ARRAY(&ah->iniAdditional, @@ -180,6 +182,10 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) INIT_INI_ARRAY(&ah->iniModesTxGain, ar9485_modes_lowest_ob_db_tx_gain_1_1); + /* Japan 2484 Mhz CCK */ + INIT_INI_ARRAY(&ah->iniCckfirJapan2484, + ar9485_1_1_baseband_core_txfir_coeff_japan_2484); + /* Load PCIE SERDES settings from INI */ /* Awake Setting */ @@ -229,9 +235,7 @@ static void ar9003_hw_init_mode_regs(struct ath_hw *ah) ar9462_modes_fast_clock_2p0); INIT_INI_ARRAY(&ah->iniCckfirJapan2484, - AR9462_BB_CTX_COEFJ(2p0)); - - INIT_INI_ARRAY(&ah->ini_japan2484, AR9462_BBC_TXIFR_COEFFJ); + ar9462_2p0_baseband_core_txfir_coeff_japan_2484); } else if (AR_SREV_9550(ah)) { /* mac */ INIT_INI_ARRAY(&ah->iniMac[ATH_INI_CORE], diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mci.c b/drivers/net/wireless/ath/ath9k/ar9003_mci.c index 42b4412d679..8dd069259e7 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mci.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mci.c @@ -714,7 +714,6 @@ bool ar9003_mci_start_reset(struct ath_hw *ah, struct ath9k_channel *chan) return true; } -EXPORT_SYMBOL(ar9003_mci_start_reset); int ar9003_mci_end_reset(struct ath_hw *ah, struct ath9k_channel *chan, struct ath9k_hw_cal_data *caldata) diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c index 759f5f5a715..ce19c09fa8e 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c @@ -784,7 +784,7 @@ static int ar9003_hw_process_ini(struct ath_hw *ah, REG_WRITE_ARRAY(&ah->iniAdditional, 1, regWrites); if (chan->channel == 2484) - ar9003_hw_prog_ini(ah, &ah->ini_japan2484, 1); + ar9003_hw_prog_ini(ah, &ah->iniCckfirJapan2484, 1); if (AR_SREV_9462(ah) || AR_SREV_9565(ah)) REG_WRITE(ah, AR_GLB_SWREG_DISCONT_MODE, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.h b/drivers/net/wireless/ath/ath9k/ar9003_phy.h index 8f585233a78..4c3d06de711 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_phy.h +++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.h @@ -698,13 +698,6 @@ #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT 0x0000ff00 #define AR_PHY_65NM_CH0_THERM_SAR_ADC_OUT_S 8 -#define AR_PHY_65NM_CH0_RXTX1 0x16100 -#define AR_PHY_65NM_CH0_RXTX2 0x16104 -#define AR_PHY_65NM_CH1_RXTX1 0x16500 -#define AR_PHY_65NM_CH1_RXTX2 0x16504 -#define AR_PHY_65NM_CH2_RXTX1 0x16900 -#define AR_PHY_65NM_CH2_RXTX2 0x16904 - #define AR_CH0_TOP2 (AR_SREV_9300(ah) ? 0x1628c : \ (AR_SREV_9462(ah) ? 0x16290 : 0x16284)) #define AR_CH0_TOP2_XPABIASLVL 0xf000 @@ -1286,4 +1279,43 @@ #define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD 0xFC000000 #define AR_BTCOEX_WL_LNADIV_BT_INACTIVE_THRESHOLD_S 26 +/* Manual Peak detector calibration */ +#define AR_PHY_65NM_BASE 0x16000 +#define AR_PHY_65NM_RXRF_GAINSTAGES(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0x8) +#define AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE 0x80000000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_RX_OVERRIDE_S 31 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC 0x00000002 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNAON_CALDC_S 1 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR 0x70000000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA2G_GAIN_OVR_S 28 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR 0x03800000 +#define AR_PHY_65NM_RXRF_GAINSTAGES_LNA5G_GAIN_OVR_S 23 + +#define AR_PHY_65NM_RXTX2(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0x104) +#define AR_PHY_65NM_RXTX2_RXON_OVR 0x00001000 +#define AR_PHY_65NM_RXTX2_RXON_OVR_S 12 +#define AR_PHY_65NM_RXTX2_RXON 0x00000800 +#define AR_PHY_65NM_RXTX2_RXON_S 11 + +#define AR_PHY_65NM_RXRF_AGC(i) (AR_PHY_65NM_BASE + \ + (i * 0x400) + 0xc) +#define AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE 0x80000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_OVERRIDE_S 31 +#define AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR 0x40000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_ON_OVR_S 30 +#define AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR 0x20000000 +#define AR_PHY_65NM_RXRF_AGC_AGC_CAL_OVR_S 29 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR 0x1E000000 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_DBDAC_OVR_S 25 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR 0x00078000 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_DBDAC_OVR_S 15 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR 0x01F80000 +#define AR_PHY_65NM_RXRF_AGC_AGC2G_CALDAC_OVR_S 19 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR 0x00007e00 +#define AR_PHY_65NM_RXRF_AGC_AGC5G_CALDAC_OVR_S 9 +#define AR_PHY_65NM_RXRF_AGC_AGC_OUT 0x00000004 +#define AR_PHY_65NM_RXRF_AGC_AGC_OUT_S 2 + #endif /* AR9003_PHY_H */ diff --git a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h index 58f30f65c6b..ccc42a71b43 100644 --- a/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9462_2p0_initvals.h @@ -78,7 +78,7 @@ static const u32 ar9462_2p0_baseband_postamble[][5] = { {0x0000a284, 0x00000000, 0x00000000, 0x00000150, 0x00000150}, {0x0000a288, 0x00000110, 0x00000110, 0x00000110, 0x00000110}, {0x0000a28c, 0x00022222, 0x00022222, 0x00022222, 0x00022222}, - {0x0000a2c4, 0x00158d18, 0x00158d18, 0x00158d18, 0x00158d18}, + {0x0000a2c4, 0x00058d18, 0x00058d18, 0x00058d18, 0x00058d18}, {0x0000a2d0, 0x00041981, 0x00041981, 0x00041981, 0x00041982}, {0x0000a2d8, 0x7999a83b, 0x7999a83b, 0x7999a83b, 0x7999a83b}, {0x0000a358, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, diff --git a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h index fb4497fc7a3..a3710f3bb90 100644 --- a/drivers/net/wireless/ath/ath9k/ar9485_initvals.h +++ b/drivers/net/wireless/ath/ath9k/ar9485_initvals.h @@ -18,7 +18,7 @@ #ifndef INITVALS_9485_H #define INITVALS_9485_H -/* AR9485 1.0 */ +/* AR9485 1.1 */ #define ar9485_1_1_mac_postamble ar9300_2p2_mac_postamble @@ -31,6 +31,11 @@ static const u32 ar9485_1_1_pcie_phy_pll_on_clkreq_disable_L1[][2] = { static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { /* Addr allmodes */ + {0x00009e00, 0x037216a0}, + {0x00009e04, 0x00182020}, + {0x00009e18, 0x00000000}, + {0x00009e2c, 0x00004121}, + {0x00009e44, 0x02282324}, {0x0000a000, 0x00060005}, {0x0000a004, 0x00810080}, {0x0000a008, 0x00830082}, @@ -164,6 +169,11 @@ static const u32 ar9485Common_wo_xlna_rx_gain_1_1[][2] = { static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, @@ -198,6 +208,22 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, @@ -234,9 +260,193 @@ static const u32 ar9485Modes_high_power_tx_gain_1_1[][5] = { {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, }; -#define ar9485Modes_high_ob_db_tx_gain_1_1 ar9485Modes_high_power_tx_gain_1_1 +static const u32 ar9485Modes_high_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xffc63a84, 0xffc63a84}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe0fc000, 0xfe0fc000}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfff00000, 0xfff00000}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, + {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; -#define ar9485Modes_low_ob_db_tx_gain_1_1 ar9485Modes_high_ob_db_tx_gain_1_1 +static const u32 ar9485Modes_low_ob_db_tx_gain_1_1[][5] = { + /* Addr 5G_HT20 5G_HT40 2G_HT40 2G_HT20 */ + {0x000098bc, 0x00000002, 0x00000002, 0x00000002, 0x00000002}, + {0x0000a2d8, 0xf999a83a, 0xf999a83a, 0x7999a83a, 0x7999a83a}, + {0x0000a2dc, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e0, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e4, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a2e8, 0x00000000, 0x00000000, 0xfe2d3552, 0xfe2d3552}, + {0x0000a410, 0x000050d9, 0x000050d9, 0x000050d8, 0x000050d8}, + {0x0000a458, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a500, 0x00022200, 0x00022200, 0x00000000, 0x00000000}, + {0x0000a504, 0x05062002, 0x05062002, 0x04000002, 0x04000002}, + {0x0000a508, 0x0c002e00, 0x0c002e00, 0x08000004, 0x08000004}, + {0x0000a50c, 0x11062202, 0x11062202, 0x0d000200, 0x0d000200}, + {0x0000a510, 0x17022e00, 0x17022e00, 0x11000202, 0x11000202}, + {0x0000a514, 0x1d000ec2, 0x1d000ec2, 0x15000400, 0x15000400}, + {0x0000a518, 0x25020ec0, 0x25020ec0, 0x19000402, 0x19000402}, + {0x0000a51c, 0x2b020ec3, 0x2b020ec3, 0x1d000404, 0x1d000404}, + {0x0000a520, 0x2f001f04, 0x2f001f04, 0x21000603, 0x21000603}, + {0x0000a524, 0x35001fc4, 0x35001fc4, 0x25000605, 0x25000605}, + {0x0000a528, 0x3c022f04, 0x3c022f04, 0x2a000a03, 0x2a000a03}, + {0x0000a52c, 0x41023e85, 0x41023e85, 0x2c000a04, 0x2c000a04}, + {0x0000a530, 0x48023ec6, 0x48023ec6, 0x34000e20, 0x34000e20}, + {0x0000a534, 0x4d023f01, 0x4d023f01, 0x35000e21, 0x35000e21}, + {0x0000a538, 0x53023f4b, 0x53023f4b, 0x43000e62, 0x43000e62}, + {0x0000a53c, 0x5a027f09, 0x5a027f09, 0x45000e63, 0x45000e63}, + {0x0000a540, 0x5f027fc9, 0x5f027fc9, 0x49000e65, 0x49000e65}, + {0x0000a544, 0x6502feca, 0x6502feca, 0x4b000e66, 0x4b000e66}, + {0x0000a548, 0x6b02ff4a, 0x6b02ff4a, 0x4d001645, 0x4d001645}, + {0x0000a54c, 0x7203feca, 0x7203feca, 0x51001865, 0x51001865}, + {0x0000a550, 0x7703ff0b, 0x7703ff0b, 0x55001a86, 0x55001a86}, + {0x0000a554, 0x7d06ffcb, 0x7d06ffcb, 0x57001ce9, 0x57001ce9}, + {0x0000a558, 0x8407ff0b, 0x8407ff0b, 0x5a001ceb, 0x5a001ceb}, + {0x0000a55c, 0x8907ffcb, 0x8907ffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a560, 0x900fff0b, 0x900fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a564, 0x960fffcb, 0x960fffcb, 0x5e001eeb, 0x5e001eeb}, + {0x0000a568, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a56c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a570, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a574, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a578, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a57c, 0x9c1fff0b, 0x9c1fff0b, 0x5e001eeb, 0x5e001eeb}, + {0x0000a580, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a584, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a588, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a58c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a590, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a594, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000a598, 0x00000000, 0x00000000, 0x01404501, 0x01404501}, + {0x0000a59c, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a0, 0x00000000, 0x00000000, 0x02808a02, 0x02808a02}, + {0x0000a5a4, 0x00000000, 0x00000000, 0x02808803, 0x02808803}, + {0x0000a5a8, 0x00000000, 0x00000000, 0x04c14b04, 0x04c14b04}, + {0x0000a5ac, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b0, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b4, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5b8, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000a5bc, 0x00000000, 0x00000000, 0x04c15305, 0x04c15305}, + {0x0000b500, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b504, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b508, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b50c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b510, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b514, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b518, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b51c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b520, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b524, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b528, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b52c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b530, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b534, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b538, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b53c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b540, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b544, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b548, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b54c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b550, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b554, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b558, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b55c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b560, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b564, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b568, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b56c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b570, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b574, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b578, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x0000b57c, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00016044, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db, 0x05d6b2db}, + {0x00016048, 0x6c924260, 0x6c924260, 0x6c924260, 0x6c924260}, +}; #define ar9485_modes_lowest_ob_db_tx_gain_1_1 ar9485Modes_low_ob_db_tx_gain_1_1 @@ -245,19 +455,19 @@ static const u32 ar9485_1_1[][2] = { {0x0000a580, 0x00000000}, {0x0000a584, 0x00000000}, {0x0000a588, 0x00000000}, - {0x0000a58c, 0x00000000}, - {0x0000a590, 0x00000000}, - {0x0000a594, 0x00000000}, - {0x0000a598, 0x00000000}, - {0x0000a59c, 0x00000000}, - {0x0000a5a0, 0x00000000}, - {0x0000a5a4, 0x00000000}, - {0x0000a5a8, 0x00000000}, - {0x0000a5ac, 0x00000000}, - {0x0000a5b0, 0x00000000}, - {0x0000a5b4, 0x00000000}, - {0x0000a5b8, 0x00000000}, - {0x0000a5bc, 0x00000000}, + {0x0000a58c, 0x01804000}, + {0x0000a590, 0x02808a02}, + {0x0000a594, 0x0340ca02}, + {0x0000a598, 0x0340cd03}, + {0x0000a59c, 0x0340cd03}, + {0x0000a5a0, 0x06415304}, + {0x0000a5a4, 0x04c11905}, + {0x0000a5a8, 0x06415905}, + {0x0000a5ac, 0x06415905}, + {0x0000a5b0, 0x06415905}, + {0x0000a5b4, 0x06415905}, + {0x0000a5b8, 0x06415905}, + {0x0000a5bc, 0x06415905}, }; static const u32 ar9485_1_1_radio_core[][2] = { @@ -340,7 +550,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x00009880, 0x201fff00}, {0x00009884, 0x00001042}, {0x000098a4, 0x00200400}, - {0x000098b0, 0x52440bbe}, + {0x000098b0, 0x32840bbe}, {0x000098d0, 0x004b6a8e}, {0x000098d4, 0x00000820}, {0x000098dc, 0x00000000}, @@ -362,7 +572,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x00009d18, 0x00000000}, {0x00009d1c, 0x00000000}, {0x00009e08, 0x0038233c}, - {0x00009e24, 0x9927b515}, + {0x00009e24, 0x992bb515}, {0x00009e28, 0x12ef0200}, {0x00009e30, 0x06336f77}, {0x00009e34, 0x6af6532f}, @@ -427,7 +637,7 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a408, 0x0e79e5c6}, {0x0000a40c, 0x00820820}, {0x0000a414, 0x1ce739cf}, - {0x0000a418, 0x2d0019ce}, + {0x0000a418, 0x2d0021ce}, {0x0000a41c, 0x1ce739ce}, {0x0000a420, 0x000001ce}, {0x0000a424, 0x1ce739ce}, @@ -443,8 +653,8 @@ static const u32 ar9485_1_1_baseband_core[][2] = { {0x0000a44c, 0x00000001}, {0x0000a450, 0x00010000}, {0x0000a5c4, 0xbfad9d74}, - {0x0000a5c8, 0x0048060a}, - {0x0000a5cc, 0x00000637}, + {0x0000a5c8, 0x00480605}, + {0x0000a5cc, 0x00002e37}, {0x0000a760, 0x03020100}, {0x0000a764, 0x09080504}, {0x0000a768, 0x0d0c0b0a}, @@ -464,17 +674,22 @@ static const u32 ar9485_1_1_baseband_core[][2] = { static const u32 ar9485_common_rx_gain_1_1[][2] = { /* Addr allmodes */ - {0x0000a000, 0x00010000}, - {0x0000a004, 0x00030002}, - {0x0000a008, 0x00050004}, - {0x0000a00c, 0x00810080}, - {0x0000a010, 0x01800082}, - {0x0000a014, 0x01820181}, - {0x0000a018, 0x01840183}, - {0x0000a01c, 0x01880185}, - {0x0000a020, 0x018a0189}, - {0x0000a024, 0x02850284}, - {0x0000a028, 0x02890288}, + {0x00009e00, 0x03721b20}, + {0x00009e04, 0x00082020}, + {0x00009e18, 0x0300501e}, + {0x00009e2c, 0x00002e21}, + {0x00009e44, 0x02182324}, + {0x0000a000, 0x00060005}, + {0x0000a004, 0x00810080}, + {0x0000a008, 0x00830082}, + {0x0000a00c, 0x00850084}, + {0x0000a010, 0x01820181}, + {0x0000a014, 0x01840183}, + {0x0000a018, 0x01880185}, + {0x0000a01c, 0x018a0189}, + {0x0000a020, 0x02850284}, + {0x0000a024, 0x02890288}, + {0x0000a028, 0x028b028a}, {0x0000a02c, 0x03850384}, {0x0000a030, 0x03890388}, {0x0000a034, 0x038b038a}, @@ -496,15 +711,15 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a074, 0x00000000}, {0x0000a078, 0x00000000}, {0x0000a07c, 0x00000000}, - {0x0000a080, 0x28282828}, - {0x0000a084, 0x28282828}, - {0x0000a088, 0x28282828}, - {0x0000a08c, 0x28282828}, - {0x0000a090, 0x28282828}, - {0x0000a094, 0x21212128}, - {0x0000a098, 0x171c1c1c}, - {0x0000a09c, 0x02020212}, - {0x0000a0a0, 0x00000202}, + {0x0000a080, 0x18181818}, + {0x0000a084, 0x18181818}, + {0x0000a088, 0x18181818}, + {0x0000a08c, 0x18181818}, + {0x0000a090, 0x18181818}, + {0x0000a094, 0x18181818}, + {0x0000a098, 0x17181818}, + {0x0000a09c, 0x02020b0b}, + {0x0000a0a0, 0x02020202}, {0x0000a0a4, 0x00000000}, {0x0000a0a8, 0x00000000}, {0x0000a0ac, 0x00000000}, @@ -512,22 +727,22 @@ static const u32 ar9485_common_rx_gain_1_1[][2] = { {0x0000a0b4, 0x00000000}, {0x0000a0b8, 0x00000000}, {0x0000a0bc, 0x00000000}, - {0x0000a0c0, 0x001f0000}, - {0x0000a0c4, 0x111f1100}, - {0x0000a0c8, 0x111d111e}, - {0x0000a0cc, 0x111b111c}, - {0x0000a0d0, 0x22032204}, - {0x0000a0d4, 0x22012202}, - {0x0000a0d8, 0x221f2200}, - {0x0000a0dc, 0x221d221e}, - {0x0000a0e0, 0x33013302}, - {0x0000a0e4, 0x331f3300}, - {0x0000a0e8, 0x4402331e}, - {0x0000a0ec, 0x44004401}, - {0x0000a0f0, 0x441e441f}, - {0x0000a0f4, 0x55015502}, - {0x0000a0f8, 0x551f5500}, - {0x0000a0fc, 0x6602551e}, + {0x0000a0c0, 0x22072208}, + {0x0000a0c4, 0x22052206}, + {0x0000a0c8, 0x22032204}, + {0x0000a0cc, 0x22012202}, + {0x0000a0d0, 0x221f2200}, + {0x0000a0d4, 0x221d221e}, + {0x0000a0d8, 0x33023303}, + {0x0000a0dc, 0x33003301}, + {0x0000a0e0, 0x331e331f}, + {0x0000a0e4, 0x4402331d}, + {0x0000a0e8, 0x44004401}, + {0x0000a0ec, 0x441e441f}, + {0x0000a0f0, 0x55025503}, + {0x0000a0f4, 0x55005501}, + {0x0000a0f8, 0x551e551f}, + {0x0000a0fc, 0x6602551d}, {0x0000a100, 0x66006601}, {0x0000a104, 0x661e661f}, {0x0000a108, 0x7703661d}, @@ -636,17 +851,12 @@ static const u32 ar9485_1_1_baseband_postamble[][5] = { {0x0000982c, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4, 0x05eea6d4}, {0x00009830, 0x0000059c, 0x0000059c, 0x0000059c, 0x0000059c}, {0x00009c00, 0x00000044, 0x00000044, 0x00000044, 0x00000044}, - {0x00009e00, 0x0372161e, 0x0372161e, 0x037216a0, 0x037216a0}, - {0x00009e04, 0x00182020, 0x00182020, 0x00182020, 0x00182020}, {0x00009e0c, 0x6c4000e2, 0x6d4000e2, 0x6d4000e2, 0x6c4000e2}, {0x00009e10, 0x7ec88d2e, 0x7ec88d2e, 0x7ec80d2e, 0x7ec80d2e}, - {0x00009e14, 0x31395d5e, 0x3139605e, 0x3139605e, 0x31395d5e}, - {0x00009e18, 0x00000000, 0x00000000, 0x00000000, 0x00000000}, + {0x00009e14, 0x31395d53, 0x31396053, 0x312e6053, 0x312e5d53}, {0x00009e1c, 0x0001cf9c, 0x0001cf9c, 0x00021f9c, 0x00021f9c}, {0x00009e20, 0x000003b5, 0x000003b5, 0x000003ce, 0x000003ce}, - {0x00009e2c, 0x0000001c, 0x0000001c, 0x00000021, 0x00000021}, {0x00009e3c, 0xcf946220, 0xcf946220, 0xcf946222, 0xcf946222}, - {0x00009e44, 0x02321e27, 0x02321e27, 0x02282324, 0x02282324}, {0x00009e48, 0x5030201a, 0x5030201a, 0x50302010, 0x50302010}, {0x00009fc8, 0x0003f000, 0x0003f000, 0x0001a000, 0x0001a000}, {0x0000a204, 0x01303fc0, 0x01303fc4, 0x01303fc4, 0x01303fc0}, @@ -850,4 +1060,6 @@ static const u32 ar9485_1_1_mac_core[][2] = { {0x000083d0, 0x000301ff}, }; +#define ar9485_1_1_baseband_core_txfir_coeff_japan_2484 ar9462_2p0_baseband_core_txfir_coeff_japan_2484 + #endif /* INITVALS_9485_H */ diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 4e125d8904a..80bab1b8447 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -129,10 +129,10 @@ void ath_descdma_cleanup(struct ath_softc *sc, struct ath_descdma *dd, #define ATH_TXMAXTRY 13 #define TID_TO_WME_AC(_tid) \ - ((((_tid) == 0) || ((_tid) == 3)) ? WME_AC_BE : \ - (((_tid) == 1) || ((_tid) == 2)) ? WME_AC_BK : \ - (((_tid) == 4) || ((_tid) == 5)) ? WME_AC_VI : \ - WME_AC_VO) + ((((_tid) == 0) || ((_tid) == 3)) ? IEEE80211_AC_BE : \ + (((_tid) == 1) || ((_tid) == 2)) ? IEEE80211_AC_BK : \ + (((_tid) == 4) || ((_tid) == 5)) ? IEEE80211_AC_VI : \ + IEEE80211_AC_VO) #define ATH_AGGR_DELIM_SZ 4 #define ATH_AGGR_MINPLEN 256 /* in bytes, minimum packet length */ @@ -259,13 +259,10 @@ struct ath_atx_tid { }; struct ath_node { -#ifdef CONFIG_ATH9K_DEBUGFS - struct list_head list; /* for sc->nodes */ -#endif struct ieee80211_sta *sta; /* station struct we're part of */ struct ieee80211_vif *vif; /* interface with which we're associated */ struct ath_atx_tid tid[WME_NUM_TID]; - struct ath_atx_ac ac[WME_NUM_AC]; + struct ath_atx_ac ac[IEEE80211_NUM_ACS]; int ps_key; u16 maxampdu; @@ -299,9 +296,9 @@ struct ath_tx { struct list_head txbuf; struct ath_txq txq[ATH9K_NUM_TX_QUEUES]; struct ath_descdma txdma; - struct ath_txq *txq_map[WME_NUM_AC]; - u32 txq_max_pending[WME_NUM_AC]; - u16 max_aggr_framelen[WME_NUM_AC][4][32]; + struct ath_txq *txq_map[IEEE80211_NUM_ACS]; + u32 txq_max_pending[IEEE80211_NUM_ACS]; + u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32]; }; struct ath_rx_edma { @@ -461,6 +458,12 @@ void ath9k_queue_reset(struct ath_softc *sc, enum ath_reset_type type); /* BTCOEX */ /**********/ +#define ATH_DUMP_BTCOEX(_s, _val) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%20s : %10d\n", _s, (_val)); \ + } while (0) + enum bt_op_flags { BT_OP_PRIORITY_DETECTED, BT_OP_SCAN, @@ -482,6 +485,7 @@ struct ath_btcoex { int rssi_count; struct ath_gen_timer *no_stomp_timer; /* Timer for no BT stomping */ struct ath_mci_profile mci; + u8 stomp_audio; }; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT @@ -494,7 +498,7 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc); void ath9k_btcoex_handle_interrupt(struct ath_softc *sc, u32 status); u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen); void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc); -int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size); +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size); #else static inline int ath9k_init_btcoex(struct ath_softc *sc) { @@ -521,8 +525,7 @@ static inline u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, static inline void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { } -static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, - u32 len, u32 size) +static inline int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) { return 0; } @@ -717,9 +720,6 @@ struct ath_softc { #ifdef CONFIG_ATH9K_DEBUGFS struct ath9k_debug debug; - spinlock_t nodes_lock; - struct list_head nodes; /* basically, stations */ - unsigned int tx_complete_poll_work_seen; #endif struct ath_beacon_config cur_beacon_conf; struct delayed_work tx_complete_work; diff --git a/drivers/net/wireless/ath/ath9k/beacon.c b/drivers/net/wireless/ath/ath9k/beacon.c index 1b48414dca9..531fffd801a 100644 --- a/drivers/net/wireless/ath/ath9k/beacon.c +++ b/drivers/net/wireless/ath/ath9k/beacon.c @@ -46,7 +46,7 @@ static void ath9k_beaconq_config(struct ath_softc *sc) qi.tqi_cwmax = 0; } else { /* Adhoc mode; important thing is to use 2x cwmin. */ - txq = sc->tx.txq_map[WME_AC_BE]; + txq = sc->tx.txq_map[IEEE80211_AC_BE]; ath9k_hw_get_txq_props(ah, txq->axq_qnum, &qi_be); qi.tqi_aifs = qi_be.tqi_aifs; if (ah->slottime == ATH9K_SLOT_TIME_20) diff --git a/drivers/net/wireless/ath/ath9k/btcoex.c b/drivers/net/wireless/ath/ath9k/btcoex.c index c90e9bc4b02..9963b0bf9f7 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.c +++ b/drivers/net/wireless/ath/ath9k/btcoex.c @@ -49,6 +49,7 @@ static const u32 mci_wlan_weights[ATH_BTCOEX_STOMP_MAX] { 0x01017d01, 0x3b3b3b01, 0x3b3b3b01, 0x3b3b3b3b }, /* STOMP_LOW */ { 0x01017d01, 0x01010101, 0x01010101, 0x01010101 }, /* STOMP_NONE */ { 0x01017d01, 0x013b0101, 0x3b3b0101, 0x3b3b013b }, /* STOMP_LOW_FTP */ + { 0xffffff01, 0xffffffff, 0xffffff01, 0xffffffff }, /* STOMP_AUDIO */ }; void ath9k_hw_init_btcoex_hw(struct ath_hw *ah, int qnum) diff --git a/drivers/net/wireless/ath/ath9k/btcoex.h b/drivers/net/wireless/ath/ath9k/btcoex.h index 2f84ab273d0..6de26ea5d5f 100644 --- a/drivers/net/wireless/ath/ath9k/btcoex.h +++ b/drivers/net/wireless/ath/ath9k/btcoex.h @@ -50,6 +50,7 @@ enum ath_stomp_type { ATH_BTCOEX_STOMP_LOW, ATH_BTCOEX_STOMP_NONE, ATH_BTCOEX_STOMP_LOW_FTP, + ATH_BTCOEX_STOMP_AUDIO, ATH_BTCOEX_STOMP_MAX }; diff --git a/drivers/net/wireless/ath/ath9k/common.h b/drivers/net/wireless/ath/ath9k/common.h index ad14fecc76c..76b54390031 100644 --- a/drivers/net/wireless/ath/ath9k/common.h +++ b/drivers/net/wireless/ath/ath9k/common.h @@ -28,13 +28,6 @@ #define WME_MAX_BA WME_BA_BMP_SIZE #define ATH_TID_MAX_BUFS (2 * WME_MAX_BA) -/* These must match mac80211 skb queue mapping numbers */ -#define WME_AC_VO 0 -#define WME_AC_VI 1 -#define WME_AC_BE 2 -#define WME_AC_BK 3 -#define WME_NUM_AC 4 - #define ATH_RSSI_DUMMY_MARKER 0x127 #define ATH_RSSI_LPF_LEN 10 #define RSSI_LPF_THRESHOLD -20 diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index a8be94b2a53..939308c2571 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -512,62 +512,19 @@ static const struct file_operations fops_interrupt = { .llseek = default_llseek, }; -#define PR_QNUM(_n) sc->tx.txq_map[_n]->axq_qnum -#define PR(str, elem) \ - do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem, \ - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem); \ - if (len >= size) \ - goto done; \ -} while(0) - -#define PRX(str, elem) \ -do { \ - len += snprintf(buf + len, size - len, \ - "%s%13u%11u%10u%10u\n", str, \ - (unsigned int)(sc->tx.txq_map[WME_AC_BE]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_BK]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_VI]->elem), \ - (unsigned int)(sc->tx.txq_map[WME_AC_VO]->elem)); \ - if (len >= size) \ - goto done; \ -} while(0) - -#define PRQLE(str, elem) \ -do { \ - len += snprintf(buf + len, size - len, \ - "%s%13i%11i%10i%10i\n", str, \ - list_empty(&sc->tx.txq_map[WME_AC_BE]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_BK]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_VI]->elem), \ - list_empty(&sc->tx.txq_map[WME_AC_VO]->elem)); \ - if (len >= size) \ - goto done; \ -} while (0) - static ssize_t read_file_xmit(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; char *buf; - unsigned int len = 0, size = 8000; - int i; + unsigned int len = 0, size = 2048; ssize_t retval = 0; - char tmp[32]; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - len += sprintf(buf, "Num-Tx-Queues: %i tx-queues-setup: 0x%x" - " poll-work-seen: %u\n" - "%30s %10s%10s%10s\n\n", - ATH9K_NUM_TX_QUEUES, sc->tx.txqsetup, - sc->tx_complete_poll_work_seen, + len += sprintf(buf, "%30s %10s%10s%10s\n\n", "BE", "BK", "VI", "VO"); PR("MPDUs Queued: ", queued); @@ -587,62 +544,11 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, PR("DELIM Underrun: ", delim_underrun); PR("TX-Pkts-All: ", tx_pkts_all); PR("TX-Bytes-All: ", tx_bytes_all); - PR("hw-put-tx-buf: ", puttxbuf); - PR("hw-tx-start: ", txstart); - PR("hw-tx-proc-desc: ", txprocdesc); + PR("HW-put-tx-buf: ", puttxbuf); + PR("HW-tx-start: ", txstart); + PR("HW-tx-proc-desc: ", txprocdesc); PR("TX-Failed: ", txfailed); - len += snprintf(buf + len, size - len, - "%s%11p%11p%10p%10p\n", "txq-memory-address:", - sc->tx.txq_map[WME_AC_BE], - sc->tx.txq_map[WME_AC_BK], - sc->tx.txq_map[WME_AC_VI], - sc->tx.txq_map[WME_AC_VO]); - if (len >= size) - goto done; - - PRX("axq-qnum: ", axq_qnum); - PRX("axq-depth: ", axq_depth); - PRX("axq-ampdu_depth: ", axq_ampdu_depth); - PRX("axq-stopped ", stopped); - PRX("tx-in-progress ", axq_tx_inprogress); - PRX("pending-frames ", pending_frames); - PRX("txq_headidx: ", txq_headidx); - PRX("txq_tailidx: ", txq_headidx); - - PRQLE("axq_q empty: ", axq_q); - PRQLE("axq_acq empty: ", axq_acq); - for (i = 0; i < ATH_TXFIFO_DEPTH; i++) { - snprintf(tmp, sizeof(tmp) - 1, "txq_fifo[%i] empty: ", i); - PRQLE(tmp, txq_fifo[i]); - } - - /* Print out more detailed queue-info */ - for (i = 0; i <= WME_AC_BK; i++) { - struct ath_txq *txq = &(sc->tx.txq[i]); - struct ath_atx_ac *ac; - struct ath_atx_tid *tid; - if (len >= size) - goto done; - spin_lock_bh(&txq->axq_lock); - if (!list_empty(&txq->axq_acq)) { - ac = list_first_entry(&txq->axq_acq, struct ath_atx_ac, - list); - len += snprintf(buf + len, size - len, - "txq[%i] first-ac: %p sched: %i\n", - i, ac, ac->sched); - if (list_empty(&ac->tid_q) || (len >= size)) - goto done_for; - tid = list_first_entry(&ac->tid_q, struct ath_atx_tid, - list); - len += snprintf(buf + len, size - len, - " first-tid: %p sched: %i paused: %i\n", - tid, tid->sched, tid->paused); - } - done_for: - spin_unlock_bh(&txq->axq_lock); - } -done: if (len > size) len = size; @@ -652,62 +558,41 @@ done: return retval; } -static ssize_t read_file_stations(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t read_file_queues(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) { struct ath_softc *sc = file->private_data; + struct ath_txq *txq; char *buf; - unsigned int len = 0, size = 64000; - struct ath_node *an = NULL; + unsigned int len = 0, size = 1024; ssize_t retval = 0; - int q; + int i; + char *qname[4] = {"VO", "VI", "BE", "BK"}; buf = kzalloc(size, GFP_KERNEL); if (buf == NULL) return -ENOMEM; - len += snprintf(buf + len, size - len, - "Stations:\n" - " tid: addr sched paused buf_q-empty an ac baw\n" - " ac: addr sched tid_q-empty txq\n"); - - spin_lock(&sc->nodes_lock); - list_for_each_entry(an, &sc->nodes, list) { - unsigned short ma = an->maxampdu; - if (ma == 0) - ma = 65535; /* see ath_lookup_rate */ - len += snprintf(buf + len, size - len, - "iface: %pM sta: %pM max-ampdu: %hu mpdu-density: %uus\n", - an->vif->addr, an->sta->addr, ma, - (unsigned int)(an->mpdudensity)); - if (len >= size) - goto done; - - for (q = 0; q < WME_NUM_TID; q++) { - struct ath_atx_tid *tid = &(an->tid[q]); - len += snprintf(buf + len, size - len, - " tid: %p %s %s %i %p %p %hu\n", - tid, tid->sched ? "sched" : "idle", - tid->paused ? "paused" : "running", - skb_queue_empty(&tid->buf_q), - tid->an, tid->ac, tid->baw_size); - if (len >= size) - goto done; - } + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = sc->tx.txq_map[i]; + len += snprintf(buf + len, size - len, "(%s): ", qname[i]); - for (q = 0; q < WME_NUM_AC; q++) { - struct ath_atx_ac *ac = &(an->ac[q]); - len += snprintf(buf + len, size - len, - " ac: %p %s %i %p\n", - ac, ac->sched ? "sched" : "idle", - list_empty(&ac->tid_q), ac->txq); - if (len >= size) - goto done; - } + ath_txq_lock(sc, txq); + + len += snprintf(buf + len, size - len, "%s: %d ", + "qnum", txq->axq_qnum); + len += snprintf(buf + len, size - len, "%s: %2d ", + "qdepth", txq->axq_depth); + len += snprintf(buf + len, size - len, "%s: %2d ", + "ampdu-depth", txq->axq_ampdu_depth); + len += snprintf(buf + len, size - len, "%s: %3d ", + "pending", txq->pending_frames); + len += snprintf(buf + len, size - len, "%s: %d\n", + "stopped", txq->stopped); + + ath_txq_unlock(sc, txq); } -done: - spin_unlock(&sc->nodes_lock); if (len > size) len = size; @@ -837,6 +722,9 @@ static ssize_t read_file_reset(struct file *file, char __user *user_buf, len += snprintf(buf + len, sizeof(buf) - len, "%17s: %2d\n", "PLL RX Hang", sc->debug.stats.reset[RESET_TYPE_PLL_HANG]); + len += snprintf(buf + len, sizeof(buf) - len, + "%17s: %2d\n", "MCI Reset", + sc->debug.stats.reset[RESET_TYPE_MCI]); if (len > sizeof(buf)) len = sizeof(buf); @@ -919,8 +807,8 @@ static const struct file_operations fops_xmit = { .llseek = default_llseek, }; -static const struct file_operations fops_stations = { - .read = read_file_stations, +static const struct file_operations fops_queues = { + .read = read_file_queues, .open = simple_open, .owner = THIS_MODULE, .llseek = default_llseek, @@ -1599,8 +1487,14 @@ static ssize_t read_file_btcoex(struct file *file, char __user *user_buf, if (buf == NULL) return -ENOMEM; - len = ath9k_dump_btcoex(sc, buf, len, size); + if (!sc->sc_ah->common.btcoex_enabled) { + len = snprintf(buf, size, "%s\n", + "BTCOEX is disabled"); + goto exit; + } + len = ath9k_dump_btcoex(sc, buf, size); +exit: retval = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); @@ -1638,16 +1532,16 @@ int ath9k_init_debug(struct ath_hw *ah) &fops_interrupt); debugfs_create_file("xmit", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_xmit); + debugfs_create_file("queues", S_IRUSR, sc->debug.debugfs_phy, sc, + &fops_queues); debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_BK]); + &sc->tx.txq_max_pending[IEEE80211_AC_BK]); debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_BE]); + &sc->tx.txq_max_pending[IEEE80211_AC_BE]); debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_VI]); + &sc->tx.txq_max_pending[IEEE80211_AC_VI]); debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy, - &sc->tx.txq_max_pending[WME_AC_VO]); - debugfs_create_file("stations", S_IRUSR, sc->debug.debugfs_phy, sc, - &fops_stations); + &sc->tx.txq_max_pending[IEEE80211_AC_VO]); debugfs_create_file("misc", S_IRUSR, sc->debug.debugfs_phy, sc, &fops_misc); debugfs_create_file("reset", S_IRUSR, sc->debug.debugfs_phy, sc, diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireless/ath/ath9k/debug.h index 2ed9785a38f..f9bee18de5a 100644 --- a/drivers/net/wireless/ath/ath9k/debug.h +++ b/drivers/net/wireless/ath/ath9k/debug.h @@ -41,6 +41,7 @@ enum ath_reset_type { RESET_TYPE_PLL_HANG, RESET_TYPE_MAC_HANG, RESET_TYPE_BEACON_STUCK, + RESET_TYPE_MCI, __RESET_TYPE_MAX }; @@ -178,6 +179,21 @@ struct ath_tx_stats { u32 txfailed; }; +/* + * Various utility macros to print TX/Queue counters. + */ +#define PR_QNUM(_n) sc->tx.txq_map[_n]->axq_qnum +#define TXSTATS sc->debug.stats.txstats +#define PR(str, elem) \ + do { \ + len += snprintf(buf + len, size - len, \ + "%s%13u%11u%10u%10u\n", str, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BE)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_BK)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_VI)].elem, \ + TXSTATS[PR_QNUM(IEEE80211_AC_VO)].elem); \ + } while(0) + #define RX_STAT_INC(c) (sc->debug.stats.rxstats.c++) /** @@ -226,7 +242,7 @@ struct ath_rx_stats { struct ath_stats { struct ath_interrupt_stats istats; - struct ath_tx_stats txstats[ATH9K_NUM_TX_QUEUES]; + struct ath_tx_stats txstats[IEEE80211_NUM_ACS]; struct ath_rx_stats rxstats; struct ath_dfs_stats dfs_stats; u32 reset[__RESET_TYPE_MAX]; diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c index ea2a6cf7ef2..24877b00cbf 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.c @@ -42,10 +42,15 @@ struct radar_types { #define MIN_PPB_THRESH 50 #define PPB_THRESH(PPB) ((PPB * MIN_PPB_THRESH + 50) / 100) #define PRF2PRI(PRF) ((1000000 + PRF / 2) / PRF) +/* percentage of pulse width tolerance */ +#define WIDTH_TOLERANCE 5 +#define WIDTH_LOWER(X) ((X*(100-WIDTH_TOLERANCE)+50)/100) +#define WIDTH_UPPER(X) ((X*(100+WIDTH_TOLERANCE)+50)/100) #define ETSI_PATTERN(ID, WMIN, WMAX, PMIN, PMAX, PRF, PPB) \ { \ - ID, WMIN, WMAX, (PRF2PRI(PMAX) - PRI_TOLERANCE), \ + ID, WIDTH_LOWER(WMIN), WIDTH_UPPER(WMAX), \ + (PRF2PRI(PMAX) - PRI_TOLERANCE), \ (PRF2PRI(PMIN) * PRF + PRI_TOLERANCE), PRF, PPB * PRF, \ PPB_THRESH(PPB), PRI_TOLERANCE, \ } @@ -274,7 +279,7 @@ static bool dpd_set_domain(struct dfs_pattern_detector *dpd, static struct dfs_pattern_detector default_dpd = { .exit = dpd_exit, - .set_domain = dpd_set_domain, + .set_dfs_domain = dpd_set_domain, .add_pulse = dpd_add_pulse, .region = NL80211_DFS_UNSET, }; @@ -291,10 +296,11 @@ dfs_pattern_detector_init(enum nl80211_dfs_regions region) *dpd = default_dpd; INIT_LIST_HEAD(&dpd->channel_detectors); - if (dpd->set_domain(dpd, region)) + if (dpd->set_dfs_domain(dpd, region)) return dpd; pr_err("Could not set DFS domain to %d. ", region); + kfree(dpd); return NULL; } EXPORT_SYMBOL(dfs_pattern_detector_init); diff --git a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h index fd0328a3099..cda52f39f28 100644 --- a/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h +++ b/drivers/net/wireless/ath/ath9k/dfs_pattern_detector.h @@ -62,7 +62,7 @@ struct radar_detector_specs { /** * struct dfs_pattern_detector - DFS pattern detector * @exit(): destructor - * @set_domain(): set DFS domain, resets detector lines upon domain changes + * @set_dfs_domain(): set DFS domain, resets detector lines upon domain changes * @add_pulse(): add radar pulse to detector, returns true on detection * @region: active DFS region, NL80211_DFS_UNSET until set * @num_radar_types: number of different radar types @@ -72,7 +72,7 @@ struct radar_detector_specs { */ struct dfs_pattern_detector { void (*exit)(struct dfs_pattern_detector *dpd); - bool (*set_domain)(struct dfs_pattern_detector *dpd, + bool (*set_dfs_domain)(struct dfs_pattern_detector *dpd, enum nl80211_dfs_regions region); bool (*add_pulse)(struct dfs_pattern_detector *dpd, struct pulse_event *pe); diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index a8ea57b9f49..4b412aaf4f3 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -247,6 +247,9 @@ static void ath_btcoex_period_timer(unsigned long data) stomp_type = ATH_BTCOEX_STOMP_ALL; timer_period = btcoex->btscan_no_stomp; } + } else if (btcoex->stomp_audio >= 5) { + stomp_type = ATH_BTCOEX_STOMP_AUDIO; + btcoex->stomp_audio = 0; } ath9k_hw_btcoex_bt_stomp(ah, stomp_type); @@ -295,7 +298,7 @@ static void ath_btcoex_no_stomp_timer(void *arg) (!(ah->caps.hw_caps & ATH9K_HW_CAP_MCI) && test_bit(BT_OP_SCAN, &btcoex->op_flags))) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_NONE); - else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) + else if (btcoex->bt_stomp_type == ATH_BTCOEX_STOMP_ALL) ath9k_hw_btcoex_bt_stomp(ah, ATH_BTCOEX_STOMP_LOW); ath9k_hw_btcoex_enable(ah); @@ -471,7 +474,7 @@ int ath9k_init_btcoex(struct ath_softc *sc) r = ath_init_btcoex_timer(sc); if (r) return -1; - txq = sc->tx.txq_map[WME_AC_BE]; + txq = sc->tx.txq_map[IEEE80211_AC_BE]; ath9k_hw_init_btcoex_hw(sc->sc_ah, txq->axq_qnum); sc->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; if (ath9k_hw_mci_is_enabled(ah)) { @@ -494,35 +497,31 @@ int ath9k_init_btcoex(struct ath_softc *sc) return 0; } -int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size) +static int ath9k_dump_mci_btcoex(struct ath_softc *sc, u8 *buf, u32 size) { -#define ATH_DUMP_BTCOEX(_s, _val) \ - do { \ - len += snprintf(buf + len, size - len, \ - "%20s : %10d\n", _s, (_val)); \ - } while (0) - struct ath_btcoex *btcoex = &sc->btcoex; struct ath_mci_profile *mci = &btcoex->mci; struct ath_hw *ah = sc->sc_ah; struct ath_btcoex_hw *btcoex_hw = &ah->btcoex_hw; + u32 len = 0; int i; ATH_DUMP_BTCOEX("Total BT profiles", NUM_PROF(mci)); - ATH_DUMP_BTCOEX("Number of MGMT", mci->num_mgmt); - ATH_DUMP_BTCOEX("Number of SCO", mci->num_sco); - ATH_DUMP_BTCOEX("Number of A2DP", mci->num_a2dp); - ATH_DUMP_BTCOEX("Number of HID", mci->num_hid); - ATH_DUMP_BTCOEX("Number of PAN", mci->num_pan); - ATH_DUMP_BTCOEX("Number of ACL", mci->num_other_acl); - ATH_DUMP_BTCOEX("Number of BDR", mci->num_bdr); + ATH_DUMP_BTCOEX("MGMT", mci->num_mgmt); + ATH_DUMP_BTCOEX("SCO", mci->num_sco); + ATH_DUMP_BTCOEX("A2DP", mci->num_a2dp); + ATH_DUMP_BTCOEX("HID", mci->num_hid); + ATH_DUMP_BTCOEX("PAN", mci->num_pan); + ATH_DUMP_BTCOEX("ACL", mci->num_other_acl); + ATH_DUMP_BTCOEX("BDR", mci->num_bdr); ATH_DUMP_BTCOEX("Aggr. Limit", mci->aggr_limit); ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type); ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period); ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle); ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time); ATH_DUMP_BTCOEX("Concurrent Tx", btcoex_hw->mci.concur_tx); - ATH_DUMP_BTCOEX("Concur RSSI count", btcoex->rssi_count); + ATH_DUMP_BTCOEX("Concurrent RSSI cnt", btcoex->rssi_count); + len += snprintf(buf + len, size - len, "BT Weights: "); for (i = 0; i < AR9300_NUM_BT_WEIGHTS; i++) len += snprintf(buf + len, size - len, "%08x ", @@ -537,9 +536,32 @@ int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 len, u32 size) for (i = 0; i < ATH_BTCOEX_STOMP_MAX; i++) len += snprintf(buf + len, size - len, "%08x ", btcoex_hw->tx_prio[i]); + len += snprintf(buf + len, size - len, "\n"); -#undef ATH_DUMP_BTCOEX return len; } + +static int ath9k_dump_legacy_btcoex(struct ath_softc *sc, u8 *buf, u32 size) +{ + + struct ath_btcoex *btcoex = &sc->btcoex; + u32 len = 0; + + ATH_DUMP_BTCOEX("Stomp Type", btcoex->bt_stomp_type); + ATH_DUMP_BTCOEX("BTCoex Period (msec)", btcoex->btcoex_period); + ATH_DUMP_BTCOEX("Duty Cycle", btcoex->duty_cycle); + ATH_DUMP_BTCOEX("BT Wait time", btcoex->bt_wait_time); + + return len; +} + +int ath9k_dump_btcoex(struct ath_softc *sc, u8 *buf, u32 size) +{ + if (ath9k_hw_mci_is_enabled(sc->sc_ah)) + return ath9k_dump_mci_btcoex(sc, buf, size); + else + return ath9k_dump_legacy_btcoex(sc, buf, size); +} + #endif /* CONFIG_ATH9K_BTCOEX_SUPPORT */ diff --git a/drivers/net/wireless/ath/ath9k/htc.h b/drivers/net/wireless/ath/ath9k/htc.h index b30596fcf73..96bfb18078f 100644 --- a/drivers/net/wireless/ath/ath9k/htc.h +++ b/drivers/net/wireless/ath/ath9k/htc.h @@ -331,7 +331,7 @@ struct ath_tx_stats { u32 skb_success; u32 skb_failed; u32 cab_queued; - u32 queue_stats[WME_NUM_AC]; + u32 queue_stats[IEEE80211_NUM_ACS]; }; struct ath_rx_stats { @@ -493,7 +493,7 @@ struct ath9k_htc_priv { int beaconq; int cabq; - int hwq_map[WME_NUM_AC]; + int hwq_map[IEEE80211_NUM_ACS]; #ifdef CONFIG_ATH9K_BTCOEX_SUPPORT struct ath_btcoex btcoex; diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index f42d2eb6af9..d0ce1f5bba1 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -33,7 +33,7 @@ void ath9k_htc_beaconq_config(struct ath9k_htc_priv *priv) qi.tqi_cwmin = 0; qi.tqi_cwmax = 0; } else if (priv->ah->opmode == NL80211_IFTYPE_ADHOC) { - int qnum = priv->hwq_map[WME_AC_BE]; + int qnum = priv->hwq_map[IEEE80211_AC_BE]; ath9k_hw_get_txq_props(ah, qnum, &qi_be); @@ -587,9 +587,9 @@ static bool ath9k_htc_check_beacon_config(struct ath9k_htc_priv *priv, (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); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_beacon_iter, &beacon_configured); if (beacon_configured) { ath_dbg(common, CONFIG, diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c index 3035deb7a0c..87110de577e 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_debug.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_debug.c @@ -218,16 +218,16 @@ static ssize_t read_file_xmit(struct file *file, char __user *user_buf, len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BE queued", - priv->debug.tx_stats.queue_stats[WME_AC_BE]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BE]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "BK queued", - priv->debug.tx_stats.queue_stats[WME_AC_BK]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_BK]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VI queued", - priv->debug.tx_stats.queue_stats[WME_AC_VI]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VI]); len += snprintf(buf + len, sizeof(buf) - len, "%20s : %10u\n", "VO queued", - priv->debug.tx_stats.queue_stats[WME_AC_VO]); + priv->debug.tx_stats.queue_stats[IEEE80211_AC_VO]); if (len > sizeof(buf)) len = sizeof(buf); diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c index 0eacfc13c91..105582d6b71 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_gpio.c @@ -207,7 +207,7 @@ void ath9k_htc_init_btcoex(struct ath9k_htc_priv *priv, char *product) priv->btcoex.bt_stomp_type = ATH_BTCOEX_STOMP_LOW; ath9k_hw_btcoex_init_3wire(priv->ah); ath_htc_init_btcoex_work(priv); - qnum = priv->hwq_map[WME_AC_BE]; + qnum = priv->hwq_map[IEEE80211_AC_BE]; ath9k_hw_init_btcoex_hw(priv->ah, qnum); break; default: diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c index 5ecf1287ddd..05d5ba66cac 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c @@ -549,20 +549,20 @@ static int ath9k_init_queues(struct ath9k_htc_priv *priv) goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_BE)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BE)) { ath_err(common, "Unable to setup xmit queue for BE traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_BK)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_BK)) { ath_err(common, "Unable to setup xmit queue for BK traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_VI)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VI)) { ath_err(common, "Unable to setup xmit queue for VI traffic\n"); goto err; } - if (!ath9k_htc_txq_setup(priv, WME_AC_VO)) { + if (!ath9k_htc_txq_setup(priv, IEEE80211_AC_VO)) { ath_err(common, "Unable to setup xmit queue for VO traffic\n"); goto err; } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 66f6a74c508..9c07a8fa513 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -127,8 +127,9 @@ 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); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_vif_iter, priv); if (priv->rearm_ani) ath9k_htc_start_ani(priv); @@ -165,8 +166,9 @@ static void ath9k_htc_set_bssid_mask(struct ath9k_htc_priv *priv, 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); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_bssid_iter, &iter_data); memcpy(common->bssidmask, iter_data.mask, ETH_ALEN); ath_hw_setbssidmask(common); @@ -1144,8 +1146,9 @@ static void ath9k_htc_remove_interface(struct ieee80211_hw *hw, */ 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); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_vif_iter, priv); if (!priv->rearm_ani) ath9k_htc_stop_ani(priv); } @@ -1346,7 +1349,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, struct ath9k_tx_queue_info qi; int ret = 0, qnum; - if (queue >= WME_NUM_AC) + if (queue >= IEEE80211_NUM_ACS) return 0; mutex_lock(&priv->mutex); @@ -1373,7 +1376,7 @@ static int ath9k_htc_conf_tx(struct ieee80211_hw *hw, } if ((priv->ah->opmode == NL80211_IFTYPE_ADHOC) && - (qnum == priv->hwq_map[WME_AC_BE])) + (qnum == priv->hwq_map[IEEE80211_AC_BE])) ath9k_htc_beaconq_config(priv); out: ath9k_htc_ps_restore(priv); @@ -1466,8 +1469,9 @@ static void ath9k_htc_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif) static void ath9k_htc_choose_set_bssid(struct ath9k_htc_priv *priv) { if (priv->num_sta_assoc_vif == 1) { - ieee80211_iterate_active_interfaces_atomic(priv->hw, - ath9k_htc_bss_iter, priv); + ieee80211_iterate_active_interfaces_atomic( + priv->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_htc_bss_iter, priv); ath9k_htc_set_bssid(priv); } } diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c index 06cdcb772d7..28cd50ee521 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c @@ -21,10 +21,10 @@ /******/ static const int subtype_txq_to_hwq[] = { - [WME_AC_BE] = ATH_TXQ_AC_BE, - [WME_AC_BK] = ATH_TXQ_AC_BK, - [WME_AC_VI] = ATH_TXQ_AC_VI, - [WME_AC_VO] = ATH_TXQ_AC_VO, + [IEEE80211_AC_BE] = ATH_TXQ_AC_BE, + [IEEE80211_AC_BK] = ATH_TXQ_AC_BK, + [IEEE80211_AC_VI] = ATH_TXQ_AC_VI, + [IEEE80211_AC_VO] = ATH_TXQ_AC_VO, }; #define ATH9K_HTC_INIT_TXQ(subtype) do { \ @@ -41,15 +41,15 @@ int get_hw_qnum(u16 queue, int *hwq_map) { switch (queue) { case 0: - return hwq_map[WME_AC_VO]; + return hwq_map[IEEE80211_AC_VO]; case 1: - return hwq_map[WME_AC_VI]; + return hwq_map[IEEE80211_AC_VI]; case 2: - return hwq_map[WME_AC_BE]; + return hwq_map[IEEE80211_AC_BE]; case 3: - return hwq_map[WME_AC_BK]; + return hwq_map[IEEE80211_AC_BK]; default: - return hwq_map[WME_AC_BE]; + return hwq_map[IEEE80211_AC_BE]; } } @@ -106,20 +106,20 @@ static inline enum htc_endpoint_id get_htc_epid(struct ath9k_htc_priv *priv, switch (qnum) { case 0: - TX_QSTAT_INC(WME_AC_VO); + TX_QSTAT_INC(IEEE80211_AC_VO); epid = priv->data_vo_ep; break; case 1: - TX_QSTAT_INC(WME_AC_VI); + TX_QSTAT_INC(IEEE80211_AC_VI); epid = priv->data_vi_ep; break; case 2: - TX_QSTAT_INC(WME_AC_BE); + TX_QSTAT_INC(IEEE80211_AC_BE); epid = priv->data_be_ep; break; case 3: default: - TX_QSTAT_INC(WME_AC_BK); + TX_QSTAT_INC(IEEE80211_AC_BK); epid = priv->data_bk_ep; break; } @@ -1082,7 +1082,7 @@ static bool ath9k_rx_prepare(struct ath9k_htc_priv *priv, 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_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; return true; diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c index 741e73dece3..e06bcec655a 100644 --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c @@ -2561,11 +2561,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah) pCap->hw_caps |= ATH9K_HW_CAP_ANT_DIV_COMB; } - if (AR_SREV_9485_10(ah)) { - pCap->pcie_lcr_extsync_en = true; - pCap->pcie_lcr_offset = 0x80; - } - if (ath9k_hw_dfs_tested(ah)) pCap->hw_caps |= ATH9K_HW_CAP_DFS; diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h index 3e73bfe2315..3636dabf03e 100644 --- a/drivers/net/wireless/ath/ath9k/hw.h +++ b/drivers/net/wireless/ath/ath9k/hw.h @@ -273,8 +273,6 @@ struct ath9k_hw_capabilities { u8 rx_status_len; u8 tx_desc_len; u8 txs_len; - u16 pcie_lcr_offset; - bool pcie_lcr_extsync_en; }; struct ath9k_ops_config { @@ -877,7 +875,6 @@ struct ath_hw { struct ar5416IniArray iniModesTxGain; struct ar5416IniArray iniCckfirNormal; struct ar5416IniArray iniCckfirJapan2484; - struct ar5416IniArray ini_japan2484; struct ar5416IniArray iniModes_9271_ANI_reg; struct ar5416IniArray ini_radio_post_sys2ant; @@ -930,7 +927,6 @@ struct ath_bus_ops { void (*read_cachesize)(struct ath_common *common, int *csz); bool (*eeprom_read)(struct ath_common *common, u32 off, u16 *data); void (*bt_coex_prep)(struct ath_common *common); - void (*extn_synch_en)(struct ath_common *common); void (*aspm_init)(struct ath_common *common); }; diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index 546bae93647..80cae53a33e 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -435,7 +435,7 @@ static int ath9k_init_queues(struct ath_softc *sc) sc->config.cabqReadytime = ATH_CABQ_READY_TIME; ath_cabq_update(sc); - for (i = 0; i < WME_NUM_AC; i++) { + for (i = 0; i < IEEE80211_NUM_ACS; i++) { sc->tx.txq_map[i] = ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i); sc->tx.txq_map[i]->mac80211_qnum = i; sc->tx.txq_max_pending[i] = ATH_MAX_QDEPTH; @@ -563,10 +563,6 @@ static int ath9k_init_softc(u16 devid, struct ath_softc *sc, spin_lock_init(&sc->sc_serial_rw); spin_lock_init(&sc->sc_pm_lock); mutex_init(&sc->mutex); -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock_init(&sc->nodes_lock); - INIT_LIST_HEAD(&sc->nodes); -#endif #ifdef CONFIG_ATH9K_MAC_DEBUG spin_lock_init(&sc->debug.samp_lock); #endif diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index 223b9693527..fc6b075ad63 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -27,9 +27,6 @@ void ath_tx_complete_poll_work(struct work_struct *work) struct ath_txq *txq; int i; bool needreset = false; -#ifdef CONFIG_ATH9K_DEBUGFS - sc->tx_complete_poll_work_seen++; -#endif for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) if (ATH_TXQ_SETUP(sc, i)) { @@ -211,7 +208,7 @@ static bool ath_paprd_send_frame(struct ath_softc *sc, struct sk_buff *skb, int int time_left; memset(&txctl, 0, sizeof(txctl)); - txctl.txq = sc->tx.txq_map[WME_AC_BE]; + txctl.txq = sc->tx.txq_map[IEEE80211_AC_BE]; memset(tx_info, 0, sizeof(*tx_info)); tx_info->band = hw->conf.channel->band; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 578a7234aa5..0653dbc99e3 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -331,11 +331,6 @@ static void ath_node_attach(struct ath_softc *sc, struct ieee80211_sta *sta, u8 density; an = (struct ath_node *)sta->drv_priv; -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock(&sc->nodes_lock); - list_add(&an->list, &sc->nodes); - spin_unlock(&sc->nodes_lock); -#endif an->sta = sta; an->vif = vif; @@ -352,13 +347,6 @@ static void ath_node_detach(struct ath_softc *sc, struct ieee80211_sta *sta) { struct ath_node *an = (struct ath_node *)sta->drv_priv; -#ifdef CONFIG_ATH9K_DEBUGFS - spin_lock(&sc->nodes_lock); - list_del(&an->list); - spin_unlock(&sc->nodes_lock); - an->sta = NULL; -#endif - if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_HT) ath_tx_node_cleanup(sc, an); } @@ -494,17 +482,6 @@ irqreturn_t ath_isr(int irq, void *dev) if (status & SCHED_INTR) sched = true; -#ifdef CONFIG_PM_SLEEP - if (status & ATH9K_INT_BMISS) { - if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { - ath_dbg(common, ANY, "during WoW we got a BMISS\n"); - atomic_inc(&sc->wow_got_bmiss_intr); - atomic_dec(&sc->wow_sleep_proc_intr); - } - ath_dbg(common, INTERRUPT, "beacon miss interrupt\n"); - } -#endif - /* * If a FATAL or RXORN interrupt is received, we have to reset the * chip immediately. @@ -523,7 +500,15 @@ irqreturn_t ath_isr(int irq, void *dev) goto chip_reset; } - +#ifdef CONFIG_PM_SLEEP + if (status & ATH9K_INT_BMISS) { + if (atomic_read(&sc->wow_sleep_proc_intr) == 0) { + ath_dbg(common, ANY, "during WoW we got a BMISS\n"); + atomic_inc(&sc->wow_got_bmiss_intr); + atomic_dec(&sc->wow_sleep_proc_intr); + } + } +#endif if (status & ATH9K_INT_SWBA) tasklet_schedule(&sc->bcon_tasklet); @@ -686,9 +671,6 @@ static int ath9k_start(struct ieee80211_hw *hw) spin_unlock_bh(&sc->sc_pcu_lock); - if (ah->caps.pcie_lcr_extsync_en && common->bus_ops->extn_synch_en) - common->bus_ops->extn_synch_en(common); - mutex_unlock(&sc->mutex); ath9k_ps_restore(sc); @@ -924,8 +906,9 @@ void ath9k_calculate_iter_data(struct ieee80211_hw *hw, ath9k_vif_iter(iter_data, vif->addr, vif); /* Get list of all active MAC addresses */ - ieee80211_iterate_active_interfaces_atomic(sc->hw, ath9k_vif_iter, - iter_data); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_vif_iter, iter_data); } /* Called with sc->mutex held. */ @@ -975,8 +958,9 @@ static void ath9k_calculate_summary_state(struct ieee80211_hw *hw, if (ah->opmode == NL80211_IFTYPE_STATION && old_opmode == NL80211_IFTYPE_AP && test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags)) { - ieee80211_iterate_active_interfaces_atomic(sc->hw, - ath9k_sta_vif_iter, sc); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_sta_vif_iter, sc); } } @@ -1329,7 +1313,7 @@ static int ath9k_conf_tx(struct ieee80211_hw *hw, struct ath9k_tx_queue_info qi; int ret = 0; - if (queue >= WME_NUM_AC) + if (queue >= IEEE80211_NUM_ACS) return 0; txq = sc->tx.txq_map[queue]; @@ -1505,8 +1489,9 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw, clear_bit(SC_OP_BEACONS, &sc->sc_flags); } - ieee80211_iterate_active_interfaces_atomic(sc->hw, - ath9k_bss_assoc_iter, sc); + ieee80211_iterate_active_interfaces_atomic( + sc->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + ath9k_bss_assoc_iter, sc); if (!test_bit(SC_OP_PRIM_STA_VIF, &sc->sc_flags) && ah->opmode == NL80211_IFTYPE_STATION) { @@ -1956,13 +1941,12 @@ static int ath9k_get_et_sset_count(struct ieee80211_hw *hw, return 0; } -#define PR_QNUM(_n) (sc->tx.txq_map[_n]->axq_qnum) #define AWDATA(elem) \ do { \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].elem; \ - data[i++] = sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].elem; \ + data[i++] = sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].elem; \ } while (0) #define AWDATA_RX(elem) \ @@ -1977,14 +1961,14 @@ static void ath9k_get_et_stats(struct ieee80211_hw *hw, struct ath_softc *sc = hw->priv; int i = 0; - data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_pkts_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_pkts_all); - data[i++] = (sc->debug.stats.txstats[PR_QNUM(WME_AC_BE)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_BK)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VI)].tx_bytes_all + - sc->debug.stats.txstats[PR_QNUM(WME_AC_VO)].tx_bytes_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_pkts_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_pkts_all); + data[i++] = (sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BE)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_BK)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VI)].tx_bytes_all + + sc->debug.stats.txstats[PR_QNUM(IEEE80211_AC_VO)].tx_bytes_all); AWDATA_RX(rx_pkts_all); AWDATA_RX(rx_bytes_all); diff --git a/drivers/net/wireless/ath/ath9k/mci.c b/drivers/net/wireless/ath/ath9k/mci.c index 0dd2cbb52d6..706378ea3ba 100644 --- a/drivers/net/wireless/ath/ath9k/mci.c +++ b/drivers/net/wireless/ath/ath9k/mci.c @@ -207,23 +207,6 @@ skip_tuning: ath9k_btcoex_timer_resume(sc); } -static void ath_mci_wait_btcal_done(struct ath_softc *sc) -{ - struct ath_hw *ah = sc->sc_ah; - - /* Stop tx & rx */ - ieee80211_stop_queues(sc->hw); - ath_stoprecv(sc); - ath_drain_all_txq(sc, false); - - /* Wait for cal done */ - ar9003_mci_start_reset(ah, ah->curchan); - - /* Resume tx & rx */ - ath_startrecv(sc); - ieee80211_wake_queues(sc->hw); -} - static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) { struct ath_hw *ah = sc->sc_ah; @@ -235,7 +218,7 @@ static void ath_mci_cal_msg(struct ath_softc *sc, u8 opcode, u8 *rx_payload) case MCI_GPM_BT_CAL_REQ: if (mci_hw->bt_state == MCI_BT_AWAKE) { mci_hw->bt_state = MCI_BT_CAL_START; - ath_mci_wait_btcal_done(sc); + ath9k_queue_reset(sc, RESET_TYPE_MCI); } ath_dbg(common, MCI, "MCI State : %d\n", mci_hw->bt_state); break; @@ -578,6 +561,8 @@ void ath_mci_intr(struct ath_softc *sc) mci_int_rxmsg &= ~AR_MCI_INTERRUPT_RX_MSG_GPM; while (more_data == MCI_GPM_MORE) { + if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) + return; pgpm = mci->gpm_buf.bf_addr; offset = ar9003_mci_get_next_gpm_offset(ah, false, @@ -744,12 +729,30 @@ void ath9k_mci_set_txpower(struct ath_softc *sc, bool setchannel, ath9k_hw_set_txpowerlimit(ah, sc->config.txpowlimit, false); } +static void ath9k_mci_stomp_audio(struct ath_softc *sc) +{ + struct ath_hw *ah = sc->sc_ah; + struct ath_btcoex *btcoex = &sc->btcoex; + struct ath_mci_profile *mci = &btcoex->mci; + + if (!mci->num_sco && !mci->num_a2dp) + return; + + if (ah->stats.avgbrssi > 25) { + btcoex->stomp_audio = 0; + return; + } + + btcoex->stomp_audio++; +} void ath9k_mci_update_rssi(struct ath_softc *sc) { struct ath_hw *ah = sc->sc_ah; struct ath_btcoex *btcoex = &sc->btcoex; struct ath9k_hw_mci *mci_hw = &sc->sc_ah->btcoex_hw.mci; + ath9k_mci_stomp_audio(sc); + if (!(mci_hw->config & ATH_MCI_CONFIG_CONCUR_TX)) return; diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index f088f4bf9a2..9553203ee62 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -96,17 +96,6 @@ static bool ath_pci_eeprom_read(struct ath_common *common, u32 off, u16 *data) return true; } -static void ath_pci_extn_synch_enable(struct ath_common *common) -{ - struct ath_softc *sc = (struct ath_softc *) common->priv; - struct pci_dev *pdev = to_pci_dev(sc->dev); - u8 lnkctl; - - pci_read_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, &lnkctl); - lnkctl |= PCI_EXP_LNKCTL_ES; - pci_write_config_byte(pdev, sc->sc_ah->caps.pcie_lcr_offset, lnkctl); -} - /* Need to be called after we discover btcoex capabilities */ static void ath_pci_aspm_init(struct ath_common *common) { @@ -153,7 +142,6 @@ static const struct ath_bus_ops ath_pci_bus_ops = { .ath_bus_type = ATH_PCI, .read_cachesize = ath_pci_read_cachesize, .eeprom_read = ath_pci_eeprom_read, - .extn_synch_en = ath_pci_extn_synch_enable, .aspm_init = ath_pci_aspm_init, }; diff --git a/drivers/net/wireless/ath/ath9k/rc.c b/drivers/net/wireless/ath/ath9k/rc.c index 27ed80b5488..714558d1ba7 100644 --- a/drivers/net/wireless/ath/ath9k/rc.c +++ b/drivers/net/wireless/ath/ath9k/rc.c @@ -982,16 +982,6 @@ static void ath_rc_update_per(struct ath_softc *sc, } } -static void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, - int xretries, int retries, u8 per) -{ - struct ath_rc_stats *stats = &rc->rcstats[rix]; - - stats->xretries += xretries; - stats->retries += retries; - stats->per = per; -} - static void ath_rc_update_ht(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct ieee80211_tx_info *tx_info, @@ -1065,14 +1055,6 @@ static void ath_rc_update_ht(struct ath_softc *sc, } -static void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) -{ - struct ath_rc_stats *stats; - - stats = &rc->rcstats[final_rate]; - stats->success++; -} - static void ath_rc_tx_status(struct ath_softc *sc, struct ath_rate_priv *ath_rc_priv, struct sk_buff *skb) @@ -1350,7 +1332,25 @@ static void ath_rate_update(void *priv, struct ieee80211_supported_band *sband, } } -#ifdef CONFIG_ATH9K_DEBUGFS +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) + +void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) +{ + struct ath_rc_stats *stats; + + stats = &rc->rcstats[final_rate]; + stats->success++; +} + +void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per) +{ + struct ath_rc_stats *stats = &rc->rcstats[rix]; + + stats->xretries += xretries; + stats->retries += retries; + stats->per = per; +} static ssize_t read_file_rcstat(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -1428,10 +1428,17 @@ static void ath_rate_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) { struct ath_rate_priv *rc = priv_sta; - debugfs_create_file("rc_stats", S_IRUGO, dir, rc, &fops_rcstat); + rc->debugfs_rcstats = debugfs_create_file("rc_stats", S_IRUGO, + dir, rc, &fops_rcstat); +} + +static void ath_rate_remove_sta_debugfs(void *priv, void *priv_sta) +{ + struct ath_rate_priv *rc = priv_sta; + debugfs_remove(rc->debugfs_rcstats); } -#endif /* CONFIG_ATH9K_DEBUGFS */ +#endif /* CONFIG_MAC80211_DEBUGFS && CONFIG_ATH9K_DEBUGFS */ static void *ath_rate_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) { @@ -1476,8 +1483,10 @@ static struct rate_control_ops ath_rate_ops = { .free = ath_rate_free, .alloc_sta = ath_rate_alloc_sta, .free_sta = ath_rate_free_sta, -#ifdef CONFIG_ATH9K_DEBUGFS + +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) .add_sta_debugfs = ath_rate_add_sta_debugfs, + .remove_sta_debugfs = ath_rate_remove_sta_debugfs, #endif }; diff --git a/drivers/net/wireless/ath/ath9k/rc.h b/drivers/net/wireless/ath/ath9k/rc.h index 268e67dc5fb..267dbfcfaa9 100644 --- a/drivers/net/wireless/ath/ath9k/rc.h +++ b/drivers/net/wireless/ath/ath9k/rc.h @@ -211,10 +211,26 @@ struct ath_rate_priv { struct ath_rateset neg_ht_rates; const struct ath_rate_table *rate_table; +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) struct dentry *debugfs_rcstats; struct ath_rc_stats rcstats[RATE_TABLE_SIZE]; +#endif }; +#if defined(CONFIG_MAC80211_DEBUGFS) && defined(CONFIG_ATH9K_DEBUGFS) +void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate); +void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per); +#else +static inline void ath_debug_stat_rc(struct ath_rate_priv *rc, int final_rate) +{ +} +static inline void ath_debug_stat_retries(struct ath_rate_priv *rc, int rix, + int xretries, int retries, u8 per) +{ +} +#endif + #ifdef CONFIG_ATH9K_RATE_CONTROL int ath_rate_control_register(void); void ath_rate_control_unregister(void); diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index a04028bce28..6aafbb77c49 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -976,7 +976,7 @@ static int ath9k_rx_skb_preprocess(struct ath_common *common, rx_status->freq = hw->conf.channel->center_freq; rx_status->signal = ah->noise + rx_stats->rs_rssi; rx_status->antenna = rx_stats->rs_antenna; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (rx_stats->rs_moreaggr) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index 741918a2027..34130943f9d 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -1354,10 +1354,10 @@ struct ath_txq *ath_txq_setup(struct ath_softc *sc, int qtype, int subtype) struct ath_hw *ah = sc->sc_ah; struct ath9k_tx_queue_info qi; static const int subtype_txq_to_hwq[] = { - [WME_AC_BE] = ATH_TXQ_AC_BE, - [WME_AC_BK] = ATH_TXQ_AC_BK, - [WME_AC_VI] = ATH_TXQ_AC_VI, - [WME_AC_VO] = ATH_TXQ_AC_VO, + [IEEE80211_AC_BE] = ATH_TXQ_AC_BE, + [IEEE80211_AC_BK] = ATH_TXQ_AC_BK, + [IEEE80211_AC_VI] = ATH_TXQ_AC_VI, + [IEEE80211_AC_VO] = ATH_TXQ_AC_VO, }; int axq_qnum, i; @@ -2319,6 +2319,8 @@ void ath_tx_edma_tasklet(struct ath_softc *sc) ath_txq_lock(sc, txq); + TX_STAT_INC(txq->axq_qnum, txprocdesc); + if (list_empty(&txq->txq_fifo[txq->txq_tailidx])) { ath_txq_unlock(sc, txq); return; @@ -2464,7 +2466,7 @@ void ath_tx_node_init(struct ath_softc *sc, struct ath_node *an) } for (acno = 0, ac = &an->ac[acno]; - acno < WME_NUM_AC; acno++, ac++) { + acno < IEEE80211_NUM_ACS; acno++, ac++) { ac->sched = false; ac->txq = sc->tx.txq_map[acno]; INIT_LIST_HEAD(&ac->tid_q); diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 24ac2876a73..aaebecd19e5 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -28,11 +28,6 @@ #include "fwcmd.h" #include "version.h" -#define MAKE_STR(symbol) #symbol -#define TO_STR(symbol) MAKE_STR(symbol) -#define CARL9170FW_API_VER_STR TO_STR(CARL9170FW_API_MAX_VER) -MODULE_VERSION(CARL9170FW_API_VER_STR ":" CARL9170FW_VERSION_GIT); - static const u8 otus_magic[4] = { OTUS_MAGIC }; static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 136510edf3c..8cb206a8908 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -796,7 +796,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_MACTIME_MPDU; + status.flag |= RX_FLAG_MACTIME_START; } chanid = (chanstat & B43_RX_CHAN_ID) >> B43_RX_CHAN_ID_SHIFT; diff --git a/drivers/net/wireless/b43legacy/xmit.c b/drivers/net/wireless/b43legacy/xmit.c index b8ffea6f5c6..849a28c8030 100644 --- a/drivers/net/wireless/b43legacy/xmit.c +++ b/drivers/net/wireless/b43legacy/xmit.c @@ -557,7 +557,7 @@ void b43legacy_rx(struct b43legacy_wldev *dev, status.mactime += mactime; if (low_mactime_now <= mactime) status.mactime -= 0x10000; - status.flag |= RX_FLAG_MACTIME_MPDU; + status.flag |= RX_FLAG_MACTIME_START; } chanid = (chanstat & B43legacy_RX_CHAN_ID) >> diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig index c9d811eb655..1d92d874ebb 100644 --- a/drivers/net/wireless/brcm80211/Kconfig +++ b/drivers/net/wireless/brcm80211/Kconfig @@ -55,13 +55,16 @@ config BRCMFMAC_USB IEEE802.11n embedded FullMAC WLAN driver. Say Y if you want to use the driver for an USB wireless card. -config BRCMISCAN - bool "Broadcom I-Scan (OBSOLETE)" - depends on BRCMFMAC +config BRCM_TRACING + bool "Broadcom device tracing" + depends on BRCMSMAC || BRCMFMAC ---help--- - This option enables the I-Scan method. By default fullmac uses the - new E-Scan method which uses less memory in firmware and gives no - limitation on the number of scan results. + If you say Y here, the Broadcom wireless drivers will register + with ftrace to dump event information into the trace ringbuffer. + Tracing can be enabled at runtime to aid in debugging wireless + issues. This option adds a small amount of overhead when tracing + is disabled. If unsure, say Y to allow developers to better help + you when wireless problems occur. config BRCMDBG bool "Broadcom driver debug functions" diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index fe80b637c51..1a6661a9f00 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_BRCMFMAC) += brcmfmac.o brcmfmac-objs += \ wl_cfg80211.o \ fwil.o \ + fweh.o \ dhd_cdc.o \ dhd_common.o \ dhd_linux.o diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index 3b2c4c20e7f..334ddab4a8c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -42,7 +42,8 @@ #ifdef CONFIG_BRCMFMAC_SDIO_OOB static irqreturn_t brcmf_sdio_irqhandler(int irq, void *dev_id) { - struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(dev_id); + struct brcmf_bus *bus_if = dev_get_drvdata(dev_id); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(INTR, "oob intr triggered\n"); @@ -71,7 +72,7 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) brcmf_dbg(ERROR, "requesting irq %d\n", sdiodev->irq); ret = request_irq(sdiodev->irq, brcmf_sdio_irqhandler, sdiodev->irq_flags, "brcmf_oob_intr", - &sdiodev->func[1]->card->dev); + &sdiodev->func[1]->dev); if (ret != 0) return ret; spin_lock_init(&sdiodev->irq_en_lock); @@ -84,6 +85,8 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) return ret; sdiodev->irq_wake = true; + sdio_claim_host(sdiodev->func[1]); + /* must configure SDIO_CCCR_IENx to enable irq */ data = brcmf_sdio_regrb(sdiodev, SDIO_CCCR_IENx, &ret); data |= 1 << SDIO_FUNC_1 | 1 << SDIO_FUNC_2 | 1; @@ -95,6 +98,8 @@ int brcmf_sdio_intr_register(struct brcmf_sdio_dev *sdiodev) data |= SDIO_SEPINT_ACT_HI; brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, data, &ret); + sdio_release_host(sdiodev->func[1]); + return 0; } @@ -102,14 +107,16 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) { brcmf_dbg(TRACE, "Entering\n"); + sdio_claim_host(sdiodev->func[1]); brcmf_sdio_regwb(sdiodev, SDIO_CCCR_BRCM_SEPINT, 0, NULL); brcmf_sdio_regwb(sdiodev, SDIO_CCCR_IENx, 0, NULL); + sdio_release_host(sdiodev->func[1]); if (sdiodev->irq_wake) { disable_irq_wake(sdiodev->irq); sdiodev->irq_wake = false; } - free_irq(sdiodev->irq, &sdiodev->func[1]->card->dev); + free_irq(sdiodev->irq, &sdiodev->func[1]->dev); sdiodev->irq_en = false; return 0; @@ -117,7 +124,8 @@ int brcmf_sdio_intr_unregister(struct brcmf_sdio_dev *sdiodev) #else /* CONFIG_BRCMFMAC_SDIO_OOB */ static void brcmf_sdio_irqhandler(struct sdio_func *func) { - struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); + struct brcmf_bus *bus_if = dev_get_drvdata(&func->dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_dbg(INTR, "ib intr triggered\n"); @@ -249,9 +257,7 @@ u8 brcmf_sdio_regrb(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); - sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - sdio_release_host(sdiodev->func[1]); brcmf_dbg(INFO, "data:0x%02x\n", data); if (ret) @@ -266,9 +272,7 @@ u32 brcmf_sdio_regrl(struct brcmf_sdio_dev *sdiodev, u32 addr, int *ret) int retval; brcmf_dbg(INFO, "addr:0x%08x\n", addr); - sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, false); - sdio_release_host(sdiodev->func[1]); brcmf_dbg(INFO, "data:0x%08x\n", data); if (ret) @@ -283,9 +287,7 @@ void brcmf_sdio_regwb(struct brcmf_sdio_dev *sdiodev, u32 addr, int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%02x\n", addr, data); - sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); - sdio_release_host(sdiodev->func[1]); if (ret) *ret = retval; @@ -297,9 +299,7 @@ void brcmf_sdio_regwl(struct brcmf_sdio_dev *sdiodev, u32 addr, int retval; brcmf_dbg(INFO, "addr:0x%08x, data:0x%08x\n", addr, data); - sdio_claim_host(sdiodev->func[1]); retval = brcmf_sdio_regrw_helper(sdiodev, addr, &data, true); - sdio_release_host(sdiodev->func[1]); if (ret) *ret = retval; @@ -364,8 +364,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pkt->len); - sdio_claim_host(sdiodev->func[1]); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) @@ -376,8 +374,6 @@ brcmf_sdcard_recv_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, fn, addr, pkt); done: - sdio_release_host(sdiodev->func[1]); - return err; } @@ -391,8 +387,6 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, brcmf_dbg(INFO, "fun = %d, addr = 0x%x, size = %d\n", fn, addr, pktq->qlen); - sdio_claim_host(sdiodev->func[1]); - width = (flags & SDIO_REQ_4BYTE) ? 4 : 2; err = brcmf_sdcard_recv_prepare(sdiodev, fn, flags, width, &addr); if (err) @@ -403,8 +397,6 @@ int brcmf_sdcard_recv_chain(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, pktq); done: - sdio_release_host(sdiodev->func[1]); - return err; } @@ -446,8 +438,6 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, if (flags & SDIO_REQ_ASYNC) return -ENOTSUPP; - sdio_claim_host(sdiodev->func[1]); - if (bar0 != sdiodev->sbwad) { err = brcmf_sdcard_set_sbaddr_window(sdiodev, bar0); if (err) @@ -467,8 +457,6 @@ brcmf_sdcard_send_pkt(struct brcmf_sdio_dev *sdiodev, u32 addr, uint fn, addr, pkt); done: - sdio_release_host(sdiodev->func[1]); - return err; } @@ -510,10 +498,8 @@ int brcmf_sdcard_abort(struct brcmf_sdio_dev *sdiodev, uint fn) brcmf_dbg(TRACE, "Enter\n"); /* issue abort cmd52 command through F0 */ - sdio_claim_host(sdiodev->func[1]); brcmf_sdioh_request_byte(sdiodev, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT, &t_func); - sdio_release_host(sdiodev->func[1]); brcmf_dbg(TRACE, "Exit\n"); return 0; @@ -530,9 +516,6 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) regs = SI_ENUM_BASE; - /* Report the BAR, to fix if needed */ - sdiodev->sbwad = SI_ENUM_BASE; - /* try to attach to the target device */ sdiodev->bus = brcmf_sdbrcm_probe(regs, sdiodev); if (!sdiodev->bus) { @@ -551,6 +534,8 @@ EXPORT_SYMBOL(brcmf_sdio_probe); int brcmf_sdio_remove(struct brcmf_sdio_dev *sdiodev) { + sdiodev->bus_if->state = BRCMF_BUS_DOWN; + if (sdiodev->bus) { brcmf_sdbrcm_disconnect(sdiodev->bus); sdiodev->bus = NULL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c index c3247d5b3c2..a8005022371 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c @@ -372,9 +372,7 @@ static int brcmf_sdioh_enablefuncs(struct brcmf_sdio_dev *sdiodev) } /* Enable Function 1 */ - sdio_claim_host(sdiodev->func[1]); err_ret = sdio_enable_func(sdiodev->func[1]); - sdio_release_host(sdiodev->func[1]); if (err_ret) brcmf_dbg(ERROR, "Failed to enable F1 Err: 0x%08x\n", err_ret); @@ -393,16 +391,14 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) sdiodev->num_funcs = 2; sdio_claim_host(sdiodev->func[1]); + err_ret = sdio_set_block_size(sdiodev->func[1], SDIO_FUNC1_BLOCKSIZE); - sdio_release_host(sdiodev->func[1]); if (err_ret) { brcmf_dbg(ERROR, "Failed to set F1 blocksize\n"); goto out; } - sdio_claim_host(sdiodev->func[2]); err_ret = sdio_set_block_size(sdiodev->func[2], SDIO_FUNC2_BLOCKSIZE); - sdio_release_host(sdiodev->func[2]); if (err_ret) { brcmf_dbg(ERROR, "Failed to set F2 blocksize\n"); goto out; @@ -411,6 +407,7 @@ int brcmf_sdioh_attach(struct brcmf_sdio_dev *sdiodev) brcmf_sdioh_enablefuncs(sdiodev); out: + sdio_release_host(sdiodev->func[1]); brcmf_dbg(TRACE, "Done\n"); return err_ret; } @@ -459,95 +456,106 @@ static inline int brcmf_sdio_getintrcfg(struct brcmf_sdio_dev *sdiodev) #endif /* CONFIG_BRCMFMAC_SDIO_OOB */ static int brcmf_ops_sdio_probe(struct sdio_func *func, - const struct sdio_device_id *id) + const struct sdio_device_id *id) { - int ret = 0; + int err; struct brcmf_sdio_dev *sdiodev; struct brcmf_bus *bus_if; brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(TRACE, "func->class=%x\n", func->class); - brcmf_dbg(TRACE, "sdio_vendor: 0x%04x\n", func->vendor); - brcmf_dbg(TRACE, "sdio_device: 0x%04x\n", func->device); - brcmf_dbg(TRACE, "Function#: 0x%04x\n", func->num); - - if (func->num == 1) { - if (dev_get_drvdata(&func->card->dev)) { - brcmf_dbg(ERROR, "card private drvdata occupied\n"); - return -ENXIO; - } - bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); - if (!bus_if) - return -ENOMEM; - sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); - if (!sdiodev) { - kfree(bus_if); - return -ENOMEM; - } - sdiodev->func[0] = func; - sdiodev->func[1] = func; - sdiodev->bus_if = bus_if; - bus_if->bus_priv.sdio = sdiodev; - bus_if->type = SDIO_BUS; - bus_if->align = BRCMF_SDALIGN; - dev_set_drvdata(&func->card->dev, sdiodev); - - atomic_set(&sdiodev->suspend, false); - init_waitqueue_head(&sdiodev->request_byte_wait); - init_waitqueue_head(&sdiodev->request_word_wait); - init_waitqueue_head(&sdiodev->request_chain_wait); - init_waitqueue_head(&sdiodev->request_buffer_wait); + brcmf_dbg(TRACE, "Class=%x\n", func->class); + brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(TRACE, "Function#: %d\n", func->num); + + /* Consume func num 1 but dont do anything with it. */ + if (func->num == 1) + return 0; + + /* Ignore anything but func 2 */ + if (func->num != 2) + return -ENODEV; + + bus_if = kzalloc(sizeof(struct brcmf_bus), GFP_KERNEL); + if (!bus_if) + return -ENOMEM; + sdiodev = kzalloc(sizeof(struct brcmf_sdio_dev), GFP_KERNEL); + if (!sdiodev) { + kfree(bus_if); + return -ENOMEM; } - if (func->num == 2) { - sdiodev = dev_get_drvdata(&func->card->dev); - if ((!sdiodev) || (sdiodev->func[1]->card != func->card)) - return -ENODEV; - - ret = brcmf_sdio_getintrcfg(sdiodev); - if (ret) - return ret; - sdiodev->func[2] = func; + sdiodev->func[0] = func->card->sdio_func[0]; + sdiodev->func[1] = func->card->sdio_func[0]; + sdiodev->func[2] = func; - bus_if = sdiodev->bus_if; - sdiodev->dev = &func->dev; - dev_set_drvdata(&func->dev, bus_if); + sdiodev->bus_if = bus_if; + bus_if->bus_priv.sdio = sdiodev; + bus_if->align = BRCMF_SDALIGN; + dev_set_drvdata(&func->dev, bus_if); + dev_set_drvdata(&sdiodev->func[1]->dev, bus_if); + sdiodev->dev = &sdiodev->func[1]->dev; - brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n"); - ret = brcmf_sdio_probe(sdiodev); + atomic_set(&sdiodev->suspend, false); + init_waitqueue_head(&sdiodev->request_byte_wait); + init_waitqueue_head(&sdiodev->request_word_wait); + init_waitqueue_head(&sdiodev->request_chain_wait); + init_waitqueue_head(&sdiodev->request_buffer_wait); + err = brcmf_sdio_getintrcfg(sdiodev); + if (err) + goto fail; + + brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_probe...\n"); + err = brcmf_sdio_probe(sdiodev); + if (err) { + brcmf_dbg(ERROR, "F2 error, probe failed %d...\n", err); + goto fail; } + brcmf_dbg(TRACE, "F2 init completed...\n"); + return 0; - return ret; +fail: + dev_set_drvdata(&func->dev, NULL); + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + kfree(sdiodev); + kfree(bus_if); + return err; } static void brcmf_ops_sdio_remove(struct sdio_func *func) { struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; + brcmf_dbg(TRACE, "Enter\n"); - brcmf_dbg(INFO, "func->class=%x\n", func->class); - brcmf_dbg(INFO, "sdio_vendor: 0x%04x\n", func->vendor); - brcmf_dbg(INFO, "sdio_device: 0x%04x\n", func->device); - brcmf_dbg(INFO, "Function#: 0x%04x\n", func->num); + brcmf_dbg(TRACE, "sdio vendor ID: 0x%04x\n", func->vendor); + brcmf_dbg(TRACE, "sdio device ID: 0x%04x\n", func->device); + brcmf_dbg(TRACE, "Function: %d\n", func->num); - if (func->num == 2) { - bus_if = dev_get_drvdata(&func->dev); + if (func->num != 1 && func->num != 2) + return; + + bus_if = dev_get_drvdata(&func->dev); + if (bus_if) { sdiodev = bus_if->bus_priv.sdio; - brcmf_dbg(TRACE, "F2 found, calling brcmf_sdio_remove...\n"); brcmf_sdio_remove(sdiodev); - dev_set_drvdata(&func->card->dev, NULL); - dev_set_drvdata(&func->dev, NULL); + + dev_set_drvdata(&sdiodev->func[1]->dev, NULL); + dev_set_drvdata(&sdiodev->func[2]->dev, NULL); + kfree(bus_if); kfree(sdiodev); } + + brcmf_dbg(TRACE, "Exit\n"); } #ifdef CONFIG_PM_SLEEP static int brcmf_sdio_suspend(struct device *dev) { mmc_pm_flag_t sdio_flags; - struct sdio_func *func = dev_to_sdio_func(dev); - struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; int ret = 0; brcmf_dbg(TRACE, "\n"); @@ -573,8 +581,8 @@ static int brcmf_sdio_suspend(struct device *dev) static int brcmf_sdio_resume(struct device *dev) { - struct sdio_func *func = dev_to_sdio_func(dev); - struct brcmf_sdio_dev *sdiodev = dev_get_drvdata(&func->card->dev); + struct brcmf_bus *bus_if = dev_get_drvdata(dev); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; brcmf_sdio_wdtmr_enable(sdiodev, true); atomic_set(&sdiodev->suspend, false); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index 8704daa2758..24bc4e3e162 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -23,6 +23,8 @@ #define BRCMF_VERSION_STR "4.218.248.5" +#include "fweh.h" + /******************************************************************************* * IO codes that are interpreted by dongle firmware ******************************************************************************/ @@ -38,8 +40,11 @@ #define BRCMF_C_GET_SSID 25 #define BRCMF_C_SET_SSID 26 #define BRCMF_C_GET_CHANNEL 29 +#define BRCMF_C_SET_CHANNEL 30 #define BRCMF_C_GET_SRL 31 +#define BRCMF_C_SET_SRL 32 #define BRCMF_C_GET_LRL 33 +#define BRCMF_C_SET_LRL 34 #define BRCMF_C_GET_RADIO 37 #define BRCMF_C_SET_RADIO 38 #define BRCMF_C_GET_PHYTYPE 39 @@ -58,6 +63,7 @@ #define BRCMF_C_SET_COUNTRY 84 #define BRCMF_C_GET_PM 85 #define BRCMF_C_SET_PM 86 +#define BRCMF_C_GET_CURR_RATESET 114 #define BRCMF_C_GET_AP 117 #define BRCMF_C_SET_AP 118 #define BRCMF_C_GET_RSSI 127 @@ -65,6 +71,7 @@ #define BRCMF_C_SET_WSEC 134 #define BRCMF_C_GET_PHY_NOISE 135 #define BRCMF_C_GET_BSS_INFO 136 +#define BRCMF_C_GET_PHYLIST 180 #define BRCMF_C_SET_SCAN_CHANNEL_TIME 185 #define BRCMF_C_SET_SCAN_UNASSOC_TIME 187 #define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON 201 @@ -100,29 +107,8 @@ #define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff #define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16 -#define BRCMF_SCAN_ACTION_START 1 -#define BRCMF_SCAN_ACTION_CONTINUE 2 -#define WL_SCAN_ACTION_ABORT 3 - -#define BRCMF_ISCAN_REQ_VERSION 1 - -/* brcmf_iscan_results status values */ -#define BRCMF_SCAN_RESULTS_SUCCESS 0 -#define BRCMF_SCAN_RESULTS_PARTIAL 1 -#define BRCMF_SCAN_RESULTS_PENDING 2 -#define BRCMF_SCAN_RESULTS_ABORTED 3 -#define BRCMF_SCAN_RESULTS_NO_MEM 4 - -/* Indicates this key is using soft encrypt */ -#define WL_SOFT_KEY (1 << 0) /* primary (ie tx) key */ #define BRCMF_PRIMARY_KEY (1 << 1) -/* Reserved for backward compat */ -#define WL_KF_RES_4 (1 << 4) -/* Reserved for backward compat */ -#define WL_KF_RES_5 (1 << 5) -/* Indicates a group key for a IBSS PEER */ -#define WL_IBSS_PEER_GROUP_KEY (1 << 6) /* For supporting multiple interfaces */ #define BRCMF_MAX_IFS 16 @@ -130,10 +116,6 @@ #define DOT11_BSSTYPE_ANY 2 #define DOT11_MAX_DEFAULT_KEYS 4 -#define BRCMF_EVENT_MSG_LINK 0x01 -#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 -#define BRCMF_EVENT_MSG_GROUP 0x04 - #define BRCMF_ESCAN_REQ_VERSION 1 #define WLC_BSS_RSSI_ON_CHANNEL 0x0002 @@ -141,108 +123,6 @@ #define BRCMF_MAXRATES_IN_SET 16 /* max # of rates in rateset */ #define BRCMF_STA_ASSOC 0x10 /* Associated */ -struct brcmf_event_msg { - __be16 version; - __be16 flags; - __be32 event_type; - __be32 status; - __be32 reason; - __be32 auth_type; - __be32 datalen; - u8 addr[ETH_ALEN]; - char ifname[IFNAMSIZ]; - u8 ifidx; - u8 bsscfgidx; -} __packed; - -struct brcm_ethhdr { - u16 subtype; - u16 length; - u8 version; - u8 oui[3]; - u16 usr_subtype; -} __packed; - -struct brcmf_event { - struct ethhdr eth; - struct brcm_ethhdr hdr; - struct brcmf_event_msg msg; -} __packed; - -/* event codes sent by the dongle to this driver */ -#define BRCMF_E_SET_SSID 0 -#define BRCMF_E_JOIN 1 -#define BRCMF_E_START 2 -#define BRCMF_E_AUTH 3 -#define BRCMF_E_AUTH_IND 4 -#define BRCMF_E_DEAUTH 5 -#define BRCMF_E_DEAUTH_IND 6 -#define BRCMF_E_ASSOC 7 -#define BRCMF_E_ASSOC_IND 8 -#define BRCMF_E_REASSOC 9 -#define BRCMF_E_REASSOC_IND 10 -#define BRCMF_E_DISASSOC 11 -#define BRCMF_E_DISASSOC_IND 12 -#define BRCMF_E_QUIET_START 13 -#define BRCMF_E_QUIET_END 14 -#define BRCMF_E_BEACON_RX 15 -#define BRCMF_E_LINK 16 -#define BRCMF_E_MIC_ERROR 17 -#define BRCMF_E_NDIS_LINK 18 -#define BRCMF_E_ROAM 19 -#define BRCMF_E_TXFAIL 20 -#define BRCMF_E_PMKID_CACHE 21 -#define BRCMF_E_RETROGRADE_TSF 22 -#define BRCMF_E_PRUNE 23 -#define BRCMF_E_AUTOAUTH 24 -#define BRCMF_E_EAPOL_MSG 25 -#define BRCMF_E_SCAN_COMPLETE 26 -#define BRCMF_E_ADDTS_IND 27 -#define BRCMF_E_DELTS_IND 28 -#define BRCMF_E_BCNSENT_IND 29 -#define BRCMF_E_BCNRX_MSG 30 -#define BRCMF_E_BCNLOST_MSG 31 -#define BRCMF_E_ROAM_PREP 32 -#define BRCMF_E_PFN_NET_FOUND 33 -#define BRCMF_E_PFN_NET_LOST 34 -#define BRCMF_E_RESET_COMPLETE 35 -#define BRCMF_E_JOIN_START 36 -#define BRCMF_E_ROAM_START 37 -#define BRCMF_E_ASSOC_START 38 -#define BRCMF_E_IBSS_ASSOC 39 -#define BRCMF_E_RADIO 40 -#define BRCMF_E_PSM_WATCHDOG 41 -#define BRCMF_E_PROBREQ_MSG 44 -#define BRCMF_E_SCAN_CONFIRM_IND 45 -#define BRCMF_E_PSK_SUP 46 -#define BRCMF_E_COUNTRY_CODE_CHANGED 47 -#define BRCMF_E_EXCEEDED_MEDIUM_TIME 48 -#define BRCMF_E_ICV_ERROR 49 -#define BRCMF_E_UNICAST_DECODE_ERROR 50 -#define BRCMF_E_MULTICAST_DECODE_ERROR 51 -#define BRCMF_E_TRACE 52 -#define BRCMF_E_IF 54 -#define BRCMF_E_RSSI 56 -#define BRCMF_E_PFN_SCAN_COMPLETE 57 -#define BRCMF_E_EXTLOG_MSG 58 -#define BRCMF_E_ACTION_FRAME 59 -#define BRCMF_E_ACTION_FRAME_COMPLETE 60 -#define BRCMF_E_PRE_ASSOC_IND 61 -#define BRCMF_E_PRE_REASSOC_IND 62 -#define BRCMF_E_CHANNEL_ADOPTED 63 -#define BRCMF_E_AP_STARTED 64 -#define BRCMF_E_DFS_AP_STOP 65 -#define BRCMF_E_DFS_AP_RESUME 66 -#define BRCMF_E_RESERVED1 67 -#define BRCMF_E_RESERVED2 68 -#define BRCMF_E_ESCAN_RESULT 69 -#define BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE 70 -#define BRCMF_E_DCS_REQUEST 73 - -#define BRCMF_E_FIFO_CREDIT_MAP 74 - -#define BRCMF_E_LAST 75 - #define BRCMF_E_STATUS_SUCCESS 0 #define BRCMF_E_STATUS_FAIL 1 #define BRCMF_E_STATUS_TIMEOUT 2 @@ -403,7 +283,7 @@ struct brcm_rateset_le { /* # rates in this set */ __le32 count; /* rates in 500kbps units w/hi bit set if basic */ - u8 rates[WL_NUMRATES]; + u8 rates[BRCMF_MAXRATES_IN_SET]; }; struct brcmf_ssid { @@ -452,14 +332,6 @@ struct brcmf_scan_params_le { __le16 channel_list[1]; /* list of chanspecs */ }; -/* incremental scan struct */ -struct brcmf_iscan_params_le { - __le32 version; - __le16 action; - __le16 scan_duration; - struct brcmf_scan_params_le params_le; -}; - struct brcmf_scan_results { u32 buflen; u32 version; @@ -467,12 +339,6 @@ struct brcmf_scan_results { struct brcmf_bss_info_le bss_info_le[]; }; -struct brcmf_scan_results_le { - __le32 buflen; - __le32 version; - __le32 count; -}; - struct brcmf_escan_params_le { __le32 version; __le16 action; @@ -508,23 +374,6 @@ struct brcmf_join_params { struct brcmf_assoc_params_le params_le; }; -/* incremental scan results struct */ -struct brcmf_iscan_results { - union { - u32 status; - __le32 status_le; - }; - union { - struct brcmf_scan_results results; - struct brcmf_scan_results_le results_le; - }; -}; - -/* size of brcmf_iscan_results not including variable length array */ -#define BRCMF_ISCAN_RESULTS_FIXED_SIZE \ - (sizeof(struct brcmf_scan_results) + \ - offsetof(struct brcmf_iscan_results, results)) - struct brcmf_wsec_key { u32 index; /* key index */ u32 len; /* key length */ @@ -661,10 +510,11 @@ struct brcmf_pub { struct mutex proto_block; unsigned char proto_buf[BRCMF_DCMD_MAXLEN]; - struct work_struct setmacaddr_work; - struct work_struct multicast_work; u8 macvalue[ETH_ALEN]; atomic_t pend_8021x_cnt; + wait_queue_head_t pend_8021x_wait; + + struct brcmf_fweh_info fweh; #ifdef DEBUG struct dentry *dbgfs_dir; #endif @@ -701,6 +551,8 @@ struct brcmf_if { struct brcmf_cfg80211_vif *vif; struct net_device *ndev; struct net_device_stats stats; + struct work_struct setmacaddr_work; + struct work_struct multicast_work; int idx; s32 bssidx; u8 mac_addr[ETH_ALEN]; @@ -714,9 +566,6 @@ static inline s32 brcmf_ndev_bssidx(struct net_device *ndev) extern const struct bcmevent_name bcmevent_names[]; -extern uint brcmf_c_mkiovar(char *name, char *data, uint datalen, - char *buf, uint len); - extern int brcmf_netdev_wait_pend8021x(struct net_device *ndev); /* Return pointer to interface name */ @@ -728,14 +577,9 @@ extern int brcmf_proto_cdc_query_dcmd(struct brcmf_pub *drvr, int ifidx, extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, uint cmd, void *buf, uint len); -extern int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name); -extern int brcmf_c_host_event(struct brcmf_pub *drvr, int *idx, - void *pktdata, struct brcmf_event_msg *, - void **data_ptr); - extern int brcmf_net_attach(struct brcmf_if *ifp); -extern struct brcmf_if *brcmf_add_if(struct device *dev, int ifidx, s32 bssidx, - char *name, u8 *mac_addr); +extern struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx, + s32 bssidx, char *name, u8 *mac_addr); extern void brcmf_del_if(struct brcmf_pub *drvr, int ifidx); #endif /* _BRCMF_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h index 265580f5b27..b8f248797f6 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h @@ -45,7 +45,6 @@ struct brcmf_bus_dcmd { /* interface structure between common and bus layer */ struct brcmf_bus { - u8 type; /* bus type */ union { struct brcmf_sdio_dev *sdio; struct brcmf_usbdev *usb; @@ -85,7 +84,7 @@ extern bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec); /* Receive frame for delivery to OS. Callee disposes of rxp. */ -extern void brcmf_rx_frame(struct device *dev, int ifidx, +extern void brcmf_rx_frame(struct device *dev, u8 ifidx, struct sk_buff_head *rxlist); static inline void brcmf_rx_packet(struct device *dev, int ifidx, struct sk_buff *pkt) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c index b9d8a5adfd4..87536d38a4c 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c @@ -23,8 +23,6 @@ #include <linux/types.h> #include <linux/netdevice.h> -#include <linux/sched.h> -#include <defs.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> @@ -277,76 +275,6 @@ done: return ret; } -int -brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, struct brcmf_dcmd *dcmd, - int len) -{ - struct brcmf_proto *prot = drvr->prot; - int ret = -1; - - if (drvr->bus_if->state == BRCMF_BUS_DOWN) { - brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n"); - return ret; - } - mutex_lock(&drvr->proto_block); - - brcmf_dbg(TRACE, "Enter\n"); - - if (len > BRCMF_DCMD_MAXLEN) - goto done; - - if (prot->pending == true) { - brcmf_dbg(TRACE, "CDC packet is pending!!!! cmd=0x%x (%lu) lastcmd=0x%x (%lu)\n", - dcmd->cmd, (unsigned long)dcmd->cmd, prot->lastcmd, - (unsigned long)prot->lastcmd); - if (dcmd->cmd == BRCMF_C_SET_VAR || - dcmd->cmd == BRCMF_C_GET_VAR) - brcmf_dbg(TRACE, "iovar cmd=%s\n", (char *)dcmd->buf); - - goto done; - } - - prot->pending = true; - prot->lastcmd = dcmd->cmd; - if (dcmd->set) - ret = brcmf_proto_cdc_set_dcmd(drvr, ifidx, dcmd->cmd, - dcmd->buf, len); - else { - ret = brcmf_proto_cdc_query_dcmd(drvr, ifidx, dcmd->cmd, - dcmd->buf, len); - if (ret > 0) - dcmd->used = ret - - sizeof(struct brcmf_proto_cdc_dcmd); - } - - if (ret >= 0) - ret = 0; - else { - struct brcmf_proto_cdc_dcmd *msg = &prot->msg; - /* len == needed when set/query fails from dongle */ - dcmd->needed = le32_to_cpu(msg->len); - } - - /* Intercept the wme_dp dongle cmd here */ - if (!ret && dcmd->cmd == BRCMF_C_SET_VAR && - !strcmp(dcmd->buf, "wme_dp")) { - int slen; - __le32 val = 0; - - slen = strlen("wme_dp") + 1; - if (len >= (int)(slen + sizeof(int))) - memcpy(&val, (char *)dcmd->buf + slen, sizeof(int)); - drvr->wme_dp = (u8) le32_to_cpu(val); - } - - prot->pending = false; - -done: - mutex_unlock(&drvr->proto_block); - - return ret; -} - static bool pkt_sum_needed(struct sk_buff *skb) { return skb->ip_summed == CHECKSUM_PARTIAL; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c index 866b66995bb..eee7175f151 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c @@ -18,10 +18,7 @@ #include <linux/kernel.h> #include <linux/string.h> -#include <linux/sched.h> #include <linux/netdevice.h> -#include <asm/unaligned.h> -#include <defs.h> #include <brcmu_wifi.h> #include <brcmu_utils.h> #include "dhd.h" @@ -30,9 +27,6 @@ #include "dhd_dbg.h" #include "fwil.h" -#define BRCM_OUI "\x00\x10\x18" -#define DOT11_OUI_LEN 3 -#define BCMILCP_BCM_SUBTYPE_EVENT 1 #define PKTFILTER_BUF_SIZE 128 #define BRCMF_ARPOL_MODE 0xb /* agent|snoop|peer_autoreply */ #define BRCMF_DEFAULT_BCN_TIMEOUT 3 @@ -40,12 +34,6 @@ #define BRCMF_DEFAULT_SCAN_UNASSOC_TIME 40 #define BRCMF_DEFAULT_PACKET_FILTER "100 0 0 0 0x01 0x00" -#define MSGTRACE_VERSION 1 - -#define BRCMF_PKT_FILTER_FIXED_LEN offsetof(struct brcmf_pkt_filter_le, u) -#define BRCMF_PKT_FILTER_PATTERN_FIXED_LEN \ - offsetof(struct brcmf_pkt_filter_pattern_le, mask_and_pattern) - #ifdef DEBUG static const char brcmf_version[] = "Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on " @@ -55,43 +43,6 @@ static const char brcmf_version[] = "Dongle Host Driver, version " BRCMF_VERSION_STR; #endif -/* Message trace header */ -struct msgtrace_hdr { - u8 version; - u8 spare; - __be16 len; /* Len of the trace */ - __be32 seqnum; /* Sequence number of message. Useful - * if the messsage has been lost - * because of DMA error or a bus reset - * (ex: SDIO Func2) - */ - __be32 discarded_bytes; /* Number of discarded bytes because of - trace overflow */ - __be32 discarded_printf; /* Number of discarded printf - because of trace overflow */ -} __packed; - - -uint -brcmf_c_mkiovar(char *name, char *data, uint datalen, char *buf, uint buflen) -{ - uint len; - - len = strlen(name) + 1; - - if ((len + datalen) > buflen) - return 0; - - strncpy(buf, name, buflen); - - /* append data onto the end of the name string */ - if (data && datalen) { - memcpy(&buf[len], data, datalen); - len += datalen; - } - - return len; -} bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, struct sk_buff *pkt, int prec) @@ -143,405 +94,6 @@ bool brcmf_c_prec_enq(struct device *dev, struct pktq *q, return p != NULL; } -#ifdef DEBUG -static void -brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data) -{ - uint i, status, reason; - bool group = false, flush_txq = false, link = false; - char *auth_str, *event_name; - unsigned char *buf; - char err_msg[256], eabuf[ETHER_ADDR_STR_LEN]; - static struct { - uint event; - char *event_name; - } event_names[] = { - { - BRCMF_E_SET_SSID, "SET_SSID"}, { - BRCMF_E_JOIN, "JOIN"}, { - BRCMF_E_START, "START"}, { - BRCMF_E_AUTH, "AUTH"}, { - BRCMF_E_AUTH_IND, "AUTH_IND"}, { - BRCMF_E_DEAUTH, "DEAUTH"}, { - BRCMF_E_DEAUTH_IND, "DEAUTH_IND"}, { - BRCMF_E_ASSOC, "ASSOC"}, { - BRCMF_E_ASSOC_IND, "ASSOC_IND"}, { - BRCMF_E_REASSOC, "REASSOC"}, { - BRCMF_E_REASSOC_IND, "REASSOC_IND"}, { - BRCMF_E_DISASSOC, "DISASSOC"}, { - BRCMF_E_DISASSOC_IND, "DISASSOC_IND"}, { - BRCMF_E_QUIET_START, "START_QUIET"}, { - BRCMF_E_QUIET_END, "END_QUIET"}, { - BRCMF_E_BEACON_RX, "BEACON_RX"}, { - BRCMF_E_LINK, "LINK"}, { - BRCMF_E_MIC_ERROR, "MIC_ERROR"}, { - BRCMF_E_NDIS_LINK, "NDIS_LINK"}, { - BRCMF_E_ROAM, "ROAM"}, { - BRCMF_E_TXFAIL, "TXFAIL"}, { - BRCMF_E_PMKID_CACHE, "PMKID_CACHE"}, { - BRCMF_E_RETROGRADE_TSF, "RETROGRADE_TSF"}, { - BRCMF_E_PRUNE, "PRUNE"}, { - BRCMF_E_AUTOAUTH, "AUTOAUTH"}, { - BRCMF_E_EAPOL_MSG, "EAPOL_MSG"}, { - BRCMF_E_SCAN_COMPLETE, "SCAN_COMPLETE"}, { - BRCMF_E_ADDTS_IND, "ADDTS_IND"}, { - BRCMF_E_DELTS_IND, "DELTS_IND"}, { - BRCMF_E_BCNSENT_IND, "BCNSENT_IND"}, { - BRCMF_E_BCNRX_MSG, "BCNRX_MSG"}, { - BRCMF_E_BCNLOST_MSG, "BCNLOST_MSG"}, { - BRCMF_E_ROAM_PREP, "ROAM_PREP"}, { - BRCMF_E_PFN_NET_FOUND, "PNO_NET_FOUND"}, { - BRCMF_E_PFN_NET_LOST, "PNO_NET_LOST"}, { - BRCMF_E_RESET_COMPLETE, "RESET_COMPLETE"}, { - BRCMF_E_JOIN_START, "JOIN_START"}, { - BRCMF_E_ROAM_START, "ROAM_START"}, { - BRCMF_E_ASSOC_START, "ASSOC_START"}, { - BRCMF_E_IBSS_ASSOC, "IBSS_ASSOC"}, { - BRCMF_E_RADIO, "RADIO"}, { - BRCMF_E_PSM_WATCHDOG, "PSM_WATCHDOG"}, { - BRCMF_E_PROBREQ_MSG, "PROBREQ_MSG"}, { - BRCMF_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"}, { - BRCMF_E_PSK_SUP, "PSK_SUP"}, { - BRCMF_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"}, { - BRCMF_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"}, { - BRCMF_E_ICV_ERROR, "ICV_ERROR"}, { - BRCMF_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"}, { - BRCMF_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"}, { - BRCMF_E_TRACE, "TRACE"}, { - BRCMF_E_ACTION_FRAME, "ACTION FRAME"}, { - BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, { - BRCMF_E_IF, "IF"}, { - BRCMF_E_RSSI, "RSSI"}, { - BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}, { - BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT"} - }; - uint event_type, flags, auth_type, datalen; - static u32 seqnum_prev; - struct msgtrace_hdr hdr; - u32 nblost; - char *s, *p; - - event_type = be32_to_cpu(event->event_type); - flags = be16_to_cpu(event->flags); - status = be32_to_cpu(event->status); - reason = be32_to_cpu(event->reason); - auth_type = be32_to_cpu(event->auth_type); - datalen = be32_to_cpu(event->datalen); - /* debug dump of event messages */ - sprintf(eabuf, "%pM", event->addr); - - event_name = "UNKNOWN"; - for (i = 0; i < ARRAY_SIZE(event_names); i++) { - if (event_names[i].event == event_type) - event_name = event_names[i].event_name; - } - - brcmf_dbg(EVENT, "EVENT: %s, event ID = %d\n", event_name, event_type); - brcmf_dbg(EVENT, "flags 0x%04x, status %d, reason %d, auth_type %d MAC %s\n", - flags, status, reason, auth_type, eabuf); - - if (flags & BRCMF_EVENT_MSG_LINK) - link = true; - if (flags & BRCMF_EVENT_MSG_GROUP) - group = true; - if (flags & BRCMF_EVENT_MSG_FLUSHTXQ) - flush_txq = true; - - switch (event_type) { - case BRCMF_E_START: - case BRCMF_E_DEAUTH: - case BRCMF_E_DISASSOC: - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); - break; - - case BRCMF_E_ASSOC_IND: - case BRCMF_E_REASSOC_IND: - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); - break; - - case BRCMF_E_ASSOC: - case BRCMF_E_REASSOC: - if (status == BRCMF_E_STATUS_SUCCESS) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, SUCCESS\n", - event_name, eabuf); - else if (status == BRCMF_E_STATUS_TIMEOUT) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, TIMEOUT\n", - event_name, eabuf); - else if (status == BRCMF_E_STATUS_FAIL) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, FAILURE, reason %d\n", - event_name, eabuf, (int)reason); - else - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, unexpected status %d\n", - event_name, eabuf, (int)status); - break; - - case BRCMF_E_DEAUTH_IND: - case BRCMF_E_DISASSOC_IND: - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, reason %d\n", - event_name, eabuf, (int)reason); - break; - - case BRCMF_E_AUTH: - case BRCMF_E_AUTH_IND: - if (auth_type == WLAN_AUTH_OPEN) - auth_str = "Open System"; - else if (auth_type == WLAN_AUTH_SHARED_KEY) - auth_str = "Shared Key"; - else { - sprintf(err_msg, "AUTH unknown: %d", (int)auth_type); - auth_str = err_msg; - } - if (event_type == BRCMF_E_AUTH_IND) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s\n", - event_name, eabuf, auth_str); - else if (status == BRCMF_E_STATUS_SUCCESS) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, SUCCESS\n", - event_name, eabuf, auth_str); - else if (status == BRCMF_E_STATUS_TIMEOUT) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, TIMEOUT\n", - event_name, eabuf, auth_str); - else if (status == BRCMF_E_STATUS_FAIL) { - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, %s, FAILURE, reason %d\n", - event_name, eabuf, auth_str, (int)reason); - } - - break; - - case BRCMF_E_JOIN: - case BRCMF_E_ROAM: - case BRCMF_E_SET_SSID: - if (status == BRCMF_E_STATUS_SUCCESS) - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", - event_name, eabuf); - else if (status == BRCMF_E_STATUS_FAIL) - brcmf_dbg(EVENT, "MACEVENT: %s, failed\n", event_name); - else if (status == BRCMF_E_STATUS_NO_NETWORKS) - brcmf_dbg(EVENT, "MACEVENT: %s, no networks found\n", - event_name); - else - brcmf_dbg(EVENT, "MACEVENT: %s, unexpected status %d\n", - event_name, (int)status); - break; - - case BRCMF_E_BEACON_RX: - if (status == BRCMF_E_STATUS_SUCCESS) - brcmf_dbg(EVENT, "MACEVENT: %s, SUCCESS\n", event_name); - else if (status == BRCMF_E_STATUS_FAIL) - brcmf_dbg(EVENT, "MACEVENT: %s, FAIL\n", event_name); - else - brcmf_dbg(EVENT, "MACEVENT: %s, status %d\n", - event_name, status); - break; - - case BRCMF_E_LINK: - brcmf_dbg(EVENT, "MACEVENT: %s %s\n", - event_name, link ? "UP" : "DOWN"); - break; - - case BRCMF_E_MIC_ERROR: - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s, Group %d, Flush %d\n", - event_name, eabuf, group, flush_txq); - break; - - case BRCMF_E_ICV_ERROR: - case BRCMF_E_UNICAST_DECODE_ERROR: - case BRCMF_E_MULTICAST_DECODE_ERROR: - brcmf_dbg(EVENT, "MACEVENT: %s, MAC %s\n", event_name, eabuf); - break; - - case BRCMF_E_TXFAIL: - brcmf_dbg(EVENT, "MACEVENT: %s, RA %s\n", event_name, eabuf); - break; - - case BRCMF_E_SCAN_COMPLETE: - case BRCMF_E_PMKID_CACHE: - brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); - break; - - case BRCMF_E_ESCAN_RESULT: - brcmf_dbg(EVENT, "MACEVENT: %s\n", event_name); - datalen = 0; - break; - - case BRCMF_E_PFN_NET_FOUND: - case BRCMF_E_PFN_NET_LOST: - case BRCMF_E_PFN_SCAN_COMPLETE: - brcmf_dbg(EVENT, "PNOEVENT: %s\n", event_name); - break; - - case BRCMF_E_PSK_SUP: - case BRCMF_E_PRUNE: - brcmf_dbg(EVENT, "MACEVENT: %s, status %d, reason %d\n", - event_name, (int)status, (int)reason); - break; - - case BRCMF_E_TRACE: - buf = (unsigned char *) event_data; - memcpy(&hdr, buf, sizeof(struct msgtrace_hdr)); - - if (hdr.version != MSGTRACE_VERSION) { - brcmf_dbg(ERROR, - "MACEVENT: %s [unsupported version --> brcmf" - " version:%d dongle version:%d]\n", - event_name, MSGTRACE_VERSION, hdr.version); - /* Reset datalen to avoid display below */ - datalen = 0; - break; - } - - /* There are 2 bytes available at the end of data */ - *(buf + sizeof(struct msgtrace_hdr) - + be16_to_cpu(hdr.len)) = '\0'; - - if (be32_to_cpu(hdr.discarded_bytes) - || be32_to_cpu(hdr.discarded_printf)) - brcmf_dbg(ERROR, - "WLC_E_TRACE: [Discarded traces in dongle -->" - " discarded_bytes %d discarded_printf %d]\n", - be32_to_cpu(hdr.discarded_bytes), - be32_to_cpu(hdr.discarded_printf)); - - nblost = be32_to_cpu(hdr.seqnum) - seqnum_prev - 1; - if (nblost > 0) - brcmf_dbg(ERROR, "WLC_E_TRACE: [Event lost --> seqnum " - " %d nblost %d\n", be32_to_cpu(hdr.seqnum), - nblost); - seqnum_prev = be32_to_cpu(hdr.seqnum); - - /* Display the trace buffer. Advance from \n to \n to - * avoid display big - * printf (issue with Linux printk ) - */ - p = (char *)&buf[sizeof(struct msgtrace_hdr)]; - while ((s = strstr(p, "\n")) != NULL) { - *s = '\0'; - pr_debug("%s\n", p); - p = s + 1; - } - pr_debug("%s\n", p); - - /* Reset datalen to avoid display below */ - datalen = 0; - break; - - case BRCMF_E_RSSI: - brcmf_dbg(EVENT, "MACEVENT: %s %d\n", - event_name, be32_to_cpu(*((__be32 *)event_data))); - break; - - default: - brcmf_dbg(EVENT, - "MACEVENT: %s %d, MAC %s, status %d, reason %d, " - "auth %d\n", event_name, event_type, eabuf, - (int)status, (int)reason, (int)auth_type); - break; - } - - /* show any appended data */ - brcmf_dbg_hex_dump(datalen, event_data, datalen, "Received data"); -} -#endif /* DEBUG */ - -int -brcmf_c_host_event(struct brcmf_pub *drvr, int *ifidx, void *pktdata, - struct brcmf_event_msg *event, void **data_ptr) -{ - /* check whether packet is a BRCM event pkt */ - struct brcmf_event *pvt_data = (struct brcmf_event *) pktdata; - struct brcmf_if_event *ifevent; - struct brcmf_if *ifp; - char *event_data; - u32 type, status; - u16 flags; - int evlen; - - if (memcmp(BRCM_OUI, &pvt_data->hdr.oui[0], DOT11_OUI_LEN)) { - brcmf_dbg(ERROR, "mismatched OUI, bailing\n"); - return -EBADE; - } - - /* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */ - if (get_unaligned_be16(&pvt_data->hdr.usr_subtype) != - BCMILCP_BCM_SUBTYPE_EVENT) { - brcmf_dbg(ERROR, "mismatched subtype, bailing\n"); - return -EBADE; - } - - *data_ptr = &pvt_data[1]; - event_data = *data_ptr; - - /* memcpy since BRCM event pkt may be unaligned. */ - memcpy(event, &pvt_data->msg, sizeof(struct brcmf_event_msg)); - - type = get_unaligned_be32(&event->event_type); - flags = get_unaligned_be16(&event->flags); - status = get_unaligned_be32(&event->status); - evlen = get_unaligned_be32(&event->datalen) + - sizeof(struct brcmf_event); - - switch (type) { - case BRCMF_E_IF: - ifevent = (struct brcmf_if_event *) event_data; - brcmf_dbg(TRACE, "if event\n"); - - if (ifevent->ifidx > 0 && ifevent->ifidx < BRCMF_MAX_IFS) { - if (ifevent->action == BRCMF_E_IF_ADD) { - ifp = brcmf_add_if(drvr->dev, ifevent->ifidx, - ifevent->bssidx, - event->ifname, - pvt_data->eth.h_dest); - if (IS_ERR(ifp)) - return PTR_ERR(ifp); - brcmf_net_attach(ifp); - } else { - brcmf_del_if(drvr, ifevent->ifidx); - } - } else { - brcmf_dbg(ERROR, "Invalid ifidx %d for %s\n", - ifevent->ifidx, event->ifname); - } - - /* send up the if event: btamp user needs it */ - *ifidx = brcmf_ifname2idx(drvr, event->ifname); - break; - - /* These are what external supplicant/authenticator wants */ - case BRCMF_E_LINK: - case BRCMF_E_ASSOC_IND: - case BRCMF_E_REASSOC_IND: - case BRCMF_E_DISASSOC_IND: - case BRCMF_E_MIC_ERROR: - default: - /* Fall through: this should get _everything_ */ - - *ifidx = brcmf_ifname2idx(drvr, event->ifname); - brcmf_dbg(TRACE, "MAC event %d, flags %x, status %x\n", - type, flags, status); - - /* put it back to BRCMF_E_NDIS_LINK */ - if (type == BRCMF_E_NDIS_LINK) { - u32 temp1; - __be32 temp2; - - temp1 = get_unaligned_be32(&event->event_type); - brcmf_dbg(TRACE, "Converted to WLC_E_LINK type %d\n", - temp1); - - temp2 = cpu_to_be32(BRCMF_E_NDIS_LINK); - memcpy((void *)(&pvt_data->msg.event_type), &temp2, - sizeof(pvt_data->msg.event_type)); - } - break; - } - -#ifdef DEBUG - if (BRCMF_EVENT_ON()) - brcmf_c_show_host_event(event, event_data); -#endif /* DEBUG */ - - return 0; -} - /* Convert user's input in hex pattern to byte-size mask */ static int brcmf_c_pattern_atoh(char *src, char *dst) { @@ -686,8 +238,8 @@ static void brcmf_c_pktfilter_offload_set(struct brcmf_if *ifp, char *arg) } pkt_filter->u.pattern.size_bytes = cpu_to_le32(mask_size); - buf_len = sizeof(*pkt_filter); - buf_len -= sizeof(pkt_filter->u.pattern.mask_and_pattern); + buf_len = offsetof(struct brcmf_pkt_filter_le, + u.pattern.mask_and_pattern); buf_len += mask_size + pattern_size; err = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add", pkt_filter, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 862d2acb7a1..7e58e8ce9ab 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -14,18 +14,12 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <linux/debugfs.h> -#include <linux/if_ether.h> -#include <linux/if.h> #include <linux/netdevice.h> -#include <linux/ieee80211.h> #include <linux/module.h> -#include <linux/netdevice.h> -#include <defs.h> #include <brcmu_wifi.h> #include <brcmu_utils.h> #include "dhd.h" -#include "dhd_bus.h" #include "dhd_dbg.h" static struct dentry *root_folder; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index eefa6c2560c..a0e18a1ceb4 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -27,11 +27,11 @@ #define BRCMF_HDRS_VAL 0x0040 #define BRCMF_BYTES_VAL 0x0080 #define BRCMF_INTR_VAL 0x0100 -#define BRCMF_GLOM_VAL 0x0400 -#define BRCMF_EVENT_VAL 0x0800 -#define BRCMF_BTA_VAL 0x1000 -#define BRCMF_ISCAN_VAL 0x2000 -#define BRCMF_FIL_VAL 0x4000 +#define BRCMF_GLOM_VAL 0x0200 +#define BRCMF_EVENT_VAL 0x0400 +#define BRCMF_BTA_VAL 0x0800 +#define BRCMF_FIL_VAL 0x1000 +#define BRCMF_USB_VAL 0x2000 #if defined(DEBUG) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 297652339fd..b6c86b046c1 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -16,27 +16,11 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <linux/init.h> #include <linux/kernel.h> -#include <linux/kthread.h> -#include <linux/slab.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> #include <linux/etherdevice.h> -#include <linux/mmc/sdio_func.h> -#include <linux/random.h> -#include <linux/spinlock.h> -#include <linux/ethtool.h> -#include <linux/fcntl.h> -#include <linux/fs.h> -#include <linux/uaccess.h> -#include <linux/hardirq.h> -#include <linux/mutex.h> -#include <linux/wait.h> #include <linux/module.h> #include <net/cfg80211.h> #include <net/rtnetlink.h> -#include <defs.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> @@ -45,35 +29,19 @@ #include "dhd_proto.h" #include "dhd_dbg.h" #include "wl_cfg80211.h" +#include "fwil.h" MODULE_AUTHOR("Broadcom Corporation"); -MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver."); -MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards"); +MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver."); +MODULE_SUPPORTED_DEVICE("Broadcom 802.11 WLAN fullmac cards"); MODULE_LICENSE("Dual BSD/GPL"); +#define MAX_WAIT_FOR_8021X_TX 50 /* msecs */ /* Error bits */ int brcmf_msg_level = BRCMF_ERROR_VAL; module_param(brcmf_msg_level, int, 0); -int brcmf_ifname2idx(struct brcmf_pub *drvr, char *name) -{ - int i = BRCMF_MAX_IFS; - struct brcmf_if *ifp; - - if (name == NULL || *name == '\0') - return 0; - - while (--i > 0) { - ifp = drvr->iflist[i]; - if (ifp && !strncmp(ifp->ndev->name, name, IFNAMSIZ)) - break; - } - - brcmf_dbg(TRACE, "return idx %d for \"%s\"\n", i, name); - - return i; /* default - the primary interface */ -} char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) { @@ -95,38 +63,33 @@ char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx) static void _brcmf_set_multicast_list(struct work_struct *work) { + struct brcmf_if *ifp; struct net_device *ndev; struct netdev_hw_addr *ha; - u32 dcmd_value, cnt; + u32 cmd_value, cnt; __le32 cnt_le; - __le32 dcmd_le_value; - - struct brcmf_dcmd dcmd; char *buf, *bufp; - uint buflen; - int ret; + u32 buflen; + s32 err; - struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, - multicast_work); + brcmf_dbg(TRACE, "enter\n"); - ndev = drvr->iflist[0]->ndev; - cnt = netdev_mc_count(ndev); + ifp = container_of(work, struct brcmf_if, multicast_work); + ndev = ifp->ndev; /* Determine initial value of allmulti flag */ - dcmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; + cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false; /* Send down the multicast list first. */ - - buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN); - bufp = buf = kmalloc(buflen, GFP_ATOMIC); - if (!bufp) + cnt = netdev_mc_count(ndev); + buflen = sizeof(cnt) + (cnt * ETH_ALEN); + buf = kmalloc(buflen, GFP_ATOMIC); + if (!buf) return; - - strcpy(bufp, "mcast_list"); - bufp += strlen("mcast_list") + 1; + bufp = buf; cnt_le = cpu_to_le32(cnt); - memcpy(bufp, &cnt_le, sizeof(cnt)); + memcpy(bufp, &cnt_le, sizeof(cnt_le)); bufp += sizeof(cnt_le); netdev_for_each_mc_addr(ha, ndev) { @@ -137,129 +100,66 @@ static void _brcmf_set_multicast_list(struct work_struct *work) cnt--; } - memset(&dcmd, 0, sizeof(dcmd)); - dcmd.cmd = BRCMF_C_SET_VAR; - dcmd.buf = buf; - dcmd.len = buflen; - dcmd.set = true; - - ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); - if (ret < 0) { - brcmf_dbg(ERROR, "%s: set mcast_list failed, cnt %d\n", - brcmf_ifname(drvr, 0), cnt); - dcmd_value = cnt ? true : dcmd_value; + err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen); + if (err < 0) { + brcmf_dbg(ERROR, "Setting mcast_list failed, %d\n", err); + cmd_value = cnt ? true : cmd_value; } kfree(buf); - /* Now send the allmulti setting. This is based on the setting in the + /* + * Now send the allmulti setting. This is based on the setting in the * net_device flags, but might be modified above to be turned on if we * were trying to set some addresses and dongle rejected it... */ - - buflen = sizeof("allmulti") + sizeof(dcmd_value); - buf = kmalloc(buflen, GFP_ATOMIC); - if (!buf) - return; - - dcmd_le_value = cpu_to_le32(dcmd_value); - - if (!brcmf_c_mkiovar - ("allmulti", (void *)&dcmd_le_value, - sizeof(dcmd_le_value), buf, buflen)) { - brcmf_dbg(ERROR, "%s: mkiovar failed for allmulti, datalen %d buflen %u\n", - brcmf_ifname(drvr, 0), - (int)sizeof(dcmd_value), buflen); - kfree(buf); - return; - } - - memset(&dcmd, 0, sizeof(dcmd)); - dcmd.cmd = BRCMF_C_SET_VAR; - dcmd.buf = buf; - dcmd.len = buflen; - dcmd.set = true; - - ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); - if (ret < 0) { - brcmf_dbg(ERROR, "%s: set allmulti %d failed\n", - brcmf_ifname(drvr, 0), - le32_to_cpu(dcmd_le_value)); - } - - kfree(buf); - - /* Finally, pick up the PROMISC flag as well, like the NIC - driver does */ - - dcmd_value = (ndev->flags & IFF_PROMISC) ? true : false; - dcmd_le_value = cpu_to_le32(dcmd_value); - - memset(&dcmd, 0, sizeof(dcmd)); - dcmd.cmd = BRCMF_C_SET_PROMISC; - dcmd.buf = &dcmd_le_value; - dcmd.len = sizeof(dcmd_le_value); - dcmd.set = true; - - ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); - if (ret < 0) { - brcmf_dbg(ERROR, "%s: set promisc %d failed\n", - brcmf_ifname(drvr, 0), - le32_to_cpu(dcmd_le_value)); - } + err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value); + if (err < 0) + brcmf_dbg(ERROR, "Setting allmulti failed, %d\n", err); + + /*Finally, pick up the PROMISC flag */ + cmd_value = (ndev->flags & IFF_PROMISC) ? true : false; + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value); + if (err < 0) + brcmf_dbg(ERROR, "Setting BRCMF_C_SET_PROMISC failed, %d\n", + err); } static void _brcmf_set_mac_address(struct work_struct *work) { - char buf[32]; - struct brcmf_dcmd dcmd; - int ret; - - struct brcmf_pub *drvr = container_of(work, struct brcmf_pub, - setmacaddr_work); + struct brcmf_if *ifp; + s32 err; brcmf_dbg(TRACE, "enter\n"); - if (!brcmf_c_mkiovar("cur_etheraddr", (char *)drvr->macvalue, - ETH_ALEN, buf, 32)) { - brcmf_dbg(ERROR, "%s: mkiovar failed for cur_etheraddr\n", - brcmf_ifname(drvr, 0)); - return; - } - memset(&dcmd, 0, sizeof(dcmd)); - dcmd.cmd = BRCMF_C_SET_VAR; - dcmd.buf = buf; - dcmd.len = 32; - dcmd.set = true; - ret = brcmf_proto_dcmd(drvr, 0, &dcmd, dcmd.len); - if (ret < 0) - brcmf_dbg(ERROR, "%s: set cur_etheraddr failed\n", - brcmf_ifname(drvr, 0)); - else - memcpy(drvr->iflist[0]->ndev->dev_addr, - drvr->macvalue, ETH_ALEN); - - return; + ifp = container_of(work, struct brcmf_if, setmacaddr_work); + err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr, + ETH_ALEN); + if (err < 0) { + brcmf_dbg(ERROR, "Setting cur_etheraddr failed, %d\n", err); + } else { + brcmf_dbg(TRACE, "MAC address updated to %pM\n", + ifp->mac_addr); + memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN); + } } static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; struct sockaddr *sa = (struct sockaddr *)addr; - memcpy(&drvr->macvalue, sa->sa_data, ETH_ALEN); - schedule_work(&drvr->setmacaddr_work); + memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN); + schedule_work(&ifp->setmacaddr_work); return 0; } static void brcmf_netdev_set_multicast_list(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_pub *drvr = ifp->drvr; - schedule_work(&drvr->multicast_work); + schedule_work(&ifp->multicast_work); } static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) @@ -272,7 +172,7 @@ static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev) /* Reject if down */ if (!drvr->bus_if->drvr_up || - (drvr->bus_if->state == BRCMF_BUS_DOWN)) { + (drvr->bus_if->state != BRCMF_BUS_DATA)) { brcmf_dbg(ERROR, "xmit rejected drvup=%d state=%d\n", drvr->bus_if->drvr_up, drvr->bus_if->state); @@ -350,32 +250,13 @@ void brcmf_txflowblock(struct device *dev, bool state) } } -static int brcmf_host_event(struct brcmf_pub *drvr, int *ifidx, - void *pktdata, struct brcmf_event_msg *event, - void **data) -{ - int bcmerror = 0; - - bcmerror = brcmf_c_host_event(drvr, ifidx, pktdata, event, data); - if (bcmerror != 0) - return bcmerror; - - if (drvr->iflist[*ifidx]->ndev) - brcmf_cfg80211_event(drvr->iflist[*ifidx]->ndev, - event, *data); - - return bcmerror; -} - -void brcmf_rx_frame(struct device *dev, int ifidx, +void brcmf_rx_frame(struct device *dev, u8 ifidx, struct sk_buff_head *skb_list) { unsigned char *eth; uint len; - void *data; struct sk_buff *skb, *pnext; struct brcmf_if *ifp; - struct brcmf_event_msg event; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_pub *drvr = bus_if->drvr; @@ -422,10 +303,7 @@ void brcmf_rx_frame(struct device *dev, int ifidx, skb_pull(skb, ETH_HLEN); /* Process special event packets and then discard them */ - if (ntohs(skb->protocol) == ETH_P_LINK_CTL) - brcmf_host_event(drvr, &ifidx, - skb_mac_header(skb), - &event, &data); + brcmf_fweh_process_skb(drvr, skb, &ifidx); if (drvr->iflist[ifidx]) { ifp = drvr->iflist[ifidx]; @@ -461,9 +339,11 @@ void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success) eh = (struct ethhdr *)(txp->data); type = ntohs(eh->h_proto); - if (type == ETH_P_PAE) + if (type == ETH_P_PAE) { atomic_dec(&drvr->pend_8021x_cnt); - + if (waitqueue_active(&drvr->pend_8021x_wait)) + wake_up(&drvr->pend_8021x_wait); + } } static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) @@ -487,83 +367,26 @@ static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev) return &ifp->stats; } -/* Retrieve current toe component enables, which are kept - as a bitmap in toe_ol iovar */ -static int brcmf_toe_get(struct brcmf_pub *drvr, int ifidx, u32 *toe_ol) -{ - struct brcmf_dcmd dcmd; - __le32 toe_le; - char buf[32]; - int ret; - - memset(&dcmd, 0, sizeof(dcmd)); - - dcmd.cmd = BRCMF_C_GET_VAR; - dcmd.buf = buf; - dcmd.len = (uint) sizeof(buf); - dcmd.set = false; - - strcpy(buf, "toe_ol"); - ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); - if (ret < 0) { - /* Check for older dongle image that doesn't support toe_ol */ - if (ret == -EIO) { - brcmf_dbg(ERROR, "%s: toe not supported by device\n", - brcmf_ifname(drvr, ifidx)); - return -EOPNOTSUPP; - } - - brcmf_dbg(INFO, "%s: could not get toe_ol: ret=%d\n", - brcmf_ifname(drvr, ifidx), ret); - return ret; - } - - memcpy(&toe_le, buf, sizeof(u32)); - *toe_ol = le32_to_cpu(toe_le); - return 0; -} - -/* Set current toe component enables in toe_ol iovar, - and set toe global enable iovar */ -static int brcmf_toe_set(struct brcmf_pub *drvr, int ifidx, u32 toe_ol) +/* + * Set current toe component enables in toe_ol iovar, + * and set toe global enable iovar + */ +static int brcmf_toe_set(struct brcmf_if *ifp, u32 toe_ol) { - struct brcmf_dcmd dcmd; - char buf[32]; - int ret; - __le32 toe_le = cpu_to_le32(toe_ol); - - memset(&dcmd, 0, sizeof(dcmd)); + s32 err; - dcmd.cmd = BRCMF_C_SET_VAR; - dcmd.buf = buf; - dcmd.len = (uint) sizeof(buf); - dcmd.set = true; - - /* Set toe_ol as requested */ - strcpy(buf, "toe_ol"); - memcpy(&buf[sizeof("toe_ol")], &toe_le, sizeof(u32)); - - ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); - if (ret < 0) { - brcmf_dbg(ERROR, "%s: could not set toe_ol: ret=%d\n", - brcmf_ifname(drvr, ifidx), ret); - return ret; + err = brcmf_fil_iovar_int_set(ifp, "toe_ol", toe_ol); + if (err < 0) { + brcmf_dbg(ERROR, "Setting toe_ol failed, %d\n", err); + return err; } - /* Enable toe globally only if any components are enabled. */ - toe_le = cpu_to_le32(toe_ol != 0); + err = brcmf_fil_iovar_int_set(ifp, "toe", (toe_ol != 0)); + if (err < 0) + brcmf_dbg(ERROR, "Setting toe failed, %d\n", err); - strcpy(buf, "toe"); - memcpy(&buf[sizeof("toe")], &toe_le, sizeof(u32)); + return err; - ret = brcmf_proto_dcmd(drvr, ifidx, &dcmd, dcmd.len); - if (ret < 0) { - brcmf_dbg(ERROR, "%s: could not set toe: ret=%d\n", - brcmf_ifname(drvr, ifidx), ret); - return ret; - } - - return 0; } static void brcmf_ethtool_get_drvinfo(struct net_device *ndev, @@ -581,8 +404,9 @@ static const struct ethtool_ops brcmf_ethtool_ops = { .get_drvinfo = brcmf_ethtool_get_drvinfo, }; -static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr) +static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr) { + struct brcmf_pub *drvr = ifp->drvr; struct ethtool_drvinfo info; char drvname[sizeof(info.driver)]; u32 cmd; @@ -633,7 +457,7 @@ static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr) /* Get toe offload components from dongle */ case ETHTOOL_GRXCSUM: case ETHTOOL_GTXCSUM: - ret = brcmf_toe_get(drvr, 0, &toe_cmpnt); + ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt); if (ret < 0) return ret; @@ -654,7 +478,7 @@ static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr) return -EFAULT; /* Read the current settings, update and write back */ - ret = brcmf_toe_get(drvr, 0, &toe_cmpnt); + ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt); if (ret < 0) return ret; @@ -666,18 +490,16 @@ static int brcmf_ethtool(struct brcmf_pub *drvr, void __user *uaddr) else toe_cmpnt &= ~csum_dir; - ret = brcmf_toe_set(drvr, 0, toe_cmpnt); + ret = brcmf_toe_set(ifp, toe_cmpnt); if (ret < 0) return ret; /* If setting TX checksum mode, tell Linux the new mode */ if (cmd == ETHTOOL_STXCSUM) { if (edata.data) - drvr->iflist[0]->ndev->features |= - NETIF_F_IP_CSUM; + ifp->ndev->features |= NETIF_F_IP_CSUM; else - drvr->iflist[0]->ndev->features &= - ~NETIF_F_IP_CSUM; + ifp->ndev->features &= ~NETIF_F_IP_CSUM; } break; @@ -701,7 +523,7 @@ static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr, return -1; if (cmd == SIOCETHTOOL) - return brcmf_ethtool(drvr, ifr->ifr_data); + return brcmf_ethtool(ifp, ifr->ifr_data); return -EOPNOTSUPP; } @@ -712,10 +534,12 @@ static int brcmf_netdev_stop(struct net_device *ndev) struct brcmf_pub *drvr = ifp->drvr; brcmf_dbg(TRACE, "Enter\n"); - brcmf_cfg80211_down(drvr->config); + if (drvr->bus_if->drvr_up == 0) return 0; + brcmf_cfg80211_down(ndev); + /* Set state and stop OS transmissions */ drvr->bus_if->drvr_up = false; netif_stop_queue(ndev); @@ -730,38 +554,35 @@ static int brcmf_netdev_open(struct net_device *ndev) struct brcmf_bus *bus_if = drvr->bus_if; u32 toe_ol; s32 ret = 0; - uint up = 0; brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx); - if (ifp->idx == 0) { /* do it only for primary eth0 */ - /* If bus is not ready, can't continue */ - if (bus_if->state != BRCMF_BUS_DATA) { - brcmf_dbg(ERROR, "failed bus is not ready\n"); - return -EAGAIN; - } + /* If bus is not ready, can't continue */ + if (bus_if->state != BRCMF_BUS_DATA) { + brcmf_dbg(ERROR, "failed bus is not ready\n"); + return -EAGAIN; + } - atomic_set(&drvr->pend_8021x_cnt, 0); + atomic_set(&drvr->pend_8021x_cnt, 0); - memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN); + memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN); - /* Get current TOE mode from dongle */ - if (brcmf_toe_get(drvr, ifp->idx, &toe_ol) >= 0 - && (toe_ol & TOE_TX_CSUM_OL) != 0) - drvr->iflist[ifp->idx]->ndev->features |= - NETIF_F_IP_CSUM; - else - drvr->iflist[ifp->idx]->ndev->features &= - ~NETIF_F_IP_CSUM; - } + /* Get current TOE mode from dongle */ + if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0 + && (toe_ol & TOE_TX_CSUM_OL) != 0) + drvr->iflist[ifp->idx]->ndev->features |= + NETIF_F_IP_CSUM; + else + drvr->iflist[ifp->idx]->ndev->features &= + ~NETIF_F_IP_CSUM; /* make sure RF is ready for work */ - brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_UP, (char *)&up, sizeof(up)); + brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); /* Allow transmit calls */ netif_start_queue(ndev); drvr->bus_if->drvr_up = true; - if (brcmf_cfg80211_up(drvr->config)) { + if (brcmf_cfg80211_up(ndev)) { brcmf_dbg(ERROR, "failed to bring up cfg80211\n"); return -1; } @@ -779,39 +600,38 @@ static const struct net_device_ops brcmf_netdev_ops_pri = { .ndo_set_rx_mode = brcmf_netdev_set_multicast_list }; +static const struct net_device_ops brcmf_netdev_ops_virt = { + .ndo_open = brcmf_cfg80211_up, + .ndo_stop = brcmf_cfg80211_down, + .ndo_get_stats = brcmf_netdev_get_stats, + .ndo_do_ioctl = brcmf_netdev_ioctl_entry, + .ndo_start_xmit = brcmf_netdev_start_xmit, + .ndo_set_mac_address = brcmf_netdev_set_mac_address, + .ndo_set_rx_mode = brcmf_netdev_set_multicast_list +}; + int brcmf_net_attach(struct brcmf_if *ifp) { struct brcmf_pub *drvr = ifp->drvr; struct net_device *ndev; - u8 temp_addr[ETH_ALEN]; - - brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx); - ndev = drvr->iflist[ifp->idx]->ndev; - ndev->netdev_ops = &brcmf_netdev_ops_pri; + brcmf_dbg(TRACE, "ifidx %d mac %pM\n", ifp->idx, ifp->mac_addr); + ndev = ifp->ndev; - /* - * determine mac address to use - */ - if (is_valid_ether_addr(ifp->mac_addr)) - memcpy(temp_addr, ifp->mac_addr, ETH_ALEN); + /* set appropriate operations */ + if (!ifp->idx) + ndev->netdev_ops = &brcmf_netdev_ops_pri; else - memcpy(temp_addr, drvr->mac, ETH_ALEN); - - if (ifp->idx == 1) { - brcmf_dbg(TRACE, "ACCESS POINT MAC:\n"); - /* ACCESSPOINT INTERFACE CASE */ - temp_addr[0] |= 0X02; /* set bit 2 , - - Locally Administered address */ + ndev->netdev_ops = &brcmf_netdev_ops_virt; - } ndev->hard_header_len = ETH_HLEN + drvr->hdrlen; ndev->ethtool_ops = &brcmf_ethtool_ops; drvr->rxsz = ndev->mtu + ndev->hard_header_len + drvr->hdrlen; - memcpy(ndev->dev_addr, temp_addr, ETH_ALEN); + /* set the mac address */ + memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN); if (register_netdev(ndev) != 0) { brcmf_dbg(ERROR, "couldn't register the net device\n"); @@ -824,17 +644,15 @@ int brcmf_net_attach(struct brcmf_if *ifp) fail: ndev->netdev_ops = NULL; - free_netdev(ndev); return -EBADE; } -struct brcmf_if *brcmf_add_if(struct device *dev, int ifidx, s32 bssidx, - char *name, u8 *mac_addr) +struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx, s32 bssidx, + char *name, u8 *addr_mask) { struct brcmf_if *ifp; struct net_device *ndev; - struct brcmf_bus *bus_if = dev_get_drvdata(dev); - struct brcmf_pub *drvr = bus_if->drvr; + int i; brcmf_dbg(TRACE, "idx %d\n", ifidx); @@ -844,12 +662,17 @@ struct brcmf_if *brcmf_add_if(struct device *dev, int ifidx, s32 bssidx, * in case we missed the BRCMF_E_IF_DEL event. */ if (ifp) { - brcmf_dbg(ERROR, "ERROR: netdev:%s already exists, try free & unregister\n", + brcmf_dbg(ERROR, "ERROR: netdev:%s already exists\n", ifp->ndev->name); - netif_stop_queue(ifp->ndev); - unregister_netdev(ifp->ndev); - free_netdev(ifp->ndev); - drvr->iflist[ifidx] = NULL; + if (ifidx) { + netif_stop_queue(ifp->ndev); + unregister_netdev(ifp->ndev); + free_netdev(ifp->ndev); + drvr->iflist[ifidx] = NULL; + } else { + brcmf_dbg(ERROR, "ignore IF event\n"); + return ERR_PTR(-EINVAL); + } } /* Allocate netdev, including space for private structure */ @@ -865,11 +688,16 @@ struct brcmf_if *brcmf_add_if(struct device *dev, int ifidx, s32 bssidx, drvr->iflist[ifidx] = ifp; ifp->idx = ifidx; ifp->bssidx = bssidx; - if (mac_addr != NULL) - memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN); - brcmf_dbg(TRACE, " ==== pid:%x, net_device for if:%s created ===\n", - current->pid, ifp->ndev->name); + INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address); + INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list); + + if (addr_mask != NULL) + for (i = 0; i < ETH_ALEN; i++) + ifp->mac_addr[i] = drvr->mac[i] ^ addr_mask[i]; + + brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n", + current->pid, ifp->ndev->name, ifp->mac_addr); return ifp; } @@ -896,6 +724,9 @@ void brcmf_del_if(struct brcmf_pub *drvr, int ifidx) netif_stop_queue(ifp->ndev); } + cancel_work_sync(&ifp->setmacaddr_work); + cancel_work_sync(&ifp->multicast_work); + unregister_netdev(ifp->ndev); drvr->iflist[ifidx] = NULL; if (ifidx == 0) @@ -934,11 +765,13 @@ int brcmf_attach(uint bus_hdrlen, struct device *dev) goto fail; } - INIT_WORK(&drvr->setmacaddr_work, _brcmf_set_mac_address); - INIT_WORK(&drvr->multicast_work, _brcmf_set_multicast_list); + /* attach firmware event handler */ + brcmf_fweh_attach(drvr); INIT_LIST_HEAD(&drvr->bus_if->dcmd_list); + init_waitqueue_head(&drvr->pend_8021x_wait); + return ret; fail: @@ -964,7 +797,7 @@ int brcmf_bus_start(struct device *dev) } /* add primary networking interface */ - ifp = brcmf_add_if(dev, 0, 0, "wlan%d", NULL); + ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL); if (IS_ERR(ifp)) return PTR_ERR(ifp); @@ -974,15 +807,25 @@ int brcmf_bus_start(struct device *dev) /* Bus is ready, do any initialization */ ret = brcmf_c_preinit_dcmds(ifp); if (ret < 0) - return ret; + goto fail; drvr->config = brcmf_cfg80211_attach(drvr); - if (drvr->config == NULL) - return -ENOMEM; + if (drvr->config == NULL) { + ret = -ENOMEM; + goto fail; + } + + ret = brcmf_fweh_activate_events(ifp); + if (ret < 0) + goto fail; ret = brcmf_net_attach(ifp); +fail: if (ret < 0) { - brcmf_dbg(ERROR, "brcmf_net_attach failed"); + brcmf_dbg(ERROR, "failed: %d\n", ret); + if (drvr->config) + brcmf_cfg80211_detach(drvr->config); + free_netdev(drvr->iflist[0]->ndev); drvr->iflist[0] = NULL; return ret; } @@ -1011,6 +854,11 @@ void brcmf_detach(struct device *dev) brcmf_dbg(TRACE, "Enter\n"); + if (drvr == NULL) + return; + + /* stop firmware event handling */ + brcmf_fweh_detach(drvr); /* make sure primary interface removed last */ for (i = BRCMF_MAX_IFS-1; i > -1; i--) @@ -1020,8 +868,6 @@ void brcmf_detach(struct device *dev) brcmf_bus_detach(drvr); if (drvr->prot) { - cancel_work_sync(&drvr->setmacaddr_work); - cancel_work_sync(&drvr->multicast_work); brcmf_proto_detach(drvr); } @@ -1035,26 +881,19 @@ static int brcmf_get_pend_8021x_cnt(struct brcmf_pub *drvr) return atomic_read(&drvr->pend_8021x_cnt); } -#define MAX_WAIT_FOR_8021X_TX 10 - int brcmf_netdev_wait_pend8021x(struct net_device *ndev) { struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_pub *drvr = ifp->drvr; - int timeout = 10 * HZ / 1000; - int ntimes = MAX_WAIT_FOR_8021X_TX; - int pend = brcmf_get_pend_8021x_cnt(drvr); - - while (ntimes && pend) { - if (pend) { - set_current_state(TASK_INTERRUPTIBLE); - schedule_timeout(timeout); - set_current_state(TASK_RUNNING); - ntimes--; - } - pend = brcmf_get_pend_8021x_cnt(drvr); - } - return pend; + int err; + + err = wait_event_timeout(drvr->pend_8021x_wait, + !brcmf_get_pend_8021x_cnt(drvr), + msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX)); + + WARN_ON(!err); + + return !err; } static void brcmf_driver_init(struct work_struct *work) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h index 7fe6779b90c..48fa7030219 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h @@ -36,14 +36,7 @@ extern void brcmf_proto_stop(struct brcmf_pub *drvr); extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx, struct sk_buff *txp); -/* Use protocol to issue command to dongle */ -extern int brcmf_proto_dcmd(struct brcmf_pub *drvr, int ifidx, - struct brcmf_dcmd *dcmd, int len); - /* Sets dongle media info (drv_version, mac address). */ extern int brcmf_c_preinit_dcmds(struct brcmf_if *ifp); -extern int brcmf_proto_cdc_set_dcmd(struct brcmf_pub *drvr, int ifidx, - uint cmd, void *buf, uint len); - #endif /* _BRCMF_PROTO_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 415f2be3637..45725454714 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -533,9 +533,11 @@ struct brcmf_sdio { u8 *rxbuf; /* Buffer for receiving control packets */ uint rxblen; /* Allocated length of rxbuf */ u8 *rxctl; /* Aligned pointer into rxbuf */ + u8 *rxctl_orig; /* pointer for freeing rxctl */ u8 *databuf; /* Buffer for receiving big glom packet */ u8 *dataptr; /* Aligned pointer into databuf */ uint rxlen; /* Length of valid data in buffer */ + spinlock_t rxctl_lock; /* protection lock for ctrl frame resources */ u8 sdpcm_ver; /* Bus protocol reported by dongle */ @@ -582,8 +584,6 @@ struct brcmf_sdio { struct list_head dpc_tsklst; spinlock_t dpc_tl_lock; - struct semaphore sdsem; - const struct firmware *firmware; u32 fw_ptr; @@ -1037,9 +1037,9 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) } } -static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, - struct brcmf_sdio_read *rd, - enum brcmf_sdio_frmtype type) +static int brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, + struct brcmf_sdio_read *rd, + enum brcmf_sdio_frmtype type) { u16 len, checksum; u8 rx_seq, fc, tx_seq_max; @@ -1054,26 +1054,26 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, /* All zero means no more to read */ if (!(len | checksum)) { bus->rxpending = false; - return false; + return -ENODATA; } if ((u16)(~(len ^ checksum))) { brcmf_dbg(ERROR, "HW header checksum error\n"); bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); - return false; + return -EIO; } if (len < SDPCM_HDRLEN) { brcmf_dbg(ERROR, "HW header length error\n"); - return false; + return -EPROTO; } if (type == BRCMF_SDIO_FT_SUPER && (roundup(len, bus->blocksize) != rd->len)) { brcmf_dbg(ERROR, "HW superframe header length error\n"); - return false; + return -EPROTO; } if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { brcmf_dbg(ERROR, "HW subframe header length error\n"); - return false; + return -EPROTO; } rd->len = len; @@ -1091,7 +1091,7 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, SDPCM_GLOMDESC(&header[SDPCM_FRAMETAG_LEN])) { brcmf_dbg(ERROR, "Glom descriptor found in superframe head\n"); rd->len = 0; - return false; + return -EINVAL; } rx_seq = SDPCM_PACKET_SEQUENCE(&header[SDPCM_FRAMETAG_LEN]); rd->channel = SDPCM_PACKET_CHANNEL(&header[SDPCM_FRAMETAG_LEN]); @@ -1102,18 +1102,18 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, bus->sdcnt.rx_toolong++; brcmf_sdbrcm_rxfail(bus, false, false); rd->len = 0; - return false; + return -EPROTO; } if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { brcmf_dbg(ERROR, "Wrong channel for superframe\n"); rd->len = 0; - return false; + return -EINVAL; } if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && rd->channel != SDPCM_EVENT_CHANNEL) { brcmf_dbg(ERROR, "Wrong channel for subframe\n"); rd->len = 0; - return false; + return -EINVAL; } rd->dat_offset = SDPCM_DOFFSET_VALUE(&header[SDPCM_FRAMETAG_LEN]); if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { @@ -1121,7 +1121,7 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, bus->sdcnt.rx_badhdr++; brcmf_sdbrcm_rxfail(bus, false, false); rd->len = 0; - return false; + return -ENXIO; } if (rd->seq_num != rx_seq) { brcmf_dbg(ERROR, "seq %d: sequence number error, expect %d\n", @@ -1131,7 +1131,7 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, } /* no need to check the reset for subframe */ if (type == BRCMF_SDIO_FT_SUB) - return true; + return 0; rd->len_nxtfrm = header[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET]; if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { /* only warm for NON glom packet */ @@ -1155,7 +1155,7 @@ static bool brcmf_sdio_hdparser(struct brcmf_sdio *bus, u8 *header, } bus->tx_max = tx_seq_max; - return true; + return 0; } static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) @@ -1272,6 +1272,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) * read directly into the chained packet, or allocate a large * packet and and copy into the chain. */ + sdio_claim_host(bus->sdiodev->func[1]); if (usechain) { errcode = brcmf_sdcard_recv_chain(bus->sdiodev, bus->sdiodev->sbwad, @@ -1293,6 +1294,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) dlen); errcode = -1; } + sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2rxdata++; /* On failure, kill the superframe, allow a couple retries */ @@ -1301,6 +1303,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) dlen, errcode); bus->sdiodev->bus_if->dstats.rx_errors++; + sdio_claim_host(bus->sdiodev->func[1]); if (bus->glomerr++ < 3) { brcmf_sdbrcm_rxfail(bus, true, true); } else { @@ -1309,6 +1312,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } + sdio_release_host(bus->sdiodev->func[1]); return 0; } @@ -1318,8 +1322,10 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) rd_new.seq_num = rxseq; rd_new.len = dlen; - errcode = -!brcmf_sdio_hdparser(bus, pfirst->data, &rd_new, - BRCMF_SDIO_FT_SUPER); + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparser(bus, pfirst->data, &rd_new, + BRCMF_SDIO_FT_SUPER); + sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = rd_new.len_nxtfrm << 4; /* Remove superframe header, remember offset */ @@ -1335,9 +1341,10 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) rd_new.len = pnext->len; rd_new.seq_num = rxseq++; - errcode = -!brcmf_sdio_hdparser(bus, pnext->data, - &rd_new, - BRCMF_SDIO_FT_SUB); + sdio_claim_host(bus->sdiodev->func[1]); + errcode = brcmf_sdio_hdparser(bus, pnext->data, &rd_new, + BRCMF_SDIO_FT_SUB); + sdio_release_host(bus->sdiodev->func[1]); brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), pnext->data, 32, "subframe:\n"); @@ -1347,6 +1354,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) if (errcode) { /* Terminate frame on error, request a couple retries */ + sdio_claim_host(bus->sdiodev->func[1]); if (bus->glomerr++ < 3) { /* Restore superframe header space */ skb_push(pfirst, sfdoff); @@ -1357,6 +1365,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) bus->sdcnt.rxglomfail++; brcmf_sdbrcm_free_glom(bus); } + sdio_release_host(bus->sdiodev->func[1]); bus->cur_read.len = 0; return 0; } @@ -1397,11 +1406,8 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) pfirst->prev); } /* sent any remaining packets up */ - if (bus->glom.qlen) { - up(&bus->sdsem); + if (bus->glom.qlen) brcmf_rx_frame(bus->sdiodev->dev, ifidx, &bus->glom); - down(&bus->sdsem); - } bus->sdcnt.rxglomframes++; bus->sdcnt.rxglompkts += bus->glom.qlen; @@ -1442,21 +1448,24 @@ static void brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) { uint rdlen, pad; - + u8 *buf = NULL, *rbuf; int sdret; brcmf_dbg(TRACE, "Enter\n"); - /* Set rxctl for frame (w/optional alignment) */ - bus->rxctl = bus->rxbuf; - bus->rxctl += BRCMF_FIRSTREAD; - pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN); + if (bus->rxblen) + buf = vzalloc(bus->rxblen); + if (!buf) { + brcmf_dbg(ERROR, "no memory for control frame\n"); + goto done; + } + rbuf = bus->rxbuf; + pad = ((unsigned long)rbuf % BRCMF_SDALIGN); if (pad) - bus->rxctl += (BRCMF_SDALIGN - pad); - bus->rxctl -= BRCMF_FIRSTREAD; + rbuf += (BRCMF_SDALIGN - pad); /* Copy the already-read portion over */ - memcpy(bus->rxctl, hdr, BRCMF_FIRSTREAD); + memcpy(buf, hdr, BRCMF_FIRSTREAD); if (len <= BRCMF_FIRSTREAD) goto gotpkt; @@ -1493,11 +1502,11 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) goto done; } - /* Read remainder of frame body into the rxctl buffer */ + /* Read remain of frame body */ sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, - F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen); + F2SYNC, rbuf, rdlen); bus->sdcnt.f2rxdata++; /* Control frame failures need retransmission */ @@ -1507,16 +1516,26 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) bus->sdcnt.rxc_errors++; brcmf_sdbrcm_rxfail(bus, true, true); goto done; - } + } else + memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); gotpkt: brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), - bus->rxctl, len, "RxCtrl:\n"); + buf, len, "RxCtrl:\n"); /* Point to valid data and indicate its length */ - bus->rxctl += doff; + spin_lock_bh(&bus->rxctl_lock); + if (bus->rxctl) { + brcmf_dbg(ERROR, "last control frame is being processed.\n"); + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); + goto done; + } + bus->rxctl = buf + doff; + bus->rxctl_orig = buf; bus->rxlen = len - doff; + spin_unlock_bh(&bus->rxctl_lock); done: /* Awake any waiters */ @@ -1571,6 +1590,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len_left = rd->len; /* read header first for unknow frame length */ + sdio_claim_host(bus->sdiodev->func[1]); if (!rd->len) { sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad, @@ -1583,6 +1603,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) sdret); bus->sdcnt.rx_hdrfail++; brcmf_sdbrcm_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); continue; } @@ -1590,8 +1611,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n"); - if (!brcmf_sdio_hdparser(bus, bus->rxhdr, rd, - BRCMF_SDIO_FT_NORMAL)) { + if (brcmf_sdio_hdparser(bus, bus->rxhdr, rd, + BRCMF_SDIO_FT_NORMAL)) { + sdio_release_host(bus->sdiodev->func[1]); if (!bus->rxpending) break; else @@ -1607,6 +1629,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd->len_nxtfrm = 0; /* treat all packet as event if we don't know */ rd->channel = SDPCM_EVENT_CHANNEL; + sdio_release_host(bus->sdiodev->func[1]); continue; } rd->len_left = rd->len > BRCMF_FIRSTREAD ? @@ -1624,6 +1647,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) bus->sdiodev->bus_if->dstats.rx_dropped++; brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); continue; } skb_pull(pkt, head_read); @@ -1632,14 +1656,17 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); bus->sdcnt.f2rxdata++; + sdio_release_host(bus->sdiodev->func[1]); if (sdret < 0) { brcmf_dbg(ERROR, "read %d bytes from channel %d failed: %d\n", rd->len, rd->channel, sdret); brcmu_pkt_buf_free_skb(pkt); bus->sdiodev->bus_if->dstats.rx_errors++; + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(rd->channel)); + sdio_release_host(bus->sdiodev->func[1]); continue; } @@ -1650,8 +1677,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } else { memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); rd_new.seq_num = rd->seq_num; - if (!brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new, - BRCMF_SDIO_FT_NORMAL)) { + sdio_claim_host(bus->sdiodev->func[1]); + if (brcmf_sdio_hdparser(bus, bus->rxhdr, &rd_new, + BRCMF_SDIO_FT_NORMAL)) { rd->len = 0; brcmu_pkt_buf_free_skb(pkt); } @@ -1662,9 +1690,11 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) roundup(rd_new.len, 16) >> 4); rd->len = 0; brcmf_sdbrcm_rxfail(bus, true, true); + sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; } + sdio_release_host(bus->sdiodev->func[1]); rd->len_nxtfrm = rd_new.len_nxtfrm; rd->channel = rd_new.channel; rd->dat_offset = rd_new.dat_offset; @@ -1680,7 +1710,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) rd_new.seq_num); /* Force retry w/normal header read */ rd->len = 0; + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_rxfail(bus, false, true); + sdio_release_host(bus->sdiodev->func[1]); brcmu_pkt_buf_free_skb(pkt); continue; } @@ -1703,7 +1735,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } else { brcmf_dbg(ERROR, "%s: glom superframe w/o " "descriptor!\n", __func__); + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_rxfail(bus, false, false); + sdio_release_host(bus->sdiodev->func[1]); } /* prepare the descriptor for the next read */ rd->len = rd->len_nxtfrm << 4; @@ -1734,10 +1768,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) continue; } - /* Unlock during rx call */ - up(&bus->sdsem); brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt); - down(&bus->sdsem); } rxcount = maxframes - rxleft; @@ -1755,15 +1786,6 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) } static void -brcmf_sdbrcm_wait_for_event(struct brcmf_sdio *bus, bool *lockvar) -{ - up(&bus->sdsem); - wait_event_interruptible_timeout(bus->ctrl_wait, !*lockvar, HZ * 2); - down(&bus->sdsem); - return; -} - -static void brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) { if (waitqueue_active(&bus->ctrl_wait)) @@ -1864,6 +1886,7 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, if (len & (ALIGNMENT - 1)) len = roundup(len, ALIGNMENT); + sdio_claim_host(bus->sdiodev->func[1]); ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, pkt); bus->sdcnt.f2txdata++; @@ -1891,15 +1914,14 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, } } + sdio_release_host(bus->sdiodev->func[1]); if (ret == 0) bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; done: /* restore pkt buffer pointer before calling tx complete routine */ skb_pull(pkt, SDPCM_HDRLEN + pad); - up(&bus->sdsem); brcmf_txcomplete(bus->sdiodev->dev, pkt, ret != 0); - down(&bus->sdsem); if (free_pkt) brcmu_pkt_buf_free_skb(pkt); @@ -1940,9 +1962,11 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) /* In poll mode, need to check for other events */ if (!bus->intr && cnt) { /* Check device status, signal pending interrupt */ + sdio_claim_host(bus->sdiodev->func[1]); ret = r_sdreg32(bus, &intstatus, offsetof(struct sdpcmd_regs, intstatus)); + sdio_release_host(bus->sdiodev->func[1]); bus->sdcnt.f2txdata++; if (ret != 0) break; @@ -1979,7 +2003,7 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) bus->watchdog_tsk = NULL; } - down(&bus->sdsem); + sdio_claim_host(bus->sdiodev->func[1]); /* Enable clock for device interrupts */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); @@ -2013,6 +2037,7 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) /* Turn off the backplane clock (only) */ brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); /* Clear the data packet queues */ brcmu_pktq_flush(&bus->txq, true, NULL, NULL); @@ -2023,14 +2048,14 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev) brcmf_sdbrcm_free_glom(bus); /* Clear rx control and wake any waiters */ + spin_lock_bh(&bus->rxctl_lock); bus->rxlen = 0; + spin_unlock_bh(&bus->rxctl_lock); brcmf_sdbrcm_dcmd_resp_wake(bus); /* Reset some F2 state stuff */ bus->rxskip = false; bus->tx_seq = bus->rx_seq = 0; - - up(&bus->sdsem); } #ifdef CONFIG_BRCMFMAC_SDIO_OOB @@ -2114,7 +2139,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); - down(&bus->sdsem); + sdio_claim_host(bus->sdiodev->func[1]); /* If waiting for HTAVAIL, check status */ if (bus->clkstate == CLK_PENDING) { @@ -2168,9 +2193,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) /* Pending interrupt indicates new device status */ if (atomic_read(&bus->ipend) > 0) { atomic_set(&bus->ipend, 0); - sdio_claim_host(bus->sdiodev->func[1]); err = brcmf_sdio_intr_rstatus(bus); - sdio_release_host(bus->sdiodev->func[1]); } /* Start with leftover status bits */ @@ -2199,6 +2222,8 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) intstatus |= brcmf_sdbrcm_hostmail(bus); } + sdio_release_host(bus->sdiodev->func[1]); + /* Generally don't ask for these, can get CRC errors... */ if (intstatus & I_WR_OOSYNC) { brcmf_dbg(ERROR, "Dongle reports WR_OOSYNC\n"); @@ -2245,6 +2270,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) (bus->clkstate == CLK_AVAIL)) { int i; + sdio_claim_host(bus->sdiodev->func[1]); err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, (u32) bus->ctrl_frame_len); @@ -2278,6 +2304,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) } else { bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP; } + sdio_release_host(bus->sdiodev->func[1]); bus->ctrl_frame_stat = false; brcmf_sdbrcm_wait_event_wakeup(bus); } @@ -2307,10 +2334,10 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) if ((bus->clkstate != CLK_PENDING) && bus->idletime == BRCMF_IDLE_IMMEDIATE) { bus->activity = false; + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); } - - up(&bus->sdsem); } static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) @@ -2601,11 +2628,10 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) /* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - /* Need to lock here to protect txseq and SDIO tx calls */ - down(&bus->sdsem); - /* Make sure backplane clock is on */ + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); + sdio_release_host(bus->sdiodev->func[1]); /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */ *(__le16 *) frame = cpu_to_le16((u16) msglen); @@ -2628,7 +2654,9 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) bus->ctrl_frame_buf = frame; bus->ctrl_frame_len = len; - brcmf_sdbrcm_wait_for_event(bus, &bus->ctrl_frame_stat); + wait_event_interruptible_timeout(bus->ctrl_wait, + !bus->ctrl_frame_stat, + msecs_to_jiffies(2000)); if (!bus->ctrl_frame_stat) { brcmf_dbg(INFO, "ctrl_frame_stat == false\n"); @@ -2647,7 +2675,9 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) frame, min_t(u16, len, 16), "TxHdr:\n"); do { + sdio_claim_host(bus->sdiodev->func[1]); ret = brcmf_tx_frame(bus, frame, len); + sdio_release_host(bus->sdiodev->func[1]); } while (ret < 0 && retries++ < TXRETRIES); } @@ -2657,13 +2687,13 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); bus->activity = false; + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); + sdio_release_host(bus->sdiodev->func[1]); } else { spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); } - up(&bus->sdsem); - if (ret) bus->sdcnt.tx_ctlerrs++; else @@ -2693,8 +2723,10 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, * Read last word in socram to determine * address of sdpcm_shared structure */ + sdio_claim_host(bus->sdiodev->func[1]); rv = brcmf_sdbrcm_membytes(bus, false, shaddr, (u8 *)&addr_le, 4); + sdio_claim_host(bus->sdiodev->func[1]); if (rv < 0) return rv; @@ -2713,8 +2745,10 @@ static int brcmf_sdio_readshared(struct brcmf_sdio *bus, } /* Read hndrte_shared structure */ + sdio_claim_host(bus->sdiodev->func[1]); rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&sh_le, sizeof(struct sdpcm_shared_le)); + sdio_release_host(bus->sdiodev->func[1]); if (rv < 0) return rv; @@ -2817,12 +2851,14 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, if ((sh->flags & SDPCM_SHARED_TRAP) == 0) return 0; + sdio_claim_host(bus->sdiodev->func[1]); error = brcmf_sdbrcm_membytes(bus, false, sh->trap_addr, (u8 *)&tr, sizeof(struct brcmf_trap_info)); if (error < 0) return error; nbytes = brcmf_sdio_dump_console(bus, sh, data, count); + sdio_release_host(bus->sdiodev->func[1]); if (nbytes < 0) return nbytes; @@ -2868,6 +2904,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, return 0; } + sdio_claim_host(bus->sdiodev->func[1]); if (sh->assert_file_addr != 0) { error = brcmf_sdbrcm_membytes(bus, false, sh->assert_file_addr, (u8 *)file, 80); @@ -2880,6 +2917,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, if (error < 0) return error; } + sdio_release_host(bus->sdiodev->func[1]); res = scnprintf(buf, sizeof(buf), "dongle assert: %s:%d: assert(%s)\n", @@ -2892,9 +2930,7 @@ static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) int error; struct sdpcm_shared sh; - down(&bus->sdsem); error = brcmf_sdio_readshared(bus, &sh); - up(&bus->sdsem); if (error < 0) return error; @@ -2921,7 +2957,6 @@ static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, if (pos != 0) return 0; - down(&bus->sdsem); error = brcmf_sdio_readshared(bus, &sh); if (error < 0) goto done; @@ -2938,7 +2973,6 @@ static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, error += nbytes; *ppos += error; done: - up(&bus->sdsem); return error; } @@ -2989,6 +3023,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) int timeleft; uint rxlen = 0; bool pending; + u8 *buf; struct brcmf_bus *bus_if = dev_get_drvdata(dev); struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct brcmf_sdio *bus = sdiodev->bus; @@ -2998,11 +3033,15 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) /* Wait until control frame is available */ timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); - down(&bus->sdsem); + spin_lock_bh(&bus->rxctl_lock); rxlen = bus->rxlen; memcpy(msg, bus->rxctl, min(msglen, rxlen)); + bus->rxctl = NULL; + buf = bus->rxctl_orig; + bus->rxctl_orig = NULL; bus->rxlen = 0; - up(&bus->sdsem); + spin_unlock_bh(&bus->rxctl_lock); + vfree(buf); if (rxlen) { brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", @@ -3338,13 +3377,16 @@ brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) { bool ret; - /* Download the firmware */ + sdio_claim_host(bus->sdiodev->func[1]); + brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); ret = _brcmf_sdbrcm_download_firmware(bus) == 0; brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); + sdio_release_host(bus->sdiodev->func[1]); + return ret; } @@ -3373,7 +3415,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) bus->sdcnt.tickcnt = 0; brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); - down(&bus->sdsem); + sdio_claim_host(bus->sdiodev->func[1]); /* Make sure backplane clock is on, needed to generate F2 interrupt */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); @@ -3442,7 +3484,7 @@ static int brcmf_sdbrcm_bus_init(struct device *dev) brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); exit: - up(&bus->sdsem); + sdio_release_host(bus->sdiodev->func[1]); return ret; } @@ -3489,8 +3531,6 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) brcmf_dbg(TIMER, "Enter\n"); - down(&bus->sdsem); - /* Poll period: check device if appropriate. */ if (bus->poll && (++bus->polltick >= bus->pollrate)) { u32 intstatus = 0; @@ -3507,9 +3547,11 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) u8 devpend; spin_unlock_irqrestore(&bus->dpc_tl_lock, flags); + sdio_claim_host(bus->sdiodev->func[1]); devpend = brcmf_sdio_regrb(bus->sdiodev, SDIO_CCCR_INTx, NULL); + sdio_release_host(bus->sdiodev->func[1]); intstatus = devpend & (INTR_STATUS_FUNC1 | INTR_STATUS_FUNC2); @@ -3534,16 +3576,18 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) } #ifdef DEBUG /* Poll for console output periodically */ - if (bus_if->state == BRCMF_BUS_DATA && + if (bus_if && bus_if->state == BRCMF_BUS_DATA && bus->console_interval != 0) { bus->console.count += BRCMF_WD_POLL_MS; if (bus->console.count >= bus->console_interval) { bus->console.count -= bus->console_interval; + sdio_claim_host(bus->sdiodev->func[1]); /* Make sure backplane clock is on */ brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); if (brcmf_sdbrcm_readconsole(bus) < 0) /* stop on error */ bus->console_interval = 0; + sdio_release_host(bus->sdiodev->func[1]); } } #endif /* DEBUG */ @@ -3556,13 +3600,13 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) bus->activity = false; brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); } else { + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); } } } - up(&bus->sdsem); - return (atomic_read(&bus->ipend) > 0); } @@ -3657,6 +3701,8 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) bus->alp_only = true; + sdio_claim_host(bus->sdiodev->func[1]); + pr_debug("F1 signature read @0x18000000=0x%4x\n", brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); @@ -3704,6 +3750,8 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) reg_val = brcmf_sdio_regrl(bus->sdiodev, reg_addr, NULL); brcmf_sdio_regwl(bus->sdiodev, reg_addr, reg_val | CC_BPRESEN, NULL); + sdio_release_host(bus->sdiodev->func[1]); + brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); /* Locate an appropriately-aligned portion of hdrbuf */ @@ -3719,6 +3767,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) return true; fail: + sdio_release_host(bus->sdiodev->func[1]); return false; } @@ -3726,6 +3775,8 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) { brcmf_dbg(TRACE, "Enter\n"); + sdio_claim_host(bus->sdiodev->func[1]); + /* Disable F2 to clear any intermediate frame state on the dongle */ brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, NULL); @@ -3736,6 +3787,8 @@ static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) /* Done with backplane-dependent accesses, can drop clock... */ brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + sdio_release_host(bus->sdiodev->func[1]); + /* ...and initialize clock/power states */ bus->clkstate = CLK_SDONLY; bus->idletime = BRCMF_IDLE_INTERVAL; @@ -3791,8 +3844,10 @@ static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) brcmf_dbg(TRACE, "Enter\n"); if (bus->ci) { + sdio_claim_host(bus->sdiodev->func[1]); brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); + sdio_release_host(bus->sdiodev->func[1]); brcmf_sdio_chip_detach(&bus->ci); if (bus->vars && bus->varsz) kfree(bus->vars); @@ -3812,7 +3867,8 @@ static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) brcmf_sdio_intr_unregister(bus->sdiodev); cancel_work_sync(&bus->datawork); - destroy_workqueue(bus->brcmf_wq); + if (bus->brcmf_wq) + destroy_workqueue(bus->brcmf_wq); if (bus->sdiodev->bus_if->drvr) { brcmf_detach(bus->sdiodev->dev); @@ -3854,31 +3910,29 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) bus->txminmax = BRCMF_TXMINMAX; bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1; + INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); + bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); + if (bus->brcmf_wq == NULL) { + brcmf_dbg(ERROR, "insufficient memory to create txworkqueue\n"); + goto fail; + } + /* attempt to attach to the dongle */ if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_attach failed\n"); goto fail; } + spin_lock_init(&bus->rxctl_lock); spin_lock_init(&bus->txqlock); init_waitqueue_head(&bus->ctrl_wait); init_waitqueue_head(&bus->dcmd_resp_wait); - bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); - if (bus->brcmf_wq == NULL) { - brcmf_dbg(ERROR, "insufficient memory to create txworkqueue\n"); - goto fail; - } - INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); - /* Set up the watchdog timer */ init_timer(&bus->timer); bus->timer.data = (unsigned long)bus; bus->timer.function = brcmf_sdbrcm_watchdog; - /* Initialize thread based operation and lock */ - sema_init(&bus->sdsem, 1); - /* Initialize watchdog thread */ init_completion(&bus->watchdog_wait); bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, @@ -3941,10 +3995,8 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) /* if firmware path present try to download and bring up bus */ ret = brcmf_bus_start(bus->sdiodev->dev); if (ret != 0) { - if (ret == -ENOLINK) { - brcmf_dbg(ERROR, "dongle is not responding\n"); - goto fail; - } + brcmf_dbg(ERROR, "dongle is not responding\n"); + goto fail; } return bus; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c new file mode 100644 index 00000000000..fa8fc443341 --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.c @@ -0,0 +1,509 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include <linux/netdevice.h> + +#include "brcmu_wifi.h" +#include "brcmu_utils.h" + +#include "dhd.h" +#include "dhd_dbg.h" +#include "fweh.h" +#include "fwil.h" + +/** + * struct brcm_ethhdr - broadcom specific ether header. + * + * @subtype: subtype for this packet. + * @length: TODO: length of appended data. + * @version: version indication. + * @oui: OUI of this packet. + * @usr_subtype: subtype for this OUI. + */ +struct brcm_ethhdr { + __be16 subtype; + __be16 length; + u8 version; + u8 oui[3]; + __be16 usr_subtype; +} __packed; + +struct brcmf_event_msg_be { + __be16 version; + __be16 flags; + __be32 event_type; + __be32 status; + __be32 reason; + __be32 auth_type; + __be32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +} __packed; + +/** + * struct brcmf_event - contents of broadcom event packet. + * + * @eth: standard ether header. + * @hdr: broadcom specific ether header. + * @msg: common part of the actual event message. + */ +struct brcmf_event { + struct ethhdr eth; + struct brcm_ethhdr hdr; + struct brcmf_event_msg_be msg; +} __packed; + +/** + * struct brcmf_fweh_queue_item - event item on event queue. + * + * @q: list element for queuing. + * @code: event code. + * @ifidx: interface index related to this event. + * @ifaddr: ethernet address for interface. + * @emsg: common parameters of the firmware event message. + * @data: event specific data part of the firmware event. + */ +struct brcmf_fweh_queue_item { + struct list_head q; + enum brcmf_fweh_event_code code; + u8 ifidx; + u8 ifaddr[ETH_ALEN]; + struct brcmf_event_msg_be emsg; + u8 data[0]; +}; + +/** + * struct brcmf_fweh_event_name - code, name mapping entry. + */ +struct brcmf_fweh_event_name { + enum brcmf_fweh_event_code code; + const char *name; +}; + +#ifdef DEBUG +/* array for mapping code to event name */ +static struct brcmf_fweh_event_name fweh_event_names[] = { + { BRCMF_E_SET_SSID, "SET_SSID" }, + { BRCMF_E_JOIN, "JOIN" }, + { BRCMF_E_START, "START" }, + { BRCMF_E_AUTH, "AUTH" }, + { BRCMF_E_AUTH_IND, "AUTH_IND" }, + { BRCMF_E_DEAUTH, "DEAUTH" }, + { BRCMF_E_DEAUTH_IND, "DEAUTH_IND" }, + { BRCMF_E_ASSOC, "ASSOC" }, + { BRCMF_E_ASSOC_IND, "ASSOC_IND" }, + { BRCMF_E_REASSOC, "REASSOC" }, + { BRCMF_E_REASSOC_IND, "REASSOC_IND" }, + { BRCMF_E_DISASSOC, "DISASSOC" }, + { BRCMF_E_DISASSOC_IND, "DISASSOC_IND" }, + { BRCMF_E_QUIET_START, "START_QUIET" }, + { BRCMF_E_QUIET_END, "END_QUIET" }, + { BRCMF_E_BEACON_RX, "BEACON_RX" }, + { BRCMF_E_LINK, "LINK" }, + { BRCMF_E_MIC_ERROR, "MIC_ERROR" }, + { BRCMF_E_NDIS_LINK, "NDIS_LINK" }, + { BRCMF_E_ROAM, "ROAM" }, + { BRCMF_E_TXFAIL, "TXFAIL" }, + { BRCMF_E_PMKID_CACHE, "PMKID_CACHE" }, + { BRCMF_E_RETROGRADE_TSF, "RETROGRADE_TSF" }, + { BRCMF_E_PRUNE, "PRUNE" }, + { BRCMF_E_AUTOAUTH, "AUTOAUTH" }, + { BRCMF_E_EAPOL_MSG, "EAPOL_MSG" }, + { BRCMF_E_SCAN_COMPLETE, "SCAN_COMPLETE" }, + { BRCMF_E_ADDTS_IND, "ADDTS_IND" }, + { BRCMF_E_DELTS_IND, "DELTS_IND" }, + { BRCMF_E_BCNSENT_IND, "BCNSENT_IND" }, + { BRCMF_E_BCNRX_MSG, "BCNRX_MSG" }, + { BRCMF_E_BCNLOST_MSG, "BCNLOST_MSG" }, + { BRCMF_E_ROAM_PREP, "ROAM_PREP" }, + { BRCMF_E_PFN_NET_FOUND, "PNO_NET_FOUND" }, + { BRCMF_E_PFN_NET_LOST, "PNO_NET_LOST" }, + { BRCMF_E_RESET_COMPLETE, "RESET_COMPLETE" }, + { BRCMF_E_JOIN_START, "JOIN_START" }, + { BRCMF_E_ROAM_START, "ROAM_START" }, + { BRCMF_E_ASSOC_START, "ASSOC_START" }, + { BRCMF_E_IBSS_ASSOC, "IBSS_ASSOC" }, + { BRCMF_E_RADIO, "RADIO" }, + { BRCMF_E_PSM_WATCHDOG, "PSM_WATCHDOG" }, + { BRCMF_E_PROBREQ_MSG, "PROBREQ_MSG" }, + { BRCMF_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND" }, + { BRCMF_E_PSK_SUP, "PSK_SUP" }, + { BRCMF_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED" }, + { BRCMF_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME" }, + { BRCMF_E_ICV_ERROR, "ICV_ERROR" }, + { BRCMF_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR" }, + { BRCMF_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR" }, + { BRCMF_E_TRACE, "TRACE" }, + { BRCMF_E_IF, "IF" }, + { BRCMF_E_RSSI, "RSSI" }, + { BRCMF_E_PFN_SCAN_COMPLETE, "PFN_SCAN_COMPLETE" }, + { BRCMF_E_EXTLOG_MSG, "EXTLOG_MSG" }, + { BRCMF_E_ACTION_FRAME, "ACTION_FRAME" }, + { BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION_FRAME_COMPLETE" }, + { BRCMF_E_PRE_ASSOC_IND, "PRE_ASSOC_IND" }, + { BRCMF_E_PRE_REASSOC_IND, "PRE_REASSOC_IND" }, + { BRCMF_E_CHANNEL_ADOPTED, "CHANNEL_ADOPTED" }, + { BRCMF_E_AP_STARTED, "AP_STARTED" }, + { BRCMF_E_DFS_AP_STOP, "DFS_AP_STOP" }, + { BRCMF_E_DFS_AP_RESUME, "DFS_AP_RESUME" }, + { BRCMF_E_ESCAN_RESULT, "ESCAN_RESULT" }, + { BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, "ACTION_FRM_OFF_CHAN_CMPLT" }, + { BRCMF_E_DCS_REQUEST, "DCS_REQUEST" }, + { BRCMF_E_FIFO_CREDIT_MAP, "FIFO_CREDIT_MAP"} +}; + +/** + * brcmf_fweh_event_name() - returns name for given event code. + * + * @code: code to lookup. + */ +static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +{ + int i; + for (i = 0; i < ARRAY_SIZE(fweh_event_names); i++) { + if (fweh_event_names[i].code == code) + return fweh_event_names[i].name; + } + return "unknown"; +} +#else +static const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) +{ + return "nodebug"; +} +#endif + +/** + * brcmf_fweh_queue_event() - create and queue event. + * + * @fweh: firmware event handling info. + * @event: event queue entry. + */ +static void brcmf_fweh_queue_event(struct brcmf_fweh_info *fweh, + struct brcmf_fweh_queue_item *event) +{ + ulong flags; + + spin_lock_irqsave(&fweh->evt_q_lock, flags); + list_add_tail(&event->q, &fweh->event_q); + spin_unlock_irqrestore(&fweh->evt_q_lock, flags); + schedule_work(&fweh->event_work); +} + +static int brcmf_fweh_call_event_handler(struct brcmf_if *ifp, + enum brcmf_fweh_event_code code, + struct brcmf_event_msg *emsg, + void *data) +{ + struct brcmf_fweh_info *fweh; + int err = -EINVAL; + + if (ifp) { + fweh = &ifp->drvr->fweh; + + /* handle the event if valid interface and handler */ + if (ifp->ndev && fweh->evt_handler[code]) + err = fweh->evt_handler[code](ifp, emsg, data); + else + brcmf_dbg(ERROR, "unhandled event %d ignored\n", code); + } else { + brcmf_dbg(ERROR, "no interface object\n"); + } + return err; +} + +/** + * brcmf_fweh_handle_if_event() - handle IF event. + * + * @drvr: driver information object. + * @item: queue entry. + * @ifpp: interface object (may change upon ADD action). + */ +static void brcmf_fweh_handle_if_event(struct brcmf_pub *drvr, + struct brcmf_event_msg *emsg, + void *data) +{ + struct brcmf_if_event *ifevent = data; + struct brcmf_if *ifp; + int err = 0; + + brcmf_dbg(EVENT, "action: %u idx: %u bsscfg: %u flags: %u\n", + ifevent->action, ifevent->ifidx, + ifevent->bssidx, ifevent->flags); + + if (ifevent->ifidx >= BRCMF_MAX_IFS) { + brcmf_dbg(ERROR, "invalid interface index: %u\n", + ifevent->ifidx); + return; + } + + ifp = drvr->iflist[ifevent->ifidx]; + + if (ifevent->action == BRCMF_E_IF_ADD) { + brcmf_dbg(EVENT, "adding %s (%pM)\n", emsg->ifname, + emsg->addr); + ifp = brcmf_add_if(drvr, ifevent->ifidx, ifevent->bssidx, + emsg->ifname, emsg->addr); + if (IS_ERR(ifp)) + return; + + if (!drvr->fweh.evt_handler[BRCMF_E_IF]) + err = brcmf_net_attach(ifp); + } + + err = brcmf_fweh_call_event_handler(ifp, emsg->event_code, emsg, data); + + if (ifevent->action == BRCMF_E_IF_DEL) + brcmf_del_if(drvr, ifevent->ifidx); +} + +/** + * brcmf_fweh_dequeue_event() - get event from the queue. + * + * @fweh: firmware event handling info. + */ +static struct brcmf_fweh_queue_item * +brcmf_fweh_dequeue_event(struct brcmf_fweh_info *fweh) +{ + struct brcmf_fweh_queue_item *event = NULL; + ulong flags; + + spin_lock_irqsave(&fweh->evt_q_lock, flags); + if (!list_empty(&fweh->event_q)) { + event = list_first_entry(&fweh->event_q, + struct brcmf_fweh_queue_item, q); + list_del(&event->q); + } + spin_unlock_irqrestore(&fweh->evt_q_lock, flags); + + return event; +} + +/** + * brcmf_fweh_event_worker() - firmware event worker. + * + * @work: worker object. + */ +static void brcmf_fweh_event_worker(struct work_struct *work) +{ + struct brcmf_pub *drvr; + struct brcmf_if *ifp; + struct brcmf_fweh_info *fweh; + struct brcmf_fweh_queue_item *event; + int err = 0; + struct brcmf_event_msg_be *emsg_be; + struct brcmf_event_msg emsg; + + fweh = container_of(work, struct brcmf_fweh_info, event_work); + drvr = container_of(fweh, struct brcmf_pub, fweh); + + while ((event = brcmf_fweh_dequeue_event(fweh))) { + ifp = drvr->iflist[event->ifidx]; + + brcmf_dbg(EVENT, "event %s (%u) ifidx %u bsscfg %u addr %pM:\n", + brcmf_fweh_event_name(event->code), event->code, + event->emsg.ifidx, event->emsg.bsscfgidx, + event->emsg.addr); + + /* convert event message */ + emsg_be = &event->emsg; + emsg.version = be16_to_cpu(emsg_be->version); + emsg.flags = be16_to_cpu(emsg_be->flags); + emsg.event_code = event->code; + emsg.status = be32_to_cpu(emsg_be->status); + emsg.reason = be32_to_cpu(emsg_be->reason); + emsg.auth_type = be32_to_cpu(emsg_be->auth_type); + emsg.datalen = be32_to_cpu(emsg_be->datalen); + memcpy(emsg.addr, emsg_be->addr, ETH_ALEN); + memcpy(emsg.ifname, emsg_be->ifname, sizeof(emsg.ifname)); + emsg.ifidx = emsg_be->ifidx; + emsg.bsscfgidx = emsg_be->bsscfgidx; + + brcmf_dbg(EVENT, " version %u flags %u status %u reason %u\n", + emsg.version, emsg.flags, emsg.status, emsg.reason); + brcmf_dbg_hex_dump(BRCMF_EVENT_ON(), event->data, + min_t(u32, emsg.datalen, 64), + "appended:"); + + /* special handling of interface event */ + if (event->code == BRCMF_E_IF) { + brcmf_fweh_handle_if_event(drvr, &emsg, event->data); + goto event_free; + } + + err = brcmf_fweh_call_event_handler(ifp, event->code, &emsg, + event->data); + if (err) { + brcmf_dbg(ERROR, "event handler failed (%d)\n", + event->code); + err = 0; + } +event_free: + kfree(event); + } +} + +/** + * brcmf_fweh_attach() - initialize firmware event handling. + * + * @drvr: driver information object. + */ +void brcmf_fweh_attach(struct brcmf_pub *drvr) +{ + struct brcmf_fweh_info *fweh = &drvr->fweh; + INIT_WORK(&fweh->event_work, brcmf_fweh_event_worker); + spin_lock_init(&fweh->evt_q_lock); + INIT_LIST_HEAD(&fweh->event_q); +} + +/** + * brcmf_fweh_detach() - cleanup firmware event handling. + * + * @drvr: driver information object. + */ +void brcmf_fweh_detach(struct brcmf_pub *drvr) +{ + struct brcmf_fweh_info *fweh = &drvr->fweh; + struct brcmf_if *ifp = drvr->iflist[0]; + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + + if (ifp) { + /* clear all events */ + memset(eventmask, 0, BRCMF_EVENTING_MASK_LEN); + (void)brcmf_fil_iovar_data_set(ifp, "event_msgs", + eventmask, + BRCMF_EVENTING_MASK_LEN); + } + /* cancel the worker */ + cancel_work_sync(&fweh->event_work); + WARN_ON(!list_empty(&fweh->event_q)); + memset(fweh->evt_handler, 0, sizeof(fweh->evt_handler)); +} + +/** + * brcmf_fweh_register() - register handler for given event code. + * + * @drvr: driver information object. + * @code: event code. + * @handler: handler for the given event code. + */ +int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, + brcmf_fweh_handler_t handler) +{ + if (drvr->fweh.evt_handler[code]) { + brcmf_dbg(ERROR, "event code %d already registered\n", code); + return -ENOSPC; + } + drvr->fweh.evt_handler[code] = handler; + brcmf_dbg(TRACE, "event handler registered for %s\n", + brcmf_fweh_event_name(code)); + return 0; +} + +/** + * brcmf_fweh_unregister() - remove handler for given code. + * + * @drvr: driver information object. + * @code: event code. + */ +void brcmf_fweh_unregister(struct brcmf_pub *drvr, + enum brcmf_fweh_event_code code) +{ + brcmf_dbg(TRACE, "event handler cleared for %s\n", + brcmf_fweh_event_name(code)); + drvr->fweh.evt_handler[code] = NULL; +} + +/** + * brcmf_fweh_activate_events() - enables firmware events registered. + * + * @ifp: primary interface object. + */ +int brcmf_fweh_activate_events(struct brcmf_if *ifp) +{ + int i, err; + s8 eventmask[BRCMF_EVENTING_MASK_LEN]; + + for (i = 0; i < BRCMF_E_LAST; i++) { + if (ifp->drvr->fweh.evt_handler[i]) { + brcmf_dbg(EVENT, "enable event %s\n", + brcmf_fweh_event_name(i)); + setbit(eventmask, i); + } + } + + /* want to handle IF event as well */ + brcmf_dbg(EVENT, "enable event IF\n"); + setbit(eventmask, BRCMF_E_IF); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", + eventmask, BRCMF_EVENTING_MASK_LEN); + if (err) + brcmf_dbg(ERROR, "Set event_msgs error (%d)\n", err); + + return err; +} + +/** + * brcmf_fweh_process_event() - process skb as firmware event. + * + * @drvr: driver information object. + * @event_packet: event packet to process. + * @ifidx: index of the firmware interface (may change). + * + * If the packet buffer contains a firmware event message it will + * dispatch the event to a registered handler (using worker). + */ +void brcmf_fweh_process_event(struct brcmf_pub *drvr, + struct brcmf_event *event_packet, u8 *ifidx) +{ + enum brcmf_fweh_event_code code; + struct brcmf_fweh_info *fweh = &drvr->fweh; + struct brcmf_fweh_queue_item *event; + gfp_t alloc_flag = GFP_KERNEL; + void *data; + u32 datalen; + + /* get event info */ + code = get_unaligned_be32(&event_packet->msg.event_type); + datalen = get_unaligned_be32(&event_packet->msg.datalen); + *ifidx = event_packet->msg.ifidx; + data = &event_packet[1]; + + if (code >= BRCMF_E_LAST) + return; + + if (code != BRCMF_E_IF && !fweh->evt_handler[code]) + return; + + if (in_interrupt()) + alloc_flag = GFP_ATOMIC; + + event = kzalloc(sizeof(*event) + datalen, alloc_flag); + if (!event) + return; + + event->code = code; + event->ifidx = *ifidx; + + /* use memcpy to get aligned event message */ + memcpy(&event->emsg, &event_packet->msg, sizeof(event->emsg)); + memcpy(event->data, data, datalen); + memcpy(event->ifaddr, event_packet->eth.h_dest, ETH_ALEN); + + brcmf_fweh_queue_event(fweh, event); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h new file mode 100644 index 00000000000..b39246a630d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/fweh.h @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2012 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + + +#ifndef FWEH_H_ +#define FWEH_H_ + +#include <asm/unaligned.h> +#include <linux/skbuff.h> +#include <linux/if_ether.h> +#include <linux/if.h> + +/* formward declarations */ +struct brcmf_pub; +struct brcmf_if; +struct brcmf_cfg80211_info; +struct brcmf_event; + +/* firmware event codes sent by the dongle */ +enum brcmf_fweh_event_code { + BRCMF_E_SET_SSID = 0, + BRCMF_E_JOIN = 1, + BRCMF_E_START = 2, + BRCMF_E_AUTH = 3, + BRCMF_E_AUTH_IND = 4, + BRCMF_E_DEAUTH = 5, + BRCMF_E_DEAUTH_IND = 6, + BRCMF_E_ASSOC = 7, + BRCMF_E_ASSOC_IND = 8, + BRCMF_E_REASSOC = 9, + BRCMF_E_REASSOC_IND = 10, + BRCMF_E_DISASSOC = 11, + BRCMF_E_DISASSOC_IND = 12, + BRCMF_E_QUIET_START = 13, + BRCMF_E_QUIET_END = 14, + BRCMF_E_BEACON_RX = 15, + BRCMF_E_LINK = 16, + BRCMF_E_MIC_ERROR = 17, + BRCMF_E_NDIS_LINK = 18, + BRCMF_E_ROAM = 19, + BRCMF_E_TXFAIL = 20, + BRCMF_E_PMKID_CACHE = 21, + BRCMF_E_RETROGRADE_TSF = 22, + BRCMF_E_PRUNE = 23, + BRCMF_E_AUTOAUTH = 24, + BRCMF_E_EAPOL_MSG = 25, + BRCMF_E_SCAN_COMPLETE = 26, + BRCMF_E_ADDTS_IND = 27, + BRCMF_E_DELTS_IND = 28, + BRCMF_E_BCNSENT_IND = 29, + BRCMF_E_BCNRX_MSG = 30, + BRCMF_E_BCNLOST_MSG = 31, + BRCMF_E_ROAM_PREP = 32, + BRCMF_E_PFN_NET_FOUND = 33, + BRCMF_E_PFN_NET_LOST = 34, + BRCMF_E_RESET_COMPLETE = 35, + BRCMF_E_JOIN_START = 36, + BRCMF_E_ROAM_START = 37, + BRCMF_E_ASSOC_START = 38, + BRCMF_E_IBSS_ASSOC = 39, + BRCMF_E_RADIO = 40, + BRCMF_E_PSM_WATCHDOG = 41, + BRCMF_E_PROBREQ_MSG = 44, + BRCMF_E_SCAN_CONFIRM_IND = 45, + BRCMF_E_PSK_SUP = 46, + BRCMF_E_COUNTRY_CODE_CHANGED = 47, + BRCMF_E_EXCEEDED_MEDIUM_TIME = 48, + BRCMF_E_ICV_ERROR = 49, + BRCMF_E_UNICAST_DECODE_ERROR = 50, + BRCMF_E_MULTICAST_DECODE_ERROR = 51, + BRCMF_E_TRACE = 52, + BRCMF_E_IF = 54, + BRCMF_E_RSSI = 56, + BRCMF_E_PFN_SCAN_COMPLETE = 57, + BRCMF_E_EXTLOG_MSG = 58, + BRCMF_E_ACTION_FRAME = 59, + BRCMF_E_ACTION_FRAME_COMPLETE = 60, + BRCMF_E_PRE_ASSOC_IND = 61, + BRCMF_E_PRE_REASSOC_IND = 62, + BRCMF_E_CHANNEL_ADOPTED = 63, + BRCMF_E_AP_STARTED = 64, + BRCMF_E_DFS_AP_STOP = 65, + BRCMF_E_DFS_AP_RESUME = 66, + BRCMF_E_ESCAN_RESULT = 69, + BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE = 70, + BRCMF_E_DCS_REQUEST = 73, + BRCMF_E_FIFO_CREDIT_MAP = 74, + BRCMF_E_LAST +}; + +/* flags field values in struct brcmf_event_msg */ +#define BRCMF_EVENT_MSG_LINK 0x01 +#define BRCMF_EVENT_MSG_FLUSHTXQ 0x02 +#define BRCMF_EVENT_MSG_GROUP 0x04 + +/** + * definitions for event packet validation. + */ +#define BRCMF_EVENT_OUI_OFFSET 19 +#define BRCM_OUI "\x00\x10\x18" +#define DOT11_OUI_LEN 3 +#define BCMILCP_BCM_SUBTYPE_EVENT 1 + + +/** + * struct brcmf_event_msg - firmware event message. + * + * @version: version information. + * @flags: event flags. + * @event_code: firmware event code. + * @status: status information. + * @reason: reason code. + * @auth_type: authentication type. + * @datalen: lenght of event data buffer. + * @addr: ether address. + * @ifname: interface name. + * @ifidx: interface index. + * @bsscfgidx: bsscfg index. + */ +struct brcmf_event_msg { + u16 version; + u16 flags; + u32 event_code; + u32 status; + u32 reason; + s32 auth_type; + u32 datalen; + u8 addr[ETH_ALEN]; + char ifname[IFNAMSIZ]; + u8 ifidx; + u8 bsscfgidx; +}; + +typedef int (*brcmf_fweh_handler_t)(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); + +/** + * struct brcmf_fweh_info - firmware event handling information. + * + * @event_work: event worker. + * @evt_q_lock: lock for event queue protection. + * @event_q: event queue. + * @evt_handler: registered event handlers. + */ +struct brcmf_fweh_info { + struct work_struct event_work; + struct spinlock evt_q_lock; + struct list_head event_q; + int (*evt_handler[BRCMF_E_LAST])(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data); +}; + +void brcmf_fweh_attach(struct brcmf_pub *drvr); +void brcmf_fweh_detach(struct brcmf_pub *drvr); +int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, + int (*handler)(struct brcmf_if *ifp, + const struct brcmf_event_msg *evtmsg, + void *data)); +void brcmf_fweh_unregister(struct brcmf_pub *drvr, + enum brcmf_fweh_event_code code); +int brcmf_fweh_activate_events(struct brcmf_if *ifp); +void brcmf_fweh_process_event(struct brcmf_pub *drvr, + struct brcmf_event *event_packet, u8 *ifidx); + +static inline void brcmf_fweh_process_skb(struct brcmf_pub *drvr, + struct sk_buff *skb, u8 *ifidx) +{ + struct brcmf_event *event_packet; + u8 *data; + u16 usr_stype; + + /* only process events when protocol matches */ + if (skb->protocol != cpu_to_be16(ETH_P_LINK_CTL)) + return; + + /* check for BRCM oui match */ + event_packet = (struct brcmf_event *)skb_mac_header(skb); + data = (u8 *)event_packet; + data += BRCMF_EVENT_OUI_OFFSET; + if (memcmp(BRCM_OUI, data, DOT11_OUI_LEN)) + return; + + /* final match on usr_subtype */ + data += DOT11_OUI_LEN; + usr_stype = get_unaligned_be16(data); + if (usr_stype != BCMILCP_BCM_SUBTYPE_EVENT) + return; + + brcmf_fweh_process_event(drvr, event_packet, ifidx); +} + +#endif /* FWEH_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c index 4b272c3d237..51a14505197 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwil.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwil.c @@ -20,7 +20,6 @@ #include <linux/kernel.h> #include <linux/netdevice.h> -#include <defs.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> #include "dhd.h" @@ -29,13 +28,16 @@ #include "fwil.h" +#define MAX_HEX_DUMP_LEN 64 + + static s32 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) { struct brcmf_pub *drvr = ifp->drvr; s32 err; - if (drvr->bus_if->state == BRCMF_BUS_DOWN) { + if (drvr->bus_if->state != BRCMF_BUS_DATA) { brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n"); return -EIO; } @@ -64,7 +66,8 @@ brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) mutex_lock(&ifp->drvr->proto_block); brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); mutex_unlock(&ifp->drvr->proto_block); @@ -81,7 +84,8 @@ brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); mutex_unlock(&ifp->drvr->proto_block); @@ -147,7 +151,8 @@ brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, void *data, mutex_lock(&drvr->proto_block); brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -186,7 +191,8 @@ brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, } brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); mutex_unlock(&drvr->proto_block); return err; @@ -268,7 +274,8 @@ brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); @@ -294,7 +301,7 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, mutex_lock(&drvr->proto_block); - buflen = brcmf_create_bsscfg(ifp->bssidx, name, NULL, len, + buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, drvr->proto_buf, sizeof(drvr->proto_buf)); if (buflen) { err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, @@ -306,7 +313,8 @@ brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, brcmf_dbg(ERROR, "Creating bsscfg failed\n"); } brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); - brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); + brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, + min_t(uint, len, MAX_HEX_DUMP_LEN), "data"); mutex_unlock(&drvr->proto_block); return err; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 484a6e4f23a..39a5baa92f2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -14,24 +14,12 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/kthread.h> -#include <linux/slab.h> -#include <linux/skbuff.h> -#include <linux/netdevice.h> -#include <linux/spinlock.h> -#include <linux/ethtool.h> -#include <linux/fcntl.h> -#include <linux/fs.h> -#include <linux/uaccess.h> #include <linux/firmware.h> #include <linux/usb.h> #include <linux/vmalloc.h> -#include <net/cfg80211.h> -#include <defs.h> #include <brcmu_utils.h> #include <brcmu_wifi.h> #include <dhd_bus.h> @@ -42,13 +30,11 @@ #define IOCTL_RESP_TIMEOUT 2000 -#define BRCMF_USB_DLIMAGE_SPINWAIT 100 /* in unit of ms */ -#define BRCMF_USB_DLIMAGE_LIMIT 500 /* spinwait limit (ms) */ +#define BRCMF_USB_RESET_GETVER_SPINWAIT 100 /* in unit of ms */ +#define BRCMF_USB_RESET_GETVER_LOOP_CNT 10 #define BRCMF_POSTBOOT_ID 0xA123 /* ID to detect if dongle has boot up */ -#define BRCMF_USB_RESETCFG_SPINWAIT 1 /* wait after resetcfg (ms) */ - #define BRCMF_USB_NRXQ 50 #define BRCMF_USB_NTXQ 50 @@ -69,16 +55,6 @@ #define BRCMF_USB_43236_FW_NAME "brcm/brcmfmac43236b.bin" #define BRCMF_USB_43242_FW_NAME "brcm/brcmfmac43242a.bin" -enum usbdev_suspend_state { - USBOS_SUSPEND_STATE_DEVICE_ACTIVE = 0, /* Device is busy, won't allow - suspend */ - USBOS_SUSPEND_STATE_SUSPEND_PENDING, /* Device is idle, can be - * suspended. Wating PM to - * suspend the device - */ - USBOS_SUSPEND_STATE_SUSPENDED /* Device suspended */ -}; - struct brcmf_usb_image { struct list_head list; s8 *fwname; @@ -99,10 +75,8 @@ struct brcmf_usbdev_info { struct list_head rx_postq; struct list_head tx_freeq; struct list_head tx_postq; - enum usbdev_suspend_state suspend_state; uint rx_pipe, tx_pipe, intr_pipe, rx_pipe2; - bool activity; int rx_low_watermark; int tx_low_watermark; int tx_high_watermark; @@ -170,6 +144,7 @@ static void brcmf_usb_ioctl_resp_wake(struct brcmf_usbdev_info *devinfo) static void brcmf_usb_ctl_complete(struct brcmf_usbdev_info *devinfo, int type, int status) { + brcmf_dbg(USB, "Enter, status=%d\n", status); if (unlikely(devinfo == NULL)) return; @@ -197,6 +172,7 @@ brcmf_usb_ctlread_complete(struct urb *urb) struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; + brcmf_dbg(USB, "Enter\n"); devinfo->ctl_urb_actual_length = urb->actual_length; brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_READ, urb->status); @@ -208,33 +184,22 @@ brcmf_usb_ctlwrite_complete(struct urb *urb) struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; + brcmf_dbg(USB, "Enter\n"); brcmf_usb_ctl_complete(devinfo, BRCMF_USB_CBCTL_WRITE, urb->status); } -static int brcmf_usb_pnp(struct brcmf_usbdev_info *devinfo, uint state) -{ - return 0; -} - static int brcmf_usb_send_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) { int ret; u16 size; + brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL || buf == NULL || len == 0 || devinfo->ctl_urb == NULL) return -EINVAL; - /* If the USB/HSIC bus in sleep state, wake it up */ - if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED) - if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) { - brcmf_dbg(ERROR, "Could not Resume the bus!\n"); - return -EIO; - } - - devinfo->activity = true; size = len; devinfo->ctl_write.wLength = cpu_to_le16p(&size); devinfo->ctl_urb->transfer_buffer_length = size; @@ -262,6 +227,7 @@ brcmf_usb_recv_ctl(struct brcmf_usbdev_info *devinfo, u8 *buf, int len) int ret; u16 size; + brcmf_dbg(USB, "Enter\n"); if ((devinfo == NULL) || (buf == NULL) || (len == 0) || (devinfo->ctl_urb == NULL)) return -EINVAL; @@ -295,10 +261,9 @@ static int brcmf_usb_tx_ctlpkt(struct device *dev, u8 *buf, u32 len) int timeout = 0; struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { - /* TODO: handle suspend/resume */ + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) return -EIO; - } if (test_and_set_bit(0, &devinfo->ctl_op)) return -EIO; @@ -325,10 +290,10 @@ static int brcmf_usb_rx_ctlpkt(struct device *dev, u8 *buf, u32 len) int timeout = 0; struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); - if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { - /* TODO: handle suspend/resume */ + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) return -EIO; - } + if (test_and_set_bit(0, &devinfo->ctl_op)) return -EIO; @@ -453,6 +418,8 @@ static void brcmf_usb_tx_complete(struct urb *urb) struct brcmf_usbreq *req = (struct brcmf_usbreq *)urb->context; struct brcmf_usbdev_info *devinfo = req->devinfo; + brcmf_dbg(USB, "Enter, urb->status=%d, skb=%p\n", urb->status, + req->skb); brcmf_usb_del_fromq(devinfo, req); if (urb->status == 0) devinfo->bus_pub.bus->dstats.tx_packets++; @@ -478,6 +445,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) struct sk_buff *skb; int ifidx = 0; + brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); brcmf_usb_del_fromq(devinfo, req); skb = req->skb; req->skb = NULL; @@ -491,7 +459,7 @@ static void brcmf_usb_rx_complete(struct urb *urb) return; } - if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) { + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { skb_put(skb, urb->actual_length); if (brcmf_proto_hdrpull(devinfo->dev, &ifidx, skb) != 0) { brcmf_dbg(ERROR, "rx protocol error\n"); @@ -544,8 +512,8 @@ static void brcmf_usb_rx_fill_all(struct brcmf_usbdev_info *devinfo) { struct brcmf_usbreq *req; - if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { - brcmf_dbg(ERROR, "bus is not up\n"); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) { + brcmf_dbg(ERROR, "bus is not up=%d\n", devinfo->bus_pub.state); return; } while ((req = brcmf_usb_deq(devinfo, &devinfo->rx_freeq, NULL)) != NULL) @@ -558,29 +526,24 @@ brcmf_usb_state_change(struct brcmf_usbdev_info *devinfo, int state) struct brcmf_bus *bcmf_bus = devinfo->bus_pub.bus; int old_state; + brcmf_dbg(USB, "Enter, current state=%d, new state=%d\n", + devinfo->bus_pub.state, state); if (devinfo->bus_pub.state == state) return; old_state = devinfo->bus_pub.state; - brcmf_dbg(TRACE, "dbus state change from %d to to %d\n", - old_state, state); - - /* Don't update state if it's PnP firmware re-download */ - if (state != BCMFMAC_USB_STATE_PNP_FWDL) /* TODO */ - devinfo->bus_pub.state = state; - - if ((old_state == BCMFMAC_USB_STATE_SLEEP) - && (state == BCMFMAC_USB_STATE_UP)) { - brcmf_usb_rx_fill_all(devinfo); - } + devinfo->bus_pub.state = state; /* update state of upper layer */ - if (state == BCMFMAC_USB_STATE_DOWN) { - brcmf_dbg(INFO, "DBUS is down\n"); + if (state == BRCMFMAC_USB_STATE_DOWN) { + brcmf_dbg(USB, "DBUS is down\n"); bcmf_bus->state = BRCMF_BUS_DOWN; + } else if (state == BRCMFMAC_USB_STATE_UP) { + brcmf_dbg(USB, "DBUS is up\n"); + bcmf_bus->state = BRCMF_BUS_DATA; } else { - brcmf_dbg(INFO, "DBUS current state=%d\n", state); + brcmf_dbg(USB, "DBUS current state=%d\n", state); } } @@ -589,30 +552,32 @@ brcmf_usb_intr_complete(struct urb *urb) { struct brcmf_usbdev_info *devinfo = (struct brcmf_usbdev_info *)urb->context; - bool killed; + int err; + + brcmf_dbg(USB, "Enter, urb->status=%d\n", urb->status); if (devinfo == NULL) return; if (unlikely(urb->status)) { - if (devinfo->suspend_state == - USBOS_SUSPEND_STATE_SUSPEND_PENDING) - killed = true; - - if ((urb->status == -ENOENT && (!killed)) - || urb->status == -ESHUTDOWN || - urb->status == -ENODEV) { - brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN); + if (urb->status == -ENOENT || + urb->status == -ESHUTDOWN || + urb->status == -ENODEV) { + brcmf_usb_state_change(devinfo, + BRCMFMAC_USB_STATE_DOWN); } } - if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) { + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) { brcmf_dbg(ERROR, "intr cb when DBUS down, ignoring\n"); return; } - if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) - usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) { + err = usb_submit_urb(devinfo->intr_urb, GFP_ATOMIC); + if (err) + brcmf_dbg(ERROR, "usb_submit_urb, err=%d\n", err); + } } static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) @@ -621,10 +586,9 @@ static int brcmf_usb_tx(struct device *dev, struct sk_buff *skb) struct brcmf_usbreq *req; int ret; - if (devinfo->bus_pub.state != BCMFMAC_USB_STATE_UP) { - /* TODO: handle suspend/resume */ + brcmf_dbg(USB, "Enter, skb=%p\n", skb); + if (devinfo->bus_pub.state != BRCMFMAC_USB_STATE_UP) return -EIO; - } req = brcmf_usb_deq(devinfo, &devinfo->tx_freeq, &devinfo->tx_freecount); @@ -664,25 +628,16 @@ static int brcmf_usb_up(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); u16 ifnum; + int ret; - if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_UP) + brcmf_dbg(USB, "Enter\n"); + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_UP) return 0; - /* If the USB/HSIC bus in sleep state, wake it up */ - if (devinfo->suspend_state == USBOS_SUSPEND_STATE_SUSPENDED) { - if (brcmf_usb_pnp(devinfo, BCMFMAC_USB_PNP_RESUME) != 0) { - brcmf_dbg(ERROR, "Could not Resume the bus!\n"); - return -EIO; - } - } - devinfo->activity = true; - /* Success, indicate devinfo is fully up */ - brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_UP); + brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_UP); if (devinfo->intr_urb) { - int ret; - usb_fill_int_urb(devinfo->intr_urb, devinfo->usbdev, devinfo->intr_pipe, &devinfo->intr, @@ -727,14 +682,14 @@ static void brcmf_usb_down(struct device *dev) { struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(dev); + brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL) return; - brcmf_dbg(TRACE, "enter\n"); - if (devinfo->bus_pub.state == BCMFMAC_USB_STATE_DOWN) + if (devinfo->bus_pub.state == BRCMFMAC_USB_STATE_DOWN) return; - brcmf_usb_state_change(devinfo, BCMFMAC_USB_STATE_DOWN); + brcmf_usb_state_change(devinfo, BRCMFMAC_USB_STATE_DOWN); if (devinfo->intr_urb) usb_kill_urb(devinfo->intr_urb); @@ -808,27 +763,25 @@ brcmf_usb_dlneeded(struct brcmf_usbdev_info *devinfo) struct bootrom_id_le id; u32 chipid, chiprev; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL) return false; /* Check if firmware downloaded already by querying runtime ID */ id.chip = cpu_to_le32(0xDEAD); - brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, - sizeof(struct bootrom_id_le)); + brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); chipid = le32_to_cpu(id.chip); chiprev = le32_to_cpu(id.chiprev); if ((chipid & 0x4300) == 0x4300) - brcmf_dbg(INFO, "chip %x rev 0x%x\n", chipid, chiprev); + brcmf_dbg(USB, "chip %x rev 0x%x\n", chipid, chiprev); else - brcmf_dbg(INFO, "chip %d rev 0x%x\n", chipid, chiprev); + brcmf_dbg(USB, "chip %d rev 0x%x\n", chipid, chiprev); if (chipid == BRCMF_POSTBOOT_ID) { - brcmf_dbg(INFO, "firmware already downloaded\n"); - brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, - sizeof(struct bootrom_id_le)); + brcmf_dbg(USB, "firmware already downloaded\n"); + brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); return false; } else { devinfo->bus_pub.devid = chipid; @@ -841,38 +794,29 @@ static int brcmf_usb_resetcfg(struct brcmf_usbdev_info *devinfo) { struct bootrom_id_le id; - u16 wait = 0, wait_time; + u32 loop_cnt; - brcmf_dbg(TRACE, "enter\n"); - - if (devinfo == NULL) - return -EINVAL; + brcmf_dbg(USB, "Enter\n"); - /* Give dongle chance to boot */ - wait_time = BRCMF_USB_DLIMAGE_SPINWAIT; - while (wait < BRCMF_USB_DLIMAGE_LIMIT) { - mdelay(wait_time); - wait += wait_time; + loop_cnt = 0; + do { + mdelay(BRCMF_USB_RESET_GETVER_SPINWAIT); + loop_cnt++; id.chip = cpu_to_le32(0xDEAD); /* Get the ID */ - brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, - sizeof(struct bootrom_id_le)); + brcmf_usb_dl_cmd(devinfo, DL_GETVER, &id, sizeof(id)); if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) break; - } + } while (loop_cnt < BRCMF_USB_RESET_GETVER_LOOP_CNT); if (id.chip == cpu_to_le32(BRCMF_POSTBOOT_ID)) { - brcmf_dbg(INFO, "download done %d ms postboot chip 0x%x/rev 0x%x\n", - wait, le32_to_cpu(id.chip), le32_to_cpu(id.chiprev)); - - brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, - sizeof(struct bootrom_id_le)); + brcmf_dbg(USB, "postboot chip 0x%x/rev 0x%x\n", + le32_to_cpu(id.chip), le32_to_cpu(id.chiprev)); - /* XXX this wait may not be necessary */ - mdelay(BRCMF_USB_RESETCFG_SPINWAIT); + brcmf_usb_dl_cmd(devinfo, DL_RESETCFG, &id, sizeof(id)); return 0; } else { brcmf_dbg(ERROR, "Cannot talk to Dongle. Firmware is not UP, %d ms\n", - wait); + BRCMF_USB_RESET_GETVER_SPINWAIT * loop_cnt); return -EINVAL; } } @@ -911,7 +855,8 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) struct rdl_state_le state; u32 rdlstate, rdlbytes; int err = 0; - brcmf_dbg(TRACE, "fw %p, len %d\n", fw, fwlen); + + brcmf_dbg(USB, "Enter, fw %p, len %d\n", fw, fwlen); bulkchunk = kmalloc(RDL_CHUNK, GFP_ATOMIC); if (bulkchunk == NULL) { @@ -986,7 +931,7 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) fail: kfree(bulkchunk); - brcmf_dbg(TRACE, "err=%d\n", err); + brcmf_dbg(USB, "Exit, err=%d\n", err); return err; } @@ -994,7 +939,7 @@ static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len) { int err; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL) return -EINVAL; @@ -1004,10 +949,10 @@ static int brcmf_usb_dlstart(struct brcmf_usbdev_info *devinfo, u8 *fw, int len) err = brcmf_usb_dl_writeimage(devinfo, fw, len); if (err == 0) - devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_DONE; + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_DONE; else - devinfo->bus_pub.state = BCMFMAC_USB_STATE_DL_PENDING; - brcmf_dbg(TRACE, "exit: err=%d\n", err); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DL_FAIL; + brcmf_dbg(USB, "Exit, err=%d\n", err); return err; } @@ -1016,7 +961,7 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) { struct rdl_state_le state; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); if (!devinfo) return -EINVAL; @@ -1039,7 +984,7 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) brcmf_dbg(ERROR, "Dongle not runnable\n"); return -EINVAL; } - brcmf_dbg(TRACE, "exit\n"); + brcmf_dbg(USB, "Exit\n"); return 0; } @@ -1066,7 +1011,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) int devid, chiprev; int err; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); if (devinfo == NULL) return -ENODEV; @@ -1094,7 +1039,7 @@ brcmf_usb_fw_download(struct brcmf_usbdev_info *devinfo) static void brcmf_usb_detach(struct brcmf_usbdev_info *devinfo) { - brcmf_dbg(TRACE, "devinfo %p\n", devinfo); + brcmf_dbg(USB, "Enter, devinfo %p\n", devinfo); /* free the URBS */ brcmf_usb_free_q(&devinfo->rx_freeq, false); @@ -1129,6 +1074,7 @@ static int check_file(const u8 *headers) struct trx_header_le *trx; int actual_len = -1; + brcmf_dbg(USB, "Enter\n"); /* Extract trx header */ trx = (struct trx_header_le *) headers; if (trx->magic != cpu_to_le32(TRX_MAGIC)) @@ -1150,6 +1096,7 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) struct brcmf_usb_image *fw_image; int err; + brcmf_dbg(USB, "Enter\n"); switch (devinfo->bus_pub.devid) { case 43143: fwname = BRCMF_USB_43143_FW_NAME; @@ -1166,7 +1113,7 @@ static int brcmf_usb_get_fw(struct brcmf_usbdev_info *devinfo) return -EINVAL; break; } - + brcmf_dbg(USB, "Loading FW %s\n", fwname); list_for_each_entry(fw_image, &fw_image_list, list) { if (fw_image->fwname == fwname) { devinfo->image = fw_image->image; @@ -1211,10 +1158,13 @@ static struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, int nrxq, int ntxq) { + brcmf_dbg(USB, "Enter\n"); + devinfo->bus_pub.nrxq = nrxq; devinfo->rx_low_watermark = nrxq / 2; devinfo->bus_pub.devinfo = devinfo; devinfo->bus_pub.ntxq = ntxq; + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_DOWN; /* flow control when too many tx urbs posted */ devinfo->tx_low_watermark = ntxq / 4; @@ -1263,7 +1213,7 @@ struct brcmf_usbdev *brcmf_usb_attach(struct brcmf_usbdev_info *devinfo, if (!brcmf_usb_dlneeded(devinfo)) return &devinfo->bus_pub; - brcmf_dbg(TRACE, "start fw downloading\n"); + brcmf_dbg(USB, "Start fw downloading\n"); if (brcmf_usb_get_fw(devinfo)) goto error; @@ -1278,14 +1228,14 @@ error: return NULL; } -static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, - const char *desc, u32 bustype, u32 hdrlen) +static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) { struct brcmf_bus *bus = NULL; struct brcmf_usbdev *bus_pub = NULL; int ret; struct device *dev = devinfo->dev; + brcmf_dbg(USB, "Enter\n"); bus_pub = brcmf_usb_attach(devinfo, BRCMF_USB_NRXQ, BRCMF_USB_NTXQ); if (!bus_pub) return -ENODEV; @@ -1302,14 +1252,13 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo, bus->brcmf_bus_stop = brcmf_usb_down; bus->brcmf_bus_txctl = brcmf_usb_tx_ctlpkt; bus->brcmf_bus_rxctl = brcmf_usb_rx_ctlpkt; - bus->type = bustype; bus->bus_priv.usb = bus_pub; dev_set_drvdata(dev, bus); /* Attach to the common driver interface */ - ret = brcmf_attach(hdrlen, dev); + ret = brcmf_attach(0, dev); if (ret) { - brcmf_dbg(ERROR, "dhd_attach failed\n"); + brcmf_dbg(ERROR, "brcmf_attach failed\n"); goto fail; } @@ -1333,7 +1282,7 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *devinfo) { if (!devinfo) return; - brcmf_dbg(TRACE, "enter: bus_pub %p\n", devinfo); + brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); brcmf_detach(devinfo->dev); kfree(devinfo->bus_pub.bus); @@ -1351,7 +1300,7 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) u8 endpoint_num; struct brcmf_usbdev_info *devinfo; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); devinfo = kzalloc(sizeof(*devinfo), GFP_ATOMIC); if (devinfo == NULL) @@ -1452,11 +1401,11 @@ brcmf_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) devinfo->interval = IFEPDESC(usb, CONTROL_IF, 0).bInterval; if (usb->speed == USB_SPEED_HIGH) - brcmf_dbg(INFO, "Broadcom high speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom high speed USB wireless device detected\n"); else - brcmf_dbg(INFO, "Broadcom full speed USB wireless device detected\n"); + brcmf_dbg(USB, "Broadcom full speed USB wireless device detected\n"); - ret = brcmf_usb_probe_cb(devinfo, "", USB_BUS, 0); + ret = brcmf_usb_probe_cb(devinfo); if (ret) goto fail; @@ -1476,40 +1425,55 @@ brcmf_usb_disconnect(struct usb_interface *intf) { struct brcmf_usbdev_info *devinfo; - brcmf_dbg(TRACE, "enter\n"); + brcmf_dbg(USB, "Enter\n"); devinfo = (struct brcmf_usbdev_info *)usb_get_intfdata(intf); brcmf_usb_disconnect_cb(devinfo); kfree(devinfo); + brcmf_dbg(USB, "Exit\n"); } /* - * only need to signal the bus being down and update the suspend state. + * only need to signal the bus being down and update the state. */ static int brcmf_usb_suspend(struct usb_interface *intf, pm_message_t state) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - brcmf_dbg(TRACE, "enter\n"); - devinfo->bus_pub.state = BCMFMAC_USB_STATE_DOWN; - devinfo->suspend_state = USBOS_SUSPEND_STATE_SUSPENDED; + brcmf_dbg(USB, "Enter\n"); + devinfo->bus_pub.state = BRCMFMAC_USB_STATE_SLEEP; + brcmf_detach(&usb->dev); return 0; } /* - * mark suspend state active and crank up the bus. + * (re-) start the bus. */ static int brcmf_usb_resume(struct usb_interface *intf) { struct usb_device *usb = interface_to_usbdev(intf); struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); - brcmf_dbg(TRACE, "enter\n"); - devinfo->suspend_state = USBOS_SUSPEND_STATE_DEVICE_ACTIVE; - brcmf_bus_start(&usb->dev); + brcmf_dbg(USB, "Enter\n"); + if (!brcmf_attach(0, devinfo->dev)) + return brcmf_bus_start(&usb->dev); + return 0; } +static int brcmf_usb_reset_resume(struct usb_interface *intf) +{ + struct usb_device *usb = interface_to_usbdev(intf); + struct brcmf_usbdev_info *devinfo = brcmf_usb_get_businfo(&usb->dev); + + brcmf_dbg(USB, "Enter\n"); + + if (!brcmf_usb_fw_download(devinfo)) + return brcmf_usb_resume(intf); + + return -EIO; +} + #define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c #define BRCMF_USB_DEVICE_ID_43143 0xbd1e #define BRCMF_USB_DEVICE_ID_43236 0xbd17 @@ -1529,7 +1493,6 @@ MODULE_FIRMWARE(BRCMF_USB_43143_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43236_FW_NAME); MODULE_FIRMWARE(BRCMF_USB_43242_FW_NAME); -/* TODO: suspend and resume entries */ static struct usb_driver brcmf_usbdrvr = { .name = KBUILD_MODNAME, .probe = brcmf_usb_probe, @@ -1537,6 +1500,7 @@ static struct usb_driver brcmf_usbdrvr = { .id_table = brcmf_usb_devid_table, .suspend = brcmf_usb_suspend, .resume = brcmf_usb_resume, + .reset_resume = brcmf_usb_reset_resume, .supports_autosuspend = 1, .disable_hub_initiated_lpm = 1, }; @@ -1554,12 +1518,14 @@ static void brcmf_release_fw(struct list_head *q) void brcmf_usb_exit(void) { + brcmf_dbg(USB, "Enter\n"); usb_deregister(&brcmf_usbdrvr); brcmf_release_fw(&fw_image_list); } void brcmf_usb_init(void) { + brcmf_dbg(USB, "Enter\n"); INIT_LIST_HEAD(&fw_image_list); usb_register(&brcmf_usbdrvr); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.h b/drivers/net/wireless/brcm80211/brcmfmac/usb.h index acfa5e89872..f483a8c9945 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.h @@ -17,19 +17,11 @@ #define BRCMFMAC_USB_H enum brcmf_usb_state { - BCMFMAC_USB_STATE_DL_PENDING, - BCMFMAC_USB_STATE_DL_DONE, - BCMFMAC_USB_STATE_UP, - BCMFMAC_USB_STATE_DOWN, - BCMFMAC_USB_STATE_PNP_FWDL, - BCMFMAC_USB_STATE_DISCONNECT, - BCMFMAC_USB_STATE_SLEEP -}; - -enum brcmf_usb_pnp_state { - BCMFMAC_USB_PNP_DISCONNECT, - BCMFMAC_USB_PNP_SLEEP, - BCMFMAC_USB_PNP_RESUME, + BRCMFMAC_USB_STATE_DOWN, + BRCMFMAC_USB_STATE_DL_FAIL, + BRCMFMAC_USB_STATE_DL_DONE, + BRCMFMAC_USB_STATE_UP, + BRCMFMAC_USB_STATE_SLEEP }; struct brcmf_stats { diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 6d554249394..2044fdb5555 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -19,14 +19,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/kernel.h> -#include <linux/if_arp.h> -#include <linux/sched.h> -#include <linux/kthread.h> -#include <linux/netdevice.h> -#include <linux/bitops.h> #include <linux/etherdevice.h> -#include <linux/ieee80211.h> -#include <linux/uaccess.h> #include <net/cfg80211.h> #include <net/netlink.h> @@ -486,13 +479,6 @@ brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, if (ap) { set_bit(BRCMF_VIF_STATUS_AP_CREATING, &ifp->vif->sme_state); - if (!cfg->ap_info) - cfg->ap_info = kzalloc(sizeof(*cfg->ap_info), - GFP_KERNEL); - if (!cfg->ap_info) { - err = -ENOMEM; - goto done; - } WL_INFO("IF Type = AP\n"); } else { err = brcmf_fil_cmd_int_set(netdev_priv(ndev), @@ -529,185 +515,6 @@ static void brcmf_set_mpc(struct net_device *ndev, int mpc) } } -static void brcmf_iscan_prep(struct brcmf_scan_params_le *params_le, - struct brcmf_ssid *ssid) -{ - memset(params_le->bssid, 0xFF, ETH_ALEN); - params_le->bss_type = DOT11_BSSTYPE_ANY; - params_le->scan_type = 0; - params_le->channel_num = 0; - params_le->nprobes = cpu_to_le32(-1); - params_le->active_time = cpu_to_le32(-1); - params_le->passive_time = cpu_to_le32(-1); - params_le->home_time = cpu_to_le32(-1); - if (ssid && ssid->SSID_len) { - params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len); - memcpy(¶ms_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len); - } -} - -static s32 -brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan, - struct brcmf_ssid *ssid, u16 action) -{ - s32 params_size = BRCMF_SCAN_PARAMS_FIXED_SIZE + - offsetof(struct brcmf_iscan_params_le, params_le); - struct brcmf_iscan_params_le *params; - s32 err = 0; - - if (ssid && ssid->SSID_len) - params_size += sizeof(struct brcmf_ssid); - params = kzalloc(params_size, GFP_KERNEL); - if (!params) - return -ENOMEM; - BUG_ON(params_size >= BRCMF_DCMD_SMLEN); - - brcmf_iscan_prep(¶ms->params_le, ssid); - - params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION); - params->action = cpu_to_le16(action); - params->scan_duration = cpu_to_le16(0); - - err = brcmf_fil_iovar_data_set(netdev_priv(iscan->ndev), "iscan", - params, params_size); - if (err) { - if (err == -EBUSY) - WL_INFO("system busy : iscan canceled\n"); - else - WL_ERR("error (%d)\n", err); - } - - kfree(params); - return err; -} - -static s32 brcmf_do_iscan(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg); - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_ssid ssid; - u32 passive_scan; - s32 err = 0; - - /* Broadcast scan by default */ - memset(&ssid, 0, sizeof(ssid)); - - iscan->state = WL_ISCAN_STATE_SCANING; - - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(netdev_priv(ndev), - BRCMF_C_SET_PASSIVE_SCAN, passive_scan); - if (err) { - WL_ERR("error (%d)\n", err); - return err; - } - brcmf_set_mpc(ndev, 0); - cfg->iscan_kickstart = true; - err = brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START); - if (err) { - brcmf_set_mpc(ndev, 1); - cfg->iscan_kickstart = false; - return err; - } - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); - iscan->timer_on = 1; - return err; -} - -static s32 -brcmf_cfg80211_iscan(struct wiphy *wiphy, struct net_device *ndev, - struct cfg80211_scan_request *request, - struct cfg80211_ssid *this_ssid) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); - struct cfg80211_ssid *ssids; - struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int; - u32 passive_scan; - bool iscan_req; - bool spec_scan; - s32 err = 0; - u32 SSID_len; - - if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - WL_ERR("Scanning already: status (%lu)\n", cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { - WL_ERR("Scanning being aborted: status (%lu)\n", - cfg->scan_status); - return -EAGAIN; - } - if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) { - WL_ERR("Connecting: status (%lu)\n", ifp->vif->sme_state); - return -EAGAIN; - } - - iscan_req = false; - spec_scan = false; - if (request) { - /* scan bss */ - ssids = request->ssids; - if (cfg->iscan_on && (!ssids || !ssids->ssid_len)) - iscan_req = true; - } else { - /* scan in ibss */ - /* we don't do iscan in ibss */ - ssids = this_ssid; - } - - cfg->scan_request = request; - set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - if (iscan_req) { - err = brcmf_do_iscan(cfg); - if (!err) - return err; - else - goto scan_out; - } else { - WL_SCAN("ssid \"%s\", ssid_len (%d)\n", - ssids->ssid, ssids->ssid_len); - memset(&sr->ssid_le, 0, sizeof(sr->ssid_le)); - SSID_len = min_t(u8, sizeof(sr->ssid_le.SSID), ssids->ssid_len); - sr->ssid_le.SSID_len = cpu_to_le32(0); - if (SSID_len) { - memcpy(sr->ssid_le.SSID, ssids->ssid, SSID_len); - sr->ssid_le.SSID_len = cpu_to_le32(SSID_len); - spec_scan = true; - } else { - WL_SCAN("Broadcast scan\n"); - } - - passive_scan = cfg->active_scan ? 0 : 1; - err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PASSIVE_SCAN, - passive_scan); - if (err) { - WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err); - goto scan_out; - } - brcmf_set_mpc(ndev, 0); - err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, - &sr->ssid_le, sizeof(sr->ssid_le)); - if (err) { - if (err == -EBUSY) - WL_INFO("system busy : scan for \"%s\" " - "canceled\n", sr->ssid_le.SSID); - else - WL_ERR("WLC_SCAN error (%d)\n", err); - - brcmf_set_mpc(ndev, 1); - goto scan_out; - } - } - - return 0; - -scan_out: - clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); - cfg->scan_request = NULL; - return err; -} - static void brcmf_escan_prep(struct brcmf_scan_params_le *params_le, struct cfg80211_scan_request *request) { @@ -931,7 +738,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_if *ifp = netdev_priv(ndev); struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); struct cfg80211_ssid *ssids; - struct brcmf_cfg80211_scan_req *sr = cfg->scan_req_int; + struct brcmf_cfg80211_scan_req *sr = &cfg->scan_req_int; u32 passive_scan; bool escan_req; bool spec_scan; @@ -973,9 +780,7 @@ brcmf_cfg80211_escan(struct wiphy *wiphy, struct net_device *ndev, set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); if (escan_req) { err = brcmf_do_escan(cfg, wiphy, ndev, request); - if (!err) - return err; - else + if (err) goto scan_out; } else { WL_SCAN("ssid \"%s\", ssid_len (%d)\n", @@ -1027,7 +832,6 @@ static s32 brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) { struct net_device *ndev = request->wdev->netdev; - struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); s32 err = 0; WL_TRACE("Enter\n"); @@ -1036,10 +840,7 @@ brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) struct brcmf_cfg80211_vif, wdev))) return -EIO; - if (cfg->iscan_on) - err = brcmf_cfg80211_iscan(wiphy, ndev, request, NULL); - else if (cfg->escan_on) - err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); + err = brcmf_cfg80211_escan(wiphy, ndev, request, NULL); if (err) WL_ERR("scan error (%d)\n", err); @@ -1075,7 +876,7 @@ static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) { s32 err = 0; - u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL); + u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); err = brcmf_fil_cmd_int_set(netdev_priv(ndev), cmd, retry); if (err) { @@ -1212,8 +1013,8 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, else WL_CONN("No BSSID specified\n"); - if (params->channel) - WL_CONN("channel: %d\n", params->channel->center_freq); + if (params->chandef.chan) + WL_CONN("channel: %d\n", params->chandef.chan->center_freq); else WL_CONN("no channel specified\n"); @@ -1258,7 +1059,7 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, else bcnprd = 100; - err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_BCNPRD, bcnprd); + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); if (err) { WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err); goto done; @@ -1286,12 +1087,12 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, } /* Channel */ - if (params->channel) { + if (params->chandef.chan) { u32 target_channel; cfg->channel = ieee80211_frequency_to_channel( - params->channel->center_freq); + params->chandef.chan->center_freq); if (params->channel_fixed) { /* adding chanspec */ brcmf_ch_to_chanspec(cfg->channel, @@ -1300,7 +1101,7 @@ brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, /* set channel for starter */ target_channel = cfg->channel; - err = brcmf_fil_cmd_int_set(ifp, BRCM_SET_CHANNEL, + err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, target_channel); if (err) { WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err); @@ -1721,7 +1522,7 @@ brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, } static s32 -brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, +brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, s32 mbm) { @@ -1770,7 +1571,9 @@ done: return err; } -static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm) +static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + s32 *dbm) { struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); @@ -2014,6 +1817,12 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, if (!check_vif_up(ifp->vif)) return -EIO; + if (key_idx >= DOT11_MAX_DEFAULT_KEYS) { + /* we ignore this key index in this case */ + WL_ERR("invalid key index (%d)\n", key_idx); + return -EINVAL; + } + memset(&key, 0, sizeof(key)); key.index = (u32) key_idx; @@ -2024,15 +1833,6 @@ brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, /* Set the new key/index */ err = send_key_to_dongle(ndev, &key); - if (err) { - if (err == -EINVAL) { - if (key.index >= DOT11_MAX_DEFAULT_KEYS) - /* we ignore this key index in this case */ - WL_ERR("invalid key index (%d)\n", key_idx); - } - /* Ignore this error, may happen during DISASSOC */ - err = -EAGAIN; - } WL_TRACE("Exit\n"); return err; @@ -2239,7 +2039,7 @@ brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *ndev, /* addr param is always NULL. ignore it */ /* Get current rateset */ - err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_CURR_RATESET, + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CURR_RATESET, &rateset_le, sizeof(rateset_le)); if (err) { WL_ERR("could not get current rateset (%d)\n", err); @@ -2354,13 +2154,14 @@ static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) int i; bss_list = cfg->bss_list; - if (bss_list->version != BRCMF_BSS_INFO_VERSION) { + if (bss_list->count != 0 && + bss_list->version != BRCMF_BSS_INFO_VERSION) { WL_ERR("Version %d != WL_BSS_INFO_VERSION\n", bss_list->version); return -EOPNOTSUPP; } WL_SCAN("scanned AP count (%d)\n", bss_list->count); - for (i = 0; i < bss_list->count && i < WL_AP_MAX; i++) { + for (i = 0; i < bss_list->count; i++) { bi = next_bss_le(bss_list, bi); err = brcmf_inform_single_bss(cfg, bi); if (err) @@ -2582,33 +2383,10 @@ update_bss_info_out: static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) { - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg); struct escan_info *escan = &cfg->escan_info; - struct brcmf_ssid ssid; set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); - if (cfg->iscan_on) { - iscan->state = WL_ISCAN_STATE_IDLE; - - if (iscan->timer_on) { - del_timer_sync(&iscan->timer); - iscan->timer_on = 0; - } - - cancel_work_sync(&iscan->work); - - /* Abort iscan running in FW */ - memset(&ssid, 0, sizeof(ssid)); - brcmf_run_iscan(iscan, &ssid, WL_SCAN_ACTION_ABORT); - - if (cfg->scan_request) { - /* Indidate scan abort to cfg80211 layer */ - WL_INFO("Terminating scan in progress\n"); - cfg80211_scan_done(cfg->scan_request, true); - cfg->scan_request = NULL; - } - } - if (cfg->escan_on && cfg->scan_request) { + if (cfg->scan_request) { escan->escan_state = WL_ESCAN_STATE_IDLE; brcmf_notify_escan_complete(cfg, escan->ndev, true, true); } @@ -2616,198 +2394,6 @@ static void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); } -static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan, - bool aborted) -{ - struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan); - struct net_device *ndev = cfg_to_ndev(cfg); - - if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - WL_ERR("Scan complete while device not scanning\n"); - return; - } - if (cfg->scan_request) { - WL_SCAN("ISCAN Completed scan: %s\n", - aborted ? "Aborted" : "Done"); - cfg80211_scan_done(cfg->scan_request, aborted); - brcmf_set_mpc(ndev, 1); - cfg->scan_request = NULL; - } - cfg->iscan_kickstart = false; -} - -static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan) -{ - if (iscan->state != WL_ISCAN_STATE_IDLE) { - WL_SCAN("wake up iscan\n"); - schedule_work(&iscan->work); - return 0; - } - - return -EIO; -} - -static s32 -brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status, - struct brcmf_scan_results **bss_list) -{ - struct brcmf_scan_results *results; - struct brcmf_scan_results_le *results_le; - struct brcmf_iscan_results *list_buf; - s32 err = 0; - - memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX); - list_buf = (struct brcmf_iscan_results *)iscan->scan_buf; - results = &list_buf->results; - results_le = &list_buf->results_le; - results_le->buflen = cpu_to_le32(sizeof(iscan->scan_buf)); - results_le->version = 0; - results_le->count = 0; - - err = brcmf_fil_iovar_data_get(netdev_priv(iscan->ndev), "iscanresults", - iscan->scan_buf, - sizeof(iscan->scan_buf)); - if (err) { - WL_ERR("error (%d)\n", err); - return err; - } - results->buflen = le32_to_cpu(results_le->buflen); - results->version = le32_to_cpu(results_le->version); - results->count = le32_to_cpu(results_le->count); - WL_SCAN("results->count = %d\n", results_le->count); - WL_SCAN("results->buflen = %d\n", results_le->buflen); - *status = le32_to_cpu(list_buf->status_le); - WL_SCAN("status = %d\n", *status); - *bss_list = results; - - return err; -} - -static s32 brcmf_iscan_done(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan; - s32 err = 0; - - iscan->state = WL_ISCAN_STATE_IDLE; - brcmf_inform_bss(cfg); - brcmf_notify_iscan_complete(iscan, false); - - return err; -} - -static s32 brcmf_iscan_pending(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan; - s32 err = 0; - - /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); - iscan->timer_on = 1; - - return err; -} - -static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan; - s32 err = 0; - - brcmf_inform_bss(cfg); - brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE); - /* Reschedule the timer */ - mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000); - iscan->timer_on = 1; - - return err; -} - -static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg->iscan; - s32 err = 0; - - iscan->state = WL_ISCAN_STATE_IDLE; - brcmf_notify_iscan_complete(iscan, true); - - return err; -} - -static void brcmf_cfg80211_iscan_handler(struct work_struct *work) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = - container_of(work, struct brcmf_cfg80211_iscan_ctrl, - work); - struct brcmf_cfg80211_info *cfg = iscan_to_cfg(iscan); - struct brcmf_cfg80211_iscan_eloop *el = &iscan->el; - u32 status = BRCMF_SCAN_RESULTS_PARTIAL; - - if (iscan->timer_on) { - del_timer_sync(&iscan->timer); - iscan->timer_on = 0; - } - - if (brcmf_get_iscan_results(iscan, &status, &cfg->bss_list)) { - status = BRCMF_SCAN_RESULTS_ABORTED; - WL_ERR("Abort iscan\n"); - } - - el->handler[status](cfg); -} - -static void brcmf_iscan_timer(unsigned long data) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = - (struct brcmf_cfg80211_iscan_ctrl *)data; - - if (iscan) { - iscan->timer_on = 0; - WL_SCAN("timer expired\n"); - brcmf_wakeup_iscan(iscan); - } -} - -static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg); - - if (cfg->iscan_on) { - iscan->state = WL_ISCAN_STATE_IDLE; - INIT_WORK(&iscan->work, brcmf_cfg80211_iscan_handler); - } - - return 0; -} - -static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el) -{ - memset(el, 0, sizeof(*el)); - el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done; - el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress; - el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending; - el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted; - el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted; -} - -static s32 brcmf_init_iscan(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg); - int err = 0; - - if (cfg->iscan_on) { - iscan->ndev = cfg_to_ndev(cfg); - brcmf_init_iscan_eloop(&iscan->el); - iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS; - init_timer(&iscan->timer); - iscan->timer.data = (unsigned long) iscan; - iscan->timer.function = brcmf_iscan_timer; - err = brcmf_invoke_iscan(cfg); - if (!err) - iscan->data = cfg; - } - - return err; -} - static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) { struct brcmf_cfg80211_info *cfg = @@ -2825,8 +2411,7 @@ static void brcmf_escan_timeout(unsigned long data) if (cfg->scan_request) { WL_ERR("timer expired\n"); - if (cfg->escan_on) - schedule_work(&cfg->escan_timeout_work); + schedule_work(&cfg->escan_timeout_work); } } @@ -2863,10 +2448,11 @@ brcmf_compare_update_same_bss(struct brcmf_bss_info_le *bss, } static s32 -brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +brcmf_cfg80211_escan_handler(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct net_device *ndev = ifp->ndev; s32 status; s32 err = 0; struct brcmf_escan_result_le *escan_result_le; @@ -2877,13 +2463,11 @@ brcmf_cfg80211_escan_handler(struct brcmf_cfg80211_info *cfg, u32 i; bool aborted; - status = be32_to_cpu(e->status); + status = e->status; - if (!ndev || !cfg->escan_on || - !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - WL_ERR("scan not ready ndev %p wl->escan_on %d drv_status %x\n", - ndev, cfg->escan_on, - !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)); + if (!ndev || !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { + WL_ERR("scan not ready ndev %p drv_status %x\n", ndev, + !test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)); return -EPERM; } @@ -2960,18 +2544,15 @@ exit: static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) { - - if (cfg->escan_on) { - cfg->el.handler[BRCMF_E_ESCAN_RESULT] = - brcmf_cfg80211_escan_handler; - cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; - /* Init scan_timeout timer */ - init_timer(&cfg->escan_timeout); - cfg->escan_timeout.data = (unsigned long) cfg; - cfg->escan_timeout.function = brcmf_escan_timeout; - INIT_WORK(&cfg->escan_timeout_work, - brcmf_cfg80211_escan_timeout_worker); - } + brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, + brcmf_cfg80211_escan_handler); + cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; + /* Init scan_timeout timer */ + init_timer(&cfg->escan_timeout); + cfg->escan_timeout.data = (unsigned long) cfg; + cfg->escan_timeout.function = brcmf_escan_timeout; + INIT_WORK(&cfg->escan_timeout_work, + brcmf_cfg80211_escan_timeout_worker); } static __always_inline void brcmf_delay(u32 ms) @@ -2986,20 +2567,8 @@ static __always_inline void brcmf_delay(u32 ms) static s32 brcmf_cfg80211_resume(struct wiphy *wiphy) { - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - - /* - * Check for BRCMF_VIF_STATUS_READY before any function call which - * could result is bus access. Don't block the resume for - * any driver error conditions - */ WL_TRACE("Enter\n"); - if (check_vif_up(ifp->vif)) - brcmf_invoke_iscan(cfg); - - WL_TRACE("Exit\n"); return 0; } @@ -3198,10 +2767,11 @@ brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) * cfg80211_scan_request one out of the received PNO event. */ static s32 -brcmf_notify_sched_scan_results(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +brcmf_notify_sched_scan_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct net_device *ndev = ifp->ndev; struct brcmf_pno_net_info_le *netinfo, *netinfo_start; struct cfg80211_scan_request *request = NULL; struct cfg80211_ssid *ssid = NULL; @@ -3216,7 +2786,7 @@ brcmf_notify_sched_scan_results(struct brcmf_cfg80211_info *cfg, WL_SCAN("Enter\n"); - if (e->event_type == cpu_to_be32(BRCMF_E_PFN_NET_LOST)) { + if (e->event_code == BRCMF_E_PFN_NET_LOST) { WL_SCAN("PFN NET LOST event. Do Nothing\n"); return 0; } @@ -3309,7 +2879,6 @@ out_err: return err; } -#ifndef CONFIG_BRCMISCAN static int brcmf_dev_pno_clean(struct net_device *ndev) { int ret; @@ -3446,7 +3015,6 @@ static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, brcmf_notify_escan_complete(cfg, ndev, true, true); return 0; } -#endif /* CONFIG_BRCMISCAN */ #ifdef CONFIG_NL80211_TESTMODE static int brcmf_cfg80211_testmode(struct wiphy *wiphy, void *data, int len) @@ -3512,7 +3080,7 @@ static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) static s32 brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie, - bool is_rsn_ie, s32 bssidx) + bool is_rsn_ie) { struct brcmf_if *ifp = netdev_priv(ndev); u32 auth = 0; /* d11 open authentication */ @@ -3689,7 +3257,7 @@ exit: } static s32 -brcmf_parse_vndr_ies(u8 *vndr_ie_buf, u32 vndr_ie_len, +brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, struct parsed_vndr_ies *vndr_ies) { s32 err = 0; @@ -3768,13 +3336,12 @@ brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) return ie_len + VNDR_IE_HDR_SIZE; } -static s32 -brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, s32 pktflag, - u8 *vndr_ie_buf, u32 vndr_ie_len) +static +s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, + const u8 *vndr_ie_buf, u32 vndr_ie_len) { - struct brcmf_if *ifp = netdev_priv(ndev); - struct vif_saved_ie *saved_ie = &ifp->vif->saved_ie; + struct brcmf_if *ifp; + struct vif_saved_ie *saved_ie; s32 err = 0; u8 *iovar_ie_buf; u8 *curr_ie_buf; @@ -3791,8 +3358,12 @@ brcmf_set_management_ie(struct brcmf_cfg80211_info *cfg, u8 *ptr; int remained_buf_len; - WL_TRACE("bssidx %d, pktflag : 0x%02X\n", - brcmf_ndev_bssidx(ndev), pktflag); + if (!vif) + return -ENODEV; + ifp = vif->ifp; + saved_ie = &vif->saved_ie; + + WL_TRACE("bssidx %d, pktflag : 0x%02X\n", ifp->bssidx, pktflag); iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!iovar_ie_buf) return -ENOMEM; @@ -3932,13 +3503,13 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, struct brcmf_tlv *rsn_ie; struct brcmf_vs_tlv *wpa_ie; struct brcmf_join_params join_params; - struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); s32 bssidx = 0; WL_TRACE("channel_type=%d, beacon_interval=%d, dtim_period=%d,\n", - settings->channel_type, settings->beacon_interval, + cfg80211_get_chandef_type(&settings->chandef), + settings->beacon_interval, settings->dtim_period); - WL_TRACE("ssid=%s(%d), auth_type=%d, inactivity_timeout=%d\n", + WL_TRACE("ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", settings->ssid, settings->ssid_len, settings->auth_type, settings->inactivity_timeout); @@ -3990,55 +3561,39 @@ brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, settings->beacon.tail_len); - kfree(cfg->ap_info->rsn_ie); - cfg->ap_info->rsn_ie = NULL; - kfree(cfg->ap_info->wpa_ie); - cfg->ap_info->wpa_ie = NULL; - if ((wpa_ie != NULL || rsn_ie != NULL)) { WL_TRACE("WPA(2) IE is found\n"); if (wpa_ie != NULL) { /* WPA IE */ - err = brcmf_configure_wpaie(ndev, wpa_ie, false, - bssidx); + err = brcmf_configure_wpaie(ndev, wpa_ie, false); if (err < 0) goto exit; - cfg->ap_info->wpa_ie = kmemdup(wpa_ie, - wpa_ie->len + - TLV_HDR_LEN, - GFP_KERNEL); } else { /* RSN IE */ err = brcmf_configure_wpaie(ndev, - (struct brcmf_vs_tlv *)rsn_ie, true, bssidx); + (struct brcmf_vs_tlv *)rsn_ie, true); if (err < 0) goto exit; - cfg->ap_info->rsn_ie = kmemdup(rsn_ie, - rsn_ie->len + - TLV_HDR_LEN, - GFP_KERNEL); } - cfg->ap_info->security_mode = true; } else { WL_TRACE("No WPA(2) IEs found\n"); brcmf_configure_opensecurity(ndev, bssidx); - cfg->ap_info->security_mode = false; } /* Set Beacon IEs to FW */ - err = brcmf_set_management_ie(cfg, ndev, - VNDR_IE_BEACON_FLAG, - (u8 *)settings->beacon.tail, - settings->beacon.tail_len); + err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev), + VNDR_IE_BEACON_FLAG, + settings->beacon.tail, + settings->beacon.tail_len); if (err) WL_ERR("Set Beacon IE Failed\n"); else WL_TRACE("Applied Vndr IEs for Beacon\n"); /* Set Probe Response IEs to FW */ - err = brcmf_set_management_ie(cfg, ndev, - VNDR_IE_PRBRSP_FLAG, - (u8 *)settings->beacon.proberesp_ies, - settings->beacon.proberesp_ies_len); + err = brcmf_vif_set_mgmt_ie(ndev_to_vif(ndev), + VNDR_IE_PRBRSP_FLAG, + settings->beacon.proberesp_ies, + settings->beacon.proberesp_ies_len); if (err) WL_ERR("Set Probe Resp IE Failed\n"); else @@ -4169,11 +3724,8 @@ static struct cfg80211_ops wl_cfg80211_ops = { .start_ap = brcmf_cfg80211_start_ap, .stop_ap = brcmf_cfg80211_stop_ap, .del_station = brcmf_cfg80211_del_station, -#ifndef CONFIG_BRCMISCAN - /* scheduled scan need e-scan, which is mutual exclusive with i-scan */ .sched_scan_start = brcmf_cfg80211_sched_scan_start, .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, -#endif #ifdef CONFIG_NL80211_TESTMODE .testmode_cmd = brcmf_cfg80211_testmode #endif @@ -4197,13 +3749,11 @@ static s32 brcmf_mode_to_nl80211_iftype(s32 mode) static void brcmf_wiphy_pno_params(struct wiphy *wiphy) { -#ifndef CONFIG_BRCMISCAN /* scheduled scan settings */ wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; -#endif } static struct wiphy *brcmf_setup_wiphy(struct device *phydev) @@ -4302,8 +3852,8 @@ static void brcmf_free_vif(struct brcmf_cfg80211_vif *vif) static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg, const struct brcmf_event_msg *e) { - u32 event = be32_to_cpu(e->event_type); - u32 status = be32_to_cpu(e->status); + u32 event = e->event_code; + u32 status = e->status; if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { WL_CONN("Processing set ssid\n"); @@ -4317,8 +3867,8 @@ static bool brcmf_is_linkup(struct brcmf_cfg80211_info *cfg, static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg, const struct brcmf_event_msg *e) { - u32 event = be32_to_cpu(e->event_type); - u16 flags = be16_to_cpu(e->flags); + u32 event = e->event_code; + u16 flags = e->flags; if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) { WL_CONN("Processing link down\n"); @@ -4330,13 +3880,12 @@ static bool brcmf_is_linkdown(struct brcmf_cfg80211_info *cfg, static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, const struct brcmf_event_msg *e) { - u32 event = be32_to_cpu(e->event_type); - u32 status = be32_to_cpu(e->status); + u32 event = e->event_code; + u32 status = e->status; if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { WL_CONN("Processing Link %s & no network found\n", - be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ? - "up" : "down"); + e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); return true; } @@ -4524,9 +4073,9 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, const struct brcmf_event_msg *e, void *data) { s32 err = 0; - u32 event = be32_to_cpu(e->event_type); - u32 reason = be32_to_cpu(e->reason); - u32 len = be32_to_cpu(e->datalen); + u32 event = e->event_code; + u32 reason = e->reason; + u32 len = e->datalen; static int generation; struct station_info sinfo; @@ -4558,11 +4107,11 @@ brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, } static s32 -brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +brcmf_notify_connect_status(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { - struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; + struct net_device *ndev = ifp->ndev; struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; s32 err = 0; @@ -4610,31 +4159,29 @@ brcmf_notify_connect_status(struct brcmf_cfg80211_info *cfg, } static s32 -brcmf_notify_roaming_status(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +brcmf_notify_roaming_status(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { - struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 err = 0; - u32 event = be32_to_cpu(e->event_type); - u32 status = be32_to_cpu(e->status); + u32 event = e->event_code; + u32 status = e->status; if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { if (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) - brcmf_bss_roaming_done(cfg, ndev, e); + brcmf_bss_roaming_done(cfg, ifp->ndev, e); else - brcmf_bss_connect_done(cfg, ndev, e, true); + brcmf_bss_connect_done(cfg, ifp->ndev, e, true); } return err; } static s32 -brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, +brcmf_notify_mic_status(struct brcmf_if *ifp, const struct brcmf_event_msg *e, void *data) { - u16 flags = be16_to_cpu(e->flags); + u16 flags = e->flags; enum nl80211_key_type key_type; if (flags & BRCMF_EVENT_MSG_GROUP) @@ -4642,84 +4189,12 @@ brcmf_notify_mic_status(struct brcmf_cfg80211_info *cfg, else key_type = NL80211_KEYTYPE_PAIRWISE; - cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1, + cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, NULL, GFP_KERNEL); return 0; } -static s32 -brcmf_notify_scan_status(struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e, void *data) -{ - struct brcmf_if *ifp = netdev_priv(ndev); - struct brcmf_channel_info_le channel_inform_le; - struct brcmf_scan_results_le *bss_list_le; - u32 len = WL_SCAN_BUF_MAX; - s32 err = 0; - bool scan_abort = false; - u32 scan_channel; - - WL_TRACE("Enter\n"); - - if (cfg->iscan_on && cfg->iscan_kickstart) { - WL_TRACE("Exit\n"); - return brcmf_wakeup_iscan(cfg_to_iscan(cfg)); - } - - if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { - WL_ERR("Scan complete while device not scanning\n"); - scan_abort = true; - err = -EINVAL; - goto scan_done_out; - } - - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_CHANNEL, - &channel_inform_le, - sizeof(channel_inform_le)); - if (err) { - WL_ERR("scan busy (%d)\n", err); - scan_abort = true; - goto scan_done_out; - } - scan_channel = le32_to_cpu(channel_inform_le.scan_channel); - if (scan_channel) - WL_CONN("channel_inform.scan_channel (%d)\n", scan_channel); - cfg->bss_list = cfg->scan_results; - bss_list_le = (struct brcmf_scan_results_le *) cfg->bss_list; - - memset(cfg->scan_results, 0, len); - bss_list_le->buflen = cpu_to_le32(len); - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_SCAN_RESULTS, - cfg->scan_results, len); - if (err) { - WL_ERR("%s Scan_results error (%d)\n", ndev->name, err); - err = -EINVAL; - scan_abort = true; - goto scan_done_out; - } - cfg->scan_results->buflen = le32_to_cpu(bss_list_le->buflen); - cfg->scan_results->version = le32_to_cpu(bss_list_le->version); - cfg->scan_results->count = le32_to_cpu(bss_list_le->count); - - err = brcmf_inform_bss(cfg); - if (err) - scan_abort = true; - -scan_done_out: - if (cfg->scan_request) { - WL_SCAN("calling cfg80211_scan_done\n"); - cfg80211_scan_done(cfg->scan_request, scan_abort); - brcmf_set_mpc(ndev, 1); - cfg->scan_request = NULL; - } - - WL_TRACE("Exit\n"); - - return err; -} - static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) { conf->mode = (u32)-1; @@ -4730,77 +4205,53 @@ static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) conf->tx_power = -1; } -static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el) -{ - memset(el, 0, sizeof(*el)); - el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status; - el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status; - el->handler[BRCMF_E_DEAUTH_IND] = brcmf_notify_connect_status; - el->handler[BRCMF_E_DEAUTH] = brcmf_notify_connect_status; - el->handler[BRCMF_E_DISASSOC_IND] = brcmf_notify_connect_status; - el->handler[BRCMF_E_ASSOC_IND] = brcmf_notify_connect_status; - el->handler[BRCMF_E_REASSOC_IND] = brcmf_notify_connect_status; - el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status; - el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status; - el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status; - el->handler[BRCMF_E_PFN_NET_FOUND] = brcmf_notify_sched_scan_results; +static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) +{ + brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, + brcmf_notify_roaming_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, + brcmf_notify_mic_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, + brcmf_notify_connect_status); + brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, + brcmf_notify_sched_scan_results); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) { - kfree(cfg->scan_results); - cfg->scan_results = NULL; - kfree(cfg->bss_info); - cfg->bss_info = NULL; kfree(cfg->conf); cfg->conf = NULL; - kfree(cfg->scan_req_int); - cfg->scan_req_int = NULL; kfree(cfg->escan_ioctl_buf); cfg->escan_ioctl_buf = NULL; - kfree(cfg->dcmd_buf); - cfg->dcmd_buf = NULL; kfree(cfg->extra_buf); cfg->extra_buf = NULL; - kfree(cfg->iscan); - cfg->iscan = NULL; kfree(cfg->pmk_list); cfg->pmk_list = NULL; - if (cfg->ap_info) { - kfree(cfg->ap_info->wpa_ie); - kfree(cfg->ap_info->rsn_ie); - kfree(cfg->ap_info); - cfg->ap_info = NULL; - } } static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) { - cfg->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL); - if (!cfg->scan_results) - goto init_priv_mem_out; cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); if (!cfg->conf) goto init_priv_mem_out; - cfg->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); - if (!cfg->bss_info) - goto init_priv_mem_out; - cfg->scan_req_int = kzalloc(sizeof(*cfg->scan_req_int), - GFP_KERNEL); - if (!cfg->scan_req_int) - goto init_priv_mem_out; cfg->escan_ioctl_buf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); if (!cfg->escan_ioctl_buf) goto init_priv_mem_out; - cfg->dcmd_buf = kzalloc(WL_DCMD_LEN_MAX, GFP_KERNEL); - if (!cfg->dcmd_buf) - goto init_priv_mem_out; cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); if (!cfg->extra_buf) goto init_priv_mem_out; - cfg->iscan = kzalloc(sizeof(*cfg->iscan), GFP_KERNEL); - if (!cfg->iscan) - goto init_priv_mem_out; cfg->pmk_list = kzalloc(sizeof(*cfg->pmk_list), GFP_KERNEL); if (!cfg->pmk_list) goto init_priv_mem_out; @@ -4813,149 +4264,22 @@ init_priv_mem_out: return -ENOMEM; } -/* -* retrieve first queued event from head -*/ - -static struct brcmf_cfg80211_event_q *brcmf_deq_event( - struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_event_q *e = NULL; - - spin_lock_irq(&cfg->evt_q_lock); - if (!list_empty(&cfg->evt_q_list)) { - e = list_first_entry(&cfg->evt_q_list, - struct brcmf_cfg80211_event_q, evt_q_list); - list_del(&e->evt_q_list); - } - spin_unlock_irq(&cfg->evt_q_lock); - - return e; -} - -/* -* push event to tail of the queue -* -* remark: this function may not sleep as it is called in atomic context. -*/ - -static s32 -brcmf_enq_event(struct brcmf_cfg80211_info *cfg, u32 event, - const struct brcmf_event_msg *msg, void *data) -{ - struct brcmf_cfg80211_event_q *e; - s32 err = 0; - ulong flags; - u32 data_len; - u32 total_len; - - total_len = sizeof(struct brcmf_cfg80211_event_q); - if (data) - data_len = be32_to_cpu(msg->datalen); - else - data_len = 0; - total_len += data_len; - e = kzalloc(total_len, GFP_ATOMIC); - if (!e) - return -ENOMEM; - - e->etype = event; - memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg)); - if (data) - memcpy(&e->edata, data, data_len); - - spin_lock_irqsave(&cfg->evt_q_lock, flags); - list_add_tail(&e->evt_q_list, &cfg->evt_q_list); - spin_unlock_irqrestore(&cfg->evt_q_lock, flags); - - return err; -} - -static void brcmf_put_event(struct brcmf_cfg80211_event_q *e) -{ - kfree(e); -} - -static void brcmf_cfg80211_event_handler(struct work_struct *work) -{ - struct brcmf_cfg80211_info *cfg = - container_of(work, struct brcmf_cfg80211_info, - event_work); - struct brcmf_cfg80211_event_q *e; - - e = brcmf_deq_event(cfg); - if (unlikely(!e)) { - WL_ERR("event queue empty...\n"); - return; - } - - do { - WL_INFO("event type (%d)\n", e->etype); - if (cfg->el.handler[e->etype]) - cfg->el.handler[e->etype](cfg, - cfg_to_ndev(cfg), - &e->emsg, e->edata); - else - WL_INFO("Unknown Event (%d): ignoring\n", e->etype); - brcmf_put_event(e); - } while ((e = brcmf_deq_event(cfg))); - -} - -static void brcmf_init_eq(struct brcmf_cfg80211_info *cfg) -{ - spin_lock_init(&cfg->evt_q_lock); - INIT_LIST_HEAD(&cfg->evt_q_list); -} - -static void brcmf_flush_eq(struct brcmf_cfg80211_info *cfg) -{ - struct brcmf_cfg80211_event_q *e; - - spin_lock_irq(&cfg->evt_q_lock); - while (!list_empty(&cfg->evt_q_list)) { - e = list_first_entry(&cfg->evt_q_list, - struct brcmf_cfg80211_event_q, evt_q_list); - list_del(&e->evt_q_list); - kfree(e); - } - spin_unlock_irq(&cfg->evt_q_lock); -} - static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) { s32 err = 0; cfg->scan_request = NULL; cfg->pwr_save = true; -#ifdef CONFIG_BRCMISCAN - cfg->iscan_on = true; /* iscan on & off switch. - we enable iscan per default */ - cfg->escan_on = false; /* escan on & off switch. - we disable escan per default */ -#else - cfg->iscan_on = false; /* iscan on & off switch. - we disable iscan per default */ - cfg->escan_on = true; /* escan on & off switch. - we enable escan per default */ -#endif cfg->roam_on = true; /* roam on & off switch. we enable roam per default */ - - cfg->iscan_kickstart = false; cfg->active_scan = true; /* we do active scan for specific scan per default */ cfg->dongle_up = false; /* dongle is not up yet */ - brcmf_init_eq(cfg); err = brcmf_init_priv_mem(cfg); if (err) return err; - INIT_WORK(&cfg->event_work, brcmf_cfg80211_event_handler); - brcmf_init_eloop_handler(&cfg->el); + brcmf_register_event_handlers(cfg); mutex_init(&cfg->usr_sync); - err = brcmf_init_iscan(cfg); - if (err) - return err; brcmf_init_escan(cfg); brcmf_init_conf(cfg->conf); brcmf_link_down(cfg); @@ -4965,9 +4289,7 @@ static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) { - cancel_work_sync(&cfg->event_work); cfg->dongle_up = false; /* dongle down */ - brcmf_flush_eq(cfg); brcmf_link_down(cfg); brcmf_abort_scanning(cfg); brcmf_deinit_priv_mem(cfg); @@ -5029,66 +4351,6 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) } } -void -brcmf_cfg80211_event(struct net_device *ndev, - const struct brcmf_event_msg *e, void *data) -{ - u32 event_type = be32_to_cpu(e->event_type); - struct brcmf_cfg80211_info *cfg = ndev_to_cfg(ndev); - - if (!brcmf_enq_event(cfg, event_type, e, data)) - schedule_work(&cfg->event_work); -} - -static s32 brcmf_dongle_eventmsg(struct net_device *ndev) -{ - s8 eventmask[BRCMF_EVENTING_MASK_LEN]; - s32 err = 0; - - WL_TRACE("Enter\n"); - - /* Setup event_msgs */ - err = brcmf_fil_iovar_data_get(netdev_priv(ndev), "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); - if (err) { - WL_ERR("Get event_msgs error (%d)\n", err); - goto dongle_eventmsg_out; - } - - setbit(eventmask, BRCMF_E_SET_SSID); - setbit(eventmask, BRCMF_E_ROAM); - setbit(eventmask, BRCMF_E_PRUNE); - setbit(eventmask, BRCMF_E_AUTH); - setbit(eventmask, BRCMF_E_REASSOC); - setbit(eventmask, BRCMF_E_REASSOC_IND); - setbit(eventmask, BRCMF_E_DEAUTH_IND); - setbit(eventmask, BRCMF_E_DISASSOC_IND); - setbit(eventmask, BRCMF_E_DISASSOC); - setbit(eventmask, BRCMF_E_JOIN); - setbit(eventmask, BRCMF_E_ASSOC_IND); - setbit(eventmask, BRCMF_E_PSK_SUP); - setbit(eventmask, BRCMF_E_LINK); - setbit(eventmask, BRCMF_E_NDIS_LINK); - setbit(eventmask, BRCMF_E_MIC_ERROR); - setbit(eventmask, BRCMF_E_PMKID_CACHE); - setbit(eventmask, BRCMF_E_TXFAIL); - setbit(eventmask, BRCMF_E_JOIN_START); - setbit(eventmask, BRCMF_E_SCAN_COMPLETE); - setbit(eventmask, BRCMF_E_ESCAN_RESULT); - setbit(eventmask, BRCMF_E_PFN_NET_FOUND); - - err = brcmf_fil_iovar_data_set(netdev_priv(ndev), "event_msgs", - eventmask, BRCMF_EVENTING_MASK_LEN); - if (err) { - WL_ERR("Set event_msgs error (%d)\n", err); - goto dongle_eventmsg_out; - } - -dongle_eventmsg_out: - WL_TRACE("Exit\n"); - return err; -} - static s32 brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout) { @@ -5190,7 +4452,7 @@ static s32 wl_update_wiphybands(struct brcmf_cfg80211_info *cfg) s8 phy; s32 err = 0; - err = brcmf_fil_cmd_data_get(ifp, BRCM_GET_PHYLIST, + err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, &phy_list, sizeof(phy_list)); if (err) { WL_ERR("error (%d)\n", err); @@ -5228,10 +4490,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME, WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME); - err = brcmf_dongle_eventmsg(ndev); - if (err) - goto default_conf_out; - power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; err = brcmf_fil_cmd_int_set(netdev_priv(ndev), BRCMF_C_SET_PM, power_mode); @@ -5262,26 +4520,18 @@ default_conf_out: } -static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg) +static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) { - struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - s32 err = 0; - set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); + if (ifp->idx) + return 0; - err = brcmf_config_dongle(cfg); - if (err) - return err; - - brcmf_invoke_iscan(cfg); - - return err; + return brcmf_config_dongle(ifp->drvr->config); } -static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg) +static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) { - struct net_device *ndev = cfg_to_ndev(cfg); - struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; /* * While going down, if associated with AP disassociate @@ -5306,23 +4556,27 @@ static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg) return 0; } -s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg) +s32 brcmf_cfg80211_up(struct net_device *ndev) { + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 err = 0; mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_up(cfg); + err = __brcmf_cfg80211_up(ifp); mutex_unlock(&cfg->usr_sync); return err; } -s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg) +s32 brcmf_cfg80211_down(struct net_device *ndev) { + struct brcmf_if *ifp = netdev_priv(ndev); + struct brcmf_cfg80211_info *cfg = ifp->drvr->config; s32 err = 0; mutex_lock(&cfg->usr_sync); - err = __brcmf_cfg80211_down(cfg); + err = __brcmf_cfg80211_down(ifp); mutex_unlock(&cfg->usr_sync); return err; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h index 1dd96f148ef..e2ef8519ea8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h @@ -84,31 +84,12 @@ do { \ #define WL_CONN(fmt, args...) #endif /* (defined DEBUG) */ -#define WL_NUM_SCAN_MAX 1 -#define WL_NUM_PMKIDS_MAX MAXPMKID /* will be used - * for 2.6.33 kernel - * or later - */ -#define WL_SCAN_BUF_MAX (1024 * 8) +#define WL_NUM_SCAN_MAX 10 +#define WL_NUM_PMKIDS_MAX MAXPMKID #define WL_TLV_INFO_MAX 1024 #define WL_BSS_INFO_MAX 2048 -#define WL_ASSOC_INFO_MAX 512 /* - * needs to grab assoc info from dongle to - * report it to cfg80211 through "connect" - * event - */ -#define WL_DCMD_LEN_MAX 1024 -#define WL_EXTRA_BUF_MAX 2048 -#define WL_ISCAN_BUF_MAX 2048 /* - * the buf length can be BRCMF_DCMD_MAXLEN - * to reduce iteration - */ -#define WL_ISCAN_TIMER_INTERVAL_MS 3000 -#define WL_SCAN_ERSULTS_LAST (BRCMF_SCAN_RESULTS_NO_MEM+1) -#define WL_AP_MAX 256 /* virtually unlimitted as long - * as kernel memory allows - */ - +#define WL_ASSOC_INFO_MAX 512 /* assoc related fil max buf */ +#define WL_EXTRA_BUF_MAX 2048 #define WL_ROAM_TRIGGER_LEVEL -75 #define WL_ROAM_DELTA 20 #define WL_BEACON_TIMEOUT 3 @@ -145,12 +126,6 @@ enum wl_mode { WL_MODE_AP }; -/* dongle iscan state */ -enum wl_iscan_state { - WL_ISCAN_STATE_IDLE, - WL_ISCAN_STATE_SCANING -}; - /* dongle configuration */ struct brcmf_cfg80211_conf { u32 mode; /* adhoc , infrastructure or ap */ @@ -162,17 +137,6 @@ struct brcmf_cfg80211_conf { struct ieee80211_channel channel; }; -/* forward declaration */ -struct brcmf_cfg80211_info; - -/* cfg80211 main event loop */ -struct brcmf_cfg80211_event_loop { - s32(*handler[BRCMF_E_LAST]) (struct brcmf_cfg80211_info *cfg, - struct net_device *ndev, - const struct brcmf_event_msg *e, - void *data); -}; - /* basic structure of scan request */ struct brcmf_cfg80211_scan_req { struct brcmf_ssid_le ssid_le; @@ -184,14 +148,6 @@ struct brcmf_cfg80211_ie { u8 buf[WL_TLV_INFO_MAX]; }; -/* event queue for cfg80211 main event */ -struct brcmf_cfg80211_event_q { - struct list_head evt_q_list; - u32 etype; - struct brcmf_event_msg emsg; - s8 edata[1]; -}; - /* security information with currently associated ap */ struct brcmf_cfg80211_security { u32 wpa_versions; @@ -270,26 +226,6 @@ struct brcmf_cfg80211_vif { struct list_head list; }; -/* dongle iscan event loop */ -struct brcmf_cfg80211_iscan_eloop { - s32 (*handler[WL_SCAN_ERSULTS_LAST]) - (struct brcmf_cfg80211_info *cfg); -}; - -/* dongle iscan controller */ -struct brcmf_cfg80211_iscan_ctrl { - struct net_device *ndev; - struct timer_list timer; - u32 timer_ms; - u32 timer_on; - s32 state; - struct work_struct work; - struct brcmf_cfg80211_iscan_eloop el; - void *data; - s8 dcmd_buf[BRCMF_DCMD_SMLEN]; - s8 scan_buf[WL_ISCAN_BUF_MAX]; -}; - /* association inform */ struct brcmf_cfg80211_connect_info { u8 *req_ie; @@ -323,17 +259,6 @@ struct escan_info { struct net_device *ndev; }; -/* Structure to hold WPS, WPA IEs for a AP */ -struct ap_info { - u8 probe_res_ie[IE_MAX_LEN]; - u8 beacon_ie[IE_MAX_LEN]; - u32 probe_res_ie_len; - u32 beacon_ie_len; - u8 *wpa_ie; - u8 *rsn_ie; - bool security_mode; -}; - /** * struct brcmf_pno_param_le - PNO scan configuration parameters * @@ -421,24 +346,16 @@ struct brcmf_pno_scanresults_le { * @wiphy: wiphy object for cfg80211 interface. * @conf: dongle configuration. * @scan_request: cfg80211 scan request object. - * @el: main event loop. - * @evt_q_list: used for event queue. - * @evt_q_lock: for event queue synchronization. * @usr_sync: mainly for dongle up/down synchronization. * @bss_list: bss_list holding scanned ap information. - * @scan_results: results of the last scan. * @scan_req_int: internal scan request object. * @bss_info: bss information for cfg80211 layer. * @ie: information element object for internal purpose. - * @iscan: iscan controller information. * @conn_info: association info. * @pmk_list: wpa2 pmk list. - * @event_work: event handler work struct. * @scan_status: scan activity on the dongle. * @pub: common driver information. * @channel: current channel. - * @iscan_on: iscan on/off switch. - * @iscan_kickstart: indicate iscan already started. * @active_scan: current scan mode. * @sched_escan: e-scan for scheduled scan support running. * @ibss_starter: indicates this sta is ibss starter. @@ -450,12 +367,10 @@ struct brcmf_pno_scanresults_le { * @dcmd_buf: dcmd buffer. * @extra_buf: mainly to grab assoc information. * @debugfsdir: debugfs folder for this device. - * @escan_on: escan on/off switch. * @escan_info: escan information. * @escan_timeout: Timer for catch scan timeout. * @escan_timeout_work: scan timeout worker. * @escan_ioctl_buf: dongle command buffer for escan commands. - * @ap_info: host ap information. * @vif_list: linked list of vif instances. * @vif_cnt: number of vif instances. */ @@ -463,24 +378,16 @@ struct brcmf_cfg80211_info { struct wiphy *wiphy; struct brcmf_cfg80211_conf *conf; struct cfg80211_scan_request *scan_request; - struct brcmf_cfg80211_event_loop el; - struct list_head evt_q_list; - spinlock_t evt_q_lock; struct mutex usr_sync; struct brcmf_scan_results *bss_list; - struct brcmf_scan_results *scan_results; - struct brcmf_cfg80211_scan_req *scan_req_int; + struct brcmf_cfg80211_scan_req scan_req_int; struct wl_cfg80211_bss_info *bss_info; struct brcmf_cfg80211_ie ie; - struct brcmf_cfg80211_iscan_ctrl *iscan; struct brcmf_cfg80211_connect_info conn_info; struct brcmf_cfg80211_pmk_list *pmk_list; - struct work_struct event_work; unsigned long scan_status; struct brcmf_pub *pub; u32 channel; - bool iscan_on; - bool iscan_kickstart; bool active_scan; bool sched_escan; bool ibss_starter; @@ -492,12 +399,10 @@ struct brcmf_cfg80211_info { u8 *dcmd_buf; u8 *extra_buf; struct dentry *debugfsdir; - bool escan_on; struct escan_info escan_info; struct timer_list escan_timeout; struct work_struct escan_timeout_work; u8 *escan_ioctl_buf; - struct ap_info *ap_info; struct list_head vif_list; u8 vif_cnt; }; @@ -536,8 +441,11 @@ static inline struct brcmf_cfg80211_profile *ndev_to_prof(struct net_device *nd) return &ifp->vif->profile; } -#define iscan_to_cfg(i) ((struct brcmf_cfg80211_info *)(i->data)) -#define cfg_to_iscan(w) (w->iscan) +static inline struct brcmf_cfg80211_vif *ndev_to_vif(struct net_device *ndev) +{ + struct brcmf_if *ifp = netdev_priv(ndev); + return ifp->vif; +} static inline struct brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) @@ -547,11 +455,7 @@ brcmf_cfg80211_connect_info *cfg_to_conn(struct brcmf_cfg80211_info *cfg) struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr); void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg); - -/* event handler from dongle */ -void brcmf_cfg80211_event(struct net_device *ndev, - const struct brcmf_event_msg *e, void *data); -s32 brcmf_cfg80211_up(struct brcmf_cfg80211_info *cfg); -s32 brcmf_cfg80211_down(struct brcmf_cfg80211_info *cfg); +s32 brcmf_cfg80211_up(struct net_device *ndev); +s32 brcmf_cfg80211_down(struct net_device *ndev); #endif /* _wl_cfg80211_h_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/brcm80211/brcmsmac/Makefile index e227c4c68ef..d3d4151c3ed 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmsmac/Makefile @@ -40,7 +40,8 @@ BRCMSMAC_OFILES := \ phy/phytbl_n.o \ phy/phy_qmath.o \ dma.o \ - brcms_trace_events.o + brcms_trace_events.o \ + debug.o MODULEPFX := brcmsmac diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c index be5bcfb9153..1de94f30564 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c @@ -21,6 +21,8 @@ #include "antsel.h" #include "main.h" #include "ampdu.h" +#include "debug.h" +#include "brcms_trace_events.h" /* max number of mpdus in an ampdu */ #define AMPDU_MAX_MPDU 32 @@ -40,8 +42,6 @@ #define AMPDU_DEF_RETRY_LIMIT 5 /* default tx retry limit at reg rate */ #define AMPDU_DEF_RR_RETRY_LIMIT 2 -/* default weight of ampdu in txfifo */ -#define AMPDU_DEF_TXPKT_WEIGHT 2 /* default ffpld reserved bytes */ #define AMPDU_DEF_FFPLD_RSVD 2048 /* # of inis to be freed on detach */ @@ -114,7 +114,6 @@ struct brcms_fifo_info { * mpdu_density: min mpdu spacing (0-7) ==> 2^(x-1)/8 usec * max_pdu: max pdus allowed in ampdu * dur: max duration of an ampdu (in msec) - * txpkt_weight: weight of ampdu in txfifo; reduces rate lag * rx_factor: maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes * ffpld_rsvd: number of bytes to reserve for preload * max_txlen: max size of ampdu per mcs, bw and sgi @@ -136,7 +135,6 @@ struct ampdu_info { u8 mpdu_density; s8 max_pdu; u8 dur; - u8 txpkt_weight; u8 rx_factor; u32 ffpld_rsvd; u32 max_txlen[MCS_TABLE_SIZE][2][2]; @@ -183,18 +181,19 @@ static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu) static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on) { struct brcms_c_info *wlc = ampdu->wlc; + struct bcma_device *core = wlc->hw->d11core; wlc->pub->_ampdu = false; if (on) { if (!(wlc->pub->_n_enab & SUPPORT_11N)) { - wiphy_err(ampdu->wlc->wiphy, "wl%d: driver not " - "nmode enabled\n", wlc->pub->unit); + brcms_err(core, "wl%d: driver not nmode enabled\n", + wlc->pub->unit); return -ENOTSUPP; } if (!brcms_c_ampdu_cap(ampdu)) { - wiphy_err(ampdu->wlc->wiphy, "wl%d: device not " - "ampdu capable\n", wlc->pub->unit); + brcms_err(core, "wl%d: device not ampdu capable\n", + wlc->pub->unit); return -ENOTSUPP; } wlc->pub->_ampdu = on; @@ -247,7 +246,6 @@ struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc) ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY; ampdu->max_pdu = AUTO; ampdu->dur = AMPDU_MAX_DUR; - ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT; ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD; /* @@ -374,7 +372,8 @@ static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) offsetof(struct macstat, txfunfl[fid])); new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl); if (new_txunfl == 0) { - BCMMSG(wlc->wiphy, "TX status FRAG set but no tx underflows\n"); + brcms_dbg_ht(wlc->hw->d11core, + "TX status FRAG set but no tx underflows\n"); return -1; } fifo->prev_txfunfl = cur_txunfl; @@ -396,8 +395,8 @@ static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) if (fifo->accum_txfunfl < 10) return 0; - BCMMSG(wlc->wiphy, "ampdu_count %d tx_underflows %d\n", - current_ampdu_cnt, fifo->accum_txfunfl); + brcms_dbg_ht(wlc->hw->d11core, "ampdu_count %d tx_underflows %d\n", + current_ampdu_cnt, fifo->accum_txfunfl); /* compute the current ratio of tx unfl per ampdu. @@ -450,9 +449,10 @@ static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid) (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size)) / (max_mpdu * FFPLD_MPDU_SIZE)) * 100; - BCMMSG(wlc->wiphy, "DMA estimated transfer rate %d; " - "pre-load size %d\n", - fifo->dmaxferrate, fifo->ampdu_pld_size); + brcms_dbg_ht(wlc->hw->d11core, + "DMA estimated transfer rate %d; " + "pre-load size %d\n", + fifo->dmaxferrate, fifo->ampdu_pld_size); } else { /* decrease ampdu size */ @@ -486,7 +486,7 @@ brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, scb_ampdu = &scb->scb_ampdu; if (!ampdu->ini_enable[tid]) { - wiphy_err(ampdu->wlc->wiphy, "%s: Rejecting tid %d\n", + brcms_err(wlc->hw->d11core, "%s: Rejecting tid %d\n", __func__, tid); return; } @@ -498,378 +498,324 @@ brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid, scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes; } -int -brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi, - struct sk_buff **pdu, int prec) +void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc) { - struct brcms_c_info *wlc; - struct sk_buff *p, *pkt[AMPDU_MAX_MPDU]; - u8 tid, ndelim; - int err = 0; - u8 preamble_type = BRCMS_GF_PREAMBLE; - u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; - u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; - u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; + session->wlc = wlc; + skb_queue_head_init(&session->skb_list); + session->max_ampdu_len = 0; /* determined from first MPDU */ + session->max_ampdu_frames = 0; /* determined from first MPDU */ + session->ampdu_len = 0; + session->dma_len = 0; +} - bool rr = true, fbr = false; - uint i, count = 0, fifo, seg_cnt = 0; - u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0; - u32 ampdu_len, max_ampdu_bytes = 0; - struct d11txh *txh = NULL; +/* + * Preps the given packet for AMPDU based on the session data. If the + * frame cannot be accomodated in the current session, -ENOSPC is + * returned. + */ +int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p) +{ + struct brcms_c_info *wlc = session->wlc; + struct ampdu_info *ampdu = wlc->ampdu; + struct scb *scb = &wlc->pri_scb; + struct scb_ampdu *scb_ampdu = &scb->scb_ampdu; + struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); + struct ieee80211_tx_rate *txrate = tx_info->status.rates; + struct d11txh *txh = (struct d11txh *)p->data; + unsigned ampdu_frames; + u8 ndelim, tid; u8 *plcp; - struct ieee80211_hdr *h; - struct scb *scb; - struct scb_ampdu *scb_ampdu; - struct scb_ampdu_tid_ini *ini; - u8 mcs = 0; - bool use_rts = false, use_cts = false; - u32 rspec = 0, rspec_fallback = 0; - u32 rts_rspec = 0, rts_rspec_fallback = 0; - u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; - struct ieee80211_rts *rts; - u8 rr_retry_limit; - struct brcms_fifo_info *f; + uint len; + u16 mcl; bool fbr_iscck; - struct ieee80211_tx_info *tx_info; - u16 qlen; - struct wiphy *wiphy; - - wlc = ampdu->wlc; - wiphy = wlc->wiphy; - p = *pdu; - - tid = (u8) (p->priority); + bool rr; - f = ampdu->fifo_tb + prio2fifo[tid]; + ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; + plcp = (u8 *)(txh + 1); + fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); + len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : + BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); + len = roundup(len, 4) + (ndelim + 1) * AMPDU_DELIMITER_LEN; - scb = &wlc->pri_scb; - scb_ampdu = &scb->scb_ampdu; - ini = &scb_ampdu->ini[tid]; + ampdu_frames = skb_queue_len(&session->skb_list); + if (ampdu_frames != 0) { + struct sk_buff *first; - /* Let pressure continue to build ... */ - qlen = pktq_plen(&qi->q, prec); - if (ini->tx_in_transit > 0 && - qlen < min(scb_ampdu->max_pdu, ini->ba_wsize)) - /* Collect multiple MPDU's to be sent in the next AMPDU */ - return -EBUSY; + if (ampdu_frames + 1 > session->max_ampdu_frames || + session->ampdu_len + len > session->max_ampdu_len) + return -ENOSPC; - /* at this point we intend to transmit an AMPDU */ - rr_retry_limit = ampdu->rr_retry_limit_tid[tid]; - ampdu_len = 0; - dma_len = 0; - while (p) { - struct ieee80211_tx_rate *txrate; - - tx_info = IEEE80211_SKB_CB(p); - txrate = tx_info->status.rates; + /* + * We aren't really out of space if the new frame is of + * a different priority, but we want the same behaviour + * so return -ENOSPC anyway. + * + * XXX: The old AMPDU code did this, but is it really + * necessary? + */ + first = skb_peek(&session->skb_list); + if (p->priority != first->priority) + return -ENOSPC; + } - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - err = brcms_c_prep_pdu(wlc, p, &fifo); - } else { - wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__); - *pdu = NULL; - err = 0; - break; - } + /* + * Now that we're sure this frame can be accomodated, update the + * session information. + */ + session->ampdu_len += len; + session->dma_len += p->len; - if (err) { - if (err == -EBUSY) { - wiphy_err(wiphy, "wl%d: sendampdu: " - "prep_xdu retry; seq 0x%x\n", - wlc->pub->unit, seq); - *pdu = p; - break; - } + tid = (u8)p->priority; - /* error in the packet; reject it */ - wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu " - "rejected; seq 0x%x\n", wlc->pub->unit, seq); - *pdu = NULL; - break; - } + /* Handle retry limits */ + if (txrate[0].count <= ampdu->rr_retry_limit_tid[tid]) { + txrate[0].count++; + rr = true; + } else { + txrate[1].count++; + rr = false; + } - /* pkt is good to be aggregated */ - txh = (struct d11txh *) p->data; - plcp = (u8 *) (txh + 1); - h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); - seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; - index = TX_SEQ_TO_INDEX(seq); + if (ampdu_frames == 0) { + u8 plcp0, plcp3, is40, sgi, mcs; + uint fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; + struct brcms_fifo_info *f = &du->fifo_tb[fifo]; - /* check mcl fields and test whether it can be agg'd */ - mcl = le16_to_cpu(txh->MacTxControlLow); - mcl &= ~TXC_AMPDU_MASK; - fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3); - txh->PreloadSize = 0; /* always default to 0 */ - - /* Handle retry limits */ - if (txrate[0].count <= rr_retry_limit) { - txrate[0].count++; - rr = true; - fbr = false; + if (rr) { + plcp0 = plcp[0]; + plcp3 = plcp[3]; } else { - fbr = true; - rr = false; - txrate[1].count++; - } - - /* extract the length info */ - len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) - : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); - - /* retrieve null delimiter count */ - ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; - seg_cnt += 1; + plcp0 = txh->FragPLCPFallback[0]; + plcp3 = txh->FragPLCPFallback[3]; - BCMMSG(wlc->wiphy, "wl%d: mpdu %d plcp_len %d\n", - wlc->pub->unit, count, len); - - /* - * aggregateable mpdu. For ucode/hw agg, - * test whether need to break or change the epoch - */ - if (count == 0) { - mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); - /* refill the bits since might be a retx mpdu */ - mcl |= TXC_STARTMSDU; - rts = (struct ieee80211_rts *)&txh->rts_frame; - - if (ieee80211_is_rts(rts->frame_control)) { - mcl |= TXC_SENDRTS; - use_rts = true; - } - if (ieee80211_is_cts(rts->frame_control)) { - mcl |= TXC_SENDCTS; - use_cts = true; - } - } else { - mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); - mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); } - len = roundup(len, 4); - ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN); + /* Limit AMPDU size based on MCS */ + is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; + sgi = plcp3_issgi(plcp3) ? 1 : 0; + mcs = plcp0 & ~MIMO_PLCP_40MHZ; + session->max_ampdu_len = min(scb_ampdu->max_rx_ampdu_bytes, + ampdu->max_txlen[mcs][is40][sgi]); - dma_len += (u16) p->len; + session->max_ampdu_frames = scb_ampdu->max_pdu; + if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { + session->max_ampdu_frames = + min_t(u16, f->mcs2ampdu_table[mcs], + session->max_ampdu_frames); + } + } - BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d" - " seg_cnt %d null delim %d\n", - wlc->pub->unit, ampdu_len, seg_cnt, ndelim); + /* + * Treat all frames as "middle" frames of AMPDU here. First and + * last frames must be fixed up after all MPDUs have been prepped. + */ + mcl = le16_to_cpu(txh->MacTxControlLow); + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT); + mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS); + txh->MacTxControlLow = cpu_to_le16(mcl); + txh->PreloadSize = 0; /* always default to 0 */ - txh->MacTxControlLow = cpu_to_le16(mcl); + skb_queue_tail(&session->skb_list, p); - /* this packet is added */ - pkt[count++] = p; + return 0; +} - /* patch the first MPDU */ - if (count == 1) { - u8 plcp0, plcp3, is40, sgi; +void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session) +{ + struct brcms_c_info *wlc = session->wlc; + struct ampdu_info *ampdu = wlc->ampdu; + struct sk_buff *first, *last; + struct d11txh *txh; + struct ieee80211_tx_info *tx_info; + struct ieee80211_tx_rate *txrate; + u8 ndelim; + u8 *plcp; + uint len; + uint fifo; + struct brcms_fifo_info *f; + u16 mcl; + bool fbr; + bool fbr_iscck; + struct ieee80211_rts *rts; + bool use_rts = false, use_cts = false; + u16 dma_len = session->dma_len; + u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; + u32 rspec = 0, rspec_fallback = 0; + u32 rts_rspec = 0, rts_rspec_fallback = 0; + u8 plcp0, plcp3, is40, sgi, mcs; + u16 mch; + u8 preamble_type = BRCMS_GF_PREAMBLE; + u8 fbr_preamble_type = BRCMS_GF_PREAMBLE; + u8 rts_preamble_type = BRCMS_LONG_PREAMBLE; + u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE; - if (rr) { - plcp0 = plcp[0]; - plcp3 = plcp[3]; - } else { - plcp0 = txh->FragPLCPFallback[0]; - plcp3 = txh->FragPLCPFallback[3]; + if (skb_queue_empty(&session->skb_list)) + return; - } - is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; - sgi = plcp3_issgi(plcp3) ? 1 : 0; - mcs = plcp0 & ~MIMO_PLCP_40MHZ; - max_ampdu_bytes = - min(scb_ampdu->max_rx_ampdu_bytes, - ampdu->max_txlen[mcs][is40][sgi]); - - if (is40) - mimo_ctlchbw = - CHSPEC_SB_UPPER(wlc_phy_chanspec_get( - wlc->band->pi)) - ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; - - /* rebuild the rspec and rspec_fallback */ - rspec = RSPEC_MIMORATE; - rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; - if (plcp[0] & MIMO_PLCP_40MHZ) - rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); - - if (fbr_iscck) /* CCK */ - rspec_fallback = cck_rspec(cck_phy2mac_rate - (txh->FragPLCPFallback[0])); - else { /* MIMO */ - rspec_fallback = RSPEC_MIMORATE; - rspec_fallback |= - txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; - if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) - rspec_fallback |= - (PHY_TXC1_BW_40MHZ << - RSPEC_BW_SHIFT); - } + first = skb_peek(&session->skb_list); + last = skb_peek_tail(&session->skb_list); + + /* Need to fix up last MPDU first to adjust AMPDU length */ + txh = (struct d11txh *)last->data; + fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; + f = &du->fifo_tb[fifo]; + + mcl = le16_to_cpu(txh->MacTxControlLow); + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); + txh->MacTxControlLow = cpu_to_le16(mcl); + + /* remove the null delimiter after last mpdu */ + ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; + txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; + session->ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; + + /* remove the pad len from last mpdu */ + fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); + len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) : + BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); + session->ampdu_len -= roundup(len, 4) - len; + + /* Now fix up the first MPDU */ + tx_info = IEEE80211_SKB_CB(first); + txrate = tx_info->status.rates; + txh = (struct d11txh *)first->data; + plcp = (u8 *)(txh + 1); + rts = (struct ieee80211_rts *)&txh->rts_frame; + + mcl = le16_to_cpu(txh->MacTxControlLow); + /* If only one MPDU leave it marked as last */ + if (first != last) { + mcl &= ~TXC_AMPDU_MASK; + mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT); + } + mcl |= TXC_STARTMSDU; + if (ieee80211_is_rts(rts->frame_control)) { + mcl |= TXC_SENDRTS; + use_rts = true; + } + if (ieee80211_is_cts(rts->frame_control)) { + mcl |= TXC_SENDCTS; + use_cts = true; + } + txh->MacTxControlLow = cpu_to_le16(mcl); - if (use_rts || use_cts) { - rts_rspec = - brcms_c_rspec_to_rts_rspec(wlc, - rspec, false, mimo_ctlchbw); - rts_rspec_fallback = - brcms_c_rspec_to_rts_rspec(wlc, - rspec_fallback, false, mimo_ctlchbw); - } - } + fbr = txrate[1].count > 0; + if (!fbr) { + plcp0 = plcp[0]; + plcp3 = plcp[3]; + } else { + plcp0 = txh->FragPLCPFallback[0]; + plcp3 = txh->FragPLCPFallback[3]; + } + is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0; + sgi = plcp3_issgi(plcp3) ? 1 : 0; + mcs = plcp0 & ~MIMO_PLCP_40MHZ; + + if (is40) { + if (CHSPEC_SB_UPPER(wlc_phy_chanspec_get(wlc->band->pi))) + mimo_ctlchbw = PHY_TXC1_BW_20MHZ_UP; + else + mimo_ctlchbw = PHY_TXC1_BW_20MHZ; + } - /* if (first mpdu for host agg) */ - /* test whether to add more */ - if ((mcs_2_rate(mcs, true, false) >= f->dmaxferrate) && - (count == f->mcs2ampdu_table[mcs])) { - BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping" - " ampdu at %d for mcs %d\n", - wlc->pub->unit, count, mcs); - break; - } + /* rebuild the rspec and rspec_fallback */ + rspec = RSPEC_MIMORATE; + rspec |= plcp[0] & ~MIMO_PLCP_40MHZ; + if (plcp[0] & MIMO_PLCP_40MHZ) + rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT); - if (count == scb_ampdu->max_pdu) - break; + fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x03); + if (fbr_iscck) { + rspec_fallback = + cck_rspec(cck_phy2mac_rate(txh->FragPLCPFallback[0])); + } else { + rspec_fallback = RSPEC_MIMORATE; + rspec_fallback |= txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ; + if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ) + rspec_fallback |= PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT; + } - /* - * check to see if the next pkt is - * a candidate for aggregation - */ - p = pktq_ppeek(&qi->q, prec); - if (p) { - tx_info = IEEE80211_SKB_CB(p); - if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && - ((u8) (p->priority) == tid)) { - plen = p->len + AMPDU_MAX_MPDU_OVERHEAD; - plen = max(scb_ampdu->min_len, plen); + if (use_rts || use_cts) { + rts_rspec = + brcms_c_rspec_to_rts_rspec(wlc, rspec, + false, mimo_ctlchbw); + rts_rspec_fallback = + brcms_c_rspec_to_rts_rspec(wlc, rspec_fallback, + false, mimo_ctlchbw); + } - if ((plen + ampdu_len) > max_ampdu_bytes) { - p = NULL; - continue; - } + BRCMS_SET_MIMO_PLCP_LEN(plcp, session->ampdu_len); + /* mark plcp to indicate ampdu */ + BRCMS_SET_MIMO_PLCP_AMPDU(plcp); - /* - * check if there are enough - * descriptors available - */ - if (*wlc->core->txavail[fifo] <= seg_cnt + 1) { - wiphy_err(wiphy, "%s: No fifo space " - "!!\n", __func__); - p = NULL; - continue; - } - /* next packet fit for aggregation so dequeue */ - p = brcmu_pktq_pdeq(&qi->q, prec); - } else { - p = NULL; - } - } - } /* end while(p) */ + /* reset the mixed mode header durations */ + if (txh->MModeLen) { + u16 mmodelen = brcms_c_calc_lsig_len(wlc, rspec, + session->ampdu_len); + txh->MModeLen = cpu_to_le16(mmodelen); + preamble_type = BRCMS_MM_PREAMBLE; + } + if (txh->MModeFbrLen) { + u16 mmfbrlen = brcms_c_calc_lsig_len(wlc, rspec_fallback, + session->ampdu_len); + txh->MModeFbrLen = cpu_to_le16(mmfbrlen); + fbr_preamble_type = BRCMS_MM_PREAMBLE; + } - ini->tx_in_transit += count; + /* set the preload length */ + if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { + dma_len = min(dma_len, f->ampdu_pld_size); + txh->PreloadSize = cpu_to_le16(dma_len); + } else { + txh->PreloadSize = 0; + } - if (count) { - /* patch up the last txh */ - txh = (struct d11txh *) pkt[count - 1]->data; - mcl = le16_to_cpu(txh->MacTxControlLow); - mcl &= ~TXC_AMPDU_MASK; - mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT); - txh->MacTxControlLow = cpu_to_le16(mcl); - - /* remove the null delimiter after last mpdu */ - ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM]; - txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0; - ampdu_len -= ndelim * AMPDU_DELIMITER_LEN; - - /* remove the pad len from last mpdu */ - fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0); - len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback) - : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback); - ampdu_len -= roundup(len, 4) - len; - - /* patch up the first txh & plcp */ - txh = (struct d11txh *) pkt[0]->data; - plcp = (u8 *) (txh + 1); + mch = le16_to_cpu(txh->MacTxControlHigh); - BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len); - /* mark plcp to indicate ampdu */ - BRCMS_SET_MIMO_PLCP_AMPDU(plcp); + /* update RTS dur fields */ + if (use_rts || use_cts) { + u16 durid; + if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == + TXC_PREAMBLE_RTS_MAIN_SHORT) + rts_preamble_type = BRCMS_SHORT_PREAMBLE; - /* reset the mixed mode header durations */ - if (txh->MModeLen) { - u16 mmodelen = - brcms_c_calc_lsig_len(wlc, rspec, ampdu_len); - txh->MModeLen = cpu_to_le16(mmodelen); - preamble_type = BRCMS_MM_PREAMBLE; - } - if (txh->MModeFbrLen) { - u16 mmfbrlen = - brcms_c_calc_lsig_len(wlc, rspec_fallback, - ampdu_len); - txh->MModeFbrLen = cpu_to_le16(mmfbrlen); - fbr_preamble_type = BRCMS_MM_PREAMBLE; - } + if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == + TXC_PREAMBLE_RTS_FB_SHORT) + rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; - /* set the preload length */ - if (mcs_2_rate(mcs, true, false) >= f->dmaxferrate) { - dma_len = min(dma_len, f->ampdu_pld_size); - txh->PreloadSize = cpu_to_le16(dma_len); - } else - txh->PreloadSize = 0; - - mch = le16_to_cpu(txh->MacTxControlHigh); - - /* update RTS dur fields */ - if (use_rts || use_cts) { - u16 durid; - rts = (struct ieee80211_rts *)&txh->rts_frame; - if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) == - TXC_PREAMBLE_RTS_MAIN_SHORT) - rts_preamble_type = BRCMS_SHORT_PREAMBLE; - - if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) == - TXC_PREAMBLE_RTS_FB_SHORT) - rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE; - - durid = - brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec, rspec, rts_preamble_type, - preamble_type, ampdu_len, - true); - rts->duration = cpu_to_le16(durid); - durid = brcms_c_compute_rtscts_dur(wlc, use_cts, - rts_rspec_fallback, - rspec_fallback, - rts_fbr_preamble_type, - fbr_preamble_type, - ampdu_len, true); - txh->RTSDurFallback = cpu_to_le16(durid); - /* set TxFesTimeNormal */ - txh->TxFesTimeNormal = rts->duration; - /* set fallback rate version of TxFesTimeNormal */ - txh->TxFesTimeFallback = txh->RTSDurFallback; - } - - /* set flag and plcp for fallback rate */ - if (fbr) { - mch |= TXC_AMPDU_FBR; - txh->MacTxControlHigh = cpu_to_le16(mch); - BRCMS_SET_MIMO_PLCP_AMPDU(plcp); - BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); - } - - BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n", - wlc->pub->unit, count, ampdu_len); - - /* inform rate_sel if it this is a rate probe pkt */ - frameid = le16_to_cpu(txh->TxFrameID); - if (frameid & TXFID_RATE_PROBE_MASK) - wiphy_err(wiphy, "%s: XXX what to do with " - "TXFID_RATE_PROBE_MASK!?\n", __func__); - - for (i = 0; i < count; i++) - brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1), - ampdu->txpkt_weight); + preamble_type, + session->ampdu_len, true); + rts->duration = cpu_to_le16(durid); + durid = brcms_c_compute_rtscts_dur(wlc, use_cts, + rts_rspec_fallback, + rspec_fallback, + rts_fbr_preamble_type, + fbr_preamble_type, + session->ampdu_len, true); + txh->RTSDurFallback = cpu_to_le16(durid); + /* set TxFesTimeNormal */ + txh->TxFesTimeNormal = rts->duration; + /* set fallback rate version of TxFesTimeNormal */ + txh->TxFesTimeFallback = txh->RTSDurFallback; + } + /* set flag and plcp for fallback rate */ + if (fbr) { + mch |= TXC_AMPDU_FBR; + txh->MacTxControlHigh = cpu_to_le16(mch); + BRCMS_SET_MIMO_PLCP_AMPDU(plcp); + BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback); } - /* endif (count) */ - return err; + + brcms_dbg_ht(wlc->hw->d11core, "wl%d: count %d ampdu_len %d\n", + wlc->pub->unit, skb_queue_len(&session->skb_list), + session->ampdu_len); } static void @@ -909,7 +855,6 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, u8 antselid = 0; u8 retry_limit, rr_retry_limit; struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p); - struct wiphy *wiphy = wlc->wiphy; #ifdef DEBUG u8 hole[AMPDU_MAX_MPDU]; @@ -955,13 +900,14 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, if (supr_status) { update_rate = false; if (supr_status == TX_STATUS_SUPR_BADCH) { - wiphy_err(wiphy, + brcms_err(wlc->hw->d11core, "%s: Pkt tx suppressed, illegal channel possibly %d\n", __func__, CHSPEC_CHANNEL( wlc->default_bss->chanspec)); } else { if (supr_status != TX_STATUS_SUPR_FRAG) - wiphy_err(wiphy, "%s: supr_status 0x%x\n", + brcms_err(wlc->hw->d11core, + "%s: supr_status 0x%x\n", __func__, supr_status); } /* no need to retry for badch; will fail again */ @@ -977,20 +923,14 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, * if there were underflows, but pre-loading * is not active, notify rate adaptation. */ - if (brcms_c_ffpld_check_txfunfl(wlc, - prio2fifo[tid]) > 0) + if (brcms_c_ffpld_check_txfunfl(wlc, queue) > 0) tx_error = true; } } else if (txs->phyerr) { update_rate = false; - wiphy_err(wiphy, "%s: ampdu tx phy error (0x%x)\n", + brcms_err(wlc->hw->d11core, + "%s: ampdu tx phy error (0x%x)\n", __func__, txs->phyerr); - - if (brcm_msg_level & LOG_ERROR_VAL) { - brcmu_prpkt("txpkt (AMPDU)", p); - brcms_c_print_txdesc((struct d11txh *) p->data); - } - brcms_c_print_txstatus(txs); } } @@ -1003,6 +943,8 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN); seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT; + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); + if (tot_mpdu == 0) { mcs = plcp[0] & MIMO_PLCP_MCS_MASK; mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel); @@ -1012,10 +954,10 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, ack_recd = false; if (ba_recd) { bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX); - BCMMSG(wiphy, - "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", - tid, seq, start_seq, bindex, - isset(bitmap, bindex), index); + brcms_dbg_ht(wlc->hw->d11core, + "tid %d seq %d, start_seq %d, bindex %d set %d, index %d\n", + tid, seq, start_seq, bindex, + isset(bitmap, bindex), index); /* if acked then clear bit and free packet */ if ((bindex < AMPDU_TX_BA_MAX_WSIZE) && isset(bitmap, bindex)) { @@ -1046,14 +988,16 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, /* either retransmit or send bar if ack not recd */ if (!ack_recd) { if (retry && (ini->txretry[index] < (int)retry_limit)) { + int ret; ini->txretry[index]++; ini->tx_in_transit--; + ret = brcms_c_txfifo(wlc, queue, p); /* - * Use high prededence for retransmit to - * give some punch + * We shouldn't be out of space in the DMA + * ring here since we're reinserting a frame + * that was just pulled out. */ - brcms_c_txq_enq(wlc, scb, p, - BRCMS_PRIO_TO_HI_PREC(tid)); + WARN_ONCE(ret, "queue %d out of txds\n", queue); } else { /* Retry timeout */ ini->tx_in_transit--; @@ -1064,9 +1008,9 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, IEEE80211_TX_STAT_AMPDU_NO_BACK; skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); - BCMMSG(wiphy, - "BA Timeout, seq %d, in_transit %d\n", - seq, ini->tx_in_transit); + brcms_dbg_ht(wlc->hw->d11core, + "BA Timeout, seq %d, in_transit %d\n", + seq, ini->tx_in_transit); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); } @@ -1080,12 +1024,9 @@ brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb, p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); } - brcms_c_send_q(wlc); /* update rate state */ antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel); - - brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight); } void @@ -1133,6 +1074,8 @@ brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, while (p) { tx_info = IEEE80211_SKB_CB(p); txh = (struct d11txh *) p->data; + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, + sizeof(*txh)); mcl = le16_to_cpu(txh->MacTxControlLow); brcmu_pkt_buf_free_skb(p); /* break out if last packet of ampdu */ @@ -1142,7 +1085,6 @@ brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); } - brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight); } } @@ -1182,23 +1124,6 @@ void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu) } /* - * callback function that helps flushing ampdu packets from a priority queue - */ -static bool cb_del_ampdu_pkt(struct sk_buff *mpdu, void *arg_a) -{ - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(mpdu); - struct cb_del_ampdu_pars *ampdu_pars = - (struct cb_del_ampdu_pars *)arg_a; - bool rc; - - rc = tx_info->flags & IEEE80211_TX_CTL_AMPDU ? true : false; - rc = rc && (tx_info->rate_driver_data[0] == NULL || ampdu_pars->sta == NULL || - tx_info->rate_driver_data[0] == ampdu_pars->sta); - rc = rc && ((u8)(mpdu->priority) == ampdu_pars->tid); - return rc; -} - -/* * callback function that helps invalidating ampdu packets in a DMA queue */ static void dma_cb_fn_ampdu(void *txi, void *arg_a) @@ -1218,15 +1143,5 @@ static void dma_cb_fn_ampdu(void *txi, void *arg_a) void brcms_c_ampdu_flush(struct brcms_c_info *wlc, struct ieee80211_sta *sta, u16 tid) { - struct brcms_txq_info *qi = wlc->pkt_queue; - struct pktq *pq = &qi->q; - int prec; - struct cb_del_ampdu_pars ampdu_pars; - - ampdu_pars.sta = sta; - ampdu_pars.tid = tid; - for (prec = 0; prec < pq->num_prec; prec++) - brcmu_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt, - (void *)&du_pars); brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h index 421f4ba7c63..73d01e58610 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h @@ -17,11 +17,34 @@ #ifndef _BRCM_AMPDU_H_ #define _BRCM_AMPDU_H_ +/* + * Data structure representing an in-progress session for accumulating + * frames for AMPDU. + * + * wlc: pointer to common driver data + * skb_list: queue of skb's for AMPDU + * max_ampdu_len: maximum length for this AMPDU + * max_ampdu_frames: maximum number of frames for this AMPDU + * ampdu_len: total number of bytes accumulated for this AMPDU + * dma_len: DMA length of this AMPDU + */ +struct brcms_ampdu_session { + struct brcms_c_info *wlc; + struct sk_buff_head skb_list; + unsigned max_ampdu_len; + u16 max_ampdu_frames; + u16 ampdu_len; + u16 dma_len; +}; + +extern void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, + struct brcms_c_info *wlc); +extern int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, + struct sk_buff *p); +extern void brcms_c_ampdu_finalize(struct brcms_ampdu_session *session); + extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc); extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu); -extern int brcms_c_sendampdu(struct ampdu_info *ampdu, - struct brcms_txq_info *qi, - struct sk_buff **aggp, int prec); extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb, struct sk_buff *p, struct tx_status *txs); extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c index 55e12c32791..54c61691959 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c @@ -21,6 +21,7 @@ #include "main.h" #include "phy_shim.h" #include "antsel.h" +#include "debug.h" #define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */ #define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */ @@ -137,7 +138,8 @@ struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc) asi->antsel_avail = false; } else { asi->antsel_avail = false; - wiphy_err(wlc->wiphy, "antsel_attach: 2o3 " + brcms_err(wlc->hw->d11core, + "antsel_attach: 2o3 " "board cfg invalid\n"); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h index 27dd73eef56..871781e6a71 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/brcms_trace_events.h @@ -14,22 +14,29 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#undef TRACE_SYSTEM -#define TRACE_SYSTEM brcmsmac - #if !defined(__TRACE_BRCMSMAC_H) || defined(TRACE_HEADER_MULTI_READ) #define __TRACE_BRCMSMAC_H +#include <linux/types.h> +#include <linux/device.h> #include <linux/tracepoint.h> #include "mac80211_if.h" -#ifndef CONFIG_BRCMDBG +#ifndef CONFIG_BRCM_TRACING #undef TRACE_EVENT #define TRACE_EVENT(name, proto, ...) \ static inline void trace_ ## name(proto) {} +#undef DECLARE_EVENT_CLASS +#define DECLARE_EVENT_CLASS(...) +#undef DEFINE_EVENT +#define DEFINE_EVENT(evt_class, name, proto, ...) \ +static inline void trace_ ## name(proto) {} #endif +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac + /* * We define a tracepoint, its arguments, its printk format and its * 'fast binary record' layout. @@ -78,9 +85,165 @@ TRACE_EVENT(brcms_dpc, ) ); +TRACE_EVENT(brcms_macintstatus, + TP_PROTO(const struct device *dev, int in_isr, u32 macintstatus, + u32 mask), + TP_ARGS(dev, in_isr, macintstatus, mask), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(int, in_isr) + __field(u32, macintstatus) + __field(u32, mask) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->in_isr = in_isr; + __entry->macintstatus = macintstatus; + __entry->mask = mask; + ), + TP_printk("[%s] in_isr=%d macintstatus=%#x mask=%#x", __get_str(dev), + __entry->in_isr, __entry->macintstatus, __entry->mask) +); + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_tx + +TRACE_EVENT(brcms_txdesc, + TP_PROTO(const struct device *dev, + void *txh, size_t txh_len), + TP_ARGS(dev, txh, txh_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __dynamic_array(u8, txh, txh_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + memcpy(__get_dynamic_array(txh), txh, txh_len); + ), + TP_printk("[%s] txdesc", __get_str(dev)) +); + +TRACE_EVENT(brcms_txstatus, + TP_PROTO(const struct device *dev, u16 framelen, u16 frameid, + u16 status, u16 lasttxtime, u16 sequence, u16 phyerr, + u16 ackphyrxsh), + TP_ARGS(dev, framelen, frameid, status, lasttxtime, sequence, phyerr, + ackphyrxsh), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(u16, framelen) + __field(u16, frameid) + __field(u16, status) + __field(u16, lasttxtime) + __field(u16, sequence) + __field(u16, phyerr) + __field(u16, ackphyrxsh) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->framelen = framelen; + __entry->frameid = frameid; + __entry->status = status; + __entry->lasttxtime = lasttxtime; + __entry->sequence = sequence; + __entry->phyerr = phyerr; + __entry->ackphyrxsh = ackphyrxsh; + ), + TP_printk("[%s] FrameId %#04x TxStatus %#04x LastTxTime %#04x " + "Seq %#04x PHYTxStatus %#04x RxAck %#04x", + __get_str(dev), __entry->frameid, __entry->status, + __entry->lasttxtime, __entry->sequence, __entry->phyerr, + __entry->ackphyrxsh) +); + +TRACE_EVENT(brcms_ampdu_session, + TP_PROTO(const struct device *dev, unsigned max_ampdu_len, + u16 max_ampdu_frames, u16 ampdu_len, u16 ampdu_frames, + u16 dma_len), + TP_ARGS(dev, max_ampdu_len, max_ampdu_frames, ampdu_len, ampdu_frames, + dma_len), + TP_STRUCT__entry( + __string(dev, dev_name(dev)) + __field(unsigned, max_ampdu_len) + __field(u16, max_ampdu_frames) + __field(u16, ampdu_len) + __field(u16, ampdu_frames) + __field(u16, dma_len) + ), + TP_fast_assign( + __assign_str(dev, dev_name(dev)); + __entry->max_ampdu_len = max_ampdu_len; + __entry->max_ampdu_frames = max_ampdu_frames; + __entry->ampdu_len = ampdu_len; + __entry->ampdu_frames = ampdu_frames; + __entry->dma_len = dma_len; + ), + TP_printk("[%s] ampdu session max_len=%u max_frames=%u len=%u frames=%u dma_len=%u", + __get_str(dev), __entry->max_ampdu_len, + __entry->max_ampdu_frames, __entry->ampdu_len, + __entry->ampdu_frames, __entry->dma_len) +); + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM brcmsmac_msg + +#define MAX_MSG_LEN 100 + +DECLARE_EVENT_CLASS(brcms_msg_event, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf), + TP_STRUCT__entry( + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s", __get_str(msg)) +); + +DEFINE_EVENT(brcms_msg_event, brcms_info, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_warn, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_err, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +DEFINE_EVENT(brcms_msg_event, brcms_crit, + TP_PROTO(struct va_format *vaf), + TP_ARGS(vaf) +); + +TRACE_EVENT(brcms_dbg, + TP_PROTO(u32 level, const char *func, struct va_format *vaf), + TP_ARGS(level, func, vaf), + TP_STRUCT__entry( + __field(u32, level) + __string(func, func) + __dynamic_array(char, msg, MAX_MSG_LEN) + ), + TP_fast_assign( + __entry->level = level; + __assign_str(func, func); + WARN_ON_ONCE(vsnprintf(__get_dynamic_array(msg), + MAX_MSG_LEN, vaf->fmt, + *vaf->va) >= MAX_MSG_LEN); + ), + TP_printk("%s: %s", __get_str(func), __get_str(msg)) +); + #endif /* __TRACE_BRCMSMAC_H */ -#ifdef CONFIG_BRCMDBG +#ifdef CONFIG_BRCM_TRACING #undef TRACE_INCLUDE_PATH #define TRACE_INCLUDE_PATH . @@ -89,4 +252,4 @@ TRACE_EVENT(brcms_dpc, #include <trace/define_trace.h> -#endif /* CONFIG_BRCMDBG */ +#endif /* CONFIG_BRCM_TRACING */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c index 64a48f06d68..a90b72202ec 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c @@ -26,6 +26,7 @@ #include "stf.h" #include "channel.h" #include "mac80211_if.h" +#include "debug.h" /* QDB() macro takes a dB value and converts to a quarter dB value */ #define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR) @@ -336,8 +337,6 @@ struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc) const char *ccode = sprom->alpha2; int ccode_len = sizeof(sprom->alpha2); - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC); if (wlc_cm == NULL) return NULL; @@ -615,8 +614,8 @@ brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, u16 chspec) /* check the chanspec */ if (brcms_c_chspec_malformed(chspec)) { - wiphy_err(wlc->wiphy, "wl%d: malformed chanspec 0x%x\n", - wlc->pub->unit, chspec); + brcms_err(wlc->hw->d11core, "wl%d: malformed chanspec 0x%x\n", + wlc->pub->unit, chspec); return false; } @@ -738,7 +737,8 @@ static int brcms_reg_notifier(struct wiphy *wiphy, mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); } else { mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE); - wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\"\n", + brcms_err(wlc->hw->d11core, + "wl%d: %s: no valid channel for \"%s\"\n", wlc->pub->unit, __func__, request->alpha2); } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.c b/drivers/net/wireless/brcm80211/brcmsmac/debug.c new file mode 100644 index 00000000000..6ba4136c7cf --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.c @@ -0,0 +1,44 @@ +#include <linux/net.h> +#include "types.h" +#include "debug.h" +#include "brcms_trace_events.h" + +#define __brcms_fn(fn) \ +void __brcms_ ##fn(struct device *dev, const char *fmt, ...) \ +{ \ + struct va_format vaf = { \ + .fmt = fmt, \ + }; \ + va_list args; \ + \ + va_start(args, fmt); \ + vaf.va = &args; \ + dev_ ##fn(dev, "%pV", &vaf); \ + trace_brcms_ ##fn(&vaf); \ + va_end(args); \ +} + +__brcms_fn(info) +__brcms_fn(warn) +__brcms_fn(err) +__brcms_fn(crit) + +#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...) +{ + struct va_format vaf = { + .fmt = fmt, + }; + va_list args; + + va_start(args, fmt); + vaf.va = &args; +#ifdef CONFIG_BRCMDBG + if ((brcm_msg_level & level) && net_ratelimit()) + dev_err(dev, "%s %pV", func, &vaf); +#endif + trace_brcms_dbg(level, func, &vaf); + va_end(args); +} +#endif diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/brcm80211/brcmsmac/debug.h new file mode 100644 index 00000000000..f77066bda9d --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.h @@ -0,0 +1,52 @@ +#ifndef _BRCMS_DEBUG_H_ +#define _BRCMS_DEBUG_H_ + +#include <linux/device.h> +#include <linux/bcma/bcma.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> +#include "main.h" +#include "mac80211_if.h" + +__printf(2, 3) +void __brcms_info(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_warn(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_err(struct device *dev, const char *fmt, ...); +__printf(2, 3) +void __brcms_crit(struct device *dev, const char *fmt, ...); + +#if defined(CONFIG_BRCMDBG) || defined(CONFIG_BRCM_TRACING) +__printf(4, 5) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...); +#else +static inline __printf(4, 5) +void __brcms_dbg(struct device *dev, u32 level, const char *func, + const char *fmt, ...) +{ +} +#endif + +/* + * Debug macros cannot be used when wlc is uninitialized. Generally + * this means any code that could run before brcms_c_attach() has + * returned successfully probably shouldn't use the following macros. + */ + +#define brcms_dbg(core, l, f, a...) __brcms_dbg(&(core)->dev, l, __func__, f, ##a) +#define brcms_info(core, f, a...) __brcms_info(&(core)->dev, f, ##a) +#define brcms_warn(core, f, a...) __brcms_warn(&(core)->dev, f, ##a) +#define brcms_err(core, f, a...) __brcms_err(&(core)->dev, f, ##a) +#define brcms_crit(core, f, a...) __brcms_crit(&(core)->dev, f, ##a) + +#define brcms_dbg_info(core, f, a...) brcms_dbg(core, BRCM_DL_INFO, f, ##a) +#define brcms_dbg_mac80211(core, f, a...) brcms_dbg(core, BRCM_DL_MAC80211, f, ##a) +#define brcms_dbg_rx(core, f, a...) brcms_dbg(core, BRCM_DL_RX, f, ##a) +#define brcms_dbg_tx(core, f, a...) brcms_dbg(core, BRCM_DL_TX, f, ##a) +#define brcms_dbg_int(core, f, a...) brcms_dbg(core, BRCM_DL_INT, f, ##a) +#define brcms_dbg_dma(core, f, a...) brcms_dbg(core, BRCM_DL_DMA, f, ##a) +#define brcms_dbg_ht(core, f, a...) brcms_dbg(core, BRCM_DL_HT, f, ##a) + +#endif /* _BRCMS_DEBUG_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c index 5e53305bd9a..511e45775c3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c @@ -14,17 +14,22 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #include <linux/slab.h> #include <linux/delay.h> #include <linux/pci.h> +#include <net/cfg80211.h> +#include <net/mac80211.h> #include <brcmu_utils.h> #include <aiutils.h> #include "types.h" +#include "main.h" #include "dma.h" #include "soc.h" +#include "scb.h" +#include "ampdu.h" +#include "debug.h" +#include "brcms_trace_events.h" /* * dma register field offset calculation @@ -176,28 +181,6 @@ #define BCMEXTRAHDROOM 172 -/* debug/trace */ -#ifdef DEBUG -#define DMA_ERROR(fmt, ...) \ -do { \ - if (*di->msg_level & 1) \ - pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ -} while (0) -#define DMA_TRACE(fmt, ...) \ -do { \ - if (*di->msg_level & 2) \ - pr_debug("%s: " fmt, __func__, ##__VA_ARGS__); \ -} while (0) -#else -#define DMA_ERROR(fmt, ...) \ - no_printk(fmt, ##__VA_ARGS__) -#define DMA_TRACE(fmt, ...) \ - no_printk(fmt, ##__VA_ARGS__) -#endif /* DEBUG */ - -#define DMA_NONE(fmt, ...) \ - no_printk(fmt, ##__VA_ARGS__) - #define MAXNAMEL 8 /* 8 char names */ /* macros to convert between byte offsets and indexes */ @@ -224,12 +207,14 @@ struct dma64desc { /* dma engine software state */ struct dma_info { struct dma_pub dma; /* exported structure */ - uint *msg_level; /* message level pointer */ char name[MAXNAMEL]; /* callers name for diag msgs */ struct bcma_device *core; struct device *dmadev; + /* session information for AMPDU */ + struct brcms_ampdu_session ampdu_session; + bool dma64; /* this dma engine is operating in 64-bit mode */ bool addrext; /* this dma engine supports DmaExtendedAddrChanges */ @@ -298,12 +283,6 @@ struct dma_info { bool aligndesc_4k; }; -/* - * default dma message level (if input msg_level - * pointer is null in dma_attach()) - */ -static uint dma_msg_level; - /* Check for odd number of 1's */ static u32 parity32(__le32 data) { @@ -353,7 +332,7 @@ static uint prevtxd(struct dma_info *di, uint i) static uint nextrxd(struct dma_info *di, uint i) { - return txd(di, i + 1); + return rxd(di, i + 1); } static uint ntxdactive(struct dma_info *di, uint h, uint t) @@ -371,7 +350,7 @@ static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags) uint dmactrlflags; if (di == NULL) { - DMA_ERROR("NULL dma handle\n"); + brcms_dbg_dma(di->core, "NULL dma handle\n"); return 0; } @@ -423,13 +402,15 @@ static bool _dma_isaddrext(struct dma_info *di) /* not all tx or rx channel are available */ if (di->d64txregbase != 0) { if (!_dma64_addrext(di, DMA64TXREGOFFS(di, control))) - DMA_ERROR("%s: DMA64 tx doesn't have AE set\n", - di->name); + brcms_dbg_dma(di->core, + "%s: DMA64 tx doesn't have AE set\n", + di->name); return true; } else if (di->d64rxregbase != 0) { if (!_dma64_addrext(di, DMA64RXREGOFFS(di, control))) - DMA_ERROR("%s: DMA64 rx doesn't have AE set\n", - di->name); + brcms_dbg_dma(di->core, + "%s: DMA64 rx doesn't have AE set\n", + di->name); return true; } @@ -530,8 +511,9 @@ static bool dma64_alloc(struct dma_info *di, uint direction) va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, &alloced, &di->txdpaorig); if (va == NULL) { - DMA_ERROR("%s: DMA_ALLOC_CONSISTENT(ntxd) failed\n", - di->name); + brcms_dbg_dma(di->core, + "%s: DMA_ALLOC_CONSISTENT(ntxd) failed\n", + di->name); return false; } align = (1 << align_bits); @@ -544,8 +526,9 @@ static bool dma64_alloc(struct dma_info *di, uint direction) va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits, &alloced, &di->rxdpaorig); if (va == NULL) { - DMA_ERROR("%s: DMA_ALLOC_CONSISTENT(nrxd) failed\n", - di->name); + brcms_dbg_dma(di->core, + "%s: DMA_ALLOC_CONSISTENT(nrxd) failed\n", + di->name); return false; } align = (1 << align_bits); @@ -564,12 +547,13 @@ static bool _dma_alloc(struct dma_info *di, uint direction) return dma64_alloc(di, direction); } -struct dma_pub *dma_attach(char *name, struct si_pub *sih, - struct bcma_device *core, +struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, uint txregbase, uint rxregbase, uint ntxd, uint nrxd, uint rxbufsize, int rxextheadroom, - uint nrxpost, uint rxoffset, uint *msg_level) + uint nrxpost, uint rxoffset) { + struct si_pub *sih = wlc->hw->sih; + struct bcma_device *core = wlc->hw->d11core; struct dma_info *di; u8 rev = core->id.rev; uint size; @@ -580,9 +564,6 @@ struct dma_pub *dma_attach(char *name, struct si_pub *sih, if (di == NULL) return NULL; - di->msg_level = msg_level ? msg_level : &dma_msg_level; - - di->dma64 = ((bcma_aread32(core, BCMA_IOST) & SISF_DMA64) == SISF_DMA64); @@ -598,11 +579,11 @@ struct dma_pub *dma_attach(char *name, struct si_pub *sih, */ _dma_ctrlflags(di, DMA_CTRL_ROC | DMA_CTRL_PEN, 0); - DMA_TRACE("%s: %s flags 0x%x ntxd %d nrxd %d " - "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d " - "txregbase %u rxregbase %u\n", name, "DMA64", - di->dma.dmactrlflags, ntxd, nrxd, rxbufsize, - rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); + brcms_dbg_dma(di->core, "%s: %s flags 0x%x ntxd %d nrxd %d " + "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d " + "txregbase %u rxregbase %u\n", name, "DMA64", + di->dma.dmactrlflags, ntxd, nrxd, rxbufsize, + rxextheadroom, nrxpost, rxoffset, txregbase, rxregbase); /* make a private copy of our callers name */ strncpy(di->name, name, MAXNAMEL); @@ -664,8 +645,8 @@ struct dma_pub *dma_attach(char *name, struct si_pub *sih, di->dmadesc_align = 4; /* 16 byte alignment */ } - DMA_NONE("DMA descriptor align_needed %d, align %d\n", - di->aligndesc_4k, di->dmadesc_align); + brcms_dbg_dma(di->core, "DMA descriptor align_needed %d, align %d\n", + di->aligndesc_4k, di->dmadesc_align); /* allocate tx packet pointer vector */ if (ntxd) { @@ -703,21 +684,27 @@ struct dma_pub *dma_attach(char *name, struct si_pub *sih, if ((di->ddoffsetlow != 0) && !di->addrext) { if (di->txdpa > SI_PCI_DMA_SZ) { - DMA_ERROR("%s: txdpa 0x%x: addrext not supported\n", - di->name, (u32)di->txdpa); + brcms_dbg_dma(di->core, + "%s: txdpa 0x%x: addrext not supported\n", + di->name, (u32)di->txdpa); goto fail; } if (di->rxdpa > SI_PCI_DMA_SZ) { - DMA_ERROR("%s: rxdpa 0x%x: addrext not supported\n", - di->name, (u32)di->rxdpa); + brcms_dbg_dma(di->core, + "%s: rxdpa 0x%x: addrext not supported\n", + di->name, (u32)di->rxdpa); goto fail; } } - DMA_TRACE("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n", - di->ddoffsetlow, di->ddoffsethigh, - di->dataoffsetlow, di->dataoffsethigh, - di->addrext); + /* Initialize AMPDU session */ + brcms_c_ampdu_reset_session(&di->ampdu_session, wlc); + + brcms_dbg_dma(di->core, + "ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh 0x%x addrext %d\n", + di->ddoffsetlow, di->ddoffsethigh, + di->dataoffsetlow, di->dataoffsethigh, + di->addrext); return (struct dma_pub *) di; @@ -763,7 +750,7 @@ void dma_detach(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); /* free dma descriptor rings */ if (di->txd64) @@ -839,7 +826,7 @@ static void _dma_rxenable(struct dma_info *di) uint dmactrlflags = di->dma.dmactrlflags; u32 control; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); control = D64_RC_RE | (bcma_read32(di->core, DMA64RXREGOFFS(di, control)) & @@ -859,7 +846,7 @@ void dma_rxinit(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); if (di->nrxd == 0) return; @@ -954,7 +941,7 @@ int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) return 0; len = le16_to_cpu(*(__le16 *) (p->data)); - DMA_TRACE("%s: dma_rx len %d\n", di->name, len); + brcms_dbg_dma(di->core, "%s: dma_rx len %d\n", di->name, len); dma_spin_for_len(len, p); /* set actual length */ @@ -981,14 +968,15 @@ int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) DMA64RXREGOFFS(di, status0)) & D64_RS0_CD_MASK) - di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc); - DMA_ERROR("rxin %d rxout %d, hw_curr %d\n", - di->rxin, di->rxout, cur); + brcms_dbg_dma(di->core, + "rxin %d rxout %d, hw_curr %d\n", + di->rxin, di->rxout, cur); } #endif /* DEBUG */ if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) { - DMA_ERROR("%s: bad frame length (%d)\n", - di->name, len); + brcms_dbg_dma(di->core, "%s: bad frame length (%d)\n", + di->name, len); skb_queue_walk_safe(&dma_frames, p, next) { skb_unlink(p, &dma_frames); brcmu_pkt_buf_free_skb(p); @@ -1005,7 +993,7 @@ int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list) static bool dma64_rxidle(struct dma_info *di) { - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); if (di->nrxd == 0) return true; @@ -1016,6 +1004,17 @@ static bool dma64_rxidle(struct dma_info *di) D64_RS0_CD_MASK)); } +static bool dma64_txidle(struct dma_info *di) +{ + if (di->ntxd == 0) + return true; + + return ((bcma_read32(di->core, + DMA64TXREGOFFS(di, status0)) & D64_XS0_CD_MASK) == + (bcma_read32(di->core, DMA64TXREGOFFS(di, ptr)) & + D64_XS0_CD_MASK)); +} + /* * post receive buffers * return false is refill failed completely and ring is empty this will stall @@ -1047,7 +1046,7 @@ bool dma_rxfill(struct dma_pub *pub) n = di->nrxpost - nrxdactive(di, rxin, rxout); - DMA_TRACE("%s: post %d\n", di->name, n); + brcms_dbg_dma(di->core, "%s: post %d\n", di->name, n); if (di->rxbufsize > BCMEXTRAHDROOM) extra_offset = di->rxextrahdrroom; @@ -1060,9 +1059,11 @@ bool dma_rxfill(struct dma_pub *pub) p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset); if (p == NULL) { - DMA_ERROR("%s: out of rxbufs\n", di->name); + brcms_dbg_dma(di->core, "%s: out of rxbufs\n", + di->name); if (i == 0 && dma64_rxidle(di)) { - DMA_ERROR("%s: ring is empty !\n", di->name); + brcms_dbg_dma(di->core, "%s: ring is empty !\n", + di->name); ring_empty = true; } di->dma.rxnobuf++; @@ -1107,7 +1108,7 @@ void dma_rxreclaim(struct dma_pub *pub) struct dma_info *di = (struct dma_info *)pub; struct sk_buff *p; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); while ((p = _dma_getnextrxp(di, true))) brcmu_pkt_buf_free_skb(p); @@ -1138,7 +1139,7 @@ void dma_txinit(struct dma_pub *pub) struct dma_info *di = (struct dma_info *)pub; u32 control = D64_XC_XE; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); if (di->ntxd == 0) return; @@ -1170,7 +1171,7 @@ void dma_txsuspend(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); if (di->ntxd == 0) return; @@ -1182,7 +1183,7 @@ void dma_txresume(struct dma_pub *pub) { struct dma_info *di = (struct dma_info *)pub; - DMA_TRACE("%s:\n", di->name); + brcms_dbg_dma(di->core, "%s:\n", di->name); if (di->ntxd == 0) return; @@ -1205,11 +1206,11 @@ void dma_txreclaim(struct dma_pub *pub, enum txd_range range) struct dma_info *di = (struct dma_info *)pub; struct sk_buff *p; - DMA_TRACE("%s: %s\n", - di->name, - range == DMA_RANGE_ALL ? "all" : - range == DMA_RANGE_TRANSMITTED ? "transmitted" : - "transferred"); + brcms_dbg_dma(di->core, "%s: %s\n", + di->name, + range == DMA_RANGE_ALL ? "all" : + range == DMA_RANGE_TRANSMITTED ? "transmitted" : + "transferred"); if (di->txin == di->txout) return; @@ -1264,39 +1265,25 @@ bool dma_rxreset(struct dma_pub *pub) return status == D64_RS0_RS_DISABLED; } -/* - * !! tx entry routine - * WARNING: call must check the return value for error. - * the error(toss frames) could be fatal and cause many subsequent hard - * to debug problems - */ -int dma_txfast(struct dma_pub *pub, struct sk_buff *p, bool commit) +static void dma_txenq(struct dma_info *di, struct sk_buff *p) { - struct dma_info *di = (struct dma_info *)pub; unsigned char *data; uint len; u16 txout; u32 flags = 0; dma_addr_t pa; - DMA_TRACE("%s:\n", di->name); - txout = di->txout; + if (WARN_ON(nexttxd(di, txout) == di->txin)) + return; + /* * obtain and initialize transmit descriptor entry. */ data = p->data; len = p->len; - /* no use to transmit a zero length packet */ - if (len == 0) - return 0; - - /* return nonzero if out of tx descriptors */ - if (nexttxd(di, txout) == di->txin) - goto outoftxd; - /* get physical address of buffer start */ pa = dma_map_single(di->dmadev, data, len, DMA_TO_DEVICE); @@ -1318,23 +1305,147 @@ int dma_txfast(struct dma_pub *pub, struct sk_buff *p, bool commit) /* bump the tx descriptor index */ di->txout = txout; +} - /* kick the chip */ - if (commit) - bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), - di->xmtptrbase + I2B(txout, struct dma64desc)); +static void ampdu_finalize(struct dma_info *di) +{ + struct brcms_ampdu_session *session = &di->ampdu_session; + struct sk_buff *p; + + trace_brcms_ampdu_session(&session->wlc->hw->d11core->dev, + session->max_ampdu_len, + session->max_ampdu_frames, + session->ampdu_len, + skb_queue_len(&session->skb_list), + session->dma_len); + + if (WARN_ON(skb_queue_empty(&session->skb_list))) + return; + + brcms_c_ampdu_finalize(session); + + while (!skb_queue_empty(&session->skb_list)) { + p = skb_dequeue(&session->skb_list); + dma_txenq(di, p); + } + + bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), + di->xmtptrbase + I2B(di->txout, struct dma64desc)); + brcms_c_ampdu_reset_session(session, session->wlc); +} + +static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p) +{ + struct brcms_ampdu_session *session = &di->ampdu_session; + int ret; + + ret = brcms_c_ampdu_add_frame(session, p); + if (ret == -ENOSPC) { + /* + * AMPDU cannot accomodate this frame. Close out the in- + * progress AMPDU session and start a new one. + */ + ampdu_finalize(di); + ret = brcms_c_ampdu_add_frame(session, p); + } + + WARN_ON(ret); +} + +/* Update count of available tx descriptors based on current DMA state */ +static void dma_update_txavail(struct dma_info *di) +{ + /* + * Available space is number of descriptors less the number of + * active descriptors and the number of queued AMPDU frames. + */ + di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - + skb_queue_len(&di->ampdu_session.skb_list) - 1; +} + +/* + * !! tx entry routine + * WARNING: call must check the return value for error. + * the error(toss frames) could be fatal and cause many subsequent hard + * to debug problems + */ +int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, + struct sk_buff *p) +{ + struct dma_info *di = (struct dma_info *)pub; + struct brcms_ampdu_session *session = &di->ampdu_session; + struct ieee80211_tx_info *tx_info; + bool is_ampdu; + + /* no use to transmit a zero length packet */ + if (p->len == 0) + return 0; + + /* return nonzero if out of tx descriptors */ + if (di->dma.txavail == 0 || nexttxd(di, di->txout) == di->txin) + goto outoftxd; + + tx_info = IEEE80211_SKB_CB(p); + is_ampdu = tx_info->flags & IEEE80211_TX_CTL_AMPDU; + if (is_ampdu) + prep_ampdu_frame(di, p); + else + dma_txenq(di, p); /* tx flow control */ - di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1; + dma_update_txavail(di); + + /* kick the chip */ + if (is_ampdu) { + /* + * Start sending data if we've got a full AMPDU, there's + * no more space in the DMA ring, or the ring isn't + * currently transmitting. + */ + if (skb_queue_len(&session->skb_list) == session->max_ampdu_frames || + di->dma.txavail == 0 || dma64_txidle(di)) + ampdu_finalize(di); + } else { + bcma_write32(di->core, DMA64TXREGOFFS(di, ptr), + di->xmtptrbase + I2B(di->txout, struct dma64desc)); + } return 0; outoftxd: - DMA_ERROR("%s: out of txds !!!\n", di->name); + brcms_dbg_dma(di->core, "%s: out of txds !!!\n", di->name); brcmu_pkt_buf_free_skb(p); di->dma.txavail = 0; di->dma.txnobuf++; - return -1; + return -ENOSPC; +} + +void dma_txflush(struct dma_pub *pub) +{ + struct dma_info *di = (struct dma_info *)pub; + struct brcms_ampdu_session *session = &di->ampdu_session; + + if (!skb_queue_empty(&session->skb_list)) + ampdu_finalize(di); +} + +int dma_txpending(struct dma_pub *pub) +{ + struct dma_info *di = (struct dma_info *)pub; + return ntxdactive(di, di->txin, di->txout); +} + +/* + * If we have an active AMPDU session and are not transmitting, + * this function will force tx to start. + */ +void dma_kick_tx(struct dma_pub *pub) +{ + struct dma_info *di = (struct dma_info *)pub; + struct brcms_ampdu_session *session = &di->ampdu_session; + + if (!skb_queue_empty(&session->skb_list) && dma64_txidle(di)) + ampdu_finalize(di); } /* @@ -1354,11 +1465,11 @@ struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range) u16 active_desc; struct sk_buff *txp; - DMA_TRACE("%s: %s\n", - di->name, - range == DMA_RANGE_ALL ? "all" : - range == DMA_RANGE_TRANSMITTED ? "transmitted" : - "transferred"); + brcms_dbg_dma(di->core, "%s: %s\n", + di->name, + range == DMA_RANGE_ALL ? "all" : + range == DMA_RANGE_TRANSMITTED ? "transmitted" : + "transferred"); if (di->ntxd == 0) return NULL; @@ -1412,13 +1523,13 @@ struct sk_buff *dma_getnexttxp(struct dma_pub *pub, enum txd_range range) di->txin = i; /* tx flow control */ - di->dma.txavail = di->ntxd - ntxdactive(di, di->txin, di->txout) - 1; + dma_update_txavail(di); return txp; bogus: - DMA_NONE("bogus curr: start %d end %d txout %d\n", - start, end, di->txout); + brcms_dbg_dma(di->core, "bogus curr: start %d end %d txout %d\n", + start, end, di->txout); return NULL; } diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/brcm80211/brcmsmac/dma.h index cc269ee5c49..ff5b80b0904 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/dma.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.h @@ -74,12 +74,11 @@ struct dma_pub { uint txnobuf; /* tx out of dma descriptors */ }; -extern struct dma_pub *dma_attach(char *name, struct si_pub *sih, - struct bcma_device *d11core, +extern struct dma_pub *dma_attach(char *name, struct brcms_c_info *wlc, uint txregbase, uint rxregbase, uint ntxd, uint nrxd, uint rxbufsize, int rxextheadroom, - uint nrxpost, uint rxoffset, uint *msg_level); + uint nrxpost, uint rxoffset); void dma_rxinit(struct dma_pub *pub); int dma_rx(struct dma_pub *pub, struct sk_buff_head *skb_list); @@ -87,7 +86,11 @@ bool dma_rxfill(struct dma_pub *pub); bool dma_rxreset(struct dma_pub *pub); bool dma_txreset(struct dma_pub *pub); void dma_txinit(struct dma_pub *pub); -int dma_txfast(struct dma_pub *pub, struct sk_buff *p0, bool commit); +int dma_txfast(struct brcms_c_info *wlc, struct dma_pub *pub, + struct sk_buff *p0); +void dma_txflush(struct dma_pub *pub); +int dma_txpending(struct dma_pub *pub); +void dma_kick_tx(struct dma_pub *pub); void dma_txsuspend(struct dma_pub *pub); bool dma_txsuspended(struct dma_pub *pub); void dma_txresume(struct dma_pub *pub); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c index a744ea5a955..1710ccba8ba 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c @@ -33,6 +33,7 @@ #include "ucode_loader.h" #include "mac80211_if.h" #include "main.h" +#include "debug.h" #define N_TX_QUEUES 4 /* #tx queues on mac80211<->driver interface */ @@ -98,10 +99,14 @@ static struct bcma_device_id brcms_coreid_table[] = { }; MODULE_DEVICE_TABLE(bcma, brcms_coreid_table); -#ifdef DEBUG -static int msglevel = 0xdeadbeef; -module_param(msglevel, int, 0); -#endif /* DEBUG */ +#if defined(CONFIG_BRCMDBG) +/* + * Module parameter for setting the debug message level. Available + * flags are specified by the BRCM_DL_* macros in + * drivers/net/wireless/brcm80211/include/defs.h. + */ +module_param_named(debug, brcm_msg_level, uint, S_IRUGO | S_IWUSR); +#endif static struct ieee80211_channel brcms_2ghz_chantable[] = { CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS), @@ -276,7 +281,7 @@ static void brcms_ops_tx(struct ieee80211_hw *hw, spin_lock_bh(&wl->lock); if (!wl->pub->up) { - wiphy_err(wl->wiphy, "ops->tx called while down\n"); + brcms_err(wl->wlc->hw->d11core, "ops->tx called while down\n"); kfree_skb(skb); goto done; } @@ -313,8 +318,8 @@ static int brcms_ops_start(struct ieee80211_hw *hw) spin_unlock_bh(&wl->lock); if (err != 0) - wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__, - err); + brcms_err(wl->wlc->hw->d11core, "%s: brcms_up() returned %d\n", + __func__, err); return err; } @@ -332,7 +337,7 @@ static void brcms_ops_stop(struct ieee80211_hw *hw) status = brcms_c_chipmatch(wl->wlc->hw->d11core); spin_unlock_bh(&wl->lock); if (!status) { - wiphy_err(wl->wiphy, + brcms_err(wl->wlc->hw->d11core, "wl: brcms_ops_stop: chipmatch failed\n"); return; } @@ -350,8 +355,9 @@ brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) /* Just STA for now */ if (vif->type != NL80211_IFTYPE_STATION) { - wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only" - " STA for now\n", __func__, vif->type); + brcms_err(wl->wlc->hw->d11core, + "%s: Attempt to add type %d, only STA for now\n", + __func__, vif->type); return -EOPNOTSUPP; } @@ -370,9 +376,9 @@ static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) { struct ieee80211_conf *conf = &hw->conf; struct brcms_info *wl = hw->priv; + struct bcma_device *core = wl->wlc->hw->d11core; int err = 0; int new_int; - struct wiphy *wiphy = hw->wiphy; spin_lock_bh(&wl->lock); if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) { @@ -380,25 +386,26 @@ static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed) conf->listen_interval); } if (changed & IEEE80211_CONF_CHANGE_MONITOR) - wiphy_dbg(wiphy, "%s: change monitor mode: %s\n", - __func__, conf->flags & IEEE80211_CONF_MONITOR ? - "true" : "false"); + brcms_dbg_info(core, "%s: change monitor mode: %s\n", + __func__, conf->flags & IEEE80211_CONF_MONITOR ? + "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_PS) - wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n", + brcms_err(core, "%s: change power-save mode: %s (implement)\n", __func__, conf->flags & IEEE80211_CONF_PS ? "true" : "false"); if (changed & IEEE80211_CONF_CHANGE_POWER) { err = brcms_c_set_tx_power(wl->wlc, conf->power_level); if (err < 0) { - wiphy_err(wiphy, "%s: Error setting power_level\n", + brcms_err(core, "%s: Error setting power_level\n", __func__); goto config_out; } new_int = brcms_c_get_tx_power(wl->wlc); if (new_int != conf->power_level) - wiphy_err(wiphy, "%s: Power level req != actual, %d %d" - "\n", __func__, conf->power_level, + brcms_err(core, + "%s: Power level req != actual, %d %d\n", + __func__, conf->power_level, new_int); } if (changed & IEEE80211_CONF_CHANGE_CHANNEL) { @@ -425,13 +432,13 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, struct ieee80211_bss_conf *info, u32 changed) { struct brcms_info *wl = hw->priv; - struct wiphy *wiphy = hw->wiphy; + struct bcma_device *core = wl->wlc->hw->d11core; if (changed & BSS_CHANGED_ASSOC) { /* association status changed (associated/disassociated) * also implies a change in the AID. */ - wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME, + brcms_err(core, "%s: %s: %sassociated\n", KBUILD_MODNAME, __func__, info->assoc ? "" : "dis"); spin_lock_bh(&wl->lock); brcms_c_associate_upd(wl->wlc, info->assoc); @@ -491,7 +498,7 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, error = brcms_c_set_rateset(wl->wlc, &rs); spin_unlock_bh(&wl->lock); if (error) - wiphy_err(wiphy, "changing basic rates failed: %d\n", + brcms_err(core, "changing basic rates failed: %d\n", error); } if (changed & BSS_CHANGED_BEACON_INT) { @@ -508,30 +515,30 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_BEACON) /* Beacon data changed, retrieve new beacon (beaconing modes) */ - wiphy_err(wiphy, "%s: beacon changed\n", __func__); + brcms_err(core, "%s: beacon changed\n", __func__); if (changed & BSS_CHANGED_BEACON_ENABLED) { /* Beaconing should be enabled/disabled (beaconing modes) */ - wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__, + brcms_err(core, "%s: Beacon enabled: %s\n", __func__, info->enable_beacon ? "true" : "false"); } if (changed & BSS_CHANGED_CQM) { /* Connection quality monitor config changed */ - wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d " + brcms_err(core, "%s: cqm change: threshold %d, hys %d " " (implement)\n", __func__, info->cqm_rssi_thold, info->cqm_rssi_hyst); } if (changed & BSS_CHANGED_IBSS) { /* IBSS join status changed */ - wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__, - info->ibss_joined ? "true" : "false"); + brcms_err(core, "%s: IBSS joined: %s (implement)\n", + __func__, info->ibss_joined ? "true" : "false"); } if (changed & BSS_CHANGED_ARP_FILTER) { /* Hardware ARP filter address list or state changed */ - wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d" + brcms_err(core, "%s: arp filtering: enabled %s, count %d" " (implement)\n", __func__, info->arp_filter_enabled ? "true" : "false", info->arp_addr_cnt); } @@ -541,8 +548,8 @@ brcms_ops_bss_info_changed(struct ieee80211_hw *hw, * QoS for this association was enabled/disabled. * Note that it is only ever disabled for station mode. */ - wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__, - info->qos ? "true" : "false"); + brcms_err(core, "%s: qos enabled: %s (implement)\n", + __func__, info->qos ? "true" : "false"); } return; } @@ -553,25 +560,25 @@ brcms_ops_configure_filter(struct ieee80211_hw *hw, unsigned int *total_flags, u64 multicast) { struct brcms_info *wl = hw->priv; - struct wiphy *wiphy = hw->wiphy; + struct bcma_device *core = wl->wlc->hw->d11core; changed_flags &= MAC_FILTERS; *total_flags &= MAC_FILTERS; if (changed_flags & FIF_PROMISC_IN_BSS) - wiphy_dbg(wiphy, "FIF_PROMISC_IN_BSS\n"); + brcms_dbg_info(core, "FIF_PROMISC_IN_BSS\n"); if (changed_flags & FIF_ALLMULTI) - wiphy_dbg(wiphy, "FIF_ALLMULTI\n"); + brcms_dbg_info(core, "FIF_ALLMULTI\n"); if (changed_flags & FIF_FCSFAIL) - wiphy_dbg(wiphy, "FIF_FCSFAIL\n"); + brcms_dbg_info(core, "FIF_FCSFAIL\n"); if (changed_flags & FIF_CONTROL) - wiphy_dbg(wiphy, "FIF_CONTROL\n"); + brcms_dbg_info(core, "FIF_CONTROL\n"); if (changed_flags & FIF_OTHER_BSS) - wiphy_dbg(wiphy, "FIF_OTHER_BSS\n"); + brcms_dbg_info(core, "FIF_OTHER_BSS\n"); if (changed_flags & FIF_PSPOLL) - wiphy_dbg(wiphy, "FIF_PSPOLL\n"); + brcms_dbg_info(core, "FIF_PSPOLL\n"); if (changed_flags & FIF_BCN_PRBRESP_PROMISC) - wiphy_dbg(wiphy, "FIF_BCN_PRBRESP_PROMISC\n"); + brcms_dbg_info(core, "FIF_BCN_PRBRESP_PROMISC\n"); spin_lock_bh(&wl->lock); brcms_c_mac_promisc(wl->wlc, *total_flags); @@ -653,8 +660,8 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, status = brcms_c_aggregatable(wl->wlc, tid); spin_unlock_bh(&wl->lock); if (!status) { - wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n", - tid); + brcms_err(wl->wlc->hw->d11core, + "START: tid %d is not agg\'able\n", tid); return -EINVAL; } ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); @@ -681,8 +688,8 @@ brcms_ops_ampdu_action(struct ieee80211_hw *hw, /* Power save wakeup */ break; default: - wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n", - __func__); + brcms_err(wl->wlc->hw->d11core, + "%s: Invalid command, ignoring\n", __func__); } return 0; @@ -1144,14 +1151,13 @@ static int brcms_suspend(struct bcma_device *pdev) wl->pub->hw_up = false; spin_unlock_bh(&wl->lock); - pr_debug("brcms_suspend ok\n"); + brcms_dbg_info(wl->wlc->hw->d11core, "brcms_suspend ok\n"); return 0; } static int brcms_resume(struct bcma_device *pdev) { - pr_debug("brcms_resume ok\n"); return 0; } @@ -1184,10 +1190,6 @@ static DECLARE_WORK(brcms_driver_work, brcms_driver_init); static int __init brcms_module_init(void) { -#ifdef DEBUG - if (msglevel != 0xdeadbeef) - brcm_msg_level = msglevel; -#endif if (!schedule_work(&brcms_driver_work)) return -EBUSY; @@ -1216,7 +1218,7 @@ module_exit(brcms_module_exit); void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, bool state, int prio) { - wiphy_err(wl->wiphy, "Shouldn't be here %s\n", __func__); + brcms_err(wl->wlc->hw->d11core, "Shouldn't be here %s\n", __func__); } /* @@ -1224,7 +1226,8 @@ void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif, */ void brcms_init(struct brcms_info *wl) { - BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit); + brcms_dbg_info(wl->wlc->hw->d11core, "Initializing wl%d\n", + wl->pub->unit); brcms_reset(wl); brcms_c_init(wl->wlc, wl->mute_tx); } @@ -1234,7 +1237,7 @@ void brcms_init(struct brcms_info *wl) */ uint brcms_reset(struct brcms_info *wl) { - BCMMSG(wl->pub->ieee_hw->wiphy, "wl%d\n", wl->pub->unit); + brcms_dbg_info(wl->wlc->hw->d11core, "Resetting wl%d\n", wl->pub->unit); brcms_c_reset(wl->wlc); /* dpc will not be rescheduled */ @@ -1248,7 +1251,7 @@ uint brcms_reset(struct brcms_info *wl) void brcms_fatal_error(struct brcms_info *wl) { - wiphy_err(wl->wlc->wiphy, "wl%d: fatal error, reinitializing\n", + brcms_err(wl->wlc->hw->d11core, "wl%d: fatal error, reinitializing\n", wl->wlc->pub->unit); brcms_reset(wl); ieee80211_restart_hw(wl->pub->ieee_hw); @@ -1396,8 +1399,9 @@ void brcms_add_timer(struct brcms_timer *t, uint ms, int periodic) #ifdef DEBUG if (t->set) - wiphy_err(hw->wiphy, "%s: Already set. Name: %s, per %d\n", - __func__, t->name, periodic); + brcms_dbg_info(t->wl->wlc->hw->d11core, + "%s: Already set. Name: %s, per %d\n", + __func__, t->name, periodic); #endif t->ms = ms; t->periodic = (bool) periodic; @@ -1486,8 +1490,8 @@ int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx) } } } - wiphy_err(wl->wiphy, "ERROR: ucode buf tag:%d can not be found!\n", - idx); + brcms_err(wl->wlc->hw->d11core, + "ERROR: ucode buf tag:%d can not be found!\n", idx); *pbuf = NULL; fail: return -ENODATA; @@ -1510,7 +1514,7 @@ int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) pdata = wl->fw.fw_bin[i]->data + le32_to_cpu(hdr->offset); if (le32_to_cpu(hdr->len) != 4) { - wiphy_err(wl->wiphy, + brcms_err(wl->wlc->hw->d11core, "ERROR: fw hdr len\n"); return -ENOMSG; } @@ -1519,7 +1523,8 @@ int brcms_ucode_init_uint(struct brcms_info *wl, size_t *n_bytes, u32 idx) } } } - wiphy_err(wl->wiphy, "ERROR: ucode tag:%d can not be found!\n", idx); + brcms_err(wl->wlc->hw->d11core, + "ERROR: ucode tag:%d can not be found!\n", idx); return -ENOMSG; } @@ -1560,8 +1565,8 @@ int brcms_check_firmwares(struct brcms_info *wl) sizeof(struct firmware_hdr)); rc = -EBADF; } else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) { - wiphy_err(wl->wiphy, "%s: out of bounds fw file size " - "%zu\n", __func__, fw->size); + wiphy_err(wl->wiphy, "%s: out of bounds fw file size %zu\n", + __func__, fw->size); rc = -EBADF; } else { /* check if ucode section overruns firmware image */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index 565c15abbed..2a44593f1e3 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -34,12 +34,9 @@ #include "ucode_loader.h" #include "main.h" #include "soc.h" - -/* - * Indication for txflowcontrol that all priority bits in - * TXQ_STOP_FOR_PRIOFC_MASK are to be considered. - */ -#define ALLPRIO -1 +#include "dma.h" +#include "debug.h" +#include "brcms_trace_events.h" /* watchdog timer, in unit of ms */ #define TIMER_INTERVAL_WATCHDOG 1000 @@ -126,21 +123,6 @@ #define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ -/* precedences numbers for wlc queues. These are twice as may levels as - * 802.1D priorities. - * Odd numbers are used for HI priority traffic at same precedence levels - * These constants are used ONLY by wlc_prio2prec_map. Do not use them - * elsewhere. - */ -#define _BRCMS_PREC_NONE 0 /* None = - */ -#define _BRCMS_PREC_BK 2 /* BK - Background */ -#define _BRCMS_PREC_BE 4 /* BE - Best-effort */ -#define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */ -#define _BRCMS_PREC_CL 8 /* CL - Controlled Load */ -#define _BRCMS_PREC_VI 10 /* Vi - Video */ -#define _BRCMS_PREC_VO 12 /* Vo - Voice */ -#define _BRCMS_PREC_NC 14 /* NC - Network Control */ - /* synthpu_dly times in us */ #define SYNTHPU_DLY_APHY_US 3700 #define SYNTHPU_DLY_BPHY_US 1050 @@ -237,17 +219,17 @@ #define MAX_DMA_SEGS 4 -/* Max # of entries in Tx FIFO based on 4kb page size */ -#define NTXD 256 +/* # of entries in Tx FIFO */ +#define NTXD 64 /* Max # of entries in Rx FIFO based on 4kb page size */ #define NRXD 256 +/* Amount of headroom to leave in Tx FIFO */ +#define TX_HEADROOM 4 + /* try to keep this # rbufs posted to the chip */ #define NRXBUFPOST 32 -/* data msg txq hiwat mark */ -#define BRCMS_DATAHIWAT 50 - /* max # frames to process in brcms_c_recv() */ #define RXBND 8 /* max # tx status to process in wlc_txstatus() */ @@ -283,24 +265,8 @@ struct edcf_acparam { u16 TXOP; } __packed; -const u8 prio2fifo[NUMPRIO] = { - TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */ - TX_AC_BK_FIFO, /* 1 BK AC_BK Background */ - TX_AC_BK_FIFO, /* 2 -- AC_BK Background */ - TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */ - TX_AC_VI_FIFO, /* 4 CL AC_VI Video */ - TX_AC_VI_FIFO, /* 5 VI AC_VI Video */ - TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */ - TX_AC_VO_FIFO /* 7 NC AC_VO Voice */ -}; - /* debug/trace */ -uint brcm_msg_level = -#if defined(DEBUG) - LOG_ERROR_VAL; -#else - 0; -#endif /* DEBUG */ +uint brcm_msg_level; /* TX FIFO number to WME/802.1E Access Category */ static const u8 wme_fifo2ac[] = { @@ -320,18 +286,6 @@ static const u8 wme_ac2fifo[] = { TX_AC_BK_FIFO }; -/* 802.1D Priority to precedence queue mapping */ -const u8 wlc_prio2prec_map[] = { - _BRCMS_PREC_BE, /* 0 BE - Best-effort */ - _BRCMS_PREC_BK, /* 1 BK - Background */ - _BRCMS_PREC_NONE, /* 2 None = - */ - _BRCMS_PREC_EE, /* 3 EE - Excellent-effort */ - _BRCMS_PREC_CL, /* 4 CL - Controlled Load */ - _BRCMS_PREC_VI, /* 5 Vi - Video */ - _BRCMS_PREC_VO, /* 6 Vo - Voice */ - _BRCMS_PREC_NC, /* 7 NC - Network Control */ -}; - static const u16 xmtfifo_sz[][NFIFO] = { /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */ {20, 192, 192, 21, 17, 5}, @@ -371,6 +325,36 @@ static const char fifo_names[6][0]; static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); #endif +/* Mapping of ieee80211 AC numbers to tx fifos */ +static const u8 ac_to_fifo_mapping[IEEE80211_NUM_ACS] = { + [IEEE80211_AC_VO] = TX_AC_VO_FIFO, + [IEEE80211_AC_VI] = TX_AC_VI_FIFO, + [IEEE80211_AC_BE] = TX_AC_BE_FIFO, + [IEEE80211_AC_BK] = TX_AC_BK_FIFO, +}; + +/* Mapping of tx fifos to ieee80211 AC numbers */ +static const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = { + [TX_AC_BK_FIFO] = IEEE80211_AC_BK, + [TX_AC_BE_FIFO] = IEEE80211_AC_BE, + [TX_AC_VI_FIFO] = IEEE80211_AC_VI, + [TX_AC_VO_FIFO] = IEEE80211_AC_VO, +}; + +static u8 brcms_ac_to_fifo(u8 ac) +{ + if (ac >= ARRAY_SIZE(ac_to_fifo_mapping)) + return TX_AC_BE_FIFO; + return ac_to_fifo_mapping[ac]; +} + +static u8 brcms_fifo_to_ac(u8 fifo) +{ + if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping)) + return IEEE80211_AC_BE; + return fifo_to_ac_mapping[fifo]; +} + /* Find basic rate for a given rate */ static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) { @@ -415,10 +399,15 @@ static bool brcms_deviceremoved(struct brcms_c_info *wlc) } /* sum the individual fifo tx pending packet counts */ -static s16 brcms_txpktpendtot(struct brcms_c_info *wlc) +static int brcms_txpktpendtot(struct brcms_c_info *wlc) { - return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] + - wlc->core->txpktpend[2] + wlc->core->txpktpend[3]; + int i; + int pending = 0; + + for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) + if (wlc->hw->di[i]) + pending += dma_txpending(wlc->hw->di[i]); + return pending; } static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) @@ -626,14 +615,11 @@ static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, uint rate = rspec2rate(ratespec); if (rate == 0) { - wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n", + brcms_err(wlc->hw->d11core, "wl%d: WAR: using rate of 1 mbps\n", wlc->pub->unit); rate = BRCM_RATE_1M; } - BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n", - wlc->pub->unit, ratespec, preamble_type, mac_len); - if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); @@ -696,7 +682,7 @@ static void brcms_c_write_inits(struct brcms_hardware *wlc_hw, u16 size; u32 value; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) { size = le16_to_cpu(inits[i].size); @@ -725,7 +711,6 @@ static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs) static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) { - struct wiphy *wiphy = wlc_hw->wlc->wiphy; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; /* init microcode host flags */ @@ -736,8 +721,9 @@ static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) if (BRCMS_ISNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); else - wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" - " %d\n", __func__, wlc_hw->unit, + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); } else { if (D11REV_IS(wlc_hw->corerev, 24)) { @@ -745,12 +731,14 @@ static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) brcms_c_write_inits(wlc_hw, ucode->d11lcn0bsinitvals24); else - wiphy_err(wiphy, "%s: wl%d: unsupported phy in" - " core rev %d\n", __func__, - wlc_hw->unit, wlc_hw->corerev); + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in core rev %d\n", + __func__, wlc_hw->unit, + wlc_hw->corerev); } else { - wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n", - __func__, wlc_hw->unit, wlc_hw->corerev); + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported corerev %d\n", + __func__, wlc_hw->unit, wlc_hw->corerev); } } } @@ -765,7 +753,7 @@ static void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v) static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk); + brcms_dbg_info(wlc_hw->d11core, "wl%d: clk %d\n", wlc_hw->unit, clk); wlc_hw->phyclk = clk; @@ -790,8 +778,8 @@ static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) /* low-level band switch utility routine */ static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, - bandunit); + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, + bandunit); wlc_hw->band = wlc_hw->bandstate[bandunit]; @@ -819,7 +807,7 @@ static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) u32 macintmask; u32 macctrl; - BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); macctrl = bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)); WARN_ON((macctrl & MCTL_EN_MAC) != 0); @@ -841,9 +829,10 @@ static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) static bool brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) { - struct sk_buff *p; - uint queue; - struct d11txh *txh; + struct sk_buff *p = NULL; + uint queue = NFIFO; + struct dma_pub *dma = NULL; + struct d11txh *txh = NULL; struct scb *scb = NULL; bool free_pdu; int tx_rts, tx_frame_count, tx_rts_count; @@ -854,6 +843,11 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *txrate; int i; + bool fatal = true; + + trace_brcms_txstatus(&wlc->hw->d11core->dev, txs->framelen, + txs->frameid, txs->status, txs->lasttxtime, + txs->sequence, txs->phyerr, txs->ackphyrxsh); /* discard intermediate indications for ucode with one legitimate case: * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, @@ -862,34 +856,36 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) */ if (!(txs->status & TX_STATUS_AMPDU) && (txs->status & TX_STATUS_INTERMEDIATE)) { - BCMMSG(wlc->wiphy, "INTERMEDIATE but not AMPDU\n"); - return false; + brcms_dbg_tx(wlc->hw->d11core, "INTERMEDIATE but not AMPDU\n"); + fatal = false; + goto out; } queue = txs->frameid & TXFID_QUEUE_MASK; if (queue >= NFIFO) { - p = NULL; - goto fatal; + brcms_err(wlc->hw->d11core, "queue %u >= NFIFO\n", queue); + goto out; } + dma = wlc->hw->di[queue]; + p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); - if (p == NULL) - goto fatal; + if (p == NULL) { + brcms_err(wlc->hw->d11core, "dma_getnexttxp returned null!\n"); + goto out; + } txh = (struct d11txh *) (p->data); mcl = le16_to_cpu(txh->MacTxControlLow); - if (txs->phyerr) { - if (brcm_msg_level & LOG_ERROR_VAL) { - wiphy_err(wlc->wiphy, "phyerr 0x%x, rate 0x%x\n", - txs->phyerr, txh->MainRates); - brcms_c_print_txdesc(txh); - } - brcms_c_print_txstatus(txs); - } + if (txs->phyerr) + brcms_err(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", + txs->phyerr, txh->MainRates); - if (txs->frameid != le16_to_cpu(txh->TxFrameID)) - goto fatal; + if (txs->frameid != le16_to_cpu(txh->TxFrameID)) { + brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n"); + goto out; + } tx_info = IEEE80211_SKB_CB(p); h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); @@ -898,14 +894,24 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); - return false; + fatal = false; + goto out; } + /* + * brcms_c_ampdu_dotxstatus() will trace tx descriptors for AMPDU + * frames; this traces them for the rest. + */ + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); + supr_status = txs->status & TX_STATUS_SUPR_MASK; - if (supr_status == TX_STATUS_SUPR_BADCH) - BCMMSG(wlc->wiphy, - "%s: Pkt tx suppressed, possibly channel %d\n", - __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)); + if (supr_status == TX_STATUS_SUPR_BADCH) { + unsigned xfts = le16_to_cpu(txh->XtraFrameTypes); + brcms_dbg_tx(wlc->hw->d11core, + "Pkt tx suppressed, dest chan %u, current %d\n", + (xfts >> XFTS_CHANNEL_SHIFT) & 0xff, + CHSPEC_CHANNEL(wlc->default_bss->chanspec)); + } tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS; tx_frame_count = @@ -916,7 +922,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) lastframe = !ieee80211_has_morefrags(h->frame_control); if (!lastframe) { - wiphy_err(wlc->wiphy, "Not last frame!\n"); + brcms_err(wlc->hw->d11core, "Not last frame!\n"); } else { /* * Set information to be consumed by Minstrel ht. @@ -982,26 +988,37 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) totlen = p->len; free_pdu = true; - brcms_c_txfifo_complete(wlc, queue, 1); - if (lastframe) { /* remove PLCP & Broadcom tx descriptor header */ skb_pull(p, D11_PHY_HDR_LEN); skb_pull(p, D11_TXH_LEN); ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); } else { - wiphy_err(wlc->wiphy, "%s: Not last frame => not calling " - "tx_status\n", __func__); + brcms_err(wlc->hw->d11core, + "%s: Not last frame => not calling tx_status\n", + __func__); } - return false; + fatal = false; - fatal: - if (p) - brcmu_pkt_buf_free_skb(p); + out: + if (fatal) { + if (txh) + trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, + sizeof(*txh)); + if (p) + brcmu_pkt_buf_free_skb(p); + } - return true; + if (dma && queue < NFIFO) { + u16 ac_queue = brcms_fifo_to_ac(queue); + if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO && + ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue)) + ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue); + dma_kick_tx(dma); + } + return fatal; } /* process tx completion events in BMAC @@ -1011,7 +1028,6 @@ static bool brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) { bool morepending = false; - struct brcms_c_info *wlc = wlc_hw->wlc; struct bcma_device *core; struct tx_status txstatus, *txs; u32 s1, s2; @@ -1022,8 +1038,6 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) */ uint max_tx_num = bound ? TXSBND : -1; - BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); - txs = &txstatus; core = wlc_hw->d11core; *fatal = false; @@ -1032,8 +1046,8 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) && (s1 & TXS_V)) { if (s1 == 0xffffffff) { - wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", - wlc_hw->unit, __func__); + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, + __func__); return morepending; } s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); @@ -1058,9 +1072,6 @@ brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) if (n >= max_tx_num) morepending = true; - if (!pktq_empty(&wlc->pkt_queue->q)) - brcms_c_send_q(wlc); - return morepending; } @@ -1112,7 +1123,6 @@ static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) u16 pio_mhf2 = 0; struct brcms_hardware *wlc_hw = wlc->hw; uint unit = wlc_hw->unit; - struct wiphy *wiphy = wlc->wiphy; /* name and offsets for dma_attach */ snprintf(name, sizeof(name), "wl%d", unit); @@ -1125,12 +1135,12 @@ static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) * TX: TX_AC_BK_FIFO (TX AC Background data packets) * RX: RX_FIFO (RX data packets) */ - wlc_hw->di[0] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, + wlc_hw->di[0] = dma_attach(name, wlc, (wme ? dmareg(DMA_TX, 0) : 0), dmareg(DMA_RX, 0), (wme ? NTXD : 0), NRXD, RXBUFSZ, -1, NRXBUFPOST, - BRCMS_HWRXOFF, &brcm_msg_level); + BRCMS_HWRXOFF); dma_attach_err |= (NULL == wlc_hw->di[0]); /* @@ -1139,10 +1149,9 @@ static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) * (legacy) TX_DATA_FIFO (TX data packets) * RX: UNUSED */ - wlc_hw->di[1] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, + wlc_hw->di[1] = dma_attach(name, wlc, dmareg(DMA_TX, 1), 0, - NTXD, 0, 0, -1, 0, 0, - &brcm_msg_level); + NTXD, 0, 0, -1, 0, 0); dma_attach_err |= (NULL == wlc_hw->di[1]); /* @@ -1150,26 +1159,26 @@ static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) * TX: TX_AC_VI_FIFO (TX AC Video data packets) * RX: UNUSED */ - wlc_hw->di[2] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, + wlc_hw->di[2] = dma_attach(name, wlc, dmareg(DMA_TX, 2), 0, - NTXD, 0, 0, -1, 0, 0, - &brcm_msg_level); + NTXD, 0, 0, -1, 0, 0); dma_attach_err |= (NULL == wlc_hw->di[2]); /* * FIFO 3 * TX: TX_AC_VO_FIFO (TX AC Voice data packets) * (legacy) TX_CTL_FIFO (TX control & mgmt packets) */ - wlc_hw->di[3] = dma_attach(name, wlc_hw->sih, wlc_hw->d11core, + wlc_hw->di[3] = dma_attach(name, wlc, dmareg(DMA_TX, 3), 0, NTXD, 0, 0, -1, - 0, 0, &brcm_msg_level); + 0, 0); dma_attach_err |= (NULL == wlc_hw->di[3]); /* Cleaner to leave this as if with AP defined */ if (dma_attach_err) { - wiphy_err(wiphy, "wl%d: wlc_attach: dma_attach failed" - "\n", unit); + brcms_err(wlc_hw->d11core, + "wl%d: wlc_attach: dma_attach failed\n", + unit); return false; } @@ -1503,8 +1512,7 @@ brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset, u16 mac_m; u16 mac_h; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: brcms_b_set_addrmatch\n", - wlc_hw->unit); + brcms_dbg_rx(core, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit); mac_l = addr[0] | (addr[1] << 8); mac_m = addr[2] | (addr[3] << 8); @@ -1527,7 +1535,7 @@ brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, __le32 word_le; __be32 word_be; bool be_bit; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); bcma_write32(core, D11REGOFFS(tplatewrptr), offset); @@ -1700,8 +1708,8 @@ static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) { struct brcms_hardware *wlc_hw = wlc->hw; - BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc_hw->band->bandunit); + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc_hw->band->bandunit); brcms_c_ucode_bsinit(wlc_hw); @@ -1736,8 +1744,6 @@ static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) /* Perform a soft reset of the PHY PLL */ void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr), ~0, 0); udelay(1); @@ -1782,7 +1788,7 @@ void brcms_b_phy_reset(struct brcms_hardware *wlc_hw) u32 phy_bw_clkbits; bool phy_in_reset = false; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d: reset phy\n", wlc_hw->unit); if (pih == NULL) return; @@ -1916,7 +1922,7 @@ static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ /* power both the pll and external oscillator on/off */ static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want); + brcms_dbg_info(wlc_hw->d11core, "wl%d: want %d\n", wlc_hw->unit, want); /* * dont power down if plldown is false or @@ -2005,7 +2011,7 @@ void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) if (flags == BRCMS_USE_COREFLAGS) flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0); - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d: core reset\n", wlc_hw->unit); /* request FAST clock if not on */ fastclk = wlc_hw->forcefastclk; @@ -2016,13 +2022,13 @@ void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) if (bcma_core_is_enabled(wlc_hw->d11core)) { for (i = 0; i < NFIFO; i++) if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) - wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: " + brcms_err(wlc_hw->d11core, "wl%d: %s: " "dma_txreset[%d]: cannot stop dma\n", wlc_hw->unit, __func__, i); if ((wlc_hw->di[RX_FIFO]) && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) - wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: dma_rxreset" + brcms_err(wlc_hw->d11core, "wl%d: %s: dma_rxreset" "[%d]: cannot stop dma\n", wlc_hw->unit, __func__, RX_FIFO); } @@ -2235,7 +2241,7 @@ static void brcms_ucode_write(struct brcms_hardware *wlc_hw, uint i; uint count; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); count = (nbytes / sizeof(u32)); @@ -2263,8 +2269,8 @@ static void brcms_ucode_download(struct brcms_hardware *wlc_hw) ucode->bcm43xx_16_mimosz); wlc_hw->ucode_loaded = true; } else - wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in " - "corerev %d\n", + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else if (D11REV_IS(wlc_hw->corerev, 24)) { if (BRCMS_ISLCNPHY(wlc_hw->band)) { @@ -2272,8 +2278,8 @@ static void brcms_ucode_download(struct brcms_hardware *wlc_hw) ucode->bcm43xx_24_lcnsz); wlc_hw->ucode_loaded = true; } else { - wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in " - "corerev %d\n", + brcms_err(wlc_hw->d11core, + "%s: wl%d: unsupported phy in corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } } @@ -2310,7 +2316,6 @@ static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) uint unit; uint intstatus, idx; struct bcma_device *core = wlc_hw->d11core; - struct wiphy *wiphy = wlc_hw->wlc->wiphy; unit = wlc_hw->unit; @@ -2323,39 +2328,39 @@ static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) if (!intstatus) continue; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: intstatus%d 0x%x\n", - unit, idx, intstatus); + brcms_dbg_int(core, "wl%d: intstatus%d 0x%x\n", + unit, idx, intstatus); if (intstatus & I_RO) { - wiphy_err(wiphy, "wl%d: fifo %d: receive fifo " + brcms_err(core, "wl%d: fifo %d: receive fifo " "overflow\n", unit, idx); fatal = true; } if (intstatus & I_PC) { - wiphy_err(wiphy, "wl%d: fifo %d: descriptor error\n", - unit, idx); + brcms_err(core, "wl%d: fifo %d: descriptor error\n", + unit, idx); fatal = true; } if (intstatus & I_PD) { - wiphy_err(wiphy, "wl%d: fifo %d: data error\n", unit, + brcms_err(core, "wl%d: fifo %d: data error\n", unit, idx); fatal = true; } if (intstatus & I_DE) { - wiphy_err(wiphy, "wl%d: fifo %d: descriptor protocol " + brcms_err(core, "wl%d: fifo %d: descriptor protocol " "error\n", unit, idx); fatal = true; } if (intstatus & I_RU) - wiphy_err(wiphy, "wl%d: fifo %d: receive descriptor " + brcms_err(core, "wl%d: fifo %d: receive descriptor " "underflow\n", idx, unit); if (intstatus & I_XU) { - wiphy_err(wiphy, "wl%d: fifo %d: transmit fifo " + brcms_err(core, "wl%d: fifo %d: transmit fifo " "underflow\n", idx, unit); fatal = true; } @@ -2516,13 +2521,13 @@ static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) { struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; - u32 macintstatus; + u32 macintstatus, mask; /* macintstatus includes a DMA interrupt summary bit */ macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); + mask = in_isr ? wlc->macintmask : wlc->defmacintmask; - BCMMSG(wlc->wiphy, "wl%d: macintstatus: 0x%x\n", wlc_hw->unit, - macintstatus); + trace_brcms_macintstatus(&core->dev, in_isr, macintstatus, mask); /* detect cardbus removed, in power down(suspend) and in reset */ if (brcms_deviceremoved(wlc)) @@ -2535,7 +2540,7 @@ static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) return 0; /* defer unsolicited interrupts */ - macintstatus &= (in_isr ? wlc->macintmask : wlc->defmacintmask); + macintstatus &= mask; /* if not for us */ if (macintstatus == 0) @@ -2605,8 +2610,8 @@ bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc) macintstatus = wlc_intstatus(wlc, true); if (macintstatus == 0xffffffff) - wiphy_err(wlc->wiphy, "DEVICEREMOVED detected in the ISR code" - " path\n"); + brcms_err(wlc_hw->d11core, + "DEVICEREMOVED detected in the ISR code path\n"); /* it is not for us */ if (macintstatus == 0) @@ -2626,10 +2631,9 @@ void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; u32 mc, mi; - struct wiphy *wiphy = wlc->wiphy; - BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc_hw->band->bandunit); + brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc_hw->band->bandunit); /* * Track overlapping suspend requests @@ -2644,7 +2648,7 @@ void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) mc = bcma_read32(core, D11REGOFFS(maccontrol)); if (mc == 0xffffffff) { - wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; @@ -2655,7 +2659,7 @@ void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) mi = bcma_read32(core, D11REGOFFS(macintstatus)); if (mi == 0xffffffff) { - wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; @@ -2668,10 +2672,10 @@ void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) BRCMS_MAX_MAC_SUSPEND); if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { - wiphy_err(wiphy, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" + brcms_err(core, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" " and MI_MACSSPNDD is still not on.\n", wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); - wiphy_err(wiphy, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " + brcms_err(core, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " "psm_brc 0x%04x\n", wlc_hw->unit, bcma_read32(core, D11REGOFFS(psmdebug)), bcma_read32(core, D11REGOFFS(phydebug)), @@ -2680,7 +2684,7 @@ void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) mc = bcma_read32(core, D11REGOFFS(maccontrol)); if (mc == 0xffffffff) { - wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return; @@ -2696,8 +2700,8 @@ void brcms_c_enable_mac(struct brcms_c_info *wlc) struct bcma_device *core = wlc_hw->d11core; u32 mc, mi; - BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit, - wlc->band->bandunit); + brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, + wlc->band->bandunit); /* * Track overlapping suspend requests @@ -2740,8 +2744,6 @@ static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) u32 w, val; struct wiphy *wiphy = wlc_hw->wlc->wiphy; - BCMMSG(wiphy, "wl%d\n", wlc_hw->unit); - /* Validate dchip register access */ bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); @@ -2802,7 +2804,7 @@ void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) struct bcma_device *core = wlc_hw->d11core; u32 tmp; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); tmp = 0; @@ -2818,8 +2820,8 @@ void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) - wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on PHY" - " PLL failed\n", __func__); + brcms_err(core, "%s: turn on PHY PLL failed\n", + __func__); } else { bcma_set32(core, D11REGOFFS(clk_ctl_st), tmp | CCS_ERSRC_REQ_D11PLL | @@ -2835,8 +2837,8 @@ void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) != (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) - wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on " - "PHY PLL failed\n", __func__); + brcms_err(core, "%s: turn on PHY PLL failed\n", + __func__); } } else { /* @@ -2854,7 +2856,7 @@ static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) { bool dev_gone; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d: disable core\n", wlc_hw->unit); dev_gone = brcms_deviceremoved(wlc_hw->wlc); @@ -2884,12 +2886,14 @@ static void brcms_c_flushqueues(struct brcms_c_info *wlc) uint i; /* free any posted tx packets */ - for (i = 0; i < NFIFO; i++) + for (i = 0; i < NFIFO; i++) { if (wlc_hw->di[i]) { dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); - wlc->core->txpktpend[i] = 0; - BCMMSG(wlc->wiphy, "pktpend fifo %d clrd\n", i); + if (i < TX_BCMC_FIFO) + ieee80211_wake_queue(wlc->pub->ieee_hw, + brcms_fifo_to_ac(i)); } + } /* free any posted rx packets */ dma_rxreclaim(wlc_hw->di[RX_FIFO]); @@ -3109,7 +3113,7 @@ static void brcms_c_statsupd(struct brcms_c_info *wlc) /* check for rx fifo 0 overflow */ delta = (u16) (wlc->core->macstat_snapshot->rxf0ovfl - rxf0ovfl); if (delta) - wiphy_err(wlc->wiphy, "wl%d: %u rx fifo 0 overflows!\n", + brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", wlc->pub->unit, delta); /* check for tx fifo underflows */ @@ -3118,8 +3122,9 @@ static void brcms_c_statsupd(struct brcms_c_info *wlc) (u16) (wlc->core->macstat_snapshot->txfunfl[i] - txfunfl[i]); if (delta) - wiphy_err(wlc->wiphy, "wl%d: %u tx fifo %d underflows!" - "\n", wlc->pub->unit, delta, i); + brcms_err(wlc->hw->d11core, + "wl%d: %u tx fifo %d underflows!\n", + wlc->pub->unit, delta, i); } #endif /* DEBUG */ @@ -3132,8 +3137,6 @@ static void brcms_c_statsupd(struct brcms_c_info *wlc) static void brcms_b_reset(struct brcms_hardware *wlc_hw) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - /* reset the core */ if (!brcms_deviceremoved(wlc_hw->wlc)) brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); @@ -3144,7 +3147,7 @@ static void brcms_b_reset(struct brcms_hardware *wlc_hw) void brcms_c_reset(struct brcms_c_info *wlc) { - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); /* slurp up hw mac counters before core reset */ brcms_c_statsupd(wlc); @@ -3189,10 +3192,9 @@ static void brcms_b_coreinit(struct brcms_c_info *wlc) bool fifosz_fixup = false; int err = 0; u16 buf[NFIFO]; - struct wiphy *wiphy = wlc->wiphy; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; - BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(core, "wl%d: core init\n", wlc_hw->unit); /* reset PSM */ brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE)); @@ -3212,7 +3214,7 @@ static void brcms_b_coreinit(struct brcms_c_info *wlc) SPINWAIT(((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0), 1000 * 1000); if ((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0) - wiphy_err(wiphy, "wl%d: wlc_coreinit: ucode did not self-" + brcms_err(core, "wl%d: wlc_coreinit: ucode did not self-" "suspend!\n", wlc_hw->unit); brcms_c_gpio_init(wlc); @@ -3223,18 +3225,18 @@ static void brcms_b_coreinit(struct brcms_c_info *wlc) if (BRCMS_ISNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16); else - wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" + brcms_err(core, "%s: wl%d: unsupported phy in corerev" " %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else if (D11REV_IS(wlc_hw->corerev, 24)) { if (BRCMS_ISLCNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24); else - wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev" + brcms_err(core, "%s: wl%d: unsupported phy in corerev" " %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else { - wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n", + brcms_err(core, "%s: wl%d: unsupported corerev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } @@ -3276,7 +3278,7 @@ static void brcms_b_coreinit(struct brcms_c_info *wlc) err = -1; } if (err != 0) - wiphy_err(wiphy, "wlc_coreinit: txfifo mismatch: ucode size %d" + brcms_err(core, "wlc_coreinit: txfifo mismatch: ucode size %d" " driver size %d index %d\n", buf[i], wlc_hw->xmtfifo_sz[i], i); @@ -3359,8 +3361,6 @@ static brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) { bool fastclk; struct brcms_c_info *wlc = wlc_hw->wlc; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - /* request FAST clock if not on */ fastclk = wlc_hw->forcefastclk; if (!fastclk) @@ -3453,7 +3453,7 @@ static void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, rate = (rateset->rates[i] & BRCMS_RATE_MASK); if (rate > BRCM_MAXRATE) { - wiphy_err(wlc->wiphy, "brcms_c_rate_lookup_init: " + brcms_err(wlc->hw->d11core, "brcms_c_rate_lookup_init: " "invalid rate 0x%X in rate set\n", rateset->rates[i]); continue; @@ -3529,7 +3529,6 @@ static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, uint parkband; uint i, band_order[2]; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); /* * We might have been bandlocked during down and the chip * power-cycled (hibernate). Figure out the right band to park on @@ -3710,8 +3709,8 @@ static void brcms_c_set_ratetable(struct brcms_c_info *wlc) /* band-specific init */ static void brcms_c_bsinit(struct brcms_c_info *wlc) { - BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", - wlc->pub->unit, wlc->band->bandunit); + brcms_dbg_info(wlc->hw->d11core, "wl%d: bandunit %d\n", + wlc->pub->unit, wlc->band->bandunit); /* write ucode ACK/CTS rate table */ brcms_c_set_ratetable(wlc); @@ -3734,7 +3733,8 @@ brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM : M_TX_IDLE_BUSY_RATIO_X_16_CCK; if (duty_cycle > 100 || duty_cycle < 0) { - wiphy_err(wlc->wiphy, "wl%d: duty cycle value off limit\n", + brcms_err(wlc->hw->d11core, + "wl%d: duty cycle value off limit\n", wlc->pub->unit); return -EINVAL; } @@ -3752,40 +3752,6 @@ brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, return 0; } -/* - * Initialize the base precedence map for dequeueing - * from txq based on WME settings - */ -static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc) -{ - wlc->tx_prec_map = BRCMS_PREC_BMP_ALL; - memset(wlc->fifo2prec_map, 0, NFIFO * sizeof(u16)); - - wlc->fifo2prec_map[TX_AC_BK_FIFO] = BRCMS_PREC_BMP_AC_BK; - wlc->fifo2prec_map[TX_AC_BE_FIFO] = BRCMS_PREC_BMP_AC_BE; - wlc->fifo2prec_map[TX_AC_VI_FIFO] = BRCMS_PREC_BMP_AC_VI; - wlc->fifo2prec_map[TX_AC_VO_FIFO] = BRCMS_PREC_BMP_AC_VO; -} - -static void -brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc, - struct brcms_txq_info *qi, bool on, int prio) -{ - /* transmit flowcontrol is not yet implemented */ -} - -static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc) -{ - struct brcms_txq_info *qi; - - for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) { - if (qi->stopped) { - brcms_c_txflowcontrol_signal(wlc, qi, OFF, ALLPRIO); - qi->stopped = 0; - } - } -} - /* push sw hps and wake state through hardware */ static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) { @@ -3795,7 +3761,8 @@ static void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) hps = brcms_c_ps_allowed(wlc); - BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps); + brcms_dbg_mac80211(wlc->hw->d11core, "wl%d: hps %d\n", wlc->pub->unit, + hps); v1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); v2 = MCTL_WAKE; @@ -3881,7 +3848,8 @@ brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, { uint bandunit; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec); + brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: 0x%x\n", wlc_hw->unit, + chanspec); wlc_hw->chanspec = chanspec; @@ -3942,7 +3910,7 @@ static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) u16 old_chanspec = wlc->chanspec; if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) { - wiphy_err(wlc->wiphy, "wl%d: %s: Bad channel %d\n", + brcms_err(wlc->hw->d11core, "wl%d: %s: Bad channel %d\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); return; } @@ -3953,8 +3921,8 @@ static void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) { switchband = true; if (wlc->bandlocked) { - wiphy_err(wlc->wiphy, "wl%d: %s: chspec %d " - "band is locked!\n", + brcms_err(wlc->hw->d11core, + "wl%d: %s: chspec %d band is locked!\n", wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); return; @@ -4018,6 +3986,10 @@ void brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, */ void brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val) { + /* + * Cannot use brcms_dbg_* here because this function is called + * before wlc is sufficiently initialized. + */ BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val); switch (idx) { @@ -4090,8 +4062,8 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, /* Only apply params if the core is out of reset and has clocks */ if (!wlc->clk) { - wiphy_err(wlc->wiphy, "wl%d: %s : no-clock\n", wlc->pub->unit, - __func__); + brcms_err(wlc->hw->d11core, "wl%d: %s : no-clock\n", + wlc->pub->unit, __func__); return; } @@ -4109,7 +4081,7 @@ void brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, if (acp_shm.aifs < EDCF_AIFSN_MIN || acp_shm.aifs > EDCF_AIFSN_MAX) { - wiphy_err(wlc->wiphy, "wl%d: edcf_setparams: bad " + brcms_err(wlc->hw->d11core, "wl%d: edcf_setparams: bad " "aifs %d\n", wlc->pub->unit, acp_shm.aifs); } else { acp_shm.cwmin = params->cw_min; @@ -4224,8 +4196,8 @@ static void brcms_c_radio_timer(void *arg) struct brcms_c_info *wlc = (struct brcms_c_info *) arg; if (brcms_deviceremoved(wlc)) { - wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit, - __func__); + brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", + wlc->pub->unit, __func__); brcms_down(wlc->wl); return; } @@ -4238,8 +4210,6 @@ static void brcms_b_watchdog(struct brcms_c_info *wlc) { struct brcms_hardware *wlc_hw = wlc->hw; - BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); - if (!wlc_hw->up) return; @@ -4258,14 +4228,14 @@ static void brcms_b_watchdog(struct brcms_c_info *wlc) /* common watchdog code */ static void brcms_c_watchdog(struct brcms_c_info *wlc) { - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); if (!wlc->pub->up) return; if (brcms_deviceremoved(wlc)) { - wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc->pub->unit, - __func__); + brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", + wlc->pub->unit, __func__); brcms_down(wlc->wl); return; } @@ -4437,13 +4407,13 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, struct ssb_sprom *sprom = &core->bus->sprom; if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) - BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, - pcidev->vendor, - pcidev->device); + brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, + pcidev->vendor, + pcidev->device); else - BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, - core->bus->boardinfo.vendor, - core->bus->boardinfo.type); + brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, + core->bus->boardinfo.vendor, + core->bus->boardinfo.type); wme = true; @@ -4715,8 +4685,9 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, goto fail; } - BCMMSG(wlc->wiphy, "deviceid 0x%x nbands %d board 0x%x\n", - wlc_hw->deviceid, wlc_hw->_nbands, ai_get_boardtype(wlc_hw->sih)); + brcms_dbg_info(wlc_hw->d11core, "deviceid 0x%x nbands %d board 0x%x\n", + wlc_hw->deviceid, wlc_hw->_nbands, + ai_get_boardtype(wlc_hw->sih)); return err; @@ -4836,56 +4807,6 @@ static void brcms_c_bss_default_init(struct brcms_c_info *wlc) bi->flags |= BRCMS_BSS_HT; } -static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc) -{ - struct brcms_txq_info *qi, *p; - - qi = kzalloc(sizeof(struct brcms_txq_info), GFP_ATOMIC); - if (qi != NULL) { - /* - * Have enough room for control packets along with HI watermark - * Also, add room to txq for total psq packets if all the SCBs - * leave PS mode. The watermark for flowcontrol to OS packets - * will remain the same - */ - brcmu_pktq_init(&qi->q, BRCMS_PREC_COUNT, - 2 * BRCMS_DATAHIWAT + PKTQ_LEN_DEFAULT); - - /* add this queue to the the global list */ - p = wlc->tx_queues; - if (p == NULL) { - wlc->tx_queues = qi; - } else { - while (p->next != NULL) - p = p->next; - p->next = qi; - } - } - return qi; -} - -static void brcms_c_txq_free(struct brcms_c_info *wlc, - struct brcms_txq_info *qi) -{ - struct brcms_txq_info *p; - - if (qi == NULL) - return; - - /* remove the queue from the linked list */ - p = wlc->tx_queues; - if (p == qi) - wlc->tx_queues = p->next; - else { - while (p != NULL && p->next != qi) - p = p->next; - if (p != NULL) - p->next = p->next->next; - } - - kfree(qi); -} - static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) { uint i; @@ -4991,8 +4912,6 @@ uint brcms_c_detach(struct brcms_c_info *wlc) if (wlc == NULL) return 0; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - callbacks += brcms_b_detach(wlc); /* delete software timers */ @@ -5005,10 +4924,6 @@ uint brcms_c_detach(struct brcms_c_info *wlc) brcms_c_detach_module(wlc); - - while (wlc->tx_queues != NULL) - brcms_c_txq_free(wlc, wlc->tx_queues); - brcms_c_detach_mfree(wlc); return callbacks; } @@ -5026,7 +4941,7 @@ static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) if (wlc_hw->wlc->pub->hw_up) return; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); /* * Enable pll and xtal, initialize the power control registers, @@ -5063,7 +4978,7 @@ static void brcms_b_hw_up(struct brcms_hardware *wlc_hw) static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); + brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); /* * Enable pll and xtal, initialize the power control registers, @@ -5102,8 +5017,6 @@ static int brcms_b_up_prep(struct brcms_hardware *wlc_hw) static int brcms_b_up_finish(struct brcms_hardware *wlc_hw) { - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - wlc_hw->up = true; wlc_phy_hw_state_upd(wlc_hw->band->pi, true); @@ -5135,7 +5048,7 @@ int brcms_c_up(struct brcms_c_info *wlc) { struct ieee80211_channel *ch; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); /* HW is turned off so don't try to access it */ if (wlc->pub->hw_off || brcms_deviceremoved(wlc)) @@ -5176,8 +5089,8 @@ int brcms_c_up(struct brcms_c_info *wlc) WL_RADIO_HW_DISABLE); if (bsscfg->enable && bsscfg->BSS) - wiphy_err(wlc->wiphy, "wl%d: up" - ": rfdisable -> " + brcms_err(wlc->hw->d11core, + "wl%d: up: rfdisable -> " "bsscfg_disable()\n", wlc->pub->unit); } @@ -5237,8 +5150,6 @@ static int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw) bool dev_gone; uint callbacks = 0; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - if (!wlc_hw->up) return callbacks; @@ -5265,8 +5176,6 @@ static int brcms_b_down_finish(struct brcms_hardware *wlc_hw) uint callbacks = 0; bool dev_gone; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); - if (!wlc_hw->up) return callbacks; @@ -5314,14 +5223,14 @@ uint brcms_c_down(struct brcms_c_info *wlc) uint callbacks = 0; int i; bool dev_gone = false; - struct brcms_txq_info *qi; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); /* check if we are already in the going down path */ if (wlc->going_down) { - wiphy_err(wlc->wiphy, "wl%d: %s: Driver going down so return" - "\n", wlc->pub->unit, __func__); + brcms_err(wlc->hw->d11core, + "wl%d: %s: Driver going down so return\n", + wlc->pub->unit, __func__); return 0; } if (!wlc->pub->up) @@ -5353,13 +5262,6 @@ uint brcms_c_down(struct brcms_c_info *wlc) wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL); - /* clear txq flow control */ - brcms_c_txflowcontrol_reset(wlc); - - /* flush tx queues */ - for (qi = wlc->tx_queues; qi != NULL; qi = qi->next) - brcmu_pktq_flush(&qi->q, true, NULL, NULL); - callbacks += brcms_b_down_finish(wlc->hw); /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */ @@ -5441,7 +5343,7 @@ int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) default: /* Error */ - wiphy_err(wlc->wiphy, "wl%d: %s: invalid gmode %d\n", + brcms_err(wlc->hw->d11core, "wl%d: %s: invalid gmode %d\n", wlc->pub->unit, __func__, gmode); return -ENOTSUPP; } @@ -5745,45 +5647,6 @@ int brcms_c_module_unregister(struct brcms_pub *pub, const char *name, return -ENODATA; } -void brcms_c_print_txstatus(struct tx_status *txs) -{ - pr_debug("\ntxpkt (MPDU) Complete\n"); - - pr_debug("FrameID: %04x TxStatus: %04x\n", txs->frameid, txs->status); - - pr_debug("[15:12] %d frame attempts\n", - (txs->status & TX_STATUS_FRM_RTX_MASK) >> - TX_STATUS_FRM_RTX_SHIFT); - pr_debug(" [11:8] %d rts attempts\n", - (txs->status & TX_STATUS_RTS_RTX_MASK) >> - TX_STATUS_RTS_RTX_SHIFT); - pr_debug(" [7] %d PM mode indicated\n", - txs->status & TX_STATUS_PMINDCTD ? 1 : 0); - pr_debug(" [6] %d intermediate status\n", - txs->status & TX_STATUS_INTERMEDIATE ? 1 : 0); - pr_debug(" [5] %d AMPDU\n", - txs->status & TX_STATUS_AMPDU ? 1 : 0); - pr_debug(" [4:2] %d Frame Suppressed Reason (%s)\n", - (txs->status & TX_STATUS_SUPR_MASK) >> TX_STATUS_SUPR_SHIFT, - (const char *[]) { - "None", - "PMQ Entry", - "Flush request", - "Previous frag failure", - "Channel mismatch", - "Lifetime Expiry", - "Underflow" - } [(txs->status & TX_STATUS_SUPR_MASK) >> - TX_STATUS_SUPR_SHIFT]); - pr_debug(" [1] %d acked\n", - txs->status & TX_STATUS_ACK_RCV ? 1 : 0); - - pr_debug("LastTxTime: %04x Seq: %04x PHYTxStatus: %04x RxAckRSSI: %04x RxAckSQ: %04x\n", - txs->lasttxtime, txs->sequence, txs->phyerr, - (txs->ackphyrxsh & PRXS1_JSSI_MASK) >> PRXS1_JSSI_SHIFT, - (txs->ackphyrxsh & PRXS1_SQ_MASK) >> PRXS1_SQ_SHIFT); -} - static bool brcms_c_chipmatch_pci(struct bcma_device *core) { struct pci_dev *pcidev = core->bus->host_pci; @@ -5832,184 +5695,6 @@ bool brcms_c_chipmatch(struct bcma_device *core) } } -#if defined(DEBUG) -void brcms_c_print_txdesc(struct d11txh *txh) -{ - u16 mtcl = le16_to_cpu(txh->MacTxControlLow); - u16 mtch = le16_to_cpu(txh->MacTxControlHigh); - u16 mfc = le16_to_cpu(txh->MacFrameControl); - u16 tfest = le16_to_cpu(txh->TxFesTimeNormal); - u16 ptcw = le16_to_cpu(txh->PhyTxControlWord); - u16 ptcw_1 = le16_to_cpu(txh->PhyTxControlWord_1); - u16 ptcw_1_Fbr = le16_to_cpu(txh->PhyTxControlWord_1_Fbr); - u16 ptcw_1_Rts = le16_to_cpu(txh->PhyTxControlWord_1_Rts); - u16 ptcw_1_FbrRts = le16_to_cpu(txh->PhyTxControlWord_1_FbrRts); - u16 mainrates = le16_to_cpu(txh->MainRates); - u16 xtraft = le16_to_cpu(txh->XtraFrameTypes); - u8 *iv = txh->IV; - u8 *ra = txh->TxFrameRA; - u16 tfestfb = le16_to_cpu(txh->TxFesTimeFallback); - u8 *rtspfb = txh->RTSPLCPFallback; - u16 rtsdfb = le16_to_cpu(txh->RTSDurFallback); - u8 *fragpfb = txh->FragPLCPFallback; - u16 fragdfb = le16_to_cpu(txh->FragDurFallback); - u16 mmodelen = le16_to_cpu(txh->MModeLen); - u16 mmodefbrlen = le16_to_cpu(txh->MModeFbrLen); - u16 tfid = le16_to_cpu(txh->TxFrameID); - u16 txs = le16_to_cpu(txh->TxStatus); - u16 mnmpdu = le16_to_cpu(txh->MaxNMpdus); - u16 mabyte = le16_to_cpu(txh->MaxABytes_MRT); - u16 mabyte_f = le16_to_cpu(txh->MaxABytes_FBR); - u16 mmbyte = le16_to_cpu(txh->MinMBytes); - - u8 *rtsph = txh->RTSPhyHeader; - struct ieee80211_rts rts = txh->rts_frame; - - /* add plcp header along with txh descriptor */ - brcmu_dbg_hex_dump(txh, sizeof(struct d11txh) + 48, - "Raw TxDesc + plcp header:\n"); - - pr_debug("TxCtlLow: %04x ", mtcl); - pr_debug("TxCtlHigh: %04x ", mtch); - pr_debug("FC: %04x ", mfc); - pr_debug("FES Time: %04x\n", tfest); - pr_debug("PhyCtl: %04x%s ", ptcw, - (ptcw & PHY_TXC_SHORT_HDR) ? " short" : ""); - pr_debug("PhyCtl_1: %04x ", ptcw_1); - pr_debug("PhyCtl_1_Fbr: %04x\n", ptcw_1_Fbr); - pr_debug("PhyCtl_1_Rts: %04x ", ptcw_1_Rts); - pr_debug("PhyCtl_1_Fbr_Rts: %04x\n", ptcw_1_FbrRts); - pr_debug("MainRates: %04x ", mainrates); - pr_debug("XtraFrameTypes: %04x ", xtraft); - pr_debug("\n"); - - print_hex_dump_bytes("SecIV:", DUMP_PREFIX_OFFSET, iv, sizeof(txh->IV)); - print_hex_dump_bytes("RA:", DUMP_PREFIX_OFFSET, - ra, sizeof(txh->TxFrameRA)); - - pr_debug("Fb FES Time: %04x ", tfestfb); - print_hex_dump_bytes("Fb RTS PLCP:", DUMP_PREFIX_OFFSET, - rtspfb, sizeof(txh->RTSPLCPFallback)); - pr_debug("RTS DUR: %04x ", rtsdfb); - print_hex_dump_bytes("PLCP:", DUMP_PREFIX_OFFSET, - fragpfb, sizeof(txh->FragPLCPFallback)); - pr_debug("DUR: %04x", fragdfb); - pr_debug("\n"); - - pr_debug("MModeLen: %04x ", mmodelen); - pr_debug("MModeFbrLen: %04x\n", mmodefbrlen); - - pr_debug("FrameID: %04x\n", tfid); - pr_debug("TxStatus: %04x\n", txs); - - pr_debug("MaxNumMpdu: %04x\n", mnmpdu); - pr_debug("MaxAggbyte: %04x\n", mabyte); - pr_debug("MaxAggbyte_fb: %04x\n", mabyte_f); - pr_debug("MinByte: %04x\n", mmbyte); - - print_hex_dump_bytes("RTS PLCP:", DUMP_PREFIX_OFFSET, - rtsph, sizeof(txh->RTSPhyHeader)); - print_hex_dump_bytes("RTS Frame:", DUMP_PREFIX_OFFSET, - (u8 *)&rts, sizeof(txh->rts_frame)); - pr_debug("\n"); -} -#endif /* defined(DEBUG) */ - -#if defined(DEBUG) -static int -brcms_c_format_flags(const struct brcms_c_bit_desc *bd, u32 flags, char *buf, - int len) -{ - int i; - char *p = buf; - char hexstr[16]; - int slen = 0, nlen = 0; - u32 bit; - const char *name; - - if (len < 2 || !buf) - return 0; - - buf[0] = '\0'; - - for (i = 0; flags != 0; i++) { - bit = bd[i].bit; - name = bd[i].name; - if (bit == 0 && flags != 0) { - /* print any unnamed bits */ - snprintf(hexstr, 16, "0x%X", flags); - name = hexstr; - flags = 0; /* exit loop */ - } else if ((flags & bit) == 0) - continue; - flags &= ~bit; - nlen = strlen(name); - slen += nlen; - /* count btwn flag space */ - if (flags != 0) - slen += 1; - /* need NULL char as well */ - if (len <= slen) - break; - /* copy NULL char but don't count it */ - strncpy(p, name, nlen + 1); - p += nlen; - /* copy btwn flag space and NULL char */ - if (flags != 0) - p += snprintf(p, 2, " "); - len -= slen; - } - - /* indicate the str was too short */ - if (flags != 0) { - if (len < 2) - p -= 2 - len; /* overwrite last char */ - p += snprintf(p, 2, ">"); - } - - return (int)(p - buf); -} -#endif /* defined(DEBUG) */ - -#if defined(DEBUG) -void brcms_c_print_rxh(struct d11rxhdr *rxh) -{ - u16 len = rxh->RxFrameSize; - u16 phystatus_0 = rxh->PhyRxStatus_0; - u16 phystatus_1 = rxh->PhyRxStatus_1; - u16 phystatus_2 = rxh->PhyRxStatus_2; - u16 phystatus_3 = rxh->PhyRxStatus_3; - u16 macstatus1 = rxh->RxStatus1; - u16 macstatus2 = rxh->RxStatus2; - char flagstr[64]; - char lenbuf[20]; - static const struct brcms_c_bit_desc macstat_flags[] = { - {RXS_FCSERR, "FCSErr"}, - {RXS_RESPFRAMETX, "Reply"}, - {RXS_PBPRES, "PADDING"}, - {RXS_DECATMPT, "DeCr"}, - {RXS_DECERR, "DeCrErr"}, - {RXS_BCNSENT, "Bcn"}, - {0, NULL} - }; - - brcmu_dbg_hex_dump(rxh, sizeof(struct d11rxhdr), "Raw RxDesc:\n"); - - brcms_c_format_flags(macstat_flags, macstatus1, flagstr, 64); - - snprintf(lenbuf, sizeof(lenbuf), "0x%x", len); - - pr_debug("RxFrameSize: %6s (%d)%s\n", lenbuf, len, - (rxh->PhyRxStatus_0 & PRXS0_SHORTH) ? " short preamble" : ""); - pr_debug("RxPHYStatus: %04x %04x %04x %04x\n", - phystatus_0, phystatus_1, phystatus_2, phystatus_3); - pr_debug("RxMACStatus: %x %s\n", macstatus1, flagstr); - pr_debug("RXMACaggtype: %x\n", - (macstatus2 & RXS_AGGTYPE_MASK)); - pr_debug("RxTSFTime: %04x\n", rxh->RxTSFTime); -} -#endif /* defined(DEBUG) */ - u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) { u16 table_ptr; @@ -6033,86 +5718,6 @@ u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); } -static bool -brcms_c_prec_enq_head(struct brcms_c_info *wlc, struct pktq *q, - struct sk_buff *pkt, int prec, bool head) -{ - struct sk_buff *p; - int eprec = -1; /* precedence to evict from */ - - /* Determine precedence from which to evict packet, if any */ - if (pktq_pfull(q, prec)) - eprec = prec; - else if (pktq_full(q)) { - p = brcmu_pktq_peek_tail(q, &eprec); - if (eprec > prec) { - wiphy_err(wlc->wiphy, "%s: Failing: eprec %d > prec %d" - "\n", __func__, eprec, prec); - return false; - } - } - - /* Evict if needed */ - if (eprec >= 0) { - bool discard_oldest; - - discard_oldest = ac_bitmap_tst(0, eprec); - - /* Refuse newer packet unless configured to discard oldest */ - if (eprec == prec && !discard_oldest) { - wiphy_err(wlc->wiphy, "%s: No where to go, prec == %d" - "\n", __func__, prec); - return false; - } - - /* Evict packet according to discard policy */ - p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) : - brcmu_pktq_pdeq_tail(q, eprec); - brcmu_pkt_buf_free_skb(p); - } - - /* Enqueue */ - if (head) - p = brcmu_pktq_penq_head(q, prec, pkt); - else - p = brcmu_pktq_penq(q, prec, pkt); - - return true; -} - -/* - * Attempts to queue a packet onto a multiple-precedence queue, - * if necessary evicting a lower precedence packet from the queue. - * - * 'prec' is the precedence number that has already been mapped - * from the packet priority. - * - * Returns true if packet consumed (queued), false if not. - */ -static bool brcms_c_prec_enq(struct brcms_c_info *wlc, struct pktq *q, - struct sk_buff *pkt, int prec) -{ - return brcms_c_prec_enq_head(wlc, q, pkt, prec, false); -} - -void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb, - struct sk_buff *sdu, uint prec) -{ - struct brcms_txq_info *qi = wlc->pkt_queue; /* Check me */ - struct pktq *q = &qi->q; - int prio; - - prio = sdu->priority; - - if (!brcms_c_prec_enq(wlc, q, sdu, prec)) { - /* - * we might hit this condtion in case - * packet flooding from mac80211 stack - */ - brcmu_pkt_buf_free_skb(sdu); - } -} - /* * bcmc_fid_generate: * Generate frame ID for a BCMC packet. The frag field is not used @@ -6140,8 +5745,6 @@ brcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec, { uint dur = 0; - BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d\n", - wlc->pub->unit, rspec, preamble_type); /* * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that * is less than or equal to the rate of the immediately previous @@ -6159,8 +5762,6 @@ static uint brcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec, u8 preamble_type) { - BCMMSG(wlc->wiphy, "wl%d: ratespec 0x%x, preamble_type %d\n", - wlc->pub->unit, rspec, preamble_type); return brcms_c_calc_ack_time(wlc, rspec, preamble_type); } @@ -6168,8 +5769,6 @@ static uint brcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec, u8 preamble_type) { - BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, " - "preamble_type %d\n", wlc->pub->unit, rspec, preamble_type); /* * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that * is less than or equal to the rate of the immediately previous @@ -6223,9 +5822,6 @@ brcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, uint nsyms, mac_len, Ndps, kNdps; uint rate = rspec2rate(ratespec); - BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, dur %d\n", - wlc->pub->unit, ratespec, preamble_type, dur); - if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); @@ -6292,7 +5888,7 @@ static bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, return true; error: if (verbose) - wiphy_err(wlc->wiphy, "wl%d: valid_rate: rate spec 0x%x " + brcms_err(wlc->hw->d11core, "wl%d: valid_rate: rate spec 0x%x " "not in hw_rateset\n", wlc->pub->unit, rspec); return false; @@ -6302,6 +5898,7 @@ static u32 mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, u32 int_val) { + struct bcma_device *core = wlc->hw->d11core; u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT; u8 rate = int_val & NRATE_RATE_MASK; u32 rspec; @@ -6318,7 +5915,7 @@ mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) { /* mcs only allowed when nmode */ if (stf > PHY_TXC1_MODE_SDM) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid stf\n", + brcms_err(core, "wl%d: %s: Invalid stf\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; @@ -6329,8 +5926,8 @@ mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, if (!CHSPEC_IS40(wlc->home_chanspec) || ((stf != PHY_TXC1_MODE_SISO) && (stf != PHY_TXC1_MODE_CDD))) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid mcs " - "32\n", wlc->pub->unit, __func__); + brcms_err(core, "wl%d: %s: Invalid mcs 32\n", + wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } @@ -6338,9 +5935,9 @@ mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, } else if (rate > HIGHEST_SINGLE_STREAM_MCS) { /* mcs > 7 must use stf SDM */ if (stf != PHY_TXC1_MODE_SDM) { - BCMMSG(wlc->wiphy, "wl%d: enabling " - "SDM mode for mcs %d\n", - wlc->pub->unit, rate); + brcms_dbg_mac80211(core, "wl%d: enabling " + "SDM mode for mcs %d\n", + wlc->pub->unit, rate); stf = PHY_TXC1_MODE_SDM; } } else { @@ -6351,15 +5948,15 @@ mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, if ((stf > PHY_TXC1_MODE_STBC) || (!BRCMS_STBC_CAP_PHY(wlc) && (stf == PHY_TXC1_MODE_STBC))) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid STBC" - "\n", wlc->pub->unit, __func__); + brcms_err(core, "wl%d: %s: Invalid STBC\n", + wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } } } else if (is_ofdm_rate(rate)) { if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid OFDM\n", + brcms_err(core, "wl%d: %s: Invalid OFDM\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; @@ -6367,20 +5964,20 @@ mac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, } else if (is_cck_rate(rate)) { if ((cur_band->bandtype != BRCM_BAND_2G) || (stf != PHY_TXC1_MODE_SISO)) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid CCK\n", + brcms_err(core, "wl%d: %s: Invalid CCK\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } } else { - wiphy_err(wlc->wiphy, "wl%d: %s: Unknown rate type\n", + brcms_err(core, "wl%d: %s: Unknown rate type\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; } /* make sure multiple antennae are available for non-siso rates */ if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) { - wiphy_err(wlc->wiphy, "wl%d: %s: SISO antenna but !SISO " + brcms_err(core, "wl%d: %s: SISO antenna but !SISO " "request\n", wlc->pub->unit, __func__); bcmerror = -EINVAL; goto done; @@ -6449,7 +6046,7 @@ static void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500, break; default: - wiphy_err(wlc->wiphy, + brcms_err(wlc->hw->d11core, "brcms_c_cck_plcp_set: unsupported rate %d\n", rate_500); rate_500 = BRCM_RATE_1M; @@ -6582,7 +6179,7 @@ static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) bw = rspec_get_bw(rspec); /* 10Mhz is not supported yet */ if (bw < PHY_TXC1_BW_20MHZ) { - wiphy_err(wlc->wiphy, "phytxctl1_calc: bw %d is " + brcms_err(wlc->hw->d11core, "phytxctl1_calc: bw %d is " "not supported yet, set to 20L\n", bw); bw = PHY_TXC1_BW_20MHZ; } @@ -6609,7 +6206,7 @@ static u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) /* get the phyctl byte from rate phycfg table */ phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec)); if (phycfg == -1) { - wiphy_err(wlc->wiphy, "phytxctl1_calc: wrong " + brcms_err(wlc->hw->d11core, "phytxctl1_calc: wrong " "legacy OFDM/CCK rate\n"); phycfg = 0; } @@ -6689,8 +6286,9 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { /* non-AP STA should never use BCMC queue */ if (queue == TX_BCMC_FIFO) { - wiphy_err(wlc->wiphy, "wl%d: %s: ASSERT queue == " - "TX_BCMC!\n", wlc->pub->unit, __func__); + brcms_err(wlc->hw->d11core, + "wl%d: %s: ASSERT queue == TX_BCMC!\n", + wlc->pub->unit, __func__); frameid = bcmc_fid_generate(wlc, NULL, txh); } else { /* Increment the counter for first fragment */ @@ -6860,7 +6458,8 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, if ((txrate[k]->flags & IEEE80211_TX_RC_MCS) && (!is_mcs_rate(rspec[k]))) { - wiphy_err(wlc->wiphy, "wl%d: %s: IEEE80211_TX_" + brcms_err(wlc->hw->d11core, + "wl%d: %s: IEEE80211_TX_" "RC_MCS != is_mcs_rate(rspec)\n", wlc->pub->unit, __func__); } @@ -7254,14 +6853,16 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, wlc->fragthresh[queue] = (u16) newfragthresh; } else { - wiphy_err(wlc->wiphy, "wl%d: %s txop invalid " + brcms_err(wlc->hw->d11core, + "wl%d: %s txop invalid " "for rate %d\n", wlc->pub->unit, fifo_names[queue], rspec2rate(rspec[0])); } if (dur > wlc->edcf_txop[ac]) - wiphy_err(wlc->wiphy, "wl%d: %s: %s txop " + brcms_err(wlc->hw->d11core, + "wl%d: %s: %s txop " "exceeded phylen %d/%d dur %d/%d\n", wlc->pub->unit, __func__, fifo_names[queue], @@ -7273,79 +6874,33 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, return 0; } -void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, - struct ieee80211_hw *hw) +static int brcms_c_tx(struct brcms_c_info *wlc, struct sk_buff *skb) { - u8 prio; - uint fifo; - struct scb *scb = &wlc->pri_scb; - struct ieee80211_hdr *d11_header = (struct ieee80211_hdr *)(sdu->data); - - /* - * 802.11 standard requires management traffic - * to go at highest priority - */ - prio = ieee80211_is_data(d11_header->frame_control) ? sdu->priority : - MAXPRIO; - fifo = prio2fifo[prio]; - if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0)) - return; - brcms_c_txq_enq(wlc, scb, sdu, BRCMS_PRIO_TO_PREC(prio)); - brcms_c_send_q(wlc); -} - -void brcms_c_send_q(struct brcms_c_info *wlc) -{ - struct sk_buff *pkt[DOT11_MAXNUMFRAGS]; - int prec; - u16 prec_map; - int err = 0, i, count; - uint fifo; - struct brcms_txq_info *qi = wlc->pkt_queue; - struct pktq *q = &qi->q; - struct ieee80211_tx_info *tx_info; - - prec_map = wlc->tx_prec_map; + struct dma_pub *dma; + int fifo, ret = -ENOSPC; + struct d11txh *txh; + u16 frameid = INVALIDFID; - /* Send all the enq'd pkts that we can. - * Dequeue packets with precedence with empty HW fifo only - */ - while (prec_map && (pkt[0] = brcmu_pktq_mdeq(q, prec_map, &prec))) { - tx_info = IEEE80211_SKB_CB(pkt[0]); - if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - err = brcms_c_sendampdu(wlc->ampdu, qi, pkt, prec); - } else { - count = 1; - err = brcms_c_prep_pdu(wlc, pkt[0], &fifo); - if (!err) { - for (i = 0; i < count; i++) - brcms_c_txfifo(wlc, fifo, pkt[i], true, - 1); - } - } + fifo = brcms_ac_to_fifo(skb_get_queue_mapping(skb)); + dma = wlc->hw->di[fifo]; + txh = (struct d11txh *)(skb->data); - if (err == -EBUSY) { - brcmu_pktq_penq_head(q, prec, pkt[0]); - /* - * If send failed due to any other reason than a - * change in HW FIFO condition, quit. Otherwise, - * read the new prec_map! - */ - if (prec_map == wlc->tx_prec_map) - break; - prec_map = wlc->tx_prec_map; - } + if (dma->txavail == 0) { + /* + * We sometimes get a frame from mac80211 after stopping + * the queues. This only ever seems to be a single frame + * and is seems likely to be a race. TX_HEADROOM should + * ensure that we have enough space to handle these stray + * packets, so warn if there isn't. If we're out of space + * in the tx ring and the tx queue isn't stopped then + * we've really got a bug; warn loudly if that happens. + */ + brcms_warn(wlc->hw->d11core, + "Received frame for tx with no space in DMA ring\n"); + WARN_ON(!ieee80211_queue_stopped(wlc->pub->ieee_hw, + skb_get_queue_mapping(skb))); + return -ENOSPC; } -} - -void -brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, - bool commit, s8 txpktpend) -{ - u16 frameid = INVALIDFID; - struct d11txh *txh; - - txh = (struct d11txh *) (p->data); /* When a BC/MC frame is being committed to the BCMC fifo * via DMA (NOT PIO), update ucode or BSS info as appropriate. @@ -7353,16 +6908,6 @@ brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, if (fifo == TX_BCMC_FIFO) frameid = le16_to_cpu(txh->TxFrameID); - /* - * Bump up pending count for if not using rpc. If rpc is - * used, this will be handled in brcms_b_txfifo() - */ - if (commit) { - wlc->core->txpktpend[fifo] += txpktpend; - BCMMSG(wlc->wiphy, "pktpend inc %d to %d\n", - txpktpend, wlc->core->txpktpend[fifo]); - } - /* Commit BCMC sequence number in the SHM frame ID location */ if (frameid != INVALIDFID) { /* @@ -7372,8 +6917,52 @@ brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p, brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid); } - if (dma_txfast(wlc->hw->di[fifo], p, commit) < 0) + ret = brcms_c_txfifo(wlc, fifo, skb); + /* + * The only reason for brcms_c_txfifo to fail is because + * there weren't any DMA descriptors, but we've already + * checked for that. So if it does fail yell loudly. + */ + WARN_ON_ONCE(ret); + + return ret; +} + +void brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, + struct ieee80211_hw *hw) +{ + uint fifo; + struct scb *scb = &wlc->pri_scb; + + fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu)); + if (brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0)) + return; + if (brcms_c_tx(wlc, sdu)) + dev_kfree_skb_any(sdu); +} + +int +brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p) +{ + struct dma_pub *dma = wlc->hw->di[fifo]; + int ret; + u16 queue; + + ret = dma_txfast(wlc, dma, p); + if (ret < 0) wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); + + /* + * Stop queue if DMA ring is full. Reserve some free descriptors, + * as we sometimes receive a frame from mac80211 after the queues + * are stopped. + */ + queue = skb_get_queue_mapping(p); + if (dma->txavail <= TX_HEADROOM && fifo < TX_BCMC_FIFO && + !ieee80211_queue_stopped(wlc->pub->ieee_hw, queue)) + ieee80211_stop_queue(wlc->pub->ieee_hw, queue); + + return ret; } u32 @@ -7423,19 +7012,6 @@ brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, return rts_rspec; } -void -brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, s8 txpktpend) -{ - wlc->core->txpktpend[fifo] -= txpktpend; - BCMMSG(wlc->wiphy, "pktpend dec %d to %d\n", txpktpend, - wlc->core->txpktpend[fifo]); - - /* There is more room; mark precedences related to this FIFO sendable */ - wlc->tx_prec_map |= wlc->fifo2prec_map[fifo]; - - /* figure out which bsscfg is being worked on... */ -} - /* Update beacon listen interval in shared memory */ static void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) { @@ -7508,7 +7084,7 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, /* fill in TSF and flag its presence */ rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); @@ -7571,7 +7147,8 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, rx_status->rate_idx = 11; break; default: - wiphy_err(wlc->wiphy, "%s: Unknown rate\n", __func__); + brcms_err(wlc->hw->d11core, + "%s: Unknown rate\n", __func__); } /* @@ -7590,7 +7167,7 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, } else if (is_ofdm_rate(rspec)) { rx_status->flag |= RX_FLAG_SHORTPRE; } else { - wiphy_err(wlc->wiphy, "%s: Unknown modulation\n", + brcms_err(wlc->hw->d11core, "%s: Unknown modulation\n", __func__); } } @@ -7600,12 +7177,12 @@ prep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, if (rxh->RxStatus1 & RXS_DECERR) { rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; - wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_PLCP_CRC\n", + brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_PLCP_CRC\n", __func__); } if (rxh->RxStatus1 & RXS_FCSERR) { rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; - wiphy_err(wlc->wiphy, "%s: RX_FLAG_FAILED_FCS_CRC\n", + brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_FCS_CRC\n", __func__); } } @@ -7649,9 +7226,6 @@ brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, { uint nsyms, len = 0, kNdps; - BCMMSG(wlc->wiphy, "wl%d: rate %d, len%d\n", - wlc->pub->unit, rspec2rate(ratespec), mac_len); - if (is_mcs_rate(ratespec)) { uint mcs = ratespec & RSPEC_RATE_MASK; int tot_streams = (mcs_2_txstreams(mcs) + 1) + @@ -7883,35 +7457,6 @@ void brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) brcms_c_bss_update_probe_resp(wlc, bsscfg, suspend); } -/* prepares pdu for transmission. returns BCM error codes */ -int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, uint *fifop) -{ - uint fifo; - struct d11txh *txh; - struct ieee80211_hdr *h; - struct scb *scb; - - txh = (struct d11txh *) (pdu->data); - h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); - - /* get the pkt queue info. This was put at brcms_c_sendctl or - * brcms_c_send for PDU */ - fifo = le16_to_cpu(txh->TxFrameID) & TXFID_QUEUE_MASK; - - scb = NULL; - - *fifop = fifo; - - /* return if insufficient dma resources */ - if (*wlc->core->txavail[fifo] < MAX_DMA_SEGS) { - /* Mark precedences related to this FIFO, unsendable */ - /* A fifo is full. Clear precedences related to that FIFO */ - wlc->tx_prec_map &= ~(wlc->fifo2prec_map[fifo]); - return -EBUSY; - } - return 0; -} - int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, uint *blocks) { @@ -7977,13 +7522,15 @@ int brcms_c_get_curband(struct brcms_c_info *wlc) void brcms_c_wait_for_tx_completion(struct brcms_c_info *wlc, bool drop) { int timeout = 20; + int i; - /* flush packet queue when requested */ - if (drop) - brcmu_pktq_flush(&wlc->pkt_queue->q, false, NULL, NULL); + /* Kick DMA to send any pending AMPDU */ + for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) + if (wlc->hw->di[i]) + dma_txflush(wlc->hw->di[i]); /* wait for queue and DMA fifos to run dry */ - while (!pktq_empty(&wlc->pkt_queue->q) || brcms_txpktpendtot(wlc) > 0) { + while (brcms_txpktpendtot(wlc) > 0) { brcms_msleep(wlc->wl, 1); if (--timeout == 0) @@ -8032,8 +7579,6 @@ static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) uint len; bool is_amsdu; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); - /* frame starts with rxhdr */ rxh = (struct d11rxhdr *) (p->data); @@ -8043,8 +7588,9 @@ static void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ if (rxh->RxStatus1 & RXS_PBPRES) { if (p->len < 2) { - wiphy_err(wlc->wiphy, "wl%d: recv: rcvd runt of " - "len %d\n", wlc->pub->unit, p->len); + brcms_err(wlc->hw->d11core, + "wl%d: recv: rcvd runt of len %d\n", + wlc->pub->unit, p->len); goto toss; } skb_pull(p, 2); @@ -8089,7 +7635,6 @@ brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) uint n = 0; uint bound_limit = bound ? RXBND : -1; - BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); skb_queue_head_init(&recv_frames); /* gather received frames */ @@ -8140,10 +7685,9 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) u32 macintstatus; struct brcms_hardware *wlc_hw = wlc->hw; struct bcma_device *core = wlc_hw->d11core; - struct wiphy *wiphy = wlc->wiphy; if (brcms_deviceremoved(wlc)) { - wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, + brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); brcms_down(wlc->wl); return false; @@ -8153,8 +7697,8 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) macintstatus = wlc->macintstatus; wlc->macintstatus = 0; - BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n", - wlc_hw->unit, macintstatus); + brcms_dbg_int(core, "wl%d: macintstatus 0x%x\n", + wlc_hw->unit, macintstatus); WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ @@ -8164,7 +7708,7 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) wlc->macintstatus |= MI_TFS; if (fatal) { - wiphy_err(wiphy, "MI_TFS: fatal\n"); + brcms_err(core, "MI_TFS: fatal\n"); goto fatal; } } @@ -8174,7 +7718,7 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) /* ATIM window end */ if (macintstatus & MI_ATIMWINEND) { - BCMMSG(wlc->wiphy, "end of ATIM window\n"); + brcms_dbg_info(core, "end of ATIM window\n"); bcma_set32(core, D11REGOFFS(maccommand), wlc->qvalid); wlc->qvalid = 0; } @@ -8192,7 +7736,7 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) wlc_phy_noise_sample_intr(wlc_hw->band->pi); if (macintstatus & MI_GP0) { - wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d " + brcms_err(core, "wl%d: PSM microcode watchdog fired at %d " "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", @@ -8206,15 +7750,11 @@ bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) bcma_write32(core, D11REGOFFS(gptimer), 0); if (macintstatus & MI_RFDISABLE) { - BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the" - " RF Disable Input\n", wlc_hw->unit); + brcms_dbg_info(core, "wl%d: BMAC Detected a change on the" + " RF Disable Input\n", wlc_hw->unit); brcms_rfkill_set_hw_state(wlc->wl); } - /* send any enq'd tx packets. Just makes sure to jump start tx */ - if (!pktq_empty(&wlc->pkt_queue->q)) - brcms_c_send_q(wlc); - /* it isn't done and needs to be resched if macintstatus is non-zero */ return wlc->macintstatus != 0; @@ -8229,7 +7769,7 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel; u16 chanspec; - BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit); + brcms_dbg_info(core, "wl%d\n", wlc->pub->unit); chanspec = ch20mhz_chspec(ch->hw_value); @@ -8286,9 +7826,6 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF); brcms_c_edcf_setparams(wlc, false); - /* Init precedence maps for empty FIFOs */ - brcms_c_tx_prec_map_init(wlc); - /* read the ucode version if we have not yet done so */ if (wlc->ucode_rev == 0) { wlc->ucode_rev = @@ -8303,9 +7840,6 @@ void brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) if (mute_tx) brcms_b_mute(wlc->hw, true); - /* clear tx flow control */ - brcms_c_txflowcontrol_reset(wlc); - /* enable the RF Disable Delay timer */ bcma_write32(core, D11REGOFFS(rfdisabledly), RFDISABLE_DEFAULT); @@ -8464,15 +7998,6 @@ brcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, * Complete the wlc default state initializations.. */ - /* allocate our initial queue */ - wlc->pkt_queue = brcms_c_txq_alloc(wlc); - if (wlc->pkt_queue == NULL) { - wiphy_err(wl->wiphy, "wl%d: %s: failed to malloc tx queue\n", - unit, __func__); - err = 100; - goto fail; - } - wlc->bsscfg->wlc = wlc; wlc->mimoft = FT_HT; diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.h b/drivers/net/wireless/brcm80211/brcmsmac/main.h index 8debc74c54e..fb447747c2c 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.h @@ -101,9 +101,6 @@ #define DATA_BLOCK_TX_SUPR (1 << 4) -/* 802.1D Priority to TX FIFO number for wme */ -extern const u8 prio2fifo[]; - /* Ucode MCTL_WAKE override bits */ #define BRCMS_WAKE_OVERRIDE_CLKCTL 0x01 #define BRCMS_WAKE_OVERRIDE_PHYREG 0x02 @@ -242,7 +239,6 @@ struct brcms_core { /* fifo */ uint *txavail[NFIFO]; /* # tx descriptors available */ - s16 txpktpend[NFIFO]; /* tx admission control */ struct macstat *macstat_snapshot; /* mac hw prev read values */ }; @@ -382,19 +378,6 @@ struct brcms_hardware { */ }; -/* TX Queue information - * - * Each flow of traffic out of the device has a TX Queue with independent - * flow control. Several interfaces may be associated with a single TX Queue - * if they belong to the same flow of traffic from the device. For multi-channel - * operation there are independent TX Queues for each channel. - */ -struct brcms_txq_info { - struct brcms_txq_info *next; - struct pktq q; - uint stopped; /* tx flow control bits */ -}; - /* * Principal common driver data structure. * @@ -435,11 +418,8 @@ struct brcms_txq_info { * WDlast: last time wlc_watchdog() was called. * edcf_txop[IEEE80211_NUM_ACS]: current txop for each ac. * wme_retries: per-AC retry limits. - * tx_prec_map: Precedence map based on HW FIFO space. - * fifo2prec_map[NFIFO]: pointer to fifo2_prec map based on WME. * bsscfg: set of BSS configurations, idx 0 is default and always valid. * cfg: the primary bsscfg (can be AP or STA). - * tx_queues: common TX Queue list. * modulecb: * mimoft: SIGN or 11N. * cck_40txbw: 11N, cck tx b/w override when in 40MHZ mode. @@ -469,7 +449,6 @@ struct brcms_txq_info { * tempsense_lasttime; * tx_duty_cycle_ofdm: maximum allowed duty cycle for OFDM. * tx_duty_cycle_cck: maximum allowed duty cycle for CCK. - * pkt_queue: txq for transmit packets. * wiphy: * pri_scb: primary Station Control Block */ @@ -533,14 +512,9 @@ struct brcms_c_info { u16 edcf_txop[IEEE80211_NUM_ACS]; u16 wme_retries[IEEE80211_NUM_ACS]; - u16 tx_prec_map; - u16 fifo2prec_map[NFIFO]; struct brcms_bss_cfg *bsscfg; - /* tx queue */ - struct brcms_txq_info *tx_queues; - struct modulecb *modulecb; u8 mimoft; @@ -585,7 +559,6 @@ struct brcms_c_info { u16 tx_duty_cycle_ofdm; u16 tx_duty_cycle_cck; - struct brcms_txq_info *pkt_queue; struct wiphy *wiphy; struct scb pri_scb; }; @@ -637,30 +610,13 @@ struct brcms_bss_cfg { struct brcms_bss_info *current_bss; }; -extern void brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, - struct sk_buff *p, - bool commit, s8 txpktpend); -extern void brcms_c_txfifo_complete(struct brcms_c_info *wlc, uint fifo, - s8 txpktpend); -extern void brcms_c_txq_enq(struct brcms_c_info *wlc, struct scb *scb, - struct sk_buff *sdu, uint prec); -extern void brcms_c_print_txstatus(struct tx_status *txs); +extern int brcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, + struct sk_buff *p); extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, uint *blocks); -#if defined(DEBUG) -extern void brcms_c_print_txdesc(struct d11txh *txh); -#else -static inline void brcms_c_print_txdesc(struct d11txh *txh) -{ -} -#endif - extern int brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config); extern void brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags); -extern void brcms_c_send_q(struct brcms_c_info *wlc); -extern int brcms_c_prep_pdu(struct brcms_c_info *wlc, struct sk_buff *pdu, - uint *fifo); extern u16 brcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, uint mac_len); extern u32 brcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, diff --git a/drivers/net/wireless/brcm80211/brcmsmac/pub.h b/drivers/net/wireless/brcm80211/brcmsmac/pub.h index 5855f4fd16d..0148dec104f 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/pub.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/pub.h @@ -200,43 +200,6 @@ enum wlc_par_id { /* WL11N Support */ #define AMPDU_AGG_HOST 1 -/* pri is priority encoded in the packet. This maps the Packet priority to - * enqueue precedence as defined in wlc_prec_map - */ -extern const u8 wlc_prio2prec_map[]; -#define BRCMS_PRIO_TO_PREC(pri) wlc_prio2prec_map[(pri) & 7] - -#define BRCMS_PREC_COUNT 16 /* Max precedence level implemented */ - -/* Mask to describe all precedence levels */ -#define BRCMS_PREC_BMP_ALL MAXBITVAL(BRCMS_PREC_COUNT) - -/* - * This maps priority to one precedence higher - Used by PS-Poll response - * packets to simulate enqueue-at-head operation, but still maintain the - * order on the queue - */ -#define BRCMS_PRIO_TO_HI_PREC(pri) min(BRCMS_PRIO_TO_PREC(pri) + 1,\ - BRCMS_PREC_COUNT - 1) - -/* Define a bitmap of precedences comprised by each AC */ -#define BRCMS_PREC_BMP_AC_BE (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BE)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BE)) | \ - NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_EE)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_EE))) -#define BRCMS_PREC_BMP_AC_BK (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_BK)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_BK)) | \ - NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NONE)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NONE))) -#define BRCMS_PREC_BMP_AC_VI (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_CL)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_CL)) | \ - NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VI)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VI))) -#define BRCMS_PREC_BMP_AC_VO (NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_VO)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_VO)) | \ - NBITVAL(BRCMS_PRIO_TO_PREC(PRIO_8021D_NC)) | \ - NBITVAL(BRCMS_PRIO_TO_HI_PREC(PRIO_8021D_NC))) - /* network protection config */ #define BRCMS_PROT_G_SPEC 1 /* SPEC g protection */ #define BRCMS_PROT_G_OVR 2 /* SPEC g prot override */ diff --git a/drivers/net/wireless/brcm80211/brcmsmac/stf.c b/drivers/net/wireless/brcm80211/brcmsmac/stf.c index ed1d1aa71d2..dd916272249 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/stf.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/stf.c @@ -23,6 +23,7 @@ #include "channel.h" #include "main.h" #include "stf.h" +#include "debug.h" #define MIN_SPATIAL_EXPANSION 0 #define MAX_SPATIAL_EXPANSION 1 @@ -160,8 +161,8 @@ bool brcms_c_stf_stbc_rx_set(struct brcms_c_info *wlc, s32 int_val) static int brcms_c_stf_txcore_set(struct brcms_c_info *wlc, u8 Nsts, u8 core_mask) { - BCMMSG(wlc->wiphy, "wl%d: Nsts %d core_mask %x\n", - wlc->pub->unit, Nsts, core_mask); + brcms_dbg_ht(wlc->hw->d11core, "wl%d: Nsts %d core_mask %x\n", + wlc->pub->unit, Nsts, core_mask); if (hweight8(core_mask) > wlc->stf->txstreams) core_mask = 0; @@ -194,7 +195,8 @@ static int brcms_c_stf_spatial_policy_set(struct brcms_c_info *wlc, int val) int i; u8 core_mask = 0; - BCMMSG(wlc->wiphy, "wl%d: val %x\n", wlc->pub->unit, val); + brcms_dbg_ht(wlc->hw->d11core, "wl%d: val %x\n", wlc->pub->unit, + val); wlc->stf->spatial_policy = (s8) val; for (i = 1; i <= MAX_STREAMS_SUPPORTED; i++) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/types.h b/drivers/net/wireless/brcm80211/brcmsmac/types.h index e11ae83111e..ae1f3ad40d4 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/types.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/types.h @@ -246,7 +246,7 @@ #define BCMMSG(dev, fmt, args...) \ do { \ - if (brcm_msg_level & LOG_TRACE_VAL) \ + if (brcm_msg_level & BRCM_DL_INFO) \ wiphy_err(dev, "%s: " fmt, __func__, ##args); \ } while (0) @@ -281,7 +281,6 @@ struct ieee80211_tx_queue_params; struct brcms_info; struct brcms_c_info; struct brcms_hardware; -struct brcms_txq_info; struct brcms_band; struct dma_pub; struct si_pub; diff --git a/drivers/net/wireless/brcm80211/include/defs.h b/drivers/net/wireless/brcm80211/include/defs.h index f0d8c04a9c8..fb7cbcf8117 100644 --- a/drivers/net/wireless/brcm80211/include/defs.h +++ b/drivers/net/wireless/brcm80211/include/defs.h @@ -78,9 +78,14 @@ #define PM_OFF 0 #define PM_MAX 1 -/* Message levels */ -#define LOG_ERROR_VAL 0x00000001 -#define LOG_TRACE_VAL 0x00000002 +/* Debug levels */ +#define BRCM_DL_INFO 0x00000001 +#define BRCM_DL_MAC80211 0x00000002 +#define BRCM_DL_RX 0x00000004 +#define BRCM_DL_TX 0x00000008 +#define BRCM_DL_INT 0x00000010 +#define BRCM_DL_DMA 0x00000020 +#define BRCM_DL_HT 0x00000040 #define PM_OFF 0 #define PM_MAX 1 diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 29b8fa1adef..46938bc9886 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -1788,10 +1788,7 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred) } /* Initialize the geo */ - if (libipw_set_geo(priv->ieee, &ipw_geos[0])) { - printk(KERN_WARNING DRV_NAME "Could not set geo\n"); - return 0; - } + libipw_set_geo(priv->ieee, &ipw_geos[0]); priv->ieee->freq_band = LIBIPW_24GHZ_BAND; lock = LOCK_NONE; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 768bf612533..482f505f3f3 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -11269,10 +11269,31 @@ static const struct libipw_geo ipw_geos[] = { } }; +static void ipw_set_geo(struct ipw_priv *priv) +{ + int j; + + for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { + if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], + ipw_geos[j].name, 3)) + break; + } + + if (j == ARRAY_SIZE(ipw_geos)) { + IPW_WARNING("SKU [%c%c%c] not recognized.\n", + priv->eeprom[EEPROM_COUNTRY_CODE + 0], + priv->eeprom[EEPROM_COUNTRY_CODE + 1], + priv->eeprom[EEPROM_COUNTRY_CODE + 2]); + j = 0; + } + + libipw_set_geo(priv->ieee, &ipw_geos[j]); +} + #define MAX_HW_RESTARTS 5 static int ipw_up(struct ipw_priv *priv) { - int rc, i, j; + int rc, i; /* Age scan list entries found before suspend */ if (priv->suspend_time) { @@ -11310,22 +11331,7 @@ static int ipw_up(struct ipw_priv *priv) memcpy(priv->net_dev->dev_addr, priv->mac_addr, ETH_ALEN); memcpy(priv->net_dev->perm_addr, priv->mac_addr, ETH_ALEN); - for (j = 0; j < ARRAY_SIZE(ipw_geos); j++) { - if (!memcmp(&priv->eeprom[EEPROM_COUNTRY_CODE], - ipw_geos[j].name, 3)) - break; - } - if (j == ARRAY_SIZE(ipw_geos)) { - IPW_WARNING("SKU [%c%c%c] not recognized.\n", - priv->eeprom[EEPROM_COUNTRY_CODE + 0], - priv->eeprom[EEPROM_COUNTRY_CODE + 1], - priv->eeprom[EEPROM_COUNTRY_CODE + 2]); - j = 0; - } - if (libipw_set_geo(priv->ieee, &ipw_geos[j])) { - IPW_WARNING("Could not set geography."); - return 0; - } + ipw_set_geo(priv); if (priv->status & STATUS_RF_KILL_SW) { IPW_WARNING("Radio disabled by module parameter.\n"); diff --git a/drivers/net/wireless/ipw2x00/libipw.h b/drivers/net/wireless/ipw2x00/libipw.h index 0b22fb42173..6eede52ad8c 100644 --- a/drivers/net/wireless/ipw2x00/libipw.h +++ b/drivers/net/wireless/ipw2x00/libipw.h @@ -978,7 +978,7 @@ extern void libipw_network_reset(struct libipw_network *network); /* libipw_geo.c */ extern const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee); -extern int libipw_set_geo(struct libipw_device *ieee, +extern void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo); extern int libipw_is_valid_channel(struct libipw_device *ieee, diff --git a/drivers/net/wireless/ipw2x00/libipw_geo.c b/drivers/net/wireless/ipw2x00/libipw_geo.c index c9fe3c99cb0..218f2a32de2 100644 --- a/drivers/net/wireless/ipw2x00/libipw_geo.c +++ b/drivers/net/wireless/ipw2x00/libipw_geo.c @@ -132,7 +132,7 @@ u8 libipw_freq_to_channel(struct libipw_device * ieee, u32 freq) return 0; } -int libipw_set_geo(struct libipw_device *ieee, +void libipw_set_geo(struct libipw_device *ieee, const struct libipw_geo *geo) { memcpy(ieee->geo.name, geo->name, 3); @@ -143,7 +143,6 @@ int libipw_set_geo(struct libipw_device *ieee, sizeof(struct libipw_channel)); memcpy(ieee->geo.a, geo->a, ieee->geo.a_channels * sizeof(struct libipw_channel)); - return 0; } const struct libipw_geo *libipw_get_geo(struct libipw_device *ieee) diff --git a/drivers/net/wireless/iwlegacy/3945.c b/drivers/net/wireless/iwlegacy/3945.c index 87e53989433..e0b9d7fa5de 100644 --- a/drivers/net/wireless/iwlegacy/3945.c +++ b/drivers/net/wireless/iwlegacy/3945.c @@ -516,7 +516,7 @@ static void il3945_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) { struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status; + struct ieee80211_rx_status rx_status = {}; struct il_rx_pkt *pkt = rxb_addr(rxb); struct il3945_rx_frame_stats *rx_stats = IL_RX_STATS(pkt); struct il3945_rx_frame_hdr *rx_hdr = IL_RX_HDR(pkt); diff --git a/drivers/net/wireless/iwlegacy/4965-mac.c b/drivers/net/wireless/iwlegacy/4965-mac.c index eac4dc8bc87..07ffa575e3e 100644 --- a/drivers/net/wireless/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/iwlegacy/4965-mac.c @@ -613,7 +613,7 @@ void il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) { struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status; + struct ieee80211_rx_status rx_status = {}; struct il_rx_pkt *pkt = rxb_addr(rxb); struct il_rx_phy_res *phy_res; __le32 rx_pkt_status; @@ -686,7 +686,7 @@ il4965_hdl_rx(struct il_priv *il, struct il_rx_buf *rxb) /* 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; */ + /*rx_status.flag |= RX_FLAG_MACTIME_START; */ il->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h index b4bb813362b..e254cba4557 100644 --- a/drivers/net/wireless/iwlegacy/common.h +++ b/drivers/net/wireless/iwlegacy/common.h @@ -2919,9 +2919,8 @@ do { \ #define IL_DBG(level, fmt, args...) \ do { \ if (il_get_debug_level(il) & level) \ - dev_printk(KERN_ERR, &il->hw->wiphy->dev, \ - "%c %s " fmt, in_interrupt() ? 'I' : 'U', \ - __func__ , ## args); \ + dev_err(&il->hw->wiphy->dev, "%c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __func__ , ##args); \ } while (0) #define il_print_hex_dump(il, level, p, len) \ diff --git a/drivers/net/wireless/iwlwifi/Kconfig b/drivers/net/wireless/iwlwifi/Kconfig index 727fbb5db9d..5cf43236421 100644 --- a/drivers/net/wireless/iwlwifi/Kconfig +++ b/drivers/net/wireless/iwlwifi/Kconfig @@ -133,12 +133,3 @@ config IWLWIFI_P2P support when it is loaded. Say Y only if you want to experiment with P2P. - -config IWLWIFI_EXPERIMENTAL_MFP - bool "support MFP (802.11w) even if uCode doesn't advertise" - depends on IWLWIFI - help - This option enables experimental MFP (802.11W) support - even if the microcode doesn't advertise it. - - Say Y only if you want to experiment with MFP. diff --git a/drivers/net/wireless/iwlwifi/dvm/agn.h b/drivers/net/wireless/iwlwifi/dvm/agn.h index 75e12f29d9e..33b3ad2e546 100644 --- a/drivers/net/wireless/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/iwlwifi/dvm/agn.h @@ -176,8 +176,8 @@ int iwlagn_hw_valid_rtc_data_addr(u32 addr); /* lib */ int iwlagn_send_tx_power(struct iwl_priv *priv); void iwlagn_temperature(struct iwl_priv *priv); -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control); -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control); +int iwlagn_txfifo_flush(struct iwl_priv *priv); +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv); int iwlagn_send_beacon_cmd(struct iwl_priv *priv); int iwl_send_statistics_request(struct iwl_priv *priv, u8 flags, bool clear); diff --git a/drivers/net/wireless/iwlwifi/dvm/commands.h b/drivers/net/wireless/iwlwifi/dvm/commands.h index 01128c96b5d..71ab76b2b39 100644 --- a/drivers/net/wireless/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/iwlwifi/dvm/commands.h @@ -986,8 +986,7 @@ struct iwl_rem_sta_cmd { #define IWL_AGG_TX_QUEUE_MSK cpu_to_le32(0xffc00) -#define IWL_DROP_SINGLE 0 -#define IWL_DROP_ALL (BIT(IWL_RXON_CTX_BSS) | BIT(IWL_RXON_CTX_PAN)) +#define IWL_DROP_ALL BIT(1) /* * REPLY_TXFIFO_FLUSH = 0x1e(command and response) @@ -1004,14 +1003,14 @@ struct iwl_rem_sta_cmd { * the flush operation ends when both the scheduler DMA done and TXFIFO empty * are set. * - * @fifo_control: bit mask for which queues to flush + * @queue_control: bit mask for which queues to flush * @flush_control: flush controls * 0: Dump single MSDU * 1: Dump multiple MSDU according to PS, INVALID STA, TTL, TID disable. * 2: Dump all FIFO */ struct iwl_txfifo_flush_cmd { - __le32 fifo_control; + __le32 queue_control; __le16 flush_control; __le16 reserved; } __packed; diff --git a/drivers/net/wireless/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/iwlwifi/dvm/debugfs.c index 1a98fa3ab06..769a08bca86 100644 --- a/drivers/net/wireless/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/iwlwifi/dvm/debugfs.c @@ -2101,7 +2101,7 @@ static ssize_t iwl_dbgfs_txfifo_flush_write(struct file *file, if (iwl_is_rfkill(priv)) return -EFAULT; - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); + iwlagn_dev_txfifo_flush(priv); return count; } diff --git a/drivers/net/wireless/iwlwifi/dvm/dev.h b/drivers/net/wireless/iwlwifi/dvm/dev.h index 8141f91c372..29c571a5625 100644 --- a/drivers/net/wireless/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/iwlwifi/dvm/dev.h @@ -789,7 +789,6 @@ struct iwl_priv { /* remain-on-channel offload support */ struct ieee80211_channel *hw_roc_channel; struct delayed_work hw_roc_disable_work; - enum nl80211_channel_type hw_roc_chantype; int hw_roc_duration; bool hw_roc_setup, hw_roc_start_notified; diff --git a/drivers/net/wireless/iwlwifi/dvm/lib.c b/drivers/net/wireless/iwlwifi/dvm/lib.c index bef88c1a2c9..7e59be4b89b 100644 --- a/drivers/net/wireless/iwlwifi/dvm/lib.c +++ b/drivers/net/wireless/iwlwifi/dvm/lib.c @@ -136,7 +136,7 @@ int iwlagn_manage_ibss_station(struct iwl_priv *priv, * 1. acquire mutex before calling * 2. make sure rf is on and not in exit state */ -int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +int iwlagn_txfifo_flush(struct iwl_priv *priv) { struct iwl_txfifo_flush_cmd flush_cmd; struct iwl_host_cmd cmd = { @@ -146,35 +146,34 @@ int iwlagn_txfifo_flush(struct iwl_priv *priv, u16 flush_control) .data = { &flush_cmd, }, }; - might_sleep(); - memset(&flush_cmd, 0, sizeof(flush_cmd)); - if (flush_control & BIT(IWL_RXON_CTX_BSS)) - flush_cmd.fifo_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | - IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | - IWL_SCD_MGMT_MSK; - if ((flush_control & BIT(IWL_RXON_CTX_PAN)) && - (priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) - flush_cmd.fifo_control |= IWL_PAN_SCD_VO_MSK | - IWL_PAN_SCD_VI_MSK | IWL_PAN_SCD_BE_MSK | - IWL_PAN_SCD_BK_MSK | IWL_PAN_SCD_MGMT_MSK | - IWL_PAN_SCD_MULTICAST_MSK; + + flush_cmd.queue_control = IWL_SCD_VO_MSK | IWL_SCD_VI_MSK | + IWL_SCD_BE_MSK | IWL_SCD_BK_MSK | + IWL_SCD_MGMT_MSK; + if ((priv->valid_contexts != BIT(IWL_RXON_CTX_BSS))) + flush_cmd.queue_control |= IWL_PAN_SCD_VO_MSK | + IWL_PAN_SCD_VI_MSK | + IWL_PAN_SCD_BE_MSK | + IWL_PAN_SCD_BK_MSK | + IWL_PAN_SCD_MGMT_MSK | + IWL_PAN_SCD_MULTICAST_MSK; if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE) - flush_cmd.fifo_control |= IWL_AGG_TX_QUEUE_MSK; + flush_cmd.queue_control |= IWL_AGG_TX_QUEUE_MSK; - IWL_DEBUG_INFO(priv, "fifo queue control: 0X%x\n", - flush_cmd.fifo_control); - flush_cmd.flush_control = cpu_to_le16(flush_control); + IWL_DEBUG_INFO(priv, "queue control: 0x%x\n", + flush_cmd.queue_control); + flush_cmd.flush_control = cpu_to_le16(IWL_DROP_ALL); return iwl_dvm_send_cmd(priv, &cmd); } -void iwlagn_dev_txfifo_flush(struct iwl_priv *priv, u16 flush_control) +void iwlagn_dev_txfifo_flush(struct iwl_priv *priv) { mutex_lock(&priv->mutex); ieee80211_stop_queues(priv->hw); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + if (iwlagn_txfifo_flush(priv)) { IWL_ERR(priv, "flush request fail\n"); goto done; } diff --git a/drivers/net/wireless/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/iwlwifi/dvm/mac80211.c index 2d9eee93c74..fb959b00b20 100644 --- a/drivers/net/wireless/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/iwlwifi/dvm/mac80211.c @@ -168,10 +168,13 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, hw->flags |= IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_SUPPORTS_STATIC_SMPS; -#ifndef CONFIG_IWLWIFI_EXPERIMENTAL_MFP - /* enable 11w if the uCode advertise */ - if (capa->flags & IWL_UCODE_TLV_FLAGS_MFP) -#endif /* !CONFIG_IWLWIFI_EXPERIMENTAL_MFP */ + /* + * Enable 11w if advertised by firmware and software crypto + * is not enabled (as the firmware will interpret some mgmt + * packets, so enabling it with software crypto isn't safe) + */ + if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP && + !iwlwifi_mod_params.sw_crypto) hw->flags |= IEEE80211_HW_MFP_CAPABLE; hw->sta_data_size = sizeof(struct iwl_station_priv); @@ -1019,7 +1022,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, bool drop) */ if (drop) { IWL_DEBUG_MAC80211(priv, "send flush command\n"); - if (iwlagn_txfifo_flush(priv, IWL_DROP_ALL)) { + if (iwlagn_txfifo_flush(priv)) { IWL_ERR(priv, "flush request fail\n"); goto done; } @@ -1032,8 +1035,8 @@ done: } static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, int duration) { struct iwl_priv *priv = IWL_MAC80211_GET_DVM(hw); @@ -1065,7 +1068,6 @@ static int iwlagn_mac_remain_on_channel(struct ieee80211_hw *hw, } priv->hw_roc_channel = channel; - priv->hw_roc_chantype = channel_type; /* convert from ms to TU */ priv->hw_roc_duration = DIV_ROUND_UP(1000 * duration, 1024); priv->hw_roc_start_notified = false; diff --git a/drivers/net/wireless/iwlwifi/dvm/main.c b/drivers/net/wireless/iwlwifi/dvm/main.c index 30e761d31e9..e3a07c91681 100644 --- a/drivers/net/wireless/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/iwlwifi/dvm/main.c @@ -511,7 +511,7 @@ static void iwl_bg_tx_flush(struct work_struct *work) return; IWL_DEBUG_INFO(priv, "device request: flush all tx frames\n"); - iwlagn_dev_txfifo_flush(priv, IWL_DROP_ALL); + iwlagn_dev_txfifo_flush(priv); } /* @@ -1191,8 +1191,6 @@ static void iwl_option_config(struct iwl_priv *priv) static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { - priv->eeprom_data->sku = priv->eeprom_data->sku; - if (priv->eeprom_data->sku & EEPROM_SKU_CAP_11N_ENABLE && !priv->cfg->ht_params) { IWL_ERR(priv, "Invalid 11n configuration\n"); @@ -1204,7 +1202,7 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) return -EINVAL; } - IWL_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); + IWL_DEBUG_INFO(priv, "Device SKU: 0x%X\n", priv->eeprom_data->sku); priv->hw_params.tx_chains_num = num_of_ant(priv->eeprom_data->valid_tx_ant); @@ -1214,9 +1212,9 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) priv->hw_params.rx_chains_num = num_of_ant(priv->eeprom_data->valid_rx_ant); - IWL_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", - priv->eeprom_data->valid_tx_ant, - priv->eeprom_data->valid_rx_ant); + IWL_DEBUG_INFO(priv, "Valid Tx ant: 0x%X, Valid Rx ant: 0x%X\n", + priv->eeprom_data->valid_tx_ant, + priv->eeprom_data->valid_rx_ant); return 0; } @@ -1231,7 +1229,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, struct iwl_op_mode *op_mode; u16 num_mac; u32 ucode_flags; - struct iwl_trans_config trans_cfg; + struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { REPLY_RX_PHY_CMD, REPLY_RX_MPDU_CMD, @@ -1507,10 +1505,6 @@ static void iwl_op_mode_dvm_stop(struct iwl_op_mode *op_mode) iwl_tt_exit(priv); - /*This will stop the queues, move the device to low power state */ - priv->ucode_loaded = false; - iwl_trans_stop_device(priv->trans); - kfree(priv->eeprom_blob); iwl_free_eeprom_data(priv->eeprom_data); @@ -1926,8 +1920,6 @@ static void iwlagn_fw_error(struct iwl_priv *priv, bool ondemand) * commands by clearing the ready bit */ clear_bit(STATUS_READY, &priv->status); - wake_up(&priv->trans->wait_command_queue); - if (!ondemand) { /* * If firmware keep reloading, then it indicate something diff --git a/drivers/net/wireless/iwlwifi/dvm/rx.c b/drivers/net/wireless/iwlwifi/dvm/rx.c index 5a9c325804f..cac4f37cc42 100644 --- a/drivers/net/wireless/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/iwlwifi/dvm/rx.c @@ -631,8 +631,6 @@ static int iwlagn_rx_card_state_notif(struct iwl_priv *priv, 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(&priv->trans->wait_command_queue); return 0; } @@ -901,7 +899,7 @@ static int iwlagn_rx_reply_rx(struct iwl_priv *priv, struct iwl_device_cmd *cmd) { struct ieee80211_hdr *header; - struct ieee80211_rx_status rx_status; + 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; @@ -951,7 +949,7 @@ static int 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_MACTIME_MPDU;*/ + /*rx_status.flag |= RX_FLAG_MACTIME_START;*/ priv->ucode_beacon_time = le32_to_cpu(phy_res->beacon_time_stamp); diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index f5ca73a8987..4ae031f6726 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1075,14 +1075,11 @@ static void iwlagn_count_tx_err_status(struct iwl_priv *priv, u16 status) static void iwlagn_set_tx_status(struct iwl_priv *priv, struct ieee80211_tx_info *info, - struct iwlagn_tx_resp *tx_resp, - bool is_agg) + struct iwlagn_tx_resp *tx_resp) { - u16 status = le16_to_cpu(tx_resp->status.status); + u16 status = le16_to_cpu(tx_resp->status.status); info->status.rates[0].count = tx_resp->failure_frame + 1; - if (is_agg) - info->flags &= ~IEEE80211_TX_CTL_AMPDU; info->flags |= iwl_tx_status_to_mac80211(status); iwlagn_hwrate_to_tx_control(priv, le32_to_cpu(tx_resp->rate_n_flags), info); @@ -1231,7 +1228,7 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb, if (is_agg && !iwl_is_tx_success(status)) info->flags |= IEEE80211_TX_STAT_AMPDU_NO_BACK; iwlagn_set_tx_status(priv, IEEE80211_SKB_CB(skb), - tx_resp, is_agg); + tx_resp); if (!is_agg) iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); diff --git a/drivers/net/wireless/iwlwifi/dvm/ucode.c b/drivers/net/wireless/iwlwifi/dvm/ucode.c index 2cb1efbc5ed..95e6d33f515 100644 --- a/drivers/net/wireless/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/iwlwifi/dvm/ucode.c @@ -254,7 +254,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) int ret; int i; - iwl_trans_fw_alive(priv->trans); + iwl_trans_fw_alive(priv->trans, 0); if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && priv->eeprom_data->sku & EEPROM_SKU_CAP_IPAN_ENABLE) { diff --git a/drivers/net/wireless/iwlwifi/iwl-config.h b/drivers/net/wireless/iwlwifi/iwl-config.h index 87f465a49df..196266aa5a9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/iwlwifi/iwl-config.h @@ -150,7 +150,7 @@ enum iwl_led_mode { struct iwl_base_params { int eeprom_size; int num_of_queues; /* def: HW dependent */ - /* for iwl_apm_init() */ + /* for iwl_pcie_apm_init() */ u32 pll_cfg_val; const u16 max_ll_items; diff --git a/drivers/net/wireless/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/iwlwifi/iwl-devtrace.h index 678717bf62e..b3fde5f7b9b 100644 --- a/drivers/net/wireless/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/iwlwifi/iwl-devtrace.h @@ -306,7 +306,7 @@ TRACE_EVENT(iwlwifi_dev_rx_data, memcpy(__get_dynamic_array(data), ((u8 *)rxbuf) + offs, len - offs); ), - TP_printk("[%s] TX frame data", __get_str(dev)) + TP_printk("[%s] RX frame data", __get_str(dev)) ); #undef TRACE_SYSTEM diff --git a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c index f10170fe879..4a9dc9629ef 100644 --- a/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/iwlwifi/iwl-eeprom-parse.c @@ -889,8 +889,8 @@ int iwl_eeprom_check_version(struct iwl_eeprom_data *data, { if (data->eeprom_version >= trans->cfg->eeprom_ver || data->calib_version >= trans->cfg->eeprom_calib_ver) { - IWL_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", - data->eeprom_version, data->calib_version); + IWL_DEBUG_INFO(trans, "device EEPROM VER=0x%x, CALIB=0x%x\n", + data->eeprom_version, data->calib_version); return 0; } diff --git a/drivers/net/wireless/iwlwifi/iwl-fh.h b/drivers/net/wireless/iwlwifi/iwl-fh.h index 80604664174..ec48563d3c6 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/iwlwifi/iwl-fh.h @@ -267,7 +267,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) #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 RX_RB_TIMEOUT (0x11) #define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) #define FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) diff --git a/drivers/net/wireless/iwlwifi/iwl-trans.h b/drivers/net/wireless/iwlwifi/iwl-trans.h index f75ea6d73ff..e378ea6dca9 100644 --- a/drivers/net/wireless/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/iwlwifi/iwl-trans.h @@ -221,14 +221,21 @@ struct iwl_device_cmd { /** * struct iwl_hcmd_dataflag - flag for each one of the chunks of the command * - * IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's + * @IWL_HCMD_DFL_NOCOPY: By default, the command is copied to the host command's * ring. The transport layer doesn't map the command's buffer to DMA, but * rather copies it to an previously allocated DMA buffer. This flag tells * the transport layer not to copy the command, but to map the existing - * buffer. This can save memcpy and is worth with very big comamnds. + * buffer (that is passed in) instead. This saves the memcpy and allows + * commands that are bigger than the fixed buffer to be submitted. + * Note that a TFD entry after a NOCOPY one cannot be a normal copied one. + * @IWL_HCMD_DFL_DUP: Only valid without NOCOPY, duplicate the memory for this + * chunk internally and free it again after the command completes. This + * can (currently) be used only once per command. + * Note that a TFD entry after a DUP one cannot be a normal copied one. */ enum iwl_hcmd_dataflag { IWL_HCMD_DFL_NOCOPY = BIT(0), + IWL_HCMD_DFL_DUP = BIT(1), }; /** @@ -348,14 +355,17 @@ struct iwl_trans; * @start_fw: allocates and inits all the resources for the transport * layer. Also kick a fw image. * May sleep - * @fw_alive: called when the fw sends alive notification + * @fw_alive: called when the fw sends alive notification. If the fw provides + * the SCD base address in SRAM, then provide it here, or 0 otherwise. * May sleep * @stop_device:stops the whole device (embedded CPU put to reset) * May sleep * @wowlan_suspend: put the device into the correct mode for WoWLAN during * suspend. This is optional, if not implemented WoWLAN will not be * supported. This callback may sleep. - * @send_cmd:send a host command + * @send_cmd:send a host command. Must return -ERFKILL if RFkill is asserted. + * If RFkill is asserted in the middle of a SYNC host command, it must + * return -ERFKILL straight away. * May sleep only if CMD_SYNC is set * @tx: send an skb * Must be atomic @@ -385,7 +395,7 @@ struct iwl_trans_ops { int (*start_hw)(struct iwl_trans *iwl_trans); void (*stop_hw)(struct iwl_trans *iwl_trans, bool op_mode_leaving); int (*start_fw)(struct iwl_trans *trans, const struct fw_img *fw); - void (*fw_alive)(struct iwl_trans *trans); + void (*fw_alive)(struct iwl_trans *trans, u32 scd_addr); void (*stop_device)(struct iwl_trans *trans); void (*wowlan_suspend)(struct iwl_trans *trans); @@ -438,7 +448,6 @@ enum iwl_trans_state { * Set during transport allocation. * @hw_id_str: a string with info about HW ID. Set during transport allocation. * @pm_support: set to true in start_hw if link pm is supported - * @wait_command_queue: the wait_queue for SYNC host commands * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @dev_cmd_headroom: room needed for the transport's private use before the @@ -465,8 +474,6 @@ struct iwl_trans { bool pm_support; - wait_queue_head_t wait_command_queue; - /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; size_t dev_cmd_headroom; @@ -508,13 +515,13 @@ static inline void iwl_trans_stop_hw(struct iwl_trans *trans, trans->state = IWL_TRANS_NO_FW; } -static inline void iwl_trans_fw_alive(struct iwl_trans *trans) +static inline void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr) { might_sleep(); trans->state = IWL_TRANS_FW_ALIVE; - trans->ops->fw_alive(trans); + trans->ops->fw_alive(trans, scd_addr); } static inline int iwl_trans_start_fw(struct iwl_trans *trans, diff --git a/drivers/net/wireless/iwlwifi/pcie/drv.c b/drivers/net/wireless/iwlwifi/pcie/drv.c index 2a467539670..956fe6c370b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/iwlwifi/pcie/drv.c @@ -69,7 +69,6 @@ #include "iwl-trans.h" #include "iwl-drv.h" -#include "iwl-trans.h" #include "cfg.h" #include "internal.h" diff --git a/drivers/net/wireless/iwlwifi/pcie/internal.h b/drivers/net/wireless/iwlwifi/pcie/internal.h index 401178f44a3..d91d2e8c62f 100644 --- a/drivers/net/wireless/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/iwlwifi/pcie/internal.h @@ -73,7 +73,7 @@ struct isr_statistics { }; /** - * struct iwl_rx_queue - Rx queue + * struct iwl_rxq - Rx queue * @bd: driver's pointer to buffer of receive buffer descriptors (rbd) * @bd_dma: bus address of buffer of receive buffer descriptors (rbd) * @pool: @@ -91,7 +91,7 @@ struct isr_statistics { * * NOTE: rx_free and rx_used are used as a FIFO for iwl_rx_mem_buffers */ -struct iwl_rx_queue { +struct iwl_rxq { __le32 *bd; dma_addr_t bd_dma; struct iwl_rx_mem_buffer pool[RX_QUEUE_SIZE + RX_FREE_BUFFERS]; @@ -157,8 +157,8 @@ struct iwl_cmd_meta { * 32 since we don't need so many commands pending. Since the HW * still uses 256 BDs for DMA though, n_bd stays 256. As a result, * the software buffers (in the variables @meta, @txb in struct - * iwl_tx_queue) only have 32 entries, while the HW buffers (@tfds - * in the same struct) have 256. + * iwl_txq) only have 32 entries, while the HW buffers (@tfds in + * the same struct) have 256. * This means that we end up with the following: * HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 | * SW entries: | 0 | ... | 31 | @@ -182,15 +182,17 @@ struct iwl_queue { #define TFD_TX_CMD_SLOTS 256 #define TFD_CMD_SLOTS 32 -struct iwl_pcie_tx_queue_entry { +struct iwl_pcie_txq_entry { struct iwl_device_cmd *cmd; struct iwl_device_cmd *copy_cmd; struct sk_buff *skb; + /* buffer to free after command completes */ + const void *free_buf; struct iwl_cmd_meta meta; }; /** - * struct iwl_tx_queue - Tx Queue for DMA + * struct iwl_txq - Tx Queue for DMA * @q: generic Rx/Tx queue descriptor * @tfds: transmit frame descriptors (DMA memory) * @entries: transmit entries (driver state) @@ -203,10 +205,10 @@ struct iwl_pcie_tx_queue_entry { * A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame * descriptors) and required locking structures. */ -struct iwl_tx_queue { +struct iwl_txq { struct iwl_queue q; struct iwl_tfd *tfds; - struct iwl_pcie_tx_queue_entry *entries; + struct iwl_pcie_txq_entry *entries; spinlock_t lock; struct timer_list stuck_timer; struct iwl_trans_pcie *trans_pcie; @@ -236,7 +238,7 @@ struct iwl_tx_queue { * @wd_timeout: queue watchdog timeout (jiffies) */ struct iwl_trans_pcie { - struct iwl_rx_queue rxq; + struct iwl_rxq rxq; struct work_struct rx_replenish; struct iwl_trans *trans; struct iwl_drv *drv; @@ -258,7 +260,7 @@ struct iwl_trans_pcie { struct iwl_dma_ptr scd_bc_tbls; struct iwl_dma_ptr kw; - struct iwl_tx_queue *txq; + struct iwl_txq *txq; unsigned long queue_used[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; unsigned long queue_stopped[BITS_TO_LONGS(IWL_MAX_HW_QUEUES)]; @@ -268,6 +270,8 @@ struct iwl_trans_pcie { bool ucode_write_complete; wait_queue_head_t ucode_write_waitq; + wait_queue_head_t wait_command_queue; + unsigned long status; u8 cmd_queue; u8 cmd_fifo; @@ -283,13 +287,23 @@ struct iwl_trans_pcie { unsigned long wd_timeout; }; -/***************************************************** -* DRIVER STATUS FUNCTIONS -******************************************************/ -#define STATUS_HCMD_ACTIVE 0 -#define STATUS_DEVICE_ENABLED 1 -#define STATUS_TPOWER_PMI 2 -#define STATUS_INT_ENABLED 3 +/** + * enum iwl_pcie_status: status of the PCIe transport + * @STATUS_HCMD_ACTIVE: a SYNC command is being processed + * @STATUS_DEVICE_ENABLED: APM is enabled + * @STATUS_TPOWER_PMI: the device might be asleep (need to wake it up) + * @STATUS_INT_ENABLED: interrupts are enabled + * @STATUS_RFKILL: the HW RFkill switch is in KILL position + * @STATUS_FW_ERROR: the fw is in error state + */ +enum iwl_pcie_status { + STATUS_HCMD_ACTIVE, + STATUS_DEVICE_ENABLED, + STATUS_TPOWER_PMI, + STATUS_INT_ENABLED, + STATUS_RFKILL, + STATUS_FW_ERROR, +}; #define IWL_TRANS_GET_PCIE_TRANS(_iwl_trans) \ ((struct iwl_trans_pcie *) ((_iwl_trans)->trans_specific)) @@ -301,6 +315,10 @@ iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) trans_specific); } +/* + * Convention: trans API functions: iwl_trans_pcie_XXX + * Other functions: iwl_pcie_XXX + */ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, const struct pci_device_id *ent, const struct iwl_cfg *cfg); @@ -309,50 +327,43 @@ void iwl_trans_pcie_free(struct iwl_trans *trans); /***************************************************** * RX ******************************************************/ -void iwl_bg_rx_replenish(struct work_struct *data); -void iwl_irq_tasklet(struct iwl_trans *trans); -void iwl_rx_replenish(struct iwl_trans *trans); -void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q); +int iwl_pcie_rx_init(struct iwl_trans *trans); +void iwl_pcie_tasklet(struct iwl_trans *trans); +int iwl_pcie_rx_stop(struct iwl_trans *trans); +void iwl_pcie_rx_free(struct iwl_trans *trans); /***************************************************** -* ICT +* ICT - interrupt handling ******************************************************/ -void iwl_reset_ict(struct iwl_trans *trans); -void iwl_disable_ict(struct iwl_trans *trans); -int iwl_alloc_isr_ict(struct iwl_trans *trans); -void iwl_free_isr_ict(struct iwl_trans *trans); -irqreturn_t iwl_isr_ict(int irq, void *data); +irqreturn_t iwl_pcie_isr_ict(int irq, void *data); +int iwl_pcie_alloc_ict(struct iwl_trans *trans); +void iwl_pcie_free_ict(struct iwl_trans *trans); +void iwl_pcie_reset_ict(struct iwl_trans *trans); +void iwl_pcie_disable_ict(struct iwl_trans *trans); /***************************************************** * TX / HCMD ******************************************************/ -void iwl_txq_update_write_ptr(struct iwl_trans *trans, - struct iwl_tx_queue *txq); -int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - dma_addr_t addr, u16 len, u8 reset); -int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id); -int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); -void iwl_tx_cmd_complete(struct iwl_trans *trans, - struct iwl_rx_cmd_buffer *rxb, int handler_status); -void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt); +int iwl_pcie_tx_init(struct iwl_trans *trans); +void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); +int iwl_pcie_tx_stop(struct iwl_trans *trans); +void iwl_pcie_tx_free(struct iwl_trans *trans); void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, int sta_id, int tid, int frame_limit, u16 ssn); void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int queue); -void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir); -int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs); -int iwl_queue_space(const struct iwl_queue *q); - +int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id); +void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq); +int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +void iwl_pcie_hcmd_complete(struct iwl_trans *trans, + struct iwl_rx_cmd_buffer *rxb, int handler_status); +void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, + struct sk_buff_head *skbs); /***************************************************** * Error handling ******************************************************/ -int iwl_dump_fh(struct iwl_trans *trans, char **buf); -void iwl_dump_csr(struct iwl_trans *trans); +int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf); +void iwl_pcie_dump_csr(struct iwl_trans *trans); /***************************************************** * Helpers @@ -388,7 +399,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) } static inline void iwl_wake_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) + struct iwl_txq *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -399,7 +410,7 @@ static inline void iwl_wake_queue(struct iwl_trans *trans, } static inline void iwl_stop_queue(struct iwl_trans *trans, - struct iwl_tx_queue *txq) + struct iwl_txq *txq) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -411,7 +422,7 @@ static inline void iwl_stop_queue(struct iwl_trans *trans, txq->q.id); } -static inline int iwl_queue_used(const struct iwl_queue *q, int i) +static inline bool iwl_queue_used(const struct iwl_queue *q, int i) { return q->write_ptr >= q->read_ptr ? (i >= q->read_ptr && i < q->write_ptr) : @@ -423,8 +434,8 @@ static inline u8 get_cmd_index(struct iwl_queue *q, u32 index) return index & (q->n_window - 1); } -static inline const char * -trans_pcie_get_cmd_string(struct iwl_trans_pcie *trans_pcie, u8 cmd) +static inline const char *get_cmd_string(struct iwl_trans_pcie *trans_pcie, + u8 cmd) { if (!trans_pcie->command_names || !trans_pcie->command_names[cmd]) return "UNKNOWN"; diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index 41d821fbdb9..bb32510fdd6 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -76,7 +76,7 @@ * + 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 + * + In iwl_pcie_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, @@ -89,28 +89,28 @@ * * Driver sequence: * - * iwl_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 + * iwl_rxq_alloc() Allocates rx_free + * iwl_pcie_rx_replenish() Replenishes rx_free list from rx_used, and calls + * iwl_pcie_rxq_restock + * iwl_pcie_rxq_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 + * are available, schedules iwl_pcie_rx_replenish * * -- enable interrupts -- - * ISR - iwl_rx() Detach iwl_rx_mem_buffers from pool up to the + * 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 + * Calls iwl_pcie_rxq_restock to refill any empty * slots. * ... * */ -/** - * iwl_rx_queue_space - Return number of free slots available in queue. +/* + * iwl_rxq_space - Return number of free slots available in queue. */ -static int iwl_rx_queue_space(const struct iwl_rx_queue *q) +static int iwl_rxq_space(const struct iwl_rxq *q) { int s = q->read - q->write; if (s <= 0) @@ -122,11 +122,28 @@ static int iwl_rx_queue_space(const struct iwl_rx_queue *q) return s; } -/** - * iwl_rx_queue_update_write_ptr - Update the write pointer for the RX queue +/* + * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr + */ +static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) +{ + return cpu_to_le32((u32)(dma_addr >> 8)); +} + +/* + * iwl_pcie_rx_stop - stops the Rx DMA + */ +int iwl_pcie_rx_stop(struct iwl_trans *trans) +{ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, + FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); +} + +/* + * iwl_pcie_rxq_inc_wr_ptr - Update the write pointer for the RX queue */ -void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, - struct iwl_rx_queue *q) +static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_rxq *q) { unsigned long flags; u32 reg; @@ -176,16 +193,8 @@ void iwl_rx_queue_update_write_ptr(struct iwl_trans *trans, spin_unlock_irqrestore(&q->lock, flags); } -/** - * iwl_dma_addr2rbd_ptr - convert a DMA address to a uCode read buffer ptr - */ -static inline __le32 iwl_dma_addr2rbd_ptr(dma_addr_t dma_addr) -{ - return cpu_to_le32((u32)(dma_addr >> 8)); -} - -/** - * iwl_rx_queue_restock - refill RX queue from pre-allocated pool +/* + * iwl_pcie_rxq_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 @@ -195,11 +204,10 @@ static inline __le32 iwl_dma_addr2rbd_ptr(dma_addr_t dma_addr) * also updates the memory address in the firmware to reference the new * target buffer. */ -static void iwl_rx_queue_restock(struct iwl_trans *trans) +static void iwl_pcie_rxq_restock(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; + struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; unsigned long flags; @@ -215,18 +223,18 @@ static void iwl_rx_queue_restock(struct iwl_trans *trans) return; spin_lock_irqsave(&rxq->lock, flags); - while ((iwl_rx_queue_space(rxq) > 0) && (rxq->free_count)) { + while ((iwl_rxq_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); + rxb = list_first_entry(&rxq->rx_free, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); /* Point to Rx buffer via next RBD in circular buffer */ - rxq->bd[rxq->write] = iwl_dma_addr2rbd_ptr(rxb->page_dma); + rxq->bd[rxq->write] = iwl_pcie_dma_addr2rbd_ptr(rxb->page_dma); rxq->queue[rxq->write] = rxb; rxq->write = (rxq->write + 1) & RX_QUEUE_MASK; rxq->free_count--; @@ -243,24 +251,23 @@ static void iwl_rx_queue_restock(struct iwl_trans *trans) spin_lock_irqsave(&rxq->lock, flags); rxq->need_update = 1; spin_unlock_irqrestore(&rxq->lock, flags); - iwl_rx_queue_update_write_ptr(trans, rxq); + iwl_pcie_rxq_inc_wr_ptr(trans, rxq); } } /* - * iwl_rx_allocate - allocate a page for each used RBD + * iwl_pcie_rxq_alloc_rbs - allocate a page for each used RBD * * A used RBD is an Rx buffer that has been given to the stack. To use it again * a page must be allocated and the RBD must point to the page. This function * doesn't change the HW pointer but handles the list of pages that is used by - * iwl_rx_queue_restock. The latter function will update the HW to use the newly + * iwl_pcie_rxq_restock. The latter function will update the HW to use the newly * allocated buffers. */ -static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) +static void iwl_pcie_rxq_alloc_rbs(struct iwl_trans *trans, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct list_head *element; + struct iwl_rxq *rxq = &trans_pcie->rxq; struct iwl_rx_mem_buffer *rxb; struct page *page; unsigned long flags; @@ -308,10 +315,9 @@ static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) __free_pages(page, trans_pcie->rx_page_order); return; } - element = rxq->rx_used.next; - rxb = list_entry(element, struct iwl_rx_mem_buffer, list); - list_del(element); - + rxb = list_first_entry(&rxq->rx_used, struct iwl_rx_mem_buffer, + list); + list_del(&rxb->list); spin_unlock_irqrestore(&rxq->lock, flags); BUG_ON(rxb->page); @@ -343,47 +349,227 @@ static void iwl_rx_allocate(struct iwl_trans *trans, gfp_t priority) } } +static void iwl_pcie_rxq_free_rbs(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + int i; + + /* 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) { + dma_unmap_page(trans->dev, rxq->pool[i].page_dma, + PAGE_SIZE << trans_pcie->rx_page_order, + DMA_FROM_DEVICE); + __free_pages(rxq->pool[i].page, + trans_pcie->rx_page_order); + rxq->pool[i].page = NULL; + } + list_add_tail(&rxq->pool[i].list, &rxq->rx_used); + } +} + /* - * iwl_rx_replenish - Move all used buffers from rx_used to rx_free + * iwl_pcie_rx_replenish - Move all used buffers from rx_used to rx_free * * When moving to rx_free an page is allocated for the slot. * - * Also restock the Rx queue via iwl_rx_queue_restock. + * Also restock the Rx queue via iwl_pcie_rxq_restock. * This is called as a scheduled work item (except for during initialization) */ -void iwl_rx_replenish(struct iwl_trans *trans) +static void iwl_pcie_rx_replenish(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; - iwl_rx_allocate(trans, GFP_KERNEL); + iwl_pcie_rxq_alloc_rbs(trans, GFP_KERNEL); spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_rx_queue_restock(trans); + iwl_pcie_rxq_restock(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); } -static void iwl_rx_replenish_now(struct iwl_trans *trans) +static void iwl_pcie_rx_replenish_now(struct iwl_trans *trans) { - iwl_rx_allocate(trans, GFP_ATOMIC); + iwl_pcie_rxq_alloc_rbs(trans, GFP_ATOMIC); - iwl_rx_queue_restock(trans); + iwl_pcie_rxq_restock(trans); } -void iwl_bg_rx_replenish(struct work_struct *data) +static void iwl_pcie_rx_replenish_work(struct work_struct *data) { struct iwl_trans_pcie *trans_pcie = container_of(data, struct iwl_trans_pcie, rx_replenish); - iwl_rx_replenish(trans_pcie->trans); + iwl_pcie_rx_replenish(trans_pcie->trans); +} + +static int iwl_pcie_rx_alloc(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct device *dev = trans->dev; + + memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); + + spin_lock_init(&rxq->lock); + + if (WARN_ON(rxq->bd || rxq->rb_stts)) + return -EINVAL; + + /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ + rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + &rxq->bd_dma, GFP_KERNEL); + if (!rxq->bd) + goto err_bd; + + /*Allocate the driver's pointer to receive buffer status */ + rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), + &rxq->rb_stts_dma, GFP_KERNEL); + if (!rxq->rb_stts) + goto err_rb_stts; + + return 0; + +err_rb_stts: + dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; +err_bd: + return -ENOMEM; +} + +static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 rb_size; + const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ + + if (trans_pcie->rx_buf_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_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); + + /* Reset driver's Rx queue write index */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); + + /* Tell device where to find RBD circular buffer in DRAM */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, + (u32)(rxq->bd_dma >> 8)); + + /* Tell device where in DRAM to update its Rx status */ + iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, + rxq->rb_stts_dma >> 4); + + /* Enable Rx DMA + * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in + * the credit mechanism in 5000 HW RX FIFO + * Direct rx interrupts to hosts + * Rx buffer size 4 or 8k + * RB timeout 0x10 + * 256 RBDs + */ + iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, + FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | + FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | + FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + rb_size| + (RX_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(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); +} + +int iwl_pcie_rx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + + int i, err; + unsigned long flags; + + if (!rxq->bd) { + err = iwl_pcie_rx_alloc(trans); + if (err) + return err; + } + + spin_lock_irqsave(&rxq->lock, flags); + INIT_LIST_HEAD(&rxq->rx_free); + INIT_LIST_HEAD(&rxq->rx_used); + + INIT_WORK(&trans_pcie->rx_replenish, + iwl_pcie_rx_replenish_work); + + iwl_pcie_rxq_free_rbs(trans); + + 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); + + iwl_pcie_rx_replenish(trans); + + iwl_pcie_rx_hw_init(trans, rxq); + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + rxq->need_update = 1; + iwl_pcie_rxq_inc_wr_ptr(trans, rxq); + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + return 0; +} + +void iwl_pcie_rx_free(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_rxq *rxq = &trans_pcie->rxq; + unsigned long flags; + + /*if rxq->bd is NULL, it means that nothing has been allocated, + * exit now */ + if (!rxq->bd) { + IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); + return; + } + + spin_lock_irqsave(&rxq->lock, flags); + iwl_pcie_rxq_free_rbs(trans); + spin_unlock_irqrestore(&rxq->lock, flags); + + dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, + rxq->bd, rxq->bd_dma); + memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); + rxq->bd = NULL; + + if (rxq->rb_stts) + dma_free_coherent(trans->dev, + sizeof(struct iwl_rb_status), + rxq->rb_stts, rxq->rb_stts_dma); + else + IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); + memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); + rxq->rb_stts = NULL; } -static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, +static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, struct iwl_rx_mem_buffer *rxb) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_rxq *rxq = &trans_pcie->rxq; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; unsigned long flags; bool page_stolen = false; int max_len = PAGE_SIZE << trans_pcie->rx_page_order; @@ -413,8 +599,7 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, break; IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x)\n", - rxcb._offset, - trans_pcie_get_cmd_string(trans_pcie, pkt->hdr.cmd), + rxcb._offset, get_cmd_string(trans_pcie, pkt->hdr.cmd), pkt->hdr.cmd); len = le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; @@ -446,7 +631,7 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, cmd_index = get_cmd_index(&txq->q, index); if (reclaim) { - struct iwl_pcie_tx_queue_entry *ent; + struct iwl_pcie_txq_entry *ent; ent = &txq->entries[cmd_index]; cmd = ent->copy_cmd; WARN_ON_ONCE(!cmd && ent->meta.flags & CMD_WANT_HCMD); @@ -460,6 +645,9 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, /* The original command isn't needed any more */ kfree(txq->entries[cmd_index].copy_cmd); txq->entries[cmd_index].copy_cmd = NULL; + /* nor is the duplicated part of the command */ + kfree(txq->entries[cmd_index].free_buf); + txq->entries[cmd_index].free_buf = NULL; } /* @@ -473,7 +661,7 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, * iwl_trans_send_cmd() * as we reclaim the driver command queue */ if (!rxcb._page_stolen) - iwl_tx_cmd_complete(trans, &rxcb, err); + iwl_pcie_hcmd_complete(trans, &rxcb, err); else IWL_WARN(trans, "Claim null rxb?\n"); } @@ -515,17 +703,13 @@ static void iwl_rx_handle_rxbuf(struct iwl_trans *trans, spin_unlock_irqrestore(&rxq->lock, flags); } -/** - * iwl_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. +/* + * iwl_pcie_rx_handle - Main entry function for receiving responses from fw */ -static void iwl_rx_handle(struct iwl_trans *trans) +static void iwl_pcie_rx_handle(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct iwl_rxq *rxq = &trans_pcie->rxq; u32 r, i; u8 fill_rx = 0; u32 count = 8; @@ -556,7 +740,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) IWL_DEBUG_RX(trans, "rxbuf: HW = %d, SW = %d (%p)\n", r, i, rxb); - iwl_rx_handle_rxbuf(trans, rxb); + iwl_pcie_rx_handle_rb(trans, rxb); i = (i + 1) & RX_QUEUE_MASK; /* If there are a lot of unused frames, @@ -565,7 +749,7 @@ static void iwl_rx_handle(struct iwl_trans *trans) count++; if (count >= 8) { rxq->read = i; - iwl_rx_replenish_now(trans); + iwl_pcie_rx_replenish_now(trans); count = 0; } } @@ -574,39 +758,41 @@ static void iwl_rx_handle(struct iwl_trans *trans) /* Backtrack one entry */ rxq->read = i; if (fill_rx) - iwl_rx_replenish_now(trans); + iwl_pcie_rx_replenish_now(trans); else - iwl_rx_queue_restock(trans); + iwl_pcie_rxq_restock(trans); } -/** - * iwl_irq_handle_error - called for HW or SW error interrupt from card +/* + * iwl_pcie_irq_handle_error - called for HW or SW error interrupt from card */ -static void iwl_irq_handle_error(struct iwl_trans *trans) +static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex && (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); iwl_op_mode_wimax_active(trans->op_mode); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); return; } - iwl_dump_csr(trans); - iwl_dump_fh(trans, NULL); + iwl_pcie_dump_csr(trans); + iwl_pcie_dump_fh(trans, NULL); + + set_bit(STATUS_FW_ERROR, &trans_pcie->status); + clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); + wake_up(&trans_pcie->wait_command_queue); iwl_op_mode_nic_error(trans->op_mode); } -/* tasklet for iwlagn interrupt */ -void iwl_irq_tasklet(struct iwl_trans *trans) +void iwl_pcie_tasklet(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct isr_statistics *isr_stats = &trans_pcie->isr_stats; @@ -658,7 +844,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) iwl_disable_interrupts(trans); isr_stats->hw++; - iwl_irq_handle_error(trans); + iwl_pcie_irq_handle_error(trans); handled |= CSR_INT_BIT_HW_ERR; @@ -695,6 +881,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans) isr_stats->rfkill++; iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill); + if (hw_rfkill) { + set_bit(STATUS_RFKILL, &trans_pcie->status); + if (test_and_clear_bit(STATUS_HCMD_ACTIVE, + &trans_pcie->status)) + IWL_DEBUG_RF_KILL(trans, + "Rfkill while SYNC HCMD in flight\n"); + wake_up(&trans_pcie->wait_command_queue); + } else { + clear_bit(STATUS_RFKILL, &trans_pcie->status); + } handled |= CSR_INT_BIT_RF_KILL; } @@ -711,17 +907,16 @@ void iwl_irq_tasklet(struct iwl_trans *trans) IWL_ERR(trans, "Microcode SW error detected. " " Restarting 0x%X.\n", inta); isr_stats->sw++; - iwl_irq_handle_error(trans); + iwl_pcie_irq_handle_error(trans); handled |= CSR_INT_BIT_SW_ERR; } /* uCode wakes up after power-down sleep */ if (inta & CSR_INT_BIT_WAKEUP) { IWL_DEBUG_ISR(trans, "Wakeup interrupt\n"); - iwl_rx_queue_update_write_ptr(trans, &trans_pcie->rxq); + iwl_pcie_rxq_inc_wr_ptr(trans, &trans_pcie->rxq); for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) - iwl_txq_update_write_ptr(trans, - &trans_pcie->txq[i]); + iwl_pcie_txq_inc_wr_ptr(trans, &trans_pcie->txq[i]); isr_stats->wakeup++; @@ -759,7 +954,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) iwl_write8(trans, CSR_INT_PERIODIC_REG, CSR_INT_PERIODIC_DIS); - iwl_rx_handle(trans); + iwl_pcie_rx_handle(trans); /* * Enable periodic interrupt in 8 msec only if we received @@ -817,7 +1012,7 @@ void iwl_irq_tasklet(struct iwl_trans *trans) #define ICT_COUNT (ICT_SIZE / sizeof(u32)) /* Free dram table */ -void iwl_free_isr_ict(struct iwl_trans *trans) +void iwl_pcie_free_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -830,13 +1025,12 @@ void iwl_free_isr_ict(struct iwl_trans *trans) } } - /* * allocate dram shared table, it is an aligned memory * block of ICT_SIZE. * also reset all data related to ICT table interrupt. */ -int iwl_alloc_isr_ict(struct iwl_trans *trans) +int iwl_pcie_alloc_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -849,7 +1043,7 @@ int iwl_alloc_isr_ict(struct iwl_trans *trans) /* just an API sanity check ... it is guaranteed to be aligned */ if (WARN_ON(trans_pcie->ict_tbl_dma & (ICT_SIZE - 1))) { - iwl_free_isr_ict(trans); + iwl_pcie_free_ict(trans); return -EINVAL; } @@ -870,7 +1064,7 @@ int iwl_alloc_isr_ict(struct iwl_trans *trans) /* Device is going up inform it about using ICT interrupt table, * also we need to tell the driver to start using ICT interrupt. */ -void iwl_reset_ict(struct iwl_trans *trans) +void iwl_pcie_reset_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 val; @@ -900,7 +1094,7 @@ void iwl_reset_ict(struct iwl_trans *trans) } /* Device is going down disable ict interrupt usage */ -void iwl_disable_ict(struct iwl_trans *trans) +void iwl_pcie_disable_ict(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; @@ -911,7 +1105,7 @@ void iwl_disable_ict(struct iwl_trans *trans) } /* legacy (non-ICT) ISR. Assumes that trans_pcie->irq_lock is held */ -static irqreturn_t iwl_isr(int irq, void *data) +static irqreturn_t iwl_pcie_isr(int irq, void *data) { struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -958,7 +1152,7 @@ static irqreturn_t iwl_isr(int irq, void *data) #endif trans_pcie->inta |= inta; - /* iwl_irq_tasklet() will service interrupts and re-enable them */ + /* iwl_pcie_tasklet() will service interrupts and re-enable them */ if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && @@ -983,7 +1177,7 @@ none: * the interrupt we need to service, driver will set the entries back to 0 and * set index. */ -irqreturn_t iwl_isr_ict(int irq, void *data) +irqreturn_t iwl_pcie_isr_ict(int irq, void *data) { struct iwl_trans *trans = data; struct iwl_trans_pcie *trans_pcie; @@ -1003,14 +1197,13 @@ irqreturn_t iwl_isr_ict(int irq, void *data) * use legacy interrupt. */ if (unlikely(!trans_pcie->use_ict)) { - irqreturn_t ret = iwl_isr(irq, data); + irqreturn_t ret = iwl_pcie_isr(irq, data); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); return ret; } trace_iwlwifi_dev_irq(trans->dev); - /* 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. @@ -1019,7 +1212,6 @@ irqreturn_t iwl_isr_ict(int irq, void *data) inta_mask = iwl_read32(trans, CSR_INT_MASK); /* just for debug */ iwl_write32(trans, CSR_INT_MASK, 0x00000000); - /* 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. */ @@ -1068,7 +1260,7 @@ irqreturn_t iwl_isr_ict(int irq, void *data) inta &= trans_pcie->inta_mask; trans_pcie->inta |= inta; - /* iwl_irq_tasklet() will service interrupts and re-enable them */ + /* iwl_pcie_tasklet() will service interrupts and re-enable them */ if (likely(inta)) tasklet_schedule(&trans_pcie->irq_tasklet); else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) && diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index f95d88df777..f6c21e7edaf 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -74,584 +74,8 @@ #include "iwl-prph.h" #include "iwl-agn-hw.h" #include "internal.h" -/* FIXME: need to abstract out TX command (once we know what it looks like) */ -#include "dvm/commands.h" -#define SCD_QUEUECHAIN_SEL_ALL(trans, trans_pcie) \ - (((1<<trans->cfg->base_params->num_of_queues) - 1) &\ - (~(1<<(trans_pcie)->cmd_queue))) - -static int iwl_trans_rx_alloc(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - struct device *dev = trans->dev; - - memset(&trans_pcie->rxq, 0, sizeof(trans_pcie->rxq)); - - spin_lock_init(&rxq->lock); - - if (WARN_ON(rxq->bd || rxq->rb_stts)) - return -EINVAL; - - /* Allocate the circular buffer of Read Buffer Descriptors (RBDs) */ - rxq->bd = dma_zalloc_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - &rxq->bd_dma, GFP_KERNEL); - if (!rxq->bd) - goto err_bd; - - /*Allocate the driver's pointer to receive buffer status */ - rxq->rb_stts = dma_zalloc_coherent(dev, sizeof(*rxq->rb_stts), - &rxq->rb_stts_dma, GFP_KERNEL); - if (!rxq->rb_stts) - goto err_rb_stts; - - return 0; - -err_rb_stts: - dma_free_coherent(dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; -err_bd: - return -ENOMEM; -} - -static void iwl_trans_rxq_free_rx_bufs(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - int i; - - /* 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) { - dma_unmap_page(trans->dev, rxq->pool[i].page_dma, - PAGE_SIZE << trans_pcie->rx_page_order, - DMA_FROM_DEVICE); - __free_pages(rxq->pool[i].page, - trans_pcie->rx_page_order); - rxq->pool[i].page = NULL; - } - list_add_tail(&rxq->pool[i].list, &rxq->rx_used); - } -} - -static void iwl_trans_rx_hw_init(struct iwl_trans *trans, - struct iwl_rx_queue *rxq) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 rb_size; - const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - u32 rb_timeout = RX_RB_TIMEOUT; /* FIXME: RX_RB_TIMEOUT for all devices? */ - - if (trans_pcie->rx_buf_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_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - - /* Reset driver's Rx queue write index */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); - - /* Tell device where to find RBD circular buffer in DRAM */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_RBDCB_BASE_REG, - (u32)(rxq->bd_dma >> 8)); - - /* Tell device where in DRAM to update its Rx status */ - iwl_write_direct32(trans, FH_RSCSR_CHNL0_STTS_WPTR_REG, - rxq->rb_stts_dma >> 4); - - /* Enable Rx DMA - * FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in - * the credit mechanism in 5000 HW RX FIFO - * Direct rx interrupts to hosts - * Rx buffer size 4 or 8k - * RB timeout 0x10 - * 256 RBDs - */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, - FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | - FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | - FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | - 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(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); -} - -static int iwl_rx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - - int i, err; - unsigned long flags; - - if (!rxq->bd) { - err = iwl_trans_rx_alloc(trans); - if (err) - return err; - } - - spin_lock_irqsave(&rxq->lock, flags); - INIT_LIST_HEAD(&rxq->rx_free); - INIT_LIST_HEAD(&rxq->rx_used); - - iwl_trans_rxq_free_rx_bufs(trans); - - 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); - - iwl_rx_replenish(trans); - - iwl_trans_rx_hw_init(trans, rxq); - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - rxq->need_update = 1; - iwl_rx_queue_update_write_ptr(trans, rxq); - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - return 0; -} - -static void iwl_trans_pcie_rx_free(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; - unsigned long flags; - - /*if rxq->bd is NULL, it means that nothing has been allocated, - * exit now */ - if (!rxq->bd) { - IWL_DEBUG_INFO(trans, "Free NULL rx context\n"); - return; - } - - spin_lock_irqsave(&rxq->lock, flags); - iwl_trans_rxq_free_rx_bufs(trans); - spin_unlock_irqrestore(&rxq->lock, flags); - - dma_free_coherent(trans->dev, sizeof(__le32) * RX_QUEUE_SIZE, - rxq->bd, rxq->bd_dma); - memset(&rxq->bd_dma, 0, sizeof(rxq->bd_dma)); - rxq->bd = NULL; - - if (rxq->rb_stts) - dma_free_coherent(trans->dev, - sizeof(struct iwl_rb_status), - rxq->rb_stts, rxq->rb_stts_dma); - else - IWL_DEBUG_INFO(trans, "Free rxq->rb_stts which is NULL\n"); - memset(&rxq->rb_stts_dma, 0, sizeof(rxq->rb_stts_dma)); - rxq->rb_stts = NULL; -} - -static int iwl_trans_rx_stop(struct iwl_trans *trans) -{ - - /* stop Rx DMA */ - iwl_write_direct32(trans, FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); - return iwl_poll_direct_bit(trans, FH_MEM_RSSR_RX_STATUS_REG, - FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000); -} - -static int iwlagn_alloc_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr, size_t size) -{ - if (WARN_ON(ptr->addr)) - return -EINVAL; - - ptr->addr = dma_alloc_coherent(trans->dev, size, - &ptr->dma, GFP_KERNEL); - if (!ptr->addr) - return -ENOMEM; - ptr->size = size; - return 0; -} - -static void iwlagn_free_dma_ptr(struct iwl_trans *trans, - struct iwl_dma_ptr *ptr) -{ - if (unlikely(!ptr->addr)) - return; - - dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); - memset(ptr, 0, sizeof(*ptr)); -} - -static void iwl_trans_pcie_queue_stuck_timer(unsigned long data) -{ - struct iwl_tx_queue *txq = (void *)data; - struct iwl_queue *q = &txq->q; - struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; - struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); - u32 scd_sram_addr = trans_pcie->scd_base_addr + - SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); - u8 buf[16]; - int i; - - spin_lock(&txq->lock); - /* check if triggered erroneously */ - if (txq->q.read_ptr == txq->q.write_ptr) { - spin_unlock(&txq->lock); - return; - } - spin_unlock(&txq->lock); - - IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, - jiffies_to_msecs(trans_pcie->wd_timeout)); - IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", - txq->q.read_ptr, txq->q.write_ptr); - - iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); - - iwl_print_hex_error(trans, buf, sizeof(buf)); - - for (i = 0; i < FH_TCSR_CHNL_NUM; i++) - IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, - iwl_read_direct32(trans, FH_TX_TRB_REG(i))); - - for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { - u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); - u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; - bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); - u32 tbl_dw = - iwl_read_targ_mem(trans, - trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE(i)); - - if (i & 0x1) - tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; - else - tbl_dw = tbl_dw & 0x0000FFFF; - - IWL_ERR(trans, - "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", - i, active ? "" : "in", fifo, tbl_dw, - iwl_read_prph(trans, - SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), - iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); - } - - for (i = q->read_ptr; i != q->write_ptr; - i = iwl_queue_inc_wrap(i, q->n_bd)) { - struct iwl_tx_cmd *tx_cmd = - (struct iwl_tx_cmd *)txq->entries[i].cmd->payload; - IWL_ERR(trans, "scratch %d = 0x%08x\n", i, - get_unaligned_le32(&tx_cmd->scratch)); - } - - iwl_op_mode_nic_error(trans->op_mode); -} - -static int iwl_trans_txq_alloc(struct iwl_trans *trans, - struct iwl_tx_queue *txq, int slots_num, - u32 txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; - int i; - - if (WARN_ON(txq->entries || txq->tfds)) - return -EINVAL; - - setup_timer(&txq->stuck_timer, iwl_trans_pcie_queue_stuck_timer, - (unsigned long)txq); - txq->trans_pcie = trans_pcie; - - txq->q.n_window = slots_num; - - txq->entries = kcalloc(slots_num, - sizeof(struct iwl_pcie_tx_queue_entry), - GFP_KERNEL); - - if (!txq->entries) - goto error; - - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) { - txq->entries[i].cmd = - kmalloc(sizeof(struct iwl_device_cmd), - GFP_KERNEL); - if (!txq->entries[i].cmd) - goto error; - } - - /* Circular buffer of transmit frame descriptors (TFDs), - * shared with device */ - txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, - &txq->q.dma_addr, GFP_KERNEL); - if (!txq->tfds) { - IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); - goto error; - } - txq->q.id = txq_id; - - return 0; -error: - if (txq->entries && txq_id == trans_pcie->cmd_queue) - for (i = 0; i < slots_num; i++) - kfree(txq->entries[i].cmd); - kfree(txq->entries); - txq->entries = NULL; - - return -ENOMEM; - -} - -static int iwl_trans_txq_init(struct iwl_trans *trans, struct iwl_tx_queue *txq, - int slots_num, u32 txq_id) -{ - int ret; - - txq->need_update = 0; - - /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise - * iwl_queue_inc_wrap and iwl_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 */ - ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, - txq_id); - if (ret) - return ret; - - spin_lock_init(&txq->lock); - - /* - * Tell nic where to find circular buffer of Tx Frame Descriptors for - * given Tx queue, and enable the DMA channel used for that queue. - * Circular buffer (TFD queue in DRAM) physical base address */ - iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), - txq->q.dma_addr >> 8); - - return 0; -} - -/** - * iwl_tx_queue_unmap - Unmap any remaining DMA mappings and free skb's - */ -static void iwl_tx_queue_unmap(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - enum dma_data_direction dma_dir; - - if (!q->n_bd) - return; - - /* In the command queue, all the TBs are mapped as BIDI - * so unmap them as such. - */ - if (txq_id == trans_pcie->cmd_queue) - dma_dir = DMA_BIDIRECTIONAL; - else - dma_dir = DMA_TO_DEVICE; - - spin_lock_bh(&txq->lock); - while (q->write_ptr != q->read_ptr) { - iwl_txq_free_tfd(trans, txq, dma_dir); - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); - } - spin_unlock_bh(&txq->lock); -} - -/** - * iwl_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. - */ -static void iwl_tx_queue_free(struct iwl_trans *trans, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct device *dev = trans->dev; - int i; - - if (WARN_ON(!txq)) - return; - - iwl_tx_queue_unmap(trans, txq_id); - - /* De-alloc array of command/tx buffers */ - if (txq_id == trans_pcie->cmd_queue) - for (i = 0; i < txq->q.n_window; i++) { - kfree(txq->entries[i].cmd); - kfree(txq->entries[i].copy_cmd); - } - - /* De-alloc circular buffer of TFDs */ - if (txq->q.n_bd) { - dma_free_coherent(dev, sizeof(struct iwl_tfd) * - txq->q.n_bd, txq->tfds, txq->q.dma_addr); - memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); - } - - kfree(txq->entries); - txq->entries = NULL; - - del_timer_sync(&txq->stuck_timer); - - /* 0-fill queue descriptor structure */ - memset(txq, 0, sizeof(*txq)); -} - -/** - * iwl_trans_tx_free - Free TXQ Context - * - * Destroy all TX DMA queues and structures - */ -static void iwl_trans_pcie_tx_free(struct iwl_trans *trans) -{ - int txq_id; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - /* Tx queues */ - if (trans_pcie->txq) { - for (txq_id = 0; - txq_id < trans->cfg->base_params->num_of_queues; txq_id++) - iwl_tx_queue_free(trans, txq_id); - } - - kfree(trans_pcie->txq); - trans_pcie->txq = NULL; - - iwlagn_free_dma_ptr(trans, &trans_pcie->kw); - - iwlagn_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); -} - -/** - * iwl_trans_tx_alloc - allocate TX context - * Allocate all Tx DMA structures and initialize them - * - * @param priv - * @return error code - */ -static int iwl_trans_tx_alloc(struct iwl_trans *trans) -{ - int ret; - int txq_id, slots_num; - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - - u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * - sizeof(struct iwlagn_scd_bc_tbl); - - /*It is not allowed to alloc twice, so warn when this happens. - * We cannot rely on the previous allocation, so free and fail */ - if (WARN_ON(trans_pcie->txq)) { - ret = -EINVAL; - goto error; - } - - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, - scd_bc_tbls_size); - if (ret) { - IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); - goto error; - } - - /* Alloc keep-warm buffer */ - ret = iwlagn_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); - if (ret) { - IWL_ERR(trans, "Keep Warm allocation failed\n"); - goto error; - } - - trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, - sizeof(struct iwl_tx_queue), GFP_KERNEL); - if (!trans_pcie->txq) { - IWL_ERR(trans, "Not enough memory for txq\n"); - ret = ENOMEM; - goto error; - } - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_alloc(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); - goto error; - } - } - - return 0; - -error: - iwl_trans_pcie_tx_free(trans); - - return ret; -} -static int iwl_tx_init(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ret; - int txq_id, slots_num; - unsigned long flags; - bool alloc = false; - - if (!trans_pcie->txq) { - ret = iwl_trans_tx_alloc(trans); - if (ret) - goto error; - alloc = true; - } - - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - /* Turn off all Tx DMA fifos */ - iwl_write_prph(trans, SCD_TXFACT, 0); - - /* Tell NIC where to find the "keep warm" buffer */ - iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, - trans_pcie->kw.dma >> 4); - - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) { - slots_num = (txq_id == trans_pcie->cmd_queue) ? - TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; - ret = iwl_trans_txq_init(trans, &trans_pcie->txq[txq_id], - slots_num, txq_id); - if (ret) { - IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); - goto error; - } - } - - return 0; -error: - /*Upon error, free only if we allocated something */ - if (alloc) - iwl_trans_pcie_tx_free(trans); - return ret; -} - -static void iwl_set_pwr_vmain(struct iwl_trans *trans) +static void iwl_pcie_set_pwr_vmain(struct iwl_trans *trans) { /* * (for documentation purposes) @@ -673,18 +97,11 @@ static void iwl_set_pwr_vmain(struct iwl_trans *trans) #define PCI_CFG_LINK_CTRL_VAL_L0S_EN 0x01 #define PCI_CFG_LINK_CTRL_VAL_L1_EN 0x02 -static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans) +static void iwl_pcie_apm_config(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u16 pci_lnk_ctl; - - pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, - &pci_lnk_ctl); - return pci_lnk_ctl; -} + u16 lctl; -static void iwl_apm_config(struct iwl_trans *trans) -{ /* * HW bug W/A for instability in PCIe bus L0S->L1 transition. * Check if BIOS (or OS) enabled L1-ASPM on this device. @@ -693,29 +110,27 @@ static void iwl_apm_config(struct iwl_trans *trans) * If not (unlikely), enable L0S, so there is at least some * power savings, even without L1. */ - u16 lctl = iwl_pciexp_link_ctrl(trans); + pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL, &lctl); if ((lctl & PCI_CFG_LINK_CTRL_VAL_L1_EN) == PCI_CFG_LINK_CTRL_VAL_L1_EN) { /* L1-ASPM enabled; disable(!) L0S */ iwl_set_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - dev_printk(KERN_INFO, trans->dev, - "L1 Enabled; Disabling L0S\n"); + dev_info(trans->dev, "L1 Enabled; Disabling L0S\n"); } else { /* L1-ASPM disabled; enable(!) L0S */ iwl_clear_bit(trans, CSR_GIO_REG, CSR_GIO_REG_VAL_L0S_ENABLED); - dev_printk(KERN_INFO, trans->dev, - "L1 Disabled; Enabling L0S\n"); + dev_info(trans->dev, "L1 Disabled; Enabling L0S\n"); } trans->pm_support = !(lctl & PCI_CFG_LINK_CTRL_VAL_L0S_EN); } /* * Start up NIC'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_pcie_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ -static int iwl_apm_init(struct iwl_trans *trans) +static int iwl_pcie_apm_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret = 0; @@ -747,7 +162,7 @@ static int iwl_apm_init(struct iwl_trans *trans) iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); - iwl_apm_config(trans); + iwl_pcie_apm_config(trans); /* Configure analog phase-lock-loop before activating to D0A */ if (trans->cfg->base_params->pll_cfg_val) @@ -793,7 +208,7 @@ out: return ret; } -static int iwl_apm_stop_master(struct iwl_trans *trans) +static int iwl_pcie_apm_stop_master(struct iwl_trans *trans) { int ret = 0; @@ -811,7 +226,7 @@ static int iwl_apm_stop_master(struct iwl_trans *trans) return ret; } -static void iwl_apm_stop(struct iwl_trans *trans) +static void iwl_pcie_apm_stop(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); IWL_DEBUG_INFO(trans, "Stop card, put in low power state\n"); @@ -819,7 +234,7 @@ static void iwl_apm_stop(struct iwl_trans *trans) clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); /* Stop device's DMA activity */ - iwl_apm_stop_master(trans); + iwl_pcie_apm_stop_master(trans); /* Reset the entire device */ iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); @@ -834,29 +249,29 @@ static void iwl_apm_stop(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } -static int iwl_nic_init(struct iwl_trans *trans) +static int iwl_pcie_nic_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); unsigned long flags; /* nic_init */ spin_lock_irqsave(&trans_pcie->irq_lock, flags); - iwl_apm_init(trans); + iwl_pcie_apm_init(trans); /* Set interrupt coalescing calibration timer to default (512 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_CALIB_TIMEOUT_DEF); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_set_pwr_vmain(trans); + iwl_pcie_set_pwr_vmain(trans); iwl_op_mode_nic_config(trans->op_mode); /* Allocate the RX queue, or reset if it is already allocated */ - iwl_rx_init(trans); + iwl_pcie_rx_init(trans); /* Allocate or reset and init all Tx and Command queues */ - if (iwl_tx_init(trans)) + if (iwl_pcie_tx_init(trans)) return -ENOMEM; if (trans->cfg->base_params->shadow_reg_enable) { @@ -871,7 +286,7 @@ static int iwl_nic_init(struct iwl_trans *trans) #define HW_READY_TIMEOUT (50) /* Note: returns poll_bit return value, which is >= 0 if success */ -static int iwl_set_hw_ready(struct iwl_trans *trans) +static int iwl_pcie_set_hw_ready(struct iwl_trans *trans) { int ret; @@ -889,14 +304,14 @@ static int iwl_set_hw_ready(struct iwl_trans *trans) } /* Note: returns standard 0/-ERROR code */ -static int iwl_prepare_card_hw(struct iwl_trans *trans) +static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) { int ret; int t = 0; IWL_DEBUG_INFO(trans, "iwl_trans_prepare_card_hw enter\n"); - ret = iwl_set_hw_ready(trans); + ret = iwl_pcie_set_hw_ready(trans); /* If the card is ready, exit 0 */ if (ret >= 0) return 0; @@ -906,7 +321,7 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans) CSR_HW_IF_CONFIG_REG_PREPARE); do { - ret = iwl_set_hw_ready(trans); + ret = iwl_pcie_set_hw_ready(trans); if (ret >= 0) return 0; @@ -920,7 +335,7 @@ static int iwl_prepare_card_hw(struct iwl_trans *trans) /* * ucode */ -static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, +static int iwl_pcie_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, dma_addr_t phy_addr, u32 byte_cnt) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -967,7 +382,7 @@ static int iwl_load_firmware_chunk(struct iwl_trans *trans, u32 dst_addr, return 0; } -static int iwl_load_section(struct iwl_trans *trans, u8 section_num, +static int iwl_pcie_load_section(struct iwl_trans *trans, u8 section_num, const struct fw_desc *section) { u8 *v_addr; @@ -988,8 +403,9 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, copy_size = min_t(u32, PAGE_SIZE, section->len - offset); memcpy(v_addr, (u8 *)section->data + offset, copy_size); - ret = iwl_load_firmware_chunk(trans, section->offset + offset, - p_addr, copy_size); + ret = iwl_pcie_load_firmware_chunk(trans, + section->offset + offset, + p_addr, copy_size); if (ret) { IWL_ERR(trans, "Could not load the [%d] uCode section\n", @@ -1002,7 +418,7 @@ static int iwl_load_section(struct iwl_trans *trans, u8 section_num, return ret; } -static int iwl_load_given_ucode(struct iwl_trans *trans, +static int iwl_pcie_load_given_ucode(struct iwl_trans *trans, const struct fw_img *image) { int i, ret = 0; @@ -1011,7 +427,7 @@ static int iwl_load_given_ucode(struct iwl_trans *trans, if (!image->sec[i].data) break; - ret = iwl_load_section(trans, i, &image->sec[i]); + ret = iwl_pcie_load_section(trans, i, &image->sec[i]); if (ret) return ret; } @@ -1025,15 +441,18 @@ static int iwl_load_given_ucode(struct iwl_trans *trans, static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, const struct fw_img *fw) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; bool hw_rfkill; /* This may fail if AMT took ownership of the device */ - if (iwl_prepare_card_hw(trans)) { + if (iwl_pcie_prepare_card_hw(trans)) { IWL_WARN(trans, "Exit HW not ready\n"); return -EIO; } + clear_bit(STATUS_FW_ERROR, &trans_pcie->status); + iwl_enable_rfkill_int(trans); /* If platform's RF_KILL switch is NOT set to KILL */ @@ -1044,7 +463,7 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, iwl_write32(trans, CSR_INT, 0xFFFFFFFF); - ret = iwl_nic_init(trans); + ret = iwl_pcie_nic_init(trans); if (ret) { IWL_ERR(trans, "Unable to init nic\n"); return ret; @@ -1064,125 +483,13 @@ static int iwl_trans_pcie_start_fw(struct iwl_trans *trans, iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ - return iwl_load_given_ucode(trans, fw); + return iwl_pcie_load_given_ucode(trans, fw); } -/* - * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask - */ -static void iwl_trans_txq_set_sched(struct iwl_trans *trans, u32 mask) +static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) { - struct iwl_trans_pcie __maybe_unused *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - - iwl_write_prph(trans, SCD_TXFACT, mask); -} - -static void iwl_tx_start(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u32 a; - int chan; - u32 reg_val; - - /* make sure all queue are not stopped/used */ - memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); - memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); - - trans_pcie->scd_base_addr = - iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); - a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; - /* reset conext data memory */ - for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - /* reset tx status memory */ - for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; - a += 4) - iwl_write_targ_mem(trans, a, 0); - for (; a < trans_pcie->scd_base_addr + - SCD_TRANS_TBL_OFFSET_QUEUE( - trans->cfg->base_params->num_of_queues); - a += 4) - iwl_write_targ_mem(trans, a, 0); - - iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, - trans_pcie->scd_bc_tbls.dma >> 10); - - /* The chain extension of the SCD doesn't work well. This feature is - * enabled by default by the HW, so we need to disable it manually. - */ - iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); - - iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, - trans_pcie->cmd_fifo); - - /* Activate all Tx DMA/FIFO channels */ - iwl_trans_txq_set_sched(trans, IWL_MASK(0, 7)); - - /* Enable DMA channel */ - for (chan = 0; chan < FH_TCSR_CHNL_NUM ; chan++) - iwl_write_direct32(trans, 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(trans, FH_TX_CHICKEN_BITS_REG); - iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, - reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); - - /* Enable L1-Active */ - iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, - APMG_PCIDEV_STT_VAL_L1_ACT_DIS); -} - -static void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) -{ - iwl_reset_ict(trans); - iwl_tx_start(trans); -} - -/** - * iwlagn_txq_ctx_stop - Stop all Tx DMA channels - */ -static int iwl_trans_tx_stop(struct iwl_trans *trans) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int ch, txq_id, ret; - unsigned long flags; - - /* Turn off all Tx DMA fifos */ - spin_lock_irqsave(&trans_pcie->irq_lock, flags); - - iwl_trans_txq_set_sched(trans, 0); - - /* Stop each Tx DMA channel, and wait for it to be idle */ - for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { - iwl_write_direct32(trans, - FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); - ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, - FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); - if (ret < 0) - IWL_ERR(trans, - "Failing on timeout while stopping DMA channel %d [0x%08x]\n", - ch, - iwl_read_direct32(trans, - FH_TSSR_TX_STATUS_REG)); - } - spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - - if (!trans_pcie->txq) { - IWL_WARN(trans, - "Stopping tx queues that aren't allocated...\n"); - return 0; - } - - /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; - txq_id++) - iwl_tx_queue_unmap(trans, txq_id); - - return 0; + iwl_pcie_reset_ict(trans); + iwl_pcie_tx_start(trans, scd_addr); } static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) @@ -1196,7 +503,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); /* device going down, Stop using ICT table */ - iwl_disable_ict(trans); + iwl_pcie_disable_ict(trans); /* * If a HW restart happens during firmware loading, @@ -1206,8 +513,8 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) * already dead. */ if (test_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status)) { - iwl_trans_tx_stop(trans); - iwl_trans_rx_stop(trans); + iwl_pcie_tx_stop(trans); + iwl_pcie_rx_stop(trans); /* Power-down device's busmaster DMA clocks */ iwl_write_prph(trans, APMG_CLK_DIS_REG, @@ -1220,7 +527,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ - iwl_apm_stop(trans); + iwl_pcie_apm_stop(trans); /* Upon stop, the APM issues an interrupt if HW RF kill is set. * Clean again the interrupt here @@ -1245,6 +552,7 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans) clear_bit(STATUS_INT_ENABLED, &trans_pcie->status); clear_bit(STATUS_DEVICE_ENABLED, &trans_pcie->status); clear_bit(STATUS_TPOWER_PMI, &trans_pcie->status); + clear_bit(STATUS_RFKILL, &trans_pcie->status); } static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) @@ -1258,171 +566,6 @@ static void iwl_trans_pcie_wowlan_suspend(struct iwl_trans *trans) CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } -static int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, - struct iwl_device_cmd *dev_cmd, int txq_id) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; - struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *) dev_cmd->payload; - struct iwl_cmd_meta *out_meta; - struct iwl_tx_queue *txq; - struct iwl_queue *q; - dma_addr_t phys_addr = 0; - dma_addr_t txcmd_phys; - dma_addr_t scratch_phys; - u16 len, firstlen, secondlen; - u8 wait_write_ptr = 0; - __le16 fc = hdr->frame_control; - u8 hdr_len = ieee80211_hdrlen(fc); - u16 __maybe_unused wifi_seq; - - txq = &trans_pcie->txq[txq_id]; - q = &txq->q; - - if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { - WARN_ON_ONCE(1); - return -EINVAL; - } - - spin_lock(&txq->lock); - - /* In AGG mode, the index in the ring must correspond to the WiFi - * sequence number. This is a HW requirements to help the SCD to parse - * the BA. - * Check here that the packets are in the right place on the ring. - */ -#ifdef CONFIG_IWLWIFI_DEBUG - wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && - ((wifi_seq & 0xff) != q->write_ptr), - "Q: %d WiFi Seq %d tfdNum %d", - txq_id, wifi_seq, q->write_ptr); -#endif - - /* Set up driver data for this TFD */ - txq->entries[q->write_ptr].skb = skb; - txq->entries[q->write_ptr].cmd = dev_cmd; - - dev_cmd->hdr.cmd = REPLY_TX; - dev_cmd->hdr.sequence = - cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | - INDEX_TO_SEQ(q->write_ptr))); - - /* Set up first empty entry in queue's array of Tx/cmd buffers */ - out_meta = &txq->entries[q->write_ptr].meta; - - /* - * 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 = dma_map_single(trans->dev, - &dev_cmd->hdr, firstlen, - DMA_BIDIRECTIONAL); - if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) - goto out_err; - dma_unmap_addr_set(out_meta, mapping, txcmd_phys); - dma_unmap_len_set(out_meta, len, firstlen); - - if (!ieee80211_has_morefrags(fc)) { - 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 = dma_map_single(trans->dev, skb->data + hdr_len, - secondlen, DMA_TO_DEVICE); - if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { - dma_unmap_single(trans->dev, - dma_unmap_addr(out_meta, mapping), - dma_unmap_len(out_meta, len), - DMA_BIDIRECTIONAL); - goto out_err; - } - } - - /* Attach buffers to TFD */ - iwlagn_txq_attach_buf_to_tfd(trans, txq, txcmd_phys, firstlen, 1); - if (secondlen > 0) - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, - secondlen, 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 */ - dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); - tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - - IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", - le16_to_cpu(dev_cmd->hdr.sequence)); - IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - - /* Set up entry for this TFD in Tx byte-count array */ - iwl_trans_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - - dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, - DMA_BIDIRECTIONAL); - - trace_iwlwifi_dev_tx(trans->dev, skb, - &txq->tfds[txq->q.write_ptr], - sizeof(struct iwl_tfd), - &dev_cmd->hdr, firstlen, - skb->data + hdr_len, secondlen); - trace_iwlwifi_dev_tx_data(trans->dev, skb, - skb->data + hdr_len, secondlen); - - /* start timer if queue currently empty */ - if (txq->need_update && q->read_ptr == q->write_ptr && - trans_pcie->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); - - /* 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(trans, txq); - - /* - * 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. - */ - if (iwl_queue_space(q) < q->high_mark) { - if (wait_write_ptr) { - txq->need_update = 1; - iwl_txq_update_write_ptr(trans, txq); - } else { - iwl_stop_queue(trans, txq); - } - } - spin_unlock(&txq->lock); - return 0; - out_err: - spin_unlock(&txq->lock); - return -1; -} - static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -1433,29 +576,28 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans) if (!trans_pcie->irq_requested) { tasklet_init(&trans_pcie->irq_tasklet, (void (*)(unsigned long)) - iwl_irq_tasklet, (unsigned long)trans); + iwl_pcie_tasklet, (unsigned long)trans); - iwl_alloc_isr_ict(trans); + iwl_pcie_alloc_ict(trans); - err = request_irq(trans_pcie->irq, iwl_isr_ict, IRQF_SHARED, - DRV_NAME, trans); + err = request_irq(trans_pcie->irq, iwl_pcie_isr_ict, + IRQF_SHARED, DRV_NAME, trans); if (err) { IWL_ERR(trans, "Error allocating IRQ %d\n", trans_pcie->irq); goto error; } - INIT_WORK(&trans_pcie->rx_replenish, iwl_bg_rx_replenish); trans_pcie->irq_requested = true; } - err = iwl_prepare_card_hw(trans); + err = iwl_pcie_prepare_card_hw(trans); if (err) { IWL_ERR(trans, "Error while preparing HW: %d\n", err); goto err_free_irq; } - iwl_apm_init(trans); + iwl_pcie_apm_init(trans); /* From now on, the op_mode will be kept updated about RF kill state */ iwl_enable_rfkill_int(trans); @@ -1469,7 +611,7 @@ err_free_irq: trans_pcie->irq_requested = false; free_irq(trans_pcie->irq, trans); error: - iwl_free_isr_ict(trans); + iwl_pcie_free_ict(trans); tasklet_kill(&trans_pcie->irq_tasklet); return err; } @@ -1485,7 +627,7 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, iwl_disable_interrupts(trans); spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - iwl_apm_stop(trans); + iwl_pcie_apm_stop(trans); spin_lock_irqsave(&trans_pcie->irq_lock, flags); iwl_disable_interrupts(trans); @@ -1509,27 +651,6 @@ static void iwl_trans_pcie_stop_hw(struct iwl_trans *trans, } } -static void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, - struct sk_buff_head *skbs) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - /* n_bd is usually 256 => n_bd - 1 = 0xff */ - int tfd_num = ssn & (txq->q.n_bd - 1); - - spin_lock(&txq->lock); - - if (txq->q.read_ptr != tfd_num) { - IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", - txq_id, txq->q.read_ptr, tfd_num, ssn); - iwl_tx_queue_reclaim(trans, txq_id, tfd_num, skbs); - if (iwl_queue_space(&txq->q) > txq->q.low_mark) - iwl_wake_queue(trans, txq); - } - - spin_unlock(&txq->lock); -} - static void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val) { writeb(val, IWL_TRANS_GET_PCIE_TRANS(trans)->hw_base + ofs); @@ -1576,12 +697,12 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - iwl_trans_pcie_tx_free(trans); - iwl_trans_pcie_rx_free(trans); + iwl_pcie_tx_free(trans); + iwl_pcie_rx_free(trans); if (trans_pcie->irq_requested == true) { free_irq(trans_pcie->irq, trans); - iwl_free_isr_ict(trans); + iwl_pcie_free_ict(trans); } pci_disable_msi(trans_pcie->pci_dev); @@ -1627,10 +748,10 @@ static int iwl_trans_pcie_resume(struct iwl_trans *trans) #define IWL_FLUSH_WAIT_MS 2000 -static int iwl_trans_pcie_wait_tx_queue_empty(struct iwl_trans *trans) +static int iwl_trans_pcie_wait_txq_empty(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq; + struct iwl_txq *txq; struct iwl_queue *q; int cnt; unsigned long now = jiffies; @@ -1674,7 +795,7 @@ static const char *get_fh_string(int cmd) #undef IWL_CMD } -int iwl_dump_fh(struct iwl_trans *trans, char **buf) +int iwl_pcie_dump_fh(struct iwl_trans *trans, char **buf) { int i; static const u32 fh_tbl[] = { @@ -1753,7 +874,7 @@ static const char *get_csr_string(int cmd) #undef IWL_CMD } -void iwl_dump_csr(struct iwl_trans *trans) +void iwl_pcie_dump_csr(struct iwl_trans *trans) { int i; static const u32 csr_tbl[] = { @@ -1810,7 +931,6 @@ static ssize_t iwl_dbgfs_##name##_write(struct file *file, \ const char __user *user_buf, \ size_t count, loff_t *ppos); - #define DEBUGFS_READ_FILE_OPS(name) \ DEBUGFS_READ_FUNC(name); \ static const struct file_operations iwl_dbgfs_##name##_ops = { \ @@ -1843,7 +963,7 @@ static ssize_t iwl_dbgfs_tx_queue_read(struct file *file, { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq; + struct iwl_txq *txq; struct iwl_queue *q; char *buf; int pos = 0; @@ -1880,7 +1000,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, { struct iwl_trans *trans = file->private_data; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_rx_queue *rxq = &trans_pcie->rxq; + struct iwl_rxq *rxq = &trans_pcie->rxq; char buf[256]; int pos = 0; const size_t bufsz = sizeof(buf); @@ -1999,7 +1119,7 @@ static ssize_t iwl_dbgfs_csr_write(struct file *file, if (sscanf(buf, "%d", &csr) != 1) return -EFAULT; - iwl_dump_csr(trans); + iwl_pcie_dump_csr(trans); return count; } @@ -2013,7 +1133,7 @@ static ssize_t iwl_dbgfs_fh_reg_read(struct file *file, int pos = 0; ssize_t ret = -EFAULT; - ret = pos = iwl_dump_fh(trans, &buf); + ret = pos = iwl_pcie_dump_fh(trans, &buf); if (buf) { ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); @@ -2082,7 +1202,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .wowlan_suspend = iwl_trans_pcie_wowlan_suspend, - .send_cmd = iwl_trans_pcie_send_cmd, + .send_cmd = iwl_trans_pcie_send_hcmd, .tx = iwl_trans_pcie_tx, .reclaim = iwl_trans_pcie_reclaim, @@ -2092,7 +1212,7 @@ static const struct iwl_trans_ops trans_ops_pcie = { .dbgfs_register = iwl_trans_pcie_dbgfs_register, - .wait_tx_queue_empty = iwl_trans_pcie_wait_tx_queue_empty, + .wait_tx_queue_empty = iwl_trans_pcie_wait_txq_empty, #ifdef CONFIG_PM_SLEEP .suspend = iwl_trans_pcie_suspend, @@ -2117,7 +1237,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans = kzalloc(sizeof(struct iwl_trans) + sizeof(struct iwl_trans_pcie), GFP_KERNEL); - if (WARN_ON(!trans)) + if (!trans) return NULL; trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2150,43 +1270,38 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, DMA_BIT_MASK(32)); /* both attempts failed: */ if (err) { - dev_printk(KERN_ERR, &pdev->dev, - "No suitable DMA available.\n"); + dev_err(&pdev->dev, "No suitable DMA available\n"); goto out_pci_disable_device; } } err = pci_request_regions(pdev, DRV_NAME); if (err) { - dev_printk(KERN_ERR, &pdev->dev, - "pci_request_regions failed\n"); + dev_err(&pdev->dev, "pci_request_regions failed\n"); goto out_pci_disable_device; } trans_pcie->hw_base = pci_ioremap_bar(pdev, 0); if (!trans_pcie->hw_base) { - dev_printk(KERN_ERR, &pdev->dev, "pci_ioremap_bar failed\n"); + dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); err = -ENODEV; goto out_pci_release_regions; } - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_len = 0x%08llx\n", - (unsigned long long) pci_resource_len(pdev, 0)); - dev_printk(KERN_INFO, &pdev->dev, - "pci_resource_base = %p\n", trans_pcie->hw_base); - - dev_printk(KERN_INFO, &pdev->dev, - "HW Revision ID = 0x%X\n", pdev->revision); - /* 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); err = pci_enable_msi(pdev); - if (err) - dev_printk(KERN_ERR, &pdev->dev, - "pci_enable_msi failed(0X%x)\n", err); + if (err) { + dev_err(&pdev->dev, "pci_enable_msi failed(0X%x)\n", err); + /* enable rfkill interrupt: hw bug w/a */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); + if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { + pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; + pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); + } + } trans->dev = &pdev->dev; trans_pcie->irq = pdev->irq; @@ -2196,16 +1311,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); - /* TODO: Move this away, not needed if not MSI */ - /* enable rfkill interrupt: hw bug w/a */ - pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); - if (pci_cmd & PCI_COMMAND_INTX_DISABLE) { - pci_cmd &= ~PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); - } - /* Initialize the wait queue for commands */ - init_waitqueue_head(&trans->wait_command_queue); + init_waitqueue_head(&trans_pcie->wait_command_queue); spin_lock_init(&trans->reg_lock); snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index db3efbb84d9..6c5b867c353 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -42,12 +42,170 @@ #define IWL_TX_CRC_SIZE 4 #define IWL_TX_DELIMITER_SIZE 4 -/** - * iwl_trans_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array +/*************** 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. + * + ***************************************************/ +static int iwl_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; +} + +/* + * iwl_queue_init - Initialize queue's high/low-water and read/write indexes + */ +static int iwl_queue_init(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_queue_inc_wrap + * and iwl_queue_dec_wrap are broken. */ + if (WARN_ON(!is_power_of_2(count))) + return -EINVAL; + + /* slots_num must be power-of-two size, otherwise + * get_cmd_index is broken. */ + if (WARN_ON(!is_power_of_2(slots_num))) + return -EINVAL; + + 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 = 0; + q->read_ptr = 0; + + return 0; +} + +static int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr, size_t size) +{ + if (WARN_ON(ptr->addr)) + return -EINVAL; + + ptr->addr = dma_alloc_coherent(trans->dev, size, + &ptr->dma, GFP_KERNEL); + if (!ptr->addr) + return -ENOMEM; + ptr->size = size; + return 0; +} + +static void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, + struct iwl_dma_ptr *ptr) +{ + if (unlikely(!ptr->addr)) + return; + + dma_free_coherent(trans->dev, ptr->size, ptr->addr, ptr->dma); + memset(ptr, 0, sizeof(*ptr)); +} + +static void iwl_pcie_txq_stuck_timer(unsigned long data) +{ + struct iwl_txq *txq = (void *)data; + struct iwl_queue *q = &txq->q; + struct iwl_trans_pcie *trans_pcie = txq->trans_pcie; + struct iwl_trans *trans = iwl_trans_pcie_get_trans(trans_pcie); + u32 scd_sram_addr = trans_pcie->scd_base_addr + + SCD_TX_STTS_QUEUE_OFFSET(txq->q.id); + u8 buf[16]; + int i; + + spin_lock(&txq->lock); + /* check if triggered erroneously */ + if (txq->q.read_ptr == txq->q.write_ptr) { + spin_unlock(&txq->lock); + return; + } + spin_unlock(&txq->lock); + + IWL_ERR(trans, "Queue %d stuck for %u ms.\n", txq->q.id, + jiffies_to_msecs(trans_pcie->wd_timeout)); + IWL_ERR(trans, "Current SW read_ptr %d write_ptr %d\n", + txq->q.read_ptr, txq->q.write_ptr); + + iwl_read_targ_mem_bytes(trans, scd_sram_addr, buf, sizeof(buf)); + + iwl_print_hex_error(trans, buf, sizeof(buf)); + + for (i = 0; i < FH_TCSR_CHNL_NUM; i++) + IWL_ERR(trans, "FH TRBs(%d) = 0x%08x\n", i, + iwl_read_direct32(trans, FH_TX_TRB_REG(i))); + + for (i = 0; i < trans->cfg->base_params->num_of_queues; i++) { + u32 status = iwl_read_prph(trans, SCD_QUEUE_STATUS_BITS(i)); + u8 fifo = (status >> SCD_QUEUE_STTS_REG_POS_TXF) & 0x7; + bool active = !!(status & BIT(SCD_QUEUE_STTS_REG_POS_ACTIVE)); + u32 tbl_dw = + iwl_read_targ_mem(trans, + trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE(i)); + + if (i & 0x1) + tbl_dw = (tbl_dw & 0xFFFF0000) >> 16; + else + tbl_dw = tbl_dw & 0x0000FFFF; + + IWL_ERR(trans, + "Q %d is %sactive and mapped to fifo %d ra_tid 0x%04x [%d,%d]\n", + i, active ? "" : "in", fifo, tbl_dw, + iwl_read_prph(trans, + SCD_QUEUE_RDPTR(i)) & (txq->q.n_bd - 1), + iwl_read_prph(trans, SCD_QUEUE_WRPTR(i))); + } + + for (i = q->read_ptr; i != q->write_ptr; + i = iwl_queue_inc_wrap(i, q->n_bd)) { + struct iwl_tx_cmd *tx_cmd = + (struct iwl_tx_cmd *)txq->entries[i].cmd->payload; + IWL_ERR(trans, "scratch %d = 0x%08x\n", i, + get_unaligned_le32(&tx_cmd->scratch)); + } + + iwl_op_mode_nic_error(trans->op_mode); +} + +/* + * iwl_pcie_txq_update_byte_cnt_tbl - Set up entry in Tx byte-count array */ -void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - u16 byte_cnt) +static void iwl_pcie_txq_update_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_txq *txq, u16 byte_cnt) { struct iwlagn_scd_bc_tbl *scd_bc_tbl; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -88,10 +246,36 @@ void iwl_trans_txq_update_byte_cnt_tbl(struct iwl_trans *trans, tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = bc_ent; } -/** - * iwl_txq_update_write_ptr - Send new write index to hardware +static void iwl_pcie_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, + struct iwl_txq *txq) +{ + struct iwl_trans_pcie *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; + int txq_id = txq->q.id; + int read_ptr = txq->q.read_ptr; + u8 sta_id = 0; + __le16 bc_ent; + struct iwl_tx_cmd *tx_cmd = + (void *)txq->entries[txq->q.read_ptr].cmd->payload; + + WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); + + if (txq_id != trans_pcie->cmd_queue) + sta_id = tx_cmd->sta_id; + + bc_ent = cpu_to_le16(1 | (sta_id << 12)); + scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; + + if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) + scd_bc_tbl[txq_id]. + tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; +} + +/* + * iwl_pcie_txq_inc_wr_ptr - Send new write index to hardware */ -void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) +void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) { u32 reg = 0; int txq_id = txq->q.id; @@ -137,7 +321,7 @@ void iwl_txq_update_write_ptr(struct iwl_trans *trans, struct iwl_tx_queue *txq) txq->need_update = 0; } -static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) +static inline dma_addr_t iwl_pcie_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) { struct iwl_tfd_tb *tb = &tfd->tbs[idx]; @@ -149,15 +333,15 @@ static inline dma_addr_t iwl_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) return addr; } -static inline u16 iwl_tfd_tb_get_len(struct iwl_tfd *tfd, u8 idx) +static inline u16 iwl_pcie_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 iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, - dma_addr_t addr, u16 len) +static inline void iwl_pcie_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; @@ -171,19 +355,20 @@ static inline void iwl_tfd_set_tb(struct iwl_tfd *tfd, u8 idx, tfd->num_tbs = idx + 1; } -static inline u8 iwl_tfd_get_num_tbs(struct iwl_tfd *tfd) +static inline u8 iwl_pcie_tfd_get_num_tbs(struct iwl_tfd *tfd) { return tfd->num_tbs & 0x1f; } -static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, - struct iwl_tfd *tfd, enum dma_data_direction dma_dir) +static void iwl_pcie_tfd_unmap(struct iwl_trans *trans, + struct iwl_cmd_meta *meta, struct iwl_tfd *tfd, + enum dma_data_direction dma_dir) { int i; int num_tbs; /* Sanity check on number of chunks */ - num_tbs = iwl_tfd_get_num_tbs(tfd); + num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); if (num_tbs >= IWL_NUM_OF_TBS) { IWL_ERR(trans, "Too many chunks: %i\n", num_tbs); @@ -200,14 +385,14 @@ static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, /* Unmap chunks, if any. */ for (i = 1; i < num_tbs; i++) - dma_unmap_single(trans->dev, iwl_tfd_tb_get_addr(tfd, i), - iwl_tfd_tb_get_len(tfd, i), dma_dir); + dma_unmap_single(trans->dev, iwl_pcie_tfd_tb_get_addr(tfd, i), + iwl_pcie_tfd_tb_get_len(tfd, i), dma_dir); tfd->num_tbs = 0; } -/** - * iwl_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] +/* + * iwl_pcie_txq_free_tfd - Free all chunks referenced by TFD [txq->q.read_ptr] * @trans - transport private data * @txq - tx queue * @dma_dir - the direction of the DMA mapping @@ -215,8 +400,8 @@ static void iwl_unmap_tfd(struct iwl_trans *trans, struct iwl_cmd_meta *meta, * Does NOT advance any TFD circular buffer read/write indexes * Does NOT free the TFD itself (which is within circular buffer) */ -void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, - enum dma_data_direction dma_dir) +static void iwl_pcie_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq, + enum dma_data_direction dma_dir) { struct iwl_tfd *tfd_tmp = txq->tfds; @@ -227,8 +412,8 @@ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, lockdep_assert_held(&txq->lock); /* We have only q->n_window txq->entries, but we use q->n_bd tfds */ - iwl_unmap_tfd(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], - dma_dir); + iwl_pcie_tfd_unmap(trans, &txq->entries[idx].meta, &tfd_tmp[rd_ptr], + dma_dir); /* free SKB */ if (txq->entries) { @@ -247,10 +432,8 @@ void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_tx_queue *txq, } } -int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, - struct iwl_tx_queue *txq, - dma_addr_t addr, u16 len, - u8 reset) +static int iwl_pcie_txq_build_tfd(struct iwl_trans *trans, struct iwl_txq *txq, + dma_addr_t addr, u16 len, u8 reset) { struct iwl_queue *q; struct iwl_tfd *tfd, *tfd_tmp; @@ -263,7 +446,7 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, if (reset) memset(tfd, 0, sizeof(*tfd)); - num_tbs = iwl_tfd_get_num_tbs(tfd); + num_tbs = iwl_pcie_tfd_get_num_tbs(tfd); /* Each TFD can point to a maximum 20 Tx buffers */ if (num_tbs >= IWL_NUM_OF_TBS) { @@ -279,108 +462,534 @@ int iwlagn_txq_attach_buf_to_tfd(struct iwl_trans *trans, IWL_ERR(trans, "Unaligned address = %llx\n", (unsigned long long)addr); - iwl_tfd_set_tb(tfd, num_tbs, addr, len); + iwl_pcie_tfd_set_tb(tfd, num_tbs, addr, len); return 0; } -/*************** 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. +static int iwl_pcie_txq_alloc(struct iwl_trans *trans, + struct iwl_txq *txq, int slots_num, + u32 txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + size_t tfd_sz = sizeof(struct iwl_tfd) * TFD_QUEUE_SIZE_MAX; + int i; + + if (WARN_ON(txq->entries || txq->tfds)) + return -EINVAL; + + setup_timer(&txq->stuck_timer, iwl_pcie_txq_stuck_timer, + (unsigned long)txq); + txq->trans_pcie = trans_pcie; + + txq->q.n_window = slots_num; + + txq->entries = kcalloc(slots_num, + sizeof(struct iwl_pcie_txq_entry), + GFP_KERNEL); + + if (!txq->entries) + goto error; + + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) { + txq->entries[i].cmd = + kmalloc(sizeof(struct iwl_device_cmd), + GFP_KERNEL); + if (!txq->entries[i].cmd) + goto error; + } + + /* Circular buffer of transmit frame descriptors (TFDs), + * shared with device */ + txq->tfds = dma_alloc_coherent(trans->dev, tfd_sz, + &txq->q.dma_addr, GFP_KERNEL); + if (!txq->tfds) { + IWL_ERR(trans, "dma_alloc_coherent(%zd) failed\n", tfd_sz); + goto error; + } + txq->q.id = txq_id; + + return 0; +error: + if (txq->entries && txq_id == trans_pcie->cmd_queue) + for (i = 0; i < slots_num; i++) + kfree(txq->entries[i].cmd); + kfree(txq->entries); + txq->entries = NULL; + + return -ENOMEM; + +} + +static int iwl_pcie_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, + int slots_num, u32 txq_id) +{ + int ret; + + txq->need_update = 0; + + /* TFD_QUEUE_SIZE_MAX must be power-of-two size, otherwise + * iwl_queue_inc_wrap and iwl_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 */ + ret = iwl_queue_init(&txq->q, TFD_QUEUE_SIZE_MAX, slots_num, + txq_id); + if (ret) + return ret; + + spin_lock_init(&txq->lock); + + /* + * Tell nic where to find circular buffer of Tx Frame Descriptors for + * given Tx queue, and enable the DMA channel used for that queue. + * Circular buffer (TFD queue in DRAM) physical base address */ + iwl_write_direct32(trans, FH_MEM_CBBC_QUEUE(txq_id), + txq->q.dma_addr >> 8); + + return 0; +} + +/* + * iwl_pcie_txq_unmap - Unmap any remaining DMA mappings and free skb's + */ +static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + enum dma_data_direction dma_dir; + + if (!q->n_bd) + return; + + /* In the command queue, all the TBs are mapped as BIDI + * so unmap them as such. + */ + if (txq_id == trans_pcie->cmd_queue) + dma_dir = DMA_BIDIRECTIONAL; + else + dma_dir = DMA_TO_DEVICE; + + spin_lock_bh(&txq->lock); + while (q->write_ptr != q->read_ptr) { + iwl_pcie_txq_free_tfd(trans, txq, dma_dir); + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd); + } + spin_unlock_bh(&txq->lock); +} + +/* + * iwl_pcie_txq_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. + */ +static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct device *dev = trans->dev; + int i; -int iwl_queue_space(const struct iwl_queue *q) + if (WARN_ON(!txq)) + return; + + iwl_pcie_txq_unmap(trans, txq_id); + + /* De-alloc array of command/tx buffers */ + if (txq_id == trans_pcie->cmd_queue) + for (i = 0; i < txq->q.n_window; i++) { + kfree(txq->entries[i].cmd); + kfree(txq->entries[i].copy_cmd); + kfree(txq->entries[i].free_buf); + } + + /* De-alloc circular buffer of TFDs */ + if (txq->q.n_bd) { + dma_free_coherent(dev, sizeof(struct iwl_tfd) * + txq->q.n_bd, txq->tfds, txq->q.dma_addr); + memset(&txq->q.dma_addr, 0, sizeof(txq->q.dma_addr)); + } + + kfree(txq->entries); + txq->entries = NULL; + + del_timer_sync(&txq->stuck_timer); + + /* 0-fill queue descriptor structure */ + memset(txq, 0, sizeof(*txq)); +} + +/* + * Activate/Deactivate Tx DMA/FIFO channels according tx fifos mask + */ +static void iwl_pcie_txq_set_sched(struct iwl_trans *trans, u32 mask) { - int s = q->read_ptr - q->write_ptr; + struct iwl_trans_pcie __maybe_unused *trans_pcie = + IWL_TRANS_GET_PCIE_TRANS(trans); - if (q->read_ptr > q->write_ptr) - s -= q->n_bd; + iwl_write_prph(trans, SCD_TXFACT, mask); +} - 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; +void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 a; + int chan; + u32 reg_val; + + /* make sure all queue are not stopped/used */ + memset(trans_pcie->queue_stopped, 0, sizeof(trans_pcie->queue_stopped)); + memset(trans_pcie->queue_used, 0, sizeof(trans_pcie->queue_used)); + + trans_pcie->scd_base_addr = + iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); + + WARN_ON(scd_base_addr != 0 && + scd_base_addr != trans_pcie->scd_base_addr); + + a = trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_LOWER_BOUND; + /* reset conext data memory */ + for (; a < trans_pcie->scd_base_addr + SCD_CONTEXT_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(trans, a, 0); + /* reset tx status memory */ + for (; a < trans_pcie->scd_base_addr + SCD_TX_STTS_MEM_UPPER_BOUND; + a += 4) + iwl_write_targ_mem(trans, a, 0); + for (; a < trans_pcie->scd_base_addr + + SCD_TRANS_TBL_OFFSET_QUEUE( + trans->cfg->base_params->num_of_queues); + a += 4) + iwl_write_targ_mem(trans, a, 0); + + iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, + trans_pcie->scd_bc_tbls.dma >> 10); + + /* The chain extension of the SCD doesn't work well. This feature is + * enabled by default by the HW, so we need to disable it manually. + */ + iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); + + iwl_trans_ac_txq_enable(trans, trans_pcie->cmd_queue, + trans_pcie->cmd_fifo); + + /* Activate all Tx DMA/FIFO channels */ + iwl_pcie_txq_set_sched(trans, IWL_MASK(0, 7)); + + /* Enable DMA channel */ + for (chan = 0; chan < FH_TCSR_CHNL_NUM; chan++) + iwl_write_direct32(trans, 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(trans, FH_TX_CHICKEN_BITS_REG); + iwl_write_direct32(trans, FH_TX_CHICKEN_BITS_REG, + reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); + + /* Enable L1-Active */ + iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, + APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } -/** - * iwl_queue_init - Initialize queue's high/low-water and read/write indexes +/* + * iwl_pcie_tx_stop - Stop all Tx DMA channels */ -int iwl_queue_init(struct iwl_queue *q, int count, int slots_num, u32 id) +int iwl_pcie_tx_stop(struct iwl_trans *trans) { - q->n_bd = count; - q->n_window = slots_num; - q->id = id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ch, txq_id, ret; + unsigned long flags; - /* count must be power-of-two size, otherwise iwl_queue_inc_wrap - * and iwl_queue_dec_wrap are broken. */ - if (WARN_ON(!is_power_of_2(count))) - return -EINVAL; + /* Turn off all Tx DMA fifos */ + spin_lock_irqsave(&trans_pcie->irq_lock, flags); - /* slots_num must be power-of-two size, otherwise - * get_cmd_index is broken. */ - if (WARN_ON(!is_power_of_2(slots_num))) - return -EINVAL; + iwl_pcie_txq_set_sched(trans, 0); - q->low_mark = q->n_window / 4; - if (q->low_mark < 4) - q->low_mark = 4; + /* Stop each Tx DMA channel, and wait for it to be idle */ + for (ch = 0; ch < FH_TCSR_CHNL_NUM; ch++) { + iwl_write_direct32(trans, + FH_TCSR_CHNL_TX_CONFIG_REG(ch), 0x0); + ret = iwl_poll_direct_bit(trans, FH_TSSR_TX_STATUS_REG, + FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(ch), 1000); + if (ret < 0) + IWL_ERR(trans, + "Failing on timeout while stopping DMA channel %d [0x%08x]\n", + ch, + iwl_read_direct32(trans, + FH_TSSR_TX_STATUS_REG)); + } + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); - q->high_mark = q->n_window / 8; - if (q->high_mark < 2) - q->high_mark = 2; + if (!trans_pcie->txq) { + IWL_WARN(trans, + "Stopping tx queues that aren't allocated...\n"); + return 0; + } - q->write_ptr = q->read_ptr = 0; + /* Unmap DMA from host system and free skb's */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) + iwl_pcie_txq_unmap(trans, txq_id); return 0; } -static void iwlagn_txq_inval_byte_cnt_tbl(struct iwl_trans *trans, - struct iwl_tx_queue *txq) +/* + * iwl_trans_tx_free - Free TXQ Context + * + * Destroy all TX DMA queues and structures + */ +void iwl_pcie_tx_free(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = - IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->scd_bc_tbls.addr; - int txq_id = txq->q.id; - int read_ptr = txq->q.read_ptr; - u8 sta_id = 0; - __le16 bc_ent; - struct iwl_tx_cmd *tx_cmd = - (void *)txq->entries[txq->q.read_ptr].cmd->payload; + int txq_id; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); + /* Tx queues */ + if (trans_pcie->txq) { + for (txq_id = 0; + txq_id < trans->cfg->base_params->num_of_queues; txq_id++) + iwl_pcie_txq_free(trans, txq_id); + } - if (txq_id != trans_pcie->cmd_queue) - sta_id = tx_cmd->sta_id; + kfree(trans_pcie->txq); + trans_pcie->txq = NULL; - bc_ent = cpu_to_le16(1 | (sta_id << 12)); - scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; + iwl_pcie_free_dma_ptr(trans, &trans_pcie->kw); - if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id]. - tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = bc_ent; + iwl_pcie_free_dma_ptr(trans, &trans_pcie->scd_bc_tbls); +} + +/* + * iwl_pcie_tx_alloc - allocate TX context + * Allocate all Tx DMA structures and initialize them + */ +static int iwl_pcie_tx_alloc(struct iwl_trans *trans) +{ + int ret; + int txq_id, slots_num; + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + u16 scd_bc_tbls_size = trans->cfg->base_params->num_of_queues * + sizeof(struct iwlagn_scd_bc_tbl); + + /*It is not allowed to alloc twice, so warn when this happens. + * We cannot rely on the previous allocation, so free and fail */ + if (WARN_ON(trans_pcie->txq)) { + ret = -EINVAL; + goto error; + } + + ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->scd_bc_tbls, + scd_bc_tbls_size); + if (ret) { + IWL_ERR(trans, "Scheduler BC Table allocation failed\n"); + goto error; + } + + /* Alloc keep-warm buffer */ + ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->kw, IWL_KW_SIZE); + if (ret) { + IWL_ERR(trans, "Keep Warm allocation failed\n"); + goto error; + } + + trans_pcie->txq = kcalloc(trans->cfg->base_params->num_of_queues, + sizeof(struct iwl_txq), GFP_KERNEL); + if (!trans_pcie->txq) { + IWL_ERR(trans, "Not enough memory for txq\n"); + ret = ENOMEM; + goto error; + } + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_pcie_txq_alloc(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); + goto error; + } + } + + return 0; + +error: + iwl_pcie_tx_free(trans); + + return ret; +} +int iwl_pcie_tx_init(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + int ret; + int txq_id, slots_num; + unsigned long flags; + bool alloc = false; + + if (!trans_pcie->txq) { + ret = iwl_pcie_tx_alloc(trans); + if (ret) + goto error; + alloc = true; + } + + spin_lock_irqsave(&trans_pcie->irq_lock, flags); + + /* Turn off all Tx DMA fifos */ + iwl_write_prph(trans, SCD_TXFACT, 0); + + /* Tell NIC where to find the "keep warm" buffer */ + iwl_write_direct32(trans, FH_KW_MEM_ADDR_REG, + trans_pcie->kw.dma >> 4); + + spin_unlock_irqrestore(&trans_pcie->irq_lock, flags); + + /* Alloc and init all Tx queues, including the command queue (#4/#9) */ + for (txq_id = 0; txq_id < trans->cfg->base_params->num_of_queues; + txq_id++) { + slots_num = (txq_id == trans_pcie->cmd_queue) ? + TFD_CMD_SLOTS : TFD_TX_CMD_SLOTS; + ret = iwl_pcie_txq_init(trans, &trans_pcie->txq[txq_id], + slots_num, txq_id); + if (ret) { + IWL_ERR(trans, "Tx %d queue init failed\n", txq_id); + goto error; + } + } + + return 0; +error: + /*Upon error, free only if we allocated something */ + if (alloc) + iwl_pcie_tx_free(trans); + return ret; +} + +static inline void iwl_pcie_txq_progress(struct iwl_trans_pcie *trans_pcie, + struct iwl_txq *txq) +{ + if (!trans_pcie->wd_timeout) + return; + + /* + * if empty delete timer, otherwise move timer forward + * since we're making progress on this queue + */ + if (txq->q.read_ptr == txq->q.write_ptr) + del_timer(&txq->stuck_timer); + else + mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); +} + +/* Frees buffers until index _not_ inclusive */ +void iwl_trans_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, + struct sk_buff_head *skbs) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + /* n_bd is usually 256 => n_bd - 1 = 0xff */ + int tfd_num = ssn & (txq->q.n_bd - 1); + struct iwl_queue *q = &txq->q; + int last_to_free; + + /* This function is not meant to release cmd queue*/ + if (WARN_ON(txq_id == trans_pcie->cmd_queue)) + return; + + spin_lock(&txq->lock); + + if (txq->q.read_ptr == tfd_num) + goto out; + + IWL_DEBUG_TX_REPLY(trans, "[Q %d] %d -> %d (%d)\n", + txq_id, txq->q.read_ptr, tfd_num, ssn); + + /*Since we free until index _not_ inclusive, the one before index is + * the last we will free. This one must be used */ + last_to_free = iwl_queue_dec_wrap(tfd_num, q->n_bd); + + if (!iwl_queue_used(q, last_to_free)) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, last_to_free, q->n_bd, + q->write_ptr, q->read_ptr); + goto out; + } + + if (WARN_ON(!skb_queue_empty(skbs))) + goto out; + + for (; + q->read_ptr != tfd_num; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) + continue; + + __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); + + txq->entries[txq->q.read_ptr].skb = NULL; + + iwl_pcie_txq_inval_byte_cnt_tbl(trans, txq); + + iwl_pcie_txq_free_tfd(trans, txq, DMA_TO_DEVICE); + } + + iwl_pcie_txq_progress(trans_pcie, txq); + + if (iwl_queue_space(&txq->q) > txq->q.low_mark) + iwl_wake_queue(trans, txq); +out: + spin_unlock(&txq->lock); +} + +/* + * iwl_pcie_cmdq_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_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + struct iwl_txq *txq = &trans_pcie->txq[txq_id]; + struct iwl_queue *q = &txq->q; + int nfreed = 0; + + lockdep_assert_held(&txq->lock); + + if ((idx >= q->n_bd) || (!iwl_queue_used(q, idx))) { + IWL_ERR(trans, + "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", + __func__, txq_id, idx, q->n_bd, + q->write_ptr, q->read_ptr); + return; + } + + for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; + q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + + if (nfreed++ > 0) { + IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", + idx, q->write_ptr, q->read_ptr); + iwl_op_mode_nic_error(trans->op_mode); + } + } + + iwl_pcie_txq_progress(trans_pcie, txq); } -static int iwl_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, +static int iwl_pcie_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, u16 txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -405,7 +1014,8 @@ static int iwl_txq_set_ratid_map(struct iwl_trans *trans, u16 ra_tid, return 0; } -static inline void iwl_txq_set_inactive(struct iwl_trans *trans, u16 txq_id) +static inline void iwl_pcie_txq_set_inactive(struct iwl_trans *trans, + 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. */ @@ -424,7 +1034,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, WARN_ONCE(1, "queue %d already used - expect issues", txq_id); /* Stop this Tx queue before configuring it */ - iwl_txq_set_inactive(trans, txq_id); + iwl_pcie_txq_set_inactive(trans, txq_id); /* Set this queue as a chain-building queue unless it is CMD queue */ if (txq_id != trans_pcie->cmd_queue) @@ -435,7 +1045,7 @@ void iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, int fifo, u16 ra_tid = BUILD_RAxTID(sta_id, tid); /* Map receiver-address / traffic-ID to this queue */ - iwl_txq_set_ratid_map(trans, ra_tid, txq_id); + iwl_pcie_txq_set_ratid_map(trans, ra_tid, txq_id); /* enable aggregations for the queue */ iwl_set_bits_prph(trans, SCD_AGGR_SEL, BIT(txq_id)); @@ -489,18 +1099,20 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) return; } - iwl_txq_set_inactive(trans, txq_id); + iwl_pcie_txq_set_inactive(trans, txq_id); _iwl_write_targ_mem_dwords(trans, stts_addr, zero_val, ARRAY_SIZE(zero_val)); + iwl_pcie_txq_unmap(trans, txq_id); + IWL_DEBUG_TX_QUEUES(trans, "Deactivate queue %d\n", txq_id); } /*************** HOST COMMAND QUEUE FUNCTIONS *****/ -/** - * iwl_enqueue_hcmd - enqueue a uCode command +/* + * iwl_pcie_enqueue_hcmd - enqueue a uCode command * @priv: device private data point * @cmd: a point to the ucode command structure * @@ -508,15 +1120,17 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id) * failed. On success, it turns the index (> 0) of command in the * command queue. */ -static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +static int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; + void *dup_buf = NULL; dma_addr_t phys_addr; - u32 idx; + int idx; u16 copy_size, cmd_size; bool had_nocopy = false; int i; @@ -533,10 +1147,33 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) continue; if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) { had_nocopy = true; + if (WARN_ON(cmd->dataflags[i] & IWL_HCMD_DFL_DUP)) { + idx = -EINVAL; + goto free_dup_buf; + } + } else if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) { + /* + * This is also a chunk that isn't copied + * to the static buffer so set had_nocopy. + */ + had_nocopy = true; + + /* only allowed once */ + if (WARN_ON(dup_buf)) { + idx = -EINVAL; + goto free_dup_buf; + } + + dup_buf = kmemdup(cmd->data[i], cmd->len[i], + GFP_ATOMIC); + if (!dup_buf) + return -ENOMEM; } else { /* NOCOPY must not be followed by normal! */ - if (WARN_ON(had_nocopy)) - return -EINVAL; + if (WARN_ON(had_nocopy)) { + idx = -EINVAL; + goto free_dup_buf; + } copy_size += cmd->len[i]; } cmd_size += cmd->len[i]; @@ -550,9 +1187,10 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) */ if (WARN(copy_size > TFD_MAX_PAYLOAD_SIZE, "Command %s (%#x) is too large (%d bytes)\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), - cmd->id, copy_size)) - return -EINVAL; + get_cmd_string(trans_pcie, cmd->id), cmd->id, copy_size)) { + idx = -EINVAL; + goto free_dup_buf; + } spin_lock_bh(&txq->lock); @@ -561,7 +1199,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) IWL_ERR(trans, "No space in command queue\n"); iwl_op_mode_cmd_queue_full(trans->op_mode); - return -ENOSPC; + idx = -ENOSPC; + goto free_dup_buf; } idx = get_cmd_index(q, q->write_ptr); @@ -585,7 +1224,8 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { if (!cmd->len[i]) continue; - if (cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY) + if (cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP)) break; memcpy((u8 *)out_cmd + cmd_pos, cmd->data[i], cmd->len[i]); cmd_pos += cmd->len[i]; @@ -610,7 +1250,7 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) IWL_DEBUG_HC(trans, "Sending command %s (#%x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", - trans_pcie_get_cmd_string(trans_pcie, out_cmd->hdr.cmd), + get_cmd_string(trans_pcie, out_cmd->hdr.cmd), out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), cmd_size, q->write_ptr, idx, trans_pcie->cmd_queue); @@ -624,28 +1264,35 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) dma_unmap_addr_set(out_meta, mapping, phys_addr); dma_unmap_len_set(out_meta, len, copy_size); - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, copy_size, 1); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, copy_size, 1); for (i = 0; i < IWL_MAX_CMD_TFDS; i++) { + const void *data = cmd->data[i]; + if (!cmd->len[i]) continue; - if (!(cmd->dataflags[i] & IWL_HCMD_DFL_NOCOPY)) + if (!(cmd->dataflags[i] & (IWL_HCMD_DFL_NOCOPY | + IWL_HCMD_DFL_DUP))) continue; - phys_addr = dma_map_single(trans->dev, (void *)cmd->data[i], + if (cmd->dataflags[i] & IWL_HCMD_DFL_DUP) + data = dup_buf; + phys_addr = dma_map_single(trans->dev, (void *)data, cmd->len[i], DMA_BIDIRECTIONAL); if (dma_mapping_error(trans->dev, phys_addr)) { - iwl_unmap_tfd(trans, out_meta, - &txq->tfds[q->write_ptr], - DMA_BIDIRECTIONAL); + iwl_pcie_tfd_unmap(trans, out_meta, + &txq->tfds[q->write_ptr], + DMA_BIDIRECTIONAL); idx = -ENOMEM; goto out; } - iwlagn_txq_attach_buf_to_tfd(trans, txq, phys_addr, - cmd->len[i], 0); + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, cmd->len[i], 0); } out_meta->flags = cmd->flags; + if (WARN_ON_ONCE(txq->entries[idx].free_buf)) + kfree(txq->entries[idx].free_buf); + txq->entries[idx].free_buf = dup_buf; txq->need_update = 1; @@ -658,70 +1305,18 @@ static int iwl_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) /* Increment and update queue's write index */ q->write_ptr = iwl_queue_inc_wrap(q->write_ptr, q->n_bd); - iwl_txq_update_write_ptr(trans, txq); + iwl_pcie_txq_inc_wr_ptr(trans, txq); out: spin_unlock_bh(&txq->lock); + free_dup_buf: + if (idx < 0) + kfree(dup_buf); return idx; } -static inline void iwl_queue_progress(struct iwl_trans_pcie *trans_pcie, - struct iwl_tx_queue *txq) -{ - if (!trans_pcie->wd_timeout) - return; - - /* - * if empty delete timer, otherwise move timer forward - * since we're making progress on this queue - */ - if (txq->q.read_ptr == txq->q.write_ptr) - del_timer(&txq->stuck_timer); - else - mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); -} - -/** - * iwl_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_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, - int idx) -{ - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - int nfreed = 0; - - lockdep_assert_held(&txq->lock); - - if ((idx >= q->n_bd) || (iwl_queue_used(q, idx) == 0)) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, idx, q->n_bd, - q->write_ptr, q->read_ptr); - return; - } - - for (idx = iwl_queue_inc_wrap(idx, q->n_bd); q->read_ptr != idx; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { - - if (nfreed++ > 0) { - IWL_ERR(trans, "HCMD skipped: index (%d) %d %d\n", - idx, q->write_ptr, q->read_ptr); - iwl_op_mode_nic_error(trans->op_mode); - } - - } - - iwl_queue_progress(trans_pcie, txq); -} - -/** - * iwl_tx_cmd_complete - Pull unused buffers off the queue and reclaim them +/* + * iwl_pcie_hcmd_complete - Pull unused buffers off the queue and reclaim them * @rxb: Rx buffer to reclaim * @handler_status: return value of the handler of the command * (put in setup_rx_handlers) @@ -730,8 +1325,8 @@ static void iwl_hcmd_queue_reclaim(struct iwl_trans *trans, int txq_id, * will be executed. The attached skb (if present) will only be freed * if the callback returns 1 */ -void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, - int handler_status) +void iwl_pcie_hcmd_complete(struct iwl_trans *trans, + struct iwl_rx_cmd_buffer *rxb, int handler_status) { struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 sequence = le16_to_cpu(pkt->hdr.sequence); @@ -741,7 +1336,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->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 @@ -761,7 +1356,7 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, cmd = txq->entries[cmd_index].cmd; meta = &txq->entries[cmd_index].meta; - iwl_unmap_tfd(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); + iwl_pcie_tfd_unmap(trans, meta, &txq->tfds[index], DMA_BIDIRECTIONAL); /* Input error checking is done when commands are added to queue. */ if (meta->flags & CMD_WANT_SKB) { @@ -773,20 +1368,18 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, meta->source->handler_status = handler_status; } - iwl_hcmd_queue_reclaim(trans, txq_id, index); + iwl_pcie_cmdq_reclaim(trans, txq_id, index); if (!(meta->flags & CMD_ASYNC)) { if (!test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { IWL_WARN(trans, "HCMD_ACTIVE already clear for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->hdr.cmd)); + get_cmd_string(trans_pcie, cmd->hdr.cmd)); } clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->hdr.cmd)); - wake_up(&trans->wait_command_queue); + get_cmd_string(trans_pcie, cmd->hdr.cmd)); + wake_up(&trans_pcie->wait_command_queue); } meta->flags = 0; @@ -796,7 +1389,8 @@ void iwl_tx_cmd_complete(struct iwl_trans *trans, struct iwl_rx_cmd_buffer *rxb, #define HOST_COMPLETE_TIMEOUT (2 * HZ) -static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +static int iwl_pcie_send_hcmd_async(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; @@ -805,59 +1399,59 @@ static int iwl_send_cmd_async(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (WARN_ON(cmd->flags & CMD_WANT_SKB)) return -EINVAL; - - ret = iwl_enqueue_hcmd(trans, cmd); + ret = iwl_pcie_enqueue_hcmd(trans, cmd); if (ret < 0) { IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); + get_cmd_string(trans_pcie, cmd->id), ret); return ret; } return 0; } -static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, + struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int cmd_idx; int ret; IWL_DEBUG_INFO(trans, "Attempting to send sync command %s\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + get_cmd_string(trans_pcie, cmd->id)); if (WARN_ON(test_and_set_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status))) { IWL_ERR(trans, "Command %s: a command is already active!\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + get_cmd_string(trans_pcie, cmd->id)); return -EIO; } IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + get_cmd_string(trans_pcie, cmd->id)); - cmd_idx = iwl_enqueue_hcmd(trans, cmd); + cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); if (cmd_idx < 0) { ret = cmd_idx; clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_ERR(trans, "Error sending %s: enqueue_hcmd failed: %d\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), ret); + get_cmd_string(trans_pcie, cmd->id), ret); return ret; } - ret = wait_event_timeout(trans->wait_command_queue, + ret = wait_event_timeout(trans_pcie->wait_command_queue, !test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status), HOST_COMPLETE_TIMEOUT); if (!ret) { if (test_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status)) { - struct iwl_tx_queue *txq = + struct iwl_txq *txq = &trans_pcie->txq[trans_pcie->cmd_queue]; struct iwl_queue *q = &txq->q; IWL_ERR(trans, "Error sending %s: time out after %dms.\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id), + get_cmd_string(trans_pcie, cmd->id), jiffies_to_msecs(HOST_COMPLETE_TIMEOUT)); IWL_ERR(trans, @@ -867,16 +1461,28 @@ static int iwl_send_cmd_sync(struct iwl_trans *trans, struct iwl_host_cmd *cmd) clear_bit(STATUS_HCMD_ACTIVE, &trans_pcie->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", - trans_pcie_get_cmd_string(trans_pcie, - cmd->id)); + get_cmd_string(trans_pcie, cmd->id)); ret = -ETIMEDOUT; goto cancel; } } + if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) { + IWL_ERR(trans, "FW error in SYNC CMD %s\n", + get_cmd_string(trans_pcie, cmd->id)); + ret = -EIO; + goto cancel; + } + + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) { + IWL_DEBUG_RF_KILL(trans, "RFKILL in SYNC CMD... no rsp\n"); + ret = -ERFKILL; + goto cancel; + } + if ((cmd->flags & CMD_WANT_SKB) && !cmd->resp_pkt) { IWL_ERR(trans, "Error: Response NULL in '%s'\n", - trans_pcie_get_cmd_string(trans_pcie, cmd->id)); + get_cmd_string(trans_pcie, cmd->id)); ret = -EIO; goto cancel; } @@ -903,64 +1509,183 @@ cancel: return ret; } -int iwl_trans_pcie_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) +int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + if (test_bit(STATUS_FW_ERROR, &trans_pcie->status)) + return -EIO; + + if (test_bit(STATUS_RFKILL, &trans_pcie->status)) + return -ERFKILL; + if (cmd->flags & CMD_ASYNC) - return iwl_send_cmd_async(trans, cmd); + return iwl_pcie_send_hcmd_async(trans, cmd); - return iwl_send_cmd_sync(trans, cmd); + /* We still can fail on RFKILL that can be asserted while we wait */ + return iwl_pcie_send_hcmd_sync(trans, cmd); } -/* Frees buffers until index _not_ inclusive */ -int iwl_tx_queue_reclaim(struct iwl_trans *trans, int txq_id, int index, - struct sk_buff_head *skbs) +int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, + struct iwl_device_cmd *dev_cmd, int txq_id) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_queue *txq = &trans_pcie->txq[txq_id]; - struct iwl_queue *q = &txq->q; - int last_to_free; - int freed = 0; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + struct iwl_cmd_meta *out_meta; + struct iwl_txq *txq; + struct iwl_queue *q; + dma_addr_t phys_addr = 0; + dma_addr_t txcmd_phys; + dma_addr_t scratch_phys; + u16 len, firstlen, secondlen; + u8 wait_write_ptr = 0; + __le16 fc = hdr->frame_control; + u8 hdr_len = ieee80211_hdrlen(fc); + u16 __maybe_unused wifi_seq; + + txq = &trans_pcie->txq[txq_id]; + q = &txq->q; - /* This function is not meant to release cmd queue*/ - if (WARN_ON(txq_id == trans_pcie->cmd_queue)) - return 0; + if (unlikely(!test_bit(txq_id, trans_pcie->queue_used))) { + WARN_ON_ONCE(1); + return -EINVAL; + } - lockdep_assert_held(&txq->lock); + spin_lock(&txq->lock); - /*Since we free until index _not_ inclusive, the one before index is - * the last we will free. This one must be used */ - last_to_free = iwl_queue_dec_wrap(index, q->n_bd); + /* In AGG mode, the index in the ring must correspond to the WiFi + * sequence number. This is a HW requirements to help the SCD to parse + * the BA. + * Check here that the packets are in the right place on the ring. + */ +#ifdef CONFIG_IWLWIFI_DEBUG + wifi_seq = SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); + WARN_ONCE((iwl_read_prph(trans, SCD_AGGR_SEL) & BIT(txq_id)) && + ((wifi_seq & 0xff) != q->write_ptr), + "Q: %d WiFi Seq %d tfdNum %d", + txq_id, wifi_seq, q->write_ptr); +#endif + + /* Set up driver data for this TFD */ + txq->entries[q->write_ptr].skb = skb; + txq->entries[q->write_ptr].cmd = dev_cmd; + + dev_cmd->hdr.cmd = REPLY_TX; + dev_cmd->hdr.sequence = + cpu_to_le16((u16)(QUEUE_TO_SEQ(txq_id) | + INDEX_TO_SEQ(q->write_ptr))); + + /* Set up first empty entry in queue's array of Tx/cmd buffers */ + out_meta = &txq->entries[q->write_ptr].meta; - if ((index >= q->n_bd) || - (iwl_queue_used(q, last_to_free) == 0)) { - IWL_ERR(trans, - "%s: Read index for DMA queue txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", - __func__, txq_id, last_to_free, q->n_bd, - q->write_ptr, q->read_ptr); - return 0; + /* + * 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 = dma_map_single(trans->dev, + &dev_cmd->hdr, firstlen, + DMA_BIDIRECTIONAL); + if (unlikely(dma_mapping_error(trans->dev, txcmd_phys))) + goto out_err; + dma_unmap_addr_set(out_meta, mapping, txcmd_phys); + dma_unmap_len_set(out_meta, len, firstlen); + + if (!ieee80211_has_morefrags(fc)) { + txq->need_update = 1; + } else { + wait_write_ptr = 1; + txq->need_update = 0; } - if (WARN_ON(!skb_queue_empty(skbs))) - return 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 = dma_map_single(trans->dev, skb->data + hdr_len, + secondlen, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(trans->dev, phys_addr))) { + dma_unmap_single(trans->dev, + dma_unmap_addr(out_meta, mapping), + dma_unmap_len(out_meta, len), + DMA_BIDIRECTIONAL); + goto out_err; + } + } - for (; - q->read_ptr != index; - q->read_ptr = iwl_queue_inc_wrap(q->read_ptr, q->n_bd)) { + /* Attach buffers to TFD */ + iwl_pcie_txq_build_tfd(trans, txq, txcmd_phys, firstlen, 1); + if (secondlen > 0) + iwl_pcie_txq_build_tfd(trans, txq, phys_addr, secondlen, 0); - if (WARN_ON_ONCE(txq->entries[txq->q.read_ptr].skb == NULL)) - continue; + scratch_phys = txcmd_phys + sizeof(struct iwl_cmd_header) + + offsetof(struct iwl_tx_cmd, scratch); - __skb_queue_tail(skbs, txq->entries[txq->q.read_ptr].skb); + /* take back ownership of DMA buffer to enable update */ + dma_sync_single_for_cpu(trans->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); + tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); - txq->entries[txq->q.read_ptr].skb = NULL; + IWL_DEBUG_TX(trans, "sequence nr = 0X%x\n", + le16_to_cpu(dev_cmd->hdr.sequence)); + IWL_DEBUG_TX(trans, "tx_flags = 0X%x\n", le32_to_cpu(tx_cmd->tx_flags)); - iwlagn_txq_inval_byte_cnt_tbl(trans, txq); + /* Set up entry for this TFD in Tx byte-count array */ + iwl_pcie_txq_update_byte_cnt_tbl(trans, txq, le16_to_cpu(tx_cmd->len)); - iwl_txq_free_tfd(trans, txq, DMA_TO_DEVICE); - freed++; - } + dma_sync_single_for_device(trans->dev, txcmd_phys, firstlen, + DMA_BIDIRECTIONAL); + + trace_iwlwifi_dev_tx(trans->dev, skb, + &txq->tfds[txq->q.write_ptr], + sizeof(struct iwl_tfd), + &dev_cmd->hdr, firstlen, + skb->data + hdr_len, secondlen); + trace_iwlwifi_dev_tx_data(trans->dev, skb, + skb->data + hdr_len, secondlen); - iwl_queue_progress(trans_pcie, txq); + /* start timer if queue currently empty */ + if (txq->need_update && q->read_ptr == q->write_ptr && + trans_pcie->wd_timeout) + mod_timer(&txq->stuck_timer, jiffies + trans_pcie->wd_timeout); + + /* 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_pcie_txq_inc_wr_ptr(trans, txq); - return freed; + /* + * 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. + */ + if (iwl_queue_space(q) < q->high_mark) { + if (wait_write_ptr) { + txq->need_update = 1; + iwl_pcie_txq_inc_wr_ptr(trans, txq); + } else { + iwl_stop_queue(trans, txq); + } + } + spin_unlock(&txq->lock); + return 0; +out_err: + spin_unlock(&txq->lock); + return -1; } diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c index 1c10b542ab2..ec36868f6fc 100644 --- a/drivers/net/wireless/libertas/cfg.c +++ b/drivers/net/wireless/libertas/cfg.c @@ -436,19 +436,19 @@ static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len) */ static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct lbs_private *priv = wiphy_priv(wiphy); int ret = -ENOTSUPP; lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d", - channel->center_freq, channel_type); + chandef->chan->center_freq, + cfg80211_get_chandef_type(chandef)); - if (channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) goto out; - ret = lbs_set_channel(priv, channel->hw_value); + ret = lbs_set_channel(priv, chandef->chan->hw_value); out: lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret); @@ -1734,7 +1734,7 @@ static void lbs_join_post(struct lbs_private *priv, /* Fake DS channel IE */ *fake++ = WLAN_EID_DS_PARAMS; *fake++ = 1; - *fake++ = params->channel->hw_value; + *fake++ = params->chandef.chan->hw_value; /* Fake IBSS params IE */ *fake++ = WLAN_EID_IBSS_PARAMS; *fake++ = 2; @@ -1755,7 +1755,7 @@ static void lbs_join_post(struct lbs_private *priv, lbs_deb_hex(LBS_DEB_CFG80211, "IE", fake_ie, fake - fake_ie); bss = cfg80211_inform_bss(priv->wdev->wiphy, - params->channel, + params->chandef.chan, bssid, 0, capability, @@ -1833,7 +1833,7 @@ static int lbs_ibss_join_existing(struct lbs_private *priv, cmd.bss.beaconperiod = cpu_to_le16(params->beacon_interval); cmd.bss.ds.header.id = WLAN_EID_DS_PARAMS; cmd.bss.ds.header.len = 1; - cmd.bss.ds.channel = params->channel->hw_value; + cmd.bss.ds.channel = params->chandef.chan->hw_value; cmd.bss.ibss.header.id = WLAN_EID_IBSS_PARAMS; cmd.bss.ibss.header.len = 2; cmd.bss.ibss.atimwindow = 0; @@ -1942,7 +1942,7 @@ static int lbs_ibss_start_new(struct lbs_private *priv, cmd.ibss.atimwindow = 0; cmd.ds.header.id = WLAN_EID_DS_PARAMS; cmd.ds.header.len = 1; - cmd.ds.channel = params->channel->hw_value; + cmd.ds.channel = params->chandef.chan->hw_value; /* Only v8 and below support setting probe delay */ if (MRVL_FW_MAJOR_REV(priv->fwrelease) <= 8) cmd.probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME); @@ -1987,18 +1987,18 @@ static int lbs_join_ibss(struct wiphy *wiphy, struct net_device *dev, lbs_deb_enter(LBS_DEB_CFG80211); - if (!params->channel) { + if (!params->chandef.chan) { ret = -ENOTSUPP; goto out; } - ret = lbs_set_channel(priv, params->channel->hw_value); + ret = lbs_set_channel(priv, params->chandef.chan->hw_value); if (ret) goto out; /* Search if someone is beaconing. This assumes that the * bss list is populated already */ - bss = cfg80211_get_bss(wiphy, params->channel, params->bssid, + bss = cfg80211_get_bss(wiphy, params->chandef.chan, params->bssid, params->ssid, params->ssid_len, WLAN_CAPABILITY_IBSS, WLAN_CAPABILITY_IBSS); diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c index 4cb234349fb..739309e70d8 100644 --- a/drivers/net/wireless/libertas/if_sdio.c +++ b/drivers/net/wireless/libertas/if_sdio.c @@ -588,17 +588,38 @@ static int if_sdio_prog_real(struct if_sdio_card *card, size = fw->size; while (size) { - ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); - if (ret) - goto release; + timeout = jiffies + HZ; + while (1) { + ret = if_sdio_wait_status(card, FW_DL_READY_STATUS); + if (ret) + goto release; - req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret); - if (ret) - goto release; + req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, + &ret); + if (ret) + goto release; + + req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, + &ret) << 8; + if (ret) + goto release; + + /* + * For SD8688 wait until the length is not 0, 1 or 2 + * before downloading the first FW block, + * since BOOT code writes the register to indicate the + * helper/FW download winner, + * the value could be 1 or 2 (Func1 or Func2). + */ + if ((size != fw->size) || (req_size > 2)) + break; + if (time_after(jiffies, timeout)) { + ret = -ETIMEDOUT; + goto release; + } + mdelay(1); + } - req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8; - if (ret) - goto release; /* lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size); */ diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c index 429ca3215fd..2aa8a1aa118 100644 --- a/drivers/net/wireless/mac80211_hwsim.c +++ b/drivers/net/wireless/mac80211_hwsim.c @@ -44,9 +44,9 @@ static int radios = 2; module_param(radios, int, 0444); MODULE_PARM_DESC(radios, "Number of simulated radios"); -static bool fake_hw_scan; -module_param(fake_hw_scan, bool, 0444); -MODULE_PARM_DESC(fake_hw_scan, "Install fake (no-op) hw-scan handler"); +static int channels = 1; +module_param(channels, int, 0444); +MODULE_PARM_DESC(channels, "Number of concurrent channels"); /** * enum hwsim_regtest - the type of regulatory tests we offer @@ -166,7 +166,9 @@ struct hwsim_vif_priv { static inline void hwsim_check_magic(struct ieee80211_vif *vif) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; - WARN_ON(vp->magic != HWSIM_VIF_MAGIC); + WARN(vp->magic != HWSIM_VIF_MAGIC, + "Invalid VIF (%p) magic %#x, %pM, %d/%d\n", + vif, vp->magic, vif->addr, vif->type, vif->p2p); } static inline void hwsim_set_magic(struct ieee80211_vif *vif) @@ -185,7 +187,7 @@ struct hwsim_sta_priv { u32 magic; }; -#define HWSIM_STA_MAGIC 0x6d537748 +#define HWSIM_STA_MAGIC 0x6d537749 static inline void hwsim_check_sta_magic(struct ieee80211_sta *sta) { @@ -205,6 +207,30 @@ static inline void hwsim_clear_sta_magic(struct ieee80211_sta *sta) sp->magic = 0; } +struct hwsim_chanctx_priv { + u32 magic; +}; + +#define HWSIM_CHANCTX_MAGIC 0x6d53774a + +static inline void hwsim_check_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + WARN_ON(cp->magic != HWSIM_CHANCTX_MAGIC); +} + +static inline void hwsim_set_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + cp->magic = HWSIM_CHANCTX_MAGIC; +} + +static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) +{ + struct hwsim_chanctx_priv *cp = (void *)c->drv_priv; + cp->magic = 0; +} + static struct class *hwsim_class; static struct net_device *hwsim_mon; /* global monitor netdev */ @@ -299,6 +325,13 @@ struct mac80211_hwsim_data { struct mac_address addresses[2]; + struct ieee80211_channel *tmp_chan; + struct delayed_work roc_done; + struct delayed_work hw_scan; + struct cfg80211_scan_request *hw_scan_request; + struct ieee80211_vif *hw_scan_vif; + int scan_chan_idx; + struct ieee80211_channel *channel; unsigned long beacon_int; /* in jiffies unit */ unsigned int rx_filter; @@ -396,7 +429,8 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, } static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, - struct sk_buff *tx_skb) + struct sk_buff *tx_skb, + struct ieee80211_channel *chan) { struct mac80211_hwsim_data *data = hw->priv; struct sk_buff *skb; @@ -423,7 +457,7 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, hdr->rt_tsft = __mac80211_hwsim_get_tsf(data); hdr->rt_flags = 0; hdr->rt_rate = txrate->bitrate / 5; - hdr->rt_channel = cpu_to_le16(data->channel->center_freq); + hdr->rt_channel = cpu_to_le16(chan->center_freq); flags = IEEE80211_CHAN_2GHZ; if (txrate->flags & IEEE80211_RATE_ERP_G) flags |= IEEE80211_CHAN_OFDM; @@ -441,9 +475,9 @@ static void mac80211_hwsim_monitor_rx(struct ieee80211_hw *hw, } -static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr) +static void mac80211_hwsim_monitor_ack(struct ieee80211_channel *chan, + const u8 *addr) { - struct mac80211_hwsim_data *data = hw->priv; struct sk_buff *skb; struct hwsim_radiotap_hdr *hdr; u16 flags; @@ -464,7 +498,7 @@ static void mac80211_hwsim_monitor_ack(struct ieee80211_hw *hw, const u8 *addr) (1 << IEEE80211_RADIOTAP_CHANNEL)); hdr->rt_flags = 0; hdr->rt_rate = 0; - hdr->rt_channel = cpu_to_le16(data->channel->center_freq); + hdr->rt_channel = cpu_to_le16(chan->center_freq); flags = IEEE80211_CHAN_2GHZ; hdr->rt_chbitmask = cpu_to_le16(flags); @@ -537,6 +571,7 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data, md.ret = false; md.addr = addr; ieee80211_iterate_active_interfaces_atomic(data->hw, + IEEE80211_IFACE_ITER_NORMAL, mac80211_hwsim_addr_iter, &md); @@ -556,12 +591,6 @@ static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw, int i; struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES]; - if (data->idle) { - wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); - dev_kfree_skb(my_skb); - return; - } - if (data->ps != PS_DISABLED) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); /* If the queue contains MAX_QUEUE skb's drop some */ @@ -629,8 +658,38 @@ nla_put_failure: printk(KERN_DEBUG "mac80211_hwsim: error occurred in %s\n", __func__); } +static bool hwsim_chans_compat(struct ieee80211_channel *c1, + struct ieee80211_channel *c2) +{ + if (!c1 || !c2) + return false; + + return c1->center_freq == c2->center_freq; +} + +struct tx_iter_data { + struct ieee80211_channel *channel; + bool receive; +}; + +static void mac80211_hwsim_tx_iter(void *_data, u8 *addr, + struct ieee80211_vif *vif) +{ + struct tx_iter_data *data = _data; + + if (!vif->chanctx_conf) + return; + + if (!hwsim_chans_compat(data->channel, + rcu_dereference(vif->chanctx_conf)->def.chan)) + return; + + data->receive = true; +} + static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, - struct sk_buff *skb) + struct sk_buff *skb, + struct ieee80211_channel *chan) { struct mac80211_hwsim_data *data = hw->priv, *data2; bool ack = false; @@ -639,15 +698,10 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, struct ieee80211_rx_status rx_status; struct ieee80211_rate *txrate = ieee80211_get_tx_rate(hw, info); - if (data->idle) { - wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); - return false; - } - memset(&rx_status, 0, sizeof(rx_status)); - rx_status.flag |= RX_FLAG_MACTIME_MPDU; - rx_status.freq = data->channel->center_freq; - rx_status.band = data->channel->band; + rx_status.flag |= RX_FLAG_MACTIME_START; + rx_status.freq = chan->center_freq; + rx_status.band = chan->band; rx_status.rate_idx = info->control.rates[0].idx; if (info->control.rates[0].flags & IEEE80211_TX_RC_MCS) rx_status.flag |= RX_FLAG_HT; @@ -673,17 +727,35 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, list_for_each_entry(data2, &hwsim_radios, list) { struct sk_buff *nskb; struct ieee80211_mgmt *mgmt; + struct tx_iter_data tx_iter_data = { + .receive = false, + .channel = chan, + }; if (data == data2) continue; - if (data2->idle || !data2->started || - !hwsim_ps_rx_ok(data2, skb) || !data2->channel || - data->channel->center_freq != data2->channel->center_freq || - !(data->group & data2->group)) + if (!data2->started || (data2->idle && !data2->tmp_chan) || + !hwsim_ps_rx_ok(data2, skb)) continue; - nskb = skb_copy(skb, GFP_ATOMIC); + if (!(data->group & data2->group)) + continue; + + if (!hwsim_chans_compat(chan, data2->tmp_chan) && + !hwsim_chans_compat(chan, data2->channel)) { + ieee80211_iterate_active_interfaces_atomic( + data2->hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_tx_iter, &tx_iter_data); + if (!tx_iter_data.receive) + continue; + } + + /* + * reserve some space for our vendor and the normal + * radiotap header, since we're copying anyway + */ + nskb = skb_copy_expand(skb, 64, 0, GFP_ATOMIC); if (nskb == NULL) continue; @@ -701,6 +773,33 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, (data->tsf_offset - data2->tsf_offset) + 24 * 8 * 10 / txrate->bitrate); +#if 0 + /* + * Don't enable this code by default as the OUI 00:00:00 + * is registered to Xerox so we shouldn't use it here, it + * might find its way into pcap files. + * Note that this code requires the headroom in the SKB + * that was allocated earlier. + */ + rx_status.vendor_radiotap_oui[0] = 0x00; + rx_status.vendor_radiotap_oui[1] = 0x00; + rx_status.vendor_radiotap_oui[2] = 0x00; + rx_status.vendor_radiotap_subns = 127; + /* + * Radiotap vendor namespaces can (and should) also be + * split into fields by using the standard radiotap + * presence bitmap mechanism. Use just BIT(0) here for + * the presence bitmap. + */ + rx_status.vendor_radiotap_bitmap = BIT(0); + /* We have 8 bytes of (dummy) data */ + rx_status.vendor_radiotap_len = 8; + /* For testing, also require it to be aligned */ + rx_status.vendor_radiotap_align = 8; + /* push the data */ + memcpy(skb_push(nskb, 8), "ABCDEFGH", 8); +#endif + memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status)); ieee80211_rx_irqsafe(data2->hw, nskb); } @@ -713,18 +812,51 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct ieee80211_tx_control *control, struct sk_buff *skb) { + struct mac80211_hwsim_data *data = hw->priv; + struct ieee80211_tx_info *txi = IEEE80211_SKB_CB(skb); + struct ieee80211_chanctx_conf *chanctx_conf; + struct ieee80211_channel *channel; bool ack; - struct ieee80211_tx_info *txi; u32 _portid; - mac80211_hwsim_monitor_rx(hw, skb); - - if (skb->len < 10) { + if (WARN_ON(skb->len < 10)) { /* Should not happen; just a sanity check for addr1 use */ dev_kfree_skb(skb); return; } + if (channels == 1) { + channel = data->channel; + } else if (txi->hw_queue == 4) { + channel = data->tmp_chan; + } else { + chanctx_conf = rcu_dereference(txi->control.vif->chanctx_conf); + if (chanctx_conf) + channel = chanctx_conf->def.chan; + else + channel = NULL; + } + + if (WARN(!channel, "TX w/o channel - queue = %d\n", txi->hw_queue)) { + dev_kfree_skb(skb); + return; + } + + if (data->idle && !data->tmp_chan) { + wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n"); + dev_kfree_skb(skb); + return; + } + + if (txi->control.vif) + hwsim_check_magic(txi->control.vif); + if (control->sta) + hwsim_check_sta_magic(control->sta); + + txi->rate_driver_data[0] = channel; + + mac80211_hwsim_monitor_rx(hw, skb, channel); + /* wmediumd mode check */ _portid = ACCESS_ONCE(wmediumd_portid); @@ -732,15 +864,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); /* NO wmediumd detected, perfect medium simulation */ - ack = mac80211_hwsim_tx_frame_no_nl(hw, skb); + ack = mac80211_hwsim_tx_frame_no_nl(hw, skb, channel); if (ack && skb->len >= 16) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; - mac80211_hwsim_monitor_ack(hw, hdr->addr2); + mac80211_hwsim_monitor_ack(channel, hdr->addr2); } - txi = IEEE80211_SKB_CB(skb); - ieee80211_tx_info_clear_status(txi); /* frame was transmitted at most favorable rate at first attempt */ @@ -778,6 +908,13 @@ static int mac80211_hwsim_add_interface(struct ieee80211_hw *hw, __func__, ieee80211_vif_type_p2p(vif), vif->addr); hwsim_set_magic(vif); + + vif->cab_queue = 0; + vif->hw_queue[IEEE80211_AC_VO] = 0; + vif->hw_queue[IEEE80211_AC_VI] = 1; + vif->hw_queue[IEEE80211_AC_BE] = 2; + vif->hw_queue[IEEE80211_AC_BK] = 3; + return 0; } @@ -807,14 +944,26 @@ static void mac80211_hwsim_remove_interface( hwsim_clear_magic(vif); } +static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw, + struct sk_buff *skb, + struct ieee80211_channel *chan) +{ + u32 _pid = ACCESS_ONCE(wmediumd_portid); + + mac80211_hwsim_monitor_rx(hw, skb, chan); + + if (_pid) + return mac80211_hwsim_tx_frame_nl(hw, skb, _pid); + + mac80211_hwsim_tx_frame_no_nl(hw, skb, chan); + dev_kfree_skb(skb); +} static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, struct ieee80211_vif *vif) { struct ieee80211_hw *hw = arg; struct sk_buff *skb; - struct ieee80211_tx_info *info; - u32 _portid; hwsim_check_magic(vif); @@ -826,18 +975,9 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, skb = ieee80211_beacon_get(hw, vif); if (skb == NULL) return; - info = IEEE80211_SKB_CB(skb); - - mac80211_hwsim_monitor_rx(hw, skb); - - /* wmediumd mode check */ - _portid = ACCESS_ONCE(wmediumd_portid); - - if (_portid) - return mac80211_hwsim_tx_frame_nl(hw, skb, _portid); - mac80211_hwsim_tx_frame_no_nl(hw, skb); - dev_kfree_skb(skb); + mac80211_hwsim_tx_frame(hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); } @@ -850,7 +990,8 @@ static void mac80211_hwsim_beacon(unsigned long arg) return; ieee80211_iterate_active_interfaces_atomic( - hw, mac80211_hwsim_beacon_tx, hw); + hw, IEEE80211_IFACE_ITER_NORMAL, + mac80211_hwsim_beacon_tx, hw); data->beacon_timer.expires = jiffies + data->beacon_int; add_timer(&data->beacon_timer); @@ -877,7 +1018,7 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) wiphy_debug(hw->wiphy, "%s (freq=%d/%s idle=%d ps=%d smps=%s)\n", __func__, - conf->channel->center_freq, + conf->channel ? conf->channel->center_freq : 0, hwsim_chantypes[conf->channel_type], !!(conf->flags & IEEE80211_CONF_IDLE), !!(conf->flags & IEEE80211_CONF_PS), @@ -886,6 +1027,9 @@ static int mac80211_hwsim_config(struct ieee80211_hw *hw, u32 changed) data->idle = !!(conf->flags & IEEE80211_CONF_IDLE); data->channel = conf->channel; + + WARN_ON(data->channel && channels > 1); + data->power_level = conf->power_level; if (!data->started || !data->beacon_int) del_timer(&data->beacon_timer); @@ -963,15 +1107,17 @@ static void mac80211_hwsim_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_HT) { - wiphy_debug(hw->wiphy, " HT: op_mode=0x%x, chantype=%s\n", - info->ht_operation_mode, - hwsim_chantypes[info->channel_type]); + wiphy_debug(hw->wiphy, " HT: op_mode=0x%x\n", + info->ht_operation_mode); } if (changed & BSS_CHANGED_BASIC_RATES) { wiphy_debug(hw->wiphy, " BASIC_RATES: 0x%llx\n", (unsigned long long) info->basic_rates); } + + if (changed & BSS_CHANGED_TXPOWER) + wiphy_debug(hw->wiphy, " TX Power: %d dBm\n", info->txpower); } static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw, @@ -1166,45 +1312,96 @@ static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop) /* Not implemented, queues only on kernel side */ } -struct hw_scan_done { - struct delayed_work w; - struct ieee80211_hw *hw; -}; - -static void hw_scan_done(struct work_struct *work) +static void hw_scan_work(struct work_struct *work) { - struct hw_scan_done *hsd = - container_of(work, struct hw_scan_done, w.work); + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, hw_scan.work); + struct cfg80211_scan_request *req = hwsim->hw_scan_request; + int dwell, i; + + mutex_lock(&hwsim->mutex); + if (hwsim->scan_chan_idx >= req->n_channels) { + wiphy_debug(hwsim->hw->wiphy, "hw scan complete\n"); + ieee80211_scan_completed(hwsim->hw, false); + hwsim->hw_scan_request = NULL; + hwsim->hw_scan_vif = NULL; + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + return; + } - ieee80211_scan_completed(hsd->hw, false); - kfree(hsd); + wiphy_debug(hwsim->hw->wiphy, "hw scan %d MHz\n", + req->channels[hwsim->scan_chan_idx]->center_freq); + + hwsim->tmp_chan = req->channels[hwsim->scan_chan_idx]; + if (hwsim->tmp_chan->flags & IEEE80211_CHAN_PASSIVE_SCAN || + !req->n_ssids) { + dwell = 120; + } else { + dwell = 30; + /* send probes */ + for (i = 0; i < req->n_ssids; i++) { + struct sk_buff *probe; + + probe = ieee80211_probereq_get(hwsim->hw, + hwsim->hw_scan_vif, + req->ssids[i].ssid, + req->ssids[i].ssid_len, + req->ie, req->ie_len); + if (!probe) + continue; + local_bh_disable(); + mac80211_hwsim_tx_frame(hwsim->hw, probe, + hwsim->tmp_chan); + local_bh_enable(); + } + } + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, + msecs_to_jiffies(dwell)); + hwsim->scan_chan_idx++; + mutex_unlock(&hwsim->mutex); } static int mac80211_hwsim_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct cfg80211_scan_request *req) { - struct hw_scan_done *hsd = kzalloc(sizeof(*hsd), GFP_KERNEL); - int i; - - if (!hsd) - return -ENOMEM; + struct mac80211_hwsim_data *hwsim = hw->priv; - hsd->hw = hw; - INIT_DELAYED_WORK(&hsd->w, hw_scan_done); + mutex_lock(&hwsim->mutex); + if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { + mutex_unlock(&hwsim->mutex); + return -EBUSY; + } + hwsim->hw_scan_request = req; + hwsim->hw_scan_vif = vif; + hwsim->scan_chan_idx = 0; + mutex_unlock(&hwsim->mutex); - printk(KERN_DEBUG "hwsim hw_scan request\n"); - for (i = 0; i < req->n_channels; i++) - printk(KERN_DEBUG "hwsim hw_scan freq %d\n", - req->channels[i]->center_freq); - print_hex_dump(KERN_DEBUG, "scan IEs: ", DUMP_PREFIX_OFFSET, - 16, 1, req->ie, req->ie_len, 1); + wiphy_debug(hw->wiphy, "hwsim hw_scan request\n"); - ieee80211_queue_delayed_work(hw, &hsd->w, 2 * HZ); + ieee80211_queue_delayed_work(hwsim->hw, &hwsim->hw_scan, 0); return 0; } +static void mac80211_hwsim_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + wiphy_debug(hw->wiphy, "hwsim cancel_hw_scan\n"); + + cancel_delayed_work_sync(&hwsim->hw_scan); + + mutex_lock(&hwsim->mutex); + ieee80211_scan_completed(hwsim->hw, true); + hwsim->tmp_chan = NULL; + hwsim->hw_scan_request = NULL; + hwsim->hw_scan_vif = NULL; + mutex_unlock(&hwsim->mutex); +} + static void mac80211_hwsim_sw_scan(struct ieee80211_hw *hw) { struct mac80211_hwsim_data *hwsim = hw->priv; @@ -1235,6 +1432,111 @@ static void mac80211_hwsim_sw_scan_complete(struct ieee80211_hw *hw) mutex_unlock(&hwsim->mutex); } +static void hw_roc_done(struct work_struct *work) +{ + struct mac80211_hwsim_data *hwsim = + container_of(work, struct mac80211_hwsim_data, roc_done.work); + + mutex_lock(&hwsim->mutex); + ieee80211_remain_on_channel_expired(hwsim->hw); + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + + wiphy_debug(hwsim->hw->wiphy, "hwsim ROC expired\n"); +} + +static int mac80211_hwsim_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel *chan, + int duration) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + mutex_lock(&hwsim->mutex); + if (WARN_ON(hwsim->tmp_chan || hwsim->hw_scan_request)) { + mutex_unlock(&hwsim->mutex); + return -EBUSY; + } + + hwsim->tmp_chan = chan; + mutex_unlock(&hwsim->mutex); + + wiphy_debug(hw->wiphy, "hwsim ROC (%d MHz, %d ms)\n", + chan->center_freq, duration); + + ieee80211_ready_on_channel(hw); + + ieee80211_queue_delayed_work(hw, &hwsim->roc_done, + msecs_to_jiffies(duration)); + return 0; +} + +static int mac80211_hwsim_croc(struct ieee80211_hw *hw) +{ + struct mac80211_hwsim_data *hwsim = hw->priv; + + cancel_delayed_work_sync(&hwsim->roc_done); + + mutex_lock(&hwsim->mutex); + hwsim->tmp_chan = NULL; + mutex_unlock(&hwsim->mutex); + + wiphy_debug(hw->wiphy, "hwsim ROC canceled\n"); + + return 0; +} + +static int mac80211_hwsim_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_set_chanctx_magic(ctx); + wiphy_debug(hw->wiphy, + "add channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); + return 0; +} + +static void mac80211_hwsim_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + wiphy_debug(hw->wiphy, + "remove channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); + hwsim_check_chanctx_magic(ctx); + hwsim_clear_chanctx_magic(ctx); +} + +static void mac80211_hwsim_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + u32 changed) +{ + hwsim_check_chanctx_magic(ctx); + wiphy_debug(hw->wiphy, + "change channel context control: %d MHz/width: %d/cfreqs:%d/%d MHz\n", + ctx->def.chan->center_freq, ctx->def.width, + ctx->def.center_freq1, ctx->def.center_freq2); +} + +static int mac80211_hwsim_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_check_magic(vif); + hwsim_check_chanctx_magic(ctx); + + return 0; +} + +static void mac80211_hwsim_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + hwsim_check_magic(vif); + hwsim_check_chanctx_magic(ctx); +} + static struct ieee80211_ops mac80211_hwsim_ops = { .tx = mac80211_hwsim_tx, @@ -1315,7 +1617,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_pspoll *pspoll; - u32 _portid; if (!vp->assoc) return; @@ -1335,25 +1636,18 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif) memcpy(pspoll->bssid, vp->bssid, ETH_ALEN); memcpy(pspoll->ta, mac, ETH_ALEN); - /* wmediumd mode check */ - _portid = ACCESS_ONCE(wmediumd_portid); - - if (_portid) - return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); - - if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) - printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__); - dev_kfree_skb(skb); + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); } - static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, struct ieee80211_vif *vif, int ps) { struct hwsim_vif_priv *vp = (void *)vif->drv_priv; struct sk_buff *skb; struct ieee80211_hdr *hdr; - u32 _portid; if (!vp->assoc) return; @@ -1374,15 +1668,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, memcpy(hdr->addr2, mac, ETH_ALEN); memcpy(hdr->addr3, vp->bssid, ETH_ALEN); - /* wmediumd mode check */ - _portid = ACCESS_ONCE(wmediumd_portid); - - if (_portid) - return mac80211_hwsim_tx_frame_nl(data->hw, skb, _portid); - - if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb)) - printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__); - dev_kfree_skb(skb); + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); + rcu_read_unlock(); } @@ -1423,14 +1712,17 @@ static int hwsim_fops_ps_write(void *dat, u64 val) if (val == PS_MANUAL_POLL) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_ps_poll, data); data->ps_poll_pending = true; } else if (old_ps == PS_DISABLED && val != PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_nullfunc_ps, data); } else if (old_ps != PS_DISABLED && val == PS_DISABLED) { ieee80211_iterate_active_interfaces(data->hw, + IEEE80211_IFACE_ITER_NORMAL, hwsim_send_nullfunc_no_ps, data); } @@ -1551,7 +1843,8 @@ static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2, (hwsim_flags & HWSIM_TX_STAT_ACK)) { if (skb->len >= 16) { hdr = (struct ieee80211_hdr *) skb->data; - mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2); + mac80211_hwsim_monitor_ack(txi->rate_driver_data[0], + hdr->addr2); } txi->flags |= IEEE80211_TX_STAT_ACK; } @@ -1566,7 +1859,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, struct genl_info *info) { - struct mac80211_hwsim_data *data2; + struct mac80211_hwsim_data *data2; struct ieee80211_rx_status rx_status; struct mac_address *dst; int frame_data_len; @@ -1574,9 +1867,9 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, struct sk_buff *skb = NULL; if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] || - !info->attrs[HWSIM_ATTR_FRAME] || - !info->attrs[HWSIM_ATTR_RX_RATE] || - !info->attrs[HWSIM_ATTR_SIGNAL]) + !info->attrs[HWSIM_ATTR_FRAME] || + !info->attrs[HWSIM_ATTR_RX_RATE] || + !info->attrs[HWSIM_ATTR_SIGNAL]) goto out; dst = (struct mac_address *)nla_data( @@ -1604,7 +1897,7 @@ static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2, /* check if radio is configured properly */ - if (data2->idle || !data2->started || !data2->channel) + if (data2->idle || !data2->started) goto out; /*A frame is received from user space*/ @@ -1688,6 +1981,11 @@ static struct notifier_block hwsim_netlink_notifier = { static int hwsim_init_netlink(void) { int rc; + + /* userspace test API hasn't been adjusted for multi-channel */ + if (channels > 1) + return 0; + printk(KERN_INFO "mac80211_hwsim: initializing netlink\n"); rc = genl_register_family_with_ops(&hwsim_genl_family, @@ -1710,6 +2008,10 @@ static void hwsim_exit_netlink(void) { int ret; + /* userspace test API hasn't been adjusted for multi-channel */ + if (channels > 1) + return; + printk(KERN_INFO "mac80211_hwsim: closing netlink\n"); /* unregister the notifier */ netlink_unregister_notifier(&hwsim_netlink_notifier); @@ -1732,7 +2034,7 @@ static const struct ieee80211_iface_limit hwsim_if_limits[] = { { .max = 1, .types = BIT(NL80211_IFTYPE_P2P_DEVICE) }, }; -static const struct ieee80211_iface_combination hwsim_if_comb = { +static struct ieee80211_iface_combination hwsim_if_comb = { .limits = hwsim_if_limits, .n_limits = ARRAY_SIZE(hwsim_if_limits), .max_interfaces = 2048, @@ -1750,10 +2052,30 @@ static int __init init_mac80211_hwsim(void) if (radios < 1 || radios > 100) return -EINVAL; - if (fake_hw_scan) { + if (channels < 1) + return -EINVAL; + + if (channels > 1) { + hwsim_if_comb.num_different_channels = channels; mac80211_hwsim_ops.hw_scan = mac80211_hwsim_hw_scan; + mac80211_hwsim_ops.cancel_hw_scan = + mac80211_hwsim_cancel_hw_scan; mac80211_hwsim_ops.sw_scan_start = NULL; mac80211_hwsim_ops.sw_scan_complete = NULL; + mac80211_hwsim_ops.remain_on_channel = + mac80211_hwsim_roc; + mac80211_hwsim_ops.cancel_remain_on_channel = + mac80211_hwsim_croc; + mac80211_hwsim_ops.add_chanctx = + mac80211_hwsim_add_chanctx; + mac80211_hwsim_ops.remove_chanctx = + mac80211_hwsim_remove_chanctx; + mac80211_hwsim_ops.change_chanctx = + mac80211_hwsim_change_chanctx; + mac80211_hwsim_ops.assign_vif_chanctx = + mac80211_hwsim_assign_vif_chanctx; + mac80211_hwsim_ops.unassign_vif_chanctx = + mac80211_hwsim_unassign_vif_chanctx; } spin_lock_init(&hwsim_radio_lock); @@ -1803,13 +2125,18 @@ static int __init init_mac80211_hwsim(void) hw->wiphy->iface_combinations = &hwsim_if_comb; hw->wiphy->n_iface_combinations = 1; - if (fake_hw_scan) { + if (channels > 1) { hw->wiphy->max_scan_ssids = 255; hw->wiphy->max_scan_ie_len = IEEE80211_MAX_DATA_LEN; + hw->wiphy->max_remain_on_channel_duration = 1000; } + INIT_DELAYED_WORK(&data->roc_done, hw_roc_done); + INIT_DELAYED_WORK(&data->hw_scan, hw_scan_work); + hw->channel_change_time = 1; - hw->queues = 4; + hw->queues = 5; + hw->offchannel_tx_hw_queue = 4; hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_AP) | @@ -1824,7 +2151,8 @@ static int __init init_mac80211_hwsim(void) IEEE80211_HW_SUPPORTS_STATIC_SMPS | IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS | IEEE80211_HW_AMPDU_AGGREGATION | - IEEE80211_HW_WANT_MONITOR_VIF; + IEEE80211_HW_WANT_MONITOR_VIF | + IEEE80211_HW_QUEUE_CONTROL; hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; @@ -1874,6 +2202,34 @@ static int __init init_mac80211_hwsim(void) sband->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; hw->wiphy->bands[band] = sband; + + if (channels == 1) + continue; + + sband->vht_cap.vht_supported = true; + sband->vht_cap.cap = + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454 | + IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ | + IEEE80211_VHT_CAP_RXLDPC | + IEEE80211_VHT_CAP_SHORT_GI_80 | + IEEE80211_VHT_CAP_SHORT_GI_160 | + IEEE80211_VHT_CAP_TXSTBC | + IEEE80211_VHT_CAP_RXSTBC_1 | + IEEE80211_VHT_CAP_RXSTBC_2 | + IEEE80211_VHT_CAP_RXSTBC_3 | + IEEE80211_VHT_CAP_RXSTBC_4 | + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT; + sband->vht_cap.vht_mcs.rx_mcs_map = + cpu_to_le16(IEEE80211_VHT_MCS_SUPPORT_0_8 << 0 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 2 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 4 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 6 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 8 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 10 | + IEEE80211_VHT_MCS_SUPPORT_0_9 << 12 | + IEEE80211_VHT_MCS_SUPPORT_0_8 << 14); + sband->vht_cap.vht_mcs.tx_mcs_map = + sband->vht_cap.vht_mcs.rx_mcs_map; } /* By default all radios are belonging to the first group */ data->group = 1; diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 395f1bfd410..68d52cfc1eb 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -197,7 +197,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, ra_list_flags); mwifiex_11n_form_amsdu_pkt(skb_aggr, skb_src, &pad); - mwifiex_write_data_complete(adapter, skb_src, 0); + mwifiex_write_data_complete(adapter, skb_src, 0, 0); spin_lock_irqsave(&priv->wmm.ra_list_spinlock, ra_list_flags); @@ -256,7 +256,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, if (!mwifiex_is_ralist_valid(priv, pra_list, ptrindex)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - mwifiex_write_data_complete(adapter, skb_aggr, -1); + mwifiex_write_data_complete(adapter, skb_aggr, 1, -1); return -1; } if (GET_BSS_ROLE(priv) == MWIFIEX_BSS_ROLE_STA && @@ -282,13 +282,13 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, dev_err(adapter->dev, "%s: host_to_card failed: %#x\n", __func__, ret); adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb_aggr, ret); + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); return 0; case -EINPROGRESS: adapter->data_sent = false; break; case 0: - mwifiex_write_data_complete(adapter, skb_aggr, ret); + mwifiex_write_data_complete(adapter, skb_aggr, 1, ret); break; default: break; diff --git a/drivers/net/wireless/mwifiex/Kconfig b/drivers/net/wireless/mwifiex/Kconfig index 8e384fae3e6..b2e27723f80 100644 --- a/drivers/net/wireless/mwifiex/Kconfig +++ b/drivers/net/wireless/mwifiex/Kconfig @@ -1,7 +1,6 @@ config MWIFIEX tristate "Marvell WiFi-Ex Driver" depends on CFG80211 - select LIB80211 ---help--- This adds support for wireless adapters based on Marvell 802.11n chipsets. diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index fdb1eb86102..3b1c27712ad 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -180,10 +180,8 @@ mwifiex_form_mgmt_frame(struct sk_buff *skb, const u8 *buf, size_t len) static int mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct sk_buff *skb; u16 pkt_len; @@ -253,7 +251,6 @@ static int mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { struct mwifiex_private *priv = mwifiex_netdev_get_priv(wdev->netdev); @@ -271,15 +268,14 @@ mwifiex_cfg80211_remain_on_channel(struct wiphy *wiphy, } ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_SET, chan, - &channel_type, duration); + duration); if (!ret) { *cookie = random32() | 1; priv->roc_cfg.cookie = *cookie; priv->roc_cfg.chan = *chan; - priv->roc_cfg.chan_type = channel_type; - cfg80211_ready_on_channel(wdev, *cookie, chan, channel_type, + cfg80211_ready_on_channel(wdev, *cookie, chan, duration, GFP_ATOMIC); wiphy_dbg(wiphy, "info: ROC, cookie = 0x%llx\n", *cookie); @@ -302,13 +298,11 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, return -ENOENT; ret = mwifiex_remain_on_chan_cfg(priv, HostCmd_ACT_GEN_REMOVE, - &priv->roc_cfg.chan, - &priv->roc_cfg.chan_type, 0); + &priv->roc_cfg.chan, 0); if (!ret) { cfg80211_remain_on_channel_expired(wdev, cookie, &priv->roc_cfg.chan, - priv->roc_cfg.chan_type, GFP_ATOMIC); memset(&priv->roc_cfg, 0, sizeof(struct mwifiex_roc_cfg)); @@ -324,6 +318,7 @@ mwifiex_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, */ static int mwifiex_cfg80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { @@ -1296,21 +1291,23 @@ static int mwifiex_cfg80211_start_ap(struct wiphy *wiphy, return -EINVAL; } - bss_cfg->channel = - (u8)ieee80211_frequency_to_channel(params->channel->center_freq); + bss_cfg->channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); /* Set appropriate bands */ - if (params->channel->band == IEEE80211_BAND_2GHZ) { + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { bss_cfg->band_cfg = BAND_CONFIG_BG; - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) config_bands = BAND_B | BAND_G; else config_bands = BAND_B | BAND_G | BAND_GN; } else { bss_cfg->band_cfg = BAND_CONFIG_A; - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) == + NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; @@ -1683,7 +1680,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, int index = 0, i; u8 config_bands = 0; - if (params->channel->band == IEEE80211_BAND_2GHZ) { + if (params->chandef.chan->band == IEEE80211_BAND_2GHZ) { if (!params->basic_rates) { config_bands = BAND_B | BAND_G; } else { @@ -1708,10 +1705,12 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, } } - if (params->channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) config_bands |= BAND_GN; } else { - if (params->channel_type == NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(¶ms->chandef) != + NL80211_CHAN_NO_HT) config_bands = BAND_A; else config_bands = BAND_AN | BAND_A; @@ -1728,9 +1727,10 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv, } adapter->sec_chan_offset = - mwifiex_chan_type_to_sec_chan_offset(params->channel_type); - priv->adhoc_channel = - ieee80211_frequency_to_channel(params->channel->center_freq); + mwifiex_chan_type_to_sec_chan_offset( + cfg80211_get_chandef_type(¶ms->chandef)); + priv->adhoc_channel = ieee80211_frequency_to_channel( + params->chandef.chan->center_freq); wiphy_dbg(wiphy, "info: set ibss band %d, chan %d, chan offset %d\n", config_bands, priv->adhoc_channel, adapter->sec_chan_offset); @@ -1764,7 +1764,8 @@ mwifiex_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev, ret = mwifiex_cfg80211_assoc(priv, params->ssid_len, params->ssid, params->bssid, priv->bss_mode, - params->channel, NULL, params->privacy); + params->chandef.chan, NULL, + params->privacy); done: if (!ret) { cfg80211_ibss_joined(priv->netdev, priv->cfg_bssid, GFP_KERNEL); @@ -2080,8 +2081,8 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } - dev = alloc_netdev_mq(sizeof(struct mwifiex_private *), name, - ether_setup, 1); + dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, + ether_setup, IEEE80211_NUM_ACS, 1); if (!dev) { wiphy_err(wiphy, "no memory available for netdevice\n"); priv->bss_mode = NL80211_IFTYPE_UNSPECIFIED; @@ -2143,8 +2144,7 @@ int mwifiex_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev) mwifiex_dev_debugfs_remove(priv); #endif - if (!netif_queue_stopped(priv->netdev)) - netif_stop_queue(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index be52a0a8d64..5f438e6c215 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -949,6 +949,9 @@ mwifiex_cmd_timeout_func(unsigned long function_context) } if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) mwifiex_init_fw_complete(adapter); + + if (adapter->if_ops.card_reset) + adapter->if_ops.card_reset(adapter); } /* diff --git a/drivers/net/wireless/mwifiex/debugfs.c b/drivers/net/wireless/mwifiex/debugfs.c index a870b5885c0..46e34aa65d1 100644 --- a/drivers/net/wireless/mwifiex/debugfs.c +++ b/drivers/net/wireless/mwifiex/debugfs.c @@ -178,6 +178,7 @@ mwifiex_info_read(struct file *file, char __user *ubuf, (struct mwifiex_private *) file->private_data; struct net_device *netdev = priv->netdev; struct netdev_hw_addr *ha; + struct netdev_queue *txq; unsigned long page = get_zeroed_page(GFP_KERNEL); char *p = (char *) page, fmt[64]; struct mwifiex_bss_info info; @@ -229,8 +230,13 @@ mwifiex_info_read(struct file *file, char __user *ubuf, p += sprintf(p, "num_rx_pkts_err = %lu\n", priv->stats.rx_errors); p += sprintf(p, "carrier %s\n", ((netif_carrier_ok(priv->netdev)) ? "on" : "off")); - p += sprintf(p, "tx queue %s\n", ((netif_queue_stopped(priv->netdev)) - ? "stopped" : "started")); + p += sprintf(p, "tx queue"); + for (i = 0; i < netdev->num_tx_queues; i++) { + txq = netdev_get_tx_queue(netdev, i); + p += sprintf(p, " %d:%s", i, netif_tx_queue_stopped(txq) ? + "stopped" : "started"); + } + p += sprintf(p, "\n"); ret = simple_read_from_buffer(ubuf, count, ppos, (char *) page, (unsigned long) p - page); diff --git a/drivers/net/wireless/mwifiex/init.c b/drivers/net/wireless/mwifiex/init.c index 482faace790..39f03ce5a5b 100644 --- a/drivers/net/wireless/mwifiex/init.c +++ b/drivers/net/wireless/mwifiex/init.c @@ -388,9 +388,17 @@ void mwifiex_wake_up_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; + unsigned int i; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - netif_tx_wake_all_queues(netdev); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (netif_tx_queue_stopped(txq)) + netif_tx_wake_queue(txq); + } + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } @@ -401,9 +409,17 @@ void mwifiex_stop_net_dev_queue(struct net_device *netdev, struct mwifiex_adapter *adapter) { unsigned long dev_queue_flags; + unsigned int i; spin_lock_irqsave(&adapter->queue_lock, dev_queue_flags); - netif_tx_stop_all_queues(netdev); + + for (i = 0; i < netdev->num_tx_queues; i++) { + struct netdev_queue *txq = netdev_get_tx_queue(netdev, i); + + if (!netif_tx_queue_stopped(txq)) + netif_tx_stop_queue(txq); + } + spin_unlock_irqrestore(&adapter->queue_lock, dev_queue_flags); } diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 7b0858af8f5..88664ae667b 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -721,8 +721,7 @@ int mwifiex_ret_802_11_associate(struct mwifiex_private *priv, if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); - if (netif_queue_stopped(priv->netdev)) - netif_wake_queue(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); if (priv->sec_info.wpa_enabled || priv->sec_info.wpa2_enabled) priv->scan_block = true; @@ -1238,8 +1237,7 @@ int mwifiex_ret_802_11_ad_hoc(struct mwifiex_private *priv, if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); - if (netif_queue_stopped(priv->netdev)) - netif_wake_queue(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); mwifiex_save_curr_bcn(priv); diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 1df767bc8b6..9c802ede9c3 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -282,6 +282,7 @@ exit_main_proc: mwifiex_shutdown_drv(adapter); return ret; } +EXPORT_SYMBOL_GPL(mwifiex_main_process); /* * This function frees the adapter structure. @@ -412,49 +413,6 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter) } /* - * This function fills a driver buffer. - * - * The function associates a given SKB with the provided driver buffer - * and also updates some of the SKB parameters, including IP header, - * priority and timestamp. - */ -static void -mwifiex_fill_buffer(struct sk_buff *skb) -{ - struct ethhdr *eth; - struct iphdr *iph; - struct timeval tv; - u8 tid = 0; - - eth = (struct ethhdr *) skb->data; - switch (eth->h_proto) { - case __constant_htons(ETH_P_IP): - iph = ip_hdr(skb); - tid = IPTOS_PREC(iph->tos); - pr_debug("data: packet type ETH_P_IP: %04x, tid=%#x prio=%#x\n", - eth->h_proto, tid, skb->priority); - break; - case __constant_htons(ETH_P_ARP): - pr_debug("data: ARP packet: %04x\n", eth->h_proto); - default: - break; - } -/* Offset for TOS field in the IP header */ -#define IPTOS_OFFSET 5 - tid = (tid >> IPTOS_OFFSET); - skb->priority = tid; - /* Record the current time the packet was queued; used to - determine the amount of time the packet was queued in - the driver before it was sent to the firmware. - The delay is then sent along with the packet to the - firmware for aggregate delay calculation for stats and - MSDU lifetime expiry. - */ - do_gettimeofday(&tv); - skb->tstamp = timeval_to_ktime(tv); -} - -/* * CFG802.11 network device handler for open. * * Starts the data queue. @@ -488,17 +446,23 @@ mwifiex_close(struct net_device *dev) */ int mwifiex_queue_tx_pkt(struct mwifiex_private *priv, struct sk_buff *skb) { - mwifiex_wmm_add_buf_txqueue(priv, skb); + struct netdev_queue *txq; + int index = mwifiex_1d_to_wmm_queue[skb->priority]; + + if (atomic_inc_return(&priv->wmm_tx_pending[index]) >= MAX_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (!netif_tx_queue_stopped(txq)) { + netif_tx_stop_queue(txq); + dev_dbg(priv->adapter->dev, "stop queue: %d\n", index); + } + } + atomic_inc(&priv->adapter->tx_pending); + mwifiex_wmm_add_buf_txqueue(priv, skb); if (priv->adapter->scan_delay_cnt) atomic_set(&priv->adapter->is_tx_received, true); - if (atomic_read(&priv->adapter->tx_pending) >= MAX_TX_PENDING) { - mwifiex_set_trans_start(priv->netdev); - mwifiex_stop_net_dev_queue(priv->netdev, priv->adapter); - } - queue_work(priv->adapter->workqueue, &priv->adapter->main_work); return 0; @@ -513,6 +477,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) struct mwifiex_private *priv = mwifiex_netdev_get_priv(dev); struct sk_buff *new_skb; struct mwifiex_txinfo *tx_info; + struct timeval tv; dev_dbg(priv->adapter->dev, "data: %lu BSS(%d-%d): Data <= kernel\n", jiffies, priv->bss_type, priv->bss_num); @@ -550,7 +515,16 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_info = MWIFIEX_SKB_TXCB(skb); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; - mwifiex_fill_buffer(skb); + + /* Record the current time the packet was queued; used to + * determine the amount of time the packet was queued in + * the driver before it was sent to the firmware. + * The delay is then sent along with the packet to the + * firmware for aggregate delay calculation for stats and + * MSDU lifetime expiry. + */ + do_gettimeofday(&tv); + skb->tstamp = timeval_to_ktime(tv); mwifiex_queue_tx_pkt(priv, skb); @@ -630,6 +604,13 @@ static struct net_device_stats *mwifiex_get_stats(struct net_device *dev) return &priv->stats; } +static u16 +mwifiex_netdev_select_wmm_queue(struct net_device *dev, struct sk_buff *skb) +{ + skb->priority = cfg80211_classify8021d(skb); + return mwifiex_1d_to_wmm_queue[skb->priority]; +} + /* Network device handlers */ static const struct net_device_ops mwifiex_netdev_ops = { .ndo_open = mwifiex_open, @@ -639,6 +620,7 @@ static const struct net_device_ops mwifiex_netdev_ops = { .ndo_tx_timeout = mwifiex_tx_timeout, .ndo_get_stats = mwifiex_get_stats, .ndo_set_rx_mode = mwifiex_set_multicast_list, + .ndo_select_queue = mwifiex_netdev_select_wmm_queue, }; /* @@ -838,9 +820,7 @@ int mwifiex_remove_card(struct mwifiex_adapter *adapter, struct semaphore *sem) for (i = 0; i < adapter->priv_num; i++) { priv = adapter->priv[i]; if (priv && priv->netdev) { - if (!netif_queue_stopped(priv->netdev)) - mwifiex_stop_net_dev_queue(priv->netdev, - adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); } diff --git a/drivers/net/wireless/mwifiex/main.h b/drivers/net/wireless/mwifiex/main.h index 81f8772dcb0..1b3cfc82194 100644 --- a/drivers/net/wireless/mwifiex/main.h +++ b/drivers/net/wireless/mwifiex/main.h @@ -371,7 +371,6 @@ struct wps { struct mwifiex_roc_cfg { u64 cookie; struct ieee80211_channel chan; - enum nl80211_channel_type chan_type; }; struct mwifiex_adapter; @@ -440,6 +439,7 @@ struct mwifiex_private { u8 wmm_enabled; u8 wmm_qosinfo; struct mwifiex_wmm_desc wmm; + atomic_t wmm_tx_pending[IEEE80211_NUM_ACS]; struct list_head sta_list; /* spin lock for associated station list */ spinlock_t sta_list_spinlock; @@ -600,6 +600,7 @@ struct mwifiex_if_ops { int (*event_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*data_complete) (struct mwifiex_adapter *, struct sk_buff *); int (*dnld_fw) (struct mwifiex_adapter *, struct mwifiex_fw_image *); + void (*card_reset) (struct mwifiex_adapter *); }; struct mwifiex_adapter { @@ -788,7 +789,7 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, struct mwifiex_tx_param *tx_param); int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags); int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int status); + struct sk_buff *skb, int aggr, int status); void mwifiex_clean_txrx(struct mwifiex_private *priv); u8 mwifiex_check_last_packet_indication(struct mwifiex_private *priv); void mwifiex_check_ps_cond(struct mwifiex_adapter *adapter); @@ -1016,7 +1017,6 @@ int mwifiex_get_ver_ext(struct mwifiex_private *priv); int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, - enum nl80211_channel_type *channel_type, unsigned int duration); int mwifiex_set_bss_role(struct mwifiex_private *priv, u8 bss_role); diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 82cf0fa2d9f..5a1c1d0e559 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -906,8 +906,8 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter) /* * SDIO interrupt handler. * - * This function reads the interrupt status from firmware and assigns - * the main process in workqueue which will handle the interrupt. + * This function reads the interrupt status from firmware and handles + * the interrupt in current thread (ksdioirqd) right away. */ static void mwifiex_sdio_interrupt(struct sdio_func *func) @@ -930,7 +930,7 @@ mwifiex_sdio_interrupt(struct sdio_func *func) adapter->ps_state = PS_STATE_AWAKE; mwifiex_interrupt_status(adapter); - queue_work(adapter->workqueue, &adapter->main_work); + mwifiex_main_process(adapter); } /* @@ -1749,6 +1749,37 @@ mwifiex_update_mp_end_port(struct mwifiex_adapter *adapter, u16 port) port, card->mp_data_port_mask); } +static struct mmc_host *reset_host; +static void sdio_card_reset_worker(struct work_struct *work) +{ + /* The actual reset operation must be run outside of driver thread. + * This is because mmc_remove_host() will cause the device to be + * instantly destroyed, and the driver then needs to end its thread, + * leading to a deadlock. + * + * We run it in a totally independent workqueue. + */ + + pr_err("Resetting card...\n"); + mmc_remove_host(reset_host); + /* 20ms delay is based on experiment with sdhci controller */ + mdelay(20); + mmc_add_host(reset_host); +} +static DECLARE_WORK(card_reset_work, sdio_card_reset_worker); + +/* This function resets the card */ +static void mwifiex_sdio_card_reset(struct mwifiex_adapter *adapter) +{ + struct sdio_mmc_card *card = adapter->card; + + if (work_pending(&card_reset_work)) + return; + + reset_host = card->func->card->host; + schedule_work(&card_reset_work); +} + static struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, @@ -1767,6 +1798,7 @@ static struct mwifiex_if_ops sdio_ops = { .cleanup_mpa_buf = mwifiex_cleanup_mpa_buf, .cmdrsp_complete = mwifiex_sdio_cmdrsp_complete, .event_complete = mwifiex_sdio_event_complete, + .card_reset = mwifiex_sdio_card_reset, }; /* @@ -1804,6 +1836,7 @@ mwifiex_sdio_cleanup_module(void) /* Set the flag as user is removing this module. */ user_rmmod = 1; + cancel_work_sync(&card_reset_work); sdio_unregister_driver(&mwifiex_sdio); } diff --git a/drivers/net/wireless/mwifiex/sdio.h b/drivers/net/wireless/mwifiex/sdio.h index 21033738ef0..8cc5468654b 100644 --- a/drivers/net/wireless/mwifiex/sdio.h +++ b/drivers/net/wireless/mwifiex/sdio.h @@ -25,6 +25,7 @@ #include <linux/mmc/sdio_ids.h> #include <linux/mmc/sdio_func.h> #include <linux/mmc/card.h> +#include <linux/mmc/host.h> #include "main.h" diff --git a/drivers/net/wireless/mwifiex/sta_event.c b/drivers/net/wireless/mwifiex/sta_event.c index 8132119e1a2..41aafc7454e 100644 --- a/drivers/net/wireless/mwifiex/sta_event.c +++ b/drivers/net/wireless/mwifiex/sta_event.c @@ -124,8 +124,7 @@ mwifiex_reset_connect_state(struct mwifiex_private *priv, u16 reason_code) } memset(priv->cfg_bssid, 0, ETH_ALEN); - if (!netif_queue_stopped(priv->netdev)) - mwifiex_stop_net_dev_queue(priv->netdev, adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); } @@ -197,8 +196,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "event: LINK_SENSED\n"); if (!netif_carrier_ok(priv->netdev)) netif_carrier_on(priv->netdev); - if (netif_queue_stopped(priv->netdev)) - mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); break; case EVENT_DEAUTHENTICATED: @@ -306,8 +304,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) dev_dbg(adapter->dev, "event: ADHOC_BCN_LOST\n"); priv->adhoc_is_link_sensed = false; mwifiex_clean_txrx(priv); - if (!netif_queue_stopped(priv->netdev)) - mwifiex_stop_net_dev_queue(priv->netdev, adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); break; @@ -424,7 +421,6 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) cfg80211_remain_on_channel_expired(priv->wdev, priv->roc_cfg.cookie, &priv->roc_cfg.chan, - priv->roc_cfg.chan_type, GFP_ATOMIC); memset(&priv->roc_cfg, 0x00, sizeof(struct mwifiex_roc_cfg)); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 552d72ed055..237c8d2ba9f 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -276,8 +276,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, dev_dbg(adapter->dev, "info: SSID found in scan list ... " "associating...\n"); - if (!netif_queue_stopped(priv->netdev)) - mwifiex_stop_net_dev_queue(priv->netdev, adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); @@ -318,8 +317,7 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, ret = mwifiex_check_network_compatibility(priv, bss_desc); - if (!netif_queue_stopped(priv->netdev)) - mwifiex_stop_net_dev_queue(priv->netdev, adapter); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); if (netif_carrier_ok(priv->netdev)) netif_carrier_off(priv->netdev); @@ -1046,7 +1044,6 @@ mwifiex_get_ver_ext(struct mwifiex_private *priv) int mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, struct ieee80211_channel *chan, - enum nl80211_channel_type *ct, unsigned int duration) { struct host_cmd_ds_remain_on_chan roc_cfg; @@ -1056,7 +1053,7 @@ mwifiex_remain_on_chan_cfg(struct mwifiex_private *priv, u16 action, roc_cfg.action = cpu_to_le16(action); if (action == HostCmd_ACT_GEN_SET) { roc_cfg.band_cfg = chan->band; - sc = mwifiex_chan_type_to_sec_chan_offset(*ct); + sc = mwifiex_chan_type_to_sec_chan_offset(NL80211_CHAN_NO_HT); roc_cfg.band_cfg |= (sc << 2); roc_cfg.channel = diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 5cb3f7af874..8c80024c30f 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -121,13 +121,13 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, dev_err(adapter->dev, "mwifiex_write_data_async failed: 0x%X\n", ret); adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, ret); + mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: adapter->data_sent = false; break; case 0: - mwifiex_write_data_complete(adapter, skb, ret); + mwifiex_write_data_complete(adapter, skb, 0, ret); break; default: break; @@ -144,11 +144,12 @@ int mwifiex_process_tx(struct mwifiex_private *priv, struct sk_buff *skb, * wakes up stalled traffic queue if required, and then frees the buffer. */ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, - struct sk_buff *skb, int status) + struct sk_buff *skb, int aggr, int status) { - struct mwifiex_private *priv, *tpriv; + struct mwifiex_private *priv; struct mwifiex_txinfo *tx_info; - int i; + struct netdev_queue *txq; + int index; if (!skb) return 0; @@ -172,15 +173,20 @@ int mwifiex_write_data_complete(struct mwifiex_adapter *adapter, if (tx_info->flags & MWIFIEX_BUF_FLAG_BRIDGED_PKT) atomic_dec_return(&adapter->pending_bridged_pkts); - if (atomic_dec_return(&adapter->tx_pending) >= LOW_TX_PENDING) + + if (aggr) + /* For skb_aggr, do not wake up tx queue */ goto done; - for (i = 0; i < adapter->priv_num; i++) { - tpriv = adapter->priv[i]; + atomic_dec(&adapter->tx_pending); - if (tpriv->media_connected && - netif_queue_stopped(tpriv->netdev)) - mwifiex_wake_up_net_dev_queue(tpriv->netdev, adapter); + index = mwifiex_1d_to_wmm_queue[skb->priority]; + if (atomic_dec_return(&priv->wmm_tx_pending[index]) < LOW_TX_PENDING) { + txq = netdev_get_tx_queue(priv->netdev, index); + if (netif_tx_queue_stopped(txq)) { + netif_tx_wake_queue(txq); + dev_dbg(adapter->dev, "wake queue: %d\n", index); + } } done: dev_kfree_skb_any(skb); diff --git a/drivers/net/wireless/mwifiex/uap_event.c b/drivers/net/wireless/mwifiex/uap_event.c index a33fa394e34..21c640d3b57 100644 --- a/drivers/net/wireless/mwifiex/uap_event.c +++ b/drivers/net/wireless/mwifiex/uap_event.c @@ -235,11 +235,18 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) break; case EVENT_UAP_BSS_IDLE: priv->media_connected = false; + if (netif_carrier_ok(priv->netdev)) + netif_carrier_off(priv->netdev); + mwifiex_stop_net_dev_queue(priv->netdev, adapter); + mwifiex_clean_txrx(priv); mwifiex_del_all_sta_list(priv); break; case EVENT_UAP_BSS_ACTIVE: priv->media_connected = true; + if (!netif_carrier_ok(priv->netdev)) + netif_carrier_on(priv->netdev); + mwifiex_wake_up_net_dev_queue(priv->netdev, adapter); break; case EVENT_UAP_BSS_START: dev_dbg(adapter->dev, "AP EVENT: event id: %#x\n", eventcause); diff --git a/drivers/net/wireless/mwifiex/usb.c b/drivers/net/wireless/mwifiex/usb.c index 22a5916564b..bbe1f3518e4 100644 --- a/drivers/net/wireless/mwifiex/usb.c +++ b/drivers/net/wireless/mwifiex/usb.c @@ -238,7 +238,7 @@ static void mwifiex_usb_tx_complete(struct urb *urb) } else { dev_dbg(adapter->dev, "%s: DATA\n", __func__); atomic_dec(&card->tx_data_urb_pending); - mwifiex_write_data_complete(adapter, context->skb, + mwifiex_write_data_complete(adapter, context->skb, 0, urb->status ? -1 : 0); } diff --git a/drivers/net/wireless/mwifiex/wmm.c b/drivers/net/wireless/mwifiex/wmm.c index 600d8194610..818f871ae98 100644 --- a/drivers/net/wireless/mwifiex/wmm.c +++ b/drivers/net/wireless/mwifiex/wmm.c @@ -483,7 +483,7 @@ mwifiex_wmm_del_pkts_in_ralist_node(struct mwifiex_private *priv, struct sk_buff *skb, *tmp; skb_queue_walk_safe(&ra_list->skb_head, skb, tmp) - mwifiex_write_data_complete(adapter, skb, -1); + mwifiex_write_data_complete(adapter, skb, 0, -1); } /* @@ -650,7 +650,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, if (!priv->media_connected && !mwifiex_is_skb_mgmt_frame(skb)) { dev_dbg(adapter->dev, "data: drop packet in disconnect\n"); - mwifiex_write_data_complete(adapter, skb, -1); + mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -680,7 +680,7 @@ mwifiex_wmm_add_buf_txqueue(struct mwifiex_private *priv, if (!ra_list) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, flags); - mwifiex_write_data_complete(adapter, skb, -1); + mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -1090,7 +1090,7 @@ mwifiex_send_single_packet(struct mwifiex_private *priv, if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - mwifiex_write_data_complete(adapter, skb, -1); + mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -1195,7 +1195,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, if (!mwifiex_is_ralist_valid(priv, ptr, ptr_index)) { spin_unlock_irqrestore(&priv->wmm.ra_list_spinlock, ra_list_flags); - mwifiex_write_data_complete(adapter, skb, -1); + mwifiex_write_data_complete(adapter, skb, 0, -1); return; } @@ -1209,7 +1209,7 @@ mwifiex_send_processed_packet(struct mwifiex_private *priv, adapter->data_sent = false; dev_err(adapter->dev, "host_to_card failed: %#x\n", ret); adapter->dbg.num_tx_host_to_card_failure++; - mwifiex_write_data_complete(adapter, skb, ret); + mwifiex_write_data_complete(adapter, skb, 0, ret); break; case -EINPROGRESS: adapter->data_sent = false; diff --git a/drivers/net/wireless/mwifiex/wmm.h b/drivers/net/wireless/mwifiex/wmm.h index ec839952d2e..b92f39d8963 100644 --- a/drivers/net/wireless/mwifiex/wmm.h +++ b/drivers/net/wireless/mwifiex/wmm.h @@ -31,6 +31,8 @@ enum ieee_types_wmm_ecw_bitmasks { MWIFIEX_ECW_MAX = (BIT(4) | BIT(5) | BIT(6) | BIT(7)), }; +static const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; + /* * This function retrieves the TID of the given RA list. */ diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index 5099e5375cb..0cdae663273 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -1851,6 +1851,7 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, bool start_ba_session = false; bool mgmtframe = false; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + bool eapol_frame = false; wh = (struct ieee80211_hdr *)skb->data; if (ieee80211_is_data_qos(wh->frame_control)) @@ -1858,6 +1859,9 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, else qos = 0; + if (skb->protocol == cpu_to_be16(ETH_P_PAE)) + eapol_frame = true; + if (ieee80211_is_mgmt(wh->frame_control)) mgmtframe = true; @@ -1916,9 +1920,8 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, txpriority = index; - if (priv->ap_fw && sta && sta->ht_cap.ht_supported - && skb->protocol != cpu_to_be16(ETH_P_PAE) - && ieee80211_is_data_qos(wh->frame_control)) { + if (priv->ap_fw && sta && sta->ht_cap.ht_supported && !eapol_frame && + ieee80211_is_data_qos(wh->frame_control)) { tid = qos & 0xf; mwl8k_tx_count_packet(sta, tid); spin_lock(&priv->stream_lock); @@ -2005,6 +2008,8 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, spin_unlock(&priv->stream_lock); } spin_unlock_bh(&priv->tx_lock); + pci_unmap_single(priv->pdev, dma, skb->len, + PCI_DMA_TODEVICE); dev_kfree_skb(skb); return; } @@ -2025,9 +2030,11 @@ mwl8k_txq_xmit(struct ieee80211_hw *hw, else tx->peer_id = 0; - if (priv->ap_fw) + if (priv->ap_fw && ieee80211_is_data(wh->frame_control) && !eapol_frame) tx->timestamp = cpu_to_le32(ioread32(priv->regs + MWL8K_HW_TIMER_REGISTER)); + else + tx->timestamp = 0; wmb(); tx->status = cpu_to_le32(MWL8K_TXD_STATUS_FW_OWNED | txstatus); @@ -3679,7 +3686,8 @@ struct mwl8k_cmd_bastream { } __packed; static int -mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) +mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, + struct ieee80211_vif *vif) { struct mwl8k_cmd_bastream *cmd; int rc; @@ -3702,7 +3710,7 @@ mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE) | cpu_to_le32(BASTREAM_FLAG_DIRECTION_UPSTREAM); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); kfree(cmd); @@ -3711,7 +3719,7 @@ mwl8k_check_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream) static int mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, - u8 buf_size) + u8 buf_size, struct ieee80211_vif *vif) { struct mwl8k_cmd_bastream *cmd; int rc; @@ -3745,7 +3753,7 @@ mwl8k_create_ba(struct ieee80211_hw *hw, struct mwl8k_ampdu_stream *stream, cpu_to_le32(BASTREAM_FLAG_IMMEDIATE_TYPE | BASTREAM_FLAG_DIRECTION_UPSTREAM); - rc = mwl8k_post_cmd(hw, &cmd->header); + rc = mwl8k_post_pervif_cmd(hw, vif, &cmd->header); wiphy_debug(hw->wiphy, "Created a BA stream for %pM : tid %d\n", stream->sta->addr, stream->tid); @@ -5085,6 +5093,7 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct mwl8k_priv *priv = hw->priv; struct mwl8k_ampdu_stream *stream; u8 *addr = sta->addr; + struct mwl8k_sta *sta_info = MWL8K_STA(sta); if (!(hw->flags & IEEE80211_HW_AMPDU_AGGREGATION)) return -ENOTSUPP; @@ -5127,7 +5136,16 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, /* Release the lock before we do the time consuming stuff */ spin_unlock(&priv->stream_lock); for (i = 0; i < MAX_AMPDU_ATTEMPTS; i++) { - rc = mwl8k_check_ba(hw, stream); + + /* Check if link is still valid */ + if (!sta_info->is_ampdu_allowed) { + spin_lock(&priv->stream_lock); + mwl8k_remove_stream(hw, stream); + spin_unlock(&priv->stream_lock); + return -EBUSY; + } + + rc = mwl8k_check_ba(hw, stream, vif); /* If HW restart is in progress mwl8k_post_cmd will * return -EBUSY. Avoid retrying mwl8k_check_ba in @@ -5167,7 +5185,7 @@ mwl8k_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, BUG_ON(stream == NULL); BUG_ON(stream->state != AMPDU_STREAM_IN_PROGRESS); spin_unlock(&priv->stream_lock); - rc = mwl8k_create_ba(hw, stream, buf_size); + rc = mwl8k_create_ba(hw, stream, buf_size, vif); spin_lock(&priv->stream_lock); if (!rc) stream->state = AMPDU_STREAM_ACTIVE; @@ -5617,6 +5635,18 @@ fail: return rc; } +static const struct ieee80211_iface_limit ap_if_limits[] = { + { .max = 8, .types = BIT(NL80211_IFTYPE_AP) }, +}; + +static const struct ieee80211_iface_combination ap_if_comb = { + .limits = ap_if_limits, + .n_limits = ARRAY_SIZE(ap_if_limits), + .max_interfaces = 8, + .num_different_channels = 1, +}; + + static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) { struct ieee80211_hw *hw = priv->hw; @@ -5696,8 +5726,13 @@ static int mwl8k_firmware_load_success(struct mwl8k_priv *priv) goto err_free_cookie; hw->wiphy->interface_modes = 0; - if (priv->ap_macids_supported || priv->device_info->fw_image_ap) + + if (priv->ap_macids_supported || priv->device_info->fw_image_ap) { hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_AP); + hw->wiphy->iface_combinations = &ap_if_comb; + hw->wiphy->n_iface_combinations = 1; + } + if (priv->sta_macids_supported || priv->device_info->fw_image_sta) hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_STATION); diff --git a/drivers/net/wireless/orinoco/cfg.c b/drivers/net/wireless/orinoco/cfg.c index 7b751fba7e1..d01edd2c50c 100644 --- a/drivers/net/wireless/orinoco/cfg.c +++ b/drivers/net/wireless/orinoco/cfg.c @@ -161,24 +161,23 @@ static int orinoco_scan(struct wiphy *wiphy, } static int orinoco_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct orinoco_private *priv = wiphy_priv(wiphy); int err = 0; unsigned long flags; int channel; - if (!chan) + if (!chandef->chan) return -EINVAL; - if (channel_type != NL80211_CHAN_NO_HT) + if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT) return -EINVAL; - if (chan->band != IEEE80211_BAND_2GHZ) + if (chandef->chan->band != IEEE80211_BAND_2GHZ) return -EINVAL; - channel = ieee80211_freq_to_dsss_chan(chan->center_freq); + channel = ieee80211_freq_to_dsss_chan(chandef->chan->center_freq); if ((channel < 1) || (channel > NUM_CHANNELS) || !(priv->channel_mask & (1 << (channel - 1)))) diff --git a/drivers/net/wireless/p54/txrx.c b/drivers/net/wireless/p54/txrx.c index 5861e13a6fd..12f0a34477f 100644 --- a/drivers/net/wireless/p54/txrx.c +++ b/drivers/net/wireless/p54/txrx.c @@ -369,7 +369,11 @@ 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_MACTIME_MPDU; + /* LMAC API Page 10/29 - s_lm_data_in - clock + * "usec accurate timestamp of hardware clock + * at end of frame (before OFDM SIFS EOF padding" + */ + rx_status->flag |= RX_FLAG_MACTIME_END; if (hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_ALIGN)) header_len += hdr->align[0]; diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c index bd1f0cb5608..abe1d039be8 100644 --- a/drivers/net/wireless/rndis_wlan.c +++ b/drivers/net/wireless/rndis_wlan.c @@ -490,9 +490,12 @@ static int rndis_scan(struct wiphy *wiphy, static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed); static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm); -static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm); +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm); static int rndis_connect(struct wiphy *wiphy, struct net_device *dev, struct cfg80211_connect_params *sme); @@ -1903,6 +1906,7 @@ static int rndis_set_wiphy_params(struct wiphy *wiphy, u32 changed) } static int rndis_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { @@ -1930,7 +1934,9 @@ static int rndis_set_tx_power(struct wiphy *wiphy, return -ENOTSUPP; } -static int rndis_get_tx_power(struct wiphy *wiphy, int *dbm) +static int rndis_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; @@ -2287,7 +2293,7 @@ static int rndis_join_ibss(struct wiphy *wiphy, struct net_device *dev, { struct rndis_wlan_private *priv = wiphy_priv(wiphy); struct usbnet *usbdev = priv->usbdev; - struct ieee80211_channel *channel = params->channel; + struct ieee80211_channel *channel = params->chandef.chan; struct ndis_80211_ssid ssid; enum nl80211_auth_type auth_type; int ret, alg, length, chan = -1; diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 3b8fb5a603f..023081286e0 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1096,6 +1096,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x177f, 0x0153) }, { USB_DEVICE(0x177f, 0x0302) }, { USB_DEVICE(0x177f, 0x0313) }, + { USB_DEVICE(0x177f, 0x0323) }, /* U-Media */ { USB_DEVICE(0x157e, 0x300e) }, { USB_DEVICE(0x157e, 0x3013) }, diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 69097d1faeb..67d167993d4 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -157,6 +157,7 @@ static void rt2x00lib_intf_scheduled(struct work_struct *work) * requested configurations. */ ieee80211_iterate_active_interfaces(rt2x00dev->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, rt2x00lib_intf_scheduled_iter, rt2x00dev); } @@ -225,9 +226,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) return; /* send buffered bc/mc frames out for every bssid */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_bc_buffer_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_bc_buffer_iter, rt2x00dev); /* * Devices with pre tbtt interrupt don't need to update the beacon * here as they will fetch the next beacon directly prior to @@ -237,9 +238,9 @@ void rt2x00lib_beacondone(struct rt2x00_dev *rt2x00dev) return; /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_beacondone); @@ -249,9 +250,9 @@ void rt2x00lib_pretbtt(struct rt2x00_dev *rt2x00dev) return; /* fetch next beacon */ - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00lib_beaconupdate_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00lib_beaconupdate_iter, rt2x00dev); } EXPORT_SYMBOL_GPL(rt2x00lib_pretbtt); diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 98a9e48f8e4..ed7a1bb3f24 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -424,9 +424,9 @@ int rt2x00mac_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, if (!test_bit(DEVICE_STATE_ENABLED_RADIO, &rt2x00dev->flags)) return 0; - ieee80211_iterate_active_interfaces_atomic(rt2x00dev->hw, - rt2x00mac_set_tim_iter, - rt2x00dev); + ieee80211_iterate_active_interfaces_atomic( + rt2x00dev->hw, IEEE80211_IFACE_ITER_RESUME_ALL, + rt2x00mac_set_tim_iter, rt2x00dev); /* queue work to upodate the beacon template */ ieee80211_queue_work(rt2x00dev->hw, &rt2x00dev->intf_work); diff --git a/drivers/net/wireless/rtl818x/rtl8180/dev.c b/drivers/net/wireless/rtl818x/rtl8180/dev.c index 021d83e1b1d..b4218a5f206 100644 --- a/drivers/net/wireless/rtl818x/rtl8180/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8180/dev.c @@ -150,7 +150,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_MACTIME_MPDU; + rx_status.flag |= RX_FLAG_MACTIME_START; if (flags & RTL818X_RX_DESC_FLAG_CRC32_ERR) rx_status.flag |= RX_FLAG_FAILED_FCS_CRC; diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c index 7811b631597..52e6bebcf08 100644 --- a/drivers/net/wireless/rtl818x/rtl8187/dev.c +++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c @@ -381,7 +381,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_MACTIME_MPDU; + rx_status.flag |= RX_FLAG_MACTIME_START; 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 6b28e92d1d2..21b1bbb93a7 100644 --- a/drivers/net/wireless/rtlwifi/Kconfig +++ b/drivers/net/wireless/rtlwifi/Kconfig @@ -32,6 +32,17 @@ config RTL8192DE If you choose to build it as a module, it will be called rtl8192de +config RTL8723AE + tristate "Realtek RTL8723AE PCIe Wireless Network Adapter" + depends on MAC80211 && PCI && EXPERIMENTAL + select FW_LOADER + select RTLWIFI + ---help--- + This is the driver for Realtek RTL8723AE 802.11n PCIe + wireless network adapters. + + If you choose to build it as a module, it will be called rtl8723ae + config RTL8192CU tristate "Realtek RTL8192CU/RTL8188CU USB Wireless Network Adapter" depends on MAC80211 && USB diff --git a/drivers/net/wireless/rtlwifi/Makefile b/drivers/net/wireless/rtlwifi/Makefile index 97935c565ba..3b1cbac741e 100644 --- a/drivers/net/wireless/rtlwifi/Makefile +++ b/drivers/net/wireless/rtlwifi/Makefile @@ -7,7 +7,8 @@ rtlwifi-objs := \ efuse.o \ ps.o \ rc.o \ - regd.o + regd.o \ + stats.o rtl8192c_common-objs += \ @@ -24,5 +25,6 @@ obj-$(CONFIG_RTL8192CE) += rtl8192ce/ obj-$(CONFIG_RTL8192CU) += rtl8192cu/ obj-$(CONFIG_RTL8192SE) += rtl8192se/ obj-$(CONFIG_RTL8192DE) += rtl8192de/ +obj-$(CONFIG_RTL8723AE) += rtl8723ae/ ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/base.c b/drivers/net/wireless/rtlwifi/base.c index 59381fe8ed0..4494d130b37 100644 --- a/drivers/net/wireless/rtlwifi/base.c +++ b/drivers/net/wireless/rtlwifi/base.c @@ -826,6 +826,30 @@ int rtlwifi_rate_mapping(struct ieee80211_hw *hw, } EXPORT_SYMBOL(rtlwifi_rate_mapping); +bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + __le16 fc = rtl_get_fc(skb); + + if (rtlpriv->dm.supp_phymode_switch && + mac->link_state < MAC80211_LINKED && + (ieee80211_is_auth(fc) || ieee80211_is_probe_req(fc))) { + if (rtlpriv->cfg->ops->check_switch_to_dmdp) + rtlpriv->cfg->ops->check_switch_to_dmdp(hw); + } + if (ieee80211_is_auth(fc)) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n"); + rtl_ips_nic_on(hw); + + mac->link_state = MAC80211_LINKING; + /* Dual mac */ + rtlpriv->phy.need_iqk = true; + } + + return true; +} + void rtl_get_tcb_desc(struct ieee80211_hw *hw, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/rtlwifi/base.h b/drivers/net/wireless/rtlwifi/base.h index f35af0fdaaf..5a8c80e259f 100644 --- a/drivers/net/wireless/rtlwifi/base.h +++ b/drivers/net/wireless/rtlwifi/base.h @@ -142,4 +142,6 @@ u8 rtl_tid_to_ac(u8 tid); extern struct attribute_group rtl_attribute_group; int rtlwifi_rate_mapping(struct ieee80211_hw *hw, bool isht, u8 desc_rate, bool first_ampdu); +bool rtl_tx_mgmt_proc(struct ieee80211_hw *hw, struct sk_buff *skb); + #endif diff --git a/drivers/net/wireless/rtlwifi/cam.c b/drivers/net/wireless/rtlwifi/cam.c index ca69e35e50f..0e510f73041 100644 --- a/drivers/net/wireless/rtlwifi/cam.c +++ b/drivers/net/wireless/rtlwifi/cam.c @@ -337,7 +337,7 @@ void rtl_cam_del_entry(struct ieee80211_hw *hw, u8 *sta_addr) if (((bitmap & BIT(0)) == BIT(0)) && (memcmp(addr, sta_addr, ETH_ALEN) == 0)) { /* Remove from HW Security CAM */ - memset(rtlpriv->sec.hwsec_cam_sta_addr[i], 0, ETH_ALEN); + eth_zero_addr(rtlpriv->sec.hwsec_cam_sta_addr[i]); rtlpriv->sec.hwsec_cam_bitmap &= ~(BIT(0) << i); RT_TRACE(rtlpriv, COMP_SEC, DBG_LOUD, "del CAM entry %d\n", i); diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index a7c0e52869b..be33aa14c8a 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -962,7 +962,6 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, int err = 0; u8 mac_addr[ETH_ALEN]; u8 bcast_addr[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - u8 zero_addr[ETH_ALEN] = { 0 }; if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, @@ -1057,7 +1056,7 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, memcpy(rtlpriv->sec.key_buf[key_idx], key->key, key->keylen); rtlpriv->sec.key_len[key_idx] = key->keylen; - memcpy(mac_addr, zero_addr, ETH_ALEN); + eth_zero_addr(mac_addr); } else if (group_key) { /* group key */ RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "set group key\n"); @@ -1108,7 +1107,7 @@ static int rtl_op_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, } memset(rtlpriv->sec.key_buf[key_idx], 0, key->keylen); rtlpriv->sec.key_len[key_idx] = 0; - memcpy(mac_addr, zero_addr, ETH_ALEN); + eth_zero_addr(mac_addr); /* *mac80211 will delete entrys one by one, *so don't use rtl_cam_reset_all_entry diff --git a/drivers/net/wireless/rtlwifi/debug.h b/drivers/net/wireless/rtlwifi/debug.h index 07493d2957f..fd3269f4768 100644 --- a/drivers/net/wireless/rtlwifi/debug.h +++ b/drivers/net/wireless/rtlwifi/debug.h @@ -106,6 +106,8 @@ #define COMP_REGD BIT(27) #define COMP_CHAN BIT(28) #define COMP_USB BIT(29) +#define COMP_EASY_CONCURRENT COMP_USB /* reuse of this bit is OK */ +#define COMP_BT_COEXIST BIT(30) /*-------------------------------------------------------------- Define the rt_print components diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index abc306b502a..f38e30a947b 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -1309,6 +1309,7 @@ static bool rtl_pci_tx_chk_waitq_insert(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv = rtl_priv(hw); struct rtl_sta_info *sta_entry = NULL; u8 tid = rtl_get_tid(skb); + __le16 fc = rtl_get_fc(skb); if (!sta) return false; @@ -1316,6 +1317,12 @@ static bool rtl_pci_tx_chk_waitq_insert(struct ieee80211_hw *hw, if (!rtlpriv->rtlhal.earlymode_enable) return false; + if (ieee80211_is_nullfunc(fc)) + return false; + if (ieee80211_is_qos_nullfunc(fc)) + return false; + if (ieee80211_is_pspoll(fc)) + return false; if (sta_entry->tids[tid].agg.agg_state != RTL_AGG_OPERATIONAL) return false; if (_rtl_mac_to_hwqueue(hw, skb) > VO_QUEUE) @@ -1357,10 +1364,8 @@ static int rtl_pci_tx(struct ieee80211_hw *hw, u8 own; u8 temp_one = 1; - if (ieee80211_is_auth(fc)) { - RT_TRACE(rtlpriv, COMP_SEND, DBG_DMESG, "MAC80211_LINKING\n"); - rtl_ips_nic_on(hw); - } + if (ieee80211_is_mgmt(fc)) + rtl_tx_mgmt_proc(hw, skb); if (rtlpriv->psc.sw_ps_enabled) { if (ieee80211_is_data(fc) && !ieee80211_is_nullfunc(fc) && @@ -1628,7 +1633,7 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev, "8192 PCI-E is found - vid/did=%x/%x\n", venderid, deviceid); rtlhal->hw_type = HARDWARE_TYPE_RTL8192E; - break; + return false; case RTL_PCI_REVISION_ID_8192SE: RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "8192SE is found - vid/did=%x/%x\n", @@ -1643,6 +1648,11 @@ static bool _rtl_pci_find_adapter(struct pci_dev *pdev, break; } + } else if (deviceid == RTL_PCI_8723AE_DID) { + rtlhal->hw_type = HARDWARE_TYPE_RTL8723AE; + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "8723AE PCI-E is found - " + "vid/did=%x/%x\n", venderid, deviceid); } else if (deviceid == RTL_PCI_8192CET_DID || deviceid == RTL_PCI_8192CE_DID || deviceid == RTL_PCI_8191CE_DID || diff --git a/drivers/net/wireless/rtlwifi/pci.h b/drivers/net/wireless/rtlwifi/pci.h index 241448fc9ed..f71b12aa8cb 100644 --- a/drivers/net/wireless/rtlwifi/pci.h +++ b/drivers/net/wireless/rtlwifi/pci.h @@ -79,6 +79,7 @@ #define RTL_PCI_8173_DID 0x8173 /*8191 SE Crab */ #define RTL_PCI_8172_DID 0x8172 /*8191 SE RE */ #define RTL_PCI_8171_DID 0x8171 /*8191 SE Unicron */ +#define RTL_PCI_8723AE_DID 0x8723 /*8723AE */ #define RTL_PCI_0045_DID 0x0045 /*8190 PCI for Ceraga */ #define RTL_PCI_0046_DID 0x0046 /*8190 Cardbus for Ceraga */ #define RTL_PCI_0044_DID 0x0044 /*8192e PCIE for Ceraga */ @@ -152,6 +153,7 @@ struct rtl8192_rx_ring { struct rtl_pci { struct pci_dev *pdev; + bool irq_enabled; bool driver_is_goingto_unload; bool up_first_time; diff --git a/drivers/net/wireless/rtlwifi/rc.c b/drivers/net/wireless/rtlwifi/rc.c index d5cbf01da8a..c1e065f136b 100644 --- a/drivers/net/wireless/rtlwifi/rc.c +++ b/drivers/net/wireless/rtlwifi/rc.c @@ -55,7 +55,8 @@ static u8 _rtl_rc_get_highest_rix(struct rtl_priv *rtlpriv, * 1M we will not use FW rate but user rate. */ if (rtlmac->opmode == NL80211_IFTYPE_AP || - rtlmac->opmode == NL80211_IFTYPE_ADHOC) { + rtlmac->opmode == NL80211_IFTYPE_ADHOC || + rtlmac->opmode == NL80211_IFTYPE_MESH_POINT) { if (sta) { sta_entry = (struct rtl_sta_info *) sta->drv_priv; wireless_mode = sta_entry->wireless_mode; diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index d7e1f0a7e48..17342475614 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -559,7 +559,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_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; diff --git a/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c b/drivers/net/wireless/rtlwifi/rtl8192cu/trx.c index 6e66f04c363..b6222eedb83 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_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index 35bb9da6196..f9f3861046c 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -509,7 +509,7 @@ bool rtl92de_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, rx_status->flag |= RX_FLAG_40MHZ; if (GET_RX_DESC_RXHT(pdesc)) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; if (stats->decrypted) rx_status->flag |= RX_FLAG_DECRYPTED; rx_status->rate_idx = rtlwifi_rate_mapping(hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 1ad51e711a3..0e9f6ebf078 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -553,7 +553,7 @@ bool rtl92se_rx_query_desc(struct ieee80211_hw *hw, struct rtl_stats *stats, if (stats->is_ht) rx_status->flag |= RX_FLAG_HT; - rx_status->flag |= RX_FLAG_MACTIME_MPDU; + rx_status->flag |= RX_FLAG_MACTIME_START; /* hw will set stats->decrypted true, if it finds the * frame is open data frame or mgmt frame, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile new file mode 100644 index 00000000000..4ed731f09b1 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/Makefile @@ -0,0 +1,22 @@ +obj-m := rtl8723ae.o + + +rtl8723ae-objs := \ + dm.o \ + fw.o \ + hal_btc.o \ + hal_bt_coexist.o\ + hw.o \ + led.o \ + phy.o \ + pwrseq.o \ + pwrseqcmd.o \ + rf.o \ + sw.o \ + table.o \ + trx.o \ + + +obj-$(CONFIG_RTL8723AE) += rtl8723ae.o + +ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/btc.h b/drivers/net/wireless/rtlwifi/rtl8723ae/btc.h new file mode 100644 index 00000000000..417afeed36a --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/btc.h @@ -0,0 +1,41 @@ +/****************************************************************************** + ** + ** Copyright(c) 2009-2012 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 __RTL8723E_BTC_H__ +#define __RTL8723E_BTC_H__ + +#include "../wifi.h" +#include "hal_bt_coexist.h" + +struct bt_coexist_c2h_info { + u8 no_parse_c2h; + u8 has_c2h; +}; + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/def.h b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h new file mode 100644 index 00000000000..8c110356dff --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/def.h @@ -0,0 +1,163 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_DEF_H__ +#define __RTL8723E_DEF_H__ + +#define HAL_PRIME_CHNL_OFFSET_LOWER 1 + +#define RX_MPDU_QUEUE 0 + +#define CHIP_8723 BIT(0) +#define NORMAL_CHIP BIT(3) +#define RF_TYPE_1T2R BIT(4) +#define RF_TYPE_2T2R BIT(5) +#define CHIP_VENDOR_UMC BIT(7) +#define B_CUT_VERSION BIT(12) +#define C_CUT_VERSION BIT(13) +#define D_CUT_VERSION ((BIT(12)|BIT(13))) +#define E_CUT_VERSION BIT(14) +#define RF_RL_ID (BIT(31)|BIT(30)|BIT(29)|BIT(28)) + +enum version_8723e { + VERSION_TEST_UMC_CHIP_8723 = 0x0081, + VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT = 0x0089, + VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT = 0x1089, +}; + +/* MASK */ +#define IC_TYPE_MASK (BIT(0)|BIT(1)|BIT(2)) +#define CHIP_TYPE_MASK BIT(3) +#define RF_TYPE_MASK (BIT(4)|BIT(5)|BIT(6)) +#define MANUFACTUER_MASK BIT(7) +#define ROM_VERSION_MASK (BIT(11)|BIT(10)|BIT(9)|BIT(8)) +#define CUT_VERSION_MASK (BIT(15)|BIT(14)|BIT(13)|BIT(12)) + +/* Get element */ +#define GET_CVID_IC_TYPE(version) ((version) & IC_TYPE_MASK) +#define GET_CVID_MANUFACTUER(version) ((version) & MANUFACTUER_MASK) +#define GET_CVID_CUT_VERSION(version) ((version) & CUT_VERSION_MASK) + +#define IS_81XXC(version) ((GET_CVID_IC_TYPE(version) == 0) ?\ + true : false) +#define IS_8723_SERIES(version) \ + ((GET_CVID_IC_TYPE(version) == CHIP_8723) ? true : false) +#define IS_CHIP_VENDOR_UMC(version) \ + ((GET_CVID_MANUFACTUER(version)) ? true : false) + +#define IS_VENDOR_UMC_A_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) ? \ + ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) +#define IS_VENDOR_8723_A_CUT(version) ((IS_8723_SERIES(version)) ? \ + ((GET_CVID_CUT_VERSION(version)) ? false : true) : false) +#define IS_81xxC_VENDOR_UMC_B_CUT(version) ((IS_CHIP_VENDOR_UMC(version)) \ + ? ((GET_CVID_CUT_VERSION(version) == B_CUT_VERSION) ? \ + true : false) : false) + +enum rf_optype { + RF_OP_BY_SW_3WIRE = 0, + RF_OP_BY_FW, + RF_OP_MAX +}; + +enum rf_power_state { + RF_ON, + RF_OFF, + RF_SLEEP, + RF_SHUT_DOWN, +}; + +enum power_save_mode { + POWER_SAVE_MODE_ACTIVE, + POWER_SAVE_MODE_SAVE, +}; + +enum power_polocy_config { + POWERCFG_MAX_POWER_SAVINGS, + POWERCFG_GLOBAL_POWER_SAVINGS, + POWERCFG_LOCAL_POWER_SAVINGS, + POWERCFG_LENOVO, +}; + +enum interface_select_pci { + INTF_SEL1_MINICARD = 0, + INTF_SEL0_PCIE = 1, + INTF_SEL2_RSV = 2, + INTF_SEL3_RSV = 3, +}; + +enum hal_fw_c2h_cmd_id { + HAL_FW_C2H_CMD_Read_MACREG = 0, + HAL_FW_C2H_CMD_Read_BBREG = 1, + HAL_FW_C2H_CMD_Read_RFREG = 2, + HAL_FW_C2H_CMD_Read_EEPROM = 3, + HAL_FW_C2H_CMD_Read_EFUSE = 4, + HAL_FW_C2H_CMD_Read_CAM = 5, + HAL_FW_C2H_CMD_Get_BasicRate = 6, + HAL_FW_C2H_CMD_Get_DataRate = 7, + HAL_FW_C2H_CMD_Survey = 8, + HAL_FW_C2H_CMD_SurveyDone = 9, + HAL_FW_C2H_CMD_JoinBss = 10, + HAL_FW_C2H_CMD_AddSTA = 11, + HAL_FW_C2H_CMD_DelSTA = 12, + HAL_FW_C2H_CMD_AtimDone = 13, + HAL_FW_C2H_CMD_TX_Report = 14, + HAL_FW_C2H_CMD_CCX_Report = 15, + HAL_FW_C2H_CMD_DTM_Report = 16, + HAL_FW_C2H_CMD_TX_Rate_Statistics = 17, + HAL_FW_C2H_CMD_C2HLBK = 18, + HAL_FW_C2H_CMD_C2HDBG = 19, + HAL_FW_C2H_CMD_C2HFEEDBACK = 20, + HAL_FW_C2H_CMD_MAX +}; + +enum rtl_desc_qsel { + QSLT_BK = 0x2, + QSLT_BE = 0x0, + QSLT_VI = 0x5, + QSLT_VO = 0x7, + QSLT_BEACON = 0x10, + QSLT_HIGH = 0x11, + QSLT_MGNT = 0x12, + QSLT_CMD = 0x13, +}; + +struct phy_sts_cck_8723e_t { + u8 adc_pwdb_X[4]; + u8 sq_rpt; + u8 cck_agc_rpt; +}; + +struct h2c_cmd_8723e { + u8 element_id; + u32 cmd_len; + u8 *p_cmdbuffer; +}; + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c new file mode 100644 index 00000000000..12e2a3cb070 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.c @@ -0,0 +1,920 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + **************************************************************************** + */ + +#include "../wifi.h" +#include "../base.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "dm.h" +#include "fw.h" +#include "hal_btc.h" + +static const u32 ofdmswing_table[OFDM_TABLE_SIZE] = { + 0x7f8001fe, + 0x788001e2, + 0x71c001c7, + 0x6b8001ae, + 0x65400195, + 0x5fc0017f, + 0x5a400169, + 0x55400155, + 0x50800142, + 0x4c000130, + 0x47c0011f, + 0x43c0010f, + 0x40000100, + 0x3c8000f2, + 0x390000e4, + 0x35c000d7, + 0x32c000cb, + 0x300000c0, + 0x2d4000b5, + 0x2ac000ab, + 0x288000a2, + 0x26000098, + 0x24000090, + 0x22000088, + 0x20000080, + 0x1e400079, + 0x1c800072, + 0x1b00006c, + 0x19800066, + 0x18000060, + 0x16c0005b, + 0x15800056, + 0x14400051, + 0x1300004c, + 0x12000048, + 0x11000044, + 0x10000040, +}; + +static const u8 cckswing_table_ch1ch13[CCK_TABLE_SIZE][8] = { + {0x36, 0x35, 0x2e, 0x25, 0x1c, 0x12, 0x09, 0x04}, + {0x33, 0x32, 0x2b, 0x23, 0x1a, 0x11, 0x08, 0x04}, + {0x30, 0x2f, 0x29, 0x21, 0x19, 0x10, 0x08, 0x03}, + {0x2d, 0x2d, 0x27, 0x1f, 0x18, 0x0f, 0x08, 0x03}, + {0x2b, 0x2a, 0x25, 0x1e, 0x16, 0x0e, 0x07, 0x03}, + {0x28, 0x28, 0x22, 0x1c, 0x15, 0x0d, 0x07, 0x03}, + {0x26, 0x25, 0x21, 0x1b, 0x14, 0x0d, 0x06, 0x03}, + {0x24, 0x23, 0x1f, 0x19, 0x13, 0x0c, 0x06, 0x03}, + {0x22, 0x21, 0x1d, 0x18, 0x11, 0x0b, 0x06, 0x02}, + {0x20, 0x20, 0x1b, 0x16, 0x11, 0x08, 0x05, 0x02}, + {0x1f, 0x1e, 0x1a, 0x15, 0x10, 0x0a, 0x05, 0x02}, + {0x1d, 0x1c, 0x18, 0x14, 0x0f, 0x0a, 0x05, 0x02}, + {0x1b, 0x1a, 0x17, 0x13, 0x0e, 0x09, 0x04, 0x02}, + {0x1a, 0x19, 0x16, 0x12, 0x0d, 0x09, 0x04, 0x02}, + {0x18, 0x17, 0x15, 0x11, 0x0c, 0x08, 0x04, 0x02}, + {0x17, 0x16, 0x13, 0x10, 0x0c, 0x08, 0x04, 0x02}, + {0x16, 0x15, 0x12, 0x0f, 0x0b, 0x07, 0x04, 0x01}, + {0x14, 0x14, 0x11, 0x0e, 0x0b, 0x07, 0x03, 0x02}, + {0x13, 0x13, 0x10, 0x0d, 0x0a, 0x06, 0x03, 0x01}, + {0x12, 0x12, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, + {0x11, 0x11, 0x0f, 0x0c, 0x09, 0x06, 0x03, 0x01}, + {0x10, 0x10, 0x0e, 0x0b, 0x08, 0x05, 0x03, 0x01}, + {0x0f, 0x0f, 0x0d, 0x0b, 0x08, 0x05, 0x03, 0x01}, + {0x0e, 0x0e, 0x0c, 0x0a, 0x08, 0x05, 0x02, 0x01}, + {0x0d, 0x0d, 0x0c, 0x0a, 0x07, 0x05, 0x02, 0x01}, + {0x0d, 0x0c, 0x0b, 0x09, 0x07, 0x04, 0x02, 0x01}, + {0x0c, 0x0c, 0x0a, 0x09, 0x06, 0x04, 0x02, 0x01}, + {0x0b, 0x0b, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x01}, + {0x0b, 0x0a, 0x09, 0x08, 0x06, 0x04, 0x02, 0x01}, + {0x0a, 0x0a, 0x09, 0x07, 0x05, 0x03, 0x02, 0x01}, + {0x0a, 0x09, 0x08, 0x07, 0x05, 0x03, 0x02, 0x01}, + {0x09, 0x09, 0x08, 0x06, 0x05, 0x03, 0x01, 0x01}, + {0x09, 0x08, 0x07, 0x06, 0x04, 0x03, 0x01, 0x01} +}; + +static const u8 cckswing_table_ch14[CCK_TABLE_SIZE][8] = { + {0x36, 0x35, 0x2e, 0x1b, 0x00, 0x00, 0x00, 0x00}, + {0x33, 0x32, 0x2b, 0x19, 0x00, 0x00, 0x00, 0x00}, + {0x30, 0x2f, 0x29, 0x18, 0x00, 0x00, 0x00, 0x00}, + {0x2d, 0x2d, 0x17, 0x17, 0x00, 0x00, 0x00, 0x00}, + {0x2b, 0x2a, 0x25, 0x15, 0x00, 0x00, 0x00, 0x00}, + {0x28, 0x28, 0x24, 0x14, 0x00, 0x00, 0x00, 0x00}, + {0x26, 0x25, 0x21, 0x13, 0x00, 0x00, 0x00, 0x00}, + {0x24, 0x23, 0x1f, 0x12, 0x00, 0x00, 0x00, 0x00}, + {0x22, 0x21, 0x1d, 0x11, 0x00, 0x00, 0x00, 0x00}, + {0x20, 0x20, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00}, + {0x1f, 0x1e, 0x1a, 0x0f, 0x00, 0x00, 0x00, 0x00}, + {0x1d, 0x1c, 0x18, 0x0e, 0x00, 0x00, 0x00, 0x00}, + {0x1b, 0x1a, 0x17, 0x0e, 0x00, 0x00, 0x00, 0x00}, + {0x1a, 0x19, 0x16, 0x0d, 0x00, 0x00, 0x00, 0x00}, + {0x18, 0x17, 0x15, 0x0c, 0x00, 0x00, 0x00, 0x00}, + {0x17, 0x16, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x00}, + {0x16, 0x15, 0x12, 0x0b, 0x00, 0x00, 0x00, 0x00}, + {0x14, 0x14, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, + {0x13, 0x13, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00}, + {0x12, 0x12, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, + {0x11, 0x11, 0x0f, 0x09, 0x00, 0x00, 0x00, 0x00}, + {0x10, 0x10, 0x0e, 0x08, 0x00, 0x00, 0x00, 0x00}, + {0x0f, 0x0f, 0x0d, 0x08, 0x00, 0x00, 0x00, 0x00}, + {0x0e, 0x0e, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, + {0x0d, 0x0d, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00}, + {0x0d, 0x0c, 0x0b, 0x06, 0x00, 0x00, 0x00, 0x00}, + {0x0c, 0x0c, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, + {0x0b, 0x0b, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00}, + {0x0b, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, + {0x0a, 0x0a, 0x09, 0x05, 0x00, 0x00, 0x00, 0x00}, + {0x0a, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, + {0x09, 0x09, 0x08, 0x05, 0x00, 0x00, 0x00, 0x00}, + {0x09, 0x08, 0x07, 0x04, 0x00, 0x00, 0x00, 0x00} +}; + +static void rtl8723ae_dm_diginit(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + dm_digtable->dig_enable_flag = true; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; + dm_digtable->cur_igvalue = 0x20; + dm_digtable->pre_igvalue = 0x0; + dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; + dm_digtable->presta_cstate = DIG_STA_DISCONNECT; + dm_digtable->curmultista_cstate = DIG_MULTISTA_DISCONNECT; + dm_digtable->rssi_lowthresh = DM_DIG_THRESH_LOW; + dm_digtable->rssi_highthresh = DM_DIG_THRESH_HIGH; + dm_digtable->fa_lowthresh = DM_FALSEALARM_THRESH_LOW; + dm_digtable->fa_highthresh = DM_FALSEALARM_THRESH_HIGH; + dm_digtable->rx_gain_range_max = DM_DIG_MAX; + dm_digtable->rx_gain_range_min = DM_DIG_MIN; + dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + dm_digtable->back_range_max = DM_DIG_BACKOFF_MAX; + dm_digtable->back_range_min = DM_DIG_BACKOFF_MIN; + dm_digtable->pre_cck_pd_state = CCK_PD_STAGE_MAX; + dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; +} + +static u8 rtl_init_gain_min_pwdb(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + long rssi_val_min = 0; + + if ((dm_digtable->curmultista_cstate == DIG_MULTISTA_CONNECT) && + (dm_digtable->cursta_cstate == DIG_STA_CONNECT)) { + if (rtlpriv->dm.entry_min_undec_sm_pwdb != 0) + rssi_val_min = + (rtlpriv->dm.entry_min_undec_sm_pwdb > + rtlpriv->dm.undec_sm_pwdb) ? + rtlpriv->dm.undec_sm_pwdb : + rtlpriv->dm.entry_min_undec_sm_pwdb; + else + rssi_val_min = rtlpriv->dm.undec_sm_pwdb; + } else if (dm_digtable->cursta_cstate == DIG_STA_CONNECT || + dm_digtable->cursta_cstate == DIG_STA_BEFORE_CONNECT) { + rssi_val_min = rtlpriv->dm.undec_sm_pwdb; + } else if (dm_digtable->curmultista_cstate == DIG_MULTISTA_CONNECT) { + rssi_val_min = rtlpriv->dm.entry_min_undec_sm_pwdb; + } + + return (u8) rssi_val_min; +} + +static void rtl8723ae_dm_false_alarm_counter_statistics(struct ieee80211_hw *hw) +{ + u32 ret_value; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct false_alarm_statistics *falsealm_cnt = &(rtlpriv->falsealm_cnt); + + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER1, MASKDWORD); + falsealm_cnt->cnt_parity_fail = ((ret_value & 0xffff0000) >> 16); + + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER2, MASKDWORD); + falsealm_cnt->cnt_rate_illegal = (ret_value & 0xffff); + falsealm_cnt->cnt_crc8_fail = ((ret_value & 0xffff0000) >> 16); + + ret_value = rtl_get_bbreg(hw, ROFDM_PHYCOUNTER3, MASKDWORD); + falsealm_cnt->cnt_mcs_fail = (ret_value & 0xffff); + falsealm_cnt->cnt_ofdm_fail = falsealm_cnt->cnt_parity_fail + + falsealm_cnt->cnt_rate_illegal + + falsealm_cnt->cnt_crc8_fail + falsealm_cnt->cnt_mcs_fail; + + rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, BIT(14), 1); + ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERLOWER, MASKBYTE0); + falsealm_cnt->cnt_cck_fail = ret_value; + + ret_value = rtl_get_bbreg(hw, RCCK0_FACOUNTERUPPER, MASKBYTE3); + falsealm_cnt->cnt_cck_fail += (ret_value & 0xff) << 8; + falsealm_cnt->cnt_all = (falsealm_cnt->cnt_parity_fail + + falsealm_cnt->cnt_rate_illegal + + falsealm_cnt->cnt_crc8_fail + + falsealm_cnt->cnt_mcs_fail + + falsealm_cnt->cnt_cck_fail); + + rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 1); + rtl_set_bbreg(hw, ROFDM1_LSTF, 0x08000000, 0); + rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 0); + rtl_set_bbreg(hw, RCCK0_FALSEALARMREPORT, 0x0000c000, 2); + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "cnt_parity_fail = %d, cnt_rate_illegal = %d, " + "cnt_crc8_fail = %d, cnt_mcs_fail = %d\n", + falsealm_cnt->cnt_parity_fail, + falsealm_cnt->cnt_rate_illegal, + falsealm_cnt->cnt_crc8_fail, falsealm_cnt->cnt_mcs_fail); + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "cnt_ofdm_fail = %x, cnt_cck_fail = %x, cnt_all = %x\n", + falsealm_cnt->cnt_ofdm_fail, + falsealm_cnt->cnt_cck_fail, falsealm_cnt->cnt_all); +} + +static void rtl92c_dm_ctrl_initgain_by_fa(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + u8 value_igi = dm_digtable->cur_igvalue; + + if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH0) + value_igi--; + else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH1) + value_igi += 0; + else if (rtlpriv->falsealm_cnt.cnt_all < DM_DIG_FA_TH2) + value_igi++; + else + value_igi += 2; + + value_igi = clamp(value_igi, (u8)DM_DIG_FA_LOWER, (u8)DM_DIG_FA_UPPER); + if (rtlpriv->falsealm_cnt.cnt_all > 10000) + value_igi = 0x32; + + dm_digtable->cur_igvalue = value_igi; + rtl8723ae_dm_write_dig(hw); +} + +static void rtl92c_dm_ctrl_initgain_by_rssi(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dgtbl = &rtlpriv->dm_digtable; + + if (rtlpriv->falsealm_cnt.cnt_all > dgtbl->fa_highthresh) { + if ((dgtbl->back_val - 2) < dgtbl->back_range_min) + dgtbl->back_val = dgtbl->back_range_min; + else + dgtbl->back_val -= 2; + } else if (rtlpriv->falsealm_cnt.cnt_all < dgtbl->fa_lowthresh) { + if ((dgtbl->back_val + 2) > dgtbl->back_range_max) + dgtbl->back_val = dgtbl->back_range_max; + else + dgtbl->back_val += 2; + } + + if ((dgtbl->rssi_val_min + 10 - dgtbl->back_val) > + dgtbl->rx_gain_range_max) + dgtbl->cur_igvalue = dgtbl->rx_gain_range_max; + else if ((dgtbl->rssi_val_min + 10 - + dgtbl->back_val) < dgtbl->rx_gain_range_min) + dgtbl->cur_igvalue = dgtbl->rx_gain_range_min; + else + dgtbl->cur_igvalue = dgtbl->rssi_val_min + 10 - dgtbl->back_val; + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "rssi_val_min = %x back_val %x\n", + dgtbl->rssi_val_min, dgtbl->back_val); + + rtl8723ae_dm_write_dig(hw); +} + +static void rtl8723ae_dm_initial_gain_multi_sta(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + long rssi_strength = rtlpriv->dm.entry_min_undec_sm_pwdb; + bool multi_sta = false; + + if (mac->opmode == NL80211_IFTYPE_ADHOC) + multi_sta = true; + + if ((!multi_sta) || + (dm_digtable->cursta_cstate != DIG_STA_DISCONNECT)) { + rtlpriv->initialized = false; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; + return; + } else if (!rtlpriv->initialized) { + rtlpriv->initialized = true; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_0; + dm_digtable->cur_igvalue = 0x20; + rtl8723ae_dm_write_dig(hw); + } + + if (dm_digtable->curmultista_cstate == DIG_MULTISTA_CONNECT) { + if ((rssi_strength < dm_digtable->rssi_lowthresh) && + (dm_digtable->dig_ext_port_stage != DIG_EXT_PORT_STAGE_1)) { + + if (dm_digtable->dig_ext_port_stage == + DIG_EXT_PORT_STAGE_2) { + dm_digtable->cur_igvalue = 0x20; + rtl8723ae_dm_write_dig(hw); + } + + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_1; + } else if (rssi_strength > dm_digtable->rssi_highthresh) { + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_2; + rtl92c_dm_ctrl_initgain_by_fa(hw); + } + } else if (dm_digtable->dig_ext_port_stage != DIG_EXT_PORT_STAGE_0) { + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_0; + dm_digtable->cur_igvalue = 0x20; + rtl8723ae_dm_write_dig(hw); + } + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "curmultista_cstate = %x dig_ext_port_stage %x\n", + dm_digtable->curmultista_cstate, + dm_digtable->dig_ext_port_stage); +} + +static void rtl8723ae_dm_initial_gain_sta(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "presta_cstate = %x, cursta_cstate = %x\n", + dm_digtable->presta_cstate, + dm_digtable->cursta_cstate); + + if (dm_digtable->presta_cstate == dm_digtable->cursta_cstate || + dm_digtable->cursta_cstate == DIG_STA_BEFORE_CONNECT || + dm_digtable->cursta_cstate == DIG_STA_CONNECT) { + + if (dm_digtable->cursta_cstate != DIG_STA_DISCONNECT) { + dm_digtable->rssi_val_min = rtl_init_gain_min_pwdb(hw); + rtl92c_dm_ctrl_initgain_by_rssi(hw); + } + } else { + dm_digtable->rssi_val_min = 0; + dm_digtable->dig_ext_port_stage = DIG_EXT_PORT_STAGE_MAX; + dm_digtable->back_val = DM_DIG_BACKOFF_DEFAULT; + dm_digtable->cur_igvalue = 0x20; + dm_digtable->pre_igvalue = 0; + rtl8723ae_dm_write_dig(hw); + } +} +static void rtl8723ae_dm_cck_packet_detection_thresh(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + if (dm_digtable->cursta_cstate == DIG_STA_CONNECT) { + dm_digtable->rssi_val_min = rtl_init_gain_min_pwdb(hw); + + if (dm_digtable->pre_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (dm_digtable->rssi_val_min <= 25) + dm_digtable->cur_cck_pd_state = + CCK_PD_STAGE_LowRssi; + else + dm_digtable->cur_cck_pd_state = + CCK_PD_STAGE_HighRssi; + } else { + if (dm_digtable->rssi_val_min <= 20) + dm_digtable->cur_cck_pd_state = + CCK_PD_STAGE_LowRssi; + else + dm_digtable->cur_cck_pd_state = + CCK_PD_STAGE_HighRssi; + } + } else { + dm_digtable->cur_cck_pd_state = CCK_PD_STAGE_MAX; + } + + if (dm_digtable->pre_cck_pd_state != dm_digtable->cur_cck_pd_state) { + if (dm_digtable->cur_cck_pd_state == CCK_PD_STAGE_LowRssi) { + if (rtlpriv->falsealm_cnt.cnt_cck_fail > 800) + dm_digtable->cur_cck_fa_state = + CCK_FA_STAGE_High; + else + dm_digtable->cur_cck_fa_state = + CCK_FA_STAGE_Low; + + if (dm_digtable->pre_cck_fa_state != + dm_digtable->cur_cck_fa_state) { + if (dm_digtable->cur_cck_fa_state == + CCK_FA_STAGE_Low) + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, + 0x83); + else + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, + 0xcd); + + dm_digtable->pre_cck_fa_state = + dm_digtable->cur_cck_fa_state; + } + + rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x40); + + } else { + rtl_set_bbreg(hw, RCCK0_CCA, MASKBYTE2, 0xcd); + rtl_set_bbreg(hw, RCCK0_SYSTEM, MASKBYTE1, 0x47); + + } + dm_digtable->pre_cck_pd_state = dm_digtable->cur_cck_pd_state; + } + + RT_TRACE(rtlpriv, COMP_DIG, DBG_TRACE, + "CCKPDStage=%x\n", dm_digtable->cur_cck_pd_state); + +} + +static void rtl8723ae_dm_ctrl_initgain_by_twoport(struct ieee80211_hw *hw) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + if (mac->act_scanning == true) + return; + + if (mac->link_state >= MAC80211_LINKED) + dm_digtable->cursta_cstate = DIG_STA_CONNECT; + else + dm_digtable->cursta_cstate = DIG_STA_DISCONNECT; + + rtl8723ae_dm_initial_gain_sta(hw); + rtl8723ae_dm_initial_gain_multi_sta(hw); + rtl8723ae_dm_cck_packet_detection_thresh(hw); + + dm_digtable->presta_cstate = dm_digtable->cursta_cstate; + +} + +static void rtl8723ae_dm_dig(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + if (rtlpriv->dm.dm_initialgain_enable == false) + return; + if (dm_digtable->dig_enable_flag == false) + return; + + rtl8723ae_dm_ctrl_initgain_by_twoport(hw); +} + +static void rtl8723ae_dm_init_dynamic_txpower(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.dynamic_txpower_enable = false; + + rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; +} + +static void rtl8723ae_dm_dynamic_txpower(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + long undec_sm_pwdb; + + if (!rtlpriv->dm.dynamic_txpower_enable) + return; + + if (rtlpriv->dm.dm_flag & HAL_DM_HIPWR_DISABLE) { + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; + return; + } + + if ((mac->link_state < MAC80211_LINKED) && + (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) { + RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, + "Not connected\n"); + + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; + + rtlpriv->dm.last_dtp_lvl = TXHIGHPWRLEVEL_NORMAL; + return; + } + + if (mac->link_state >= MAC80211_LINKED) { + if (mac->opmode == NL80211_IFTYPE_ADHOC) { + undec_sm_pwdb = rtlpriv->dm.entry_min_undec_sm_pwdb; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "AP Client PWDB = 0x%lx\n", + undec_sm_pwdb); + } else { + undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "STA Default Port PWDB = 0x%lx\n", + undec_sm_pwdb); + } + } else { + undec_sm_pwdb = rtlpriv->dm.entry_min_undec_sm_pwdb; + + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "AP Ext Port PWDB = 0x%lx\n", + undec_sm_pwdb); + } + + if (undec_sm_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL2) { + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x0)\n"); + } else if ((undec_sm_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL2 - 3)) && + (undec_sm_pwdb >= TX_POWER_NEAR_FIELD_THRESH_LVL1)) { + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_LEVEL1; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "TXHIGHPWRLEVEL_LEVEL1 (TxPwr=0x10)\n"); + } else if (undec_sm_pwdb < (TX_POWER_NEAR_FIELD_THRESH_LVL1 - 5)) { + rtlpriv->dm.dynamic_txhighpower_lvl = TXHIGHPWRLEVEL_NORMAL; + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "TXHIGHPWRLEVEL_NORMAL\n"); + } + + if ((rtlpriv->dm.dynamic_txhighpower_lvl != rtlpriv->dm.last_dtp_lvl)) { + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "PHY_SetTxPowerLevel8192S() Channel = %d\n", + rtlphy->current_channel); + rtl8723ae_phy_set_txpower_level(hw, rtlphy->current_channel); + } + + rtlpriv->dm.last_dtp_lvl = rtlpriv->dm.dynamic_txhighpower_lvl; +} + +void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + RT_TRACE(rtlpriv, COMP_DIG, DBG_LOUD, + "cur_igvalue = 0x%x, " + "pre_igvalue = 0x%x, back_val = %d\n", + dm_digtable->cur_igvalue, dm_digtable->pre_igvalue, + dm_digtable->back_val); + + if (dm_digtable->pre_igvalue != dm_digtable->cur_igvalue) { + rtl_set_bbreg(hw, ROFDM0_XAAGCCORE1, 0x7f, + dm_digtable->cur_igvalue); + rtl_set_bbreg(hw, ROFDM0_XBAGCCORE1, 0x7f, + dm_digtable->cur_igvalue); + + dm_digtable->pre_igvalue = dm_digtable->cur_igvalue; + } +} + +static void rtl8723ae_dm_pwdmonitor(struct ieee80211_hw *hw) +{ +} + +void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.current_turbo_edca = false; + rtlpriv->dm.is_any_nonbepkts = false; + rtlpriv->dm.is_cur_rdlstate = false; +} + +static void rtl8723ae_dm_check_edca_turbo(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + + u64 cur_txok_cnt = 0; + u64 cur_rxok_cnt = 0; + u32 edca_be_ul = 0x5ea42b; + u32 edca_be_dl = 0x5ea42b; + bool bt_change_edca = false; + + if ((mac->last_bt_edca_ul != rtlpcipriv->bt_coexist.bt_edca_ul) || + (mac->last_bt_edca_dl != rtlpcipriv->bt_coexist.bt_edca_dl)) { + rtlpriv->dm.current_turbo_edca = false; + mac->last_bt_edca_ul = rtlpcipriv->bt_coexist.bt_edca_ul; + mac->last_bt_edca_dl = rtlpcipriv->bt_coexist.bt_edca_dl; + } + + if (rtlpcipriv->bt_coexist.bt_edca_ul != 0) { + edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_ul; + bt_change_edca = true; + } + + if (rtlpcipriv->bt_coexist.bt_edca_dl != 0) { + edca_be_ul = rtlpcipriv->bt_coexist.bt_edca_dl; + bt_change_edca = true; + } + + if (mac->link_state != MAC80211_LINKED) { + rtlpriv->dm.current_turbo_edca = false; + return; + } + + if ((!mac->ht_enable) && (!rtlpcipriv->bt_coexist.bt_coexistence)) { + if (!(edca_be_ul & 0xffff0000)) + edca_be_ul |= 0x005e0000; + + if (!(edca_be_dl & 0xffff0000)) + edca_be_dl |= 0x005e0000; + } + + if ((bt_change_edca) || ((!rtlpriv->dm.is_any_nonbepkts) && + (!rtlpriv->dm.disable_framebursting))) { + + cur_txok_cnt = rtlpriv->stats.txbytesunicast - + mac->last_txok_cnt; + cur_rxok_cnt = rtlpriv->stats.rxbytesunicast - + mac->last_rxok_cnt; + + if (cur_rxok_cnt > 4 * cur_txok_cnt) { + if (!rtlpriv->dm.is_cur_rdlstate || + !rtlpriv->dm.current_turbo_edca) { + rtl_write_dword(rtlpriv, + REG_EDCA_BE_PARAM, + edca_be_dl); + rtlpriv->dm.is_cur_rdlstate = true; + } + } else { + if (rtlpriv->dm.is_cur_rdlstate || + !rtlpriv->dm.current_turbo_edca) { + rtl_write_dword(rtlpriv, + REG_EDCA_BE_PARAM, + edca_be_ul); + rtlpriv->dm.is_cur_rdlstate = false; + } + } + rtlpriv->dm.current_turbo_edca = true; + } else { + if (rtlpriv->dm.current_turbo_edca) { + u8 tmp = AC0_BE; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_AC_PARAM, + (u8 *) (&tmp)); + rtlpriv->dm.current_turbo_edca = false; + } + } + + rtlpriv->dm.is_any_nonbepkts = false; + mac->last_txok_cnt = rtlpriv->stats.txbytesunicast; + mac->last_rxok_cnt = rtlpriv->stats.rxbytesunicast; +} + +static void rtl8723ae_dm_initialize_txpower_tracking(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.txpower_tracking = true; + rtlpriv->dm.txpower_trackinginit = false; + + RT_TRACE(rtlpriv, COMP_POWER_TRACKING, DBG_LOUD, + "pMgntInfo->txpower_tracking = %d\n", + rtlpriv->dm.txpower_tracking); +} + +void rtl8723ae_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rate_adaptive *p_ra = &(rtlpriv->ra); + + p_ra->ratr_state = DM_RATR_STA_INIT; + p_ra->pre_ratr_state = DM_RATR_STA_INIT; + + if (rtlpriv->dm.dm_type == DM_TYPE_BYDRIVER) + rtlpriv->dm.useramask = true; + else + rtlpriv->dm.useramask = false; +} + +static void rtl8723ae_dm_init_dynamic_bpowersaving(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm_pstable.pre_ccastate = CCA_MAX; + rtlpriv->dm_pstable.cur_ccasate = CCA_MAX; + rtlpriv->dm_pstable.pre_rfstate = RF_MAX; + rtlpriv->dm_pstable.cur_rfstate = RF_MAX; + rtlpriv->dm_pstable.rssi_val_min = 0; +} + +void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 force_in_normal) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct ps_t *dm_pstable = &rtlpriv->dm_pstable; + + if (!rtlpriv->reg_init) { + rtlpriv->reg_874 = (rtl_get_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + MASKDWORD) & 0x1CC000) >> 14; + + rtlpriv->reg_c70 = (rtl_get_bbreg(hw, ROFDM0_AGCPARAMETER1, + MASKDWORD) & BIT(3)) >> 3; + + rtlpriv->reg_85c = (rtl_get_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, + MASKDWORD) & 0xFF000000) >> 24; + + rtlpriv->reg_a74 = (rtl_get_bbreg(hw, 0xa74, MASKDWORD) & + 0xF000) >> 12; + + rtlpriv->reg_init = true; + } + + if (!force_in_normal) { + if (dm_pstable->rssi_val_min != 0) { + if (dm_pstable->pre_rfstate == RF_NORMAL) { + if (dm_pstable->rssi_val_min >= 30) + dm_pstable->cur_rfstate = RF_SAVE; + else + dm_pstable->cur_rfstate = RF_NORMAL; + } else { + if (dm_pstable->rssi_val_min <= 25) + dm_pstable->cur_rfstate = RF_NORMAL; + else + dm_pstable->cur_rfstate = RF_SAVE; + } + } else { + dm_pstable->cur_rfstate = RF_MAX; + } + } else { + dm_pstable->cur_rfstate = RF_NORMAL; + } + + if (dm_pstable->pre_rfstate != dm_pstable->cur_rfstate) { + if (dm_pstable->cur_rfstate == RF_SAVE) { + + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + BIT(5), 0x1); + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + 0x1C0000, 0x2); + rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, BIT(3), 0); + rtl_set_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, + 0xFF000000, 0x63); + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + 0xC000, 0x2); + rtl_set_bbreg(hw, 0xa74, 0xF000, 0x3); + rtl_set_bbreg(hw, 0x818, BIT(28), 0x0); + rtl_set_bbreg(hw, 0x818, BIT(28), 0x1); + } else { + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + 0x1CC000, rtlpriv->reg_874); + rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, BIT(3), + rtlpriv->reg_c70); + rtl_set_bbreg(hw, RFPGA0_XCD_SWITCHCONTROL, 0xFF000000, + rtlpriv->reg_85c); + rtl_set_bbreg(hw, 0xa74, 0xF000, rtlpriv->reg_a74); + rtl_set_bbreg(hw, 0x818, BIT(28), 0x0); + rtl_set_bbreg(hw, RFPGA0_XCD_RFINTERFACESW, + BIT(5), 0x0); + } + + dm_pstable->pre_rfstate = dm_pstable->cur_rfstate; + } +} + +static void rtl8723ae_dm_dynamic_bpowersaving(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct ps_t *dm_pstable = &rtlpriv->dm_pstable; + + if (((mac->link_state == MAC80211_NOLINK)) && + (rtlpriv->dm.entry_min_undec_sm_pwdb == 0)) { + dm_pstable->rssi_val_min = 0; + RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, + "Not connected to any\n"); + } + + if (mac->link_state == MAC80211_LINKED) { + if (mac->opmode == NL80211_IFTYPE_ADHOC) { + dm_pstable->rssi_val_min = + rtlpriv->dm.entry_min_undec_sm_pwdb; + RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, + "AP Client PWDB = 0x%lx\n", + dm_pstable->rssi_val_min); + } else { + dm_pstable->rssi_val_min = rtlpriv->dm.undec_sm_pwdb; + RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, + "STA Default Port PWDB = 0x%lx\n", + dm_pstable->rssi_val_min); + } + } else { + dm_pstable->rssi_val_min = rtlpriv->dm.entry_min_undec_sm_pwdb; + + RT_TRACE(rtlpriv, DBG_LOUD, DBG_LOUD, + "AP Ext Port PWDB = 0x%lx\n", + dm_pstable->rssi_val_min); + } + + rtl8723ae_dm_rf_saving(hw, false); +} + +void rtl8723ae_dm_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpriv->dm.dm_type = DM_TYPE_BYDRIVER; + rtl8723ae_dm_diginit(hw); + rtl8723ae_dm_init_dynamic_txpower(hw); + rtl8723ae_dm_init_edca_turbo(hw); + rtl8723ae_dm_init_rate_adaptive_mask(hw); + rtl8723ae_dm_initialize_txpower_tracking(hw); + rtl8723ae_dm_init_dynamic_bpowersaving(hw); +} + +void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + bool fw_current_inpsmode = false; + bool fw_ps_awake = true; + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *) (&fw_current_inpsmode)); + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON, + (u8 *) (&fw_ps_awake)); + + if ((ppsc->rfpwr_state == ERFON) && + ((!fw_current_inpsmode) && fw_ps_awake) && + (!ppsc->rfchange_inprogress)) { + rtl8723ae_dm_pwdmonitor(hw); + rtl8723ae_dm_dig(hw); + rtl8723ae_dm_false_alarm_counter_statistics(hw); + rtl8723ae_dm_dynamic_bpowersaving(hw); + rtl8723ae_dm_dynamic_txpower(hw); + /* rtl92c_dm_refresh_rate_adaptive_mask(hw); */ + rtl8723ae_dm_bt_coexist(hw); + rtl8723ae_dm_check_edca_turbo(hw); + } + if (rtlpcipriv->bt_coexist.init_set) + rtl_write_byte(rtlpriv, 0x76e, 0xc); +} + +static void rtl8723ae_dm_init_bt_coexist(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + rtlpcipriv->bt_coexist.bt_rfreg_origin_1e + = rtl_get_rfreg(hw, (enum radio_path)0, RF_RCK1, 0xfffff); + rtlpcipriv->bt_coexist.bt_rfreg_origin_1f + = rtl_get_rfreg(hw, (enum radio_path)0, RF_RCK2, 0xf0); + + rtlpcipriv->bt_coexist.cstate = 0; + rtlpcipriv->bt_coexist.previous_state = 0; + rtlpcipriv->bt_coexist.cstate_h = 0; + rtlpcipriv->bt_coexist.previous_state_h = 0; + rtlpcipriv->bt_coexist.lps_counter = 0; + + /* Enable counter statistics */ + rtl_write_byte(rtlpriv, 0x76e, 0x4); + rtl_write_byte(rtlpriv, 0x778, 0x3); + rtl_write_byte(rtlpriv, 0x40, 0x20); + + rtlpcipriv->bt_coexist.init_set = true; +} + +void rtl8723ae_dm_bt_coexist(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 tmp_byte = 0; + if (!rtlpcipriv->bt_coexist.bt_coexistence) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[DM]{BT], BT not exist!!\n"); + return; + } + + if (!rtlpcipriv->bt_coexist.init_set) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[DM][BT], rtl8723ae_dm_bt_coexist()\n"); + + rtl8723ae_dm_init_bt_coexist(hw); + } + + tmp_byte = rtl_read_byte(rtlpriv, 0x40); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_LOUD, + "[DM][BT], 0x40 is 0x%x", tmp_byte); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[DM][BT], bt_dm_coexist start"); + rtl8723ae_dm_bt_coexist_8723(hw); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h new file mode 100644 index 00000000000..39d24619624 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/dm.h @@ -0,0 +1,149 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_DM_H__ +#define __RTL8723E_DM_H__ + +#define HAL_DM_HIPWR_DISABLE BIT(1) + +#define OFDM_TABLE_SIZE 37 +#define CCK_TABLE_SIZE 33 + +#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 DM_RATR_STA_INIT 0 + +#define TXHIGHPWRLEVEL_NORMAL 0 +#define TXHIGHPWRLEVEL_LEVEL1 1 +#define TXHIGHPWRLEVEL_LEVEL2 2 +#define TXHIGHPWRLEVEL_BT1 3 +#define TXHIGHPWRLEVEL_BT2 4 + +#define DM_TYPE_BYDRIVER 1 + +#define TX_POWER_NEAR_FIELD_THRESH_LVL2 74 +#define TX_POWER_NEAR_FIELD_THRESH_LVL1 67 + +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 +}; + +#define GET_UNDECORATED_AVERAGE_RSSI(_priv) \ + ((((struct rtl_priv *)(_priv))->mac80211.opmode == \ + NL80211_IFTYPE_ADHOC) ? \ + (((struct rtl_priv *)(_priv))->dm.entry_min_undec_sm_pwdb) \ + : (((struct rtl_priv *)(_priv))->dm.undec_sm_pwdb)) + +void rtl8723ae_dm_init(struct ieee80211_hw *hw); +void rtl8723ae_dm_watchdog(struct ieee80211_hw *hw); +void rtl8723ae_dm_write_dig(struct ieee80211_hw *hw); +void rtl8723ae_dm_init_edca_turbo(struct ieee80211_hw *hw); +void rtl8723ae_dm_init_rate_adaptive_mask(struct ieee80211_hw *hw); +void rtl8723ae_dm_rf_saving(struct ieee80211_hw *hw, u8 bforce_in_normal); +void rtl8723ae_dm_bt_coexist(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c new file mode 100644 index 00000000000..f55b1767ef5 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.c @@ -0,0 +1,745 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + **************************************************************************** + */ + +#include "../wifi.h" +#include "../pci.h" +#include "../base.h" +#include "reg.h" +#include "def.h" +#include "fw.h" + +static void _rtl8723ae_enable_fw_download(struct ieee80211_hw *hw, bool enable) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp; + if (enable) { + tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, tmp | 0x04); + + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); + rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp | 0x01); + + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL + 2); + rtl_write_byte(rtlpriv, REG_MCUFWDL + 2, tmp & 0xf7); + } else { + tmp = rtl_read_byte(rtlpriv, REG_MCUFWDL); + rtl_write_byte(rtlpriv, REG_MCUFWDL, tmp & 0xfe); + + rtl_write_byte(rtlpriv, REG_MCUFWDL + 1, 0x00); + } +} + +static void _rtl8723ae_fw_block_write(struct ieee80211_hw *hw, + const u8 *buffer, u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 blockSize = sizeof(u32); + u8 *bufferPtr = (u8 *) buffer; + u32 *pu4BytePtr = (u32 *) buffer; + u32 i, offset, blockCount, remainSize; + + blockCount = size / blockSize; + remainSize = size % blockSize; + + for (i = 0; i < blockCount; i++) { + offset = i * blockSize; + rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset), + *(pu4BytePtr + i)); + } + + if (remainSize) { + offset = blockCount * blockSize; + bufferPtr += offset; + for (i = 0; i < remainSize; i++) { + rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS + + offset + i), *(bufferPtr + i)); + } + } +} + +static void _rtl8723ae_fw_page_write(struct ieee80211_hw *hw, + u32 page, const u8 *buffer, u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 value8; + u8 u8page = (u8) (page & 0x07); + + value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page; + + rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8); + _rtl8723ae_fw_block_write(hw, buffer, size); +} + +static void _rtl8723ae_write_fw(struct ieee80211_hw *hw, + enum version_8723e version, u8 *buffer, + u32 size) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 *bufferPtr = (u8 *) buffer; + u32 page_nums, remain_size; + u32 page, offset; + + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size); + + page_nums = size / FW_8192C_PAGE_SIZE; + remain_size = size % FW_8192C_PAGE_SIZE; + + if (page_nums > 6) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Page numbers should not be greater then 6\n"); + } + + for (page = 0; page < page_nums; page++) { + offset = page * FW_8192C_PAGE_SIZE; + _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset), + FW_8192C_PAGE_SIZE); + } + + if (remain_size) { + offset = page_nums * FW_8192C_PAGE_SIZE; + page = page_nums; + _rtl8723ae_fw_page_write(hw, page, (bufferPtr + offset), + remain_size); + } + + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW write done.\n"); +} + +static int _rtl8723ae_fw_free_to_go(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int err = -EIO; + u32 counter = 0; + u32 value32; + + do { + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); + } while ((counter++ < FW_8192C_POLLING_TIMEOUT_COUNT) && + (!(value32 & FWDL_ChkSum_rpt))); + + if (counter >= FW_8192C_POLLING_TIMEOUT_COUNT) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "chksum report faill ! REG_MCUFWDL:0x%08x .\n", + value32); + goto exit; + } + + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "Checksum report OK ! REG_MCUFWDL:0x%08x .\n", value32); + + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); + value32 |= MCUFWDL_RDY; + value32 &= ~WINTINI_RDY; + rtl_write_dword(rtlpriv, REG_MCUFWDL, value32); + + counter = 0; + + do { + value32 = rtl_read_dword(rtlpriv, REG_MCUFWDL); + if (value32 & WINTINI_RDY) { + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "Polling FW ready success!! REG_MCUFWDL:0x%08x .\n", + value32); + err = 0; + goto exit; + } + + mdelay(FW_8192C_POLLING_DELAY); + + } while (counter++ < FW_8192C_POLLING_TIMEOUT_COUNT); + + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Polling FW ready fail!! REG_MCUFWDL:0x%08x .\n", value32); + +exit: + return err; +} + +int rtl8723ae_download_fw(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl8723ae_firmware_header *pfwheader; + u8 *pfwdata; + u32 fwsize; + int err; + enum version_8723e version = rtlhal->version; + + if (!rtlhal->pfirmware) + return 1; + + pfwheader = (struct rtl8723ae_firmware_header *)rtlhal->pfirmware; + pfwdata = (u8 *) rtlhal->pfirmware; + fwsize = rtlhal->fwsize; + + if (IS_FW_HEADER_EXIST(pfwheader)) { + RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, + "Firmware Version(%d), Signature(%#x),Size(%d)\n", + pfwheader->version, pfwheader->signature, + (int)sizeof(struct rtl8723ae_firmware_header)); + + pfwdata = pfwdata + sizeof(struct rtl8723ae_firmware_header); + fwsize = fwsize - sizeof(struct rtl8723ae_firmware_header); + } + + if (rtl_read_byte(rtlpriv, REG_MCUFWDL)&BIT(7)) { + rtl8723ae_firmware_selfreset(hw); + rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); + } + _rtl8723ae_enable_fw_download(hw, true); + _rtl8723ae_write_fw(hw, version, pfwdata, fwsize); + _rtl8723ae_enable_fw_download(hw, false); + + err = _rtl8723ae_fw_free_to_go(hw); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Firmware is not ready to run!\n"); + } else { + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "Firmware is ready to run!\n"); + } + return 0; +} + +static bool rtl8723ae_check_fw_read_last_h2c(struct ieee80211_hw *hw, u8 boxnum) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 val_hmetfr, val_mcutst_1; + bool result = false; + + val_hmetfr = rtl_read_byte(rtlpriv, REG_HMETFR); + val_mcutst_1 = rtl_read_byte(rtlpriv, (REG_MCUTST_1 + boxnum)); + + if (((val_hmetfr >> boxnum) & BIT(0)) == 0 && val_mcutst_1 == 0) + result = true; + return result; +} + +static void _rtl8723ae_fill_h2c_command(struct ieee80211_hw *hw, + u8 element_id, u32 cmd_len, + u8 *p_cmdbuffer) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 boxnum; + u16 box_reg = 0, box_extreg = 0; + u8 u1tmp; + bool isfw_rd = false; + bool bwrite_sucess = false; + u8 wait_h2c_limmit = 100; + u8 wait_writeh2c_limmit = 100; + u8 boxcontent[4], boxextcontent[2]; + u32 h2c_waitcounter = 0; + unsigned long flag; + u8 idx; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "come in\n"); + + while (true) { + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); + if (rtlhal->h2c_setinprogress) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "H2C set in progress! Wait to set..element_id(%d).\n", + element_id); + + while (rtlhal->h2c_setinprogress) { + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, + flag); + h2c_waitcounter++; + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wait 100 us (%d times)...\n", + h2c_waitcounter); + udelay(100); + + if (h2c_waitcounter > 1000) + return; + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, + flag); + } + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + } else { + rtlhal->h2c_setinprogress = true; + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + break; + } + } + + while (!bwrite_sucess) { + wait_writeh2c_limmit--; + if (wait_writeh2c_limmit == 0) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Write H2C fail because no trigger " + "for FW INT!\n"); + break; + } + + boxnum = rtlhal->last_hmeboxnum; + switch (boxnum) { + case 0: + box_reg = REG_HMEBOX_0; + box_extreg = REG_HMEBOX_EXT_0; + break; + case 1: + box_reg = REG_HMEBOX_1; + box_extreg = REG_HMEBOX_EXT_1; + break; + case 2: + box_reg = REG_HMEBOX_2; + box_extreg = REG_HMEBOX_EXT_2; + break; + case 3: + box_reg = REG_HMEBOX_3; + box_extreg = REG_HMEBOX_EXT_3; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + + isfw_rd = rtl8723ae_check_fw_read_last_h2c(hw, boxnum); + while (!isfw_rd) { + + wait_h2c_limmit--; + if (wait_h2c_limmit == 0) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wating too long for FW read clear HMEBox(%d)!\n", + boxnum); + break; + } + + udelay(10); + + isfw_rd = rtl8723ae_check_fw_read_last_h2c(hw, boxnum); + u1tmp = rtl_read_byte(rtlpriv, 0x1BF); + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Wating for FW read clear HMEBox(%d)!!! " + "0x1BF = %2x\n", boxnum, u1tmp); + } + + if (!isfw_rd) { + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Write H2C register BOX[%d] fail!!!!! " + "Fw do not read.\n", boxnum); + break; + } + + memset(boxcontent, 0, sizeof(boxcontent)); + memset(boxextcontent, 0, sizeof(boxextcontent)); + boxcontent[0] = element_id; + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "Write element_id box_reg(%4x) = %2x\n", + box_reg, element_id); + + switch (cmd_len) { + case 1: + boxcontent[0] &= ~(BIT(7)); + memcpy((u8 *) (boxcontent) + 1, + p_cmdbuffer, 1); + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + case 2: + boxcontent[0] &= ~(BIT(7)); + memcpy((u8 *) (boxcontent) + 1, + p_cmdbuffer, 2); + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + case 3: + boxcontent[0] &= ~(BIT(7)); + memcpy((u8 *) (boxcontent) + 1, + p_cmdbuffer, 3); + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + case 4: + boxcontent[0] |= (BIT(7)); + memcpy((u8 *) (boxextcontent), + p_cmdbuffer, 2); + memcpy((u8 *) (boxcontent) + 1, + p_cmdbuffer + 2, 2); + + for (idx = 0; idx < 2; idx++) { + rtl_write_byte(rtlpriv, box_extreg + idx, + boxextcontent[idx]); + } + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + case 5: + boxcontent[0] |= (BIT(7)); + memcpy((u8 *) (boxextcontent), + p_cmdbuffer, 2); + memcpy((u8 *) (boxcontent) + 1, + p_cmdbuffer + 2, 3); + + for (idx = 0; idx < 2; idx++) { + rtl_write_byte(rtlpriv, box_extreg + idx, + boxextcontent[idx]); + } + + for (idx = 0; idx < 4; idx++) { + rtl_write_byte(rtlpriv, box_reg + idx, + boxcontent[idx]); + } + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + + bwrite_sucess = true; + + rtlhal->last_hmeboxnum = boxnum + 1; + if (rtlhal->last_hmeboxnum == 4) + rtlhal->last_hmeboxnum = 0; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, + "pHalData->last_hmeboxnum = %d\n", + rtlhal->last_hmeboxnum); + } + + spin_lock_irqsave(&rtlpriv->locks.h2c_lock, flag); + rtlhal->h2c_setinprogress = false; + spin_unlock_irqrestore(&rtlpriv->locks.h2c_lock, flag); + + RT_TRACE(rtlpriv, COMP_CMD, DBG_LOUD, "go out\n"); +} + +void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw, + u8 element_id, u32 cmd_len, u8 *p_cmdbuffer) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + + if (rtlhal->fw_ready == false) { + RT_ASSERT(false, + "return H2C cmd because of Fw download fail!!!\n"); + return; + } + + _rtl8723ae_fill_h2c_command(hw, element_id, cmd_len, p_cmdbuffer); + return; +} + +void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw) +{ + u8 u1tmp; + u8 delay = 100; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, REG_HMETFR + 3, 0x20); + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + + while (u1tmp & BIT(2)) { + delay--; + if (delay == 0) + break; + udelay(50); + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + } + if (delay == 0) { + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN + 1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN + 1, u1tmp&(~BIT(2))); + } +} + +void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 u1_h2c_set_pwrmode[3] = { 0 }; + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, "FW LPS mode = %d\n", mode); + + SET_H2CCMD_PWRMODE_PARM_MODE(u1_h2c_set_pwrmode, mode); + SET_H2CCMD_PWRMODE_PARM_SMART_PS(u1_h2c_set_pwrmode, 1); + SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(u1_h2c_set_pwrmode, + ppsc->reg_max_lps_awakeintvl); + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "rtl8723ae_set_fw_rsvdpagepkt(): u1_h2c_set_pwrmode\n", + u1_h2c_set_pwrmode, 3); + rtl8723ae_fill_h2c_cmd(hw, H2C_SETPWRMODE, 3, u1_h2c_set_pwrmode); + +} + +static bool _rtl8723ae_cmd_send_packet(struct ieee80211_hw *hw, + struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl8192_tx_ring *ring; + struct rtl_tx_desc *pdesc; + u8 own; + unsigned long flags; + struct sk_buff *pskb = NULL; + + ring = &rtlpci->tx_ring[BEACON_QUEUE]; + + pskb = __skb_dequeue(&ring->queue); + if (pskb) + kfree_skb(pskb); + + spin_lock_irqsave(&rtlpriv->locks.irq_th_lock, flags); + + pdesc = &ring->desc[0]; + own = (u8) rtlpriv->cfg->ops->get_desc((u8 *) pdesc, true, HW_DESC_OWN); + + rtlpriv->cfg->ops->fill_tx_cmddesc(hw, (u8 *) pdesc, 1, 1, skb); + + __skb_queue_tail(&ring->queue, skb); + + spin_unlock_irqrestore(&rtlpriv->locks.irq_th_lock, flags); + + rtlpriv->cfg->ops->tx_polling(hw, BEACON_QUEUE); + + return true; +} + +static u8 reserved_page_packet[TOTAL_RESERVED_PKT_LEN] = { + /* page 0 beacon */ + 0x80, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, + 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x50, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, + 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, + 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, + 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, + 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, + 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, + 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 1 beacon */ + 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, + 0x10, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 2 ps-poll */ + 0xA4, 0x10, 0x01, 0xC0, 0x00, 0x40, 0x10, 0x10, + 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, + 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, + 0x18, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 3 null */ + 0x48, 0x01, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, + 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, + 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 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, + 0x72, 0x00, 0x20, 0x8C, 0x00, 0x12, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 4 probe_resp */ + 0x50, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x10, + 0x00, 0x03, 0x00, 0xE0, 0x4C, 0x76, 0x00, 0x42, + 0x00, 0x40, 0x10, 0x10, 0x00, 0x03, 0x00, 0x00, + 0x9E, 0x46, 0x15, 0x32, 0x27, 0xF2, 0x2D, 0x00, + 0x64, 0x00, 0x00, 0x04, 0x00, 0x0C, 0x6C, 0x69, + 0x6E, 0x6B, 0x73, 0x79, 0x73, 0x5F, 0x77, 0x6C, + 0x61, 0x6E, 0x01, 0x04, 0x82, 0x84, 0x8B, 0x96, + 0x03, 0x01, 0x01, 0x06, 0x02, 0x00, 0x00, 0x2A, + 0x01, 0x00, 0x32, 0x08, 0x24, 0x30, 0x48, 0x6C, + 0x0C, 0x12, 0x18, 0x60, 0x2D, 0x1A, 0x6C, 0x18, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3D, 0x00, 0xDD, 0x06, 0x00, 0xE0, 0x4C, 0x02, + 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* page 5 probe_resp */ + 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, +}; + +void rtl8723ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool dl_finished) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct sk_buff *skb = NULL; + + u32 totalpacketlen; + bool rtstatus; + u8 u1RsvdPageLoc[3] = { 0 }; + bool dlok = false; + + u8 *beacon; + u8 *p_pspoll; + u8 *nullfunc; + u8 *p_probersp; + /*--------------------------------------------------------- + (1) beacon + --------------------------------------------------------- + */ + beacon = &reserved_page_packet[BEACON_PG * 128]; + SET_80211_HDR_ADDRESS2(beacon, mac->mac_addr); + SET_80211_HDR_ADDRESS3(beacon, mac->bssid); + + /*------------------------------------------------------- + (2) ps-poll + -------------------------------------------------------- + */ + p_pspoll = &reserved_page_packet[PSPOLL_PG * 128]; + SET_80211_PS_POLL_AID(p_pspoll, (mac->assoc_id | 0xc000)); + SET_80211_PS_POLL_BSSID(p_pspoll, mac->bssid); + SET_80211_PS_POLL_TA(p_pspoll, mac->mac_addr); + + SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(u1RsvdPageLoc, PSPOLL_PG); + + /*-------------------------------------------------------- + (3) null data + ---------------------------------------------------------i + */ + nullfunc = &reserved_page_packet[NULL_PG * 128]; + SET_80211_HDR_ADDRESS1(nullfunc, mac->bssid); + SET_80211_HDR_ADDRESS2(nullfunc, mac->mac_addr); + SET_80211_HDR_ADDRESS3(nullfunc, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(u1RsvdPageLoc, NULL_PG); + + /*--------------------------------------------------------- + (4) probe response + ---------------------------------------------------------- + */ + p_probersp = &reserved_page_packet[PROBERSP_PG * 128]; + SET_80211_HDR_ADDRESS1(p_probersp, mac->bssid); + SET_80211_HDR_ADDRESS2(p_probersp, mac->mac_addr); + SET_80211_HDR_ADDRESS3(p_probersp, mac->bssid); + + SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(u1RsvdPageLoc, PROBERSP_PG); + + totalpacketlen = TOTAL_RESERVED_PKT_LEN; + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, + "rtl8723ae_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n", + &reserved_page_packet[0], totalpacketlen); + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "rtl8723ae_set_fw_rsvdpagepkt(): HW_VAR_SET_TX_CMD: ALL\n", + u1RsvdPageLoc, 3); + + skb = dev_alloc_skb(totalpacketlen); + memcpy((u8 *) skb_put(skb, totalpacketlen), + &reserved_page_packet, totalpacketlen); + + rtstatus = _rtl8723ae_cmd_send_packet(hw, skb); + + if (rtstatus) + dlok = true; + + if (dlok) { + RT_TRACE(rtlpriv, COMP_POWER, DBG_LOUD, + "Set RSVD page location to Fw.\n"); + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_DMESG, + "H2C_RSVDPAGE:\n", + u1RsvdPageLoc, 3); + rtl8723ae_fill_h2c_cmd(hw, H2C_RSVDPAGE, + sizeof(u1RsvdPageLoc), u1RsvdPageLoc); + } else + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set RSVD page location to Fw FAIL!!!!!!.\n"); +} + +void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus) +{ + u8 u1_joinbssrpt_parm[1] = { 0 }; + + SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(u1_joinbssrpt_parm, mstatus); + + rtl8723ae_fill_h2c_cmd(hw, H2C_JOINBSSRPT, 1, u1_joinbssrpt_parm); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h new file mode 100644 index 00000000000..89994e16dc8 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/fw.h @@ -0,0 +1,101 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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__FW__H__ +#define __RTL92C__FW__H__ + +#define FW_8192C_START_ADDRESS 0x1000 +#define FW_8192C_END_ADDRESS 0x3FFF +#define FW_8192C_PAGE_SIZE 4096 +#define FW_8192C_POLLING_DELAY 5 +#define FW_8192C_POLLING_TIMEOUT_COUNT 1000 + +#define BEACON_PG 0 +#define PSPOLL_PG 2 +#define NULL_PG 3 +#define PROBERSP_PG 4 /* ->5 */ + +#define TOTAL_RESERVED_PKT_LEN 768 + +#define IS_FW_HEADER_EXIST(_pfwhdr) \ + ((_pfwhdr->signature&0xFF00) == 0x2300) + +struct rtl8723ae_firmware_header { + u16 signature; + u8 category; + u8 function; + u16 version; + u8 subversion; + u8 rsvd1; + u8 month; + u8 date; + u8 hour; + u8 minute; + u16 ramcodeSize; + u16 rsvd2; + u32 svnindex; + u32 rsvd3; + u32 rsvd4; + u32 rsvd5; +}; + +enum rtl8192c_h2c_cmd { + H2C_AP_OFFLOAD = 0, + H2C_SETPWRMODE = 1, + H2C_JOINBSSRPT = 2, + H2C_RSVDPAGE = 3, + H2C_RSSI_REPORT = 5, + H2C_RA_MASK = 6, + MAX_H2CCMD +}; + +#define SET_H2CCMD_PWRMODE_PARM_MODE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_PWRMODE_PARM_SMART_PS(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_H2CCMD_PWRMODE_PARM_BCN_PASS_TIME(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) +#define SET_H2CCMD_JOINBSSRPT_PARM_OPMODE(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_PROBE_RSP(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE(__ph2ccmd, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_PSPOLL(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+1, 0, 8, __val) +#define SET_H2CCMD_RSVDPAGE_LOC_NULL_DATA(__ph2ccmd, __val) \ + SET_BITS_TO_LE_1BYTE((__ph2ccmd)+2, 0, 8, __val) + +int rtl8723ae_download_fw(struct ieee80211_hw *hw); +void rtl8723ae_fill_h2c_cmd(struct ieee80211_hw *hw, u8 element_id, + u32 cmd_len, u8 *p_cmdbuffer); +void rtl8723ae_firmware_selfreset(struct ieee80211_hw *hw); +void rtl8723ae_set_fw_pwrmode_cmd(struct ieee80211_hw *hw, u8 mode); +void rtl8723ae_set_fw_rsvdpagepkt(struct ieee80211_hw *hw, bool b_dl_finished); +void rtl8723ae_set_fw_joinbss_report_cmd(struct ieee80211_hw *hw, u8 mstatus); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c new file mode 100644 index 00000000000..3d092e4b0b7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.c @@ -0,0 +1,542 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "hal_bt_coexist.h" +#include "../pci.h" +#include "dm.h" +#include "fw.h" +#include "phy.h" +#include "reg.h" +#include "hal_btc.h" + +void rtl8723ae_dm_bt_reject_ap_aggregated_packet(struct ieee80211_hw *hw, + bool reject) +{ +} + +void _rtl8723_dm_bt_check_wifi_state(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + if (rtlpriv->link_info.busytraffic) { + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_IDLE; + + if (rtlpriv->link_info.tx_busy_traffic) + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_UPLINK; + else + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_UPLINK; + + if (rtlpriv->link_info.rx_busy_traffic) + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_DOWNLINK; + else + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_DOWNLINK; + } else { + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_WIFI_IDLE; + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_UPLINK; + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_DOWNLINK; + } + + if (rtlpriv->mac80211.mode == WIRELESS_MODE_G || + rtlpriv->mac80211.mode == WIRELESS_MODE_B) { + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_WIFI_LEGACY; + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_HT20; + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_HT40; + } else { + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_WIFI_LEGACY; + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_HT40; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_HT20; + } else { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_HT20; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_HT40; + } + } + + if (rtlpriv->bt_operation_on) + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_BT30; + else + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_BT30; +} + +u8 rtl8723ae_dm_bt_check_coex_rssi_state1(struct ieee80211_hw *hw, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) + +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + long smooth; + u8 bt_rssi_state = 0; + + smooth = rtl8723ae_dm_bt_get_rx_ss(hw); + + if (level_num == 2) { + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM; + + if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_LOW) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_LOW)) { + if (smooth >= (rssi_thresh + + BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_HIGH; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_HIGH; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to High\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state stay at Low\n"); + } + } else { + if (smooth < rssi_thresh) { + bt_rssi_state = BT_RSSI_STATE_LOW; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_LOW; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to Low\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state stay at High\n"); + } + } + } else if (level_num == 3) { + if (rssi_thresh > rssi_thresh1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 thresh error!!\n"); + return rtlpcipriv->bt_coexist.bt_pre_rssi_state; + } + + if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_LOW) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_LOW)) { + if (smooth >= + (rssi_thresh+BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_MEDIUM; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_MEDIUM; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_LOW; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to Medium\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state stay at Low\n"); + } + } else if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_MEDIUM) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_MEDIUM)) { + if (smooth >= (rssi_thresh1 + + BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_HIGH; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_HIGH; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_LOW; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to High\n"); + } else if (smooth < rssi_thresh) { + bt_rssi_state = BT_RSSI_STATE_LOW; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_LOW; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_HIGH; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to Low\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state stay at Medium\n"); + } + } else { + if (smooth < rssi_thresh1) { + bt_rssi_state = BT_RSSI_STATE_MEDIUM; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_1_MEDIUM; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_HIGH; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_1_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state switch to Medium\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI_1 state stay at High\n"); + } + } + } + + rtlpcipriv->bt_coexist.bt_pre_rssi_state1 = bt_rssi_state; + + return bt_rssi_state; +} + +u8 rtl8723ae_dm_bt_check_coex_rssi_state(struct ieee80211_hw *hw, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + long smooth; + u8 bt_rssi_state = 0; + + smooth = rtl8723ae_dm_bt_get_rx_ss(hw); + + if (level_num == 2) { + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_MEDIUM; + + if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_LOW) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_LOW)){ + if (smooth >= + (rssi_thresh + BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_HIGH; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_HIGH; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to High\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state stay at Low\n"); + } + } else { + if (smooth < rssi_thresh) { + bt_rssi_state = BT_RSSI_STATE_LOW; + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_WIFI_RSSI_LOW; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_WIFI_RSSI_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to Low\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state stay at High\n"); + } + } + } else if (level_num == 3) { + if (rssi_thresh > rssi_thresh1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI thresh error!!\n"); + return rtlpcipriv->bt_coexist.bt_pre_rssi_state; + } + if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_LOW) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_LOW)) { + if (smooth >= + (rssi_thresh + BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_MEDIUM; + rtlpcipriv->bt_coexist.cstate + |= BT_COEX_STATE_WIFI_RSSI_MEDIUM; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_LOW; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to Medium\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state stay at Low\n"); + } + } else if ((rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_MEDIUM) || + (rtlpcipriv->bt_coexist.bt_pre_rssi_state == + BT_RSSI_STATE_STAY_MEDIUM)) { + if (smooth >= + (rssi_thresh1 + BT_FW_COEX_THRESH_TOL)) { + bt_rssi_state = BT_RSSI_STATE_HIGH; + rtlpcipriv->bt_coexist.cstate + |= BT_COEX_STATE_WIFI_RSSI_HIGH; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_LOW; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to High\n"); + } else if (smooth < rssi_thresh) { + bt_rssi_state = BT_RSSI_STATE_LOW; + rtlpcipriv->bt_coexist.cstate + |= BT_COEX_STATE_WIFI_RSSI_LOW; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_HIGH; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to Low\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_MEDIUM; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state stay at Medium\n"); + } + } else { + if (smooth < rssi_thresh1) { + bt_rssi_state = BT_RSSI_STATE_MEDIUM; + rtlpcipriv->bt_coexist.cstate + |= BT_COEX_STATE_WIFI_RSSI_MEDIUM; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_HIGH; + rtlpcipriv->bt_coexist.cstate + &= ~BT_COEX_STATE_WIFI_RSSI_LOW; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state switch to Medium\n"); + } else { + bt_rssi_state = BT_RSSI_STATE_STAY_HIGH; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], RSSI state stay at High\n"); + } + } + } + + rtlpcipriv->bt_coexist.bt_pre_rssi_state = bt_rssi_state; + return bt_rssi_state; +} + +long rtl8723ae_dm_bt_get_rx_ss(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + long smooth = 0; + + if (rtlpriv->mac80211.link_state >= MAC80211_LINKED) + smooth = GET_UNDECORATED_AVERAGE_RSSI(rtlpriv); + else + smooth = rtlpriv->dm.entry_min_undec_sm_pwdb; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_get_rx_ss() = %ld\n", smooth); + + return smooth; +} + +void rtl8723ae_dm_bt_balance(struct ieee80211_hw *hw, + bool balance_on, u8 ms0, u8 ms1) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[3] = {0}; + + if (balance_on) { + h2c_parameter[2] = 1; + h2c_parameter[1] = ms1; + h2c_parameter[0] = ms0; + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } else { + h2c_parameter[2] = 0; + h2c_parameter[1] = 0; + h2c_parameter[0] = 0; + } + rtlpcipriv->bt_coexist.balance_on = balance_on; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[DM][BT], Balance=[%s:%dms:%dms], write 0xc=0x%x\n", + balance_on ? "ON" : "OFF", ms0, ms1, + h2c_parameter[0]<<16 | h2c_parameter[1]<<8 | h2c_parameter[2]); + + rtl8723ae_fill_h2c_cmd(hw, 0xc, 3, h2c_parameter); +} + + +void rtl8723ae_dm_bt_agc_table(struct ieee80211_hw *hw, u8 type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + if (type == BT_AGCTABLE_OFF) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BT]AGCTable Off!\n"); + rtl_write_dword(rtlpriv, 0xc78, 0x641c0001); + rtl_write_dword(rtlpriv, 0xc78, 0x631d0001); + rtl_write_dword(rtlpriv, 0xc78, 0x621e0001); + rtl_write_dword(rtlpriv, 0xc78, 0x611f0001); + rtl_write_dword(rtlpriv, 0xc78, 0x60200001); + + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0x32000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0x71000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0xb0000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0xfc000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_G1, 0xfffff, 0x30355); + } else if (type == BT_AGCTABLE_ON) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BT]AGCTable On!\n"); + rtl_write_dword(rtlpriv, 0xc78, 0x4e1c0001); + rtl_write_dword(rtlpriv, 0xc78, 0x4d1d0001); + rtl_write_dword(rtlpriv, 0xc78, 0x4c1e0001); + rtl_write_dword(rtlpriv, 0xc78, 0x4b1f0001); + rtl_write_dword(rtlpriv, 0xc78, 0x4a200001); + + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0xdc000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0x90000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0x51000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_AGC_HP, 0xfffff, 0x12000); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, + RF_RX_G1, 0xfffff, 0x00355); + + rtlpcipriv->bt_coexist.sw_coexist_all_off = false; + } +} + +void rtl8723ae_dm_bt_bback_off_level(struct ieee80211_hw *hw, u8 type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + if (type == BT_BB_BACKOFF_OFF) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BT]BBBackOffLevel Off!\n"); + rtl_write_dword(rtlpriv, 0xc04, 0x3a05611); + } else if (type == BT_BB_BACKOFF_ON) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BT]BBBackOffLevel On!\n"); + rtl_write_dword(rtlpriv, 0xc04, 0x3a07611); + rtlpcipriv->bt_coexist.sw_coexist_all_off = false; + } +} + +void rtl8723ae_dm_bt_fw_coex_all_off(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_fw_coex_all_off()\n"); + + if (rtlpcipriv->bt_coexist.fw_coexist_all_off) + return; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_fw_coex_all_off(), real Do\n"); + rtl8723ae_dm_bt_fw_coex_all_off_8723a(hw); + rtlpcipriv->bt_coexist.fw_coexist_all_off = true; +} + +void rtl8723ae_dm_bt_sw_coex_all_off(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_sw_coex_all_off()\n"); + + if (rtlpcipriv->bt_coexist.sw_coexist_all_off) + return; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_sw_coex_all_off(), real Do\n"); + rtl8723ae_dm_bt_sw_coex_all_off_8723a(hw); + rtlpcipriv->bt_coexist.sw_coexist_all_off = true; +} + +void rtl8723ae_dm_bt_hw_coex_all_off(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_hw_coex_all_off()\n"); + + if (rtlpcipriv->bt_coexist.hw_coexist_all_off) + return; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "rtl8723ae_dm_bt_hw_coex_all_off(), real Do\n"); + + rtl8723ae_dm_bt_hw_coex_all_off_8723a(hw); + + rtlpcipriv->bt_coexist.hw_coexist_all_off = true; +} + +void rtl8723ae_btdm_coex_all_off(struct ieee80211_hw *hw) +{ + rtl8723ae_dm_bt_fw_coex_all_off(hw); + rtl8723ae_dm_bt_sw_coex_all_off(hw); + rtl8723ae_dm_bt_hw_coex_all_off(hw); +} + +bool rtl8723ae_dm_bt_is_coexist_state_changed(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + if ((rtlpcipriv->bt_coexist.previous_state == + rtlpcipriv->bt_coexist.cstate) && + (rtlpcipriv->bt_coexist.previous_state_h == + rtlpcipriv->bt_coexist.cstate_h)) + return false; + else + return true; +} + +bool rtl8723ae_dm_bt_is_wifi_up_link(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->link_info.tx_busy_traffic) + return true; + else + return false; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.h b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.h new file mode 100644 index 00000000000..76f4d122dbc --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_bt_coexist.h @@ -0,0 +1,160 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_HAL_BT_COEXIST_H__ +#define __RTL8723E_HAL_BT_COEXIST_H__ + +#include "../wifi.h" + +/* The reg define is for 8723 */ +#define REG_HIGH_PRIORITY_TXRX 0x770 +#define REG_LOW_PRIORITY_TXRX 0x774 + +#define BT_FW_COEX_THRESH_TOL 6 +#define BT_FW_COEX_THRESH_20 20 +#define BT_FW_COEX_THRESH_23 23 +#define BT_FW_COEX_THRESH_25 25 +#define BT_FW_COEX_THRESH_30 30 +#define BT_FW_COEX_THRESH_35 35 +#define BT_FW_COEX_THRESH_40 40 +#define BT_FW_COEX_THRESH_45 45 +#define BT_FW_COEX_THRESH_47 47 +#define BT_FW_COEX_THRESH_50 50 +#define BT_FW_COEX_THRESH_55 55 + +#define BT_COEX_STATE_BT30 BIT(0) +#define BT_COEX_STATE_WIFI_HT20 BIT(1) +#define BT_COEX_STATE_WIFI_HT40 BIT(2) +#define BT_COEX_STATE_WIFI_LEGACY BIT(3) + +#define BT_COEX_STATE_WIFI_RSSI_LOW BIT(4) +#define BT_COEX_STATE_WIFI_RSSI_MEDIUM BIT(5) +#define BT_COEX_STATE_WIFI_RSSI_HIGH BIT(6) +#define BT_COEX_STATE_DEC_BT_POWER BIT(7) + +#define BT_COEX_STATE_WIFI_IDLE BIT(8) +#define BT_COEX_STATE_WIFI_UPLINK BIT(9) +#define BT_COEX_STATE_WIFI_DOWNLINK BIT(10) + +#define BT_COEX_STATE_BT_INQ_PAGE BIT(11) +#define BT_COEX_STATE_BT_IDLE BIT(12) +#define BT_COEX_STATE_BT_UPLINK BIT(13) +#define BT_COEX_STATE_BT_DOWNLINK BIT(14) + +#define BT_COEX_STATE_HOLD_FOR_BT_OPERATION BIT(15) +#define BT_COEX_STATE_BT_RSSI_LOW BIT(19) + +#define BT_COEX_STATE_PROFILE_HID BIT(20) +#define BT_COEX_STATE_PROFILE_A2DP BIT(21) +#define BT_COEX_STATE_PROFILE_PAN BIT(22) +#define BT_COEX_STATE_PROFILE_SCO BIT(23) + +#define BT_COEX_STATE_WIFI_RSSI_1_LOW BIT(24) +#define BT_COEX_STATE_WIFI_RSSI_1_MEDIUM BIT(25) +#define BT_COEX_STATE_WIFI_RSSI_1_HIGH BIT(26) + +#define BT_COEX_STATE_BTINFO_COMMON BIT(30) +#define BT_COEX_STATE_BTINFO_B_HID_SCOESCO BIT(31) +#define BT_COEX_STATE_BTINFO_B_FTP_A2DP BIT(29) + +#define BT_COEX_STATE_BT_CNT_LEVEL_0 BIT(0) +#define BT_COEX_STATE_BT_CNT_LEVEL_1 BIT(1) +#define BT_COEX_STATE_BT_CNT_LEVEL_2 BIT(2) +#define BT_COEX_STATE_BT_CNT_LEVEL_3 BIT(3) + +#define BT_RSSI_STATE_HIGH 0 +#define BT_RSSI_STATE_MEDIUM 1 +#define BT_RSSI_STATE_LOW 2 +#define BT_RSSI_STATE_STAY_HIGH 3 +#define BT_RSSI_STATE_STAY_MEDIUM 4 +#define BT_RSSI_STATE_STAY_LOW 5 + +#define BT_AGCTABLE_OFF 0 +#define BT_AGCTABLE_ON 1 +#define BT_BB_BACKOFF_OFF 0 +#define BT_BB_BACKOFF_ON 1 +#define BT_FW_NAV_OFF 0 +#define BT_FW_NAV_ON 1 + +#define BT_COEX_MECH_NONE 0 +#define BT_COEX_MECH_SCO 1 +#define BT_COEX_MECH_HID 2 +#define BT_COEX_MECH_A2DP 3 +#define BT_COEX_MECH_PAN 4 +#define BT_COEX_MECH_HID_A2DP 5 +#define BT_COEX_MECH_HID_PAN 6 +#define BT_COEX_MECH_PAN_A2DP 7 +#define BT_COEX_MECH_HID_SCO_ESCO 8 +#define BT_COEX_MECH_FTP_A2DP 9 +#define BT_COEX_MECH_COMMON 10 +#define BT_COEX_MECH_MAX 11 + +#define BT_DBG_PROFILE_NONE 0 +#define BT_DBG_PROFILE_SCO 1 +#define BT_DBG_PROFILE_HID 2 +#define BT_DBG_PROFILE_A2DP 3 +#define BT_DBG_PROFILE_PAN 4 +#define BT_DBG_PROFILE_HID_A2DP 5 +#define BT_DBG_PROFILE_HID_PAN 6 +#define BT_DBG_PROFILE_PAN_A2DP 7 +#define BT_DBG_PROFILE_MAX 9 + +#define BTINFO_B_FTP BIT(7) +#define BTINFO_B_A2DP BIT(6) +#define BTINFO_B_HID BIT(5) +#define BTINFO_B_SCO_BUSY BIT(4) +#define BTINFO_B_ACL_BUSY BIT(3) +#define BTINFO_B_INQ_PAGE BIT(2) +#define BTINFO_B_SCO_ESCO BIT(1) +#define BTINFO_B_CONNECTION BIT(0) + + +void rtl8723ae_btdm_coex_all_off(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_fw_coex_all_off(struct ieee80211_hw *hw); + +void rtl8723ae_dm_bt_sw_coex_all_off(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_hw_coex_all_off(struct ieee80211_hw *hw); +long rtl8723ae_dm_bt_get_rx_ss(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_balance(struct ieee80211_hw *hw, + bool balance_on, u8 ms0, u8 ms1); +void rtl8723ae_dm_bt_agc_table(struct ieee80211_hw *hw, u8 type); +void rtl8723ae_dm_bt_bback_off_level(struct ieee80211_hw *hw, u8 type); +u8 rtl8723ae_dm_bt_check_coex_rssi_state(struct ieee80211_hw *hw, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1); +u8 rtl8723ae_dm_bt_check_coex_rssi_state1(struct ieee80211_hw *hw, + u8 level_num, u8 rssi_thresh, + u8 rssi_thresh1); +void _rtl8723_dm_bt_check_wifi_state(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_reject_ap_aggregated_packet(struct ieee80211_hw *hw, + bool reject); + +bool rtl8723ae_dm_bt_is_coexist_state_changed(struct ieee80211_hw *hw); +bool rtl8723ae_dm_bt_is_wifi_up_link(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c new file mode 100644 index 00000000000..887d521fe69 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.c @@ -0,0 +1,1786 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + **************************************************************************** + */ +#include "hal_btc.h" +#include "../pci.h" +#include "phy.h" +#include "fw.h" +#include "reg.h" +#include "def.h" + +void rtl8723ae_bt_coex_off_before_lps(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + if (!rtlpcipriv->bt_coexist.bt_coexistence) + return; + + if (ppsc->inactiveps) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BT][DM], Before enter IPS, turn off all Coexist DM\n"); + rtlpcipriv->bt_coexist.cstate = 0; + rtlpcipriv->bt_coexist.previous_state = 0; + rtlpcipriv->bt_coexist.cstate_h = 0; + rtlpcipriv->bt_coexist.previous_state_h = 0; + rtl8723ae_btdm_coex_all_off(hw); + } +} + +static enum _RT_MEDIA_STATUS mgnt_link_status_query(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + enum _RT_MEDIA_STATUS m_status = RT_MEDIA_DISCONNECT; + + u8 bibss = (mac->opmode == NL80211_IFTYPE_ADHOC) ? 1 : 0; + + if (bibss || rtlpriv->mac80211.link_state >= MAC80211_LINKED) + m_status = RT_MEDIA_CONNECT; + + return m_status; +} + +void rtl_8723e_bt_wifi_media_status_notify(struct ieee80211_hw *hw, + bool mstatus) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 h2c_parameter[3] = {0}; + u8 chnl; + + if (!rtlpcipriv->bt_coexist.bt_coexistence) + return; + + if (RT_MEDIA_CONNECT == mstatus) + h2c_parameter[0] = 0x1; /* 0: disconnected, 1:connected */ + else + h2c_parameter[0] = 0x0; + + if (mgnt_link_status_query(hw)) { + chnl = rtlphy->current_channel; + h2c_parameter[1] = chnl; + } + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) + h2c_parameter[2] = 0x30; + else + h2c_parameter[2] = 0x20; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], FW write 0x19 = 0x%x\n", + h2c_parameter[0]<<16|h2c_parameter[1]<<8|h2c_parameter[2]); + + rtl8723ae_fill_h2c_cmd(hw, 0x19, 3, h2c_parameter); + +} + +static bool rtl8723ae_dm_bt_is_wifi_busy(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + if (rtlpriv->link_info.busytraffic || + rtlpriv->link_info.rx_busy_traffic || + rtlpriv->link_info.tx_busy_traffic) + return true; + else + return false; +} + +static void rtl8723ae_dm_bt_set_fw_3a(struct ieee80211_hw *hw, + u8 byte1, u8 byte2, u8 byte3, + u8 byte4, u8 byte5) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[5] = {0}; + + h2c_parameter[0] = byte1; + h2c_parameter[1] = byte2; + h2c_parameter[2] = byte3; + h2c_parameter[3] = byte4; + h2c_parameter[4] = byte5; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], FW write 0x3a(4bytes) = 0x%x%8x\n", + h2c_parameter[0], h2c_parameter[1]<<24 | h2c_parameter[2]<<16 | + h2c_parameter[3]<<8 | h2c_parameter[4]); + rtl8723ae_fill_h2c_cmd(hw, 0x3a, 5, h2c_parameter); +} + +static bool rtl8723ae_dm_bt_need_to_dec_bt_pwr(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (mgnt_link_status_query(hw) == RT_MEDIA_CONNECT) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Need to decrease bt power\n"); + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_DEC_BT_POWER; + return true; + } + + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_DEC_BT_POWER; + return false; +} + +static bool rtl8723ae_dm_bt_is_same_coexist_state(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + if ((rtlpcipriv->bt_coexist.previous_state == + rtlpcipriv->bt_coexist.cstate) && + (rtlpcipriv->bt_coexist.previous_state_h == + rtlpcipriv->bt_coexist.cstate_h)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[DM][BT], Coexist state do not chang!!\n"); + return true; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[DM][BT], Coexist state changed!!\n"); + return false; + } +} + +static void rtl8723ae_dm_bt_set_coex_table(struct ieee80211_hw *hw, + u32 val_0x6c0, u32 val_0x6c8, + u32 val_0x6cc) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "set coex table, set 0x6c0 = 0x%x\n", val_0x6c0); + rtl_write_dword(rtlpriv, 0x6c0, val_0x6c0); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "set coex table, set 0x6c8 = 0x%x\n", val_0x6c8); + rtl_write_dword(rtlpriv, 0x6c8, val_0x6c8); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "set coex table, set 0x6cc = 0x%x\n", val_0x6cc); + rtl_write_byte(rtlpriv, 0x6cc, val_0x6cc); +} + +static void rtl8723ae_dm_bt_set_hw_pta_mode(struct ieee80211_hw *hw, bool mode) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (BT_PTA_MODE_ON == mode) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, "PTA mode on, "); + /* Enable GPIO 0/1/2/3/8 pins for bt */ + rtl_write_byte(rtlpriv, 0x40, 0x20); + rtlpcipriv->bt_coexist.hw_coexist_all_off = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, "PTA mode off\n"); + rtl_write_byte(rtlpriv, 0x40, 0x0); + } +} + +static void rtl8723ae_dm_bt_set_sw_rf_rx_lpf_corner(struct ieee80211_hw *hw, + u8 type) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (BT_RF_RX_LPF_CORNER_SHRINK == type) { + /* Shrink RF Rx LPF corner, 0x1e[7:4]=1111 ==> [11:4] by Jenyu*/ + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "Shrink RF Rx LPF corner!!\n"); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, 0x1e, 0xfffff, + 0xf0ff7); + rtlpcipriv->bt_coexist.sw_coexist_all_off = false; + } else if (BT_RF_RX_LPF_CORNER_RESUME == type) { + /*Resume RF Rx LPF corner*/ + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "Resume RF Rx LPF corner!!\n"); + rtl8723ae_phy_set_rf_reg(hw, RF90_PATH_A, 0x1e, 0xfffff, + rtlpcipriv->bt_coexist.bt_rfreg_origin_1e); + } +} + +static void rtl8723ae_bt_set_penalty_tx_rate_adap(struct ieee80211_hw *hw, + u8 ra_type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 tmu1; + + tmu1 = rtl_read_byte(rtlpriv, 0x4fd); + tmu1 |= BIT(0); + if (BT_TX_RATE_ADAPTIVE_LOW_PENALTY == ra_type) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "Tx rate adaptive, set low penalty!!\n"); + tmu1 &= ~BIT(2); + rtlpcipriv->bt_coexist.sw_coexist_all_off = false; + } else if (BT_TX_RATE_ADAPTIVE_NORMAL == ra_type) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "Tx rate adaptive, set normal!!\n"); + tmu1 |= BIT(2); + } + rtl_write_byte(rtlpriv, 0x4fd, tmu1); +} + +static void rtl8723ae_dm_bt_btdm_structure_reload(struct ieee80211_hw *hw, + struct btdm_8723 *btdm) +{ + btdm->all_off = false; + btdm->agc_table_en = false; + btdm->adc_back_off_on = false; + btdm->b2_ant_hid_en = false; + btdm->low_penalty_rate_adaptive = false; + btdm->rf_rx_lpf_shrink = false; + btdm->reject_aggre_pkt = false; + + btdm->tdma_on = false; + btdm->tdma_ant = TDMA_2ANT; + btdm->tdma_nav = TDMA_NAV_OFF; + btdm->tdma_dac_swing = TDMA_DAC_SWING_OFF; + btdm->fw_dac_swing_lvl = 0x20; + + btdm->tra_tdma_on = false; + btdm->tra_tdma_ant = TDMA_2ANT; + btdm->tra_tdma_nav = TDMA_NAV_OFF; + btdm->ignore_wlan_act = false; + + btdm->ps_tdma_on = false; + btdm->ps_tdma_byte[0] = 0x0; + btdm->ps_tdma_byte[1] = 0x0; + btdm->ps_tdma_byte[2] = 0x0; + btdm->ps_tdma_byte[3] = 0x8; + btdm->ps_tdma_byte[4] = 0x0; + + btdm->pta_on = true; + btdm->val_0x6c0 = 0x5a5aaaaa; + btdm->val_0x6c8 = 0xcc; + btdm->val_0x6cc = 0x3; + + btdm->sw_dac_swing_on = false; + btdm->sw_dac_swing_lvl = 0xc0; + btdm->wlan_act_hi = 0x20; + btdm->wlan_act_lo = 0x10; + btdm->bt_retry_index = 2; + + btdm->dec_bt_pwr = false; +} + +static void dm_bt_btdm_structure_reload_all_off(struct ieee80211_hw *hw, + struct btdm_8723 *btdm) +{ + rtl8723ae_dm_bt_btdm_structure_reload(hw, btdm); + btdm->all_off = true; + btdm->pta_on = false; + btdm->wlan_act_hi = 0x10; +} + +static bool rtl8723ae_dm_bt_is_2_ant_common_action(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct btdm_8723 btdm8723; + bool common = false; + + rtl8723ae_dm_bt_btdm_structure_reload(hw, &btdm8723); + + if (!rtl8723ae_dm_bt_is_wifi_busy(hw) + && !rtlpcipriv->bt_coexist.bt_busy) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi idle + Bt idle, bt coex mechanism always off!!\n"); + dm_bt_btdm_structure_reload_all_off(hw, &btdm8723); + common = true; + } else if (rtl8723ae_dm_bt_is_wifi_busy(hw) + && !rtlpcipriv->bt_coexist.bt_busy) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi non-idle + Bt disabled/idle!!\n"); + btdm8723.low_penalty_rate_adaptive = true; + btdm8723.rf_rx_lpf_shrink = false; + btdm8723.reject_aggre_pkt = false; + + /* sw mechanism */ + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = false; + btdm8723.sw_dac_swing_on = false; + + btdm8723.pta_on = true; + btdm8723.val_0x6c0 = 0x5a5aaaaa; + btdm8723.val_0x6c8 = 0xcccc; + btdm8723.val_0x6cc = 0x3; + + btdm8723.tdma_on = false; + btdm8723.tdma_dac_swing = TDMA_DAC_SWING_OFF; + btdm8723.b2_ant_hid_en = false; + + common = true; + } else if (rtlpcipriv->bt_coexist.bt_busy) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Bt non-idle!\n"); + if (mgnt_link_status_query(hw) == RT_MEDIA_CONNECT) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi connection exist\n"); + common = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "No Wifi connection!\n"); + btdm8723.rf_rx_lpf_shrink = true; + btdm8723.low_penalty_rate_adaptive = false; + btdm8723.reject_aggre_pkt = false; + + /* sw mechanism */ + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = false; + btdm8723.sw_dac_swing_on = false; + + btdm8723.pta_on = true; + btdm8723.val_0x6c0 = 0x55555555; + btdm8723.val_0x6c8 = 0x0000ffff; + btdm8723.val_0x6cc = 0x3; + + btdm8723.tdma_on = false; + btdm8723.tdma_dac_swing = TDMA_DAC_SWING_OFF; + btdm8723.b2_ant_hid_en = false; + + common = true; + } + } + + if (rtl8723ae_dm_bt_need_to_dec_bt_pwr(hw)) + btdm8723.dec_bt_pwr = true; + + if (common) + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_BTINFO_COMMON; + + if (common && rtl8723ae_dm_bt_is_coexist_state_changed(hw)) + rtl8723ae_dm_bt_set_bt_dm(hw, &btdm8723); + + return common; +} + +static void rtl8723ae_dm_bt_set_sw_full_time_dac_swing(struct ieee80211_hw *hw, + bool sw_dac_swing_on, + u32 sw_dac_swing_lvl) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (sw_dac_swing_on) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], SwDacSwing = 0x%x\n", sw_dac_swing_lvl); + rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000, + sw_dac_swing_lvl); + rtlpcipriv->bt_coexist.sw_coexist_all_off = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], SwDacSwing Off!\n"); + rtl8723ae_phy_set_bb_reg(hw, 0x880, 0xff000000, 0xc0); + } +} + +static void rtl8723ae_dm_bt_set_fw_dec_bt_pwr(struct ieee80211_hw *hw, + bool dec_bt_pwr) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[1] = {0}; + + h2c_parameter[0] = 0; + + if (dec_bt_pwr) { + h2c_parameter[0] |= BIT(1); + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], decrease Bt Power : %s, write 0x21 = 0x%x\n", + (dec_bt_pwr ? "Yes!!" : "No!!"), h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x21, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_2_ant_hid(struct ieee80211_hw *hw, + bool enable, bool dac_swing_on) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[1] = {0}; + + if (enable) { + h2c_parameter[0] |= BIT(0); + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } + if (dac_swing_on) + h2c_parameter[0] |= BIT(1); /* Dac Swing default enable */ + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], turn 2-Ant+HID mode %s, DACSwing:%s, write 0x15 = 0x%x\n", + (enable ? "ON!!" : "OFF!!"), (dac_swing_on ? "ON" : "OFF"), + h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x15, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_tdma_ctrl(struct ieee80211_hw *hw, + bool enable, u8 ant_num, u8 nav_en, + u8 dac_swing_en) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 h2c_parameter[1] = {0}; + u8 h2c_parameter1[1] = {0}; + + h2c_parameter[0] = 0; + h2c_parameter1[0] = 0; + + if (enable) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], set BT PTA update manager to trigger update!!\n"); + h2c_parameter1[0] |= BIT(0); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], turn TDMA mode ON!!\n"); + h2c_parameter[0] |= BIT(0); /* function enable */ + if (TDMA_1ANT == ant_num) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_1ANT\n"); + h2c_parameter[0] |= BIT(1); + } else if (TDMA_2ANT == ant_num) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_2ANT\n"); + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Unknown Ant\n"); + } + + if (TDMA_NAV_OFF == nav_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_NAV_OFF\n"); + } else if (TDMA_NAV_ON == nav_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_NAV_ON\n"); + h2c_parameter[0] |= BIT(2); + } + + if (TDMA_DAC_SWING_OFF == dac_swing_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_DAC_SWING_OFF\n"); + } else if (TDMA_DAC_SWING_ON == dac_swing_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TDMA_DAC_SWING_ON\n"); + h2c_parameter[0] |= BIT(4); + } + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], set BT PTA update manager to no update!!\n"); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], turn TDMA mode OFF!!\n"); + } + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], FW2AntTDMA, write 0x26 = 0x%x\n", + h2c_parameter1[0]); + rtl8723ae_fill_h2c_cmd(hw, 0x26, 1, h2c_parameter1); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], FW2AntTDMA, write 0x14 = 0x%x\n", h2c_parameter[0]); + rtl8723ae_fill_h2c_cmd(hw, 0x14, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_ignore_wlan_act(struct ieee80211_hw *hw, + bool enable) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 h2c_parameter[1] = {0}; + + if (enable) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], BT Ignore Wlan_Act !!\n"); + h2c_parameter[0] |= BIT(0); /* function enable */ + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], BT don't ignore Wlan_Act !!\n"); + } + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], set FW for BT Ignore Wlan_Act, write 0x25 = 0x%x\n", + h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x25, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(struct ieee80211_hw *hw, + bool enable, u8 ant_num, + u8 nav_en) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 h2c_parameter[2] = {0}; + + /* Only 8723 B cut should do this */ + if (IS_VENDOR_8723_A_CUT(rtlhal->version)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], not 8723B cut, don't set Traditional TDMA!!\n"); + return; + } + + if (enable) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], turn TTDMA mode ON!!\n"); + h2c_parameter[0] |= BIT(0); /* function enable */ + if (TDMA_1ANT == ant_num) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TTDMA_1ANT\n"); + h2c_parameter[0] |= BIT(1); + } else if (TDMA_2ANT == ant_num) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TTDMA_2ANT\n"); + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Unknown Ant\n"); + } + + if (TDMA_NAV_OFF == nav_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TTDMA_NAV_OFF\n"); + } else if (TDMA_NAV_ON == nav_en) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], TTDMA_NAV_ON\n"); + h2c_parameter[1] |= BIT(0); + } + + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], turn TTDMA mode OFF!!\n"); + } + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], FW Traditional TDMA, write 0x33 = 0x%x\n", + h2c_parameter[0] << 8 | h2c_parameter[1]); + + rtl8723ae_fill_h2c_cmd(hw, 0x33, 2, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_dac_swing_level(struct ieee80211_hw *hw, + u8 dac_swing_lvl) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[1] = {0}; + + h2c_parameter[0] = dac_swing_lvl; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Set Dac Swing Level = 0x%x\n", dac_swing_lvl); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], write 0x29 = 0x%x\n", h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x29, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_bt_hid_info(struct ieee80211_hw *hw, + bool enable) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[1] = {0}; + + h2c_parameter[0] = 0; + + if (enable) { + h2c_parameter[0] |= BIT(0); + rtlpcipriv->bt_coexist.fw_coexist_all_off = false; + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Set BT HID information = 0x%x\n", enable); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], write 0x24 = 0x%x\n", h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x24, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_bt_retry_index(struct ieee80211_hw *hw, + u8 retry_index) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter[1] = {0}; + + h2c_parameter[0] = retry_index; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Set BT Retry Index=%d\n", retry_index); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], write 0x23 = 0x%x\n", h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x23, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_set_fw_wlan_act(struct ieee80211_hw *hw, + u8 wlan_act_hi, u8 wlan_act_lo) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 h2c_parameter_hi[1] = {0}; + u8 h2c_parameter_lo[1] = {0}; + + h2c_parameter_hi[0] = wlan_act_hi; + h2c_parameter_lo[0] = wlan_act_lo; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], Set WLAN_ACT Hi:Lo = 0x%x/0x%x\n", wlan_act_hi, + wlan_act_lo); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], write 0x22 = 0x%x\n", h2c_parameter_hi[0]); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], write 0x11 = 0x%x\n", h2c_parameter_lo[0]); + + /* WLAN_ACT = High duration, unit:ms */ + rtl8723ae_fill_h2c_cmd(hw, 0x22, 1, h2c_parameter_hi); + /* WLAN_ACT = Low duration, unit:3*625us */ + rtl8723ae_fill_h2c_cmd(hw, 0x11, 1, h2c_parameter_lo); +} + +void rtl8723ae_dm_bt_set_bt_dm(struct ieee80211_hw *hw, struct btdm_8723 *btdm) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct btdm_8723 *btdm_8723 = &rtlhal->hal_coex_8723.btdm; + u8 i; + bool fw_current_inpsmode = false; + bool fw_ps_awake = true; + + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FW_PSMODE_STATUS, + (u8 *)(&fw_current_inpsmode)); + rtlpriv->cfg->ops->get_hw_reg(hw, HW_VAR_FWLPS_RF_ON, + (u8 *)(&fw_ps_awake)); + + /* check new setting is different than the old one, + * if all the same, don't do the setting again. + */ + if (memcmp(btdm_8723, btdm, sizeof(struct btdm_8723)) == 0) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], the same coexist setting, return!!\n"); + return; + } else { /* save the new coexist setting */ + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], UPDATE TO NEW COEX SETTING!!\n"); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bAllOff = 0x%x/ 0x%x\n", + btdm_8723->all_off, btdm->all_off); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new agc_table_en = 0x%x/ 0x%x\n", + btdm_8723->agc_table_en, btdm->agc_table_en); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new adc_back_off_on = 0x%x/ 0x%x\n", + btdm_8723->adc_back_off_on, btdm->adc_back_off_on); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new b2_ant_hid_en = 0x%x/ 0x%x\n", + btdm_8723->b2_ant_hid_en, btdm->b2_ant_hid_en); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bLowPenaltyRateAdaptive = 0x%x/ 0x%x\n", + btdm_8723->low_penalty_rate_adaptive, + btdm->low_penalty_rate_adaptive); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bRfRxLpfShrink = 0x%x/ 0x%x\n", + btdm_8723->rf_rx_lpf_shrink, btdm->rf_rx_lpf_shrink); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bRejectAggrePkt = 0x%x/ 0x%x\n", + btdm_8723->reject_aggre_pkt, btdm->reject_aggre_pkt); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new tdma_on = 0x%x/ 0x%x\n", + btdm_8723->tdma_on, btdm->tdma_on); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new tdmaAnt = 0x%x/ 0x%x\n", + btdm_8723->tdma_ant, btdm->tdma_ant); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new tdmaNav = 0x%x/ 0x%x\n", + btdm_8723->tdma_nav, btdm->tdma_nav); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new tdma_dac_swing = 0x%x/ 0x%x\n", + btdm_8723->tdma_dac_swing, btdm->tdma_dac_swing); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new fwDacSwingLvl = 0x%x/ 0x%x\n", + btdm_8723->fw_dac_swing_lvl, btdm->fw_dac_swing_lvl); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bTraTdmaOn = 0x%x/ 0x%x\n", + btdm_8723->tra_tdma_on, btdm->tra_tdma_on); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new traTdmaAnt = 0x%x/ 0x%x\n", + btdm_8723->tra_tdma_ant, btdm->tra_tdma_ant); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new traTdmaNav = 0x%x/ 0x%x\n", + btdm_8723->tra_tdma_nav, btdm->tra_tdma_nav); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bPsTdmaOn = 0x%x/ 0x%x\n", + btdm_8723->ps_tdma_on, btdm->ps_tdma_on); + for (i = 0; i < 5; i++) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new psTdmaByte[i] = 0x%x/ 0x%x\n", + btdm_8723->ps_tdma_byte[i], + btdm->ps_tdma_byte[i]); + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bIgnoreWlanAct = 0x%x/ 0x%x\n", + btdm_8723->ignore_wlan_act, btdm->ignore_wlan_act); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new bPtaOn = 0x%x/ 0x%x\n", + btdm_8723->pta_on, btdm->pta_on); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new val_0x6c0 = 0x%x/ 0x%x\n", + btdm_8723->val_0x6c0, btdm->val_0x6c0); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new val_0x6c8 = 0x%x/ 0x%x\n", + btdm_8723->val_0x6c8, btdm->val_0x6c8); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new val_0x6cc = 0x%x/ 0x%x\n", + btdm_8723->val_0x6cc, btdm->val_0x6cc); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new sw_dac_swing_on = 0x%x/ 0x%x\n", + btdm_8723->sw_dac_swing_on, btdm->sw_dac_swing_on); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new sw_dac_swing_lvl = 0x%x/ 0x%x\n", + btdm_8723->sw_dac_swing_lvl, + btdm->sw_dac_swing_lvl); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new wlanActHi = 0x%x/ 0x%x\n", + btdm_8723->wlan_act_hi, btdm->wlan_act_hi); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new wlanActLo = 0x%x/ 0x%x\n", + btdm_8723->wlan_act_lo, btdm->wlan_act_lo); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], original/new btRetryIndex = 0x%x/ 0x%x\n", + btdm_8723->bt_retry_index, btdm->bt_retry_index); + + memcpy(btdm_8723, btdm, sizeof(struct btdm_8723)); + } + /* + * Here we only consider when Bt Operation + * inquiry/paging/pairing is ON + * we only need to turn off TDMA + */ + + if (rtlpcipriv->bt_coexist.hold_for_bt_operation) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], set to ignore wlanAct for BT OP!!\n"); + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, true); + return; + } + + if (btdm->all_off) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], disable all coexist mechanism !!\n"); + rtl8723ae_btdm_coex_all_off(hw); + return; + } + + rtl8723ae_dm_bt_reject_ap_aggregated_packet(hw, btdm->reject_aggre_pkt); + + if (btdm->low_penalty_rate_adaptive) + rtl8723ae_bt_set_penalty_tx_rate_adap(hw, + BT_TX_RATE_ADAPTIVE_LOW_PENALTY); + else + rtl8723ae_bt_set_penalty_tx_rate_adap(hw, + BT_TX_RATE_ADAPTIVE_NORMAL); + + if (btdm->rf_rx_lpf_shrink) + rtl8723ae_dm_bt_set_sw_rf_rx_lpf_corner(hw, + BT_RF_RX_LPF_CORNER_SHRINK); + else + rtl8723ae_dm_bt_set_sw_rf_rx_lpf_corner(hw, + BT_RF_RX_LPF_CORNER_RESUME); + + if (btdm->agc_table_en) + rtl8723ae_dm_bt_agc_table(hw, BT_AGCTABLE_ON); + else + rtl8723ae_dm_bt_agc_table(hw, BT_AGCTABLE_OFF); + + if (btdm->adc_back_off_on) + rtl8723ae_dm_bt_bback_off_level(hw, BT_BB_BACKOFF_ON); + else + rtl8723ae_dm_bt_bback_off_level(hw, BT_BB_BACKOFF_OFF); + + rtl8723ae_dm_bt_set_fw_bt_retry_index(hw, btdm->bt_retry_index); + + rtl8723ae_dm_bt_set_fw_dac_swing_level(hw, btdm->fw_dac_swing_lvl); + rtl8723ae_dm_bt_set_fw_wlan_act(hw, btdm->wlan_act_hi, + btdm->wlan_act_lo); + + rtl8723ae_dm_bt_set_coex_table(hw, btdm->val_0x6c0, + btdm->val_0x6c8, btdm->val_0x6cc); + rtl8723ae_dm_bt_set_hw_pta_mode(hw, btdm->pta_on); + + /* Note: There is a constraint between TDMA and 2AntHID + * Only one of 2AntHid and tdma can be turned on + * We should turn off those mechanisms first + * and then turn on them on. + */ + if (btdm->b2_ant_hid_en) { + /* turn off tdma */ + rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(hw, btdm->tra_tdma_on, + btdm->tra_tdma_ant, + btdm->tra_tdma_nav); + rtl8723ae_dm_bt_set_fw_tdma_ctrl(hw, false, btdm->tdma_ant, + btdm->tdma_nav, + btdm->tdma_dac_swing); + + /* turn off Pstdma */ + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, + btdm->ignore_wlan_act); + /* Antenna control by PTA, 0x870 = 0x300. */ + rtl8723ae_dm_bt_set_fw_3a(hw, 0x0, 0x0, 0x0, 0x8, 0x0); + + /* turn on 2AntHid */ + rtl8723ae_dm_bt_set_fw_bt_hid_info(hw, true); + rtl8723ae_dm_bt_set_fw_2_ant_hid(hw, true, true); + } else if (btdm->tdma_on) { + /* turn off 2AntHid */ + rtl8723ae_dm_bt_set_fw_bt_hid_info(hw, false); + rtl8723ae_dm_bt_set_fw_2_ant_hid(hw, false, false); + + /* turn off pstdma */ + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, + btdm->ignore_wlan_act); + /* Antenna control by PTA, 0x870 = 0x300. */ + rtl8723ae_dm_bt_set_fw_3a(hw, 0x0, 0x0, 0x0, 0x8, 0x0); + + /* turn on tdma */ + rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(hw, btdm->tra_tdma_on, + btdm->tra_tdma_ant, btdm->tra_tdma_nav); + rtl8723ae_dm_bt_set_fw_tdma_ctrl(hw, true, btdm->tdma_ant, + btdm->tdma_nav, btdm->tdma_dac_swing); + } else if (btdm->ps_tdma_on) { + /* turn off 2AntHid */ + rtl8723ae_dm_bt_set_fw_bt_hid_info(hw, false); + rtl8723ae_dm_bt_set_fw_2_ant_hid(hw, false, false); + + /* turn off tdma */ + rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(hw, btdm->tra_tdma_on, + btdm->tra_tdma_ant, btdm->tra_tdma_nav); + rtl8723ae_dm_bt_set_fw_tdma_ctrl(hw, false, btdm->tdma_ant, + btdm->tdma_nav, btdm->tdma_dac_swing); + + /* turn on pstdma */ + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, + btdm->ignore_wlan_act); + rtl8723ae_dm_bt_set_fw_3a(hw, + btdm->ps_tdma_byte[0], + btdm->ps_tdma_byte[1], + btdm->ps_tdma_byte[2], + btdm->ps_tdma_byte[3], + btdm->ps_tdma_byte[4]); + } else { + /* turn off 2AntHid */ + rtl8723ae_dm_bt_set_fw_bt_hid_info(hw, false); + rtl8723ae_dm_bt_set_fw_2_ant_hid(hw, false, false); + + /* turn off tdma */ + rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(hw, btdm->tra_tdma_on, + btdm->tra_tdma_ant, btdm->tra_tdma_nav); + rtl8723ae_dm_bt_set_fw_tdma_ctrl(hw, false, btdm->tdma_ant, + btdm->tdma_nav, btdm->tdma_dac_swing); + + /* turn off pstdma */ + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, + btdm->ignore_wlan_act); + /* Antenna control by PTA, 0x870 = 0x300. */ + rtl8723ae_dm_bt_set_fw_3a(hw, 0x0, 0x0, 0x0, 0x8, 0x0); + } + + /* Note: + * We should add delay for making sure sw DacSwing can be set + * sucessfully. Because of that rtl8723ae_dm_bt_set_fw_2_ant_hid() + * and rtl8723ae_dm_bt_set_fw_tdma_ctrl() + * will overwrite the reg 0x880. + */ + mdelay(30); + rtl8723ae_dm_bt_set_sw_full_time_dac_swing(hw, + btdm->sw_dac_swing_on, btdm->sw_dac_swing_lvl); + rtl8723ae_dm_bt_set_fw_dec_bt_pwr(hw, btdm->dec_bt_pwr); +} + +/*============================================================ + * extern function start with BTDM_ + *============================================================ + */ +static u32 rtl8723ae_dm_bt_tx_rx_couter_h(struct ieee80211_hw *hw) +{ + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u32 counters = 0; + + counters = rtlhal->hal_coex_8723.high_priority_tx + + rtlhal->hal_coex_8723.high_priority_rx; + return counters; +} + +static u32 rtl8723ae_dm_bt_tx_rx_couter_l(struct ieee80211_hw *hw) +{ + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + return rtlhal->hal_coex_8723.low_priority_tx + + rtlhal->hal_coex_8723.low_priority_rx; +} + +static u8 rtl8723ae_dm_bt_bt_tx_rx_counter_level(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u32 bt_tx_rx_cnt = 0; + u8 bt_tx_rx_cnt_lvl = 0; + + bt_tx_rx_cnt = rtl8723ae_dm_bt_tx_rx_couter_h(hw) + + rtl8723ae_dm_bt_tx_rx_couter_l(hw); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters = %d\n", bt_tx_rx_cnt); + + rtlpcipriv->bt_coexist.cstate_h &= + ~(BT_COEX_STATE_BT_CNT_LEVEL_0 | BT_COEX_STATE_BT_CNT_LEVEL_1 | + BT_COEX_STATE_BT_CNT_LEVEL_2); + + if (bt_tx_rx_cnt >= BT_TXRX_CNT_THRES_3) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters at level 3\n"); + bt_tx_rx_cnt_lvl = BT_TXRX_CNT_LEVEL_3; + rtlpcipriv->bt_coexist.cstate_h |= BT_COEX_STATE_BT_CNT_LEVEL_3; + } else if (bt_tx_rx_cnt >= BT_TXRX_CNT_THRES_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters at level 2\n"); + bt_tx_rx_cnt_lvl = BT_TXRX_CNT_LEVEL_2; + rtlpcipriv->bt_coexist.cstate_h |= BT_COEX_STATE_BT_CNT_LEVEL_2; + } else if (bt_tx_rx_cnt >= BT_TXRX_CNT_THRES_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters at level 1\n"); + bt_tx_rx_cnt_lvl = BT_TXRX_CNT_LEVEL_1; + rtlpcipriv->bt_coexist.cstate_h |= BT_COEX_STATE_BT_CNT_LEVEL_1; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters at level 0\n"); + bt_tx_rx_cnt_lvl = BT_TXRX_CNT_LEVEL_0; + rtlpcipriv->bt_coexist.cstate_h |= BT_COEX_STATE_BT_CNT_LEVEL_0; + } + return bt_tx_rx_cnt_lvl; +} + +static void rtl8723ae_dm_bt_2_ant_hid_sco_esco(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct btdm_8723 btdm8723; + u8 bt_rssi_state, bt_rssi_state1; + u8 bt_tx_rx_cnt_lvl; + + rtl8723ae_dm_bt_btdm_structure_reload(hw, &btdm8723); + + btdm8723.rf_rx_lpf_shrink = true; + btdm8723.low_penalty_rate_adaptive = true; + btdm8723.reject_aggre_pkt = false; + + bt_tx_rx_cnt_lvl = rtl8723ae_dm_bt_bt_tx_rx_counter_level(hw); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters = %d\n", bt_tx_rx_cnt_lvl); + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, "HT40\n"); + /* coex table */ + btdm8723.val_0x6c0 = 0x55555555; + btdm8723.val_0x6c8 = 0xffff; + btdm8723.val_0x6cc = 0x3; + + /* sw mechanism */ + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = false; + btdm8723.sw_dac_swing_on = false; + + /* fw mechanism */ + btdm8723.ps_tdma_on = true; + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "HT20 or Legacy\n"); + bt_rssi_state = rtl8723ae_dm_bt_check_coex_rssi_state(hw, 2, + 47, 0); + bt_rssi_state1 = rtl8723ae_dm_bt_check_coex_rssi_state1(hw, 2, + 27, 0); + + /* coex table */ + btdm8723.val_0x6c0 = 0x55555555; + btdm8723.val_0x6c8 = 0xffff; + btdm8723.val_0x6cc = 0x3; + + /* sw mechanism */ + if ((bt_rssi_state == BT_RSSI_STATE_HIGH) || + (bt_rssi_state == BT_RSSI_STATE_STAY_HIGH)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi high\n"); + btdm8723.agc_table_en = true; + btdm8723.adc_back_off_on = true; + btdm8723.sw_dac_swing_on = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi low\n"); + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = false; + btdm8723.sw_dac_swing_on = false; + } + + /* fw mechanism */ + btdm8723.ps_tdma_on = true; + if ((bt_rssi_state1 == BT_RSSI_STATE_HIGH) || + (bt_rssi_state1 == BT_RSSI_STATE_STAY_HIGH)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi-1 high\n"); + /* only rssi high we need to do this, + * when rssi low, the value will modified by fw + */ + rtl_write_byte(rtlpriv, 0x883, 0x40); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x83; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x83; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x83; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi-1 low\n"); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } + } + + if (rtl8723ae_dm_bt_need_to_dec_bt_pwr(hw)) + btdm8723.dec_bt_pwr = true; + + /* Always ignore WlanAct if bHid|bSCOBusy|bSCOeSCO */ + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT btInqPageStartTime = 0x%x, btTxRxCntLvl = %d\n", + rtlhal->hal_coex_8723.bt_inq_page_start_time, + bt_tx_rx_cnt_lvl); + if ((rtlhal->hal_coex_8723.bt_inq_page_start_time) || + (BT_TXRX_CNT_LEVEL_3 == bt_tx_rx_cnt_lvl)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], Set BT inquiry / page scan 0x3a setting\n"); + btdm8723.ps_tdma_on = true; + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x2; + btdm8723.ps_tdma_byte[4] = 0x80; + } + + if (rtl8723ae_dm_bt_is_coexist_state_changed(hw)) + rtl8723ae_dm_bt_set_bt_dm(hw, &btdm8723); +} + +static void rtl8723ae_dm_bt_2_ant_fta2dp(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct btdm_8723 btdm8723; + u8 bt_rssi_state, bt_rssi_state1; + u32 bt_tx_rx_cnt_lvl; + + rtl8723ae_dm_bt_btdm_structure_reload(hw, &btdm8723); + btdm8723.rf_rx_lpf_shrink = true; + btdm8723.low_penalty_rate_adaptive = true; + btdm8723.reject_aggre_pkt = false; + + bt_tx_rx_cnt_lvl = rtl8723ae_dm_bt_bt_tx_rx_counter_level(hw); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters = %d\n", bt_tx_rx_cnt_lvl); + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, "HT40\n"); + bt_rssi_state = rtl8723ae_dm_bt_check_coex_rssi_state(hw, 2, + 37, 0); + + /* coex table */ + btdm8723.val_0x6c0 = 0x55555555; + btdm8723.val_0x6c8 = 0xffff; + btdm8723.val_0x6cc = 0x3; + + /* sw mechanism */ + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = true; + btdm8723.sw_dac_swing_on = false; + + /* fw mechanism */ + btdm8723.ps_tdma_on = true; + if ((bt_rssi_state == BT_RSSI_STATE_HIGH) || + (bt_rssi_state == BT_RSSI_STATE_STAY_HIGH)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi high\n"); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi low\n"); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "HT20 or Legacy\n"); + bt_rssi_state = rtl8723ae_dm_bt_check_coex_rssi_state(hw, 2, + 47, 0); + bt_rssi_state1 = rtl8723ae_dm_bt_check_coex_rssi_state1(hw, 2, + 27, 0); + + /* coex table */ + btdm8723.val_0x6c0 = 0x55555555; + btdm8723.val_0x6c8 = 0xffff; + btdm8723.val_0x6cc = 0x3; + + /* sw mechanism */ + if ((bt_rssi_state == BT_RSSI_STATE_HIGH) || + (bt_rssi_state == BT_RSSI_STATE_STAY_HIGH)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi high\n"); + btdm8723.agc_table_en = true; + btdm8723.adc_back_off_on = true; + btdm8723.sw_dac_swing_on = false; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi low\n"); + btdm8723.agc_table_en = false; + btdm8723.adc_back_off_on = false; + btdm8723.sw_dac_swing_on = false; + } + + /* fw mechanism */ + btdm8723.ps_tdma_on = true; + if ((bt_rssi_state1 == BT_RSSI_STATE_HIGH) || + (bt_rssi_state1 == BT_RSSI_STATE_STAY_HIGH)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi-1 high\n"); + /* only rssi high we need to do this, + * when rssi low, the value will modified by fw + */ + rtl_write_byte(rtlpriv, 0x883, 0x40); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x81; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Wifi rssi-1 low\n"); + if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } else if (bt_tx_rx_cnt_lvl == BT_TXRX_CNT_LEVEL_1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters >= 1200 && < 1400\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xa; + btdm8723.ps_tdma_byte[2] = 0xa; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT TxRx Counters < 1200\n"); + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0xf; + btdm8723.ps_tdma_byte[2] = 0xf; + btdm8723.ps_tdma_byte[3] = 0x0; + btdm8723.ps_tdma_byte[4] = 0x80; + } + } + } + + if (rtl8723ae_dm_bt_need_to_dec_bt_pwr(hw)) + btdm8723.dec_bt_pwr = true; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT btInqPageStartTime = 0x%x, btTxRxCntLvl = %d\n", + rtlhal->hal_coex_8723.bt_inq_page_start_time, + bt_tx_rx_cnt_lvl); + + if ((rtlhal->hal_coex_8723.bt_inq_page_start_time) || + (BT_TXRX_CNT_LEVEL_3 == bt_tx_rx_cnt_lvl)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], Set BT inquiry / page scan 0x3a setting\n"); + btdm8723.ps_tdma_on = true; + btdm8723.ps_tdma_byte[0] = 0xa3; + btdm8723.ps_tdma_byte[1] = 0x5; + btdm8723.ps_tdma_byte[2] = 0x5; + btdm8723.ps_tdma_byte[3] = 0x83; + btdm8723.ps_tdma_byte[4] = 0x80; + } + + if (rtl8723ae_dm_bt_is_coexist_state_changed(hw)) + rtl8723ae_dm_bt_set_bt_dm(hw, &btdm8723); +} + +static void rtl8723ae_dm_bt_inq_page_monitor(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u32 cur_time = jiffies; + + if (rtlhal->hal_coex_8723.c2h_bt_inquiry_page) { + /* bt inquiry or page is started. */ + if (rtlhal->hal_coex_8723.bt_inq_page_start_time == 0) { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_BT_INQ_PAGE; + rtlhal->hal_coex_8723.bt_inq_page_start_time = cur_time; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT Inquiry/page is started at time : 0x%x\n", + rtlhal->hal_coex_8723.bt_inq_page_start_time); + } + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT Inquiry/page started time : 0x%x, cur_time : 0x%x\n", + rtlhal->hal_coex_8723.bt_inq_page_start_time, cur_time); + + if (rtlhal->hal_coex_8723.bt_inq_page_start_time) { + if ((((long)cur_time - + (long)rtlhal->hal_coex_8723.bt_inq_page_start_time) / HZ) >= + 10) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BT Inquiry/page >= 10sec!!!"); + rtlhal->hal_coex_8723.bt_inq_page_start_time = 0; + rtlpcipriv->bt_coexist.cstate &= + ~BT_COEX_STATE_BT_INQ_PAGE; + } + } +} + +static void rtl8723ae_dm_bt_reset_action_profile_state(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + rtlpcipriv->bt_coexist.cstate &= + ~(BT_COEX_STATE_PROFILE_HID | BT_COEX_STATE_PROFILE_A2DP | + BT_COEX_STATE_PROFILE_PAN | BT_COEX_STATE_PROFILE_SCO); + + rtlpcipriv->bt_coexist.cstate &= + ~(BT_COEX_STATE_BTINFO_COMMON | + BT_COEX_STATE_BTINFO_B_HID_SCOESCO | + BT_COEX_STATE_BTINFO_B_FTP_A2DP); +} + +static void _rtl8723ae_dm_bt_coexist_2_ant(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 bt_retry_cnt; + u8 bt_info_original; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex] Get bt info by fw!!\n"); + + _rtl8723_dm_bt_check_wifi_state(hw); + + if (rtlhal->hal_coex_8723.c2h_bt_info_req_sent) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex] c2h for btInfo not rcvd yet!!\n"); + } + + bt_retry_cnt = rtlhal->hal_coex_8723.bt_retry_cnt; + bt_info_original = rtlhal->hal_coex_8723.c2h_bt_info_original; + + /* when bt inquiry or page scan, we have to set h2c 0x25 + * ignore wlanact for continuous 4x2secs + */ + rtl8723ae_dm_bt_inq_page_monitor(hw); + rtl8723ae_dm_bt_reset_action_profile_state(hw); + + if (rtl8723ae_dm_bt_is_2_ant_common_action(hw)) { + rtlpcipriv->bt_coexist.bt_profile_case = BT_COEX_MECH_COMMON; + rtlpcipriv->bt_coexist.bt_profile_action = BT_COEX_MECH_COMMON; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Action 2-Ant common.\n"); + } else { + if ((bt_info_original & BTINFO_B_HID) || + (bt_info_original & BTINFO_B_SCO_BUSY) || + (bt_info_original & BTINFO_B_SCO_ESCO)) { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_BTINFO_B_HID_SCOESCO; + rtlpcipriv->bt_coexist.bt_profile_case = + BT_COEX_MECH_HID_SCO_ESCO; + rtlpcipriv->bt_coexist.bt_profile_action = + BT_COEX_MECH_HID_SCO_ESCO; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BTInfo: bHid|bSCOBusy|bSCOeSCO\n"); + rtl8723ae_dm_bt_2_ant_hid_sco_esco(hw); + } else if ((bt_info_original & BTINFO_B_FTP) || + (bt_info_original & BTINFO_B_A2DP)) { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_BTINFO_B_FTP_A2DP; + rtlpcipriv->bt_coexist.bt_profile_case = + BT_COEX_MECH_FTP_A2DP; + rtlpcipriv->bt_coexist.bt_profile_action = + BT_COEX_MECH_FTP_A2DP; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "BTInfo: bFTP|bA2DP\n"); + rtl8723ae_dm_bt_2_ant_fta2dp(hw); + } else { + rtlpcipriv->bt_coexist.cstate |= + BT_COEX_STATE_BTINFO_B_HID_SCOESCO; + rtlpcipriv->bt_coexist.bt_profile_case = + BT_COEX_MECH_NONE; + rtlpcipriv->bt_coexist.bt_profile_action = + BT_COEX_MECH_NONE; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], BTInfo: undefined case!!!!\n"); + rtl8723ae_dm_bt_2_ant_hid_sco_esco(hw); + } + } +} + +static void _rtl8723ae_dm_bt_coexist_1_ant(struct ieee80211_hw *hw) +{ +} + +void rtl8723ae_dm_bt_hw_coex_all_off_8723a(struct ieee80211_hw *hw) +{ + rtl8723ae_dm_bt_set_coex_table(hw, 0x5a5aaaaa, 0xcc, 0x3); + rtl8723ae_dm_bt_set_hw_pta_mode(hw, true); +} + +void rtl8723ae_dm_bt_fw_coex_all_off_8723a(struct ieee80211_hw *hw) +{ + rtl8723ae_dm_bt_set_fw_ignore_wlan_act(hw, false); + rtl8723ae_dm_bt_set_fw_3a(hw, 0x0, 0x0, 0x0, 0x8, 0x0); + rtl8723ae_dm_bt_set_fw_2_ant_hid(hw, false, false); + rtl8723ae_dm_bt_set_fw_tra_tdma_ctrl(hw, false, + TDMA_2ANT, TDMA_NAV_OFF); + rtl8723ae_dm_bt_set_fw_tdma_ctrl(hw, false, TDMA_2ANT, + TDMA_NAV_OFF, TDMA_DAC_SWING_OFF); + rtl8723ae_dm_bt_set_fw_dac_swing_level(hw, 0); + rtl8723ae_dm_bt_set_fw_bt_hid_info(hw, false); + rtl8723ae_dm_bt_set_fw_bt_retry_index(hw, 2); + rtl8723ae_dm_bt_set_fw_wlan_act(hw, 0x10, 0x10); + rtl8723ae_dm_bt_set_fw_dec_bt_pwr(hw, false); +} + +void rtl8723ae_dm_bt_sw_coex_all_off_8723a(struct ieee80211_hw *hw) +{ + rtl8723ae_dm_bt_agc_table(hw, BT_AGCTABLE_OFF); + rtl8723ae_dm_bt_bback_off_level(hw, BT_BB_BACKOFF_OFF); + rtl8723ae_dm_bt_reject_ap_aggregated_packet(hw, false); + + rtl8723ae_bt_set_penalty_tx_rate_adap(hw, BT_TX_RATE_ADAPTIVE_NORMAL); + rtl8723ae_dm_bt_set_sw_rf_rx_lpf_corner(hw, BT_RF_RX_LPF_CORNER_RESUME); + rtl8723ae_dm_bt_set_sw_full_time_dac_swing(hw, false, 0xc0); +} + +static void rtl8723ae_dm_bt_query_bt_information(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + u8 h2c_parameter[1] = {0}; + + rtlhal->hal_coex_8723.c2h_bt_info_req_sent = true; + + h2c_parameter[0] |= BIT(0); + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "Query Bt information, write 0x38 = 0x%x\n", + h2c_parameter[0]); + + rtl8723ae_fill_h2c_cmd(hw, 0x38, 1, h2c_parameter); +} + +static void rtl8723ae_dm_bt_bt_hw_counters_monitor(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u32 reg_htx_rx, reg_ltx_rx, u32_tmp; + u32 reg_htx, reg_hrx, reg_ltx, reg_lrx; + + reg_htx_rx = REG_HIGH_PRIORITY_TXRX; + reg_ltx_rx = REG_LOW_PRIORITY_TXRX; + + u32_tmp = rtl_read_dword(rtlpriv, reg_htx_rx); + reg_htx = u32_tmp & MASKLWORD; + reg_hrx = (u32_tmp & MASKHWORD)>>16; + + u32_tmp = rtl_read_dword(rtlpriv, reg_ltx_rx); + reg_ltx = u32_tmp & MASKLWORD; + reg_lrx = (u32_tmp & MASKHWORD)>>16; + + if (rtlpcipriv->bt_coexist.lps_counter > 1) { + reg_htx %= rtlpcipriv->bt_coexist.lps_counter; + reg_hrx %= rtlpcipriv->bt_coexist.lps_counter; + reg_ltx %= rtlpcipriv->bt_coexist.lps_counter; + reg_lrx %= rtlpcipriv->bt_coexist.lps_counter; + } + + rtlhal->hal_coex_8723.high_priority_tx = reg_htx; + rtlhal->hal_coex_8723.high_priority_rx = reg_hrx; + rtlhal->hal_coex_8723.low_priority_tx = reg_ltx; + rtlhal->hal_coex_8723.low_priority_rx = reg_lrx; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "High Priority Tx/Rx (reg 0x%x)=%x(%d)/%x(%d)\n", + reg_htx_rx, reg_htx, reg_htx, reg_hrx, reg_hrx); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "Low Priority Tx/Rx (reg 0x%x)=%x(%d)/%x(%d)\n", + reg_ltx_rx, reg_ltx, reg_ltx, reg_lrx, reg_lrx); + rtlpcipriv->bt_coexist.lps_counter = 0; +} + +static void rtl8723ae_dm_bt_bt_enable_disable_check(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + bool bt_alife = true; + + if (rtlhal->hal_coex_8723.high_priority_tx == 0 && + rtlhal->hal_coex_8723.high_priority_rx == 0 && + rtlhal->hal_coex_8723.low_priority_tx == 0 && + rtlhal->hal_coex_8723.low_priority_rx == 0) + bt_alife = false; + if (rtlhal->hal_coex_8723.high_priority_tx == 0xeaea && + rtlhal->hal_coex_8723.high_priority_rx == 0xeaea && + rtlhal->hal_coex_8723.low_priority_tx == 0xeaea && + rtlhal->hal_coex_8723.low_priority_rx == 0xeaea) + bt_alife = false; + if (rtlhal->hal_coex_8723.high_priority_tx == 0xffff && + rtlhal->hal_coex_8723.high_priority_rx == 0xffff && + rtlhal->hal_coex_8723.low_priority_tx == 0xffff && + rtlhal->hal_coex_8723.low_priority_rx == 0xffff) + bt_alife = false; + if (bt_alife) { + rtlpcipriv->bt_coexist.bt_active_zero_cnt = 0; + rtlpcipriv->bt_coexist.cur_bt_disabled = false; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "8723A BT is enabled !!\n"); + } else { + rtlpcipriv->bt_coexist.bt_active_zero_cnt++; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "8723A bt all counters = 0, %d times!!\n", + rtlpcipriv->bt_coexist.bt_active_zero_cnt); + if (rtlpcipriv->bt_coexist.bt_active_zero_cnt >= 2) { + rtlpcipriv->bt_coexist.cur_bt_disabled = true; + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "8723A BT is disabled !!\n"); + } + } + if (rtlpcipriv->bt_coexist.pre_bt_disabled != + rtlpcipriv->bt_coexist.cur_bt_disabled) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "8723A BT is from %s to %s!!\n", + (rtlpcipriv->bt_coexist.pre_bt_disabled ? + "disabled" : "enabled"), + (rtlpcipriv->bt_coexist.cur_bt_disabled ? + "disabled" : "enabled")); + rtlpcipriv->bt_coexist.pre_bt_disabled + = rtlpcipriv->bt_coexist.cur_bt_disabled; + } +} + + +void rtl8723ae_dm_bt_coexist_8723(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + + rtl8723ae_dm_bt_query_bt_information(hw); + rtl8723ae_dm_bt_bt_hw_counters_monitor(hw); + rtl8723ae_dm_bt_bt_enable_disable_check(hw); + + if (rtlpcipriv->bt_coexist.bt_ant_num == ANT_X2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], 2 Ant mechanism\n"); + _rtl8723ae_dm_bt_coexist_2_ant(hw); + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "[BTCoex], 1 Ant mechanism\n"); + _rtl8723ae_dm_bt_coexist_1_ant(hw); + } + + if (!rtl8723ae_dm_bt_is_same_coexist_state(hw)) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTCoex], Coexist State[bitMap] change from 0x%x%8x to 0x%x%8x\n", + rtlpcipriv->bt_coexist.previous_state_h, + rtlpcipriv->bt_coexist.previous_state, + rtlpcipriv->bt_coexist.cstate_h, + rtlpcipriv->bt_coexist.cstate); + rtlpcipriv->bt_coexist.previous_state + = rtlpcipriv->bt_coexist.cstate; + rtlpcipriv->bt_coexist.previous_state_h + = rtlpcipriv->bt_coexist.cstate_h; + } +} + +static void rtl8723ae_dm_bt_parse_bt_info(struct ieee80211_hw *hw, + u8 *tmbuf, u8 len) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtlpriv); + struct rtl_pci_priv *rtlpcipriv = rtl_pcipriv(hw); + u8 bt_info; + u8 i; + + rtlhal->hal_coex_8723.c2h_bt_info_req_sent = false; + rtlhal->hal_coex_8723.bt_retry_cnt = 0; + for (i = 0; i < len; i++) { + if (i == 0) + rtlhal->hal_coex_8723.c2h_bt_info_original = tmbuf[i]; + else if (i == 1) + rtlhal->hal_coex_8723.bt_retry_cnt = tmbuf[i]; + if (i == len-1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "0x%2x]", tmbuf[i]); + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "0x%2x, ", tmbuf[i]); + } + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "BT info bt_info (Data)= 0x%x\n", + rtlhal->hal_coex_8723.c2h_bt_info_original); + bt_info = rtlhal->hal_coex_8723.c2h_bt_info_original; + + if (bt_info & BIT(2)) + rtlhal->hal_coex_8723.c2h_bt_inquiry_page = true; + else + rtlhal->hal_coex_8723.c2h_bt_inquiry_page = false; + + if (bt_info & BTINFO_B_CONNECTION) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTC2H], BTInfo: bConnect=true\n"); + rtlpcipriv->bt_coexist.bt_busy = true; + rtlpcipriv->bt_coexist.cstate &= ~BT_COEX_STATE_BT_IDLE; + } else { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_DMESG, + "[BTC2H], BTInfo: bConnect=false\n"); + rtlpcipriv->bt_coexist.bt_busy = false; + rtlpcipriv->bt_coexist.cstate |= BT_COEX_STATE_BT_IDLE; + } +} +void rtl_8723e_c2h_command_handle(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct c2h_evt_hdr c2h_event; + u8 *ptmbuf; + u8 index; + u8 u1tmp; + + memset(&c2h_event, 0, sizeof(c2h_event)); + u1tmp = rtl_read_byte(rtlpriv, REG_C2HEVT_MSG_NORMAL); + RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, + "&&&&&&: REG_C2HEVT_MSG_NORMAL is 0x%x\n", u1tmp); + c2h_event.cmd_id = u1tmp & 0xF; + c2h_event.cmd_len = (u1tmp & 0xF0) >> 4; + c2h_event.cmd_seq = rtl_read_byte(rtlpriv, REG_C2HEVT_MSG_NORMAL + 1); + RT_TRACE(rtlpriv, COMP_FW, DBG_DMESG, + "cmd_id: %d, cmd_len: %d, cmd_seq: %d\n", + c2h_event.cmd_id , c2h_event.cmd_len, c2h_event.cmd_seq); + u1tmp = rtl_read_byte(rtlpriv, 0x01AF); + if (u1tmp == C2H_EVT_HOST_CLOSE) { + return; + } else if (u1tmp != C2H_EVT_FW_CLOSE) { + rtl_write_byte(rtlpriv, 0x1AF, 0x00); + return; + } + ptmbuf = kmalloc(c2h_event.cmd_len, GFP_KERNEL); + if (ptmbuf == NULL) { + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "malloc cmd buf failed\n"); + return; + } + + /* Read the content */ + for (index = 0; index < c2h_event.cmd_len; index++) + ptmbuf[index] = rtl_read_byte(rtlpriv, REG_C2HEVT_MSG_NORMAL + + 2 + index); + + switch (c2h_event.cmd_id) { + case C2H_BT_RSSI: + break; + + case C2H_BT_OP_MODE: + break; + + case BT_INFO: + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "BT info Byte[0] (ID) is 0x%x\n", c2h_event.cmd_id); + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "BT info Byte[1] (Seq) is 0x%x\n", c2h_event.cmd_seq); + RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, + "BT info Byte[2] (Data)= 0x%x\n", ptmbuf[0]); + + rtl8723ae_dm_bt_parse_bt_info(hw, ptmbuf, c2h_event.cmd_len); + break; + default: + break; + } + kfree(ptmbuf); + + rtl_write_byte(rtlpriv, 0x01AF, C2H_EVT_HOST_CLOSE); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.h b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.h new file mode 100644 index 00000000000..4325ecd58f0 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hal_btc.h @@ -0,0 +1,151 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_HAL_BTC_H__ +#define __RTL8723E_HAL_BTC_H__ + +#include "../wifi.h" +#include "btc.h" +#include "hal_bt_coexist.h" + +#define BT_TXRX_CNT_THRES_1 1200 +#define BT_TXRX_CNT_THRES_2 1400 +#define BT_TXRX_CNT_THRES_3 3000 +#define BT_TXRX_CNT_LEVEL_0 0 /* < 1200 */ +#define BT_TXRX_CNT_LEVEL_1 1 /* >= 1200 && < 1400 */ +#define BT_TXRX_CNT_LEVEL_2 2 /* >= 1400 */ +#define BT_TXRX_CNT_LEVEL_3 3 + +/* TDMA mode definition */ +#define TDMA_2ANT 0 +#define TDMA_1ANT 1 +#define TDMA_NAV_OFF 0 +#define TDMA_NAV_ON 1 +#define TDMA_DAC_SWING_OFF 0 +#define TDMA_DAC_SWING_ON 1 + +/* PTA mode related definition */ +#define BT_PTA_MODE_OFF 0 +#define BT_PTA_MODE_ON 1 + +/* Penalty Tx Rate Adaptive */ +#define BT_TX_RATE_ADAPTIVE_NORMAL 0 +#define BT_TX_RATE_ADAPTIVE_LOW_PENALTY 1 + +/* RF Corner */ +#define BT_RF_RX_LPF_CORNER_RESUME 0 +#define BT_RF_RX_LPF_CORNER_SHRINK 1 + +#define C2H_EVT_HOST_CLOSE 0x00 +#define C2H_EVT_FW_CLOSE 0xFF + +enum bt_traffic_mode { + BT_MOTOR_EXT_BE = 0x00, + BT_MOTOR_EXT_GUL = 0x01, + BT_MOTOR_EXT_GUB = 0x02, + BT_MOTOR_EXT_GULB = 0x03 +}; + +enum bt_traffic_mode_profile { + BT_PROFILE_NONE, + BT_PROFILE_A2DP, + BT_PROFILE_PAN, + BT_PROFILE_HID, + BT_PROFILE_SCO +}; + +enum hci_ext_bt_operation { + HCI_BT_OP_NONE = 0x0, + HCI_BT_OP_INQUIRE_START = 0x1, + HCI_BT_OP_INQUIRE_FINISH = 0x2, + HCI_BT_OP_PAGING_START = 0x3, + HCI_BT_OP_PAGING_SUCCESS = 0x4, + HCI_BT_OP_PAGING_UNSUCCESS = 0x5, + HCI_BT_OP_PAIRING_START = 0x6, + HCI_BT_OP_PAIRING_FINISH = 0x7, + HCI_BT_OP_BT_DEV_ENABLE = 0x8, + HCI_BT_OP_BT_DEV_DISABLE = 0x9, + HCI_BT_OP_MAX, +}; + +enum bt_spec { + BT_SPEC_1_0_b = 0x00, + BT_SPEC_1_1 = 0x01, + BT_SPEC_1_2 = 0x02, + BT_SPEC_2_0_EDR = 0x03, + BT_SPEC_2_1_EDR = 0x04, + BT_SPEC_3_0_HS = 0x05, + BT_SPEC_4_0 = 0x06 +}; + +struct c2h_evt_hdr { + u8 cmd_id; + u8 cmd_len; + u8 cmd_seq; +}; + +enum bt_state { + BT_INFO_STATE_DISABLED = 0, + BT_INFO_STATE_NO_CONNECTION = 1, + BT_INFO_STATE_CONNECT_IDLE = 2, + BT_INFO_STATE_INQ_OR_PAG = 3, + BT_INFO_STATE_ACL_ONLY_BUSY = 4, + BT_INFO_STATE_SCO_ONLY_BUSY = 5, + BT_INFO_STATE_ACL_SCO_BUSY = 6, + BT_INFO_STATE_HID_BUSY = 7, + BT_INFO_STATE_HID_SCO_BUSY = 8, + BT_INFO_STATE_MAX = 7 +}; + +enum rtl8723ae_c2h_evt { + C2H_DBG = 0, + C2H_TSF = 1, + C2H_AP_RPT_RSP = 2, + C2H_CCX_TX_RPT = 3, /* The FW notify the report of the specific */ + /* tx packet. */ + C2H_BT_RSSI = 4, + C2H_BT_OP_MODE = 5, + C2H_HW_INFO_EXCH = 10, + C2H_C2H_H2C_TEST = 11, + BT_INFO = 12, + MAX_C2HEVENT +}; + +void rtl8723ae_dm_bt_fw_coex_all_off_8723a(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_sw_coex_all_off_8723a(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_hw_coex_all_off_8723a(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_coexist_8723(struct ieee80211_hw *hw); +void rtl8723ae_dm_bt_set_bt_dm(struct ieee80211_hw *hw, + struct btdm_8723 *p_btdm); +void rtl_8723e_c2h_command_handle(struct ieee80211_hw *hw); +void rtl_8723e_bt_wifi_media_status_notify(struct ieee80211_hw *hw, + bool mstatus); +void rtl8723ae_bt_coex_off_before_lps(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c new file mode 100644 index 00000000000..0a8c03863fb --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.c @@ -0,0 +1,2380 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../efuse.h" +#include "../base.h" +#include "../regd.h" +#include "../cam.h" +#include "../ps.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "dm.h" +#include "fw.h" +#include "led.h" +#include "hw.h" +#include "pwrseqcmd.h" +#include "pwrseq.h" +#include "btc.h" + +static void _rtl8723ae_set_bcn_ctrl_reg(struct ieee80211_hw *hw, + u8 set_bits, u8 clear_bits) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtlpci->reg_bcn_ctrl_val |= set_bits; + rtlpci->reg_bcn_ctrl_val &= ~clear_bits; + + rtl_write_byte(rtlpriv, REG_BCN_CTRL, (u8) rtlpci->reg_bcn_ctrl_val); +} + +static void _rtl8723ae_stop_tx_beacon(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp1byte; + + tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte & (~BIT(6))); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0x64); + tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); + tmp1byte &= ~(BIT(0)); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); +} + +static void _rtl8723ae_resume_tx_beacon(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmp1byte; + + tmp1byte = rtl_read_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2); + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, tmp1byte | BIT(6)); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); + tmp1byte = rtl_read_byte(rtlpriv, REG_TBTT_PROHIBIT + 2); + tmp1byte |= BIT(1); + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 2, tmp1byte); +} + +static void _rtl8723ae_enable_bcn_sufunc(struct ieee80211_hw *hw) +{ + _rtl8723ae_set_bcn_ctrl_reg(hw, 0, BIT(1)); +} + +static void _rtl8723ae_disable_bcn_sufunc(struct ieee80211_hw *hw) +{ + _rtl8723ae_set_bcn_ctrl_reg(hw, BIT(1), 0); +} + +void rtl8723ae_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + switch (variable) { + case HW_VAR_RCR: + *((u32 *) (val)) = rtlpci->receive_config; + break; + case HW_VAR_RF_STATE: + *((enum rf_pwrstate *)(val)) = ppsc->rfpwr_state; + break; + case HW_VAR_FWLPS_RF_ON:{ + enum rf_pwrstate rfState; + u32 val_rcr; + + rtlpriv->cfg->ops->get_hw_reg(hw, + HW_VAR_RF_STATE, + (u8 *) (&rfState)); + if (rfState == ERFOFF) { + *((bool *) (val)) = true; + } else { + val_rcr = rtl_read_dword(rtlpriv, REG_RCR); + val_rcr &= 0x00070000; + if (val_rcr) + *((bool *) (val)) = false; + else + *((bool *) (val)) = true; + } + break; } + case HW_VAR_FW_PSMODE_STATUS: + *((bool *) (val)) = ppsc->fw_current_inpsmode; + break; + case HW_VAR_CORRECT_TSF:{ + u64 tsf; + u32 *ptsf_low = (u32 *)&tsf; + u32 *ptsf_high = ((u32 *)&tsf) + 1; + + *ptsf_high = rtl_read_dword(rtlpriv, (REG_TSFTR + 4)); + *ptsf_low = rtl_read_dword(rtlpriv, REG_TSFTR); + + *((u64 *) (val)) = tsf; + + break; } + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } +} + +void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + u8 idx; + + switch (variable) { + case HW_VAR_ETHER_ADDR: + for (idx = 0; idx < ETH_ALEN; idx++) { + rtl_write_byte(rtlpriv, (REG_MACID + idx), + val[idx]); + } + break; + case HW_VAR_BASIC_RATE:{ + u16 rate_cfg = ((u16 *) val)[0]; + u8 rate_index = 0; + rate_cfg = rate_cfg & 0x15f; + rate_cfg |= 0x01; + rtl_write_byte(rtlpriv, REG_RRSR, rate_cfg & 0xff); + rtl_write_byte(rtlpriv, REG_RRSR + 1, + (rate_cfg >> 8) & 0xff); + while (rate_cfg > 0x1) { + rate_cfg = (rate_cfg >> 1); + rate_index++; + } + rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, + rate_index); + break; } + case HW_VAR_BSSID: + for (idx = 0; idx < ETH_ALEN; idx++) { + rtl_write_byte(rtlpriv, (REG_BSSID + idx), + val[idx]); + } + break; + case HW_VAR_SIFS: + rtl_write_byte(rtlpriv, REG_SIFS_CTX + 1, val[0]); + rtl_write_byte(rtlpriv, REG_SIFS_TRX + 1, val[1]); + + rtl_write_byte(rtlpriv, REG_SPEC_SIFS + 1, val[0]); + rtl_write_byte(rtlpriv, REG_MAC_SPEC_SIFS + 1, val[0]); + + if (!mac->ht_enable) + rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, + 0x0e0e); + else + rtl_write_word(rtlpriv, REG_RESP_SIFS_OFDM, + *((u16 *) val)); + break; + case HW_VAR_SLOT_TIME:{ + u8 e_aci; + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "HW_VAR_SLOT_TIME %x\n", val[0]); + + rtl_write_byte(rtlpriv, REG_SLOT, val[0]); + + for (e_aci = 0; e_aci < AC_MAX; e_aci++) { + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_AC_PARAM, + (u8 *) (&e_aci)); + } + break; } + case HW_VAR_ACK_PREAMBLE:{ + u8 reg_tmp; + u8 short_preamble = (bool) (*(u8 *) val); + reg_tmp = (mac->cur_40_prime_sc) << 5; + if (short_preamble) + reg_tmp |= 0x80; + + rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_tmp); + break; } + case HW_VAR_AMPDU_MIN_SPACE:{ + u8 min_spacing_to_set; + u8 sec_min_space; + + min_spacing_to_set = *((u8 *) val); + if (min_spacing_to_set <= 7) { + sec_min_space = 0; + + if (min_spacing_to_set < sec_min_space) + min_spacing_to_set = sec_min_space; + + mac->min_space_cfg = ((mac->min_space_cfg & + 0xf8) | + min_spacing_to_set); + + *val = min_spacing_to_set; + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_AMPDU_MIN_SPACE: %#x\n", + mac->min_space_cfg); + + rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, + mac->min_space_cfg); + } + break; } + case HW_VAR_SHORTGI_DENSITY:{ + u8 density_to_set; + + density_to_set = *((u8 *) val); + mac->min_space_cfg |= (density_to_set << 3); + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_SHORTGI_DENSITY: %#x\n", + mac->min_space_cfg); + + rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, + mac->min_space_cfg); + + break; } + case HW_VAR_AMPDU_FACTOR:{ + u8 regtoset_normal[4] = {0x41, 0xa8, 0x72, 0xb9}; + u8 regtoset_bt[4] = {0x31, 0x74, 0x42, 0x97}; + u8 factor_toset; + u8 *p_regtoset = NULL; + u8 index; + + if ((pcipriv->bt_coexist.bt_coexistence) && + (pcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) + p_regtoset = regtoset_bt; + else + p_regtoset = regtoset_normal; + + factor_toset = *((u8 *) val); + if (factor_toset <= 3) { + factor_toset = (1 << (factor_toset + 2)); + if (factor_toset > 0xf) + factor_toset = 0xf; + + for (index = 0; index < 4; index++) { + if ((p_regtoset[index] & 0xf0) > + (factor_toset << 4)) + p_regtoset[index] = + (p_regtoset[index] & 0x0f) | + (factor_toset << 4); + + if ((p_regtoset[index] & 0x0f) > + factor_toset) + p_regtoset[index] = + (p_regtoset[index] & 0xf0) | + (factor_toset); + + rtl_write_byte(rtlpriv, + (REG_AGGLEN_LMT + index), + p_regtoset[index]); + + } + + RT_TRACE(rtlpriv, COMP_MLME, DBG_LOUD, + "Set HW_VAR_AMPDU_FACTOR: %#x\n", + factor_toset); + } + break; } + case HW_VAR_AC_PARAM:{ + u8 e_aci = *((u8 *) val); + rtl8723ae_dm_init_edca_turbo(hw); + + if (rtlpci->acm_method != eAcmWay2_SW) + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_ACM_CTRL, + (u8 *) (&e_aci)); + break; } + case HW_VAR_ACM_CTRL:{ + u8 e_aci = *((u8 *) val); + union aci_aifsn *p_aci_aifsn = + (union aci_aifsn *)(&(mac->ac[0].aifs)); + u8 acm = p_aci_aifsn->f.acm; + u8 acm_ctrl = rtl_read_byte(rtlpriv, REG_ACMHWCTRL); + + acm_ctrl |= ((rtlpci->acm_method == 2) ? 0x0 : 0x1); + + if (acm) { + switch (e_aci) { + case AC0_BE: + acm_ctrl |= AcmHw_BeqEn; + break; + case AC2_VI: + acm_ctrl |= AcmHw_ViqEn; + break; + case AC3_VO: + acm_ctrl |= AcmHw_VoqEn; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "HW_VAR_ACM_CTRL acm set failed: eACI is %d\n", + acm); + break; + } + } else { + switch (e_aci) { + case AC0_BE: + acm_ctrl &= (~AcmHw_BeqEn); + break; + case AC2_VI: + acm_ctrl &= (~AcmHw_ViqEn); + break; + case AC3_VO: + acm_ctrl &= (~AcmHw_BeqEn); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + } + + RT_TRACE(rtlpriv, COMP_QOS, DBG_TRACE, + "SetHwReg8190pci(): [HW_VAR_ACM_CTRL] Write 0x%X\n", + acm_ctrl); + rtl_write_byte(rtlpriv, REG_ACMHWCTRL, acm_ctrl); + break; } + case HW_VAR_RCR: + rtl_write_dword(rtlpriv, REG_RCR, ((u32 *) (val))[0]); + rtlpci->receive_config = ((u32 *) (val))[0]; + break; + case HW_VAR_RETRY_LIMIT:{ + u8 retry_limit = ((u8 *) (val))[0]; + + rtl_write_word(rtlpriv, REG_RL, + retry_limit << RETRY_LIMIT_SHORT_SHIFT | + retry_limit << RETRY_LIMIT_LONG_SHIFT); + break; } + case HW_VAR_DUAL_TSF_RST: + rtl_write_byte(rtlpriv, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); + break; + case HW_VAR_EFUSE_BYTES: + rtlefuse->efuse_usedbytes = *((u16 *) val); + break; + case HW_VAR_EFUSE_USAGE: + rtlefuse->efuse_usedpercentage = *((u8 *) val); + break; + case HW_VAR_IO_CMD: + rtl8723ae_phy_set_io_cmd(hw, (*(enum io_type *)val)); + break; + case HW_VAR_WPA_CONFIG: + rtl_write_byte(rtlpriv, REG_SECCFG, *((u8 *) val)); + break; + case HW_VAR_SET_RPWM:{ + u8 rpwm_val; + + rpwm_val = rtl_read_byte(rtlpriv, REG_PCIE_HRPWM); + udelay(1); + + if (rpwm_val & BIT(7)) { + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, + (*(u8 *) val)); + } else { + rtl_write_byte(rtlpriv, REG_PCIE_HRPWM, + ((*(u8 *) val) | BIT(7))); + } + + break; } + case HW_VAR_H2C_FW_PWRMODE:{ + u8 psmode = (*(u8 *) val); + + if (psmode != FW_PS_ACTIVE_MODE) + rtl8723ae_dm_rf_saving(hw, true); + + rtl8723ae_set_fw_pwrmode_cmd(hw, (*(u8 *) val)); + break; } + case HW_VAR_FW_PSMODE_STATUS: + ppsc->fw_current_inpsmode = *((bool *) val); + break; + case HW_VAR_H2C_FW_JOINBSSRPT:{ + u8 mstatus = (*(u8 *) val); + u8 tmp_regcr, tmp_reg422; + bool recover = false; + + if (mstatus == RT_MEDIA_CONNECT) { + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_AID, NULL); + + tmp_regcr = rtl_read_byte(rtlpriv, REG_CR + 1); + rtl_write_byte(rtlpriv, REG_CR + 1, + (tmp_regcr | BIT(0))); + + _rtl8723ae_set_bcn_ctrl_reg(hw, 0, BIT(3)); + _rtl8723ae_set_bcn_ctrl_reg(hw, BIT(4), 0); + + tmp_reg422 = rtl_read_byte(rtlpriv, + REG_FWHW_TXQ_CTRL + 2); + if (tmp_reg422 & BIT(6)) + recover = true; + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, + tmp_reg422 & (~BIT(6))); + + rtl8723ae_set_fw_rsvdpagepkt(hw, 0); + + _rtl8723ae_set_bcn_ctrl_reg(hw, BIT(3), 0); + _rtl8723ae_set_bcn_ctrl_reg(hw, 0, BIT(4)); + + if (recover) + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 2, + tmp_reg422); + + rtl_write_byte(rtlpriv, REG_CR + 1, + (tmp_regcr & ~(BIT(0)))); + } + rtl8723ae_set_fw_joinbss_report_cmd(hw, (*(u8 *) val)); + + break; } + case HW_VAR_AID:{ + u16 u2btmp; + u2btmp = rtl_read_word(rtlpriv, REG_BCN_PSR_RPT); + u2btmp &= 0xC000; + rtl_write_word(rtlpriv, REG_BCN_PSR_RPT, (u2btmp | + mac->assoc_id)); + break; } + case HW_VAR_CORRECT_TSF:{ + u8 btype_ibss = ((u8 *) (val))[0]; + + if (btype_ibss == true) + _rtl8723ae_stop_tx_beacon(hw); + + _rtl8723ae_set_bcn_ctrl_reg(hw, 0, BIT(3)); + + rtl_write_dword(rtlpriv, REG_TSFTR, + (u32) (mac->tsf & 0xffffffff)); + rtl_write_dword(rtlpriv, REG_TSFTR + 4, + (u32) ((mac->tsf >> 32) & 0xffffffff)); + + _rtl8723ae_set_bcn_ctrl_reg(hw, BIT(3), 0); + + if (btype_ibss == true) + _rtl8723ae_resume_tx_beacon(hw); + break; } + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } +} + +static bool _rtl8723ae_llt_write(struct ieee80211_hw *hw, u32 address, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + bool status = true; + long count = 0; + u32 value = _LLT_INIT_ADDR(address) | + _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS); + + rtl_write_dword(rtlpriv, REG_LLT_INIT, value); + + do { + value = rtl_read_dword(rtlpriv, REG_LLT_INIT); + if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) + break; + + if (count > POLLING_LLT_THRESHOLD) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to polling write LLT done at address %d!\n", + address); + status = false; + break; + } + } while (++count); + + return status; +} + +static bool _rtl8723ae_llt_table_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + unsigned short i; + u8 txpktbuf_bndy; + u8 maxPage; + bool status; + u8 ubyte; + + maxPage = 255; + txpktbuf_bndy = 246; + + rtl_write_byte(rtlpriv, REG_CR, 0x8B); + + rtl_write_word(rtlpriv, REG_RQPN_NPQ, 0x0000); + + rtl_write_dword(rtlpriv, REG_RQPN, 0x80ac1c29); + rtl_write_byte(rtlpriv, REG_RQPN_NPQ, 0x03); + + rtl_write_dword(rtlpriv, REG_TRXFF_BNDY, (0x27FF0000 | txpktbuf_bndy)); + rtl_write_byte(rtlpriv, REG_TDECTRL + 1, txpktbuf_bndy); + + rtl_write_byte(rtlpriv, REG_TXPKTBUF_BCNQ_BDNY, txpktbuf_bndy); + rtl_write_byte(rtlpriv, REG_TXPKTBUF_MGQ_BDNY, txpktbuf_bndy); + + rtl_write_byte(rtlpriv, 0x45D, txpktbuf_bndy); + rtl_write_byte(rtlpriv, REG_PBP, 0x11); + rtl_write_byte(rtlpriv, REG_RX_DRVINFO_SZ, 0x4); + + for (i = 0; i < (txpktbuf_bndy - 1); i++) { + status = _rtl8723ae_llt_write(hw, i, i + 1); + if (true != status) + return status; + } + + status = _rtl8723ae_llt_write(hw, (txpktbuf_bndy - 1), 0xFF); + if (true != status) + return status; + + for (i = txpktbuf_bndy; i < maxPage; i++) { + status = _rtl8723ae_llt_write(hw, i, (i + 1)); + if (true != status) + return status; + } + + status = _rtl8723ae_llt_write(hw, maxPage, txpktbuf_bndy); + if (true != status) + return status; + + rtl_write_byte(rtlpriv, REG_CR, 0xff); + ubyte = rtl_read_byte(rtlpriv, REG_RQPN + 3); + rtl_write_byte(rtlpriv, REG_RQPN + 3, ubyte | BIT(7)); + + return true; +} + +static void _rtl8723ae_gen_refresh_led_state(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); + + if (rtlpriv->rtlhal.up_first_time) + return; + + if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) + rtl8723ae_sw_led_on(hw, pLed0); + else if (ppsc->rfoff_reason == RF_CHANGE_BY_INIT) + rtl8723ae_sw_led_on(hw, pLed0); + else + rtl8723ae_sw_led_off(hw, pLed0); +} + +static bool _rtl8712e_init_mac(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + unsigned char bytetmp; + unsigned short wordtmp; + u16 retry = 0; + u16 tmpu2b; + bool mac_func_enable; + + rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x00); + bytetmp = rtl_read_byte(rtlpriv, REG_CR); + if (bytetmp == 0xFF) + mac_func_enable = true; + else + mac_func_enable = false; + + + /* HW Power on sequence */ + if (!rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, Rtl8723_NIC_ENABLE_FLOW)) + return false; + + bytetmp = rtl_read_byte(rtlpriv, REG_PCIE_CTRL_REG+2); + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG+2, bytetmp | BIT(4)); + + /* eMAC time out function enable, 0x369[7]=1 */ + bytetmp = rtl_read_byte(rtlpriv, 0x369); + rtl_write_byte(rtlpriv, 0x369, bytetmp | BIT(7)); + + /* ePHY reg 0x1e bit[4]=1 using MDIO interface, + * we should do this before Enabling ASPM backdoor. + */ + do { + rtl_write_word(rtlpriv, 0x358, 0x5e); + udelay(100); + rtl_write_word(rtlpriv, 0x356, 0xc280); + rtl_write_word(rtlpriv, 0x354, 0xc290); + rtl_write_word(rtlpriv, 0x358, 0x3e); + udelay(100); + rtl_write_word(rtlpriv, 0x358, 0x5e); + udelay(100); + tmpu2b = rtl_read_word(rtlpriv, 0x356); + retry++; + } while (tmpu2b != 0xc290 && retry < 100); + + if (retry >= 100) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "InitMAC(): ePHY configure fail!!!\n"); + return false; + } + + rtl_write_word(rtlpriv, REG_CR, 0x2ff); + rtl_write_word(rtlpriv, REG_CR + 1, 0x06); + + if (!mac_func_enable) { + if (_rtl8723ae_llt_table_init(hw) == false) + return false; + } + + rtl_write_dword(rtlpriv, REG_HISR, 0xffffffff); + rtl_write_byte(rtlpriv, REG_HISRE, 0xff); + + rtl_write_word(rtlpriv, REG_TRXFF_BNDY + 2, 0x27ff); + + wordtmp = rtl_read_word(rtlpriv, REG_TRXDMA_CTRL) & 0xf; + wordtmp |= 0xF771; + rtl_write_word(rtlpriv, REG_TRXDMA_CTRL, wordtmp); + + rtl_write_byte(rtlpriv, REG_FWHW_TXQ_CTRL + 1, 0x1F); + rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); + rtl_write_word(rtlpriv, REG_RXFLTMAP2, 0xFFFF); + rtl_write_dword(rtlpriv, REG_TCR, rtlpci->transmit_config); + + rtl_write_byte(rtlpriv, 0x4d0, 0x0); + + rtl_write_dword(rtlpriv, REG_BCNQ_DESA, + ((u64) rtlpci->tx_ring[BEACON_QUEUE].dma) & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_MGQ_DESA, + (u64) rtlpci->tx_ring[MGNT_QUEUE].dma & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_VOQ_DESA, + (u64) rtlpci->tx_ring[VO_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_VIQ_DESA, + (u64) rtlpci->tx_ring[VI_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_BEQ_DESA, + (u64) rtlpci->tx_ring[BE_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_BKQ_DESA, + (u64) rtlpci->tx_ring[BK_QUEUE].dma & DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_HQ_DESA, + (u64) rtlpci->tx_ring[HIGH_QUEUE].dma & + DMA_BIT_MASK(32)); + rtl_write_dword(rtlpriv, REG_RX_DESA, + (u64) rtlpci->rx_ring[RX_MPDU_QUEUE].dma & + DMA_BIT_MASK(32)); + + rtl_write_byte(rtlpriv, REG_PCIE_CTRL_REG + 3, 0x74); + + rtl_write_dword(rtlpriv, REG_INT_MIG, 0); + + bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); + rtl_write_byte(rtlpriv, REG_APSD_CTRL, bytetmp & ~BIT(6)); + do { + retry++; + bytetmp = rtl_read_byte(rtlpriv, REG_APSD_CTRL); + } while ((retry < 200) && (bytetmp & BIT(7))); + + _rtl8723ae_gen_refresh_led_state(hw); + + rtl_write_dword(rtlpriv, REG_MCUTST_1, 0x0); + + return true; +} + +static void _rtl8723ae_hw_configure(struct ieee80211_hw *hw) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + u8 reg_bw_opmode; + u32 reg_ratr, reg_prsr; + + reg_bw_opmode = BW_OPMODE_20MHZ; + reg_ratr = RATE_ALL_CCK | RATE_ALL_OFDM_AG | + RATE_ALL_OFDM_1SS | RATE_ALL_OFDM_2SS; + reg_prsr = RATE_ALL_CCK | RATE_ALL_OFDM_AG; + + rtl_write_byte(rtlpriv, REG_INIRTS_RATE_SEL, 0x8); + + rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); + + rtl_write_dword(rtlpriv, REG_RRSR, reg_prsr); + + rtl_write_byte(rtlpriv, REG_SLOT, 0x09); + + rtl_write_byte(rtlpriv, REG_AMPDU_MIN_SPACE, 0x0); + + rtl_write_word(rtlpriv, REG_FWHW_TXQ_CTRL, 0x1F80); + + rtl_write_word(rtlpriv, REG_RL, 0x0707); + + rtl_write_dword(rtlpriv, REG_BAR_MODE_CTRL, 0x02012802); + + rtl_write_byte(rtlpriv, REG_HWSEQ_CTRL, 0xFF); + + rtl_write_dword(rtlpriv, REG_DARFRC, 0x01000000); + rtl_write_dword(rtlpriv, REG_DARFRC + 4, 0x07060504); + rtl_write_dword(rtlpriv, REG_RARFRC, 0x01000000); + rtl_write_dword(rtlpriv, REG_RARFRC + 4, 0x07060504); + + if ((pcipriv->bt_coexist.bt_coexistence) && + (pcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) + rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0x97427431); + else + rtl_write_dword(rtlpriv, REG_AGGLEN_LMT, 0xb972a841); + + rtl_write_byte(rtlpriv, REG_ATIMWND, 0x2); + + rtl_write_byte(rtlpriv, REG_BCN_MAX_ERR, 0xff); + + rtlpci->reg_bcn_ctrl_val = 0x1f; + rtl_write_byte(rtlpriv, REG_BCN_CTRL, rtlpci->reg_bcn_ctrl_val); + + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); + + rtl_write_byte(rtlpriv, REG_TBTT_PROHIBIT + 1, 0xff); + + rtl_write_byte(rtlpriv, REG_PIFS, 0x1C); + rtl_write_byte(rtlpriv, REG_AGGR_BREAK_TIME, 0x16); + + if ((pcipriv->bt_coexist.bt_coexistence) && + (pcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) { + rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); + rtl_write_word(rtlpriv, REG_PROT_MODE_CTRL, 0x0402); + } else { + rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); + rtl_write_word(rtlpriv, REG_NAV_PROT_LEN, 0x0020); + } + + if ((pcipriv->bt_coexist.bt_coexistence) && + (pcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4)) + rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x03086666); + else + rtl_write_dword(rtlpriv, REG_FAST_EDCA_CTRL, 0x086666); + + rtl_write_byte(rtlpriv, REG_ACKTO, 0x40); + + rtl_write_word(rtlpriv, REG_SPEC_SIFS, 0x1010); + rtl_write_word(rtlpriv, REG_MAC_SPEC_SIFS, 0x1010); + + rtl_write_word(rtlpriv, REG_SIFS_CTX, 0x1010); + + rtl_write_word(rtlpriv, REG_SIFS_TRX, 0x1010); + + rtl_write_dword(rtlpriv, REG_MAR, 0xffffffff); + rtl_write_dword(rtlpriv, REG_MAR + 4, 0xffffffff); + + rtl_write_dword(rtlpriv, 0x394, 0x1); +} + +static void _rtl8723ae_enable_aspm_back_door(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + rtl_write_byte(rtlpriv, 0x34b, 0x93); + rtl_write_word(rtlpriv, 0x350, 0x870c); + rtl_write_byte(rtlpriv, 0x352, 0x1); + + if (ppsc->support_backdoor) + rtl_write_byte(rtlpriv, 0x349, 0x1b); + else + rtl_write_byte(rtlpriv, 0x349, 0x03); + + rtl_write_word(rtlpriv, 0x350, 0x2718); + rtl_write_byte(rtlpriv, 0x352, 0x1); +} + +void rtl8723ae_enable_hw_security_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 sec_reg_value; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "PairwiseEncAlgorithm = %d GroupEncAlgorithm = %d\n", + rtlpriv->sec.pairwise_enc_algorithm, + rtlpriv->sec.group_enc_algorithm); + + if (rtlpriv->cfg->mod_params->sw_crypto || rtlpriv->sec.use_sw_sec) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "not open hw encryption\n"); + return; + } + + sec_reg_value = SCR_TxEncEnable | SCR_RxDecEnable; + + if (rtlpriv->sec.use_defaultkey) { + sec_reg_value |= SCR_TxUseDK; + sec_reg_value |= SCR_RxUseDK; + } + + sec_reg_value |= (SCR_RXBCUSEDK | SCR_TXBCUSEDK); + + rtl_write_byte(rtlpriv, REG_CR + 1, 0x02); + + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "The SECR-value %x\n", sec_reg_value); + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_WPA_CONFIG, &sec_reg_value); + +} + +int rtl8723ae_hw_init(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + bool rtstatus = true; + int err; + u8 tmp_u1b; + + rtlpriv->rtlhal.being_init_adapter = true; + rtlpriv->intf_ops->disable_aspm(hw); + rtstatus = _rtl8712e_init_mac(hw); + if (rtstatus != true) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Init MAC failed\n"); + err = 1; + return err; + } + + err = rtl8723ae_download_fw(hw); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Failed to download FW. Init HW without FW now..\n"); + err = 1; + rtlhal->fw_ready = false; + return err; + } else { + rtlhal->fw_ready = true; + } + + rtlhal->last_hmeboxnum = 0; + rtl8723ae_phy_mac_config(hw); + /* because the last function modifies RCR, we update + * rcr var here, or TP will be unstable as ther receive_config + * is wrong, RX RCR_ACRC32 will cause TP unstable & Rx + * RCR_APP_ICV will cause mac80211 unassoc for cisco 1252 + */ + rtlpci->receive_config = rtl_read_dword(rtlpriv, REG_RCR); + rtlpci->receive_config &= ~(RCR_ACRC32 | RCR_AICV); + rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); + + rtl8723ae_phy_bb_config(hw); + rtlphy->rf_mode = RF_OP_BY_SW_3WIRE; + rtl8723ae_phy_rf_config(hw); + if (IS_VENDOR_UMC_A_CUT(rtlhal->version)) { + rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G1, MASKDWORD, 0x30255); + rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G2, MASKDWORD, 0x50a00); + } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) { + rtl_set_rfreg(hw, RF90_PATH_A, 0x0C, MASKDWORD, 0x894AE); + rtl_set_rfreg(hw, RF90_PATH_A, 0x0A, MASKDWORD, 0x1AF31); + rtl_set_rfreg(hw, RF90_PATH_A, RF_IPA, MASKDWORD, 0x8F425); + rtl_set_rfreg(hw, RF90_PATH_A, RF_SYN_G2, MASKDWORD, 0x4F200); + rtl_set_rfreg(hw, RF90_PATH_A, RF_RCK1, MASKDWORD, 0x44053); + rtl_set_rfreg(hw, RF90_PATH_A, RF_RCK2, MASKDWORD, 0x80201); + } + rtlphy->rfreg_chnlval[0] = rtl_get_rfreg(hw, (enum radio_path)0, + RF_CHNLBW, RFREG_OFFSET_MASK); + rtlphy->rfreg_chnlval[1] = rtl_get_rfreg(hw, (enum radio_path)1, + RF_CHNLBW, RFREG_OFFSET_MASK); + rtl_set_bbreg(hw, RFPGA0_RFMOD, BCCKEN, 0x1); + rtl_set_bbreg(hw, RFPGA0_RFMOD, BOFDMEN, 0x1); + rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); + _rtl8723ae_hw_configure(hw); + rtl_cam_reset_all_entry(hw); + rtl8723ae_enable_hw_security_config(hw); + + ppsc->rfpwr_state = ERFON; + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_ETHER_ADDR, mac->mac_addr); + _rtl8723ae_enable_aspm_back_door(hw); + rtlpriv->intf_ops->enable_aspm(hw); + + rtl8723ae_bt_hw_init(hw); + + if (ppsc->rfpwr_state == ERFON) { + rtl8723ae_phy_set_rfpath_switch(hw, 1); + if (rtlphy->iqk_initialized) { + rtl8723ae_phy_iq_calibrate(hw, true); + } else { + rtl8723ae_phy_iq_calibrate(hw, false); + rtlphy->iqk_initialized = true; + } + + rtl8723ae_phy_lc_calibrate(hw); + } + + tmp_u1b = efuse_read_1byte(hw, 0x1FA); + if (!(tmp_u1b & BIT(0))) { + rtl_set_rfreg(hw, RF90_PATH_A, 0x15, 0x0F, 0x05); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "PA BIAS path A\n"); + } + + if (!(tmp_u1b & BIT(4))) { + tmp_u1b = rtl_read_byte(rtlpriv, 0x16) & 0x0F; + rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x80); + udelay(10); + rtl_write_byte(rtlpriv, 0x16, tmp_u1b | 0x90); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "under 1.5V\n"); + } + rtl8723ae_dm_init(hw); + rtlpriv->rtlhal.being_init_adapter = false; + return err; +} + +static enum version_8723e _rtl8723ae_read_chip_version(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + enum version_8723e version = 0x0000; + u32 value32; + + value32 = rtl_read_dword(rtlpriv, REG_SYS_CFG); + if (value32 & TRP_VAUX_EN) { + version = (enum version_8723e)(version | + ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0)); + /* RTL8723 with BT function. */ + version = (enum version_8723e)(version | + ((value32 & BT_FUNC) ? CHIP_8723 : 0)); + + } else { + /* Normal mass production chip. */ + version = (enum version_8723e) NORMAL_CHIP; + version = (enum version_8723e)(version | + ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0)); + /* RTL8723 with BT function. */ + version = (enum version_8723e)(version | + ((value32 & BT_FUNC) ? CHIP_8723 : 0)); + if (IS_CHIP_VENDOR_UMC(version)) + version = (enum version_8723e)(version | + ((value32 & CHIP_VER_RTL_MASK)));/* IC version (CUT) */ + if (IS_8723_SERIES(version)) { + value32 = rtl_read_dword(rtlpriv, REG_GPIO_OUTSTS); + /* ROM code version */ + version = (enum version_8723e)(version | + ((value32 & RF_RL_ID)>>20)); + } + } + + if (IS_8723_SERIES(version)) { + value32 = rtl_read_dword(rtlpriv, REG_MULTI_FUNC_CTRL); + rtlphy->polarity_ctl = ((value32 & WL_HWPDN_SL) ? + RT_POLARITY_HIGH_ACT : + RT_POLARITY_LOW_ACT); + } + switch (version) { + case VERSION_TEST_UMC_CHIP_8723: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Chip Version ID: VERSION_TEST_UMC_CHIP_8723.\n"); + break; + case VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Chip Version ID: VERSION_NORMAL_UMC_CHIP_8723_1T1R_A_CUT.\n"); + break; + case VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Chip Version ID: VERSION_NORMAL_UMC_CHIP_8723_1T1R_B_CUT.\n"); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Chip Version ID: Unknown. Bug?\n"); + break; + } + + if (IS_8723_SERIES(version)) + rtlphy->rf_type = RF_1T1R; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Chip RF Type: %s\n", + (rtlphy->rf_type == RF_2T2R) ? "RF_2T2R" : "RF_1T1R"); + + return version; +} + +static int _rtl8723ae_set_media_status(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 bt_msr = rtl_read_byte(rtlpriv, MSR) & 0xfc; + enum led_ctl_mode ledaction = LED_CTL_NO_LINK; + + rtl_write_dword(rtlpriv, REG_BCN_CTRL, 0); + RT_TRACE(rtlpriv, COMP_BEACON, DBG_LOUD, + "clear 0x550 when set HW_VAR_MEDIA_STATUS\n"); + + if (type == NL80211_IFTYPE_UNSPECIFIED || + type == NL80211_IFTYPE_STATION) { + _rtl8723ae_stop_tx_beacon(hw); + _rtl8723ae_enable_bcn_sufunc(hw); + } else if (type == NL80211_IFTYPE_ADHOC || + type == NL80211_IFTYPE_AP) { + _rtl8723ae_resume_tx_beacon(hw); + _rtl8723ae_disable_bcn_sufunc(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "Set HW_VAR_MEDIA_STATUS: No such media status(%x).\n", + type); + } + + switch (type) { + case NL80211_IFTYPE_UNSPECIFIED: + bt_msr |= MSR_NOLINK; + ledaction = LED_CTL_LINK; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to NO LINK!\n"); + break; + case NL80211_IFTYPE_ADHOC: + bt_msr |= MSR_ADHOC; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to Ad Hoc!\n"); + break; + case NL80211_IFTYPE_STATION: + bt_msr |= MSR_INFRA; + ledaction = LED_CTL_LINK; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to STA!\n"); + break; + case NL80211_IFTYPE_AP: + bt_msr |= MSR_AP; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Set Network type to AP!\n"); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Network type %d not supported!\n", + type); + return 1; + break; + + } + + rtl_write_byte(rtlpriv, (MSR), bt_msr); + rtlpriv->cfg->ops->led_control(hw, ledaction); + if ((bt_msr & 0x03) == MSR_AP) + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x00); + else + rtl_write_byte(rtlpriv, REG_BCNTCFG + 1, 0x66); + return 0; +} + +void rtl8723ae_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + u32 reg_rcr = rtlpci->receive_config; + + if (rtlpriv->psc.rfpwr_state != ERFON) + return; + + if (check_bssid == true) { + reg_rcr |= (RCR_CBSSID_DATA | RCR_CBSSID_BCN); + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_RCR, + (u8 *)(®_rcr)); + _rtl8723ae_set_bcn_ctrl_reg(hw, 0, BIT(4)); + } else if (check_bssid == false) { + reg_rcr &= (~(RCR_CBSSID_DATA | RCR_CBSSID_BCN)); + _rtl8723ae_set_bcn_ctrl_reg(hw, BIT(4), 0); + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_RCR, (u8 *) (®_rcr)); + } +} + +int rtl8723ae_set_network_type(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (_rtl8723ae_set_media_status(hw, type)) + return -EOPNOTSUPP; + + if (rtlpriv->mac80211.link_state == MAC80211_LINKED) { + if (type != NL80211_IFTYPE_AP) + rtl8723ae_set_check_bssid(hw, true); + } else { + rtl8723ae_set_check_bssid(hw, false); + } + return 0; +} + +/* don't set REG_EDCA_BE_PARAM here because mac80211 will send pkt when scan */ +void rtl8723ae_set_qos(struct ieee80211_hw *hw, int aci) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl8723ae_dm_init_edca_turbo(hw); + switch (aci) { + case AC1_BK: + rtl_write_dword(rtlpriv, REG_EDCA_BK_PARAM, 0xa44f); + break; + case AC0_BE: + /* rtl_write_dword(rtlpriv, REG_EDCA_BE_PARAM, u4ac_param); */ + break; + case AC2_VI: + rtl_write_dword(rtlpriv, REG_EDCA_VI_PARAM, 0x5e4322); + break; + case AC3_VO: + rtl_write_dword(rtlpriv, REG_EDCA_VO_PARAM, 0x2f3222); + break; + default: + RT_ASSERT(false, "invalid aci: %d !\n", aci); + break; + } +} + +void rtl8723ae_enable_interrupt(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + rtl_write_dword(rtlpriv, 0x3a8, rtlpci->irq_mask[0] & 0xFFFFFFFF); + rtl_write_dword(rtlpriv, 0x3ac, rtlpci->irq_mask[1] & 0xFFFFFFFF); + rtlpci->irq_enabled = true; +} + +void rtl8723ae_disable_interrupt(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + rtl_write_dword(rtlpriv, 0x3a8, IMR8190_DISABLED); + rtl_write_dword(rtlpriv, 0x3ac, IMR8190_DISABLED); + rtlpci->irq_enabled = false; + synchronize_irq(rtlpci->pdev->irq); +} + +static void _rtl8723ae_poweroff_adapter(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 u1tmp; + + /* Combo (PCIe + USB) Card and PCIe-MF Card */ + /* 1. Run LPS WL RFOFF flow */ + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, Rtl8723_NIC_LPS_ENTER_FLOW); + + /* 2. 0x1F[7:0] = 0 */ + /* turn off RF */ + rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x00); + if ((rtl_read_byte(rtlpriv, REG_MCUFWDL) & BIT(7)) && rtlhal->fw_ready) + rtl8723ae_firmware_selfreset(hw); + + /* Reset MCU. Suggested by Filen. */ + u1tmp = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN+1); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN+1, (u1tmp & (~BIT(2)))); + + /* g. MCUFWDL 0x80[1:0]=0 */ + /* reset MCU ready status */ + rtl_write_byte(rtlpriv, REG_MCUFWDL, 0x00); + + /* HW card disable configuration. */ + rtl_hal_pwrseqcmdparsing(rtlpriv, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, + PWR_INTF_PCI_MSK, Rtl8723_NIC_DISABLE_FLOW); + + /* Reset MCU IO Wrapper */ + u1tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, (u1tmp & (~BIT(0)))); + u1tmp = rtl_read_byte(rtlpriv, REG_RSV_CTRL + 1); + rtl_write_byte(rtlpriv, REG_RSV_CTRL + 1, u1tmp | BIT(0)); + + /* 7. RSV_CTRL 0x1C[7:0] = 0x0E */ + /* lock ISO/CLK/Power control register */ + rtl_write_byte(rtlpriv, REG_RSV_CTRL, 0x0e); +} + +void rtl8723ae_card_disable(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + enum nl80211_iftype opmode; + + mac->link_state = MAC80211_NOLINK; + opmode = NL80211_IFTYPE_UNSPECIFIED; + _rtl8723ae_set_media_status(hw, opmode); + if (rtlpci->driver_is_goingto_unload || + ppsc->rfoff_reason > RF_CHANGE_BY_PS) + rtlpriv->cfg->ops->led_control(hw, LED_CTL_POWER_OFF); + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + _rtl8723ae_poweroff_adapter(hw); + + /* after power off we should do iqk again */ + rtlpriv->phy.iqk_initialized = false; +} + +void rtl8723ae_interrupt_recognized(struct ieee80211_hw *hw, + u32 *p_inta, u32 *p_intb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + *p_inta = rtl_read_dword(rtlpriv, 0x3a0) & rtlpci->irq_mask[0]; + rtl_write_dword(rtlpriv, 0x3a0, *p_inta); +} + +void rtl8723ae_set_beacon_related_registers(struct ieee80211_hw *hw) +{ + + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 bcn_interval, atim_window; + + bcn_interval = mac->beacon_interval; + atim_window = 2; /*FIX MERGE */ + rtl8723ae_disable_interrupt(hw); + rtl_write_word(rtlpriv, REG_ATIMWND, atim_window); + rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); + rtl_write_word(rtlpriv, REG_BCNTCFG, 0x660f); + rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_CCK, 0x18); + rtl_write_byte(rtlpriv, REG_RXTSF_OFFSET_OFDM, 0x18); + rtl_write_byte(rtlpriv, 0x606, 0x30); + rtl8723ae_enable_interrupt(hw); +} + +void rtl8723ae_set_beacon_interval(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 bcn_interval = mac->beacon_interval; + + RT_TRACE(rtlpriv, COMP_BEACON, DBG_DMESG, + "beacon_interval:%d\n", bcn_interval); + rtl8723ae_disable_interrupt(hw); + rtl_write_word(rtlpriv, REG_BCN_INTERVAL, bcn_interval); + rtl8723ae_enable_interrupt(hw); +} + +void rtl8723ae_update_interrupt_mask(struct ieee80211_hw *hw, + u32 add_msr, u32 rm_msr) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + RT_TRACE(rtlpriv, COMP_INTR, DBG_LOUD, + "add_msr:%x, rm_msr:%x\n", add_msr, rm_msr); + + if (add_msr) + rtlpci->irq_mask[0] |= add_msr; + if (rm_msr) + rtlpci->irq_mask[0] &= (~rm_msr); + rtl8723ae_disable_interrupt(hw); + rtl8723ae_enable_interrupt(hw); +} + +static u8 _rtl8723ae_get_chnl_group(u8 chnl) +{ + u8 group; + + if (chnl < 3) + group = 0; + else if (chnl < 9) + group = 1; + else + group = 2; + return group; +} + +static void _rtl8723ae_read_txpower_info_from_hwpg(struct ieee80211_hw *hw, + bool autoload_fail, + u8 *hwinfo) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 rf_path, index, tempval; + u16 i; + + for (rf_path = 0; rf_path < 1; rf_path++) { + for (i = 0; i < 3; i++) { + if (!autoload_fail) { + rtlefuse->eeprom_chnlarea_txpwr_cck + [rf_path][i] = + hwinfo[EEPROM_TXPOWERCCK + rf_path * 3 + i]; + rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][i] = + hwinfo[EEPROM_TXPOWERHT40_1S + rf_path * + 3 + i]; + } else { + rtlefuse->eeprom_chnlarea_txpwr_cck + [rf_path][i] = + EEPROM_DEFAULT_TXPOWERLEVEL; + rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][i] = + EEPROM_DEFAULT_TXPOWERLEVEL; + } + } + } + + for (i = 0; i < 3; i++) { + if (!autoload_fail) + tempval = hwinfo[EEPROM_TXPOWERHT40_2SDIFF + i]; + else + tempval = EEPROM_DEFAULT_HT40_2SDIFF; + rtlefuse->eprom_chnl_txpwr_ht40_2sdf[RF90_PATH_A][i] = + (tempval & 0xf); + rtlefuse->eprom_chnl_txpwr_ht40_2sdf[RF90_PATH_B][i] = + ((tempval & 0xf0) >> 4); + } + + for (rf_path = 0; rf_path < 2; rf_path++) + for (i = 0; i < 3; i++) + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "RF(%d) EEPROM CCK Area(%d) = 0x%x\n", rf_path, + i, rtlefuse->eeprom_chnlarea_txpwr_cck + [rf_path][i]); + for (rf_path = 0; rf_path < 2; rf_path++) + for (i = 0; i < 3; i++) + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "RF(%d) EEPROM HT40 1S Area(%d) = 0x%x\n", + rf_path, i, + rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][i]); + for (rf_path = 0; rf_path < 2; rf_path++) + for (i = 0; i < 3; i++) + RTPRINT(rtlpriv, FINIT, INIT_EEPROM, + "RF(%d) EEPROM HT40 2S Diff Area(%d) = 0x%x\n", + rf_path, i, + rtlefuse->eprom_chnl_txpwr_ht40_2sdf + [rf_path][i]); + + for (rf_path = 0; rf_path < 2; rf_path++) { + for (i = 0; i < 14; i++) { + index = _rtl8723ae_get_chnl_group((u8) i); + + rtlefuse->txpwrlevel_cck[rf_path][i] = + rtlefuse->eeprom_chnlarea_txpwr_cck + [rf_path][index]; + rtlefuse->txpwrlevel_ht40_1s[rf_path][i] = + rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][index]; + + if ((rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][index] - + rtlefuse->eprom_chnl_txpwr_ht40_2sdf[rf_path] + [index]) > 0) { + rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = + rtlefuse->eeprom_chnlarea_txpwr_ht40_1s + [rf_path][index] - + rtlefuse->eprom_chnl_txpwr_ht40_2sdf + [rf_path][index]; + } else { + rtlefuse->txpwrlevel_ht40_2s[rf_path][i] = 0; + } + } + + for (i = 0; i < 14; i++) { + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF(%d)-Ch(%d) [CCK / HT40_1S / HT40_2S] = " + "[0x%x / 0x%x / 0x%x]\n", rf_path, i, + rtlefuse->txpwrlevel_cck[rf_path][i], + rtlefuse->txpwrlevel_ht40_1s[rf_path][i], + rtlefuse->txpwrlevel_ht40_2s[rf_path][i]); + } + } + + for (i = 0; i < 3; i++) { + if (!autoload_fail) { + rtlefuse->eeprom_pwrlimit_ht40[i] = + hwinfo[EEPROM_TXPWR_GROUP + i]; + rtlefuse->eeprom_pwrlimit_ht20[i] = + hwinfo[EEPROM_TXPWR_GROUP + 3 + i]; + } else { + rtlefuse->eeprom_pwrlimit_ht40[i] = 0; + rtlefuse->eeprom_pwrlimit_ht20[i] = 0; + } + } + + for (rf_path = 0; rf_path < 2; rf_path++) { + for (i = 0; i < 14; i++) { + index = _rtl8723ae_get_chnl_group((u8) i); + + if (rf_path == RF90_PATH_A) { + rtlefuse->pwrgroup_ht20[rf_path][i] = + (rtlefuse->eeprom_pwrlimit_ht20[index] & + 0xf); + rtlefuse->pwrgroup_ht40[rf_path][i] = + (rtlefuse->eeprom_pwrlimit_ht40[index] & + 0xf); + } else if (rf_path == RF90_PATH_B) { + rtlefuse->pwrgroup_ht20[rf_path][i] = + ((rtlefuse->eeprom_pwrlimit_ht20[index] & + 0xf0) >> 4); + rtlefuse->pwrgroup_ht40[rf_path][i] = + ((rtlefuse->eeprom_pwrlimit_ht40[index] & + 0xf0) >> 4); + } + + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-%d pwrgroup_ht20[%d] = 0x%x\n", rf_path, i, + rtlefuse->pwrgroup_ht20[rf_path][i]); + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-%d pwrgroup_ht40[%d] = 0x%x\n", rf_path, i, + rtlefuse->pwrgroup_ht40[rf_path][i]); + } + } + + for (i = 0; i < 14; i++) { + index = _rtl8723ae_get_chnl_group((u8) i); + + if (!autoload_fail) + tempval = hwinfo[EEPROM_TXPOWERHT20DIFF + index]; + else + tempval = EEPROM_DEFAULT_HT20_DIFF; + + rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] = (tempval & 0xF); + rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] = + ((tempval >> 4) & 0xF); + + if (rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] & BIT(3)) + rtlefuse->txpwr_ht20diff[RF90_PATH_A][i] |= 0xF0; + + if (rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] & BIT(3)) + rtlefuse->txpwr_ht20diff[RF90_PATH_B][i] |= 0xF0; + + index = _rtl8723ae_get_chnl_group((u8) i); + + if (!autoload_fail) + tempval = hwinfo[EEPROM_TXPOWER_OFDMDIFF + index]; + else + tempval = EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF; + + rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i] = (tempval & 0xF); + rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i] = + ((tempval >> 4) & 0xF); + } + + rtlefuse->legacy_ht_txpowerdiff = + rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][7]; + + for (i = 0; i < 14; i++) + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-A Ht20 to HT40 Diff[%d] = 0x%x\n", i, + rtlefuse->txpwr_ht20diff[RF90_PATH_A][i]); + for (i = 0; i < 14; i++) + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-A Legacy to Ht40 Diff[%d] = 0x%x\n", i, + rtlefuse->txpwr_legacyhtdiff[RF90_PATH_A][i]); + for (i = 0; i < 14; i++) + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-B Ht20 to HT40 Diff[%d] = 0x%x\n", i, + rtlefuse->txpwr_ht20diff[RF90_PATH_B][i]); + for (i = 0; i < 14; i++) + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "RF-B Legacy to HT40 Diff[%d] = 0x%x\n", i, + rtlefuse->txpwr_legacyhtdiff[RF90_PATH_B][i]); + + if (!autoload_fail) + rtlefuse->eeprom_regulatory = (hwinfo[RF_OPTION1] & 0x7); + else + rtlefuse->eeprom_regulatory = 0; + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "eeprom_regulatory = 0x%x\n", rtlefuse->eeprom_regulatory); + + if (!autoload_fail) + rtlefuse->eeprom_tssi[RF90_PATH_A] = hwinfo[EEPROM_TSSI_A]; + else + rtlefuse->eeprom_tssi[RF90_PATH_A] = EEPROM_DEFAULT_TSSI; + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "TSSI_A = 0x%x, TSSI_B = 0x%x\n", + rtlefuse->eeprom_tssi[RF90_PATH_A], + rtlefuse->eeprom_tssi[RF90_PATH_B]); + + if (!autoload_fail) + tempval = hwinfo[EEPROM_THERMAL_METER]; + else + tempval = EEPROM_DEFAULT_THERMALMETER; + rtlefuse->eeprom_thermalmeter = (tempval & 0x1f); + + if (rtlefuse->eeprom_thermalmeter == 0x1f || autoload_fail) + rtlefuse->apk_thermalmeterignore = true; + + rtlefuse->thermalmeter[0] = rtlefuse->eeprom_thermalmeter; + RTPRINT(rtlpriv, FINIT, INIT_TxPower, + "thermalmeter = 0x%x\n", rtlefuse->eeprom_thermalmeter); +} + +static void _rtl8723ae_read_adapter_info(struct ieee80211_hw *hw, + bool pseudo_test) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u16 i, usvalue; + u8 hwinfo[HWSET_MAX_SIZE]; + u16 eeprom_id; + + if (pseudo_test) { + /* need add */ + return; + } + if (rtlefuse->epromtype == EEPROM_BOOT_EFUSE) { + rtl_efuse_shadow_map_update(hw); + + memcpy(hwinfo, &rtlefuse->efuse_map[EFUSE_INIT_MAP][0], + HWSET_MAX_SIZE); + } else if (rtlefuse->epromtype == EEPROM_93C46) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "RTL819X Not boot from eeprom, check it !!"); + } + + RT_PRINT_DATA(rtlpriv, COMP_INIT, DBG_DMESG, ("MAP\n"), + hwinfo, HWSET_MAX_SIZE); + + eeprom_id = *((u16 *)&hwinfo[0]); + if (eeprom_id != RTL8190_EEPROM_ID) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "EEPROM ID(%#x) is invalid!!\n", eeprom_id); + rtlefuse->autoload_failflag = true; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); + rtlefuse->autoload_failflag = false; + } + + if (rtlefuse->autoload_failflag == true) + return; + + rtlefuse->eeprom_vid = *(u16 *) &hwinfo[EEPROM_VID]; + rtlefuse->eeprom_did = *(u16 *) &hwinfo[EEPROM_DID]; + rtlefuse->eeprom_svid = *(u16 *) &hwinfo[EEPROM_SVID]; + rtlefuse->eeprom_smid = *(u16 *) &hwinfo[EEPROM_SMID]; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROMId = 0x%4x\n", eeprom_id); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM VID = 0x%4x\n", rtlefuse->eeprom_vid); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM DID = 0x%4x\n", rtlefuse->eeprom_did); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM SVID = 0x%4x\n", rtlefuse->eeprom_svid); + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM SMID = 0x%4x\n", rtlefuse->eeprom_smid); + + for (i = 0; i < 6; i += 2) { + usvalue = *(u16 *)&hwinfo[EEPROM_MAC_ADDR + i]; + *((u16 *) (&rtlefuse->dev_addr[i])) = usvalue; + } + + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "dev_addr: %pM\n", rtlefuse->dev_addr); + + _rtl8723ae_read_txpower_info_from_hwpg(hw, + rtlefuse->autoload_failflag, hwinfo); + + rtl8723ae_read_bt_coexist_info_from_hwpg(hw, + rtlefuse->autoload_failflag, hwinfo); + + rtlefuse->eeprom_channelplan = *(u8 *)&hwinfo[EEPROM_CHANNELPLAN]; + rtlefuse->eeprom_version = *(u16 *)&hwinfo[EEPROM_VERSION]; + rtlefuse->txpwr_fromeprom = true; + rtlefuse->eeprom_oemid = *(u8 *)&hwinfo[EEPROM_CUSTOMER_ID]; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, + "EEPROM Customer ID: 0x%2x\n", rtlefuse->eeprom_oemid); + + /* set channel paln to world wide 13 */ + rtlefuse->channel_plan = COUNTRY_CODE_WORLD_WIDE_13; + + if (rtlhal->oem_id == RT_CID_DEFAULT) { + switch (rtlefuse->eeprom_oemid) { + case EEPROM_CID_DEFAULT: + if (rtlefuse->eeprom_did == 0x8176) { + if (CHK_SVID_SMID(0x10EC, 0x6151) || + CHK_SVID_SMID(0x10EC, 0x6152) || + CHK_SVID_SMID(0x10EC, 0x6154) || + CHK_SVID_SMID(0x10EC, 0x6155) || + CHK_SVID_SMID(0x10EC, 0x6177) || + CHK_SVID_SMID(0x10EC, 0x6178) || + CHK_SVID_SMID(0x10EC, 0x6179) || + CHK_SVID_SMID(0x10EC, 0x6180) || + CHK_SVID_SMID(0x10EC, 0x8151) || + CHK_SVID_SMID(0x10EC, 0x8152) || + CHK_SVID_SMID(0x10EC, 0x8154) || + CHK_SVID_SMID(0x10EC, 0x8155) || + CHK_SVID_SMID(0x10EC, 0x8181) || + CHK_SVID_SMID(0x10EC, 0x8182) || + CHK_SVID_SMID(0x10EC, 0x8184) || + CHK_SVID_SMID(0x10EC, 0x8185) || + CHK_SVID_SMID(0x10EC, 0x9151) || + CHK_SVID_SMID(0x10EC, 0x9152) || + CHK_SVID_SMID(0x10EC, 0x9154) || + CHK_SVID_SMID(0x10EC, 0x9155) || + CHK_SVID_SMID(0x10EC, 0x9181) || + CHK_SVID_SMID(0x10EC, 0x9182) || + CHK_SVID_SMID(0x10EC, 0x9184) || + CHK_SVID_SMID(0x10EC, 0x9185)) + rtlhal->oem_id = RT_CID_TOSHIBA; + else if (rtlefuse->eeprom_svid == 0x1025) + rtlhal->oem_id = RT_CID_819x_Acer; + else if (CHK_SVID_SMID(0x10EC, 0x6191) || + CHK_SVID_SMID(0x10EC, 0x6192) || + CHK_SVID_SMID(0x10EC, 0x6193) || + CHK_SVID_SMID(0x10EC, 0x7191) || + CHK_SVID_SMID(0x10EC, 0x7192) || + CHK_SVID_SMID(0x10EC, 0x7193) || + CHK_SVID_SMID(0x10EC, 0x8191) || + CHK_SVID_SMID(0x10EC, 0x8192) || + CHK_SVID_SMID(0x10EC, 0x8193)) + rtlhal->oem_id = RT_CID_819x_SAMSUNG; + else if (CHK_SVID_SMID(0x10EC, 0x8195) || + CHK_SVID_SMID(0x10EC, 0x9195) || + CHK_SVID_SMID(0x10EC, 0x7194) || + CHK_SVID_SMID(0x10EC, 0x8200) || + CHK_SVID_SMID(0x10EC, 0x8201) || + CHK_SVID_SMID(0x10EC, 0x8202) || + CHK_SVID_SMID(0x10EC, 0x9200)) + rtlhal->oem_id = RT_CID_819x_Lenovo; + else if (CHK_SVID_SMID(0x10EC, 0x8197) || + CHK_SVID_SMID(0x10EC, 0x9196)) + rtlhal->oem_id = RT_CID_819x_CLEVO; + else if (CHK_SVID_SMID(0x1028, 0x8194) || + CHK_SVID_SMID(0x1028, 0x8198) || + CHK_SVID_SMID(0x1028, 0x9197) || + CHK_SVID_SMID(0x1028, 0x9198)) + rtlhal->oem_id = RT_CID_819x_DELL; + else if (CHK_SVID_SMID(0x103C, 0x1629)) + rtlhal->oem_id = RT_CID_819x_HP; + else if (CHK_SVID_SMID(0x1A32, 0x2315)) + rtlhal->oem_id = RT_CID_819x_QMI; + else if (CHK_SVID_SMID(0x10EC, 0x8203)) + rtlhal->oem_id = RT_CID_819x_PRONETS; + else if (CHK_SVID_SMID(0x1043, 0x84B5)) + rtlhal->oem_id = + RT_CID_819x_Edimax_ASUS; + else + rtlhal->oem_id = RT_CID_DEFAULT; + } else if (rtlefuse->eeprom_did == 0x8178) { + if (CHK_SVID_SMID(0x10EC, 0x6181) || + CHK_SVID_SMID(0x10EC, 0x6182) || + CHK_SVID_SMID(0x10EC, 0x6184) || + CHK_SVID_SMID(0x10EC, 0x6185) || + CHK_SVID_SMID(0x10EC, 0x7181) || + CHK_SVID_SMID(0x10EC, 0x7182) || + CHK_SVID_SMID(0x10EC, 0x7184) || + CHK_SVID_SMID(0x10EC, 0x7185) || + CHK_SVID_SMID(0x10EC, 0x8181) || + CHK_SVID_SMID(0x10EC, 0x8182) || + CHK_SVID_SMID(0x10EC, 0x8184) || + CHK_SVID_SMID(0x10EC, 0x8185) || + CHK_SVID_SMID(0x10EC, 0x9181) || + CHK_SVID_SMID(0x10EC, 0x9182) || + CHK_SVID_SMID(0x10EC, 0x9184) || + CHK_SVID_SMID(0x10EC, 0x9185)) + rtlhal->oem_id = RT_CID_TOSHIBA; + else if (rtlefuse->eeprom_svid == 0x1025) + rtlhal->oem_id = RT_CID_819x_Acer; + else if (CHK_SVID_SMID(0x10EC, 0x8186)) + rtlhal->oem_id = RT_CID_819x_PRONETS; + else if (CHK_SVID_SMID(0x1043, 0x8486)) + rtlhal->oem_id = + RT_CID_819x_Edimax_ASUS; + else + rtlhal->oem_id = RT_CID_DEFAULT; + } else { + rtlhal->oem_id = RT_CID_DEFAULT; + } + break; + case EEPROM_CID_TOSHIBA: + rtlhal->oem_id = RT_CID_TOSHIBA; + break; + case EEPROM_CID_CCX: + rtlhal->oem_id = RT_CID_CCX; + break; + case EEPROM_CID_QMI: + rtlhal->oem_id = RT_CID_819x_QMI; + break; + case EEPROM_CID_WHQL: + break; + default: + rtlhal->oem_id = RT_CID_DEFAULT; + break; + + } + } +} + +static void _rtl8723ae_hal_customized_behavior(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + switch (rtlhal->oem_id) { + case RT_CID_819x_HP: + pcipriv->ledctl.led_opendrain = true; + break; + case RT_CID_819x_Lenovo: + case RT_CID_DEFAULT: + case RT_CID_TOSHIBA: + case RT_CID_CCX: + case RT_CID_819x_Acer: + case RT_CID_WHQL: + default: + break; + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, + "RT Customized ID: 0x%02X\n", rtlhal->oem_id); +} + +void rtl8723ae_read_eeprom_info(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp_u1b; + u32 value32; + + value32 = rtl_read_dword(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST]); + value32 = (value32 & ~EFUSE_SEL_MASK) | EFUSE_SEL(EFUSE_WIFI_SEL_0); + rtl_write_dword(rtlpriv, rtlpriv->cfg->maps[EFUSE_TEST], value32); + + rtlhal->version = _rtl8723ae_read_chip_version(hw); + + if (get_rf_type(rtlphy) == RF_1T1R) + rtlpriv->dm.rfpath_rxenable[0] = true; + else + rtlpriv->dm.rfpath_rxenable[0] = + rtlpriv->dm.rfpath_rxenable[1] = true; + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "VersionID = 0x%4x\n", + rtlhal->version); + + tmp_u1b = rtl_read_byte(rtlpriv, REG_9346CR); + if (tmp_u1b & BIT(4)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EEPROM\n"); + rtlefuse->epromtype = EEPROM_93C46; + } else { + RT_TRACE(rtlpriv, COMP_INIT, DBG_DMESG, "Boot from EFUSE\n"); + rtlefuse->epromtype = EEPROM_BOOT_EFUSE; + } + if (tmp_u1b & BIT(5)) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_LOUD, "Autoload OK\n"); + rtlefuse->autoload_failflag = false; + _rtl8723ae_read_adapter_info(hw, false); + } else { + rtlefuse->autoload_failflag = true; + _rtl8723ae_read_adapter_info(hw, false); + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Autoload ERR!!\n"); + } + _rtl8723ae_hal_customized_behavior(hw); +} + +static void rtl8723ae_update_hal_rate_table(struct ieee80211_hw *hw, + struct ieee80211_sta *sta) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u32 ratr_value; + u8 ratr_index = 0; + u8 nmode = mac->ht_enable; + u8 mimo_ps = IEEE80211_SMPS_OFF; + u8 curtxbw_40mhz = mac->bw_40; + u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + 1 : 0; + u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? + 1 : 0; + enum wireless_mode wirelessmode = mac->mode; + + if (rtlhal->current_bandtype == BAND_ON_5G) + ratr_value = sta->supp_rates[1] << 4; + else + ratr_value = sta->supp_rates[0]; + if (mac->opmode == NL80211_IFTYPE_ADHOC) + ratr_value = 0xfff; + ratr_value |= (sta->ht_cap.mcs.rx_mask[1] << 20 | + sta->ht_cap.mcs.rx_mask[0] << 12); + switch (wirelessmode) { + case WIRELESS_MODE_B: + if (ratr_value & 0x0000000c) + ratr_value &= 0x0000000d; + else + ratr_value &= 0x0000000f; + break; + case WIRELESS_MODE_G: + ratr_value &= 0x00000FF5; + break; + case WIRELESS_MODE_N_24G: + case WIRELESS_MODE_N_5G: + nmode = 1; + if (mimo_ps == IEEE80211_SMPS_STATIC) { + ratr_value &= 0x0007F005; + } else { + u32 ratr_mask; + + if (get_rf_type(rtlphy) == RF_1T2R || + get_rf_type(rtlphy) == RF_1T1R) + ratr_mask = 0x000ff005; + else + ratr_mask = 0x0f0ff005; + + ratr_value &= ratr_mask; + } + break; + default: + if (rtlphy->rf_type == RF_1T2R) + ratr_value &= 0x000ff0ff; + else + ratr_value &= 0x0f0ff0ff; + + break; + } + + if ((pcipriv->bt_coexist.bt_coexistence) && + (pcipriv->bt_coexist.bt_coexist_type == BT_CSR_BC4) && + (pcipriv->bt_coexist.bt_cur_state) && + (pcipriv->bt_coexist.bt_ant_isolation) && + ((pcipriv->bt_coexist.bt_service == BT_SCO) || + (pcipriv->bt_coexist.bt_service == BT_BUSY))) + ratr_value &= 0x0fffcfc0; + else + ratr_value &= 0x0FFFFFFF; + + if (nmode && ((curtxbw_40mhz && curshortgi_40mhz) || + (!curtxbw_40mhz && curshortgi_20mhz))) + ratr_value |= 0x10000000; + + rtl_write_dword(rtlpriv, REG_ARFR0 + ratr_index * 4, ratr_value); + + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "%x\n", rtl_read_dword(rtlpriv, REG_ARFR0)); +} + +static void rtl8723ae_update_hal_rate_mask(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 rssi_level) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_sta_info *sta_entry = NULL; + u32 ratr_bitmap; + u8 ratr_index; + u8 curtxbw_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) + ? 1 : 0; + u8 curshortgi_40mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) ? + 1 : 0; + u8 curshortgi_20mhz = (sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) ? + 1 : 0; + enum wireless_mode wirelessmode = 0; + bool shortgi = false; + u8 rate_mask[5]; + u8 macid = 0; + u8 mimo_ps = IEEE80211_SMPS_OFF; + + sta_entry = (struct rtl_sta_info *) sta->drv_priv; + wirelessmode = sta_entry->wireless_mode; + if (mac->opmode == NL80211_IFTYPE_STATION) + curtxbw_40mhz = mac->bw_40; + else if (mac->opmode == NL80211_IFTYPE_AP || + mac->opmode == NL80211_IFTYPE_ADHOC) + macid = sta->aid + 1; + + if (rtlhal->current_bandtype == BAND_ON_5G) + ratr_bitmap = sta->supp_rates[1] << 4; + else + ratr_bitmap = sta->supp_rates[0]; + if (mac->opmode == NL80211_IFTYPE_ADHOC) + ratr_bitmap = 0xfff; + ratr_bitmap |= (sta->ht_cap.mcs.rx_mask[1] << 20 | + sta->ht_cap.mcs.rx_mask[0] << 12); + switch (wirelessmode) { + case WIRELESS_MODE_B: + ratr_index = RATR_INX_WIRELESS_B; + if (ratr_bitmap & 0x0000000c) + ratr_bitmap &= 0x0000000d; + else + ratr_bitmap &= 0x0000000f; + break; + case WIRELESS_MODE_G: + ratr_index = RATR_INX_WIRELESS_GB; + + if (rssi_level == 1) + ratr_bitmap &= 0x00000f00; + else if (rssi_level == 2) + ratr_bitmap &= 0x00000ff0; + else + ratr_bitmap &= 0x00000ff5; + break; + case WIRELESS_MODE_A: + ratr_index = RATR_INX_WIRELESS_A; + ratr_bitmap &= 0x00000ff0; + break; + case WIRELESS_MODE_N_24G: + case WIRELESS_MODE_N_5G: + ratr_index = RATR_INX_WIRELESS_NGB; + + if (mimo_ps == IEEE80211_SMPS_STATIC) { + if (rssi_level == 1) + ratr_bitmap &= 0x00070000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0007f000; + else + ratr_bitmap &= 0x0007f005; + } else { + if (rtlphy->rf_type == RF_1T2R || + rtlphy->rf_type == RF_1T1R) { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x000f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x000ff000; + else + ratr_bitmap &= 0x000ff005; + } + } else { + if (curtxbw_40mhz) { + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff015; + } else { + if (rssi_level == 1) + ratr_bitmap &= 0x0f0f0000; + else if (rssi_level == 2) + ratr_bitmap &= 0x0f0ff000; + else + ratr_bitmap &= 0x0f0ff005; + } + } + } + + if ((curtxbw_40mhz && curshortgi_40mhz) || + (!curtxbw_40mhz && curshortgi_20mhz)) { + if (macid == 0) + shortgi = true; + else if (macid == 1) + shortgi = false; + } + break; + default: + ratr_index = RATR_INX_WIRELESS_NGB; + + if (rtlphy->rf_type == RF_1T2R) + ratr_bitmap &= 0x000ff0ff; + else + ratr_bitmap &= 0x0f0ff0ff; + break; + } + sta_entry->ratr_index = ratr_index; + + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "ratr_bitmap :%x\n", ratr_bitmap); + /* convert ratr_bitmap to le byte array */ + rate_mask[0] = ratr_bitmap; + rate_mask[1] = (ratr_bitmap >>= 8); + rate_mask[2] = (ratr_bitmap >>= 8); + rate_mask[3] = ((ratr_bitmap >> 8) & 0x0f) | (ratr_index << 4); + rate_mask[4] = macid | (shortgi ? 0x20 : 0x00) | 0x80; + RT_TRACE(rtlpriv, COMP_RATR, DBG_DMESG, + "Rate_index:%x, ratr_bitmap: %*phC\n", + ratr_index, 5, rate_mask); + rtl8723ae_fill_h2c_cmd(hw, H2C_RA_MASK, 5, rate_mask); +} + +void rtl8723ae_update_hal_rate_tbl(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 rssi_level) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->dm.useramask) + rtl8723ae_update_hal_rate_mask(hw, sta, rssi_level); + else + rtl8723ae_update_hal_rate_table(hw, sta); +} + +void rtl8723ae_update_channel_access_setting(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u16 sifs_timer; + + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SLOT_TIME, + (u8 *)&mac->slot_time); + if (!mac->ht_enable) + sifs_timer = 0x0a0a; + else + sifs_timer = 0x1010; + rtlpriv->cfg->ops->set_hw_reg(hw, HW_VAR_SIFS, (u8 *)&sifs_timer); +} + +bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + enum rf_pwrstate e_rfpowerstate_toset, cur_rfstate; + u8 u1tmp; + bool actuallyset = false; + + if (rtlpriv->rtlhal.being_init_adapter) + return false; + + if (ppsc->swrf_processing) + return false; + + spin_lock(&rtlpriv->locks.rf_ps_lock); + if (ppsc->rfchange_inprogress) { + spin_unlock(&rtlpriv->locks.rf_ps_lock); + return false; + } else { + ppsc->rfchange_inprogress = true; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } + + cur_rfstate = ppsc->rfpwr_state; + + rtl_write_byte(rtlpriv, REG_GPIO_IO_SEL_2, + rtl_read_byte(rtlpriv, REG_GPIO_IO_SEL_2)&~(BIT(1))); + + u1tmp = rtl_read_byte(rtlpriv, REG_GPIO_PIN_CTRL_2); + + if (rtlphy->polarity_ctl) + e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFOFF : ERFON; + else + e_rfpowerstate_toset = (u1tmp & BIT(1)) ? ERFON : ERFOFF; + + if ((ppsc->hwradiooff == true) && (e_rfpowerstate_toset == ERFON)) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "GPIOChangeRF - HW Radio ON, RF ON\n"); + + e_rfpowerstate_toset = ERFON; + ppsc->hwradiooff = false; + actuallyset = true; + } else if ((ppsc->hwradiooff == false) + && (e_rfpowerstate_toset == ERFOFF)) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "GPIOChangeRF - HW Radio OFF, RF OFF\n"); + + e_rfpowerstate_toset = ERFOFF; + ppsc->hwradiooff = true; + actuallyset = true; + } + + if (actuallyset) { + spin_lock(&rtlpriv->locks.rf_ps_lock); + ppsc->rfchange_inprogress = false; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } else { + if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + + spin_lock(&rtlpriv->locks.rf_ps_lock); + ppsc->rfchange_inprogress = false; + spin_unlock(&rtlpriv->locks.rf_ps_lock); + } + + *valid = 1; + return !ppsc->hwradiooff; +} + +void rtl8723ae_set_key(struct ieee80211_hw *hw, u32 key_index, + u8 *p_macaddr, bool is_group, u8 enc_algo, + bool is_wepkey, bool clear_all) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 *macaddr = p_macaddr; + u32 entry_id = 0; + bool is_pairwise = false; + static u8 cam_const_addr[4][6] = { + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x03} + }; + static u8 cam_const_broad[] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + }; + + if (clear_all) { + u8 idx = 0; + u8 cam_offset = 0; + u8 clear_number = 5; + + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, "clear_all\n"); + + for (idx = 0; idx < clear_number; idx++) { + rtl_cam_mark_invalid(hw, cam_offset + idx); + rtl_cam_empty_entry(hw, cam_offset + idx); + + if (idx < 5) { + memset(rtlpriv->sec.key_buf[idx], 0, + MAX_KEY_LEN); + rtlpriv->sec.key_len[idx] = 0; + } + } + } else { + switch (enc_algo) { + case WEP40_ENCRYPTION: + enc_algo = CAM_WEP40; + break; + case WEP104_ENCRYPTION: + enc_algo = CAM_WEP104; + break; + case TKIP_ENCRYPTION: + enc_algo = CAM_TKIP; + break; + case AESCCMP_ENCRYPTION: + enc_algo = CAM_AES; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + enc_algo = CAM_TKIP; + break; + } + + if (is_wepkey || rtlpriv->sec.use_defaultkey) { + macaddr = cam_const_addr[key_index]; + entry_id = key_index; + } else { + if (is_group) { + macaddr = cam_const_broad; + entry_id = key_index; + } else { + if (mac->opmode == NL80211_IFTYPE_AP) { + entry_id = rtl_cam_get_free_entry(hw, + macaddr); + if (entry_id >= TOTAL_CAM_ENTRY) { + RT_TRACE(rtlpriv, COMP_SEC, + DBG_EMERG, + "Can not find free hw security cam entry\n"); + return; + } + } else { + entry_id = CAM_PAIRWISE_KEY_POSITION; + } + + key_index = PAIRWISE_KEYIDX; + is_pairwise = true; + } + } + + if (rtlpriv->sec.key_len[key_index] == 0) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "delete one entry, entry_id is %d\n", + entry_id); + if (mac->opmode == NL80211_IFTYPE_AP) + rtl_cam_del_entry(hw, p_macaddr); + rtl_cam_delete_one_entry(hw, p_macaddr, entry_id); + } else { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "add one entry\n"); + if (is_pairwise) { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "set Pairwiase key\n"); + + rtl_cam_add_one_entry(hw, macaddr, key_index, + entry_id, enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf[key_index]); + } else { + RT_TRACE(rtlpriv, COMP_SEC, DBG_DMESG, + "set group key\n"); + + if (mac->opmode == NL80211_IFTYPE_ADHOC) { + rtl_cam_add_one_entry(hw, + rtlefuse->dev_addr, + PAIRWISE_KEYIDX, + CAM_PAIRWISE_KEY_POSITION, + enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf + [entry_id]); + } + + rtl_cam_add_one_entry(hw, macaddr, key_index, + entry_id, enc_algo, + CAM_CONFIG_NO_USEDK, + rtlpriv->sec.key_buf[entry_id]); + } + + } + } +} + +static void rtl8723ae_bt_var_init(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + + pcipriv->bt_coexist.bt_coexistence = + pcipriv->bt_coexist.eeprom_bt_coexist; + pcipriv->bt_coexist.bt_ant_num = + pcipriv->bt_coexist.eeprom_bt_ant_num; + pcipriv->bt_coexist.bt_coexist_type = + pcipriv->bt_coexist.eeprom_bt_type; + + pcipriv->bt_coexist.bt_ant_isolation = + pcipriv->bt_coexist.eeprom_bt_ant_isol; + + pcipriv->bt_coexist.bt_radio_shared_type = + pcipriv->bt_coexist.eeprom_bt_radio_shared; + + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BT Coexistance = 0x%x\n", + pcipriv->bt_coexist.bt_coexistence); + + if (pcipriv->bt_coexist.bt_coexistence) { + pcipriv->bt_coexist.bt_busy_traffic = false; + pcipriv->bt_coexist.bt_traffic_mode_set = false; + pcipriv->bt_coexist.bt_non_traffic_mode_set = false; + + pcipriv->bt_coexist.cstate = 0; + pcipriv->bt_coexist.previous_state = 0; + + if (pcipriv->bt_coexist.bt_ant_num == ANT_X2) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_Ant_Num = Antx2\n"); + } else if (pcipriv->bt_coexist.bt_ant_num == ANT_X1) { + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_Ant_Num = Antx1\n"); + } + + switch (pcipriv->bt_coexist.bt_coexist_type) { + case BT_2WIRE: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_2Wire\n"); + break; + case BT_ISSC_3WIRE: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_ISSC_3Wire\n"); + break; + case BT_ACCEL: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_ACCEL\n"); + break; + case BT_CSR_BC4: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_CSR_BC4\n"); + break; + case BT_CSR_BC8: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_CSR_BC8\n"); + break; + case BT_RTL8756: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = BT_RTL8756\n"); + break; + default: + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_CoexistType = Unknown\n"); + break; + } + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BlueTooth BT_Ant_isolation = %d\n", + pcipriv->bt_coexist.bt_ant_isolation); + RT_TRACE(rtlpriv, COMP_BT_COEXIST, DBG_TRACE, + "BT_RadioSharedType = 0x%x\n", + pcipriv->bt_coexist.bt_radio_shared_type); + pcipriv->bt_coexist.bt_active_zero_cnt = 0; + pcipriv->bt_coexist.cur_bt_disabled = false; + pcipriv->bt_coexist.pre_bt_disabled = false; + } +} + +void rtl8723ae_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + bool auto_load_fail, u8 *hwinfo) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 value; + u32 tmpu_32; + + if (!auto_load_fail) { + tmpu_32 = rtl_read_dword(rtlpriv, REG_MULTI_FUNC_CTRL); + if (tmpu_32 & BIT(18)) + pcipriv->bt_coexist.eeprom_bt_coexist = 1; + else + pcipriv->bt_coexist.eeprom_bt_coexist = 0; + value = hwinfo[RF_OPTION4]; + pcipriv->bt_coexist.eeprom_bt_type = BT_RTL8723A; + pcipriv->bt_coexist.eeprom_bt_ant_num = (value & 0x1); + pcipriv->bt_coexist.eeprom_bt_ant_isol = ((value & 0x10) >> 4); + pcipriv->bt_coexist.eeprom_bt_radio_shared = + ((value & 0x20) >> 5); + } else { + pcipriv->bt_coexist.eeprom_bt_coexist = 0; + pcipriv->bt_coexist.eeprom_bt_type = BT_RTL8723A; + pcipriv->bt_coexist.eeprom_bt_ant_num = ANT_X2; + pcipriv->bt_coexist.eeprom_bt_ant_isol = 0; + pcipriv->bt_coexist.eeprom_bt_radio_shared = BT_RADIO_SHARED; + } + + rtl8723ae_bt_var_init(hw); +} + +void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + + /* 0:Low, 1:High, 2:From Efuse. */ + pcipriv->bt_coexist.reg_bt_iso = 2; + /* 0:Idle, 1:None-SCO, 2:SCO, 3:From Counter. */ + pcipriv->bt_coexist.reg_bt_sco = 3; + /* 0:Disable BT control A-MPDU, 1:Enable BT control A-MPDU. */ + pcipriv->bt_coexist.reg_bt_sco = 0; +} + + +void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw) +{ +} + +void rtl8723ae_suspend(struct ieee80211_hw *hw) +{ +} + +void rtl8723ae_resume(struct ieee80211_hw *hw) +{ +} + +/* Turn on AAP (RCR:bit 0) for promicuous mode. */ +void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, + bool allow_all_da, bool write_into_reg) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + if (allow_all_da) /* Set BIT0 */ + rtlpci->receive_config |= RCR_AAP; + else /* Clear BIT0 */ + rtlpci->receive_config &= ~RCR_AAP; + + if (write_into_reg) + rtl_write_dword(rtlpriv, REG_RCR, rtlpci->receive_config); + + + RT_TRACE(rtlpriv, COMP_TURBO | COMP_INIT, DBG_LOUD, + "receive_config=0x%08X, write_into_reg=%d\n", + rtlpci->receive_config, write_into_reg); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h new file mode 100644 index 00000000000..6fa24f79b1d --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/hw.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_HW_H__ +#define __RTL8723E_HW_H__ + +#define CHK_SVID_SMID(_val1, _val2) \ + ((rtlefuse->eeprom_svid == (_val1)) && \ + (rtlefuse->eeprom_smid == (_val2))) + +void rtl8723ae_get_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); +void rtl8723ae_read_eeprom_info(struct ieee80211_hw *hw); + +void rtl8723ae_interrupt_recognized(struct ieee80211_hw *hw, + u32 *p_inta, u32 *p_intb); +int rtl8723ae_hw_init(struct ieee80211_hw *hw); +void rtl8723ae_card_disable(struct ieee80211_hw *hw); +void rtl8723ae_enable_interrupt(struct ieee80211_hw *hw); +void rtl8723ae_disable_interrupt(struct ieee80211_hw *hw); +int rtl8723ae_set_network_type(struct ieee80211_hw *hw, + enum nl80211_iftype type); +void rtl8723ae_set_check_bssid(struct ieee80211_hw *hw, bool check_bssid); +void rtl8723ae_set_qos(struct ieee80211_hw *hw, int aci); +void rtl8723ae_set_beacon_related_registers(struct ieee80211_hw *hw); +void rtl8723ae_set_beacon_interval(struct ieee80211_hw *hw); +void rtl8723ae_update_interrupt_mask(struct ieee80211_hw *hw, + u32 add_msr, u32 rm_msr); +void rtl8723ae_set_hw_reg(struct ieee80211_hw *hw, u8 variable, u8 *val); +void rtl8723ae_update_hal_rate_tbl(struct ieee80211_hw *hw, + struct ieee80211_sta *sta, u8 rssi_level); +void rtl8723ae_update_channel_access_setting(struct ieee80211_hw *hw); +bool rtl8723ae_gpio_radio_on_off_checking(struct ieee80211_hw *hw, u8 *valid); +void rtl8723ae_enable_hw_security_config(struct ieee80211_hw *hw); +void rtl8723ae_set_key(struct ieee80211_hw *hw, u32 key_index, + u8 *p_macaddr, bool is_group, u8 enc_algo, + bool is_wepkey, bool clear_all); + +void rtl8723ae_read_bt_coexist_info_from_hwpg(struct ieee80211_hw *hw, + bool autoload_fail, u8 *hwinfo); +void rtl8723ae_bt_reg_init(struct ieee80211_hw *hw); +void rtl8723ae_bt_hw_init(struct ieee80211_hw *hw); +void rtl8723ae_suspend(struct ieee80211_hw *hw); +void rtl8723ae_resume(struct ieee80211_hw *hw); +void rtl8723ae_allow_all_destaddr(struct ieee80211_hw *hw, + bool allow_all_da, bool write_into_reg); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/led.c b/drivers/net/wireless/rtlwifi/rtl8723ae/led.c new file mode 100644 index 00000000000..9c4e1d81118 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/led.c @@ -0,0 +1,151 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "reg.h" +#include "led.h" + +static void _rtl8723ae_init_led(struct ieee80211_hw *hw, + struct rtl_led *pled, enum rtl_led_pin ledpin) +{ + pled->hw = hw; + pled->ledpin = ledpin; + pled->ledon = false; +} + +void rtl8723ae_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 ledcfg; + + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, + "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); + + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); + + switch (pled->ledpin) { + case LED_PIN_GPIO0: + break; + case LED_PIN_LED0: + rtl_write_byte(rtlpriv, + REG_LEDCFG2, (ledcfg & 0xf0) | BIT(5) | BIT(6)); + break; + case LED_PIN_LED1: + rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg & 0x0f) | BIT(5)); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + pled->ledon = true; +} + +void rtl8723ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + u8 ledcfg; + + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, + "LedAddr:%X ledpin=%d\n", REG_LEDCFG2, pled->ledpin); + + ledcfg = rtl_read_byte(rtlpriv, REG_LEDCFG2); + + switch (pled->ledpin) { + case LED_PIN_GPIO0: + break; + case LED_PIN_LED0: + ledcfg &= 0xf0; + if (pcipriv->ledctl.led_opendrain) + rtl_write_byte(rtlpriv, REG_LEDCFG2, + (ledcfg | BIT(1) | BIT(5) | BIT(6))); + else + rtl_write_byte(rtlpriv, REG_LEDCFG2, + (ledcfg | BIT(3) | BIT(5) | BIT(6))); + break; + case LED_PIN_LED1: + ledcfg &= 0x0f; + rtl_write_byte(rtlpriv, REG_LEDCFG2, (ledcfg | BIT(3))); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + break; + } + pled->ledon = false; +} + +void rtl8723ae_init_sw_leds(struct ieee80211_hw *hw) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + + _rtl8723ae_init_led(hw, &(pcipriv->ledctl.sw_led0), LED_PIN_LED0); + _rtl8723ae_init_led(hw, &(pcipriv->ledctl.sw_led1), LED_PIN_LED1); +} + +static void _rtl8723ae_sw_led_control(struct ieee80211_hw *hw, + enum led_ctl_mode ledaction) +{ + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_led *pLed0 = &(pcipriv->ledctl.sw_led0); + + switch (ledaction) { + case LED_CTL_POWER_ON: + case LED_CTL_LINK: + case LED_CTL_NO_LINK: + rtl8723ae_sw_led_on(hw, pLed0); + break; + case LED_CTL_POWER_OFF: + rtl8723ae_sw_led_off(hw, pLed0); + break; + default: + break; + } +} + +void rtl8723ae_led_control(struct ieee80211_hw *hw, enum led_ctl_mode ledaction) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + + if ((ppsc->rfoff_reason > RF_CHANGE_BY_PS) && + (ledaction == LED_CTL_TX || + ledaction == LED_CTL_RX || + ledaction == LED_CTL_SITE_SURVEY || + ledaction == LED_CTL_LINK || + ledaction == LED_CTL_NO_LINK || + ledaction == LED_CTL_START_TO_LINK || + ledaction == LED_CTL_POWER_ON)) { + return; + } + RT_TRACE(rtlpriv, COMP_LED, DBG_LOUD, "ledaction %d,\n", ledaction); + _rtl8723ae_sw_led_control(hw, ledaction); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/led.h b/drivers/net/wireless/rtlwifi/rtl8723ae/led.h new file mode 100644 index 00000000000..2cb88e78f62 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/led.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL92CE_LED_H__ +#define __RTL92CE_LED_H__ + +void rtl8723ae_init_sw_leds(struct ieee80211_hw *hw); +void rtl8723ae_sw_led_on(struct ieee80211_hw *hw, struct rtl_led *pled); +void rtl8723ae_sw_led_off(struct ieee80211_hw *hw, struct rtl_led *pled); +void rtl8723ae_led_control(struct ieee80211_hw *hw, + enum led_ctl_mode ledaction); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c new file mode 100644 index 00000000000..39cc7938eed --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -0,0 +1,2044 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../ps.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "rf.h" +#include "dm.h" +#include "table.h" + +/* static forward definitions */ +static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset); +static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 offset, u32 data); +static u32 _phy_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset); +static void _phy_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset, u32 data); +static u32 _phy_calculate_bit_shift(u32 bitmask); +static bool _phy_bb8192c_config_parafile(struct ieee80211_hw *hw); +static bool _phy_cfg_mac_w_header(struct ieee80211_hw *hw); +static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype); +static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype); +static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw); +static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, + u32 cmdtableidx, u32 cmdtablesz, + enum swchnlcmd_id cmdid, + u32 para1, u32 para2, + u32 msdelay); +static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, + u8 *stage, u8 *step, u32 *delay); +static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + long power_indbm); +static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, u8 txpwridx); +static void rtl8723ae_phy_set_io(struct ieee80211_hw *hw); + +u32 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, u32 regaddr, + u32 bitmask) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 returnvalue, originalvalue, bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x)\n", regaddr, bitmask); + originalvalue = rtl_read_dword(rtlpriv, regaddr); + bitshift = _phy_calculate_bit_shift(bitmask); + returnvalue = (originalvalue & bitmask) >> bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "BBR MASK=0x%x Addr[0x%x]=0x%x\n", bitmask, regaddr, + originalvalue); + + return returnvalue; +} + +void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 originalvalue, bitshift; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x)\n", regaddr, + bitmask, data); + + if (bitmask != MASKDWORD) { + originalvalue = rtl_read_dword(rtlpriv, regaddr); + bitshift = _phy_calculate_bit_shift(bitmask); + data = ((originalvalue & (~bitmask)) | (data << bitshift)); + } + + rtl_write_dword(rtlpriv, regaddr, data); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x)\n", + regaddr, bitmask, data); +} + +u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, u32 bitmask) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 original_value, readback_value, bitshift; + struct rtl_phy *rtlphy = &(rtlpriv->phy); + unsigned long flags; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), rfpath(%#x), bitmask(%#x)\n", + regaddr, rfpath, bitmask); + + spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); + + if (rtlphy->rf_mode != RF_OP_BY_FW) + original_value = _phy_rf_serial_read(hw, rfpath, regaddr); + else + original_value = _phy_fw_rf_serial_read(hw, rfpath, regaddr); + + bitshift = _phy_calculate_bit_shift(bitmask); + readback_value = (original_value & bitmask) >> bitshift; + + spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), rfpath(%#x), bitmask(%#x), original_value(%#x)\n", + regaddr, rfpath, bitmask, original_value); + + return readback_value; +} + +void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 regaddr, u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 original_value, bitshift; + unsigned long flags; + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", + regaddr, bitmask, data, rfpath); + + spin_lock_irqsave(&rtlpriv->locks.rf_lock, flags); + + if (rtlphy->rf_mode != RF_OP_BY_FW) { + if (bitmask != RFREG_OFFSET_MASK) { + original_value = _phy_rf_serial_read(hw, rfpath, + regaddr); + bitshift = _phy_calculate_bit_shift(bitmask); + data = ((original_value & (~bitmask)) | + (data << bitshift)); + } + + _phy_rf_serial_write(hw, rfpath, regaddr, data); + } else { + if (bitmask != RFREG_OFFSET_MASK) { + original_value = _phy_fw_rf_serial_read(hw, rfpath, + regaddr); + bitshift = _phy_calculate_bit_shift(bitmask); + data = ((original_value & (~bitmask)) | + (data << bitshift)); + } + _phy_fw_rf_serial_write(hw, rfpath, regaddr, data); + } + + spin_unlock_irqrestore(&rtlpriv->locks.rf_lock, flags); + + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, + "regaddr(%#x), bitmask(%#x), data(%#x), rfpath(%#x)\n", + regaddr, bitmask, data, rfpath); +} + +static u32 _phy_fw_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset) +{ + RT_ASSERT(false, "deprecated!\n"); + return 0; +} + +static void _phy_fw_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, + u32 offset, u32 data) +{ + RT_ASSERT(false, "deprecated!\n"); +} + +static u32 _phy_rf_serial_read(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; + u32 newoffset; + u32 tmplong, tmplong2; + u8 rfpi_enable = 0; + u32 retvalue; + + offset &= 0x3f; + newoffset = offset; + if (RT_CANNOT_IO(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "return all one\n"); + return 0xFFFFFFFF; + } + tmplong = rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD); + if (rfpath == RF90_PATH_A) + tmplong2 = tmplong; + else + tmplong2 = rtl_get_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD); + tmplong2 = (tmplong2 & (~BLSSIREADADDRESS)) | + (newoffset << 23) | BLSSIREADEDGE; + rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, + tmplong & (~BLSSIREADEDGE)); + mdelay(1); + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, MASKDWORD, tmplong2); + mdelay(1); + rtl_set_bbreg(hw, RFPGA0_XA_HSSIPARAMETER2, MASKDWORD, + tmplong | BLSSIREADEDGE); + mdelay(1); + if (rfpath == RF90_PATH_A) + rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XA_HSSIPARAMETER1, + BIT(8)); + else if (rfpath == RF90_PATH_B) + rfpi_enable = (u8) rtl_get_bbreg(hw, RFPGA0_XB_HSSIPARAMETER1, + BIT(8)); + if (rfpi_enable) + retvalue = rtl_get_bbreg(hw, pphyreg->rf_rbpi, + BLSSIREADBACKDATA); + else + retvalue = rtl_get_bbreg(hw, pphyreg->rf_rb, + BLSSIREADBACKDATA); + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFR-%d Addr[0x%x]=0x%x\n", + rfpath, pphyreg->rf_rb, retvalue); + return retvalue; +} + +static void _phy_rf_serial_write(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 offset, u32 data) +{ + u32 data_and_addr; + u32 newoffset; + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct bb_reg_def *pphyreg = &rtlphy->phyreg_def[rfpath]; + + if (RT_CANNOT_IO(hw)) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "stop\n"); + return; + } + offset &= 0x3f; + newoffset = offset; + data_and_addr = ((newoffset << 20) | (data & 0x000fffff)) & 0x0fffffff; + rtl_set_bbreg(hw, pphyreg->rf3wire_offset, MASKDWORD, data_and_addr); + RT_TRACE(rtlpriv, COMP_RF, DBG_TRACE, "RFW-%d Addr[0x%x]=0x%x\n", + rfpath, pphyreg->rf3wire_offset, data_and_addr); +} + +static u32 _phy_calculate_bit_shift(u32 bitmask) +{ + u32 i; + + for (i = 0; i <= 31; i++) { + if (((bitmask >> i) & 0x1) == 1) + break; + } + return i; +} + +static void _rtl8723ae_phy_bb_config_1t(struct ieee80211_hw *hw) +{ + rtl_set_bbreg(hw, RFPGA0_TXINFO, 0x3, 0x2); + rtl_set_bbreg(hw, RFPGA1_TXINFO, 0x300033, 0x200022); + rtl_set_bbreg(hw, RCCK0_AFESETTING, MASKBYTE3, 0x45); + rtl_set_bbreg(hw, ROFDM0_TRXPATHENABLE, MASKBYTE0, 0x23); + rtl_set_bbreg(hw, ROFDM0_AGCPARAMETER1, 0x30, 0x1); + rtl_set_bbreg(hw, 0xe74, 0x0c000000, 0x2); + rtl_set_bbreg(hw, 0xe78, 0x0c000000, 0x2); + rtl_set_bbreg(hw, 0xe7c, 0x0c000000, 0x2); + rtl_set_bbreg(hw, 0xe80, 0x0c000000, 0x2); + rtl_set_bbreg(hw, 0xe88, 0x0c000000, 0x2); +} + +bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + bool rtstatus = _phy_cfg_mac_w_header(hw); + rtl_write_byte(rtlpriv, 0x04CA, 0x0A); + return rtstatus; +} + +bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw) +{ + bool rtstatus = true; + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmpu1b; + u8 reg_hwparafile = 1; + + _phy_init_bb_rf_reg_def(hw); + + /* 1. 0x28[1] = 1 */ + tmpu1b = rtl_read_byte(rtlpriv, REG_AFE_PLL_CTRL); + udelay(2); + rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL, (tmpu1b|BIT(1))); + udelay(2); + /* 2. 0x29[7:0] = 0xFF */ + rtl_write_byte(rtlpriv, REG_AFE_PLL_CTRL+1, 0xff); + udelay(2); + + /* 3. 0x02[1:0] = 2b'11 */ + tmpu1b = rtl_read_byte(rtlpriv, REG_SYS_FUNC_EN); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, (tmpu1b | + FEN_BB_GLB_RSTn | FEN_BBRSTB)); + + /* 4. 0x25[6] = 0 */ + tmpu1b = rtl_read_byte(rtlpriv, REG_AFE_XTAL_CTRL+1); + rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL+1, (tmpu1b&(~BIT(6)))); + + /* 5. 0x24[20] = 0 Advised by SD3 Alex Wang. 2011.02.09. */ + tmpu1b = rtl_read_byte(rtlpriv, REG_AFE_XTAL_CTRL+2); + rtl_write_byte(rtlpriv, REG_AFE_XTAL_CTRL+2, (tmpu1b&(~BIT(4)))); + + /* 6. 0x1f[7:0] = 0x07 */ + rtl_write_byte(rtlpriv, REG_RF_CTRL, 0x07); + + if (reg_hwparafile == 1) + rtstatus = _phy_bb8192c_config_parafile(hw); + return rtstatus; +} + +bool rtl8723ae_phy_rf_config(struct ieee80211_hw *hw) +{ + return rtl8723ae_phy_rf6052_config(hw); +} + +static bool _phy_bb8192c_config_parafile(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + bool rtstatus; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "==>\n"); + rtstatus = _phy_cfg_bb_w_header(hw, BASEBAND_CONFIG_PHY_REG); + if (rtstatus != true) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "Write BB Reg Fail!!"); + return false; + } + + if (rtlphy->rf_type == RF_1T2R) { + _rtl8723ae_phy_bb_config_1t(hw); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Config to 1T!!\n"); + } + if (rtlefuse->autoload_failflag == false) { + rtlphy->pwrgroup_cnt = 0; + rtstatus = _phy_cfg_bb_w_pgheader(hw, BASEBAND_CONFIG_PHY_REG); + } + if (rtstatus != true) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "BB_PG Reg Fail!!"); + return false; + } + rtstatus = _phy_cfg_bb_w_header(hw, BASEBAND_CONFIG_AGC_TAB); + if (rtstatus != true) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, "AGC Table Fail\n"); + return false; + } + rtlphy->cck_high_power = (bool) (rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER2, 0x200)); + return true; +} + +static bool _phy_cfg_mac_w_header(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + u32 arraylength; + u32 *ptrarray; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Read Rtl723MACPHY_Array\n"); + arraylength = RTL8723E_MACARRAYLENGTH; + ptrarray = RTL8723EMAC_ARRAY; + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Img:RTL8192CEMAC_2T_ARRAY\n"); + for (i = 0; i < arraylength; i = i + 2) + rtl_write_byte(rtlpriv, ptrarray[i], (u8) ptrarray[i + 1]); + return true; +} + +static bool _phy_cfg_bb_w_header(struct ieee80211_hw *hw, u8 configtype) +{ + int i; + u32 *phy_regarray_table; + u32 *agctab_array_table; + u16 phy_reg_arraylen, agctab_arraylen; + struct rtl_priv *rtlpriv = rtl_priv(hw); + + agctab_arraylen = RTL8723E_AGCTAB_1TARRAYLENGTH; + agctab_array_table = RTL8723EAGCTAB_1TARRAY; + phy_reg_arraylen = RTL8723E_PHY_REG_1TARRAY_LENGTH; + phy_regarray_table = RTL8723EPHY_REG_1TARRAY; + if (configtype == BASEBAND_CONFIG_PHY_REG) { + for (i = 0; i < phy_reg_arraylen; i = i + 2) { + if (phy_regarray_table[i] == 0xfe) + mdelay(50); + else if (phy_regarray_table[i] == 0xfd) + mdelay(5); + else if (phy_regarray_table[i] == 0xfc) + mdelay(1); + else if (phy_regarray_table[i] == 0xfb) + udelay(50); + else if (phy_regarray_table[i] == 0xfa) + udelay(5); + else if (phy_regarray_table[i] == 0xf9) + udelay(1); + rtl_set_bbreg(hw, phy_regarray_table[i], MASKDWORD, + phy_regarray_table[i + 1]); + udelay(1); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "The phy_regarray_table[0] is %x" + " Rtl819XPHY_REGArray[1] is %x\n", + phy_regarray_table[i], + phy_regarray_table[i + 1]); + } + } 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, + agctab_array_table[i + 1]); + udelay(1); + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "The agctab_array_table[0] is " + "%x Rtl819XPHY_REGArray[1] is %x\n", + agctab_array_table[i], + agctab_array_table[i + 1]); + } + } + return true; +} + +static void _st_pwrIdx_dfrate_off(struct ieee80211_hw *hw, u32 regaddr, + u32 bitmask, u32 data) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + switch (regaddr) { + case RTXAGC_A_RATE18_06: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][0] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][0] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][0]); + break; + case RTXAGC_A_RATE54_24: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][1] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][1] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][1]); + break; + case RTXAGC_A_CCK1_MCS32: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][6] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][6] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][6]); + break; + case RTXAGC_B_CCK11_A_CCK2_11: + if (bitmask == 0xffffff00) { + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][7] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][7] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][7]); + } + if (bitmask == 0x000000ff) { + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][15] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][15] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][15]); + } + break; + case RTXAGC_A_MCS03_MCS00: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][2] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][2] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][2]); + break; + case RTXAGC_A_MCS07_MCS04: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][3] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][3] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][3]); + break; + case RTXAGC_A_MCS11_MCS08: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][4] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][4] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][4]); + break; + case RTXAGC_A_MCS15_MCS12: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][5] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][5] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][5]); + break; + case RTXAGC_B_RATE18_06: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][8] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][8] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][8]); + break; + case RTXAGC_B_RATE54_24: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][9] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][9] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][9]); + break; + case RTXAGC_B_CCK1_55_MCS32: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][14] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][14] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][14]); + break; + case RTXAGC_B_MCS03_MCS00: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][10] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][10] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][10]); + break; + case RTXAGC_B_MCS07_MCS04: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][11] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][11] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][11]); + break; + case RTXAGC_B_MCS11_MCS08: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][12] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][12] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][12]); + break; + case RTXAGC_B_MCS15_MCS12: + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][13] = data; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "MCSTxPowerLevelOriginalOffset[%d][13] = 0x%x\n", + rtlphy->pwrgroup_cnt, + rtlphy->mcs_offset[rtlphy->pwrgroup_cnt][13]); + rtlphy->pwrgroup_cnt++; + break; + } +} + +static bool _phy_cfg_bb_w_pgheader(struct ieee80211_hw *hw, u8 configtype) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int i; + u32 *phy_regarray_table_pg; + u16 phy_regarray_pg_len; + + phy_regarray_pg_len = RTL8723E_PHY_REG_ARRAY_PGLENGTH; + phy_regarray_table_pg = RTL8723EPHY_REG_ARRAY_PG; + + if (configtype == BASEBAND_CONFIG_PHY_REG) { + for (i = 0; i < phy_regarray_pg_len; i = i + 3) { + if (phy_regarray_table_pg[i] == 0xfe) + mdelay(50); + else if (phy_regarray_table_pg[i] == 0xfd) + mdelay(5); + else if (phy_regarray_table_pg[i] == 0xfc) + mdelay(1); + else if (phy_regarray_table_pg[i] == 0xfb) + udelay(50); + else if (phy_regarray_table_pg[i] == 0xfa) + udelay(5); + else if (phy_regarray_table_pg[i] == 0xf9) + udelay(1); + + _st_pwrIdx_dfrate_off(hw, phy_regarray_table_pg[i], + phy_regarray_table_pg[i + 1], + phy_regarray_table_pg[i + 2]); + } + } else { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "configtype != BaseBand_Config_PHY_REG\n"); + } + return true; +} + +bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int i; + bool rtstatus = true; + u32 *radioa_array_table; + u32 *radiob_array_table; + u16 radioa_arraylen, radiob_arraylen; + + radioa_arraylen = Rtl8723ERADIOA_1TARRAYLENGTH; + radioa_array_table = RTL8723E_RADIOA_1TARRAY; + radiob_arraylen = RTL8723E_RADIOB_1TARRAYLENGTH; + radiob_array_table = RTL8723E_RADIOB_1TARRAY; + + rtstatus = true; + + switch (rfpath) { + case RF90_PATH_A: + for (i = 0; i < radioa_arraylen; i = i + 2) { + if (radioa_array_table[i] == 0xfe) + mdelay(50); + else if (radioa_array_table[i] == 0xfd) + mdelay(5); + else if (radioa_array_table[i] == 0xfc) + mdelay(1); + else if (radioa_array_table[i] == 0xfb) + udelay(50); + else if (radioa_array_table[i] == 0xfa) + udelay(5); + else if (radioa_array_table[i] == 0xf9) + udelay(1); + else { + rtl_set_rfreg(hw, rfpath, radioa_array_table[i], + RFREG_OFFSET_MASK, + radioa_array_table[i + 1]); + udelay(1); + } + } + break; + case RF90_PATH_B: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + case RF90_PATH_C: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + case RF90_PATH_D: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + return true; +} + +void rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + rtlphy->default_initialgain[0] = + (u8) rtl_get_bbreg(hw, ROFDM0_XAAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[1] = + (u8) rtl_get_bbreg(hw, ROFDM0_XBAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[2] = + (u8) rtl_get_bbreg(hw, ROFDM0_XCAGCCORE1, MASKBYTE0); + rtlphy->default_initialgain[3] = + (u8) rtl_get_bbreg(hw, ROFDM0_XDAGCCORE1, MASKBYTE0); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Default initial gain (c50=0x%x, c58=0x%x, c60=0x%x, c68=0x%x\n", + rtlphy->default_initialgain[0], + rtlphy->default_initialgain[1], + rtlphy->default_initialgain[2], + rtlphy->default_initialgain[3]); + + rtlphy->framesync = (u8) rtl_get_bbreg(hw, + ROFDM0_RXDETECTOR3, MASKBYTE0); + rtlphy->framesync_c34 = rtl_get_bbreg(hw, + ROFDM0_RXDETECTOR2, MASKDWORD); + + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Default framesync (0x%x) = 0x%x\n", + ROFDM0_RXDETECTOR3, rtlphy->framesync); +} + +static void _phy_init_bb_rf_reg_def(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + rtlphy->phyreg_def[RF90_PATH_A].rfintfs = RFPGA0_XAB_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_B].rfintfs = RFPGA0_XAB_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_C].rfintfs = RFPGA0_XCD_RFINTERFACESW; + rtlphy->phyreg_def[RF90_PATH_D].rfintfs = RFPGA0_XCD_RFINTERFACESW; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfi = RFPGA0_XAB_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_B].rfintfi = RFPGA0_XAB_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_C].rfintfi = RFPGA0_XCD_RFINTERFACERB; + rtlphy->phyreg_def[RF90_PATH_D].rfintfi = RFPGA0_XCD_RFINTERFACERB; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfo = RFPGA0_XA_RFINTERFACEOE; + rtlphy->phyreg_def[RF90_PATH_B].rfintfo = RFPGA0_XB_RFINTERFACEOE; + + rtlphy->phyreg_def[RF90_PATH_A].rfintfe = RFPGA0_XA_RFINTERFACEOE; + rtlphy->phyreg_def[RF90_PATH_B].rfintfe = RFPGA0_XB_RFINTERFACEOE; + + rtlphy->phyreg_def[RF90_PATH_A].rf3wire_offset = + RFPGA0_XA_LSSIPARAMETER; + rtlphy->phyreg_def[RF90_PATH_B].rf3wire_offset = + RFPGA0_XB_LSSIPARAMETER; + + rtlphy->phyreg_def[RF90_PATH_A].rflssi_select = rFPGA0_XAB_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_B].rflssi_select = rFPGA0_XAB_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_C].rflssi_select = rFPGA0_XCD_RFPARAMETER; + rtlphy->phyreg_def[RF90_PATH_D].rflssi_select = rFPGA0_XCD_RFPARAMETER; + + rtlphy->phyreg_def[RF90_PATH_A].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_B].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_C].rftxgain_stage = RFPGA0_TXGAINSTAGE; + rtlphy->phyreg_def[RF90_PATH_D].rftxgain_stage = RFPGA0_TXGAINSTAGE; + + rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para1 = RFPGA0_XA_HSSIPARAMETER1; + rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para1 = RFPGA0_XB_HSSIPARAMETER1; + + rtlphy->phyreg_def[RF90_PATH_A].rfhssi_para2 = RFPGA0_XA_HSSIPARAMETER2; + rtlphy->phyreg_def[RF90_PATH_B].rfhssi_para2 = RFPGA0_XB_HSSIPARAMETER2; + + rtlphy->phyreg_def[RF90_PATH_A].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_B].rfsw_ctrl = RFPGA0_XAB_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_C].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; + rtlphy->phyreg_def[RF90_PATH_D].rfsw_ctrl = RFPGA0_XCD_SWITCHCONTROL; + + rtlphy->phyreg_def[RF90_PATH_A].rfagc_control1 = ROFDM0_XAAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_B].rfagc_control1 = ROFDM0_XBAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_C].rfagc_control1 = ROFDM0_XCAGCCORE1; + rtlphy->phyreg_def[RF90_PATH_D].rfagc_control1 = ROFDM0_XDAGCCORE1; + + rtlphy->phyreg_def[RF90_PATH_A].rfagc_control2 = ROFDM0_XAAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_B].rfagc_control2 = ROFDM0_XBAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_C].rfagc_control2 = ROFDM0_XCAGCCORE2; + rtlphy->phyreg_def[RF90_PATH_D].rfagc_control2 = ROFDM0_XDAGCCORE2; + + rtlphy->phyreg_def[RF90_PATH_A].rfrxiq_imbal = ROFDM0_XARXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_B].rfrxiq_imbal = ROFDM0_XBRXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_C].rfrxiq_imbal = ROFDM0_XCRXIQIMBANLANCE; + rtlphy->phyreg_def[RF90_PATH_D].rfrxiq_imbal = ROFDM0_XDRXIQIMBALANCE; + + rtlphy->phyreg_def[RF90_PATH_A].rfrx_afe = ROFDM0_XARXAFE; + rtlphy->phyreg_def[RF90_PATH_B].rfrx_afe = ROFDM0_XBRXAFE; + rtlphy->phyreg_def[RF90_PATH_C].rfrx_afe = ROFDM0_XCRXAFE; + rtlphy->phyreg_def[RF90_PATH_D].rfrx_afe = ROFDM0_XDRXAFE; + + rtlphy->phyreg_def[RF90_PATH_A].rftxiq_imbal = ROFDM0_XATXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_B].rftxiq_imbal = ROFDM0_XBTXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_C].rftxiq_imbal = ROFDM0_XCTXIQIMBALANCE; + rtlphy->phyreg_def[RF90_PATH_D].rftxiq_imbal = ROFDM0_XDTXIQIMBALANCE; + + rtlphy->phyreg_def[RF90_PATH_A].rftx_afe = ROFDM0_XATXAFE; + rtlphy->phyreg_def[RF90_PATH_B].rftx_afe = ROFDM0_XBTXAFE; + rtlphy->phyreg_def[RF90_PATH_C].rftx_afe = ROFDM0_XCTXAFE; + rtlphy->phyreg_def[RF90_PATH_D].rftx_afe = ROFDM0_XDTXAFE; + + rtlphy->phyreg_def[RF90_PATH_A].rf_rb = RFPGA0_XA_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_B].rf_rb = RFPGA0_XB_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_C].rf_rb = RFPGA0_XC_LSSIREADBACK; + rtlphy->phyreg_def[RF90_PATH_D].rf_rb = RFPGA0_XD_LSSIREADBACK; + + rtlphy->phyreg_def[RF90_PATH_A].rf_rbpi = TRANSCEIVEA_HSPI_READBACK; + rtlphy->phyreg_def[RF90_PATH_B].rf_rbpi = TRANSCEIVEB_HSPI_READBACK; +} + +void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, long *powerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 txpwr_level; + long txpwr_dbm; + + txpwr_level = rtlphy->cur_cck_txpwridx; + txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_B, txpwr_level); + txpwr_level = rtlphy->cur_ofdm24g_txpwridx + + rtlefuse->legacy_ht_txpowerdiff; + if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, txpwr_level) > txpwr_dbm) + txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_G, + txpwr_level); + txpwr_level = rtlphy->cur_ofdm24g_txpwridx; + if (_phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, txpwr_level) > + txpwr_dbm) + txpwr_dbm = _phy_txpwr_idx_to_dbm(hw, WIRELESS_MODE_N_24G, + txpwr_level); + *powerlevel = txpwr_dbm; +} + +static void _rtl8723ae_get_txpower_index(struct ieee80211_hw *hw, u8 channel, + u8 *cckpowerlevel, u8 *ofdmpowerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 index = (channel - 1); + + cckpowerlevel[RF90_PATH_A] = + rtlefuse->txpwrlevel_cck[RF90_PATH_A][index]; + cckpowerlevel[RF90_PATH_B] = + rtlefuse->txpwrlevel_cck[RF90_PATH_B][index]; + if (get_rf_type(rtlphy) == RF_1T2R || get_rf_type(rtlphy) == RF_1T1R) { + ofdmpowerlevel[RF90_PATH_A] = + rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_A][index]; + ofdmpowerlevel[RF90_PATH_B] = + rtlefuse->txpwrlevel_ht40_1s[RF90_PATH_B][index]; + } else if (get_rf_type(rtlphy) == RF_2T2R) { + ofdmpowerlevel[RF90_PATH_A] = + rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_A][index]; + ofdmpowerlevel[RF90_PATH_B] = + rtlefuse->txpwrlevel_ht40_2s[RF90_PATH_B][index]; + } +} + +static void _rtl8723ae_ccxpower_index_check(struct ieee80211_hw *hw, + u8 channel, u8 *cckpowerlevel, + u8 *ofdmpowerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + rtlphy->cur_cck_txpwridx = cckpowerlevel[0]; + rtlphy->cur_ofdm24g_txpwridx = ofdmpowerlevel[0]; +} + +void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, u8 channel) +{ + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 cckpowerlevel[2], ofdmpowerlevel[2]; + + if (rtlefuse->txpwr_fromeprom == false) + return; + _rtl8723ae_get_txpower_index(hw, channel, &cckpowerlevel[0], + &ofdmpowerlevel[0]); + _rtl8723ae_ccxpower_index_check(hw, channel, &cckpowerlevel[0], + &ofdmpowerlevel[0]); + rtl8723ae_phy_rf6052_set_cck_txpower(hw, &cckpowerlevel[0]); + rtl8723ae_phy_rf6052_set_ofdm_txpower(hw, &ofdmpowerlevel[0], channel); +} + +bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, long power_indbm) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 idx; + u8 rf_path; + u8 ccktxpwridx = _phy_dbm_to_txpwr_Idx(hw, WIRELESS_MODE_B, + power_indbm); + u8 ofdmtxpwridx = _phy_dbm_to_txpwr_Idx(hw, WIRELESS_MODE_N_24G, + power_indbm); + if (ofdmtxpwridx - rtlefuse->legacy_ht_txpowerdiff > 0) + ofdmtxpwridx -= rtlefuse->legacy_ht_txpowerdiff; + else + ofdmtxpwridx = 0; + RT_TRACE(rtlpriv, COMP_TXAGC, DBG_TRACE, + "%lx dBm, ccktxpwridx = %d, ofdmtxpwridx = %d\n", + power_indbm, ccktxpwridx, ofdmtxpwridx); + for (idx = 0; idx < 14; idx++) { + for (rf_path = 0; rf_path < 2; rf_path++) { + rtlefuse->txpwrlevel_cck[rf_path][idx] = ccktxpwridx; + rtlefuse->txpwrlevel_ht40_1s[rf_path][idx] = + ofdmtxpwridx; + rtlefuse->txpwrlevel_ht40_2s[rf_path][idx] = + ofdmtxpwridx; + } + } + rtl8723ae_phy_set_txpower_level(hw, rtlphy->current_channel); + return true; +} + +static u8 _phy_dbm_to_txpwr_Idx(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, + long power_indbm) +{ + u8 txpwridx; + long offset; + + switch (wirelessmode) { + case WIRELESS_MODE_B: + offset = -7; + break; + case WIRELESS_MODE_G: + case WIRELESS_MODE_N_24G: + offset = -8; + break; + default: + offset = -8; + break; + } + + if ((power_indbm - offset) > 0) + txpwridx = (u8) ((power_indbm - offset) * 2); + else + txpwridx = 0; + + if (txpwridx > MAX_TXPWR_IDX_NMODE_92S) + txpwridx = MAX_TXPWR_IDX_NMODE_92S; + + return txpwridx; +} + +static long _phy_txpwr_idx_to_dbm(struct ieee80211_hw *hw, + enum wireless_mode wirelessmode, u8 txpwridx) +{ + long offset; + long pwrout_dbm; + + switch (wirelessmode) { + case WIRELESS_MODE_B: + offset = -7; + break; + case WIRELESS_MODE_G: + case WIRELESS_MODE_N_24G: + offset = -8; + break; + default: + offset = -8; + break; + } + pwrout_dbm = txpwridx / 2 + offset; + return pwrout_dbm; +} + +void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, u8 operation) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + enum io_type iotype; + + if (!is_hal_stop(rtlhal)) { + switch (operation) { + case SCAN_OPT_BACKUP: + iotype = IO_CMD_PAUSE_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + + break; + case SCAN_OPT_RESTORE: + iotype = IO_CMD_RESUME_DM_BY_SCAN; + rtlpriv->cfg->ops->set_hw_reg(hw, + HW_VAR_IO_CMD, + (u8 *)&iotype); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Unknown Scan Backup operation.\n"); + break; + } + } +} + +void rtl8723ae_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)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + u8 reg_bw_opmode; + u8 reg_prsr_rsc; + + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, + "Switch to %s bandwidth\n", + rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20 ? + "20MHz" : "40MHz"); + + if (is_hal_stop(rtlhal)) { + rtlphy->set_bwmode_inprogress = false; + return; + } + + reg_bw_opmode = rtl_read_byte(rtlpriv, REG_BWOPMODE); + reg_prsr_rsc = rtl_read_byte(rtlpriv, REG_RRSR + 2); + + switch (rtlphy->current_chan_bw) { + case HT_CHANNEL_WIDTH_20: + reg_bw_opmode |= BW_OPMODE_20MHZ; + rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); + break; + case HT_CHANNEL_WIDTH_20_40: + reg_bw_opmode &= ~BW_OPMODE_20MHZ; + rtl_write_byte(rtlpriv, REG_BWOPMODE, reg_bw_opmode); + reg_prsr_rsc = + (reg_prsr_rsc & 0x90) | (mac->cur_40_prime_sc << 5); + rtl_write_byte(rtlpriv, REG_RRSR + 2, reg_prsr_rsc); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); + break; + } + + switch (rtlphy->current_chan_bw) { + case HT_CHANNEL_WIDTH_20: + rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x0); + rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x0); + rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 1); + break; + case HT_CHANNEL_WIDTH_20_40: + rtl_set_bbreg(hw, RFPGA0_RFMOD, BRFMOD, 0x1); + rtl_set_bbreg(hw, RFPGA1_RFMOD, BRFMOD, 0x1); + + rtl_set_bbreg(hw, RCCK0_SYSTEM, BCCK_SIDEBAND, + (mac->cur_40_prime_sc >> 1)); + rtl_set_bbreg(hw, ROFDM1_LSTF, 0xC00, mac->cur_40_prime_sc); + rtl_set_bbreg(hw, RFPGA0_ANALOGPARAMETER2, BIT(10), 0); + + rtl_set_bbreg(hw, 0x818, (BIT(26) | BIT(27)), + (mac->cur_40_prime_sc == + HAL_PRIME_CHNL_OFFSET_LOWER) ? 2 : 1); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "unknown bandwidth: %#X\n", rtlphy->current_chan_bw); + break; + } + rtl8723ae_phy_rf6052_set_bandwidth(hw, rtlphy->current_chan_bw); + rtlphy->set_bwmode_inprogress = false; + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); +} + +void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + u8 tmp_bw = rtlphy->current_chan_bw; + + if (rtlphy->set_bwmode_inprogress) + return; + rtlphy->set_bwmode_inprogress = true; + if ((!is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { + rtl8723ae_phy_set_bw_mode_callback(hw); + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "FALSE driver sleep or unload\n"); + rtlphy->set_bwmode_inprogress = false; + rtlphy->current_chan_bw = tmp_bw; + } +} + +void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 delay; + + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, + "switch to channel%d\n", rtlphy->current_channel); + if (is_hal_stop(rtlhal)) + return; + do { + if (!rtlphy->sw_chnl_inprogress) + break; + if (!_phy_sw_chnl_step_by_step + (hw, rtlphy->current_channel, &rtlphy->sw_chnl_stage, + &rtlphy->sw_chnl_step, &delay)) { + if (delay > 0) + mdelay(delay); + else + continue; + } else { + rtlphy->sw_chnl_inprogress = false; + } + break; + } while (true); + RT_TRACE(rtlpriv, COMP_SCAN, DBG_TRACE, "<==\n"); +} + +u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + if (rtlphy->sw_chnl_inprogress) + return 0; + if (rtlphy->set_bwmode_inprogress) + return 0; + RT_ASSERT((rtlphy->current_channel <= 14), + "WIRELESS_MODE_G but channel>14"); + rtlphy->sw_chnl_inprogress = true; + rtlphy->sw_chnl_stage = 0; + rtlphy->sw_chnl_step = 0; + if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) { + rtl8723ae_phy_sw_chnl_callback(hw); + RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, + "sw_chnl_inprogress false schdule workitem\n"); + rtlphy->sw_chnl_inprogress = false; + } else { + RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, + "sw_chnl_inprogress false driver sleep or unload\n"); + rtlphy->sw_chnl_inprogress = false; + } + return 1; +} + +static void _rtl8723ae_phy_sw_rf_seting(struct ieee80211_hw *hw, u8 channel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) { + if (channel == 6 && rtlphy->current_chan_bw == + HT_CHANNEL_WIDTH_20) + rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G1, MASKDWORD, + 0x00255); + else{ + u32 backupRF0x1A = (u32)rtl_get_rfreg(hw, RF90_PATH_A, + RF_RX_G1, RFREG_OFFSET_MASK); + rtl_set_rfreg(hw, RF90_PATH_A, RF_RX_G1, MASKDWORD, + backupRF0x1A); + } + } +} + +static bool _phy_sw_chnl_step_by_step(struct ieee80211_hw *hw, u8 channel, + u8 *stage, u8 *step, u32 *delay) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct swchnlcmd precommoncmd[MAX_PRECMD_CNT]; + u32 precommoncmdcnt; + struct swchnlcmd postcommoncmd[MAX_POSTCMD_CNT]; + u32 postcommoncmdcnt; + struct swchnlcmd rfdependcmd[MAX_RFDEPENDCMD_CNT]; + u32 rfdependcmdcnt; + struct swchnlcmd *currentcmd = NULL; + u8 rfpath; + u8 num_total_rfpath = rtlphy->num_total_rfpath; + + precommoncmdcnt = 0; + _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, CMDID_SET_TXPOWEROWER_LEVEL, + 0, 0, 0); + _phy_set_sw_chnl_cmdarray(precommoncmd, precommoncmdcnt++, + MAX_PRECMD_CNT, CMDID_END, 0, 0, 0); + postcommoncmdcnt = 0; + + _phy_set_sw_chnl_cmdarray(postcommoncmd, postcommoncmdcnt++, + MAX_POSTCMD_CNT, CMDID_END, 0, 0, 0); + rfdependcmdcnt = 0; + + RT_ASSERT((channel >= 1 && channel <= 14), + "illegal channel for Zebra: %d\n", channel); + + _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, CMDID_RF_WRITEREG, + RF_CHNLBW, channel, 10); + + _phy_set_sw_chnl_cmdarray(rfdependcmd, rfdependcmdcnt++, + MAX_RFDEPENDCMD_CNT, CMDID_END, 0, 0, 0); + + do { + switch (*stage) { + case 0: + currentcmd = &precommoncmd[*step]; + break; + case 1: + currentcmd = &rfdependcmd[*step]; + break; + case 2: + currentcmd = &postcommoncmd[*step]; + break; + } + + if (currentcmd->cmdid == CMDID_END) { + if ((*stage) == 2) { + return true; + } else { + (*stage)++; + (*step) = 0; + continue; + } + } + + switch (currentcmd->cmdid) { + case CMDID_SET_TXPOWEROWER_LEVEL: + rtl8723ae_phy_set_txpower_level(hw, channel); + break; + case CMDID_WRITEPORT_ULONG: + rtl_write_dword(rtlpriv, currentcmd->para1, + currentcmd->para2); + break; + case CMDID_WRITEPORT_USHORT: + rtl_write_word(rtlpriv, currentcmd->para1, + (u16) currentcmd->para2); + break; + case CMDID_WRITEPORT_UCHAR: + rtl_write_byte(rtlpriv, currentcmd->para1, + (u8) currentcmd->para2); + break; + case CMDID_RF_WRITEREG: + for (rfpath = 0; rfpath < num_total_rfpath; rfpath++) { + rtlphy->rfreg_chnlval[rfpath] = + ((rtlphy->rfreg_chnlval[rfpath] & + 0xfffffc00) | currentcmd->para2); + + rtl_set_rfreg(hw, (enum radio_path)rfpath, + currentcmd->para1, + RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[rfpath]); + } + _rtl8723ae_phy_sw_rf_seting(hw, channel); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + + break; + } while (true); + + (*delay) = currentcmd->msdelay; + (*step)++; + return false; +} + +static bool _phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, + u32 cmdtableidx, u32 cmdtablesz, + enum swchnlcmd_id cmdid, u32 para1, + u32 para2, u32 msdelay) +{ + struct swchnlcmd *pcmd; + + if (cmdtable == NULL) { + RT_ASSERT(false, "cmdtable cannot be NULL.\n"); + return false; + } + + if (cmdtableidx >= cmdtablesz) + return false; + + pcmd = cmdtable + cmdtableidx; + pcmd->cmdid = cmdid; + pcmd->para1 = para1; + pcmd->para2 = para2; + pcmd->msdelay = msdelay; + return true; +} + +static u8 _rtl8723ae_phy_path_a_iqk(struct ieee80211_hw *hw, bool config_pathb) +{ + u32 reg_eac, reg_e94, reg_e9c, reg_ea4; + u8 result = 0x00; + + rtl_set_bbreg(hw, 0xe30, MASKDWORD, 0x10008c1f); + rtl_set_bbreg(hw, 0xe34, MASKDWORD, 0x10008c1f); + rtl_set_bbreg(hw, 0xe38, MASKDWORD, 0x82140102); + rtl_set_bbreg(hw, 0xe3c, MASKDWORD, + config_pathb ? 0x28160202 : 0x28160502); + + if (config_pathb) { + rtl_set_bbreg(hw, 0xe50, MASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe54, MASKDWORD, 0x10008c22); + rtl_set_bbreg(hw, 0xe58, MASKDWORD, 0x82140102); + rtl_set_bbreg(hw, 0xe5c, MASKDWORD, 0x28160202); + } + + rtl_set_bbreg(hw, 0xe4c, MASKDWORD, 0x001028d1); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf9000000); + rtl_set_bbreg(hw, 0xe48, MASKDWORD, 0xf8000000); + + mdelay(IQK_DELAY_TIME); + + reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); + reg_e94 = rtl_get_bbreg(hw, 0xe94, MASKDWORD); + reg_e9c = rtl_get_bbreg(hw, 0xe9c, MASKDWORD); + reg_ea4 = rtl_get_bbreg(hw, 0xea4, MASKDWORD); + + if (!(reg_eac & BIT(28)) && + (((reg_e94 & 0x03FF0000) >> 16) != 0x142) && + (((reg_e9c & 0x03FF0000) >> 16) != 0x42)) + result |= 0x01; + else + return result; + + if (!(reg_eac & BIT(27)) && + (((reg_ea4 & 0x03FF0000) >> 16) != 0x132) && + (((reg_eac & 0x03FF0000) >> 16) != 0x36)) + result |= 0x02; + return result; +} + +static u8 _rtl8723ae_phy_path_b_iqk(struct ieee80211_hw *hw) +{ + u32 reg_eac, reg_eb4, reg_ebc, reg_ec4, reg_ecc; + u8 result = 0x00; + + rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000002); + rtl_set_bbreg(hw, 0xe60, MASKDWORD, 0x00000000); + mdelay(IQK_DELAY_TIME); + reg_eac = rtl_get_bbreg(hw, 0xeac, MASKDWORD); + reg_eb4 = rtl_get_bbreg(hw, 0xeb4, MASKDWORD); + reg_ebc = rtl_get_bbreg(hw, 0xebc, MASKDWORD); + reg_ec4 = rtl_get_bbreg(hw, 0xec4, MASKDWORD); + reg_ecc = rtl_get_bbreg(hw, 0xecc, MASKDWORD); + + if (!(reg_eac & BIT(31)) && + (((reg_eb4 & 0x03FF0000) >> 16) != 0x142) && + (((reg_ebc & 0x03FF0000) >> 16) != 0x42)) + result |= 0x01; + else + return result; + if (!(reg_eac & BIT(30)) && + (((reg_ec4 & 0x03FF0000) >> 16) != 0x132) && + (((reg_ecc & 0x03FF0000) >> 16) != 0x36)) + result |= 0x02; + return result; +} + +static void phy_path_a_fill_iqk_matrix(struct ieee80211_hw *hw, bool iqk_ok, + long result[][8], u8 final_candidate, + bool btxonly) +{ + u32 oldval_0, x, tx0_a, reg; + long y, tx0_c; + + if (final_candidate == 0xFF) { + return; + } else if (iqk_ok) { + oldval_0 = (rtl_get_bbreg(hw, ROFDM0_XATXIQIMBALANCE, + MASKDWORD) >> 22) & 0x3FF; + x = result[final_candidate][0]; + if ((x & 0x00000200) != 0) + x = x | 0xFFFFFC00; + tx0_a = (x * oldval_0) >> 8; + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x3FF, tx0_a); + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(31), + ((x * oldval_0 >> 7) & 0x1)); + y = result[final_candidate][1]; + if ((y & 0x00000200) != 0) + y = y | 0xFFFFFC00; + tx0_c = (y * oldval_0) >> 8; + rtl_set_bbreg(hw, ROFDM0_XCTXAFE, 0xF0000000, + ((tx0_c & 0x3C0) >> 6)); + rtl_set_bbreg(hw, ROFDM0_XATXIQIMBALANCE, 0x003F0000, + (tx0_c & 0x3F)); + rtl_set_bbreg(hw, ROFDM0_ECCATHRESHOLD, BIT(29), + ((y * oldval_0 >> 7) & 0x1)); + if (btxonly) + return; + reg = result[final_candidate][2]; + rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0x3FF, reg); + reg = result[final_candidate][3] & 0x3F; + rtl_set_bbreg(hw, ROFDM0_XARXIQIMBALANCE, 0xFC00, reg); + reg = (result[final_candidate][3] >> 6) & 0xF; + rtl_set_bbreg(hw, 0xca0, 0xF0000000, reg); + } +} + +static void phy_save_adda_regs(struct ieee80211_hw *hw, + u32 *addareg, u32 *addabackup, + u32 registernum) +{ + u32 i; + + for (i = 0; i < registernum; i++) + addabackup[i] = rtl_get_bbreg(hw, addareg[i], MASKDWORD); +} + +static void phy_save_mac_regs(struct ieee80211_hw *hw, u32 *macreg, + u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + + for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) + macbackup[i] = rtl_read_byte(rtlpriv, macreg[i]); + macbackup[i] = rtl_read_dword(rtlpriv, macreg[i]); +} + +static void phy_reload_adda_regs(struct ieee80211_hw *hw, u32 *addareg, + u32 *addabackup, u32 regiesternum) +{ + u32 i; + + for (i = 0; i < regiesternum; i++) + rtl_set_bbreg(hw, addareg[i], MASKDWORD, addabackup[i]); +} + +static void phy_reload_mac_regs(struct ieee80211_hw *hw, u32 *macreg, + u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i; + + for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) + rtl_write_byte(rtlpriv, macreg[i], (u8) macbackup[i]); + rtl_write_dword(rtlpriv, macreg[i], macbackup[i]); +} + +static void _rtl8723ae_phy_path_adda_on(struct ieee80211_hw *hw, + u32 *addareg, bool is_patha_on, + bool is2t) +{ + u32 pathOn; + u32 i; + + pathOn = is_patha_on ? 0x04db25a4 : 0x0b1b25a4; + if (false == is2t) { + pathOn = 0x0bdb25a0; + rtl_set_bbreg(hw, addareg[0], MASKDWORD, 0x0b1b25a0); + } else { + rtl_set_bbreg(hw, addareg[0], MASKDWORD, pathOn); + } + + for (i = 1; i < IQK_ADDA_REG_NUM; i++) + rtl_set_bbreg(hw, addareg[i], MASKDWORD, pathOn); +} + +static void _rtl8723ae_phy_mac_setting_calibration(struct ieee80211_hw *hw, + u32 *macreg, u32 *macbackup) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 i = 0; + + rtl_write_byte(rtlpriv, macreg[i], 0x3F); + + for (i = 1; i < (IQK_MAC_REG_NUM - 1); i++) + rtl_write_byte(rtlpriv, macreg[i], + (u8) (macbackup[i] & (~BIT(3)))); + rtl_write_byte(rtlpriv, macreg[i], (u8) (macbackup[i] & (~BIT(5)))); +} + +static void _rtl8723ae_phy_path_a_standby(struct ieee80211_hw *hw) +{ + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x0); + rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); +} + +static void _rtl8723ae_phy_pi_mode_switch(struct ieee80211_hw *hw, bool pi_mode) +{ + u32 mode; + + mode = pi_mode ? 0x01000100 : 0x01000000; + rtl_set_bbreg(hw, 0x820, MASKDWORD, mode); + rtl_set_bbreg(hw, 0x828, MASKDWORD, mode); +} + +static bool phy_simularity_comp(struct ieee80211_hw *hw, long result[][8], + u8 c1, u8 c2) +{ + u32 i, j, diff, simularity_bitmap, bound; + + u8 final_candidate[2] = { 0xFF, 0xFF }; + bool bresult = true; + + bound = 4; + + simularity_bitmap = 0; + + for (i = 0; i < bound; i++) { + diff = (result[c1][i] > result[c2][i]) ? + (result[c1][i] - result[c2][i]) : + (result[c2][i] - result[c1][i]); + + if (diff > MAX_TOLERANCE) { + if ((i == 2 || i == 6) && !simularity_bitmap) { + if (result[c1][i] + result[c1][i + 1] == 0) + final_candidate[(i / 4)] = c2; + else if (result[c2][i] + result[c2][i + 1] == 0) + final_candidate[(i / 4)] = c1; + else + simularity_bitmap = simularity_bitmap | + (1 << i); + } else + simularity_bitmap = + simularity_bitmap | (1 << i); + } + } + + if (simularity_bitmap == 0) { + for (i = 0; i < (bound / 4); i++) { + if (final_candidate[i] != 0xFF) { + for (j = i * 4; j < (i + 1) * 4 - 2; j++) + result[3][j] = + result[final_candidate[i]][j]; + bresult = false; + } + } + return bresult; + } else if (!(simularity_bitmap & 0x0F)) { + for (i = 0; i < 4; i++) + result[3][i] = result[c1][i]; + return false; + } else { + return false; + } + +} + +static void _rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, + long result[][8], u8 t, bool is2t) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 i; + u8 patha_ok, pathb_ok; + u32 adda_reg[IQK_ADDA_REG_NUM] = { + 0x85c, 0xe6c, 0xe70, 0xe74, + 0xe78, 0xe7c, 0xe80, 0xe84, + 0xe88, 0xe8c, 0xed0, 0xed4, + 0xed8, 0xedc, 0xee0, 0xeec + }; + u32 iqk_mac_reg[IQK_MAC_REG_NUM] = { + 0x522, 0x550, 0x551, 0x040 + }; + const u32 retrycount = 2; + u32 bbvalue; + + if (t == 0) { + bbvalue = rtl_get_bbreg(hw, 0x800, MASKDWORD); + + phy_save_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16); + phy_save_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + } + _rtl8723ae_phy_path_adda_on(hw, adda_reg, true, is2t); + if (t == 0) { + rtlphy->rfpi_enable = (u8) rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER1, + BIT(8)); + } + + if (!rtlphy->rfpi_enable) + _rtl8723ae_phy_pi_mode_switch(hw, true); + if (t == 0) { + rtlphy->reg_c04 = rtl_get_bbreg(hw, 0xc04, MASKDWORD); + rtlphy->reg_c08 = rtl_get_bbreg(hw, 0xc08, MASKDWORD); + rtlphy->reg_874 = rtl_get_bbreg(hw, 0x874, MASKDWORD); + } + rtl_set_bbreg(hw, 0xc04, MASKDWORD, 0x03a05600); + rtl_set_bbreg(hw, 0xc08, MASKDWORD, 0x000800e4); + rtl_set_bbreg(hw, 0x874, MASKDWORD, 0x22204000); + if (is2t) { + rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00010000); + rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00010000); + } + _rtl8723ae_phy_mac_setting_calibration(hw, iqk_mac_reg, + rtlphy->iqk_mac_backup); + rtl_set_bbreg(hw, 0xb68, MASKDWORD, 0x00080000); + if (is2t) + rtl_set_bbreg(hw, 0xb6c, MASKDWORD, 0x00080000); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0x80800000); + rtl_set_bbreg(hw, 0xe40, MASKDWORD, 0x01007c00); + rtl_set_bbreg(hw, 0xe44, MASKDWORD, 0x01004800); + for (i = 0; i < retrycount; i++) { + patha_ok = _rtl8723ae_phy_path_a_iqk(hw, is2t); + if (patha_ok == 0x03) { + result[t][0] = (rtl_get_bbreg(hw, 0xe94, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][1] = (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][2] = (rtl_get_bbreg(hw, 0xea4, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][3] = (rtl_get_bbreg(hw, 0xeac, MASKDWORD) & + 0x3FF0000) >> 16; + break; + } else if (i == (retrycount - 1) && patha_ok == 0x01) + + result[t][0] = (rtl_get_bbreg(hw, 0xe94, + MASKDWORD) & 0x3FF0000) >> 16; + result[t][1] = + (rtl_get_bbreg(hw, 0xe9c, MASKDWORD) & 0x3FF0000) >> 16; + + } + + if (is2t) { + _rtl8723ae_phy_path_a_standby(hw); + _rtl8723ae_phy_path_adda_on(hw, adda_reg, false, is2t); + for (i = 0; i < retrycount; i++) { + pathb_ok = _rtl8723ae_phy_path_b_iqk(hw); + if (pathb_ok == 0x03) { + result[t][4] = + (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][5] = + (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][6] = + (rtl_get_bbreg(hw, 0xec4, MASKDWORD) & + 0x3FF0000) >> 16; + result[t][7] = + (rtl_get_bbreg(hw, 0xecc, MASKDWORD) & + 0x3FF0000) >> 16; + break; + } else if (i == (retrycount - 1) && pathb_ok == 0x01) { + result[t][4] = + (rtl_get_bbreg(hw, 0xeb4, MASKDWORD) & + 0x3FF0000) >> 16; + } + result[t][5] = (rtl_get_bbreg(hw, 0xebc, MASKDWORD) & + 0x3FF0000) >> 16; + } + } + rtl_set_bbreg(hw, 0xc04, MASKDWORD, rtlphy->reg_c04); + rtl_set_bbreg(hw, 0x874, MASKDWORD, rtlphy->reg_874); + rtl_set_bbreg(hw, 0xc08, MASKDWORD, rtlphy->reg_c08); + rtl_set_bbreg(hw, 0xe28, MASKDWORD, 0); + rtl_set_bbreg(hw, 0x840, MASKDWORD, 0x00032ed3); + if (is2t) + rtl_set_bbreg(hw, 0x844, MASKDWORD, 0x00032ed3); + if (t != 0) { + if (!rtlphy->rfpi_enable) + _rtl8723ae_phy_pi_mode_switch(hw, false); + phy_reload_adda_regs(hw, adda_reg, rtlphy->adda_backup, 16); + phy_reload_mac_regs(hw, iqk_mac_reg, rtlphy->iqk_mac_backup); + } +} + +static void _rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u8 tmpreg; + u32 rf_a_mode = 0, rf_b_mode = 0, lc_cal; + + tmpreg = rtl_read_byte(rtlpriv, 0xd03); + + if ((tmpreg & 0x70) != 0) + rtl_write_byte(rtlpriv, 0xd03, tmpreg & 0x8F); + else + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); + + if ((tmpreg & 0x70) != 0) { + rf_a_mode = rtl_get_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS); + + if (is2t) + rf_b_mode = rtl_get_rfreg(hw, RF90_PATH_B, 0x00, + MASK12BITS); + + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, + (rf_a_mode & 0x8FFFF) | 0x10000); + + if (is2t) + rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, + (rf_b_mode & 0x8FFFF) | 0x10000); + } + lc_cal = rtl_get_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS); + + rtl_set_rfreg(hw, RF90_PATH_A, 0x18, MASK12BITS, lc_cal | 0x08000); + + mdelay(100); + + if ((tmpreg & 0x70) != 0) { + rtl_write_byte(rtlpriv, 0xd03, tmpreg); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, MASK12BITS, rf_a_mode); + + if (is2t) + rtl_set_rfreg(hw, RF90_PATH_B, 0x00, MASK12BITS, + rf_b_mode); + } else { + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); + } +} + +static void _rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, + bool bmain, bool is2t) +{ + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + + if (is_hal_stop(rtlhal)) { + rtl_set_bbreg(hw, REG_LEDCFG0, BIT(23), 0x01); + rtl_set_bbreg(hw, rFPGA0_XAB_RFPARAMETER, BIT(13), 0x01); + } + if (is2t) { + if (bmain) + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(6), 0x1); + else + rtl_set_bbreg(hw, RFPGA0_XB_RFINTERFACEOE, + BIT(5) | BIT(6), 0x2); + } else { + if (bmain) + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, 0x300, 0x2); + else + rtl_set_bbreg(hw, RFPGA0_XA_RFINTERFACEOE, 0x300, 0x1); + + } +} + +#undef IQK_ADDA_REG_NUM +#undef IQK_DELAY_TIME + +void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + long result[4][8]; + u8 i, final_candidate; + bool patha_ok, pathb_ok; + long reg_e94, reg_e9c, reg_ea4, reg_eac, reg_eb4, reg_ebc, reg_ec4, + reg_ecc, reg_tmp = 0; + bool is12simular, is13simular, is23simular; + bool start_conttx = false, singletone = false; + u32 iqk_bb_reg[10] = { + ROFDM0_XARXIQIMBALANCE, + ROFDM0_XBRXIQIMBALANCE, + ROFDM0_ECCATHRESHOLD, + ROFDM0_AGCRSSITABLE, + ROFDM0_XATXIQIMBALANCE, + ROFDM0_XBTXIQIMBALANCE, + ROFDM0_XCTXIQIMBALANCE, + ROFDM0_XCTXAFE, + ROFDM0_XDTXAFE, + ROFDM0_RXIQEXTANTA + }; + + if (recovery) { + phy_reload_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); + return; + } + if (start_conttx || singletone) + return; + for (i = 0; i < 8; i++) { + result[0][i] = 0; + result[1][i] = 0; + result[2][i] = 0; + result[3][i] = 0; + } + final_candidate = 0xff; + patha_ok = false; + pathb_ok = false; + is12simular = false; + is23simular = false; + is13simular = false; + for (i = 0; i < 3; i++) { + _rtl8723ae_phy_iq_calibrate(hw, result, i, false); + if (i == 1) { + is12simular = phy_simularity_comp(hw, result, 0, 1); + if (is12simular) { + final_candidate = 0; + break; + } + } + if (i == 2) { + is13simular = phy_simularity_comp(hw, result, 0, 2); + if (is13simular) { + final_candidate = 0; + break; + } + is23simular = phy_simularity_comp(hw, result, 1, 2); + if (is23simular) { + final_candidate = 1; + } else { + for (i = 0; i < 8; i++) + reg_tmp += result[3][i]; + + if (reg_tmp != 0) + final_candidate = 3; + else + final_candidate = 0xFF; + } + } + } + for (i = 0; i < 4; i++) { + reg_e94 = result[i][0]; + reg_e9c = result[i][1]; + reg_ea4 = result[i][2]; + reg_eac = result[i][3]; + reg_eb4 = result[i][4]; + reg_ebc = result[i][5]; + reg_ec4 = result[i][6]; + reg_ecc = result[i][7]; + } + if (final_candidate != 0xff) { + rtlphy->reg_e94 = reg_e94 = result[final_candidate][0]; + rtlphy->reg_e9c = reg_e9c = result[final_candidate][1]; + reg_ea4 = result[final_candidate][2]; + reg_eac = result[final_candidate][3]; + rtlphy->reg_eb4 = reg_eb4 = result[final_candidate][4]; + rtlphy->reg_ebc = reg_ebc = result[final_candidate][5]; + reg_ec4 = result[final_candidate][6]; + reg_ecc = result[final_candidate][7]; + patha_ok = pathb_ok = true; + } else { + rtlphy->reg_e94 = rtlphy->reg_eb4 = 0x100; + rtlphy->reg_e9c = rtlphy->reg_ebc = 0x0; + } + if (reg_e94 != 0) /*&&(reg_ea4 != 0) */ + phy_path_a_fill_iqk_matrix(hw, patha_ok, result, + final_candidate, (reg_ea4 == 0)); + phy_save_adda_regs(hw, iqk_bb_reg, rtlphy->iqk_bb_backup, 10); +} + +void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw) +{ + bool start_conttx = false, singletone = false; + + if (start_conttx || singletone) + return; + _rtl8723ae_phy_lc_calibrate(hw, false); +} + +void rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) +{ + _rtl8723ae_phy_set_rfpath_switch(hw, bmain, false); +} + +bool rtl8723ae_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + bool postprocessing = false; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "-->IO Cmd(%#x), set_io_inprogress(%d)\n", + iotype, rtlphy->set_io_inprogress); + do { + switch (iotype) { + case IO_CMD_RESUME_DM_BY_SCAN: + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "[IO CMD] Resume DM after scan.\n"); + postprocessing = true; + break; + case IO_CMD_PAUSE_DM_BY_SCAN: + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "[IO CMD] Pause DM before scan.\n"); + postprocessing = true; + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + } while (false); + if (postprocessing && !rtlphy->set_io_inprogress) { + rtlphy->set_io_inprogress = true; + rtlphy->current_io_type = iotype; + } else { + return false; + } + rtl8723ae_phy_set_io(hw); + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, "<--IO Type(%#x)\n", iotype); + return true; +} + +static void rtl8723ae_phy_set_io(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct dig_t *dm_digtable = &rtlpriv->dm_digtable; + + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "--->Cmd(%#x), set_io_inprogress(%d)\n", + rtlphy->current_io_type, rtlphy->set_io_inprogress); + switch (rtlphy->current_io_type) { + case IO_CMD_RESUME_DM_BY_SCAN: + dm_digtable->cur_igvalue = rtlphy->initgain_backup.xaagccore1; + rtl8723ae_dm_write_dig(hw); + rtl8723ae_phy_set_txpower_level(hw, rtlphy->current_channel); + break; + case IO_CMD_PAUSE_DM_BY_SCAN: + rtlphy->initgain_backup.xaagccore1 = dm_digtable->cur_igvalue; + dm_digtable->cur_igvalue = 0x17; + rtl8723ae_dm_write_dig(hw); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not process\n"); + break; + } + rtlphy->set_io_inprogress = false; + RT_TRACE(rtlpriv, COMP_CMD, DBG_TRACE, + "<---(%#x)\n", rtlphy->current_io_type); +} + +static void rtl8723ae_phy_set_rf_on(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x2b); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); + rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); +} + +static void _rtl8723ae_phy_set_rf_sleep(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 u4b_tmp; + u8 delay = 5; + + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0xFF); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); + rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); + u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); + while (u4b_tmp != 0 && delay > 0) { + rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x0); + rtl_set_rfreg(hw, RF90_PATH_A, 0x00, RFREG_OFFSET_MASK, 0x00); + rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x40); + u4b_tmp = rtl_get_rfreg(hw, RF90_PATH_A, 0, RFREG_OFFSET_MASK); + delay--; + } + if (delay == 0) { + rtl_write_byte(rtlpriv, REG_APSD_CTRL, 0x00); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE3); + rtl_write_byte(rtlpriv, REG_TXPAUSE, 0x00); + RT_TRACE(rtlpriv, COMP_POWER, DBG_TRACE, + "Switch RF timeout !!!.\n"); + return; + } + rtl_write_byte(rtlpriv, REG_SYS_FUNC_EN, 0xE2); + rtl_write_byte(rtlpriv, REG_SPS0_CTRL, 0x22); +} + +static bool _rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + struct rtl8192_tx_ring *ring = NULL; + bool bresult = true; + u8 i, queue_id; + + switch (rfpwr_state) { + case ERFON: + if ((ppsc->rfpwr_state == ERFOFF) && + RT_IN_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC)) { + bool rtstatus; + u32 InitializeCount = 0; + do { + InitializeCount++; + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "IPS Set eRf nic enable\n"); + rtstatus = rtl_ps_enable_nic(hw); + } while ((rtstatus != true) && (InitializeCount < 10)); + RT_CLEAR_PS_LEVEL(ppsc, + RT_RF_OFF_LEVL_HALT_NIC); + } else { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "Set ERFON sleeped:%d ms\n", + jiffies_to_msecs(jiffies - + ppsc->last_sleep_jiffies)); + ppsc->last_awake_jiffies = jiffies; + rtl8723ae_phy_set_rf_on(hw); + } + if (mac->link_state == MAC80211_LINKED) { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_LINK); + } else { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_NO_LINK); + } + break; + case ERFOFF: + if (ppsc->reg_rfps_level & RT_RF_OFF_LEVL_HALT_NIC) { + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "IPS Set eRf nic disable\n"); + rtl_ps_disable_nic(hw); + RT_SET_PS_LEVEL(ppsc, RT_RF_OFF_LEVL_HALT_NIC); + } else { + if (ppsc->rfoff_reason == RF_CHANGE_BY_IPS) { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_NO_LINK); + } else { + rtlpriv->cfg->ops->led_control(hw, + LED_CTL_POWER_OFF); + } + } + break; + case ERFSLEEP: + if (ppsc->rfpwr_state == ERFOFF) + break; + for (queue_id = 0, i = 0; + queue_id < RTL_PCI_MAX_TX_QUEUE_COUNT;) { + ring = &pcipriv->dev.tx_ring[queue_id]; + if (skb_queue_len(&ring->queue) == 0) { + queue_id++; + continue; + } else { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "eRf Off/Sleep: %d times TcbBusyQueue[%d] =%d before doze!\n", + (i + 1), queue_id, + skb_queue_len(&ring->queue)); + + udelay(10); + i++; + } + if (i >= MAX_DOZE_WAITING_TIMES_9x) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_WARNING, + "\n ERFSLEEP: %d times TcbBusyQueue[%d] = %d !\n", + MAX_DOZE_WAITING_TIMES_9x, + queue_id, + skb_queue_len(&ring->queue)); + break; + } + } + RT_TRACE(rtlpriv, COMP_RF, DBG_DMESG, + "Set ERFSLEEP awaked:%d ms\n", + jiffies_to_msecs(jiffies - ppsc->last_awake_jiffies)); + ppsc->last_sleep_jiffies = jiffies; + _rtl8723ae_phy_set_rf_sleep(hw); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "switch case not processed\n"); + bresult = false; + break; + } + if (bresult) + ppsc->rfpwr_state = rfpwr_state; + return bresult; +} + +bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state) +{ + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + bool bresult = false; + + if (rfpwr_state == ppsc->rfpwr_state) + return bresult; + bresult = _rtl8723ae_phy_set_rf_power_state(hw, rfpwr_state); + return bresult; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h new file mode 100644 index 00000000000..e7a59eba351 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.h @@ -0,0 +1,224 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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_MAC_REG_NUM 4 + +#define RF6052_MAX_PATH 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_DIFF 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 + +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 rtl8723ae_phy_query_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask); +extern void rtl8723ae_phy_set_bb_reg(struct ieee80211_hw *hw, + u32 regaddr, u32 bitmask, u32 data); +extern u32 rtl8723ae_phy_query_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask); +extern void rtl8723ae_phy_set_rf_reg(struct ieee80211_hw *hw, + enum radio_path rfpath, u32 regaddr, + u32 bitmask, u32 data); +extern bool rtl8723ae_phy_mac_config(struct ieee80211_hw *hw); +extern bool rtl8723ae_phy_bb_config(struct ieee80211_hw *hw); +extern bool rtl8723ae_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 rtl8723ae_phy_get_hw_reg_originalvalue(struct ieee80211_hw *hw); +extern void rtl8723ae_phy_get_txpower_level(struct ieee80211_hw *hw, + long *powerlevel); +extern void rtl8723ae_phy_set_txpower_level(struct ieee80211_hw *hw, + u8 channel); +extern bool rtl8723ae_phy_update_txpower_dbm(struct ieee80211_hw *hw, + long power_indbm); +extern void rtl8723ae_phy_scan_operation_backup(struct ieee80211_hw *hw, + u8 operation); +extern void rtl8723ae_phy_set_bw_mode_callback(struct ieee80211_hw *hw); +extern void rtl8723ae_phy_set_bw_mode(struct ieee80211_hw *hw, + enum nl80211_channel_type ch_type); +extern void rtl8723ae_phy_sw_chnl_callback(struct ieee80211_hw *hw); +extern u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw); +extern void rtl8723ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool recovery); +void rtl8723ae_phy_lc_calibrate(struct ieee80211_hw *hw); +void rtl8723ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); +bool rtl8723ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, + enum radio_path rfpath); +bool rtl8723ae_phy_set_io_cmd(struct ieee80211_hw *hw, enum io_type iotype); +extern bool rtl8723ae_phy_set_rf_power_state(struct ieee80211_hw *hw, + enum rf_pwrstate rfpwr_state); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.c b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.c new file mode 100644 index 00000000000..df6ca9a57f7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.c @@ -0,0 +1,109 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "pwrseqcmd.h" +#include "pwrseq.h" + +/* drivers should parse arrays below and do the corresponding actions */ + +/*3 Power on Array*/ +struct wlan_pwr_cfg rtl8723A_power_on_flow[RTL8723A_TRANS_CARDEMU_TO_ACT_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_CARDEMU_TO_ACT, + RTL8723A_TRANS_END +}; + +/*3Radio off GPIO Array */ +struct wlan_pwr_cfg rtl8723A_radio_off_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_ACT_TO_CARDEMU, + RTL8723A_TRANS_END +}; + +/*3Card Disable Array*/ +struct wlan_pwr_cfg +rtl8723A_card_disable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_ACT_TO_CARDEMU, + RTL8723A_TRANS_CARDEMU_TO_CARDDIS, + RTL8723A_TRANS_END +}; + +/*3 Card Enable Array*/ +struct wlan_pwr_cfg rtl8723A_card_enable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_CARDDIS_TO_CARDEMU, + RTL8723A_TRANS_CARDEMU_TO_ACT, + RTL8723A_TRANS_END +}; + +/*3Suspend Array*/ +struct wlan_pwr_cfg rtl8723A_suspend_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_SUS_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_ACT_TO_CARDEMU, + RTL8723A_TRANS_CARDEMU_TO_SUS, + RTL8723A_TRANS_END +}; + +/*3 Resume Array*/ +struct wlan_pwr_cfg rtl8723A_resume_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_SUS_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_SUS_TO_CARDEMU, + RTL8723A_TRANS_CARDEMU_TO_ACT, + RTL8723A_TRANS_END +}; + +/*3HWPDN Array*/ +struct wlan_pwr_cfg rtl8723A_hwpdn_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS] = { + RTL8723A_TRANS_ACT_TO_CARDEMU, + RTL8723A_TRANS_CARDEMU_TO_PDN, + RTL8723A_TRANS_END +}; + +/*3 Enter LPS */ +struct wlan_pwr_cfg rtl8723A_enter_lps_flow[RTL8723A_TRANS_ACT_TO_LPS_STPS + + RTL8723A_TRANS_END_STPS] = { + /*FW behavior*/ + RTL8723A_TRANS_ACT_TO_LPS, + RTL8723A_TRANS_END +}; + +/*3 Leave LPS */ +struct wlan_pwr_cfg rtl8723A_leave_lps_flow[RTL8723A_TRANS_LPS_TO_ACT_STPS + + RTL8723A_TRANS_END_STPS] = { + /*FW behavior*/ + RTL8723A_TRANS_LPS_TO_ACT, + RTL8723A_TRANS_END +}; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h new file mode 100644 index 00000000000..7a46f9fdf55 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseq.h @@ -0,0 +1,322 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_PWRSEQ_H__ +#define __RTL8723E_PWRSEQ_H__ + +#include "pwrseqcmd.h" +/* + Check document WM-20110607-Paul-RTL8723A_Power_Architecture-R02.vsd + There are 6 HW Power States: + 0: POFF--Power Off + 1: PDN--Power Down + 2: CARDEMU--Card Emulation + 3: ACT--Active Mode + 4: LPS--Low Power State + 5: SUS--Suspend + + The transision from different states are defined below + TRANS_CARDEMU_TO_ACT + TRANS_ACT_TO_CARDEMU + TRANS_CARDEMU_TO_SUS + TRANS_SUS_TO_CARDEMU + TRANS_CARDEMU_TO_PDN + TRANS_ACT_TO_LPS + TRANS_LPS_TO_ACT + + TRANS_END +*/ + +#define RTL8723A_TRANS_CARDEMU_TO_ACT_STPS 10 +#define RTL8723A_TRANS_ACT_TO_CARDEMU_STPS 10 +#define RTL8723A_TRANS_CARDEMU_TO_SUS_STPS 10 +#define RTL8723A_TRANS_SUS_TO_CARDEMU_STPS 10 +#define RTL8723A_TRANS_CARDEMU_TO_PDN_STPS 10 +#define RTL8723A_TRANS_PDN_TO_CARDEMU_STPS 10 +#define RTL8723A_TRANS_ACT_TO_LPS_STPS 15 +#define RTL8723A_TRANS_LPS_TO_ACT_STPS 15 +#define RTL8723A_TRANS_END_STPS 1 + + +#define RTL8723A_TRANS_CARDEMU_TO_ACT \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, \ + * comments here*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), 0}, \ + /* disable SW LPS 0x04[10]=0*/ \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + /* wait till 0x04[17] = 1 power ready*/ \ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + /* release WLON reset 0x04[16]=1*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0}, \ + /* disable HWPDN 0x04[15]=0*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, (BIT(4)|BIT(3)), 0}, \ + /* disable WL suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + /* polling until return 0*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(0), 0} + +#define RTL8723A_TRANS_ACT_TO_CARDEMU \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, \ + * comments here*/ \ + {0x001F, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0}, \ + /*0x1F[7:0] = 0 turn off RF*/ \ + {0x004E, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(1), 0} + +#define RTL8723A_TRANS_CARDEMU_TO_SUS \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, \ + * comments here*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4)|BIT(3), \ + (BIT(4)|BIT(3))}, \ + /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK | \ + PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)},\ + /*0x04[12:11] = 2b'01 enable WL suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, \ + PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)|BIT(4)}, \ + /*0x04[12:11] = 2b'11 enable WL suspend for PCIe*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, \ + PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, \ + PWR_CMD_POLLING, BIT(1), 0} \ + /*wait power state to suspend*/ + +#define RTL8723A_TRANS_SUS_TO_CARDEMU \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \ + /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + /*wait power state to suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0} \ + /*0x04[12:11] = 2b'01enable WL suspend*/ + +#define RTL8723A_TRANS_CARDEMU_TO_CARDDIS \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, \ + PWR_INTF_USB_MSK|PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), BIT(3)},\ + /*0x04[12:11] = 2b'01 enable WL suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(2), BIT(2)}, \ + /*0x04[10] = 1, enable SW LPS*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), BIT(0)}, \ + /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), 0} \ + /*wait power state to suspend*/ + +#define RTL8723A_TRANS_CARDDIS_TO_CARDEMU \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, BIT(0), 0}, \ + /*Set SDIO suspend local register*/ \ + {0x0086, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_POLLING, BIT(1), BIT(1)}, \ + /*wait power state to suspend*/ \ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(3)|BIT(4), 0}, \ + /*0x04[12:11] = 2b'00enable WL suspend*/ \ + {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0} \ + /*PCIe DMA start*/ + +#define RTL8723A_TRANS_CARDEMU_TO_PDN \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0006, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + /* 0x04[16] = 0*/\ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), BIT(7)} \ + /* 0x04[15] = 1*/ + +#define RTL8723A_TRANS_PDN_TO_CARDEMU \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0005, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(7), 0} \ + /* 0x04[15] = 0*/ + +#define RTL8723A_TRANS_ACT_TO_LPS \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0301, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \ + /*PCIe DMA stop*/ \ + {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x7F}, \ + /*Tx Pause*/ \ + {0x05F8, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + /*Should be zero if no packet is transmitting*/ \ + {0x05F9, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + /*Should be zero if no packet is transmitting*/ \ + {0x05FA, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + /*Should be zero if no packet is transmitting*/ \ + {0x05FB, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, 0xFF, 0}, \ + /*Should be zero if no packet is transmitting*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(0), 0}, \ + /*CCK and OFDM are disabled,and clock are gated*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US}, \ + /*Delay 1us*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + /*Whole BB is reset*/ \ + {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x3F}, \ + /*Reset MAC TRX*/ \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), 0}, \ + /*check if removed later*/ \ + {0x0553, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(5), BIT(5)} \ + /*Respond TxOK to scheduler*/ + +#define RTL8723A_TRANS_LPS_TO_ACT \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0x0080, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_SDIO_MSK, \ + PWR_BASEADDR_SDIO, PWR_CMD_WRITE, 0xFF, 0x84}, \ + /*SDIO RPWM*/ \ + {0xFE58, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_USB_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \ + /*USB RPWM*/ \ + {0x0361, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_PCI_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0x84}, \ + /*PCIe RPWM*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_MS}, \ + /*Delay*/ \ + {0x0008, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(4), 0}, \ + /* 0x08[4] = 0 switch TSF to 40M*/ \ + {0x0109, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_POLLING, BIT(7), 0}, \ + /*Polling 0x109[7]=0 TSF in 40M*/ \ + {0x0029, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(6)|BIT(7), 0}, \ + /*. 0x29[7:6] = 2b'00 enable BB clock*/ \ + {0x0101, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1), BIT(1)}, \ + /*. 0x101[1] = 1*/ \ + {0x0100, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0xFF}, \ + /* 0x100[7:0] = 0xFF enable WMAC TRX*/ \ + {0x0002, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, BIT(1)|BIT(0), \ + BIT(1)|BIT(0)}, \ + /* 0x02[1:0] = 2b'11 enable BB macro*/ \ + {0x0522, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + PWR_BASEADDR_MAC, PWR_CMD_WRITE, 0xFF, 0} \ + /*. 0x522 = 0*/ + +#define RTL8723A_TRANS_END \ + /* format */ \ + /* { offset, cut_msk, fab_msk|interface_msk, base|cmd, msk, value }, */\ + {0xFFFF, PWR_CUT_ALL_MSK, PWR_FAB_ALL_MSK, PWR_INTF_ALL_MSK, \ + 0, PWR_CMD_END, 0, 0} + +extern struct +wlan_pwr_cfg rtl8723A_power_on_flow[RTL8723A_TRANS_CARDEMU_TO_ACT_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_radio_off_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_card_disable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_card_enable_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_suspend_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_SUS_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_resume_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_SUS_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_hwpdn_flow[RTL8723A_TRANS_ACT_TO_CARDEMU_STPS + + RTL8723A_TRANS_CARDEMU_TO_PDN_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_enter_lps_flow[RTL8723A_TRANS_ACT_TO_LPS_STPS + + RTL8723A_TRANS_END_STPS]; +extern struct +wlan_pwr_cfg rtl8723A_leave_lps_flow[RTL8723A_TRANS_LPS_TO_ACT_STPS + + RTL8723A_TRANS_END_STPS]; + +/* RTL8723 Power Configuration CMDs for PCIe interface */ +#define Rtl8723_NIC_PWR_ON_FLOW rtl8723A_power_on_flow +#define Rtl8723_NIC_RF_OFF_FLOW rtl8723A_radio_off_flow +#define Rtl8723_NIC_DISABLE_FLOW rtl8723A_card_disable_flow +#define Rtl8723_NIC_ENABLE_FLOW rtl8723A_card_enable_flow +#define Rtl8723_NIC_SUSPEND_FLOW rtl8723A_suspend_flow +#define Rtl8723_NIC_RESUME_FLOW rtl8723A_resume_flow +#define Rtl8723_NIC_PDN_FLOW rtl8723A_hwpdn_flow +#define Rtl8723_NIC_LPS_ENTER_FLOW rtl8723A_enter_lps_flow +#define Rtl8723_NIC_LPS_LEAVE_FLOW rtl8723A_leave_lps_flow + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.c b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.c new file mode 100644 index 00000000000..2044b5936b7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.c @@ -0,0 +1,129 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "pwrseq.h" + +/* Description: + * This routine deals with the Power Configuration CMD + * parsing for RTL8723/RTL8188E Series IC. + * Assumption: + * We should follow specific format that was released from HW SD. + */ +bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 faversion, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]) +{ + struct wlan_pwr_cfg cfg_cmd = {0}; + bool polling_bit = false; + u32 ary_idx = 0; + u8 value = 0; + u32 offset = 0; + u32 polling_count = 0; + u32 max_polling_cnt = 5000; + + do { + cfg_cmd = pwrcfgcmd[ary_idx]; + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): offset(%#x),cut_msk(%#x), famsk(%#x)," + "interface_msk(%#x), base(%#x), cmd(%#x), msk(%#x), value(%#x)\n", + GET_PWR_CFG_OFFSET(cfg_cmd), + GET_PWR_CFG_CUT_MASK(cfg_cmd), + GET_PWR_CFG_FAB_MASK(cfg_cmd), + GET_PWR_CFG_INTF_MASK(cfg_cmd), + GET_PWR_CFG_BASE(cfg_cmd), GET_PWR_CFG_CMD(cfg_cmd), + GET_PWR_CFG_MASK(cfg_cmd), GET_PWR_CFG_VALUE(cfg_cmd)); + + if ((GET_PWR_CFG_FAB_MASK(cfg_cmd)&faversion) && + (GET_PWR_CFG_CUT_MASK(cfg_cmd)&cut_version) && + (GET_PWR_CFG_INTF_MASK(cfg_cmd)&interface_type)) { + switch (GET_PWR_CFG_CMD(cfg_cmd)) { + case PWR_CMD_READ: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_READ\n"); + break; + case PWR_CMD_WRITE: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_WRITE\n"); + offset = GET_PWR_CFG_OFFSET(cfg_cmd); + + /*Read the value from system register*/ + value = rtl_read_byte(rtlpriv, offset); + value &= (~(GET_PWR_CFG_MASK(cfg_cmd))); + value |= (GET_PWR_CFG_VALUE(cfg_cmd) & + GET_PWR_CFG_MASK(cfg_cmd)); + + /*Write the value back to sytem register*/ + rtl_write_byte(rtlpriv, offset, value); + break; + case PWR_CMD_POLLING: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_POLLING\n"); + polling_bit = false; + offset = GET_PWR_CFG_OFFSET(cfg_cmd); + + do { + value = rtl_read_byte(rtlpriv, offset); + + value &= GET_PWR_CFG_MASK(cfg_cmd); + if (value == + (GET_PWR_CFG_VALUE(cfg_cmd) + & GET_PWR_CFG_MASK(cfg_cmd))) + polling_bit = true; + else + udelay(10); + + if (polling_count++ > max_polling_cnt) + return false; + } while (!polling_bit); + break; + case PWR_CMD_DELAY: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_DELAY\n"); + if (GET_PWR_CFG_VALUE(cfg_cmd) == + PWRSEQ_DELAY_US) + udelay(GET_PWR_CFG_OFFSET(cfg_cmd)); + else + mdelay(GET_PWR_CFG_OFFSET(cfg_cmd)); + break; + case PWR_CMD_END: + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "rtl_hal_pwrseqcmdparsing(): PWR_CMD_END\n"); + return true; + default: + RT_ASSERT(false, + "rtl_hal_pwrseqcmdparsing(): Unknown CMD!!\n"); + break; + } + + } + ary_idx++; + } while (1); + + return true; +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.h b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.h new file mode 100644 index 00000000000..6e0f3ea37ec --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/pwrseqcmd.h @@ -0,0 +1,98 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_PWRSEQCMD_H__ +#define __RTL8723E_PWRSEQCMD_H__ + +#include "../wifi.h" +/*--------------------------------------------- + * 3 The value of cmd: 4 bits + *--------------------------------------------- + */ +#define PWR_CMD_READ 0x00 +#define PWR_CMD_WRITE 0x01 +#define PWR_CMD_POLLING 0x02 +#define PWR_CMD_DELAY 0x03 +#define PWR_CMD_END 0x04 + +/* define the base address of each block */ +#define PWR_BASEADDR_MAC 0x00 +#define PWR_BASEADDR_USB 0x01 +#define PWR_BASEADDR_PCIE 0x02 +#define PWR_BASEADDR_SDIO 0x03 + +#define PWR_INTF_SDIO_MSK BIT(0) +#define PWR_INTF_USB_MSK BIT(1) +#define PWR_INTF_PCI_MSK BIT(2) +#define PWR_INTF_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3)) + +#define PWR_FAB_TSMC_MSK BIT(0) +#define PWR_FAB_UMC_MSK BIT(1) +#define PWR_FAB_ALL_MSK (BIT(0)|BIT(1)|BIT(2)|BIT(3)) + +#define PWR_CUT_TESTCHIP_MSK BIT(0) +#define PWR_CUT_A_MSK BIT(1) +#define PWR_CUT_B_MSK BIT(2) +#define PWR_CUT_C_MSK BIT(3) +#define PWR_CUT_D_MSK BIT(4) +#define PWR_CUT_E_MSK BIT(5) +#define PWR_CUT_F_MSK BIT(6) +#define PWR_CUT_G_MSK BIT(7) +#define PWR_CUT_ALL_MSK 0xFF + +enum pwrseq_delay_unit { + PWRSEQ_DELAY_US, + PWRSEQ_DELAY_MS, +}; + +struct wlan_pwr_cfg { + u16 offset; + u8 cut_msk; + u8 fab_msk:4; + u8 interface_msk:4; + u8 base:4; + u8 cmd:4; + u8 msk; + u8 value; +}; + +#define GET_PWR_CFG_OFFSET(__PWR_CMD) (__PWR_CMD.offset) +#define GET_PWR_CFG_CUT_MASK(__PWR_CMD) (__PWR_CMD.cut_msk) +#define GET_PWR_CFG_FAB_MASK(__PWR_CMD) (__PWR_CMD.fab_msk) +#define GET_PWR_CFG_INTF_MASK(__PWR_CMD) (__PWR_CMD.interface_msk) +#define GET_PWR_CFG_BASE(__PWR_CMD) (__PWR_CMD.base) +#define GET_PWR_CFG_CMD(__PWR_CMD) (__PWR_CMD.cmd) +#define GET_PWR_CFG_MASK(__PWR_CMD) (__PWR_CMD.msk) +#define GET_PWR_CFG_VALUE(__PWR_CMD) (__PWR_CMD.value) + +bool rtl_hal_pwrseqcmdparsing(struct rtl_priv *rtlpriv, u8 cut_version, + u8 fab_version, u8 interface_type, + struct wlan_pwr_cfg pwrcfgcmd[]); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h new file mode 100644 index 00000000000..199da366c6d --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/reg.h @@ -0,0 +1,2097 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_REG_H__ +#define __RTL8723E_REG_H__ + +#define REG_SYS_ISO_CTRL 0x0000 +#define REG_SYS_FUNC_EN 0x0002 +#define REG_APS_FSMCO 0x0004 +#define REG_SYS_CLKR 0x0008 +#define REG_9346CR 0x000A +#define REG_EE_VPD 0x000C +#define REG_AFE_MISC 0x0010 +#define REG_SPS0_CTRL 0x0011 +#define REG_SPS_OCP_CFG 0x0018 +#define REG_RSV_CTRL 0x001C +#define REG_RF_CTRL 0x001F +#define REG_LDOA15_CTRL 0x0020 +#define REG_LDOV12D_CTRL 0x0021 +#define REG_LDOHCI12_CTRL 0x0022 +#define REG_LPLDO_CTRL 0x0023 +#define REG_AFE_XTAL_CTRL 0x0024 +#define REG_AFE_PLL_CTRL 0x0028 +#define REG_EFUSE_CTRL 0x0030 +#define REG_EFUSE_TEST 0x0034 +#define REG_PWR_DATA 0x0038 +#define REG_CAL_TIMER 0x003C +#define REG_ACLK_MON 0x003E +#define REG_GPIO_MUXCFG 0x0040 +#define REG_GPIO_IO_SEL 0x0042 +#define REG_MAC_PINMUX_CFG 0x0043 +#define REG_GPIO_PIN_CTRL 0x0044 +#define REG_GPIO_INTM 0x0048 +#define REG_LEDCFG0 0x004C +#define REG_LEDCFG1 0x004D +#define REG_LEDCFG2 0x004E +#define REG_LEDCFG3 0x004F +#define REG_FSIMR 0x0050 +#define REG_FSISR 0x0054 +#define REG_GPIO_PIN_CTRL_2 0x0060 +#define REG_GPIO_IO_SEL_2 0x0062 +#define REG_MULTI_FUNC_CTRL 0x0068 + +#define REG_MCUFWDL 0x0080 + +#define REG_HMEBOX_EXT_0 0x0088 +#define REG_HMEBOX_EXT_1 0x008A +#define REG_HMEBOX_EXT_2 0x008C +#define REG_HMEBOX_EXT_3 0x008E + +#define REG_BIST_SCAN 0x00D0 +#define REG_BIST_RPT 0x00D4 +#define REG_BIST_ROM_RPT 0x00D8 +#define REG_USB_SIE_INTF 0x00E0 +#define REG_PCIE_MIO_INTF 0x00E4 +#define REG_PCIE_MIO_INTD 0x00E8 +#define REG_SYS_CFG 0x00F0 +#define REG_GPIO_OUTSTS 0x00F4 + +#define REG_CR 0x0100 +#define REG_PBP 0x0104 +#define REG_TRXDMA_CTRL 0x010C +#define REG_TRXFF_BNDY 0x0114 +#define REG_TRXFF_STATUS 0x0118 +#define REG_RXFF_PTR 0x011C +#define REG_HIMR 0x0120 +#define REG_HISR 0x0124 +#define REG_HIMRE 0x0128 +#define REG_HISRE 0x012C +#define REG_CPWM 0x012F +#define REG_FWIMR 0x0130 +#define REG_FWISR 0x0134 +#define REG_PKTBUF_DBG_CTRL 0x0140 +#define REG_PKTBUF_DBG_DATA_L 0x0144 +#define REG_PKTBUF_DBG_DATA_H 0x0148 + +#define REG_TC0_CTRL 0x0150 +#define REG_TC1_CTRL 0x0154 +#define REG_TC2_CTRL 0x0158 +#define REG_TC3_CTRL 0x015C +#define REG_TC4_CTRL 0x0160 +#define REG_TCUNIT_BASE 0x0164 +#define REG_MBIST_START 0x0174 +#define REG_MBIST_DONE 0x0178 +#define REG_MBIST_FAIL 0x017C +#define REG_C2HEVT_MSG_NORMAL 0x01A0 +#define REG_C2HEVT_MSG_TEST 0x01B8 +#define REG_MCUTST_1 0x01c0 +#define REG_FMETHR 0x01C8 +#define REG_HMETFR 0x01CC +#define REG_HMEBOX_0 0x01D0 +#define REG_HMEBOX_1 0x01D4 +#define REG_HMEBOX_2 0x01D8 +#define REG_HMEBOX_3 0x01DC + +#define REG_LLT_INIT 0x01E0 +#define REG_BB_ACCEESS_CTRL 0x01E8 +#define REG_BB_ACCESS_DATA 0x01EC + +#define REG_RQPN 0x0200 +#define REG_FIFOPAGE 0x0204 +#define REG_TDECTRL 0x0208 +#define REG_TXDMA_OFFSET_CHK 0x020C +#define REG_TXDMA_STATUS 0x0210 +#define REG_RQPN_NPQ 0x0214 + +#define REG_RXDMA_AGG_PG_TH 0x0280 +#define REG_RXPKT_NUM 0x0284 +#define REG_RXDMA_STATUS 0x0288 + +#define REG_PCIE_CTRL_REG 0x0300 +#define REG_INT_MIG 0x0304 +#define REG_BCNQ_DESA 0x0308 +#define REG_HQ_DESA 0x0310 +#define REG_MGQ_DESA 0x0318 +#define REG_VOQ_DESA 0x0320 +#define REG_VIQ_DESA 0x0328 +#define REG_BEQ_DESA 0x0330 +#define REG_BKQ_DESA 0x0338 +#define REG_RX_DESA 0x0340 +#define REG_DBI 0x0348 +#define REG_MDIO 0x0354 +#define REG_DBG_SEL 0x0360 +#define REG_PCIE_HRPWM 0x0361 +#define REG_PCIE_HCPWM 0x0363 +#define REG_UART_CTRL 0x0364 +#define REG_UART_TX_DESA 0x0370 +#define REG_UART_RX_DESA 0x0378 + +#define REG_HDAQ_DESA_NODEF 0x0000 +#define REG_CMDQ_DESA_NODEF 0x0000 + +#define REG_VOQ_INFORMATION 0x0400 +#define REG_VIQ_INFORMATION 0x0404 +#define REG_BEQ_INFORMATION 0x0408 +#define REG_BKQ_INFORMATION 0x040C +#define REG_MGQ_INFORMATION 0x0410 +#define REG_HGQ_INFORMATION 0x0414 +#define REG_BCNQ_INFORMATION 0x0418 + +#define REG_CPU_MGQ_INFORMATION 0x041C +#define REG_FWHW_TXQ_CTRL 0x0420 +#define REG_HWSEQ_CTRL 0x0423 +#define REG_TXPKTBUF_BCNQ_BDNY 0x0424 +#define REG_TXPKTBUF_MGQ_BDNY 0x0425 +#define REG_MULTI_BCNQ_EN 0x0426 +#define REG_MULTI_BCNQ_OFFSET 0x0427 +#define REG_SPEC_SIFS 0x0428 +#define REG_RL 0x042A +#define REG_DARFRC 0x0430 +#define REG_RARFRC 0x0438 +#define REG_RRSR 0x0440 +#define REG_ARFR0 0x0444 +#define REG_ARFR1 0x0448 +#define REG_ARFR2 0x044C +#define REG_ARFR3 0x0450 +#define REG_AGGLEN_LMT 0x0458 +#define REG_AMPDU_MIN_SPACE 0x045C +#define REG_TXPKTBUF_WMAC_LBK_BF_HD 0x045D +#define REG_FAST_EDCA_CTRL 0x0460 +#define REG_RD_RESP_PKT_TH 0x0463 +#define REG_INIRTS_RATE_SEL 0x0480 +#define REG_INIDATA_RATE_SEL 0x0484 +#define REG_POWER_STATUS 0x04A4 +#define REG_POWER_STAGE1 0x04B4 +#define REG_POWER_STAGE2 0x04B8 +#define REG_PKT_LIFE_TIME 0x04C0 +#define REG_STBC_SETTING 0x04C4 +#define REG_PROT_MODE_CTRL 0x04C8 +#define REG_BAR_MODE_CTRL 0x04CC +#define REG_RA_TRY_RATE_AGG_LMT 0x04CF +#define REG_NQOS_SEQ 0x04DC +#define REG_QOS_SEQ 0x04DE +#define REG_NEED_CPU_HANDLE 0x04E0 +#define REG_PKT_LOSE_RPT 0x04E1 +#define REG_PTCL_ERR_STATUS 0x04E2 +#define REG_DUMMY 0x04FC + +#define REG_EDCA_VO_PARAM 0x0500 +#define REG_EDCA_VI_PARAM 0x0504 +#define REG_EDCA_BE_PARAM 0x0508 +#define REG_EDCA_BK_PARAM 0x050C +#define REG_BCNTCFG 0x0510 +#define REG_PIFS 0x0512 +#define REG_RDG_PIFS 0x0513 +#define REG_SIFS_CTX 0x0514 +#define REG_SIFS_TRX 0x0516 +#define REG_AGGR_BREAK_TIME 0x051A +#define REG_SLOT 0x051B +#define REG_TX_PTCL_CTRL 0x0520 +#define REG_TXPAUSE 0x0522 +#define REG_DIS_TXREQ_CLR 0x0523 +#define REG_RD_CTRL 0x0524 +#define REG_TBTT_PROHIBIT 0x0540 +#define REG_RD_NAV_NXT 0x0544 +#define REG_NAV_PROT_LEN 0x0546 +#define REG_BCN_CTRL 0x0550 +#define REG_USTIME_TSF 0x0551 +#define REG_MBID_NUM 0x0552 +#define REG_DUAL_TSF_RST 0x0553 +#define REG_BCN_INTERVAL 0x0554 +#define REG_MBSSID_BCN_SPACE 0x0554 +#define REG_DRVERLYINT 0x0558 +#define REG_BCNDMATIM 0x0559 +#define REG_ATIMWND 0x055A +#define REG_BCN_MAX_ERR 0x055D +#define REG_RXTSF_OFFSET_CCK 0x055E +#define REG_RXTSF_OFFSET_OFDM 0x055F +#define REG_TSFTR 0x0560 +#define REG_INIT_TSFTR 0x0564 +#define REG_PSTIMER 0x0580 +#define REG_TIMER0 0x0584 +#define REG_TIMER1 0x0588 +#define REG_ACMHWCTRL 0x05C0 +#define REG_ACMRSTCTRL 0x05C1 +#define REG_ACMAVG 0x05C2 +#define REG_VO_ADMTIME 0x05C4 +#define REG_VI_ADMTIME 0x05C6 +#define REG_BE_ADMTIME 0x05C8 +#define REG_EDCA_RANDOM_GEN 0x05CC +#define REG_SCH_TXCMD 0x05D0 + +#define REG_APSD_CTRL 0x0600 +#define REG_BWOPMODE 0x0603 +#define REG_TCR 0x0604 +#define REG_RCR 0x0608 +#define REG_RX_PKT_LIMIT 0x060C +#define REG_RX_DLK_TIME 0x060D +#define REG_RX_DRVINFO_SZ 0x060F + +#define REG_MACID 0x0610 +#define REG_BSSID 0x0618 +#define REG_MAR 0x0620 +#define REG_MBIDCAMCFG 0x0628 + +#define REG_USTIME_EDCA 0x0638 +#define REG_MAC_SPEC_SIFS 0x063A +#define REG_RESP_SIFS_CCK 0x063C +#define REG_RESP_SIFS_OFDM 0x063E +#define REG_ACKTO 0x0640 +#define REG_CTS2TO 0x0641 +#define REG_EIFS 0x0642 + +#define REG_NAV_CTRL 0x0650 +#define REG_BACAMCMD 0x0654 +#define REG_BACAMCONTENT 0x0658 +#define REG_LBDLY 0x0660 +#define REG_FWDLY 0x0661 +#define REG_RXERR_RPT 0x0664 +#define REG_WMAC_TRXPTCL_CTL 0x0668 + +#define REG_CAMCMD 0x0670 +#define REG_CAMWRITE 0x0674 +#define REG_CAMREAD 0x0678 +#define REG_CAMDBG 0x067C +#define REG_SECCFG 0x0680 + +#define REG_WOW_CTRL 0x0690 +#define REG_PSSTATUS 0x0691 +#define REG_PS_RX_INFO 0x0692 +#define REG_LPNAV_CTRL 0x0694 +#define REG_WKFMCAM_CMD 0x0698 +#define REG_WKFMCAM_RWD 0x069C +#define REG_RXFLTMAP0 0x06A0 +#define REG_RXFLTMAP1 0x06A2 +#define REG_RXFLTMAP2 0x06A4 +#define REG_BCN_PSR_RPT 0x06A8 +#define REG_CALB32K_CTRL 0x06AC +#define REG_PKT_MON_CTRL 0x06B4 +#define REG_BT_COEX_TABLE 0x06C0 +#define REG_WMAC_RESP_TXINFO 0x06D8 + +#define REG_USB_INFO 0xFE17 +#define REG_USB_SPECIAL_OPTION 0xFE55 +#define REG_USB_DMA_AGG_TO 0xFE5B +#define REG_USB_AGG_TO 0xFE5C +#define REG_USB_AGG_TH 0xFE5D + +#define REG_TEST_USB_TXQS 0xFE48 +#define REG_TEST_SIE_VID 0xFE60 +#define REG_TEST_SIE_PID 0xFE62 +#define REG_TEST_SIE_OPTIONAL 0xFE64 +#define REG_TEST_SIE_CHIRP_K 0xFE65 +#define REG_TEST_SIE_PHY 0xFE66 +#define REG_TEST_SIE_MAC_ADDR 0xFE70 +#define REG_TEST_SIE_STRING 0xFE80 + +#define REG_NORMAL_SIE_VID 0xFE60 +#define REG_NORMAL_SIE_PID 0xFE62 +#define REG_NORMAL_SIE_OPTIONAL 0xFE64 +#define REG_NORMAL_SIE_EP 0xFE65 +#define REG_NORMAL_SIE_PHY 0xFE68 +#define REG_NORMAL_SIE_MAC_ADDR 0xFE70 +#define REG_NORMAL_SIE_STRING 0xFE80 + +#define CR9346 REG_9346CR +#define MSR (REG_CR + 2) +#define ISR REG_HISR +#define TSFR REG_TSFTR + +#define MACIDR0 REG_MACID +#define MACIDR4 (REG_MACID + 4) + +#define PBP REG_PBP + +#define IDR0 MACIDR0 +#define IDR4 MACIDR4 + +#define UNUSED_REGISTER 0x1BF +#define DCAM UNUSED_REGISTER +#define PSR UNUSED_REGISTER +#define BBADDR UNUSED_REGISTER +#define PHYDATAR UNUSED_REGISTER + +#define INVALID_BBRF_VALUE 0x12345678 + +#define MAX_MSS_DENSITY_2T 0x13 +#define MAX_MSS_DENSITY_1T 0x0A + +#define CMDEEPROM_EN BIT(5) +#define CMDEEPROM_SEL BIT(4) +#define CMD9346CR_9356SEL BIT(4) +#define AUTOLOAD_EEPROM (CMDEEPROM_EN|CMDEEPROM_SEL) +#define AUTOLOAD_EFUSE CMDEEPROM_EN + +#define GPIOSEL_GPIO 0 +#define GPIOSEL_ENBT BIT(5) + +#define GPIO_IN REG_GPIO_PIN_CTRL +#define GPIO_OUT (REG_GPIO_PIN_CTRL+1) +#define GPIO_IO_SEL (REG_GPIO_PIN_CTRL+2) +#define GPIO_MOD (REG_GPIO_PIN_CTRL+3) + +#define MSR_NOLINK 0x00 +#define MSR_ADHOC 0x01 +#define MSR_INFRA 0x02 +#define MSR_AP 0x03 + +#define RRSR_RSC_OFFSET 21 +#define RRSR_SHORT_OFFSET 23 +#define RRSR_RSC_BW_40M 0x600000 +#define RRSR_RSC_UPSUBCHNL 0x400000 +#define RRSR_RSC_LOWSUBCHNL 0x200000 +#define RRSR_SHORT 0x800000 +#define RRSR_1M BIT(0) +#define RRSR_2M BIT(1) +#define RRSR_5_5M BIT(2) +#define RRSR_11M BIT(3) +#define RRSR_6M BIT(4) +#define RRSR_9M BIT(5) +#define RRSR_12M BIT(6) +#define RRSR_18M BIT(7) +#define RRSR_24M BIT(8) +#define RRSR_36M BIT(9) +#define RRSR_48M BIT(10) +#define RRSR_54M BIT(11) +#define RRSR_MCS0 BIT(12) +#define RRSR_MCS1 BIT(13) +#define RRSR_MCS2 BIT(14) +#define RRSR_MCS3 BIT(15) +#define RRSR_MCS4 BIT(16) +#define RRSR_MCS5 BIT(17) +#define RRSR_MCS6 BIT(18) +#define RRSR_MCS7 BIT(19) +#define BRSR_ACKSHORTPMB BIT(23) + +#define RATR_1M 0x00000001 +#define RATR_2M 0x00000002 +#define RATR_55M 0x00000004 +#define RATR_11M 0x00000008 +#define RATR_6M 0x00000010 +#define RATR_9M 0x00000020 +#define RATR_12M 0x00000040 +#define RATR_18M 0x00000080 +#define RATR_24M 0x00000100 +#define RATR_36M 0x00000200 +#define RATR_48M 0x00000400 +#define RATR_54M 0x00000800 +#define RATR_MCS0 0x00001000 +#define RATR_MCS1 0x00002000 +#define RATR_MCS2 0x00004000 +#define RATR_MCS3 0x00008000 +#define RATR_MCS4 0x00010000 +#define RATR_MCS5 0x00020000 +#define RATR_MCS6 0x00040000 +#define RATR_MCS7 0x00080000 +#define RATR_MCS8 0x00100000 +#define RATR_MCS9 0x00200000 +#define RATR_MCS10 0x00400000 +#define RATR_MCS11 0x00800000 +#define RATR_MCS12 0x01000000 +#define RATR_MCS13 0x02000000 +#define RATR_MCS14 0x04000000 +#define RATR_MCS15 0x08000000 + +#define RATE_ALL_CCK (RATR_1M | RATR_2M | RATR_55M | RATR_11M) +#define RATE_ALL_OFDM_AG (RATR_6M | RATR_9M | RATR_12M | RATR_18M |\ + RATR_24M | RATR_36M | RATR_48M | RATR_54M) +#define RATE_ALL_OFDM_1SS (RATR_MCS0 | RATR_MCS1 | RATR_MCS2 |\ + RATR_MCS3 | RATR_MCS4 | RATR_MCS5 |\ + RATR_MCS6 | RATR_MCS7) +#define RATE_ALL_OFDM_2SS (RATR_MCS8 | RATR_MCS9 | RATR_MCS10 |\ + RATR_MCS11 | RATR_MCS12 | RATR_MCS13 |\ + RATR_MCS14 | RATR_MCS15) + +#define BW_OPMODE_20MHZ BIT(2) +#define BW_OPMODE_5G BIT(1) +#define BW_OPMODE_11J BIT(0) + +#define CAM_VALID BIT(15) +#define CAM_NOTVALID 0x0000 +#define CAM_USEDK BIT(5) + +#define CAM_NONE 0x0 +#define CAM_WEP40 0x01 +#define CAM_TKIP 0x02 +#define CAM_AES 0x04 +#define CAM_WEP104 0x05 + +#define TOTAL_CAM_ENTRY 32 +#define HALF_CAM_ENTRY 16 + +#define CAM_WRITE BIT(16) +#define CAM_READ 0x00000000 +#define CAM_POLLINIG BIT(31) + +#define SCR_USEDK 0x01 +#define SCR_TXSEC_ENABLE 0x02 +#define SCR_RXSEC_ENABLE 0x04 + +#define WOW_PMEN BIT(0) +#define WOW_WOMEN BIT(1) +#define WOW_MAGIC BIT(2) +#define WOW_UWF BIT(3) + +#define IMR8190_DISABLED 0x0 +#define IMR_BCNDMAINT6 BIT(31) +#define IMR_BCNDMAINT5 BIT(30) +#define IMR_BCNDMAINT4 BIT(29) +#define IMR_BCNDMAINT3 BIT(28) +#define IMR_BCNDMAINT2 BIT(27) +#define IMR_BCNDMAINT1 BIT(26) +#define IMR_BCNDOK8 BIT(25) +#define IMR_BCNDOK7 BIT(24) +#define IMR_BCNDOK6 BIT(23) +#define IMR_BCNDOK5 BIT(22) +#define IMR_BCNDOK4 BIT(21) +#define IMR_BCNDOK3 BIT(20) +#define IMR_BCNDOK2 BIT(19) +#define IMR_BCNDOK1 BIT(18) +#define IMR_TIMEOUT2 BIT(17) +#define IMR_TIMEOUT1 BIT(16) +#define IMR_TXFOVW BIT(15) +#define IMR_PSTIMEOUT BIT(14) +#define IMR_BCNINT BIT(13) +#define IMR_RXFOVW BIT(12) +#define IMR_RDU BIT(11) +#define IMR_ATIMEND BIT(10) +#define IMR_BDOK BIT(9) +#define IMR_HIGHDOK BIT(8) +#define IMR_TBDOK BIT(7) +#define IMR_MGNTDOK BIT(6) +#define IMR_TBDER BIT(5) +#define IMR_BKDOK BIT(4) +#define IMR_BEDOK BIT(3) +#define IMR_VIDOK BIT(2) +#define IMR_VODOK BIT(1) +#define IMR_ROK BIT(0) + +#define IMR_TXERR BIT(11) +#define IMR_RXERR BIT(10) +#define IMR_CPWM BIT(8) +#define IMR_OCPINT BIT(1) +#define IMR_WLANOFF BIT(0) + +/* 8723E series PCIE Host IMR/ISR bit */ +/* IMR DW0 Bit 0-31 */ +#define PHIMR_TIMEOUT2 BIT(31) +#define PHIMR_TIMEOUT1 BIT(30) +#define PHIMR_PSTIMEOUT BIT(29) +#define PHIMR_GTINT4 BIT(28) +#define PHIMR_GTINT3 BIT(27) +#define PHIMR_TXBCNERR BIT(26) +#define PHIMR_TXBCNOK BIT(25) +#define PHIMR_TSF_BIT32_TOGGLE BIT(24) +#define PHIMR_BCNDMAINT3 BIT(23) +#define PHIMR_BCNDMAINT2 BIT(22) +#define PHIMR_BCNDMAINT1 BIT(21) +#define PHIMR_BCNDMAINT0 BIT(20) +#define PHIMR_BCNDOK3 BIT(19) +#define PHIMR_BCNDOK2 BIT(18) +#define PHIMR_BCNDOK1 BIT(17) +#define PHIMR_BCNDOK0 BIT(16) +#define PHIMR_HSISR_IND_ON BIT(15) +#define PHIMR_BCNDMAINT_E BIT(14) +#define PHIMR_ATIMEND_E BIT(13) +#define PHIMR_ATIM_CTW_END BIT(12) +#define PHIMR_HISRE_IND BIT(11) +#define PHIMR_C2HCMD BIT(10) +#define PHIMR_CPWM2 BIT(9) +#define PHIMR_CPWM BIT(8) +#define PHIMR_HIGHDOK BIT(7) +#define PHIMR_MGNTDOK BIT(6) +#define PHIMR_BKDOK BIT(5) +#define PHIMR_BEDOK BIT(4) +#define PHIMR_VIDOK BIT(3) +#define PHIMR_VODOK BIT(2) +#define PHIMR_RDU BIT(1) +#define PHIMR_ROK BIT(0) + +/* PCIE Host Interrupt Status Extension bit */ +#define PHIMR_BCNDMAINT7 BIT(23) +#define PHIMR_BCNDMAINT6 BIT(22) +#define PHIMR_BCNDMAINT5 BIT(21) +#define PHIMR_BCNDMAINT4 BIT(20) +#define PHIMR_BCNDOK7 BIT(19) +#define PHIMR_BCNDOK6 BIT(18) +#define PHIMR_BCNDOK5 BIT(17) +#define PHIMR_BCNDOK4 BIT(16) +/* bit12-15: RSVD */ +#define PHIMR_TXERR BIT(11) +#define PHIMR_RXERR BIT(10) +#define PHIMR_TXFOVW BIT(9) +#define PHIMR_RXFOVW BIT(8) +/* bit2-7: RSV */ +#define PHIMR_OCPINT BIT(1) + +#define HWSET_MAX_SIZE 256 +#define EFUSE_MAX_SECTION 32 +#define EFUSE_REAL_CONTENT_LEN 512 +#define EFUSE_OOB_PROTECT_BYTES 15 + +#define EEPROM_DEFAULT_TSSI 0x0 +#define EEPROM_DEFAULT_TXPOWERDIFF 0x0 +#define EEPROM_DEFAULT_CRYSTALCAP 0x5 +#define EEPROM_DEFAULT_BOARDTYPE 0x02 +#define EEPROM_DEFAULT_TXPOWER 0x1010 +#define EEPROM_DEFAULT_HT2T_TXPWR 0x10 + +#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 +#define EEPROM_DEFAULT_THERMALMETER 0x12 +#define EEPROM_DEFAULT_ANTTXPOWERDIFF 0x0 +#define EEPROM_DEFAULT_TXPWDIFF_CRYSTALCAP 0x5 +#define EEPROM_DEFAULT_TXPOWERLEVEL 0x22 +#define EEPROM_DEFAULT_HT40_2SDIFF 0x0 +#define EEPROM_DEFAULT_HT20_DIFF 2 +#define EEPROM_DEFAULT_LEGACYHTTXPOWERDIFF 0x3 +#define EEPROM_DEFAULT_HT40_PWRMAXOFFSET 0 +#define EEPROM_DEFAULT_HT20_PWRMAXOFFSET 0 + + +#define EEPROM_DEFAULT_PID 0x1234 +#define EEPROM_DEFAULT_VID 0x5678 +#define EEPROM_DEFAULT_CUSTOMERID 0xAB +#define EEPROM_DEFAULT_SUBCUSTOMERID 0xCD +#define EEPROM_DEFAULT_VERSION 0 + +#define EEPROM_CHANNEL_PLAN_FCC 0x0 +#define EEPROM_CHANNEL_PLAN_IC 0x1 +#define EEPROM_CHANNEL_PLAN_ETSI 0x2 +#define EEPROM_CHANNEL_PLAN_SPAIN 0x3 +#define EEPROM_CHANNEL_PLAN_FRANCE 0x4 +#define EEPROM_CHANNEL_PLAN_MKK 0x5 +#define EEPROM_CHANNEL_PLAN_MKK1 0x6 +#define EEPROM_CHANNEL_PLAN_ISRAEL 0x7 +#define EEPROM_CHANNEL_PLAN_TELEC 0x8 +#define EEPROM_CHANNEL_PLAN_GLOBAL_DOMAIN 0x9 +#define EEPROM_CHANNEL_PLAN_WORLD_WIDE_13 0xA +#define EEPROM_CHANNEL_PLAN_NCC 0xB +#define EEPROM_CHANNEL_PLAN_BY_HW_MASK 0x80 + +#define EEPROM_CID_DEFAULT 0x0 +#define EEPROM_CID_TOSHIBA 0x4 +#define EEPROM_CID_CCX 0x10 +#define EEPROM_CID_QMI 0x0D +#define EEPROM_CID_WHQL 0xFE + +#define RTL8192_EEPROM_ID 0x8129 + +#define RTL8190_EEPROM_ID 0x8129 +#define EEPROM_HPON 0x02 +#define EEPROM_CLK 0x06 +#define EEPROM_TESTR 0x08 + +#define EEPROM_VID 0x49 +#define EEPROM_DID 0x4B +#define EEPROM_SVID 0x4D +#define EEPROM_SMID 0x4F + +#define EEPROM_MAC_ADDR 0x67 + +#define EEPROM_CCK_TX_PWR_INX 0x5A +#define EEPROM_HT40_1S_TX_PWR_INX 0x60 +#define EEPROM_HT40_2S_TX_PWR_INX_DIFF 0x66 +#define EEPROM_HT20_TX_PWR_INX_DIFF 0x69 +#define EEPROM_OFDM_TX_PWR_INX_DIFF 0x6C +#define EEPROM_HT40_MAX_PWR_OFFSET 0x25 +#define EEPROM_HT20_MAX_PWR_OFFSET 0x22 + +#define EEPROM_THERMAL_METER 0x2a +#define EEPROM_XTAL_K 0x78 +#define EEPROM_RF_OPT1 0x79 +#define EEPROM_RF_OPT2 0x7A +#define EEPROM_RF_OPT3 0x7B +#define EEPROM_RF_OPT4 0x7C +#define EEPROM_CHANNEL_PLAN 0x28 +#define EEPROM_VERSION 0x30 +#define EEPROM_CUSTOMER_ID 0x31 + +#define EEPROM_PWRDIFF 0x54 + +#define EEPROM_TXPOWERCCK 0x10 +#define EEPROM_TXPOWERHT40_1S 0x16 +#define EEPROM_TXPOWERHT40_2SDIFF 0x66 +#define EEPROM_TXPOWERHT20DIFF 0x1C +#define EEPROM_TXPOWER_OFDMDIFF 0x1F + +#define EEPROM_TXPWR_GROUP 0x22 + +#define EEPROM_TSSI_A 0x29 +#define EEPROM_TSSI_B 0x77 + +#define EEPROM_CHANNELPLAN 0x28 + +#define RF_OPTION1 0x2B +#define RF_OPTION2 0x2C +#define RF_OPTION3 0x2D +#define RF_OPTION4 0x2E + +#define STOPBECON BIT(6) +#define STOPHIGHT BIT(5) +#define STOPMGT BIT(4) +#define STOPVO BIT(3) +#define STOPVI BIT(2) +#define STOPBE BIT(1) +#define STOPBK BIT(0) + +#define RCR_APPFCS BIT(31) +#define RCR_APP_MIC BIT(30) +#define RCR_APP_ICV BIT(29) +#define RCR_APP_PHYST_RXFF BIT(28) +#define RCR_APP_BA_SSN BIT(27) +#define RCR_ENMBID BIT(24) +#define RCR_LSIGEN BIT(23) +#define RCR_MFBEN BIT(22) +#define RCR_HTC_LOC_CTRL BIT(14) +#define RCR_AMF BIT(13) +#define RCR_ACF BIT(12) +#define RCR_ADF BIT(11) +#define RCR_AICV BIT(9) +#define RCR_ACRC32 BIT(8) +#define RCR_CBSSID_BCN BIT(7) +#define RCR_CBSSID_DATA BIT(6) +#define RCR_CBSSID RCR_CBSSID_DATA +#define RCR_APWRMGT BIT(5) +#define RCR_ADD3 BIT(4) +#define RCR_AB BIT(3) +#define RCR_AM BIT(2) +#define RCR_APM BIT(1) +#define RCR_AAP BIT(0) +#define RCR_MXDMA_OFFSET 8 +#define RCR_FIFO_OFFSET 13 + +#define RSV_CTRL 0x001C +#define RD_CTRL 0x0524 + +#define REG_USB_INFO 0xFE17 +#define REG_USB_SPECIAL_OPTION 0xFE55 +#define REG_USB_DMA_AGG_TO 0xFE5B +#define REG_USB_AGG_TO 0xFE5C +#define REG_USB_AGG_TH 0xFE5D + +#define REG_USB_VID 0xFE60 +#define REG_USB_PID 0xFE62 +#define REG_USB_OPTIONAL 0xFE64 +#define REG_USB_CHIRP_K 0xFE65 +#define REG_USB_PHY 0xFE66 +#define REG_USB_MAC_ADDR 0xFE70 +#define REG_USB_HRPWM 0xFE58 +#define REG_USB_HCPWM 0xFE57 + +#define SW18_FPWM BIT(3) + +#define ISO_MD2PP BIT(0) +#define ISO_UA2USB BIT(1) +#define ISO_UD2CORE BIT(2) +#define ISO_PA2PCIE BIT(3) +#define ISO_PD2CORE BIT(4) +#define ISO_IP2MAC BIT(5) +#define ISO_DIOP BIT(6) +#define ISO_DIOE BIT(7) +#define ISO_EB2CORE BIT(8) +#define ISO_DIOR BIT(9) + +#define PWC_EV25V BIT(14) +#define PWC_EV12V BIT(15) + +#define FEN_BBRSTB BIT(0) +#define FEN_BB_GLB_RSTn BIT(1) +#define FEN_USBA BIT(2) +#define FEN_UPLL BIT(3) +#define FEN_USBD BIT(4) +#define FEN_DIO_PCIE BIT(5) +#define FEN_PCIEA BIT(6) +#define FEN_PPLL BIT(7) +#define FEN_PCIED BIT(8) +#define FEN_DIOE BIT(9) +#define FEN_CPUEN BIT(10) +#define FEN_DCORE BIT(11) +#define FEN_ELDR BIT(12) +#define FEN_DIO_RF BIT(13) +#define FEN_HWPDN BIT(14) +#define FEN_MREGEN BIT(15) + +#define PFM_LDALL BIT(0) +#define PFM_ALDN BIT(1) +#define PFM_LDKP BIT(2) +#define PFM_WOWL BIT(3) +#define EnPDN BIT(4) +#define PDN_PL BIT(5) +#define APFM_ONMAC BIT(8) +#define APFM_OFF BIT(9) +#define APFM_RSM BIT(10) +#define AFSM_HSUS BIT(11) +#define AFSM_PCIE BIT(12) +#define APDM_MAC BIT(13) +#define APDM_HOST BIT(14) +#define APDM_HPDN BIT(15) +#define RDY_MACON BIT(16) +#define SUS_HOST BIT(17) +#define ROP_ALD BIT(20) +#define ROP_PWR BIT(21) +#define ROP_SPS BIT(22) +#define SOP_MRST BIT(25) +#define SOP_FUSE BIT(26) +#define SOP_ABG BIT(27) +#define SOP_AMB BIT(28) +#define SOP_RCK BIT(29) +#define SOP_A8M BIT(30) +#define XOP_BTCK BIT(31) + +#define ANAD16V_EN BIT(0) +#define ANA8M BIT(1) +#define MACSLP BIT(4) +#define LOADER_CLK_EN BIT(5) +#define _80M_SSC_DIS BIT(7) +#define _80M_SSC_EN_HO BIT(8) +#define PHY_SSC_RSTB BIT(9) +#define SEC_CLK_EN BIT(10) +#define MAC_CLK_EN BIT(11) +#define SYS_CLK_EN BIT(12) +#define RING_CLK_EN BIT(13) + +#define BOOT_FROM_EEPROM BIT(4) +#define EEPROM_EN BIT(5) + +#define AFE_BGEN BIT(0) +#define AFE_MBEN BIT(1) +#define MAC_ID_EN BIT(7) + +#define WLOCK_ALL BIT(0) +#define WLOCK_00 BIT(1) +#define WLOCK_04 BIT(2) +#define WLOCK_08 BIT(3) +#define WLOCK_40 BIT(4) +#define R_DIS_PRST_0 BIT(5) +#define R_DIS_PRST_1 BIT(6) +#define LOCK_ALL_EN BIT(7) + +#define RF_EN BIT(0) +#define RF_RSTB BIT(1) +#define RF_SDMRSTB BIT(2) + +#define LDA15_EN BIT(0) +#define LDA15_STBY BIT(1) +#define LDA15_OBUF BIT(2) +#define LDA15_REG_VOS BIT(3) +#define _LDA15_VOADJ(x) (((x) & 0x7) << 4) + +#define LDV12_EN BIT(0) +#define LDV12_SDBY BIT(1) +#define LPLDO_HSM BIT(2) +#define LPLDO_LSM_DIS BIT(3) +#define _LDV12_VADJ(x) (((x) & 0xF) << 4) + +#define XTAL_EN BIT(0) +#define XTAL_BSEL BIT(1) +#define _XTAL_BOSC(x) (((x) & 0x3) << 2) +#define _XTAL_CADJ(x) (((x) & 0xF) << 4) +#define XTAL_GATE_USB BIT(8) +#define _XTAL_USB_DRV(x) (((x) & 0x3) << 9) +#define XTAL_GATE_AFE BIT(11) +#define _XTAL_AFE_DRV(x) (((x) & 0x3) << 12) +#define XTAL_RF_GATE BIT(14) +#define _XTAL_RF_DRV(x) (((x) & 0x3) << 15) +#define XTAL_GATE_DIG BIT(17) +#define _XTAL_DIG_DRV(x) (((x) & 0x3) << 18) +#define XTAL_BT_GATE BIT(20) +#define _XTAL_BT_DRV(x) (((x) & 0x3) << 21) +#define _XTAL_GPIO(x) (((x) & 0x7) << 23) + +#define CKDLY_AFE BIT(26) +#define CKDLY_USB BIT(27) +#define CKDLY_DIG BIT(28) +#define CKDLY_BT BIT(29) + +#define APLL_EN BIT(0) +#define APLL_320_EN BIT(1) +#define APLL_FREF_SEL BIT(2) +#define APLL_EDGE_SEL BIT(3) +#define APLL_WDOGB BIT(4) +#define APLL_LPFEN BIT(5) + +#define APLL_REF_CLK_13MHZ 0x1 +#define APLL_REF_CLK_19_2MHZ 0x2 +#define APLL_REF_CLK_20MHZ 0x3 +#define APLL_REF_CLK_25MHZ 0x4 +#define APLL_REF_CLK_26MHZ 0x5 +#define APLL_REF_CLK_38_4MHZ 0x6 +#define APLL_REF_CLK_40MHZ 0x7 + +#define APLL_320EN BIT(14) +#define APLL_80EN BIT(15) +#define APLL_1MEN BIT(24) + +#define ALD_EN BIT(18) +#define EF_PD BIT(19) +#define EF_FLAG BIT(31) + +#define EF_TRPT BIT(7) +#define LDOE25_EN BIT(31) + +#define RSM_EN BIT(0) +#define Timer_EN BIT(4) + +#define TRSW0EN BIT(2) +#define TRSW1EN BIT(3) +#define EROM_EN BIT(4) +#define EnBT BIT(5) +#define EnUart BIT(8) +#define Uart_910 BIT(9) +#define EnPMAC BIT(10) +#define SIC_SWRST BIT(11) +#define EnSIC BIT(12) +#define SIC_23 BIT(13) +#define EnHDP BIT(14) +#define SIC_LBK BIT(15) + +#define LED0PL BIT(4) +#define LED1PL BIT(12) +#define LED0DIS BIT(7) + +#define MCUFWDL_EN BIT(0) +#define MCUFWDL_RDY BIT(1) +#define FWDL_ChkSum_rpt BIT(2) +#define MACINI_RDY BIT(3) +#define BBINI_RDY BIT(4) +#define RFINI_RDY BIT(5) +#define WINTINI_RDY BIT(6) +#define CPRST BIT(23) + +#define XCLK_VLD BIT(0) +#define ACLK_VLD BIT(1) +#define UCLK_VLD BIT(2) +#define PCLK_VLD BIT(3) +#define PCIRSTB BIT(4) +#define V15_VLD BIT(5) +#define TRP_B15V_EN BIT(7) +#define SIC_IDLE BIT(8) +#define BD_MAC2 BIT(9) +#define BD_MAC1 BIT(10) +#define IC_MACPHY_MODE BIT(11) +#define BT_FUNC BIT(16) +#define VENDOR_ID BIT(19) +#define PAD_HWPD_IDN BIT(22) +#define TRP_VAUX_EN BIT(23) +#define TRP_BT_EN BIT(24) +#define BD_PKG_SEL BIT(25) +#define BD_HCI_SEL BIT(26) +#define TYPE_ID BIT(27) + +#define CHIP_VER_RTL_MASK 0xF000 +#define CHIP_VER_RTL_SHIFT 12 + +#define REG_LBMODE (REG_CR + 3) + +#define HCI_TXDMA_EN BIT(0) +#define HCI_RXDMA_EN BIT(1) +#define TXDMA_EN BIT(2) +#define RXDMA_EN BIT(3) +#define PROTOCOL_EN BIT(4) +#define SCHEDULE_EN BIT(5) +#define MACTXEN BIT(6) +#define MACRXEN BIT(7) +#define ENSWBCN BIT(8) +#define ENSEC BIT(9) + +#define _NETTYPE(x) (((x) & 0x3) << 16) +#define MASK_NETTYPE 0x30000 +#define NT_NO_LINK 0x0 +#define NT_LINK_AD_HOC 0x1 +#define NT_LINK_AP 0x2 +#define NT_AS_AP 0x3 + +#define _LBMODE(x) (((x) & 0xF) << 24) +#define MASK_LBMODE 0xF000000 +#define LOOPBACK_NORMAL 0x0 +#define LOOPBACK_IMMEDIATELY 0xB +#define LOOPBACK_MAC_DELAY 0x3 +#define LOOPBACK_PHY 0x1 +#define LOOPBACK_DMA 0x7 + +#define GET_RX_PAGE_SIZE(value) ((value) & 0xF) +#define GET_TX_PAGE_SIZE(value) (((value) & 0xF0) >> 4) +#define _PSRX_MASK 0xF +#define _PSTX_MASK 0xF0 +#define _PSRX(x) (x) +#define _PSTX(x) ((x) << 4) + +#define PBP_64 0x0 +#define PBP_128 0x1 +#define PBP_256 0x2 +#define PBP_512 0x3 +#define PBP_1024 0x4 + +#define RXDMA_ARBBW_EN BIT(0) +#define RXSHFT_EN BIT(1) +#define RXDMA_AGG_EN BIT(2) +#define QS_VO_QUEUE BIT(8) +#define QS_VI_QUEUE BIT(9) +#define QS_BE_QUEUE BIT(10) +#define QS_BK_QUEUE BIT(11) +#define QS_MANAGER_QUEUE BIT(12) +#define QS_HIGH_QUEUE BIT(13) + +#define HQSEL_VOQ BIT(0) +#define HQSEL_VIQ BIT(1) +#define HQSEL_BEQ BIT(2) +#define HQSEL_BKQ BIT(3) +#define HQSEL_MGTQ BIT(4) +#define HQSEL_HIQ BIT(5) + +#define _TXDMA_HIQ_MAP(x) (((x)&0x3) << 14) +#define _TXDMA_MGQ_MAP(x) (((x)&0x3) << 12) +#define _TXDMA_BKQ_MAP(x) (((x)&0x3) << 10) +#define _TXDMA_BEQ_MAP(x) (((x)&0x3) << 8) +#define _TXDMA_VIQ_MAP(x) (((x)&0x3) << 6) +#define _TXDMA_VOQ_MAP(x) (((x)&0x3) << 4) + +#define QUEUE_LOW 1 +#define QUEUE_NORMAL 2 +#define QUEUE_HIGH 3 + +#define _LLT_NO_ACTIVE 0x0 +#define _LLT_WRITE_ACCESS 0x1 +#define _LLT_READ_ACCESS 0x2 + +#define _LLT_INIT_DATA(x) ((x) & 0xFF) +#define _LLT_INIT_ADDR(x) (((x) & 0xFF) << 8) +#define _LLT_OP(x) (((x) & 0x3) << 30) +#define _LLT_OP_VALUE(x) (((x) >> 30) & 0x3) + +#define BB_WRITE_READ_MASK (BIT(31) | BIT(30)) +#define BB_WRITE_EN BIT(30) +#define BB_READ_EN BIT(31) + +#define _HPQ(x) ((x) & 0xFF) +#define _LPQ(x) (((x) & 0xFF) << 8) +#define _PUBQ(x) (((x) & 0xFF) << 16) +#define _NPQ(x) ((x) & 0xFF) + +#define HPQ_PUBLIC_DIS BIT(24) +#define LPQ_PUBLIC_DIS BIT(25) +#define LD_RQPN BIT(31) + +#define BCN_VALID BIT(16) +#define BCN_HEAD(x) (((x) & 0xFF) << 8) +#define BCN_HEAD_MASK 0xFF00 + +#define BLK_DESC_NUM_SHIFT 4 +#define BLK_DESC_NUM_MASK 0xF + +#define DROP_DATA_EN BIT(9) + +#define EN_AMPDU_RTY_NEW BIT(7) + +#define _INIRTSMCS_SEL(x) ((x) & 0x3F) + +#define _SPEC_SIFS_CCK(x) ((x) & 0xFF) +#define _SPEC_SIFS_OFDM(x) (((x) & 0xFF) << 8) + +#define RATE_REG_BITMAP_ALL 0xFFFFF + +#define _RRSC_BITMAP(x) ((x) & 0xFFFFF) + +#define _RRSR_RSC(x) (((x) & 0x3) << 21) +#define RRSR_RSC_RESERVED 0x0 +#define RRSR_RSC_UPPER_SUBCHANNEL 0x1 +#define RRSR_RSC_LOWER_SUBCHANNEL 0x2 +#define RRSR_RSC_DUPLICATE_MODE 0x3 + +#define USE_SHORT_G1 BIT(20) + +#define _AGGLMT_MCS0(x) ((x) & 0xF) +#define _AGGLMT_MCS1(x) (((x) & 0xF) << 4) +#define _AGGLMT_MCS2(x) (((x) & 0xF) << 8) +#define _AGGLMT_MCS3(x) (((x) & 0xF) << 12) +#define _AGGLMT_MCS4(x) (((x) & 0xF) << 16) +#define _AGGLMT_MCS5(x) (((x) & 0xF) << 20) +#define _AGGLMT_MCS6(x) (((x) & 0xF) << 24) +#define _AGGLMT_MCS7(x) (((x) & 0xF) << 28) + +#define RETRY_LIMIT_SHORT_SHIFT 8 +#define RETRY_LIMIT_LONG_SHIFT 0 + +#define _DARF_RC1(x) ((x) & 0x1F) +#define _DARF_RC2(x) (((x) & 0x1F) << 8) +#define _DARF_RC3(x) (((x) & 0x1F) << 16) +#define _DARF_RC4(x) (((x) & 0x1F) << 24) +#define _DARF_RC5(x) ((x) & 0x1F) +#define _DARF_RC6(x) (((x) & 0x1F) << 8) +#define _DARF_RC7(x) (((x) & 0x1F) << 16) +#define _DARF_RC8(x) (((x) & 0x1F) << 24) + +#define _RARF_RC1(x) ((x) & 0x1F) +#define _RARF_RC2(x) (((x) & 0x1F) << 8) +#define _RARF_RC3(x) (((x) & 0x1F) << 16) +#define _RARF_RC4(x) (((x) & 0x1F) << 24) +#define _RARF_RC5(x) ((x) & 0x1F) +#define _RARF_RC6(x) (((x) & 0x1F) << 8) +#define _RARF_RC7(x) (((x) & 0x1F) << 16) +#define _RARF_RC8(x) (((x) & 0x1F) << 24) + +#define AC_PARAM_TXOP_LIMIT_OFFSET 16 +#define AC_PARAM_ECW_MAX_OFFSET 12 +#define AC_PARAM_ECW_MIN_OFFSET 8 +#define AC_PARAM_AIFS_OFFSET 0 + +#define _AIFS(x) (x) +#define _ECW_MAX_MIN(x) ((x) << 8) +#define _TXOP_LIMIT(x) ((x) << 16) + +#define _BCNIFS(x) ((x) & 0xFF) +#define _BCNECW(x) ((((x) & 0xF)) << 8) + +#define _LRL(x) ((x) & 0x3F) +#define _SRL(x) (((x) & 0x3F) << 8) + +#define _SIFS_CCK_CTX(x) ((x) & 0xFF) +#define _SIFS_CCK_TRX(x) (((x) & 0xFF) << 8); + +#define _SIFS_OFDM_CTX(x) ((x) & 0xFF) +#define _SIFS_OFDM_TRX(x) (((x) & 0xFF) << 8); + +#define _TBTT_PROHIBIT_HOLD(x) (((x) & 0xFF) << 8) + +#define DIS_EDCA_CNT_DWN BIT(11) + +#define EN_MBSSID BIT(1) +#define EN_TXBCN_RPT BIT(2) +#define EN_BCN_FUNCTION BIT(3) + +#define TSFTR_RST BIT(0) +#define TSFTR1_RST BIT(1) + +#define STOP_BCNQ BIT(6) + +#define DIS_TSF_UDT0_NORMAL_CHIP BIT(4) +#define DIS_TSF_UDT0_TEST_CHIP BIT(5) + +#define AcmHw_HwEn BIT(0) +#define AcmHw_BeqEn BIT(1) +#define AcmHw_ViqEn BIT(2) +#define AcmHw_VoqEn BIT(3) +#define AcmHw_BeqStatus BIT(4) +#define AcmHw_ViqStatus BIT(5) +#define AcmHw_VoqStatus BIT(6) + +#define APSDOFF BIT(6) +#define APSDOFF_STATUS BIT(7) + +#define BW_20MHZ BIT(2) + +#define RATE_BITMAP_ALL 0xFFFFF + +#define RATE_RRSR_CCK_ONLY_1M 0xFFFF1 + +#define TSFRST BIT(0) +#define DIS_GCLK BIT(1) +#define PAD_SEL BIT(2) +#define PWR_ST BIT(6) +#define PWRBIT_OW_EN BIT(7) +#define ACRC BIT(8) +#define CFENDFORM BIT(9) +#define ICV BIT(10) + +#define AAP BIT(0) +#define APM BIT(1) +#define AM BIT(2) +#define AB BIT(3) +#define ADD3 BIT(4) +#define APWRMGT BIT(5) +#define CBSSID BIT(6) +#define CBSSID_DATA BIT(6) +#define CBSSID_BCN BIT(7) +#define ACRC32 BIT(8) +#define AICV BIT(9) +#define ADF BIT(11) +#define ACF BIT(12) +#define AMF BIT(13) +#define HTC_LOC_CTRL BIT(14) +#define UC_DATA_EN BIT(16) +#define BM_DATA_EN BIT(17) +#define MFBEN BIT(22) +#define LSIGEN BIT(23) +#define EnMBID BIT(24) +#define APP_BASSN BIT(27) +#define APP_PHYSTS BIT(28) +#define APP_ICV BIT(29) +#define APP_MIC BIT(30) +#define APP_FCS BIT(31) + +#define _MIN_SPACE(x) ((x) & 0x7) +#define _SHORT_GI_PADDING(x) (((x) & 0x1F) << 3) + +#define RXERR_TYPE_OFDM_PPDU 0 +#define RXERR_TYPE_OFDM_FALSE_ALARM 1 +#define RXERR_TYPE_OFDM_MPDU_OK 2 +#define RXERR_TYPE_OFDM_MPDU_FAIL 3 +#define RXERR_TYPE_CCK_PPDU 4 +#define RXERR_TYPE_CCK_FALSE_ALARM 5 +#define RXERR_TYPE_CCK_MPDU_OK 6 +#define RXERR_TYPE_CCK_MPDU_FAIL 7 +#define RXERR_TYPE_HT_PPDU 8 +#define RXERR_TYPE_HT_FALSE_ALARM 9 +#define RXERR_TYPE_HT_MPDU_TOTAL 10 +#define RXERR_TYPE_HT_MPDU_OK 11 +#define RXERR_TYPE_HT_MPDU_FAIL 12 +#define RXERR_TYPE_RX_FULL_DROP 15 + +#define RXERR_COUNTER_MASK 0xFFFFF +#define RXERR_RPT_RST BIT(27) +#define _RXERR_RPT_SEL(type) ((type) << 28) + +#define SCR_TxUseDK BIT(0) +#define SCR_RxUseDK BIT(1) +#define SCR_TxEncEnable BIT(2) +#define SCR_RxDecEnable BIT(3) +#define SCR_SKByA2 BIT(4) +#define SCR_NoSKMC BIT(5) +#define SCR_TXBCUSEDK BIT(6) +#define SCR_RXBCUSEDK BIT(7) + +#define USB_IS_HIGH_SPEED 0 +#define USB_IS_FULL_SPEED 1 +#define USB_SPEED_MASK BIT(5) + +#define USB_NORMAL_SIE_EP_MASK 0xF +#define USB_NORMAL_SIE_EP_SHIFT 4 + +#define USB_TEST_EP_MASK 0x30 +#define USB_TEST_EP_SHIFT 4 + +#define USB_AGG_EN BIT(3) + +#define MAC_ADDR_LEN 6 +#define LAST_ENTRY_OF_TX_PKT_BUFFER 255 + +#define POLLING_LLT_THRESHOLD 20 +#define POLLING_READY_TIMEOUT_COUNT 1000 + +#define MAX_MSS_DENSITY_2T 0x13 +#define MAX_MSS_DENSITY_1T 0x0A + +#define EPROM_CMD_OPERATING_MODE_MASK ((1<<7)|(1<<6)) +#define EPROM_CMD_CONFIG 0x3 +#define EPROM_CMD_LOAD 1 + +#define HWSET_MAX_SIZE_92S HWSET_MAX_SIZE + +#define HAL_8192C_HW_GPIO_WPS_BIT BIT(2) + +#define RPMAC_RESET 0x100 +#define RPMAC_TXSTART 0x104 +#define RPMAC_TXLEGACYSIG 0x108 +#define RPMAC_TXHTSIG1 0x10c +#define RPMAC_TXHTSIG2 0x110 +#define RPMAC_PHYDEBUG 0x114 +#define RPMAC_TXPACKETNUM 0x118 +#define RPMAC_TXIDLE 0x11c +#define RPMAC_TXMACHEADER0 0x120 +#define RPMAC_TXMACHEADER1 0x124 +#define RPMAC_TXMACHEADER2 0x128 +#define RPMAC_TXMACHEADER3 0x12c +#define RPMAC_TXMACHEADER4 0x130 +#define RPMAC_TXMACHEADER5 0x134 +#define RPMAC_TXDADATYPE 0x138 +#define RPMAC_TXRANDOMSEED 0x13c +#define RPMAC_CCKPLCPPREAMBLE 0x140 +#define RPMAC_CCKPLCPHEADER 0x144 +#define RPMAC_CCKCRC16 0x148 +#define RPMAC_OFDMRXCRC32OK 0x170 +#define RPMAC_OFDMRXCRC32Er 0x174 +#define RPMAC_OFDMRXPARITYER 0x178 +#define RPMAC_OFDMRXCRC8ER 0x17c +#define RPMAC_CCKCRXRC16ER 0x180 +#define RPMAC_CCKCRXRC32ER 0x184 +#define RPMAC_CCKCRXRC32OK 0x188 +#define RPMAC_TXSTATUS 0x18c + +#define RFPGA0_RFMOD 0x800 + +#define RFPGA0_TXINFO 0x804 +#define RFPGA0_PSDFUNCTION 0x808 + +#define RFPGA0_TXGAINSTAGE 0x80c + +#define RFPGA0_RFTIMING1 0x810 +#define RFPGA0_RFTIMING2 0x814 + +#define RFPGA0_XA_HSSIPARAMETER1 0x820 +#define RFPGA0_XA_HSSIPARAMETER2 0x824 +#define RFPGA0_XB_HSSIPARAMETER1 0x828 +#define RFPGA0_XB_HSSIPARAMETER2 0x82c + +#define RFPGA0_XA_LSSIPARAMETER 0x840 +#define RFPGA0_XB_LSSIPARAMETER 0x844 + +#define RFPGA0_RFWAKEUPPARAMETER 0x850 +#define RFPGA0_RFSLEEPUPPARAMETER 0x854 + +#define RFPGA0_XAB_SWITCHCONTROL 0x858 +#define RFPGA0_XCD_SWITCHCONTROL 0x85c + +#define RFPGA0_XA_RFINTERFACEOE 0x860 +#define RFPGA0_XB_RFINTERFACEOE 0x864 + +#define RFPGA0_XAB_RFINTERFACESW 0x870 +#define RFPGA0_XCD_RFINTERFACESW 0x874 + +#define rFPGA0_XAB_RFPARAMETER 0x878 +#define rFPGA0_XCD_RFPARAMETER 0x87c + +#define RFPGA0_ANALOGPARAMETER1 0x880 +#define RFPGA0_ANALOGPARAMETER2 0x884 +#define RFPGA0_ANALOGPARAMETER3 0x888 +#define RFPGA0_ANALOGPARAMETER4 0x88c + +#define RFPGA0_XA_LSSIREADBACK 0x8a0 +#define RFPGA0_XB_LSSIREADBACK 0x8a4 +#define RFPGA0_XC_LSSIREADBACK 0x8a8 +#define RFPGA0_XD_LSSIREADBACK 0x8ac + +#define RFPGA0_PSDREPORT 0x8b4 +#define TRANSCEIVEA_HSPI_READBACK 0x8b8 +#define TRANSCEIVEB_HSPI_READBACK 0x8bc +#define RFPGA0_XAB_RFINTERFACERB 0x8e0 +#define RFPGA0_XCD_RFINTERFACERB 0x8e4 + +#define RFPGA1_RFMOD 0x900 + +#define RFPGA1_TXBLOCK 0x904 +#define RFPGA1_DEBUGSELECT 0x908 +#define RFPGA1_TXINFO 0x90c + +#define RCCK0_SYSTEM 0xa00 + +#define RCCK0_AFESETTING 0xa04 +#define RCCK0_CCA 0xa08 + +#define RCCK0_RXAGC1 0xa0c +#define RCCK0_RXAGC2 0xa10 + +#define RCCK0_RXHP 0xa14 + +#define RCCK0_DSPPARAMETER1 0xa18 +#define RCCK0_DSPPARAMETER2 0xa1c + +#define RCCK0_TXFILTER1 0xa20 +#define RCCK0_TXFILTER2 0xa24 +#define RCCK0_DEBUGPORT 0xa28 +#define RCCK0_FALSEALARMREPORT 0xa2c +#define RCCK0_TRSSIREPORT 0xa50 +#define RCCK0_RXREPORT 0xa54 +#define RCCK0_FACOUNTERLOWER 0xa5c +#define RCCK0_FACOUNTERUPPER 0xa58 + +#define ROFDM0_LSTF 0xc00 + +#define ROFDM0_TRXPATHENABLE 0xc04 +#define ROFDM0_TRMUXPAR 0xc08 +#define ROFDM0_TRSWISOLATION 0xc0c + +#define ROFDM0_XARXAFE 0xc10 +#define ROFDM0_XARXIQIMBALANCE 0xc14 +#define ROFDM0_XBRXAFE 0xc18 +#define ROFDM0_XBRXIQIMBALANCE 0xc1c +#define ROFDM0_XCRXAFE 0xc20 +#define ROFDM0_XCRXIQIMBANLANCE 0xc24 +#define ROFDM0_XDRXAFE 0xc28 +#define ROFDM0_XDRXIQIMBALANCE 0xc2c + +#define ROFDM0_RXDETECTOR1 0xc30 +#define ROFDM0_RXDETECTOR2 0xc34 +#define ROFDM0_RXDETECTOR3 0xc38 +#define ROFDM0_RXDETECTOR4 0xc3c + +#define ROFDM0_RXDSP 0xc40 +#define ROFDM0_CFOANDDAGC 0xc44 +#define ROFDM0_CCADROPTHRESHOLD 0xc48 +#define ROFDM0_ECCATHRESHOLD 0xc4c + +#define ROFDM0_XAAGCCORE1 0xc50 +#define ROFDM0_XAAGCCORE2 0xc54 +#define ROFDM0_XBAGCCORE1 0xc58 +#define ROFDM0_XBAGCCORE2 0xc5c +#define ROFDM0_XCAGCCORE1 0xc60 +#define ROFDM0_XCAGCCORE2 0xc64 +#define ROFDM0_XDAGCCORE1 0xc68 +#define ROFDM0_XDAGCCORE2 0xc6c + +#define ROFDM0_AGCPARAMETER1 0xc70 +#define ROFDM0_AGCPARAMETER2 0xc74 +#define ROFDM0_AGCRSSITABLE 0xc78 +#define ROFDM0_HTSTFAGC 0xc7c + +#define ROFDM0_XATXIQIMBALANCE 0xc80 +#define ROFDM0_XATXAFE 0xc84 +#define ROFDM0_XBTXIQIMBALANCE 0xc88 +#define ROFDM0_XBTXAFE 0xc8c +#define ROFDM0_XCTXIQIMBALANCE 0xc90 +#define ROFDM0_XCTXAFE 0xc94 +#define ROFDM0_XDTXIQIMBALANCE 0xc98 +#define ROFDM0_XDTXAFE 0xc9c + +#define ROFDM0_RXIQEXTANTA 0xca0 + +#define ROFDM0_RXHPPARAMETER 0xce0 +#define ROFDM0_TXPSEUDONOISEWGT 0xce4 +#define ROFDM0_FRAMESYNC 0xcf0 +#define ROFDM0_DFSREPORT 0xcf4 +#define ROFDM0_TXCOEFF1 0xca4 +#define ROFDM0_TXCOEFF2 0xca8 +#define ROFDM0_TXCOEFF3 0xcac +#define ROFDM0_TXCOEFF4 0xcb0 +#define ROFDM0_TXCOEFF5 0xcb4 +#define ROFDM0_TXCOEFF6 0xcb8 + +#define ROFDM1_LSTF 0xd00 +#define ROFDM1_TRXPATHENABLE 0xd04 + +#define ROFDM1_CF0 0xd08 +#define ROFDM1_CSI1 0xd10 +#define ROFDM1_SBD 0xd14 +#define ROFDM1_CSI2 0xd18 +#define ROFDM1_CFOTRACKING 0xd2c +#define ROFDM1_TRXMESAURE1 0xd34 +#define ROFDM1_INTFDET 0xd3c +#define ROFDM1_PSEUDONOISESTATEAB 0xd50 +#define ROFDM1_PSEUDONOISESTATECD 0xd54 +#define ROFDM1_RXPSEUDONOISEWGT 0xd58 + +#define ROFDM_PHYCOUNTER1 0xda0 +#define ROFDM_PHYCOUNTER2 0xda4 +#define ROFDM_PHYCOUNTER3 0xda8 + +#define ROFDM_SHORTCFOAB 0xdac +#define ROFDM_SHORTCFOCD 0xdb0 +#define ROFDM_LONGCFOAB 0xdb4 +#define ROFDM_LONGCFOCD 0xdb8 +#define ROFDM_TAILCF0AB 0xdbc +#define ROFDM_TAILCF0CD 0xdc0 +#define ROFDM_PWMEASURE1 0xdc4 +#define ROFDM_PWMEASURE2 0xdc8 +#define ROFDM_BWREPORT 0xdcc +#define ROFDM_AGCREPORT 0xdd0 +#define ROFDM_RXSNR 0xdd4 +#define ROFDM_RXEVMCSI 0xdd8 +#define ROFDM_SIGREPORT 0xddc + +#define RTXAGC_A_RATE18_06 0xe00 +#define RTXAGC_A_RATE54_24 0xe04 +#define RTXAGC_A_CCK1_MCS32 0xe08 +#define RTXAGC_A_MCS03_MCS00 0xe10 +#define RTXAGC_A_MCS07_MCS04 0xe14 +#define RTXAGC_A_MCS11_MCS08 0xe18 +#define RTXAGC_A_MCS15_MCS12 0xe1c + +#define RTXAGC_B_RATE18_06 0x830 +#define RTXAGC_B_RATE54_24 0x834 +#define RTXAGC_B_CCK1_55_MCS32 0x838 +#define RTXAGC_B_MCS03_MCS00 0x83c +#define RTXAGC_B_MCS07_MCS04 0x848 +#define RTXAGC_B_MCS11_MCS08 0x84c +#define RTXAGC_B_MCS15_MCS12 0x868 +#define RTXAGC_B_CCK11_A_CCK2_11 0x86c + +#define RZEBRA1_HSSIENABLE 0x0 +#define RZEBRA1_TRXENABLE1 0x1 +#define RZEBRA1_TRXENABLE2 0x2 +#define RZEBRA1_AGC 0x4 +#define RZEBRA1_CHARGEPUMP 0x5 +#define RZEBRA1_CHANNEL 0x7 + +#define RZEBRA1_TXGAIN 0x8 +#define RZEBRA1_TXLPF 0x9 +#define RZEBRA1_RXLPF 0xb +#define RZEBRA1_RXHPFCORNER 0xc + +#define RGLOBALCTRL 0 +#define RRTL8256_TXLPF 19 +#define RRTL8256_RXLPF 11 +#define RRTL8258_TXLPF 0x11 +#define RRTL8258_RXLPF 0x13 +#define RRTL8258_RSSILPF 0xa + +#define RF_AC 0x00 + +#define RF_IQADJ_G1 0x01 +#define RF_IQADJ_G2 0x02 +#define RF_POW_TRSW 0x05 + +#define RF_GAIN_RX 0x06 +#define RF_GAIN_TX 0x07 + +#define RF_TXM_IDAC 0x08 +#define RF_BS_IQGEN 0x0F + +#define RF_MODE1 0x10 +#define RF_MODE2 0x11 + +#define RF_RX_AGC_HP 0x12 +#define RF_TX_AGC 0x13 +#define RF_BIAS 0x14 +#define RF_IPA 0x15 +#define RF_POW_ABILITY 0x17 +#define RF_MODE_AG 0x18 +#define RRFCHANNEL 0x18 +#define RF_CHNLBW 0x18 +#define RF_TOP 0x19 + +#define RF_RX_G1 0x1A +#define RF_RX_G2 0x1B + +#define RF_RX_BB2 0x1C +#define RF_RX_BB1 0x1D + +#define RF_RCK1 0x1E +#define RF_RCK2 0x1F + +#define RF_TX_G1 0x20 +#define RF_TX_G2 0x21 +#define RF_TX_G3 0x22 + +#define RF_TX_BB1 0x23 +#define RF_T_METER 0x24 + +#define RF_SYN_G1 0x25 +#define RF_SYN_G2 0x26 +#define RF_SYN_G3 0x27 +#define RF_SYN_G4 0x28 +#define RF_SYN_G5 0x29 +#define RF_SYN_G6 0x2A +#define RF_SYN_G7 0x2B +#define RF_SYN_G8 0x2C + +#define RF_RCK_OS 0x30 +#define RF_TXPA_G1 0x31 +#define RF_TXPA_G2 0x32 +#define RF_TXPA_G3 0x33 + +#define BBBRESETB 0x100 +#define BGLOBALRESETB 0x200 +#define BOFDMTXSTART 0x4 +#define BCCKTXSTART 0x8 +#define BCRC32DEBUG 0x100 +#define BPMACLOOPBACK 0x10 +#define BTXLSIG 0xffffff +#define BOFDMTXRATE 0xf +#define BOFDMTXRESERVED 0x10 +#define BOFDMTXLENGTH 0x1ffe0 +#define BOFDMTXPARITY 0x20000 +#define BTXHTSIG1 0xffffff +#define BTXHTMCSRATE 0x7f +#define BTXHTBW 0x80 +#define BTXHTLENGTH 0xffff00 +#define BTXHTSIG2 0xffffff +#define BTXHTSMOOTHING 0x1 +#define BTXHTSOUNDING 0x2 +#define BTXHTRESERVED 0x4 +#define BTXHTAGGREATION 0x8 +#define BTXHTSTBC 0x30 +#define BTXHTADVANCECODING 0x40 +#define BTXHTSHORTGI 0x80 +#define BTXHTNUMBERHT_LTF 0x300 +#define BTXHTCRC8 0x3fc00 +#define BCOUNTERRESET 0x10000 +#define BNUMOFOFDMTX 0xffff +#define BNUMOFCCKTX 0xffff0000 +#define BTXIDLEINTERVAL 0xffff +#define BOFDMSERVICE 0xffff0000 +#define BTXMACHEADER 0xffffffff +#define BTXDATAINIT 0xff +#define BTXHTMODE 0x100 +#define BTXDATATYPE 0x30000 +#define BTXRANDOMSEED 0xffffffff +#define BCCKTXPREAMBLE 0x1 +#define BCCKTXSFD 0xffff0000 +#define BCCKTXSIG 0xff +#define BCCKTXSERVICE 0xff00 +#define BCCKLENGTHEXT 0x8000 +#define BCCKTXLENGHT 0xffff0000 +#define BCCKTXCRC16 0xffff +#define BCCKTXSTATUS 0x1 +#define BOFDMTXSTATUS 0x2 +#define IS_BB_REG_OFFSET_92S(_Offset) \ + ((_Offset >= 0x800) && (_Offset <= 0xfff)) + +#define BRFMOD 0x1 +#define BJAPANMODE 0x2 +#define BCCKTXSC 0x30 +#define BCCKEN 0x1000000 +#define BOFDMEN 0x2000000 + +#define BOFDMRXADCPHASE 0x10000 +#define BOFDMTXDACPHASE 0x40000 +#define BXATXAGC 0x3f + +#define BXBTXAGC 0xf00 +#define BXCTXAGC 0xf000 +#define BXDTXAGC 0xf0000 + +#define BPASTART 0xf0000000 +#define BTRSTART 0x00f00000 +#define BRFSTART 0x0000f000 +#define BBBSTART 0x000000f0 +#define BBBCCKSTART 0x0000000f +#define BPAEND 0xf +#define BTREND 0x0f000000 +#define BRFEND 0x000f0000 +#define BCCAMASK 0x000000f0 +#define BR2RCCAMASK 0x00000f00 +#define BHSSI_R2TDELAY 0xf8000000 +#define BHSSI_T2RDELAY 0xf80000 +#define BCONTXHSSI 0x400 +#define BIGFROMCCK 0x200 +#define BAGCADDRESS 0x3f +#define BRXHPTX 0x7000 +#define BRXHP2RX 0x38000 +#define BRXHPCCKINI 0xc0000 +#define BAGCTXCODE 0xc00000 +#define BAGCRXCODE 0x300000 + +#define B3WIREDATALENGTH 0x800 +#define B3WIREADDREAALENGTH 0x400 + +#define B3WIRERFPOWERDOWN 0x1 +#define B5GPAPEPOLARITY 0x40000000 +#define B2GPAPEPOLARITY 0x80000000 +#define BRFSW_TXDEFAULTANT 0x3 +#define BRFSW_TXOPTIONANT 0x30 +#define BRFSW_RXDEFAULTANT 0x300 +#define BRFSW_RXOPTIONANT 0x3000 +#define BRFSI_3WIREDATA 0x1 +#define BRFSI_3WIRECLOCK 0x2 +#define BRFSI_3WIRELOAD 0x4 +#define BRFSI_3WIRERW 0x8 +#define BRFSI_3WIRE 0xf + +#define BRFSI_RFENV 0x10 + +#define BRFSI_TRSW 0x20 +#define BRFSI_TRSWB 0x40 +#define BRFSI_ANTSW 0x100 +#define BRFSI_ANTSWB 0x200 +#define BRFSI_PAPE 0x400 +#define BRFSI_PAPE5G 0x800 +#define BBANDSELECT 0x1 +#define BHTSIG2_GI 0x80 +#define BHTSIG2_SMOOTHING 0x01 +#define BHTSIG2_SOUNDING 0x02 +#define BHTSIG2_AGGREATON 0x08 +#define BHTSIG2_STBC 0x30 +#define BHTSIG2_ADVCODING 0x40 +#define BHTSIG2_NUMOFHTLTF 0x300 +#define BHTSIG2_CRC8 0x3fc +#define BHTSIG1_MCS 0x7f +#define BHTSIG1_BANDWIDTH 0x80 +#define BHTSIG1_HTLENGTH 0xffff +#define BLSIG_RATE 0xf +#define BLSIG_RESERVED 0x10 +#define BLSIG_LENGTH 0x1fffe +#define BLSIG_PARITY 0x20 +#define BCCKRXPHASE 0x4 + +#define BLSSIREADADDRESS 0x7f800000 +#define BLSSIREADEDGE 0x80000000 + +#define BLSSIREADBACKDATA 0xfffff + +#define BLSSIREADOKFLAG 0x1000 +#define BCCKSAMPLERATE 0x8 +#define BREGULATOR0STANDBY 0x1 +#define BREGULATORPLLSTANDBY 0x2 +#define BREGULATOR1STANDBY 0x4 +#define BPLLPOWERUP 0x8 +#define BDPLLPOWERUP 0x10 +#define BDA10POWERUP 0x20 +#define BAD7POWERUP 0x200 +#define BDA6POWERUP 0x2000 +#define BXTALPOWERUP 0x4000 +#define B40MDCLKPOWERUP 0x8000 +#define BDA6DEBUGMODE 0x20000 +#define BDA6SWING 0x380000 + +#define BADCLKPHASE 0x4000000 +#define B80MCLKDELAY 0x18000000 +#define BAFEWATCHDOGENABLE 0x20000000 + +#define BXTALCAP01 0xc0000000 +#define BXTALCAP23 0x3 +#define BXTALCAP92X 0x0f000000 +#define BXTALCAP 0x0f000000 + +#define BINTDIFCLKENABLE 0x400 +#define BEXTSIGCLKENABLE 0x800 +#define BBANDGAP_MBIAS_POWERUP 0x10000 +#define BAD11SH_GAIN 0xc0000 +#define BAD11NPUT_RANGE 0x700000 +#define BAD110P_CURRENT 0x3800000 +#define BLPATH_LOOPBACK 0x4000000 +#define BQPATH_LOOPBACK 0x8000000 +#define BAFE_LOOPBACK 0x10000000 +#define BDA10_SWING 0x7e0 +#define BDA10_REVERSE 0x800 +#define BDA_CLK_SOURCE 0x1000 +#define BDA7INPUT_RANGE 0x6000 +#define BDA7_GAIN 0x38000 +#define BDA7OUTPUT_CM_MODE 0x40000 +#define BDA7INPUT_CM_MODE 0x380000 +#define BDA7CURRENT 0xc00000 +#define BREGULATOR_ADJUST 0x7000000 +#define BAD11POWERUP_ATTX 0x1 +#define BDA10PS_ATTX 0x10 +#define BAD11POWERUP_ATRX 0x100 +#define BDA10PS_ATRX 0x1000 +#define BCCKRX_AGC_FORMAT 0x200 +#define BPSDFFT_SAMPLE_POINT 0xc000 +#define BPSD_AVERAGE_NUM 0x3000 +#define BIQPATH_CONTROL 0xc00 +#define BPSD_FREQ 0x3ff +#define BPSD_ANTENNA_PATH 0x30 +#define BPSD_IQ_SWITCH 0x40 +#define BPSD_RX_TRIGGER 0x400000 +#define BPSD_TX_TRIGGER 0x80000000 +#define BPSD_SINE_TONE_SCALE 0x7f000000 +#define BPSD_REPORT 0xffff + +#define BOFDM_TXSC 0x30000000 +#define BCCK_TXON 0x1 +#define BOFDM_TXON 0x2 +#define BDEBUG_PAGE 0xfff +#define BDEBUG_ITEM 0xff +#define BANTL 0x10 +#define BANT_NONHT 0x100 +#define BANT_HT1 0x1000 +#define BANT_HT2 0x10000 +#define BANT_HT1S1 0x100000 +#define BANT_NONHTS1 0x1000000 + +#define BCCK_BBMODE 0x3 +#define BCCK_TXPOWERSAVING 0x80 +#define BCCK_RXPOWERSAVING 0x40 + +#define BCCK_SIDEBAND 0x10 + +#define BCCK_SCRAMBLE 0x8 +#define BCCK_ANTDIVERSITY 0x8000 +#define BCCK_CARRIER_RECOVERY 0x4000 +#define BCCK_TXRATE 0x3000 +#define BCCK_DCCANCEL 0x0800 +#define BCCK_ISICANCEL 0x0400 +#define BCCK_MATCH_FILTER 0x0200 +#define BCCK_EQUALIZER 0x0100 +#define BCCK_PREAMBLE_DETECT 0x800000 +#define BCCK_FAST_FALSECCAi 0x400000 +#define BCCK_CH_ESTSTARTi 0x300000 +#define BCCK_CCA_COUNTi 0x080000 +#define BCCK_CS_LIM 0x070000 +#define BCCK_BIST_MODEi 0x80000000 +#define BCCK_CCAMASK 0x40000000 +#define BCCK_TX_DAC_PHASE 0x4 +#define BCCK_RX_ADC_PHASE 0x20000000 +#define BCCKR_CP_MODE 0x0100 +#define BCCK_TXDC_OFFSET 0xf0 +#define BCCK_RXDC_OFFSET 0xf +#define BCCK_CCA_MODE 0xc000 +#define BCCK_FALSECS_LIM 0x3f00 +#define BCCK_CS_RATIO 0xc00000 +#define BCCK_CORGBIT_SEL 0x300000 +#define BCCK_PD_LIM 0x0f0000 +#define BCCK_NEWCCA 0x80000000 +#define BCCK_RXHP_OF_IG 0x8000 +#define BCCK_RXIG 0x7f00 +#define BCCK_LNA_POLARITY 0x800000 +#define BCCK_RX1ST_BAIN 0x7f0000 +#define BCCK_RF_EXTEND 0x20000000 +#define BCCK_RXAGC_SATLEVEL 0x1f000000 +#define BCCK_RXAGC_SATCOUNT 0xe0 +#define bCCKRxRFSettle 0x1f +#define BCCK_FIXED_RXAGC 0x8000 +#define BCCK_ANTENNA_POLARITY 0x2000 +#define BCCK_TXFILTER_TYPE 0x0c00 +#define BCCK_RXAGC_REPORTTYPE 0x0300 +#define BCCK_RXDAGC_EN 0x80000000 +#define BCCK_RXDAGC_PERIOD 0x20000000 +#define BCCK_RXDAGC_SATLEVEL 0x1f000000 +#define BCCK_TIMING_RECOVERY 0x800000 +#define BCCK_TXC0 0x3f0000 +#define BCCK_TXC1 0x3f000000 +#define BCCK_TXC2 0x3f +#define BCCK_TXC3 0x3f00 +#define BCCK_TXC4 0x3f0000 +#define BCCK_TXC5 0x3f000000 +#define BCCK_TXC6 0x3f +#define BCCK_TXC7 0x3f00 +#define BCCK_DEBUGPORT 0xff0000 +#define BCCK_DAC_DEBUG 0x0f000000 +#define BCCK_FALSEALARM_ENABLE 0x8000 +#define BCCK_FALSEALARM_READ 0x4000 +#define BCCK_TRSSI 0x7f +#define BCCK_RXAGC_REPORT 0xfe +#define BCCK_RXREPORT_ANTSEL 0x80000000 +#define BCCK_RXREPORT_MFOFF 0x40000000 +#define BCCK_RXREPORT_SQLOSS 0x20000000 +#define BCCK_RXREPORT_PKTLOSS 0x10000000 +#define BCCK_RXREPORT_LOCKEDBIT 0x08000000 +#define BCCK_RXREPORT_RATEERROR 0x04000000 +#define BCCK_RXREPORT_RXRATE 0x03000000 +#define BCCK_RXFA_COUNTER_LOWER 0xff +#define BCCK_RXFA_COUNTER_UPPER 0xff000000 +#define BCCK_RXHPAGC_START 0xe000 +#define BCCK_RXHPAGC_FINAL 0x1c00 +#define BCCK_RXFALSEALARM_ENABLE 0x8000 +#define BCCK_FACOUNTER_FREEZE 0x4000 +#define BCCK_TXPATH_SEL 0x10000000 +#define BCCK_DEFAULT_RXPATH 0xc000000 +#define BCCK_OPTION_RXPATH 0x3000000 + +#define BNUM_OFSTF 0x3 +#define BSHIFT_L 0xc0 +#define BGI_TH 0xc +#define BRXPATH_A 0x1 +#define BRXPATH_B 0x2 +#define BRXPATH_C 0x4 +#define BRXPATH_D 0x8 +#define BTXPATH_A 0x1 +#define BTXPATH_B 0x2 +#define BTXPATH_C 0x4 +#define BTXPATH_D 0x8 +#define BTRSSI_FREQ 0x200 +#define BADC_BACKOFF 0x3000 +#define BDFIR_BACKOFF 0xc000 +#define BTRSSI_LATCH_PHASE 0x10000 +#define BRX_LDC_OFFSET 0xff +#define BRX_QDC_OFFSET 0xff00 +#define BRX_DFIR_MODE 0x1800000 +#define BRX_DCNF_TYPE 0xe000000 +#define BRXIQIMB_A 0x3ff +#define BRXIQIMB_B 0xfc00 +#define BRXIQIMB_C 0x3f0000 +#define BRXIQIMB_D 0xffc00000 +#define BDC_DC_NOTCH 0x60000 +#define BRXNB_NOTCH 0x1f000000 +#define BPD_TH 0xf +#define BPD_TH_OPT2 0xc000 +#define BPWED_TH 0x700 +#define BIFMF_WIN_L 0x800 +#define BPD_OPTION 0x1000 +#define BMF_WIN_L 0xe000 +#define BBW_SEARCH_L 0x30000 +#define BWIN_ENH_L 0xc0000 +#define BBW_TH 0x700000 +#define BED_TH2 0x3800000 +#define BBW_OPTION 0x4000000 +#define BRADIO_TH 0x18000000 +#define BWINDOW_L 0xe0000000 +#define BSBD_OPTION 0x1 +#define BFRAME_TH 0x1c +#define BFS_OPTION 0x60 +#define BDC_SLOPE_CHECK 0x80 +#define BFGUARD_COUNTER_DC_L 0xe00 +#define BFRAME_WEIGHT_SHORT 0x7000 +#define BSUB_TUNE 0xe00000 +#define BFRAME_DC_LENGTH 0xe000000 +#define BSBD_START_OFFSET 0x30000000 +#define BFRAME_TH_2 0x7 +#define BFRAME_GI2_TH 0x38 +#define BGI2_SYNC_EN 0x40 +#define BSARCH_SHORT_EARLY 0x300 +#define BSARCH_SHORT_LATE 0xc00 +#define BSARCH_GI2_LATE 0x70000 +#define BCFOANTSUM 0x1 +#define BCFOACC 0x2 +#define BCFOSTARTOFFSET 0xc +#define BCFOLOOPBACK 0x70 +#define BCFOSUMWEIGHT 0x80 +#define BDAGCENABLE 0x10000 +#define BTXIQIMB_A 0x3ff +#define BTXIQIMB_b 0xfc00 +#define BTXIQIMB_C 0x3f0000 +#define BTXIQIMB_D 0xffc00000 +#define BTXIDCOFFSET 0xff +#define BTXIQDCOFFSET 0xff00 +#define BTXDFIRMODE 0x10000 +#define BTXPESUDO_NOISEON 0x4000000 +#define BTXPESUDO_NOISE_A 0xff +#define BTXPESUDO_NOISE_B 0xff00 +#define BTXPESUDO_NOISE_C 0xff0000 +#define BTXPESUDO_NOISE_D 0xff000000 +#define BCCA_DROPOPTION 0x20000 +#define BCCA_DROPTHRES 0xfff00000 +#define BEDCCA_H 0xf +#define BEDCCA_L 0xf0 +#define BLAMBDA_ED 0x300 +#define BRX_INITIALGAIN 0x7f +#define BRX_ANTDIV_EN 0x80 +#define BRX_AGC_ADDRESS_FOR_LNA 0x7f00 +#define BRX_HIGHPOWER_FLOW 0x8000 +#define BRX_AGC_FREEZE_THRES 0xc0000 +#define BRX_FREEZESTEP_AGC1 0x300000 +#define BRX_FREEZESTEP_AGC2 0xc00000 +#define BRX_FREEZESTEP_AGC3 0x3000000 +#define BRX_FREEZESTEP_AGC0 0xc000000 +#define BRXRSSI_CMP_EN 0x10000000 +#define BRXQUICK_AGCEN 0x20000000 +#define BRXAGC_FREEZE_THRES_MODE 0x40000000 +#define BRX_OVERFLOW_CHECKTYPE 0x80000000 +#define BRX_AGCSHIFT 0x7f +#define BTRSW_TRI_ONLY 0x80 +#define BPOWER_THRES 0x300 +#define BRXAGC_EN 0x1 +#define BRXAGC_TOGETHER_EN 0x2 +#define BRXAGC_MIN 0x4 +#define BRXHP_INI 0x7 +#define BRXHP_TRLNA 0x70 +#define BRXHP_RSSI 0x700 +#define BRXHP_BBP1 0x7000 +#define BRXHP_BBP2 0x70000 +#define BRXHP_BBP3 0x700000 +#define BRSSI_H 0x7f0000 +#define BRSSI_GEN 0x7f000000 +#define BRXSETTLE_TRSW 0x7 +#define BRXSETTLE_LNA 0x38 +#define BRXSETTLE_RSSI 0x1c0 +#define BRXSETTLE_BBP 0xe00 +#define BRXSETTLE_RXHP 0x7000 +#define BRXSETTLE_ANTSW_RSSI 0x38000 +#define BRXSETTLE_ANTSW 0xc0000 +#define BRXPROCESS_TIME_DAGC 0x300000 +#define BRXSETTLE_HSSI 0x400000 +#define BRXPROCESS_TIME_BBPPW 0x800000 +#define BRXANTENNA_POWER_SHIFT 0x3000000 +#define BRSSI_TABLE_SELECT 0xc000000 +#define BRXHP_FINAL 0x7000000 +#define BRXHPSETTLE_BBP 0x7 +#define BRXHTSETTLE_HSSI 0x8 +#define BRXHTSETTLE_RXHP 0x70 +#define BRXHTSETTLE_BBPPW 0x80 +#define BRXHTSETTLE_IDLE 0x300 +#define BRXHTSETTLE_RESERVED 0x1c00 +#define BRXHT_RXHP_EN 0x8000 +#define BRXAGC_FREEZE_THRES 0x30000 +#define BRXAGC_TOGETHEREN 0x40000 +#define BRXHTAGC_MIN 0x80000 +#define BRXHTAGC_EN 0x100000 +#define BRXHTDAGC_EN 0x200000 +#define BRXHT_RXHP_BBP 0x1c00000 +#define BRXHT_RXHP_FINAL 0xe0000000 +#define BRXPW_RADIO_TH 0x3 +#define BRXPW_RADIO_EN 0x4 +#define BRXMF_HOLD 0x3800 +#define BRXPD_DELAY_TH1 0x38 +#define BRXPD_DELAY_TH2 0x1c0 +#define BRXPD_DC_COUNT_MAX 0x600 +#define BRXPD_DELAY_TH 0x8000 +#define BRXPROCESS_DELAY 0xf0000 +#define BRXSEARCHRANGE_GI2_EARLY 0x700000 +#define BRXFRAME_FUARD_COUNTER_L 0x3800000 +#define BRXSGI_GUARD_L 0xc000000 +#define BRXSGI_SEARCH_L 0x30000000 +#define BRXSGI_TH 0xc0000000 +#define BDFSCNT0 0xff +#define BDFSCNT1 0xff00 +#define BDFSFLAG 0xf0000 +#define BMF_WEIGHT_SUM 0x300000 +#define BMINIDX_TH 0x7f000000 +#define BDAFORMAT 0x40000 +#define BTXCH_EMU_ENABLE 0x01000000 +#define BTRSW_ISOLATION_A 0x7f +#define BTRSW_ISOLATION_B 0x7f00 +#define BTRSW_ISOLATION_C 0x7f0000 +#define BTRSW_ISOLATION_D 0x7f000000 +#define BEXT_LNA_GAIN 0x7c00 + +#define BSTBC_EN 0x4 +#define BANTENNA_MAPPING 0x10 +#define BNSS 0x20 +#define BCFO_ANTSUM_ID 0x200 +#define BPHY_COUNTER_RESET 0x8000000 +#define BCFO_REPORT_GET 0x4000000 +#define BOFDM_CONTINUE_TX 0x10000000 +#define BOFDM_SINGLE_CARRIER 0x20000000 +#define BOFDM_SINGLE_TONE 0x40000000 +#define BHT_DETECT 0x100 +#define BCFOEN 0x10000 +#define BCFOVALUE 0xfff00000 +#define BSIGTONE_RE 0x3f +#define BSIGTONE_IM 0x7f00 +#define BCOUNTER_CCA 0xffff +#define BCOUNTER_PARITYFAIL 0xffff0000 +#define BCOUNTER_RATEILLEGAL 0xffff +#define BCOUNTER_CRC8FAIL 0xffff0000 +#define BCOUNTER_MCSNOSUPPORT 0xffff +#define BCOUNTER_FASTSYNC 0xffff +#define BSHORTCFO 0xfff +#define BSHORTCFOT_LENGTH 12 +#define BSHORTCFOF_LENGTH 11 +#define BLONGCFO 0x7ff +#define BLONGCFOT_LENGTH 11 +#define BLONGCFOF_LENGTH 11 +#define BTAILCFO 0x1fff +#define BTAILCFOT_LENGTH 13 +#define BTAILCFOF_LENGTH 12 +#define BNOISE_EN_PWDB 0xffff +#define BCC_POWER_DB 0xffff0000 +#define BMOISE_PWDB 0xffff +#define BPOWERMEAST_LENGTH 10 +#define BPOWERMEASF_LENGTH 3 +#define BRX_HT_BW 0x1 +#define BRXSC 0x6 +#define BRX_HT 0x8 +#define BNB_INTF_DET_ON 0x1 +#define BINTF_WIN_LEN_CFG 0x30 +#define BNB_INTF_TH_CFG 0x1c0 +#define BRFGAIN 0x3f +#define BTABLESEL 0x40 +#define BTRSW 0x80 +#define BRXSNR_A 0xff +#define BRXSNR_B 0xff00 +#define BRXSNR_C 0xff0000 +#define BRXSNR_D 0xff000000 +#define BSNR_EVMT_LENGTH 8 +#define BSNR_EVMF_LENGTH 1 +#define BCSI1ST 0xff +#define BCSI2ND 0xff00 +#define BRXEVM1ST 0xff0000 +#define BRXEVM2ND 0xff000000 +#define BSIGEVM 0xff +#define BPWDB 0xff00 +#define BSGIEN 0x10000 + +#define BSFACTOR_QMA1 0xf +#define BSFACTOR_QMA2 0xf0 +#define BSFACTOR_QMA3 0xf00 +#define BSFACTOR_QMA4 0xf000 +#define BSFACTOR_QMA5 0xf0000 +#define BSFACTOR_QMA6 0xf0000 +#define BSFACTOR_QMA7 0xf00000 +#define BSFACTOR_QMA8 0xf000000 +#define BSFACTOR_QMA9 0xf0000000 +#define BCSI_SCHEME 0x100000 + +#define BNOISE_LVL_TOP_SET 0x3 +#define BCHSMOOTH 0x4 +#define BCHSMOOTH_CFG1 0x38 +#define BCHSMOOTH_CFG2 0x1c0 +#define BCHSMOOTH_CFG3 0xe00 +#define BCHSMOOTH_CFG4 0x7000 +#define BMRCMODE 0x800000 +#define BTHEVMCFG 0x7000000 + +#define BLOOP_FIT_TYPE 0x1 +#define BUPD_CFO 0x40 +#define BUPD_CFO_OFFDATA 0x80 +#define BADV_UPD_CFO 0x100 +#define BADV_TIME_CTRL 0x800 +#define BUPD_CLKO 0x1000 +#define BFC 0x6000 +#define BTRACKING_MODE 0x8000 +#define BPHCMP_ENABLE 0x10000 +#define BUPD_CLKO_LTF 0x20000 +#define BCOM_CH_CFO 0x40000 +#define BCSI_ESTI_MODE 0x80000 +#define BADV_UPD_EQZ 0x100000 +#define BUCHCFG 0x7000000 +#define BUPDEQZ 0x8000000 + +#define BRX_PESUDO_NOISE_ON 0x20000000 +#define BRX_PESUDO_NOISE_A 0xff +#define BRX_PESUDO_NOISE_B 0xff00 +#define BRX_PESUDO_NOISE_C 0xff0000 +#define BRX_PESUDO_NOISE_D 0xff000000 +#define BRX_PESUDO_NOISESTATE_A 0xffff +#define BRX_PESUDO_NOISESTATE_B 0xffff0000 +#define BRX_PESUDO_NOISESTATE_C 0xffff +#define BRX_PESUDO_NOISESTATE_D 0xffff0000 + +#define BZEBRA1_HSSIENABLE 0x8 +#define BZEBRA1_TRXCONTROL 0xc00 +#define BZEBRA1_TRXGAINSETTING 0x07f +#define BZEBRA1_RXCOUNTER 0xc00 +#define BZEBRA1_TXCHANGEPUMP 0x38 +#define BZEBRA1_RXCHANGEPUMP 0x7 +#define BZEBRA1_CHANNEL_NUM 0xf80 +#define BZEBRA1_TXLPFBW 0x400 +#define BZEBRA1_RXLPFBW 0x600 + +#define BRTL8256REG_MODE_CTRL1 0x100 +#define BRTL8256REG_MODE_CTRL0 0x40 +#define BRTL8256REG_TXLPFBW 0x18 +#define BRTL8256REG_RXLPFBW 0x600 + +#define BRTL8258_TXLPFBW 0xc +#define BRTL8258_RXLPFBW 0xc00 +#define BRTL8258_RSSILPFBW 0xc0 + +#define BBYTE0 0x1 +#define BBYTE1 0x2 +#define BBYTE2 0x4 +#define BBYTE3 0x8 +#define BWORD0 0x3 +#define BWORD1 0xc +#define BWORD 0xf + +#define MASKBYTE0 0xff +#define MASKBYTE1 0xff00 +#define MASKBYTE2 0xff0000 +#define MASKBYTE3 0xff000000 +#define MASKHWORD 0xffff0000 +#define MASKLWORD 0x0000ffff +#define MASKDWORD 0xffffffff +#define MASK12BITS 0xfff +#define MASKH4BITS 0xf0000000 +#define MASKOFDM_D 0xffc00000 +#define MASKCCK 0x3f3f3f3f + +#define MASK4BITS 0x0f +#define MASK20BITS 0xfffff +#define RFREG_OFFSET_MASK 0xfffff + +#define BENABLE 0x1 +#define BDISABLE 0x0 + +#define LEFT_ANTENNA 0x0 +#define RIGHT_ANTENNA 0x1 + +#define TCHECK_TXSTATUS 500 +#define TUPDATE_RXCOUNTER 100 + +/* 2 EFUSE_TEST (For RTL8723 partially) */ +#define EFUSE_SEL(x) (((x) & 0x3) << 8) +#define EFUSE_SEL_MASK 0x300 +#define EFUSE_WIFI_SEL_0 0x0 + +/* Enable GPIO[9] as WiFi HW PDn source*/ +#define WL_HWPDN_EN BIT(0) +/* WiFi HW PDn polarity control*/ +#define WL_HWPDN_SL BIT(1) + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.c b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.c new file mode 100644 index 00000000000..50dd2fb2c93 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.c @@ -0,0 +1,505 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "rf.h" +#include "dm.h" + +void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, u8 bandwidth) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + switch (bandwidth) { + case HT_CHANNEL_WIDTH_20: + rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & + 0xfffff3ff) | 0x0400); + rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[0]); + break; + case HT_CHANNEL_WIDTH_20_40: + rtlphy->rfreg_chnlval[0] = ((rtlphy->rfreg_chnlval[0] & + 0xfffff3ff)); + rtl_set_rfreg(hw, RF90_PATH_A, RF_CHNLBW, RFREG_OFFSET_MASK, + rtlphy->rfreg_chnlval[0]); + break; + default: + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "unknown bandwidth: %#X\n", bandwidth); + break; + } +} + +void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u32 tx_agc[2] = {0, 0}, tmpval; + bool turbo_scanoff = false; + u8 idx1, idx2; + u8 *ptr; + + if (rtlefuse->eeprom_regulatory != 0) + turbo_scanoff = true; + + if (mac->act_scanning == true) { + tx_agc[RF90_PATH_A] = 0x3f3f3f3f; + tx_agc[RF90_PATH_B] = 0x3f3f3f3f; + + if (turbo_scanoff) { + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + tx_agc[idx1] = ppowerlevel[idx1] | + (ppowerlevel[idx1] << 8) | + (ppowerlevel[idx1] << 16) | + (ppowerlevel[idx1] << 24); + } + } + } else { + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + tx_agc[idx1] = ppowerlevel[idx1] | + (ppowerlevel[idx1] << 8) | + (ppowerlevel[idx1] << 16) | + (ppowerlevel[idx1] << 24); + } + + if (rtlefuse->eeprom_regulatory == 0) { + tmpval = (rtlphy->mcs_offset[0][6]) + + (rtlphy->mcs_offset[0][7] << 8); + tx_agc[RF90_PATH_A] += tmpval; + + tmpval = (rtlphy->mcs_offset[0][14]) + + (rtlphy->mcs_offset[0][15] << 24); + tx_agc[RF90_PATH_B] += tmpval; + } + } + + for (idx1 = RF90_PATH_A; idx1 <= RF90_PATH_B; idx1++) { + ptr = (u8 *) (&(tx_agc[idx1])); + for (idx2 = 0; idx2 < 4; idx2++) { + if (*ptr > RF6052_MAX_TX_PWR) + *ptr = RF6052_MAX_TX_PWR; + ptr++; + } + } + + tmpval = tx_agc[RF90_PATH_A] & 0xff; + rtl_set_bbreg(hw, RTXAGC_A_CCK1_MCS32, MASKBYTE1, tmpval); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "CCK PWR 1M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_A_CCK1_MCS32); + + tmpval = tx_agc[RF90_PATH_A] >> 8; + + tmpval = tmpval & 0xff00ffff; + + rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, 0xffffff00, tmpval); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "CCK PWR 2~11M (rf-A) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK11_A_CCK2_11); + + tmpval = tx_agc[RF90_PATH_B] >> 24; + rtl_set_bbreg(hw, RTXAGC_B_CCK11_A_CCK2_11, MASKBYTE0, tmpval); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "CCK PWR 11M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK11_A_CCK2_11); + + tmpval = tx_agc[RF90_PATH_B] & 0x00ffffff; + rtl_set_bbreg(hw, RTXAGC_B_CCK1_55_MCS32, 0xffffff00, tmpval); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "CCK PWR 1~5.5M (rf-B) = 0x%x (reg 0x%x)\n", tmpval, + RTXAGC_B_CCK1_55_MCS32); +} + +static void rtl8723ae_phy_get_power_base(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel, + u32 *ofdmbase, u32 *mcsbase) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u32 powerBase0, powerBase1; + u8 legacy_pwrdiff, ht20_pwrdiff; + u8 i, powerlevel[2]; + + for (i = 0; i < 2; i++) { + powerlevel[i] = ppowerlevel[i]; + legacy_pwrdiff = rtlefuse->txpwr_legacyhtdiff[i][channel - 1]; + powerBase0 = powerlevel[i] + legacy_pwrdiff; + + powerBase0 = (powerBase0 << 24) | (powerBase0 << 16) | + (powerBase0 << 8) | powerBase0; + *(ofdmbase + i) = powerBase0; + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + " [OFDM power base index rf(%c) = 0x%x]\n", + ((i == 0) ? 'A' : 'B'), *(ofdmbase + i)); + } + + for (i = 0; i < 2; i++) { + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20) { + ht20_pwrdiff = rtlefuse->txpwr_ht20diff[i][channel - 1]; + powerlevel[i] += ht20_pwrdiff; + } + powerBase1 = powerlevel[i]; + powerBase1 = (powerBase1 << 24) | + (powerBase1 << 16) | (powerBase1 << 8) | powerBase1; + + *(mcsbase + i) = powerBase1; + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + " [MCS power base index rf(%c) = 0x%x]\n", + ((i == 0) ? 'A' : 'B'), *(mcsbase + i)); + } +} + +static void rtl8723ae_get_txpwr_val_by_reg(struct ieee80211_hw *hw, + u8 channel, u8 index, + u32 *powerBase0, + u32 *powerBase1, + u32 *p_outwriteval) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + u8 i, chnlgroup = 0, pwr_diff_limit[4]; + u32 writeVal, customer_limit, rf; + + for (rf = 0; rf < 2; rf++) { + switch (rtlefuse->eeprom_regulatory) { + case 0: + chnlgroup = 0; + + writeVal = rtlphy->mcs_offset[chnlgroup] + [index + (rf ? 8 : 0)] + + ((index < 2) ? powerBase0[rf] : + powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "RTK better performance, " + "writeVal(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + break; + case 1: + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + writeVal = ((index < 2) ? powerBase0[rf] : + powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Realtek regulatory, 40MHz, " + "writeVal(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + } else { + if (rtlphy->pwrgroup_cnt == 1) + chnlgroup = 0; + if (rtlphy->pwrgroup_cnt >= 3) { + if (channel <= 3) + chnlgroup = 0; + else if (channel >= 4 && channel <= 9) + chnlgroup = 1; + else if (channel > 9) + chnlgroup = 2; + if (rtlphy->current_chan_bw == + HT_CHANNEL_WIDTH_20) + chnlgroup++; + else + chnlgroup += 4; + } + + writeVal = rtlphy->mcs_offset[chnlgroup] + [index + (rf ? 8 : 0)] + ((index < 2) ? + powerBase0[rf] : + powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Realtek regulatory, 20MHz, writeVal(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + } + break; + case 2: + writeVal = + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Better regulatory, writeVal(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + break; + case 3: + chnlgroup = 0; + + if (rtlphy->current_chan_bw == HT_CHANNEL_WIDTH_20_40) { + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "customer's limit, 40MHz rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), + rtlefuse->pwrgroup_ht40[rf][channel-1]); + } else { + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "customer's limit, 20MHz rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), + rtlefuse->pwrgroup_ht20[rf][channel-1]); + } + for (i = 0; i < 4; i++) { + pwr_diff_limit[i] = + (u8) ((rtlphy->mcs_offset + [chnlgroup][index + (rf ? 8 : 0)] & + (0x7f << (i * 8))) >> (i * 8)); + + if (rtlphy->current_chan_bw == + HT_CHANNEL_WIDTH_20_40) { + if (pwr_diff_limit[i] > + rtlefuse-> + pwrgroup_ht40[rf][channel - 1]) + pwr_diff_limit[i] = + rtlefuse->pwrgroup_ht40[rf] + [channel - 1]; + } else { + if (pwr_diff_limit[i] > + rtlefuse-> + pwrgroup_ht20[rf][channel - 1]) + pwr_diff_limit[i] = + rtlefuse->pwrgroup_ht20[rf] + [channel - 1]; + } + } + + customer_limit = (pwr_diff_limit[3] << 24) | + (pwr_diff_limit[2] << 16) | + (pwr_diff_limit[1] << 8) | (pwr_diff_limit[0]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Customer's limit rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), customer_limit); + + writeVal = customer_limit + + ((index < 2) ? powerBase0[rf] : powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Customer, writeVal rf(%c)= 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + break; + default: + chnlgroup = 0; + writeVal = rtlphy->mcs_offset[chnlgroup][index + + (rf ? 8 : 0)] + ((index < 2) ? powerBase0[rf] : + powerBase1[rf]); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "RTK better performance, writeVal rf(%c) = 0x%x\n", + ((rf == 0) ? 'A' : 'B'), writeVal); + break; + } + + if (rtlpriv->dm.dynamic_txhighpower_lvl == TXHIGHPWRLEVEL_BT1) + writeVal = writeVal - 0x06060606; + else if (rtlpriv->dm.dynamic_txhighpower_lvl == + TXHIGHPWRLEVEL_BT2) + writeVal = writeVal - 0x0c0c0c0c; + *(p_outwriteval + rf) = writeVal; + } +} + +static void _rtl8723ae_write_ofdm_power_reg(struct ieee80211_hw *hw, + u8 index, u32 *pValue) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + u16 regoffset_a[6] = { + RTXAGC_A_RATE18_06, RTXAGC_A_RATE54_24, + RTXAGC_A_MCS03_MCS00, RTXAGC_A_MCS07_MCS04, + RTXAGC_A_MCS11_MCS08, RTXAGC_A_MCS15_MCS12 + }; + u16 regoffset_b[6] = { + RTXAGC_B_RATE18_06, RTXAGC_B_RATE54_24, + RTXAGC_B_MCS03_MCS00, RTXAGC_B_MCS07_MCS04, + RTXAGC_B_MCS11_MCS08, RTXAGC_B_MCS15_MCS12 + }; + u8 i, rf, pwr_val[4]; + u32 writeVal; + u16 regoffset; + + for (rf = 0; rf < 2; rf++) { + writeVal = pValue[rf]; + for (i = 0; i < 4; i++) { + pwr_val[i] = (u8) ((writeVal & (0x7f << + (i * 8))) >> (i * 8)); + + if (pwr_val[i] > RF6052_MAX_TX_PWR) + pwr_val[i] = RF6052_MAX_TX_PWR; + } + writeVal = (pwr_val[3] << 24) | (pwr_val[2] << 16) | + (pwr_val[1] << 8) | pwr_val[0]; + + if (rf == 0) + regoffset = regoffset_a[index]; + else + regoffset = regoffset_b[index]; + rtl_set_bbreg(hw, regoffset, MASKDWORD, writeVal); + + RTPRINT(rtlpriv, FPHY, PHY_TXPWR, + "Set 0x%x = %08x\n", regoffset, writeVal); + + if (((get_rf_type(rtlphy) == RF_2T2R) && + (regoffset == RTXAGC_A_MCS15_MCS12 || + regoffset == RTXAGC_B_MCS15_MCS12)) || + ((get_rf_type(rtlphy) != RF_2T2R) && + (regoffset == RTXAGC_A_MCS07_MCS04 || + regoffset == RTXAGC_B_MCS07_MCS04))) { + + writeVal = pwr_val[3]; + if (regoffset == RTXAGC_A_MCS15_MCS12 || + regoffset == RTXAGC_A_MCS07_MCS04) + regoffset = 0xc90; + if (regoffset == RTXAGC_B_MCS15_MCS12 || + regoffset == RTXAGC_B_MCS07_MCS04) + regoffset = 0xc98; + + for (i = 0; i < 3; i++) { + writeVal = (writeVal > 6) ? (writeVal - 6) : 0; + rtl_write_byte(rtlpriv, (u32) (regoffset + i), + (u8) writeVal); + } + } + } +} + +void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel) +{ + u32 writeVal[2], powerBase0[2], powerBase1[2]; + u8 index; + + rtl8723ae_phy_get_power_base(hw, ppowerlevel, + channel, &powerBase0[0], &powerBase1[0]); + + for (index = 0; index < 6; index++) { + rtl8723ae_get_txpwr_val_by_reg(hw, channel, index, + &powerBase0[0], + &powerBase1[0], + &writeVal[0]); + + _rtl8723ae_write_ofdm_power_reg(hw, index, &writeVal[0]); + } +} + +static bool _rtl8723ae_phy_rf6052_config_parafile(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u32 u4_regvalue = 0; + u8 rfpath; + bool rtstatus = true; + struct bb_reg_def *pphyreg; + + for (rfpath = 0; rfpath < rtlphy->num_total_rfpath; rfpath++) { + + pphyreg = &rtlphy->phyreg_def[rfpath]; + + switch (rfpath) { + case RF90_PATH_A: + case RF90_PATH_C: + u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV); + break; + case RF90_PATH_B: + case RF90_PATH_D: + u4_regvalue = rtl_get_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV << 16); + break; + } + + rtl_set_bbreg(hw, pphyreg->rfintfe, BRFSI_RFENV << 16, 0x1); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfintfo, BRFSI_RFENV, 0x1); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, + B3WIREADDREAALENGTH, 0x0); + udelay(1); + + rtl_set_bbreg(hw, pphyreg->rfhssi_para2, B3WIREDATALENGTH, 0x0); + udelay(1); + + switch (rfpath) { + case RF90_PATH_A: + rtstatus = rtl8723ae_phy_config_rf_with_headerfile(hw, + (enum radio_path)rfpath); + break; + case RF90_PATH_B: + rtstatus = rtl8723ae_phy_config_rf_with_headerfile(hw, + (enum radio_path)rfpath); + break; + case RF90_PATH_C: + break; + case RF90_PATH_D: + break; + } + switch (rfpath) { + case RF90_PATH_A: + case RF90_PATH_C: + rtl_set_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV, u4_regvalue); + break; + case RF90_PATH_B: + case RF90_PATH_D: + rtl_set_bbreg(hw, pphyreg->rfintfs, + BRFSI_RFENV << 16, u4_regvalue); + break; + } + if (rtstatus != true) { + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, + "Radio[%d] Fail!!", rfpath); + return false; + } + } + RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "<---\n"); + return rtstatus; +} + +bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + + if (rtlphy->rf_type == RF_1T1R) + rtlphy->num_total_rfpath = 1; + else + rtlphy->num_total_rfpath = 2; + + return _rtl8723ae_phy_rf6052_config_parafile(hw); +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h new file mode 100644 index 00000000000..d0f9dd79abe --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/rf.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_RF_H__ +#define __RTL8723E_RF_H__ + +#define RF6052_MAX_TX_PWR 0x3F + +extern void rtl8723ae_phy_rf6052_set_bandwidth(struct ieee80211_hw *hw, + u8 bandwidth); +extern void rtl8723ae_phy_rf6052_set_cck_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel); +extern void rtl8723ae_phy_rf6052_set_ofdm_txpower(struct ieee80211_hw *hw, + u8 *ppowerlevel, u8 channel); +extern bool rtl8723ae_phy_rf6052_config(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c new file mode 100644 index 00000000000..0afdc240f2f --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -0,0 +1,387 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include <linux/vmalloc.h> +#include <linux/module.h> + +#include "../core.h" +#include "../pci.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "dm.h" +#include "hw.h" +#include "sw.h" +#include "trx.h" +#include "led.h" +#include "table.h" +#include "hal_btc.h" + +static void rtl8723ae_init_aspm_vars(struct ieee80211_hw *hw) +{ + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + + /*close ASPM for AMD defaultly */ + rtlpci->const_amdpci_aspm = 0; + + /* ASPM PS mode. + * 0 - Disable ASPM, + * 1 - Enable ASPM without Clock Req, + * 2 - Enable ASPM with Clock Req, + * 3 - Alwyas Enable ASPM with Clock Req, + * 4 - Always Enable ASPM without Clock Req. + * set defult to RTL8192CE:3 RTL8192E:2 + */ + rtlpci->const_pci_aspm = 3; + + /*Setting for PCI-E device */ + rtlpci->const_devicepci_aspm_setting = 0x03; + + /*Setting for PCI-E bridge */ + rtlpci->const_hostpci_aspm_setting = 0x02; + + /* In Hw/Sw Radio Off situation. + * 0 - Default, + * 1 - From ASPM setting without low Mac Pwr, + * 2 - From ASPM setting with low Mac Pwr, + * 3 - Bus D3 + * set default to RTL8192CE:0 RTL8192SE:2 + */ + rtlpci->const_hwsw_rfoff_d3 = 0; + + /* This setting works for those device with + * backdoor ASPM setting such as EPHY setting. + * 0 - Not support ASPM, + * 1 - Support ASPM, + * 2 - According to chipset. + */ + rtlpci->const_support_pciaspm = 1; +} + +int rtl8723ae_init_sw_vars(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw)); + int err; + + rtl8723ae_bt_reg_init(hw); + rtlpriv->dm.dm_initialgain_enable = 1; + rtlpriv->dm.dm_flag = 0; + rtlpriv->dm.disable_framebursting = 0; + rtlpriv->dm.thermalvalue = 0; + rtlpci->transmit_config = CFENDFORM | BIT(12) | BIT(13); + + /* compatible 5G band 88ce just 2.4G band & smsp */ + rtlpriv->rtlhal.current_bandtype = BAND_ON_2_4G; + rtlpriv->rtlhal.bandset = BAND_ON_2_4G; + rtlpriv->rtlhal.macphymode = SINGLEMAC_SINGLEPHY; + + rtlpci->receive_config = (RCR_APPFCS | + RCR_APP_MIC | + RCR_APP_ICV | + RCR_APP_PHYST_RXFF | + RCR_HTC_LOC_CTRL | + RCR_AMF | + RCR_ACF | + RCR_ADF | + RCR_AICV | + RCR_AB | + RCR_AM | + RCR_APM | + 0); + + rtlpci->irq_mask[0] = + (u32) (PHIMR_ROK | + PHIMR_RDU | + PHIMR_VODOK | + PHIMR_VIDOK | + PHIMR_BEDOK | + PHIMR_BKDOK | + PHIMR_MGNTDOK | + PHIMR_HIGHDOK | + PHIMR_C2HCMD | + PHIMR_HISRE_IND | + PHIMR_TSF_BIT32_TOGGLE | + PHIMR_TXBCNOK | + PHIMR_PSTIMEOUT | + 0); + + rtlpci->irq_mask[1] = (u32)(PHIMR_RXFOVW | 0); + + /* for debug level */ + rtlpriv->dbg.global_debuglevel = rtlpriv->cfg->mod_params->debug; + /* for LPS & IPS */ + rtlpriv->psc.inactiveps = rtlpriv->cfg->mod_params->inactiveps; + rtlpriv->psc.swctrl_lps = rtlpriv->cfg->mod_params->swctrl_lps; + rtlpriv->psc.fwctrl_lps = rtlpriv->cfg->mod_params->fwctrl_lps; + rtlpriv->psc.reg_fwctrl_lps = 3; + rtlpriv->psc.reg_max_lps_awakeintvl = 5; + /* for ASPM, you can close aspm through + * set const_support_pciaspm = 0 + */ + rtl8723ae_init_aspm_vars(hw); + + if (rtlpriv->psc.reg_fwctrl_lps == 1) + rtlpriv->psc.fwctrl_psmode = FW_PS_MIN_MODE; + else if (rtlpriv->psc.reg_fwctrl_lps == 2) + rtlpriv->psc.fwctrl_psmode = FW_PS_MAX_MODE; + else if (rtlpriv->psc.reg_fwctrl_lps == 3) + rtlpriv->psc.fwctrl_psmode = FW_PS_DTIM_MODE; + + /* for firmware buf */ + rtlpriv->rtlhal.pfirmware = vmalloc(0x6000); + if (!rtlpriv->rtlhal.pfirmware) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Can't alloc buffer for fw.\n"); + return 1; + } + + if (IS_VENDOR_8723_A_CUT(rtlhal->version)) + rtlpriv->cfg->fw_name = "rtlwifi/rtl8723fw.bin"; + else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) + rtlpriv->cfg->fw_name = "rtlwifi/rtl8723fw_B.bin"; + + rtlpriv->max_fw_size = 0x6000; + pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name); + err = request_firmware_nowait(THIS_MODULE, 1, rtlpriv->cfg->fw_name, + rtlpriv->io.dev, GFP_KERNEL, hw, + rtl_fw_cb); + if (err) { + RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG, + "Failed to request firmware!\n"); + return 1; + } + return 0; +} + +void rtl8723ae_deinit_sw_vars(struct ieee80211_hw *hw) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + + if (rtlpriv->rtlhal.pfirmware) { + vfree(rtlpriv->rtlhal.pfirmware); + rtlpriv->rtlhal.pfirmware = NULL; + } +} + +static struct rtl_hal_ops rtl8723ae_hal_ops = { + .init_sw_vars = rtl8723ae_init_sw_vars, + .deinit_sw_vars = rtl8723ae_deinit_sw_vars, + .read_eeprom_info = rtl8723ae_read_eeprom_info, + .interrupt_recognized = rtl8723ae_interrupt_recognized, + .hw_init = rtl8723ae_hw_init, + .hw_disable = rtl8723ae_card_disable, + .hw_suspend = rtl8723ae_suspend, + .hw_resume = rtl8723ae_resume, + .enable_interrupt = rtl8723ae_enable_interrupt, + .disable_interrupt = rtl8723ae_disable_interrupt, + .set_network_type = rtl8723ae_set_network_type, + .set_chk_bssid = rtl8723ae_set_check_bssid, + .set_qos = rtl8723ae_set_qos, + .set_bcn_reg = rtl8723ae_set_beacon_related_registers, + .set_bcn_intv = rtl8723ae_set_beacon_interval, + .update_interrupt_mask = rtl8723ae_update_interrupt_mask, + .get_hw_reg = rtl8723ae_get_hw_reg, + .set_hw_reg = rtl8723ae_set_hw_reg, + .update_rate_tbl = rtl8723ae_update_hal_rate_tbl, + .fill_tx_desc = rtl8723ae_tx_fill_desc, + .fill_tx_cmddesc = rtl8723ae_tx_fill_cmddesc, + .query_rx_desc = rtl8723ae_rx_query_desc, + .set_channel_access = rtl8723ae_update_channel_access_setting, + .radio_onoff_checking = rtl8723ae_gpio_radio_on_off_checking, + .set_bw_mode = rtl8723ae_phy_set_bw_mode, + .switch_channel = rtl8723ae_phy_sw_chnl, + .dm_watchdog = rtl8723ae_dm_watchdog, + .scan_operation_backup = rtl8723ae_phy_scan_operation_backup, + .set_rf_power_state = rtl8723ae_phy_set_rf_power_state, + .led_control = rtl8723ae_led_control, + .set_desc = rtl8723ae_set_desc, + .get_desc = rtl8723ae_get_desc, + .tx_polling = rtl8723ae_tx_polling, + .enable_hw_sec = rtl8723ae_enable_hw_security_config, + .set_key = rtl8723ae_set_key, + .init_sw_leds = rtl8723ae_init_sw_leds, + .allow_all_destaddr = rtl8723ae_allow_all_destaddr, + .get_bbreg = rtl8723ae_phy_query_bb_reg, + .set_bbreg = rtl8723ae_phy_set_bb_reg, + .get_rfreg = rtl8723ae_phy_query_rf_reg, + .set_rfreg = rtl8723ae_phy_set_rf_reg, + .c2h_command_handle = rtl_8723e_c2h_command_handle, + .bt_wifi_media_status_notify = rtl_8723e_bt_wifi_media_status_notify, + .bt_coex_off_before_lps = rtl8723ae_bt_coex_off_before_lps, +}; + +static struct rtl_mod_params rtl8723ae_mod_params = { + .sw_crypto = false, + .inactiveps = true, + .swctrl_lps = false, + .fwctrl_lps = true, + .debug = DBG_EMERG, +}; + +static struct rtl_hal_cfg rtl8723ae_hal_cfg = { + .bar_id = 2, + .write_readback = true, + .name = "rtl8723ae_pci", + .fw_name = "rtlwifi/rtl8723aefw.bin", + .ops = &rtl8723ae_hal_ops, + .mod_params = &rtl8723ae_mod_params, + .maps[SYS_ISO_CTRL] = REG_SYS_ISO_CTRL, + .maps[SYS_FUNC_EN] = REG_SYS_FUNC_EN, + .maps[SYS_CLK] = REG_SYS_CLKR, + .maps[MAC_RCR_AM] = AM, + .maps[MAC_RCR_AB] = AB, + .maps[MAC_RCR_ACRC32] = ACRC32, + .maps[MAC_RCR_ACF] = ACF, + .maps[MAC_RCR_AAP] = AAP, + .maps[EFUSE_TEST] = REG_EFUSE_TEST, + .maps[EFUSE_CTRL] = REG_EFUSE_CTRL, + .maps[EFUSE_CLK] = 0, + .maps[EFUSE_CLK_CTRL] = REG_EFUSE_CTRL, + .maps[EFUSE_PWC_EV12V] = PWC_EV12V, + .maps[EFUSE_FEN_ELDR] = FEN_ELDR, + .maps[EFUSE_LOADER_CLK_EN] = LOADER_CLK_EN, + .maps[EFUSE_ANA8M] = ANA8M, + .maps[EFUSE_HWSET_MAX_SIZE] = HWSET_MAX_SIZE, + .maps[EFUSE_MAX_SECTION_MAP] = EFUSE_MAX_SECTION, + .maps[EFUSE_REAL_CONTENT_SIZE] = EFUSE_REAL_CONTENT_LEN, + .maps[EFUSE_OOB_PROTECT_BYTES_LEN] = EFUSE_OOB_PROTECT_BYTES, + + .maps[RWCAM] = REG_CAMCMD, + .maps[WCAMI] = REG_CAMWRITE, + .maps[RCAMO] = REG_CAMREAD, + .maps[CAMDBG] = REG_CAMDBG, + .maps[SECR] = REG_SECCFG, + .maps[SEC_CAM_NONE] = CAM_NONE, + .maps[SEC_CAM_WEP40] = CAM_WEP40, + .maps[SEC_CAM_TKIP] = CAM_TKIP, + .maps[SEC_CAM_AES] = CAM_AES, + .maps[SEC_CAM_WEP104] = CAM_WEP104, + + .maps[RTL_IMR_BCNDMAINT6] = IMR_BCNDMAINT6, + .maps[RTL_IMR_BCNDMAINT5] = IMR_BCNDMAINT5, + .maps[RTL_IMR_BCNDMAINT4] = IMR_BCNDMAINT4, + .maps[RTL_IMR_BCNDMAINT3] = IMR_BCNDMAINT3, + .maps[RTL_IMR_BCNDMAINT2] = IMR_BCNDMAINT2, + .maps[RTL_IMR_BCNDMAINT1] = IMR_BCNDMAINT1, + .maps[RTL_IMR_BCNDOK8] = IMR_BCNDOK8, + .maps[RTL_IMR_BCNDOK7] = IMR_BCNDOK7, + .maps[RTL_IMR_BCNDOK6] = IMR_BCNDOK6, + .maps[RTL_IMR_BCNDOK5] = IMR_BCNDOK5, + .maps[RTL_IMR_BCNDOK4] = IMR_BCNDOK4, + .maps[RTL_IMR_BCNDOK3] = IMR_BCNDOK3, + .maps[RTL_IMR_BCNDOK2] = IMR_BCNDOK2, + .maps[RTL_IMR_BCNDOK1] = IMR_BCNDOK1, + .maps[RTL_IMR_TIMEOUT2] = IMR_TIMEOUT2, + .maps[RTL_IMR_TIMEOUT1] = IMR_TIMEOUT1, + + .maps[RTL_IMR_TXFOVW] = PHIMR_TXFOVW, + .maps[RTL_IMR_PSTIMEOUT] = PHIMR_PSTIMEOUT, + .maps[RTL_IMR_BcnInt] = PHIMR_BCNDMAINT0, + .maps[RTL_IMR_RXFOVW] = PHIMR_RXFOVW, + .maps[RTL_IMR_RDU] = PHIMR_RDU, + .maps[RTL_IMR_ATIMEND] = PHIMR_ATIMEND_E, + .maps[RTL_IMR_BDOK] = PHIMR_BCNDOK0, + .maps[RTL_IMR_MGNTDOK] = PHIMR_MGNTDOK, + .maps[RTL_IMR_TBDER] = PHIMR_TXBCNERR, + .maps[RTL_IMR_HIGHDOK] = PHIMR_HIGHDOK, + .maps[RTL_IMR_TBDOK] = PHIMR_TXBCNOK, + .maps[RTL_IMR_BKDOK] = PHIMR_BKDOK, + .maps[RTL_IMR_BEDOK] = PHIMR_BEDOK, + .maps[RTL_IMR_VIDOK] = PHIMR_VIDOK, + .maps[RTL_IMR_VODOK] = PHIMR_VODOK, + .maps[RTL_IMR_ROK] = PHIMR_ROK, + .maps[RTL_IBSS_INT_MASKS] = (PHIMR_BCNDMAINT0 | + PHIMR_TXBCNOK | PHIMR_TXBCNERR), + .maps[RTL_IMR_C2HCMD] = PHIMR_C2HCMD, + + + .maps[RTL_RC_CCK_RATE1M] = DESC92_RATE1M, + .maps[RTL_RC_CCK_RATE2M] = DESC92_RATE2M, + .maps[RTL_RC_CCK_RATE5_5M] = DESC92_RATE5_5M, + .maps[RTL_RC_CCK_RATE11M] = DESC92_RATE11M, + .maps[RTL_RC_OFDM_RATE6M] = DESC92_RATE6M, + .maps[RTL_RC_OFDM_RATE9M] = DESC92_RATE9M, + .maps[RTL_RC_OFDM_RATE12M] = DESC92_RATE12M, + .maps[RTL_RC_OFDM_RATE18M] = DESC92_RATE18M, + .maps[RTL_RC_OFDM_RATE24M] = DESC92_RATE24M, + .maps[RTL_RC_OFDM_RATE36M] = DESC92_RATE36M, + .maps[RTL_RC_OFDM_RATE48M] = DESC92_RATE48M, + .maps[RTL_RC_OFDM_RATE54M] = DESC92_RATE54M, + + .maps[RTL_RC_HT_RATEMCS7] = DESC92_RATEMCS7, + .maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15, +}; + +static struct pci_device_id rtl8723ae_pci_ids[] __devinitdata = { + {RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8723, rtl8723ae_hal_cfg)}, + {}, +}; + +MODULE_DEVICE_TABLE(pci, rtl8723ae_pci_ids); + +MODULE_AUTHOR("lizhaoming <chaoming_li@realsil.com.cn>"); +MODULE_AUTHOR("Realtek WlanFAE <wlanfae@realtek.com>"); +MODULE_AUTHOR("Larry Finger <Larry.Finger@lwfinger.net>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Realtek 8723E 802.11n PCI wireless"); +MODULE_FIRMWARE("rtlwifi/rtl8723aefw.bin"); +MODULE_FIRMWARE("rtlwifi/rtl8723aefw_B.bin"); + +module_param_named(swenc, rtl8723ae_mod_params.sw_crypto, bool, 0444); +module_param_named(debug, rtl8723ae_mod_params.debug, int, 0444); +module_param_named(ips, rtl8723ae_mod_params.inactiveps, bool, 0444); +module_param_named(swlps, rtl8723ae_mod_params.swctrl_lps, bool, 0444); +module_param_named(fwlps, rtl8723ae_mod_params.fwctrl_lps, bool, 0444); +MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); +MODULE_PARM_DESC(ips, "Set to 0 to not use link power save (default 1)\n"); +MODULE_PARM_DESC(swlps, "Set to 1 to use SW control power save (default 0)\n"); +MODULE_PARM_DESC(fwlps, "Set to 1 to use FW control power save (default 1)\n"); +MODULE_PARM_DESC(debug, "Set debug level (0-5) (default 0)"); + +static const struct dev_pm_ops rtlwifi_pm_ops = { + .suspend = rtl_pci_suspend, + .resume = rtl_pci_resume, + .freeze = rtl_pci_suspend, + .thaw = rtl_pci_resume, + .poweroff = rtl_pci_suspend, + .restore = rtl_pci_resume, +}; + +static struct pci_driver rtl8723ae_driver = { + .name = KBUILD_MODNAME, + .id_table = rtl8723ae_pci_ids, + .probe = rtl_pci_probe, + .remove = rtl_pci_disconnect, + .driver.pm = &rtlwifi_pm_ops, +}; + +module_pci_driver(rtl8723ae_driver); diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.h b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.h new file mode 100644 index 00000000000..fc4fde5e3eb --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_SW_H__ +#define __RTL8723E_SW_H__ + +int rtl8723ae_init_sw_vars(struct ieee80211_hw *hw); +void rtl8723ae_deinit_sw_vars(struct ieee80211_hw *hw); +void rtl8723ae_init_var_map(struct ieee80211_hw *hw); + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/table.c b/drivers/net/wireless/rtlwifi/rtl8723ae/table.c new file mode 100644 index 00000000000..9b0b50cc4ad --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/table.c @@ -0,0 +1,738 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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. + * + * Created on 2010/ 5/18, 1:41 + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#include "table.h" + +u32 RTL8723EPHY_REG_1TARRAY[RTL8723E_PHY_REG_1TARRAY_LENGTH] = { + 0x800, 0x80040000, + 0x804, 0x00000003, + 0x808, 0x0000fc00, + 0x80c, 0x0000000a, + 0x810, 0x10005388, + 0x814, 0x020c3d10, + 0x818, 0x02200385, + 0x81c, 0x00000000, + 0x820, 0x01000100, + 0x824, 0x00390004, + 0x828, 0x00000000, + 0x82c, 0x00000000, + 0x830, 0x00000000, + 0x834, 0x00000000, + 0x838, 0x00000000, + 0x83c, 0x00000000, + 0x840, 0x00010000, + 0x844, 0x00000000, + 0x848, 0x00000000, + 0x84c, 0x00000000, + 0x850, 0x00000000, + 0x854, 0x00000000, + 0x858, 0x569a569a, + 0x85c, 0x001b25a4, + 0x860, 0x66f60110, + 0x864, 0x061f0130, + 0x868, 0x00000000, + 0x86c, 0x32323200, + 0x870, 0x07000760, + 0x874, 0x22004000, + 0x878, 0x00000808, + 0x87c, 0x00000000, + 0x880, 0xc0083070, + 0x884, 0x000004d5, + 0x888, 0x00000000, + 0x88c, 0xccc000c0, + 0x890, 0x00000800, + 0x894, 0xfffffffe, + 0x898, 0x40302010, + 0x89c, 0x00706050, + 0x900, 0x00000000, + 0x904, 0x00000023, + 0x908, 0x00000000, + 0x90c, 0x81121111, + 0xa00, 0x00d047c8, + 0xa04, 0x80ff000c, + 0xa08, 0x8c838300, + 0xa0c, 0x2e68120f, + 0xa10, 0x9500bb78, + 0xa14, 0x11144028, + 0xa18, 0x00881117, + 0xa1c, 0x89140f00, + 0xa20, 0x1a1b0000, + 0xa24, 0x090e1317, + 0xa28, 0x00000204, + 0xa2c, 0x00d30000, + 0xa70, 0x101fbf00, + 0xa74, 0x00000007, + 0xa78, 0x00000900, + 0xc00, 0x48071d40, + 0xc04, 0x03a05611, + 0xc08, 0x000000e4, + 0xc0c, 0x6c6c6c6c, + 0xc10, 0x08800000, + 0xc14, 0x40000100, + 0xc18, 0x08800000, + 0xc1c, 0x40000100, + 0xc20, 0x00000000, + 0xc24, 0x00000000, + 0xc28, 0x00000000, + 0xc2c, 0x00000000, + 0xc30, 0x69e9ac44, + 0xc34, 0x469652cf, + 0xc38, 0x49795994, + 0xc3c, 0x0a97971c, + 0xc40, 0x1f7c403f, + 0xc44, 0x000100b7, + 0xc48, 0xec020107, + 0xc4c, 0x007f037f, + 0xc50, 0x69543420, + 0xc54, 0x43bc0094, + 0xc58, 0x69543420, + 0xc5c, 0x433c0094, + 0xc60, 0x00000000, + 0xc64, 0x7116848b, + 0xc68, 0x47c00bff, + 0xc6c, 0x00000036, + 0xc70, 0x2c7f000d, + 0xc74, 0x018610db, + 0xc78, 0x0000001f, + 0xc7c, 0x00b91612, + 0xc80, 0x40000100, + 0xc84, 0x20f60000, + 0xc88, 0x40000100, + 0xc8c, 0x20200000, + 0xc90, 0x00121820, + 0xc94, 0x00000000, + 0xc98, 0x00121820, + 0xc9c, 0x00007f7f, + 0xca0, 0x00000000, + 0xca4, 0x00000080, + 0xca8, 0x00000000, + 0xcac, 0x00000000, + 0xcb0, 0x00000000, + 0xcb4, 0x00000000, + 0xcb8, 0x00000000, + 0xcbc, 0x28000000, + 0xcc0, 0x00000000, + 0xcc4, 0x00000000, + 0xcc8, 0x00000000, + 0xccc, 0x00000000, + 0xcd0, 0x00000000, + 0xcd4, 0x00000000, + 0xcd8, 0x64b22427, + 0xcdc, 0x00766932, + 0xce0, 0x00222222, + 0xce4, 0x00000000, + 0xce8, 0x37644302, + 0xcec, 0x2f97d40c, + 0xd00, 0x00080740, + 0xd04, 0x00020401, + 0xd08, 0x0000907f, + 0xd0c, 0x20010201, + 0xd10, 0xa0633333, + 0xd14, 0x3333bc43, + 0xd18, 0x7a8f5b6b, + 0xd2c, 0xcc979975, + 0xd30, 0x00000000, + 0xd34, 0x80608000, + 0xd38, 0x00000000, + 0xd3c, 0x00027293, + 0xd40, 0x00000000, + 0xd44, 0x00000000, + 0xd48, 0x00000000, + 0xd4c, 0x00000000, + 0xd50, 0x6437140a, + 0xd54, 0x00000000, + 0xd58, 0x00000000, + 0xd5c, 0x30032064, + 0xd60, 0x4653de68, + 0xd64, 0x04518a3c, + 0xd68, 0x00002101, + 0xd6c, 0x2a201c16, + 0xd70, 0x1812362e, + 0xd74, 0x322c2220, + 0xd78, 0x000e3c24, + 0xe00, 0x2a2a2a2a, + 0xe04, 0x2a2a2a2a, + 0xe08, 0x03902a2a, + 0xe10, 0x2a2a2a2a, + 0xe14, 0x2a2a2a2a, + 0xe18, 0x2a2a2a2a, + 0xe1c, 0x2a2a2a2a, + 0xe28, 0x00000000, + 0xe30, 0x1000dc1f, + 0xe34, 0x10008c1f, + 0xe38, 0x02140102, + 0xe3c, 0x681604c2, + 0xe40, 0x01007c00, + 0xe44, 0x01004800, + 0xe48, 0xfb000000, + 0xe4c, 0x000028d1, + 0xe50, 0x1000dc1f, + 0xe54, 0x10008c1f, + 0xe58, 0x02140102, + 0xe5c, 0x28160d05, + 0xe60, 0x00000008, + 0xe68, 0x001b25a4, + 0xe6c, 0x631b25a0, + 0xe70, 0x631b25a0, + 0xe74, 0x081b25a0, + 0xe78, 0x081b25a0, + 0xe7c, 0x081b25a0, + 0xe80, 0x081b25a0, + 0xe84, 0x631b25a0, + 0xe88, 0x081b25a0, + 0xe8c, 0x631b25a0, + 0xed0, 0x631b25a0, + 0xed4, 0x631b25a0, + 0xed8, 0x631b25a0, + 0xedc, 0x001b25a0, + 0xee0, 0x001b25a0, + 0xeec, 0x6b1b25a0, + 0xf14, 0x00000003, + 0xf4c, 0x00000000, + 0xf00, 0x00000300, +}; + +u32 RTL8723EPHY_REG_ARRAY_PG[RTL8723E_PHY_REG_ARRAY_PGLENGTH] = { + 0xe00, 0xffffffff, 0x0a0c0c0c, + 0xe04, 0xffffffff, 0x02040608, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x0a0c0d0e, + 0xe14, 0xffffffff, 0x02040608, + 0xe18, 0xffffffff, 0x0a0c0d0e, + 0xe1c, 0xffffffff, 0x02040608, + 0x830, 0xffffffff, 0x0a0c0c0c, + 0x834, 0xffffffff, 0x02040608, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x0a0c0d0e, + 0x848, 0xffffffff, 0x02040608, + 0x84c, 0xffffffff, 0x0a0c0d0e, + 0x868, 0xffffffff, 0x02040608, + 0xe00, 0xffffffff, 0x00000000, + 0xe04, 0xffffffff, 0x00000000, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x00000000, + 0xe14, 0xffffffff, 0x00000000, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x00000000, + 0x834, 0xffffffff, 0x00000000, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x00000000, + 0x848, 0xffffffff, 0x00000000, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, + 0xe00, 0xffffffff, 0x04040404, + 0xe04, 0xffffffff, 0x00020204, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x06060606, + 0xe14, 0xffffffff, 0x00020406, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x04040404, + 0x834, 0xffffffff, 0x00020204, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x06060606, + 0x848, 0xffffffff, 0x00020406, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, + 0xe00, 0xffffffff, 0x00000000, + 0xe04, 0xffffffff, 0x00000000, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x00000000, + 0xe14, 0xffffffff, 0x00000000, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x00000000, + 0x834, 0xffffffff, 0x00000000, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x00000000, + 0x848, 0xffffffff, 0x00000000, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, + 0xe00, 0xffffffff, 0x00000000, + 0xe04, 0xffffffff, 0x00000000, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x00000000, + 0xe14, 0xffffffff, 0x00000000, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x00000000, + 0x834, 0xffffffff, 0x00000000, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x00000000, + 0x848, 0xffffffff, 0x00000000, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, + 0xe00, 0xffffffff, 0x04040404, + 0xe04, 0xffffffff, 0x00020204, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x00000000, + 0xe14, 0xffffffff, 0x00000000, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x04040404, + 0x834, 0xffffffff, 0x00020204, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x00000000, + 0x848, 0xffffffff, 0x00000000, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, + 0xe00, 0xffffffff, 0x00000000, + 0xe04, 0xffffffff, 0x00000000, + 0xe08, 0x0000ff00, 0x00000000, + 0x86c, 0xffffff00, 0x00000000, + 0xe10, 0xffffffff, 0x00000000, + 0xe14, 0xffffffff, 0x00000000, + 0xe18, 0xffffffff, 0x00000000, + 0xe1c, 0xffffffff, 0x00000000, + 0x830, 0xffffffff, 0x00000000, + 0x834, 0xffffffff, 0x00000000, + 0x838, 0xffffff00, 0x00000000, + 0x86c, 0x000000ff, 0x00000000, + 0x83c, 0xffffffff, 0x00000000, + 0x848, 0xffffffff, 0x00000000, + 0x84c, 0xffffffff, 0x00000000, + 0x868, 0xffffffff, 0x00000000, +}; + +u32 RTL8723E_RADIOA_1TARRAY[Rtl8723ERADIOA_1TARRAYLENGTH] = { + 0x000, 0x00030159, + 0x001, 0x00031284, + 0x002, 0x00098000, + 0x003, 0x00018c63, + 0x004, 0x000210e7, + 0x009, 0x0002044f, + 0x00a, 0x0001a3f1, + 0x00b, 0x00014787, + 0x00c, 0x000896fe, + 0x00d, 0x0000e02c, + 0x00e, 0x00039ce7, + 0x00f, 0x00000451, + 0x019, 0x00000000, + 0x01a, 0x00030355, + 0x01b, 0x00060a00, + 0x01c, 0x000fc378, + 0x01d, 0x000a1250, + 0x01e, 0x0004445f, + 0x01f, 0x00080001, + 0x020, 0x0000b614, + 0x021, 0x0006c000, + 0x022, 0x00000000, + 0x023, 0x00001558, + 0x024, 0x00000060, + 0x025, 0x00000483, + 0x026, 0x0004f000, + 0x027, 0x000ec7d9, + 0x028, 0x00057730, + 0x029, 0x00004783, + 0x02a, 0x00000001, + 0x02b, 0x00021334, + 0x02a, 0x00000000, + 0x02b, 0x00000054, + 0x02a, 0x00000001, + 0x02b, 0x00000808, + 0x02b, 0x00053333, + 0x02c, 0x0000000c, + 0x02a, 0x00000002, + 0x02b, 0x00000808, + 0x02b, 0x0005b333, + 0x02c, 0x0000000d, + 0x02a, 0x00000003, + 0x02b, 0x00000808, + 0x02b, 0x00063333, + 0x02c, 0x0000000d, + 0x02a, 0x00000004, + 0x02b, 0x00000808, + 0x02b, 0x0006b333, + 0x02c, 0x0000000d, + 0x02a, 0x00000005, + 0x02b, 0x00000808, + 0x02b, 0x00073333, + 0x02c, 0x0000000d, + 0x02a, 0x00000006, + 0x02b, 0x00000709, + 0x02b, 0x0005b333, + 0x02c, 0x0000000d, + 0x02a, 0x00000007, + 0x02b, 0x00000709, + 0x02b, 0x00063333, + 0x02c, 0x0000000d, + 0x02a, 0x00000008, + 0x02b, 0x0000060a, + 0x02b, 0x0004b333, + 0x02c, 0x0000000d, + 0x02a, 0x00000009, + 0x02b, 0x0000060a, + 0x02b, 0x00053333, + 0x02c, 0x0000000d, + 0x02a, 0x0000000a, + 0x02b, 0x0000060a, + 0x02b, 0x0005b333, + 0x02c, 0x0000000d, + 0x02a, 0x0000000b, + 0x02b, 0x0000060a, + 0x02b, 0x00063333, + 0x02c, 0x0000000d, + 0x02a, 0x0000000c, + 0x02b, 0x0000060a, + 0x02b, 0x0006b333, + 0x02c, 0x0000000d, + 0x02a, 0x0000000d, + 0x02b, 0x0000060a, + 0x02b, 0x00073333, + 0x02c, 0x0000000d, + 0x02a, 0x0000000e, + 0x02b, 0x0000050b, + 0x02b, 0x00066666, + 0x02c, 0x0000001a, + 0x02a, 0x000e0000, + 0x010, 0x0004000f, + 0x011, 0x000e31fc, + 0x010, 0x0006000f, + 0x011, 0x000ff9f8, + 0x010, 0x0002000f, + 0x011, 0x000203f9, + 0x010, 0x0003000f, + 0x011, 0x000ff500, + 0x010, 0x00000000, + 0x011, 0x00000000, + 0x010, 0x0008000f, + 0x011, 0x0003f100, + 0x010, 0x0009000f, + 0x011, 0x00023100, + 0x012, 0x00032000, + 0x012, 0x00071000, + 0x012, 0x000b0000, + 0x012, 0x000fc000, + 0x013, 0x000287b3, + 0x013, 0x000244b7, + 0x013, 0x000204ab, + 0x013, 0x0001c49f, + 0x013, 0x00018493, + 0x013, 0x0001429b, + 0x013, 0x00010299, + 0x013, 0x0000c29c, + 0x013, 0x000081a0, + 0x013, 0x000040ac, + 0x013, 0x00000020, + 0x014, 0x0001944c, + 0x014, 0x00059444, + 0x014, 0x0009944c, + 0x014, 0x000d9444, + 0x015, 0x0000f424, + 0x015, 0x0004f407, + 0x015, 0x0008f424, + 0x015, 0x000cf424, + 0x016, 0x00000339, + 0x016, 0x00040339, + 0x016, 0x00080339, + 0x016, 0x000c0336, + 0x000, 0x00010159, + 0x018, 0x0000f401, + 0x0fe, 0x00000000, + 0x0fe, 0x00000000, + 0x01f, 0x00080003, + 0x0fe, 0x00000000, + 0x0fe, 0x00000000, + 0x01e, 0x00044457, + 0x01f, 0x00080000, + 0x000, 0x00030159, +}; + + +u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH] = { + 0x0, +}; + + +u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH] = { + 0x420, 0x00000080, + 0x423, 0x00000000, + 0x430, 0x00000000, + 0x431, 0x00000000, + 0x432, 0x00000000, + 0x433, 0x00000001, + 0x434, 0x00000004, + 0x435, 0x00000005, + 0x436, 0x00000006, + 0x437, 0x00000007, + 0x438, 0x00000000, + 0x439, 0x00000000, + 0x43a, 0x00000000, + 0x43b, 0x00000001, + 0x43c, 0x00000004, + 0x43d, 0x00000005, + 0x43e, 0x00000006, + 0x43f, 0x00000007, + 0x440, 0x0000005d, + 0x441, 0x00000001, + 0x442, 0x00000000, + 0x444, 0x00000015, + 0x445, 0x000000f0, + 0x446, 0x0000000f, + 0x447, 0x00000000, + 0x458, 0x00000041, + 0x459, 0x000000a8, + 0x45a, 0x00000072, + 0x45b, 0x000000b9, + 0x460, 0x00000066, + 0x461, 0x00000066, + 0x462, 0x00000008, + 0x463, 0x00000003, + 0x4c8, 0x000000ff, + 0x4c9, 0x00000008, + 0x4cc, 0x000000ff, + 0x4cd, 0x000000ff, + 0x4ce, 0x00000001, + 0x500, 0x00000026, + 0x501, 0x000000a2, + 0x502, 0x0000002f, + 0x503, 0x00000000, + 0x504, 0x00000028, + 0x505, 0x000000a3, + 0x506, 0x0000005e, + 0x507, 0x00000000, + 0x508, 0x0000002b, + 0x509, 0x000000a4, + 0x50a, 0x0000005e, + 0x50b, 0x00000000, + 0x50c, 0x0000004f, + 0x50d, 0x000000a4, + 0x50e, 0x00000000, + 0x50f, 0x00000000, + 0x512, 0x0000001c, + 0x514, 0x0000000a, + 0x515, 0x00000010, + 0x516, 0x0000000a, + 0x517, 0x00000010, + 0x51a, 0x00000016, + 0x524, 0x0000000f, + 0x525, 0x0000004f, + 0x546, 0x00000040, + 0x547, 0x00000000, + 0x550, 0x00000010, + 0x551, 0x00000010, + 0x559, 0x00000002, + 0x55a, 0x00000002, + 0x55d, 0x000000ff, + 0x605, 0x00000030, + 0x608, 0x0000000e, + 0x609, 0x0000002a, + 0x652, 0x00000020, + 0x63c, 0x0000000a, + 0x63d, 0x0000000e, + 0x63e, 0x0000000a, + 0x63f, 0x0000000e, + 0x66e, 0x00000005, + 0x700, 0x00000021, + 0x701, 0x00000043, + 0x702, 0x00000065, + 0x703, 0x00000087, + 0x708, 0x00000021, + 0x709, 0x00000043, + 0x70a, 0x00000065, + 0x70b, 0x00000087, +}; + +u32 RTL8723EAGCTAB_1TARRAY[RTL8723E_AGCTAB_1TARRAYLENGTH] = { + 0xc78, 0x7b000001, + 0xc78, 0x7b010001, + 0xc78, 0x7b020001, + 0xc78, 0x7b030001, + 0xc78, 0x7b040001, + 0xc78, 0x7b050001, + 0xc78, 0x7a060001, + 0xc78, 0x79070001, + 0xc78, 0x78080001, + 0xc78, 0x77090001, + 0xc78, 0x760a0001, + 0xc78, 0x750b0001, + 0xc78, 0x740c0001, + 0xc78, 0x730d0001, + 0xc78, 0x720e0001, + 0xc78, 0x710f0001, + 0xc78, 0x70100001, + 0xc78, 0x6f110001, + 0xc78, 0x6e120001, + 0xc78, 0x6d130001, + 0xc78, 0x6c140001, + 0xc78, 0x6b150001, + 0xc78, 0x6a160001, + 0xc78, 0x69170001, + 0xc78, 0x68180001, + 0xc78, 0x67190001, + 0xc78, 0x661a0001, + 0xc78, 0x651b0001, + 0xc78, 0x641c0001, + 0xc78, 0x631d0001, + 0xc78, 0x621e0001, + 0xc78, 0x611f0001, + 0xc78, 0x60200001, + 0xc78, 0x49210001, + 0xc78, 0x48220001, + 0xc78, 0x47230001, + 0xc78, 0x46240001, + 0xc78, 0x45250001, + 0xc78, 0x44260001, + 0xc78, 0x43270001, + 0xc78, 0x42280001, + 0xc78, 0x41290001, + 0xc78, 0x402a0001, + 0xc78, 0x262b0001, + 0xc78, 0x252c0001, + 0xc78, 0x242d0001, + 0xc78, 0x232e0001, + 0xc78, 0x222f0001, + 0xc78, 0x21300001, + 0xc78, 0x20310001, + 0xc78, 0x06320001, + 0xc78, 0x05330001, + 0xc78, 0x04340001, + 0xc78, 0x03350001, + 0xc78, 0x02360001, + 0xc78, 0x01370001, + 0xc78, 0x00380001, + 0xc78, 0x00390001, + 0xc78, 0x003a0001, + 0xc78, 0x003b0001, + 0xc78, 0x003c0001, + 0xc78, 0x003d0001, + 0xc78, 0x003e0001, + 0xc78, 0x003f0001, + 0xc78, 0x7b400001, + 0xc78, 0x7b410001, + 0xc78, 0x7b420001, + 0xc78, 0x7b430001, + 0xc78, 0x7b440001, + 0xc78, 0x7b450001, + 0xc78, 0x7a460001, + 0xc78, 0x79470001, + 0xc78, 0x78480001, + 0xc78, 0x77490001, + 0xc78, 0x764a0001, + 0xc78, 0x754b0001, + 0xc78, 0x744c0001, + 0xc78, 0x734d0001, + 0xc78, 0x724e0001, + 0xc78, 0x714f0001, + 0xc78, 0x70500001, + 0xc78, 0x6f510001, + 0xc78, 0x6e520001, + 0xc78, 0x6d530001, + 0xc78, 0x6c540001, + 0xc78, 0x6b550001, + 0xc78, 0x6a560001, + 0xc78, 0x69570001, + 0xc78, 0x68580001, + 0xc78, 0x67590001, + 0xc78, 0x665a0001, + 0xc78, 0x655b0001, + 0xc78, 0x645c0001, + 0xc78, 0x635d0001, + 0xc78, 0x625e0001, + 0xc78, 0x615f0001, + 0xc78, 0x60600001, + 0xc78, 0x49610001, + 0xc78, 0x48620001, + 0xc78, 0x47630001, + 0xc78, 0x46640001, + 0xc78, 0x45650001, + 0xc78, 0x44660001, + 0xc78, 0x43670001, + 0xc78, 0x42680001, + 0xc78, 0x41690001, + 0xc78, 0x406a0001, + 0xc78, 0x266b0001, + 0xc78, 0x256c0001, + 0xc78, 0x246d0001, + 0xc78, 0x236e0001, + 0xc78, 0x226f0001, + 0xc78, 0x21700001, + 0xc78, 0x20710001, + 0xc78, 0x06720001, + 0xc78, 0x05730001, + 0xc78, 0x04740001, + 0xc78, 0x03750001, + 0xc78, 0x02760001, + 0xc78, 0x01770001, + 0xc78, 0x00780001, + 0xc78, 0x00790001, + 0xc78, 0x007a0001, + 0xc78, 0x007b0001, + 0xc78, 0x007c0001, + 0xc78, 0x007d0001, + 0xc78, 0x007e0001, + 0xc78, 0x007f0001, + 0xc78, 0x3800001e, + 0xc78, 0x3801001e, + 0xc78, 0x3802001e, + 0xc78, 0x3803001e, + 0xc78, 0x3804001e, + 0xc78, 0x3805001e, + 0xc78, 0x3806001e, + 0xc78, 0x3807001e, + 0xc78, 0x3808001e, + 0xc78, 0x3c09001e, + 0xc78, 0x3e0a001e, + 0xc78, 0x400b001e, + 0xc78, 0x440c001e, + 0xc78, 0x480d001e, + 0xc78, 0x4c0e001e, + 0xc78, 0x500f001e, + 0xc78, 0x5210001e, + 0xc78, 0x5611001e, + 0xc78, 0x5a12001e, + 0xc78, 0x5e13001e, + 0xc78, 0x6014001e, + 0xc78, 0x6015001e, + 0xc78, 0x6016001e, + 0xc78, 0x6217001e, + 0xc78, 0x6218001e, + 0xc78, 0x6219001e, + 0xc78, 0x621a001e, + 0xc78, 0x621b001e, + 0xc78, 0x621c001e, + 0xc78, 0x621d001e, + 0xc78, 0x621e001e, + 0xc78, 0x621f001e, +}; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/table.h b/drivers/net/wireless/rtlwifi/rtl8723ae/table.h new file mode 100644 index 00000000000..f5ce71375c2 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/table.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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. + * + * Created on 2010/ 5/18, 1:41 + * + * Larry Finger <Larry.Finger@lwfinger.net> + * + *****************************************************************************/ + +#ifndef __RTL8723E_TABLE__H_ +#define __RTL8723E_TABLE__H_ + +#include <linux/types.h> + +#define RTL8723E_PHY_REG_1TARRAY_LENGTH 372 +extern u32 RTL8723EPHY_REG_1TARRAY[RTL8723E_PHY_REG_1TARRAY_LENGTH]; +#define RTL8723E_PHY_REG_ARRAY_PGLENGTH 336 +extern u32 RTL8723EPHY_REG_ARRAY_PG[RTL8723E_PHY_REG_ARRAY_PGLENGTH]; +#define Rtl8723ERADIOA_1TARRAYLENGTH 282 +extern u32 RTL8723E_RADIOA_1TARRAY[Rtl8723ERADIOA_1TARRAYLENGTH]; +#define RTL8723E_RADIOB_1TARRAYLENGTH 1 +extern u32 RTL8723E_RADIOB_1TARRAY[RTL8723E_RADIOB_1TARRAYLENGTH]; +#define RTL8723E_MACARRAYLENGTH 172 +extern u32 RTL8723EMAC_ARRAY[RTL8723E_MACARRAYLENGTH]; +#define RTL8723E_AGCTAB_1TARRAYLENGTH 320 +extern u32 RTL8723EAGCTAB_1TARRAY[RTL8723E_AGCTAB_1TARRAYLENGTH]; + +#endif diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c new file mode 100644 index 00000000000..87331d826d7 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -0,0 +1,670 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ + +#include "../wifi.h" +#include "../pci.h" +#include "../base.h" +#include "../stats.h" +#include "reg.h" +#include "def.h" +#include "phy.h" +#include "trx.h" +#include "led.h" + +static u8 _rtl8723ae_map_hwqueue_to_fwqueue(struct sk_buff *skb, u8 hw_queue) +{ + __le16 fc = rtl_get_fc(skb); + + if (unlikely(ieee80211_is_beacon(fc))) + return QSLT_BEACON; + if (ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)) + return QSLT_MGNT; + + return skb->priority; +} + +static void _rtl8723ae_query_rxphystatus(struct ieee80211_hw *hw, + struct rtl_stats *pstatus, u8 *pdesc, + struct rx_fwinfo_8723e *p_drvinfo, + bool bpacket_match_bssid, + bool bpacket_toself, bool packet_beacon) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_ps_ctl *ppsc = rtl_psc(rtlpriv); + struct phy_sts_cck_8723e_t *cck_buf; + s8 rx_pwr_all, rx_pwr[4]; + u8 rf_rx_num = 0, evm, pwdb_all; + u8 i, max_spatial_stream; + u32 rssi, total_rssi = 0; + bool is_cck = pstatus->is_cck; + + /* Record it for next packet processing */ + pstatus->packet_matchbssid = bpacket_match_bssid; + pstatus->packet_toself = bpacket_toself; + pstatus->packet_beacon = packet_beacon; + pstatus->rx_mimo_sig_qual[0] = -1; + pstatus->rx_mimo_sig_qual[1] = -1; + + if (is_cck) { + u8 report, cck_highpwr; + + /* CCK Driver info Structure is not the same as OFDM packet. */ + cck_buf = (struct phy_sts_cck_8723e_t *)p_drvinfo; + + /* (1)Hardware does not provide RSSI for CCK + * (2)PWDB, Average PWDB cacluated by + * hardware (for rate adaptive) + */ + if (ppsc->rfpwr_state == ERFON) + cck_highpwr = (u8) rtl_get_bbreg(hw, + RFPGA0_XA_HSSIPARAMETER2, + BIT(9)); + else + cck_highpwr = false; + + if (!cck_highpwr) { + u8 cck_agc_rpt = cck_buf->cck_agc_rpt; + report = cck_buf->cck_agc_rpt & 0xc0; + report = report >> 6; + switch (report) { + case 0x3: + rx_pwr_all = -46 - (cck_agc_rpt & 0x3e); + break; + case 0x2: + rx_pwr_all = -26 - (cck_agc_rpt & 0x3e); + break; + case 0x1: + rx_pwr_all = -12 - (cck_agc_rpt & 0x3e); + break; + case 0x0: + rx_pwr_all = 16 - (cck_agc_rpt & 0x3e); + break; + } + } else { + u8 cck_agc_rpt = cck_buf->cck_agc_rpt; + report = p_drvinfo->cfosho[0] & 0x60; + report = report >> 5; + switch (report) { + case 0x3: + rx_pwr_all = -46 - ((cck_agc_rpt & 0x1f) << 1); + break; + case 0x2: + rx_pwr_all = -26 - ((cck_agc_rpt & 0x1f) << 1); + break; + case 0x1: + rx_pwr_all = -12 - ((cck_agc_rpt & 0x1f) << 1); + break; + case 0x0: + rx_pwr_all = 16 - ((cck_agc_rpt & 0x1f) << 1); + break; + } + } + + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); + /* CCK gain is smaller than OFDM/MCS gain, + * so we add gain diff. From experience, the val is 6 + */ + pwdb_all += 6; + if (pwdb_all > 100) + pwdb_all = 100; + /* modify the offset to make the same + * gain index with OFDM. + */ + if (pwdb_all > 34 && pwdb_all <= 42) + pwdb_all -= 2; + else if (pwdb_all > 26 && pwdb_all <= 34) + pwdb_all -= 6; + else if (pwdb_all > 14 && pwdb_all <= 26) + pwdb_all -= 8; + else if (pwdb_all > 4 && pwdb_all <= 14) + pwdb_all -= 4; + + pstatus->rx_pwdb_all = pwdb_all; + pstatus->recvsignalpower = rx_pwr_all; + + /* (3) Get Signal Quality (EVM) */ + if (bpacket_match_bssid) { + u8 sq; + + if (pstatus->rx_pwdb_all > 40) { + sq = 100; + } else { + sq = cck_buf->sq_rpt; + if (sq > 64) + sq = 0; + else if (sq < 20) + sq = 100; + else + sq = ((64 - sq) * 100) / 44; + } + + pstatus->signalquality = sq; + pstatus->rx_mimo_sig_qual[0] = sq; + pstatus->rx_mimo_sig_qual[1] = -1; + } + } else { + rtlpriv->dm.rfpath_rxenable[0] = + rtlpriv->dm.rfpath_rxenable[1] = true; + + /* (1)Get RSSI for HT rate */ + for (i = RF90_PATH_A; i < RF6052_MAX_PATH; i++) { + + /* we will judge RF RX path now. */ + if (rtlpriv->dm.rfpath_rxenable[i]) + rf_rx_num++; + + rx_pwr[i] = ((p_drvinfo->gain_trsw[i] & 0x3f)*2) - 110; + + /* Translate DBM to percentage. */ + rssi = rtl_query_rxpwrpercentage(rx_pwr[i]); + total_rssi += rssi; + + /* Get Rx snr value in DB */ + rtlpriv->stats.rx_snr_db[i] = (p_drvinfo->rxsnr[i] / 2); + + /* Record Signal Strength for next packet */ + if (bpacket_match_bssid) + pstatus->rx_mimo_signalstrength[i] = (u8) rssi; + } + + /* (2)PWDB, Average PWDB cacluated by + * hardware (for rate adaptive) + */ + rx_pwr_all = ((p_drvinfo->pwdb_all >> 1) & 0x7f) - 110; + + pwdb_all = rtl_query_rxpwrpercentage(rx_pwr_all); + pstatus->rx_pwdb_all = pwdb_all; + pstatus->rxpower = rx_pwr_all; + pstatus->recvsignalpower = rx_pwr_all; + + /* (3)EVM of HT rate */ + if (pstatus->is_ht && pstatus->rate >= DESC92_RATEMCS8 && + pstatus->rate <= DESC92_RATEMCS15) + max_spatial_stream = 2; + else + max_spatial_stream = 1; + + for (i = 0; i < max_spatial_stream; i++) { + evm = rtl_evm_db_to_percentage(p_drvinfo->rxevm[i]); + + if (bpacket_match_bssid) { + /* Fill value in RFD, Get the first + * spatial stream only + */ + if (i == 0) + pstatus->signalquality = (evm & 0xff); + pstatus->rx_mimo_sig_qual[i] = (evm & 0xff); + } + } + } + + /* UI BSS List signal strength(in percentage), + * make it good looking, from 0~100. + */ + if (is_cck) + pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw, + pwdb_all)); + else if (rf_rx_num != 0) + pstatus->signalstrength = (u8)(rtl_signal_scale_mapping(hw, + total_rssi /= rf_rx_num)); +} + +static void _rtl8723ae_translate_rx_signal_stuff(struct ieee80211_hw *hw, + struct sk_buff *skb, struct rtl_stats *pstatus, + u8 *pdesc, struct rx_fwinfo_8723e *p_drvinfo) +{ + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_efuse *rtlefuse = rtl_efuse(rtl_priv(hw)); + struct ieee80211_hdr *hdr; + u8 *tmp_buf; + u8 *praddr; + u8 *psaddr; + __le16 fc; + u16 type; + bool packet_matchbssid, packet_toself, packet_beacon; + + tmp_buf = skb->data + pstatus->rx_drvinfo_size + pstatus->rx_bufshift; + + hdr = (struct ieee80211_hdr *)tmp_buf; + fc = hdr->frame_control; + type = WLAN_FC_GET_TYPE(fc); + praddr = hdr->addr1; + psaddr = ieee80211_get_SA(hdr); + + packet_matchbssid = ((IEEE80211_FTYPE_CTL != type) && + (!compare_ether_addr(mac->bssid, + (le16_to_cpu(fc) & IEEE80211_FCTL_TODS) ? + hdr->addr1 : (le16_to_cpu(fc) & + IEEE80211_FCTL_FROMDS) ? + hdr->addr2 : hdr->addr3)) && (!pstatus->hwerror) && + (!pstatus->crc) && (!pstatus->icv)); + + packet_toself = packet_matchbssid && + (!compare_ether_addr(praddr, rtlefuse->dev_addr)); + + if (ieee80211_is_beacon(fc)) + packet_beacon = true; + + _rtl8723ae_query_rxphystatus(hw, pstatus, pdesc, p_drvinfo, + packet_matchbssid, packet_toself, + packet_beacon); + + rtl_process_phyinfo(hw, tmp_buf, pstatus); +} + +bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, + struct rtl_stats *status, + struct ieee80211_rx_status *rx_status, + u8 *pdesc, struct sk_buff *skb) +{ + struct rx_fwinfo_8723e *p_drvinfo; + struct ieee80211_hdr *hdr; + u32 phystatus = GET_RX_DESC_PHYST(pdesc); + + status->length = (u16) GET_RX_DESC_PKT_LEN(pdesc); + status->rx_drvinfo_size = (u8) GET_RX_DESC_DRV_INFO_SIZE(pdesc) * + RX_DRV_INFO_SIZE_UNIT; + status->rx_bufshift = (u8) (GET_RX_DESC_SHIFT(pdesc) & 0x03); + status->icv = (u16) GET_RX_DESC_ICV(pdesc); + status->crc = (u16) GET_RX_DESC_CRC32(pdesc); + status->hwerror = (status->crc | status->icv); + status->decrypted = !GET_RX_DESC_SWDEC(pdesc); + status->rate = (u8) GET_RX_DESC_RXMCS(pdesc); + status->shortpreamble = (u16) GET_RX_DESC_SPLCP(pdesc); + status->isampdu = (bool) (GET_RX_DESC_PAGGR(pdesc) == 1); + status->isfirst_ampdu = (bool) ((GET_RX_DESC_PAGGR(pdesc) == 1) + && (GET_RX_DESC_FAGGR(pdesc) == 1)); + status->timestamp_low = GET_RX_DESC_TSFL(pdesc); + status->rx_is40Mhzpacket = (bool) GET_RX_DESC_BW(pdesc); + status->is_ht = (bool)GET_RX_DESC_RXHT(pdesc); + + status->is_cck = RTL8723E_RX_HAL_IS_CCK_RATE(status->rate); + + rx_status->freq = hw->conf.channel->center_freq; + rx_status->band = hw->conf.channel->band; + + hdr = (struct ieee80211_hdr *)(skb->data + status->rx_drvinfo_size + + status->rx_bufshift); + + if (status->crc) + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + + if (status->rx_is40Mhzpacket) + rx_status->flag |= RX_FLAG_40MHZ; + + if (status->is_ht) + rx_status->flag |= RX_FLAG_HT; + + rx_status->flag |= RX_FLAG_MACTIME_START; + + /* hw will set status->decrypted true, if it finds the + * frame is open data frame or mgmt frame. + * Thus hw will not decrypt a robust managment frame + * for IEEE80211w but still set status->decrypted + * true, so here we should set it back to undecrypted + * for IEEE80211w frame, and mac80211 sw will help + * to decrypt it + */ + if (status->decrypted) { + if ((ieee80211_is_robust_mgmt_frame(hdr)) && + (ieee80211_has_protected(hdr->frame_control))) + rx_status->flag &= ~RX_FLAG_DECRYPTED; + else + rx_status->flag |= RX_FLAG_DECRYPTED; + } + + /* rate_idx: index of data rate into band's + * supported rates or MCS index if HT rates + * are use (RX_FLAG_HT) + */ + rx_status->rate_idx = rtlwifi_rate_mapping(hw, status->is_ht, + status->rate, false); + + rx_status->mactime = status->timestamp_low; + if (phystatus == true) { + p_drvinfo = (struct rx_fwinfo_8723e *)(skb->data + + status->rx_bufshift); + + _rtl8723ae_translate_rx_signal_stuff(hw, + skb, status, pdesc, p_drvinfo); + } + + /*rx_status->qual = status->signal; */ + rx_status->signal = status->recvsignalpower + 10; + /*rx_status->noise = -status->noise; */ + + return true; +} + +void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, + struct ieee80211_hdr *hdr, u8 *pdesc_tx, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 hw_queue, + struct rtl_tcb_desc *ptcdesc) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_mac *mac = rtl_mac(rtl_priv(hw)); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct rtl_ps_ctl *ppsc = rtl_psc(rtl_priv(hw)); + bool defaultadapter = true; + u8 *pdesc = (u8 *) pdesc_tx; + u16 seq_number; + __le16 fc = hdr->frame_control; + u8 fw_qsel = _rtl8723ae_map_hwqueue_to_fwqueue(skb, hw_queue); + bool firstseg = ((hdr->seq_ctrl & + cpu_to_le16(IEEE80211_SCTL_FRAG)) == 0); + bool lastseg = ((hdr->frame_control & + cpu_to_le16(IEEE80211_FCTL_MOREFRAGS)) == 0); + dma_addr_t mapping = pci_map_single(rtlpci->pdev, + skb->data, skb->len, + PCI_DMA_TODEVICE); + u8 bw_40 = 0; + + if (mac->opmode == NL80211_IFTYPE_STATION) { + bw_40 = mac->bw_40; + } else if (mac->opmode == NL80211_IFTYPE_AP || + mac->opmode == NL80211_IFTYPE_ADHOC) { + if (sta) + bw_40 = sta->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + + seq_number = (le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_SEQ) >> 4; + + rtl_get_tcb_desc(hw, info, sta, skb, ptcdesc); + + CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_8723e)); + + if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) { + firstseg = true; + lastseg = true; + } + + if (firstseg) { + SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + + SET_TX_DESC_TX_RATE(pdesc, ptcdesc->hw_rate); + + if (ptcdesc->use_shortgi || ptcdesc->use_shortpreamble) + SET_TX_DESC_DATA_SHORTGI(pdesc, 1); + + if (info->flags & IEEE80211_TX_CTL_AMPDU) { + SET_TX_DESC_AGG_BREAK(pdesc, 1); + SET_TX_DESC_MAX_AGG_NUM(pdesc, 0x14); + } + SET_TX_DESC_SEQ(pdesc, seq_number); + + SET_TX_DESC_RTS_ENABLE(pdesc, ((ptcdesc->rts_enable && + !ptcdesc-> + cts_enable) ? 1 : 0)); + SET_TX_DESC_HW_RTS_ENABLE(pdesc, + ((ptcdesc->rts_enable + || ptcdesc->cts_enable) ? 1 : 0)); + SET_TX_DESC_CTS2SELF(pdesc, ((ptcdesc->cts_enable) ? 1 : 0)); + SET_TX_DESC_RTS_STBC(pdesc, ((ptcdesc->rts_stbc) ? 1 : 0)); + + SET_TX_DESC_RTS_RATE(pdesc, ptcdesc->rts_rate); + SET_TX_DESC_RTS_BW(pdesc, 0); + SET_TX_DESC_RTS_SC(pdesc, ptcdesc->rts_sc); + SET_TX_DESC_RTS_SHORT(pdesc, + ((ptcdesc->rts_rate <= DESC92_RATE54M) ? + (ptcdesc->rts_use_shortpreamble ? 1 : 0) + : (ptcdesc->rts_use_shortgi ? 1 : 0))); + + if (bw_40) { + if (ptcdesc->packet_bw) { + SET_TX_DESC_DATA_BW(pdesc, 1); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, 3); + } else { + SET_TX_DESC_DATA_BW(pdesc, 0); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, + mac->cur_40_prime_sc); + } + } else { + SET_TX_DESC_DATA_BW(pdesc, 0); + SET_TX_DESC_TX_SUB_CARRIER(pdesc, 0); + } + + SET_TX_DESC_LINIP(pdesc, 0); + SET_TX_DESC_PKT_SIZE(pdesc, (u16) skb->len); + + if (sta) { + u8 ampdu_density = sta->ht_cap.ampdu_density; + SET_TX_DESC_AMPDU_DENSITY(pdesc, ampdu_density); + } + + if (info->control.hw_key) { + struct ieee80211_key_conf *keyconf = + info->control.hw_key; + + switch (keyconf->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + case WLAN_CIPHER_SUITE_TKIP: + SET_TX_DESC_SEC_TYPE(pdesc, 0x1); + break; + case WLAN_CIPHER_SUITE_CCMP: + SET_TX_DESC_SEC_TYPE(pdesc, 0x3); + break; + default: + SET_TX_DESC_SEC_TYPE(pdesc, 0x0); + break; + } + } + + SET_TX_DESC_PKT_ID(pdesc, 0); + SET_TX_DESC_QUEUE_SEL(pdesc, fw_qsel); + + SET_TX_DESC_DATA_RATE_FB_LIMIT(pdesc, 0x1F); + SET_TX_DESC_RTS_RATE_FB_LIMIT(pdesc, 0xF); + SET_TX_DESC_DISABLE_FB(pdesc, 0); + SET_TX_DESC_USE_RATE(pdesc, ptcdesc->use_driver_rate ? 1 : 0); + + if (ieee80211_is_data_qos(fc)) { + if (mac->rdg_en) { + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, + "Enable RDG function.\n"); + SET_TX_DESC_RDG_ENABLE(pdesc, 1); + SET_TX_DESC_HTC(pdesc, 1); + } + } + } + + SET_TX_DESC_FIRST_SEG(pdesc, (firstseg ? 1 : 0)); + SET_TX_DESC_LAST_SEG(pdesc, (lastseg ? 1 : 0)); + + SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) skb->len); + + SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); + + if (rtlpriv->dm.useramask) { + SET_TX_DESC_RATE_ID(pdesc, ptcdesc->ratr_index); + SET_TX_DESC_MACID(pdesc, ptcdesc->mac_id); + } else { + SET_TX_DESC_RATE_ID(pdesc, 0xC + ptcdesc->ratr_index); + SET_TX_DESC_MACID(pdesc, ptcdesc->ratr_index); + } + + if ((!ieee80211_is_data_qos(fc)) && ppsc->fwctrl_lps) { + SET_TX_DESC_HWSEQ_EN_8723(pdesc, 1); + + if (!defaultadapter) + SET_TX_DESC_HWSEQ_SEL_8723(pdesc, 1); + } + + SET_TX_DESC_MORE_FRAG(pdesc, (lastseg ? 0 : 1)); + + if (is_multicast_ether_addr(ieee80211_get_DA(hdr)) || + is_broadcast_ether_addr(ieee80211_get_DA(hdr))) { + SET_TX_DESC_BMC(pdesc, 1); + } + + RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, "\n"); +} + +void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, + u8 *pdesc, bool firstseg, + bool lastseg, struct sk_buff *skb) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw)); + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data); + u8 fw_queue = QSLT_BEACON; + dma_addr_t mapping = pci_map_single(rtlpci->pdev, + skb->data, skb->len, + PCI_DMA_TODEVICE); + __le16 fc = hdr->frame_control; + + CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE); + + if (firstseg) + SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); + + SET_TX_DESC_TX_RATE(pdesc, DESC92_RATE1M); + + SET_TX_DESC_SEQ(pdesc, 0); + + SET_TX_DESC_LINIP(pdesc, 0); + + SET_TX_DESC_QUEUE_SEL(pdesc, fw_queue); + + SET_TX_DESC_FIRST_SEG(pdesc, 1); + SET_TX_DESC_LAST_SEG(pdesc, 1); + + SET_TX_DESC_TX_BUFFER_SIZE(pdesc, (u16) (skb->len)); + + SET_TX_DESC_TX_BUFFER_ADDRESS(pdesc, mapping); + + SET_TX_DESC_RATE_ID(pdesc, 7); + SET_TX_DESC_MACID(pdesc, 0); + + SET_TX_DESC_OWN(pdesc, 1); + + SET_TX_DESC_PKT_SIZE((u8 *) pdesc, (u16) (skb->len)); + + SET_TX_DESC_FIRST_SEG(pdesc, 1); + SET_TX_DESC_LAST_SEG(pdesc, 1); + + SET_TX_DESC_OFFSET(pdesc, 0x20); + + SET_TX_DESC_USE_RATE(pdesc, 1); + + if (!ieee80211_is_data_qos(fc)) { + SET_TX_DESC_HWSEQ_EN_8723(pdesc, 1); + /* SET_TX_DESC_HWSEQ_EN(pdesc, 1); */ + /* SET_TX_DESC_PKT_ID(pdesc, 8); */ + } + + RT_PRINT_DATA(rtlpriv, COMP_CMD, DBG_LOUD, + "H2C Tx Cmd Content\n", + pdesc, TX_DESC_SIZE); +} + +void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val) +{ + if (istx == true) { + switch (desc_name) { + case HW_DESC_OWN: + SET_TX_DESC_OWN(pdesc, 1); + break; + case HW_DESC_TX_NEXTDESC_ADDR: + SET_TX_DESC_NEXT_DESC_ADDRESS(pdesc, *(u32 *) val); + break; + default: + RT_ASSERT(false, "ERR txdesc :%d not process\n", + desc_name); + break; + } + } else { + switch (desc_name) { + case HW_DESC_RXOWN: + SET_RX_DESC_OWN(pdesc, 1); + break; + case HW_DESC_RXBUFF_ADDR: + SET_RX_DESC_BUFF_ADDR(pdesc, *(u32 *) val); + break; + case HW_DESC_RXPKT_LEN: + SET_RX_DESC_PKT_LEN(pdesc, *(u32 *) val); + break; + case HW_DESC_RXERO: + SET_RX_DESC_EOR(pdesc, 1); + break; + default: + RT_ASSERT(false, "ERR rxdesc :%d not process\n", + desc_name); + break; + } + } +} + +u32 rtl8723ae_get_desc(u8 *pdesc, bool istx, u8 desc_name) +{ + u32 ret = 0; + + if (istx == true) { + switch (desc_name) { + case HW_DESC_OWN: + ret = GET_TX_DESC_OWN(pdesc); + break; + case HW_DESC_TXBUFF_ADDR: + ret = GET_TX_DESC_TX_BUFFER_ADDRESS(pdesc); + break; + default: + RT_ASSERT(false, "ERR txdesc :%d not process\n", + desc_name); + break; + } + } else { + switch (desc_name) { + case HW_DESC_OWN: + ret = GET_RX_DESC_OWN(pdesc); + break; + case HW_DESC_RXPKT_LEN: + ret = GET_RX_DESC_PKT_LEN(pdesc); + break; + default: + RT_ASSERT(false, "ERR rxdesc :%d not process\n", + desc_name); + break; + } + } + return ret; +} + +void rtl8723ae_tx_polling(struct ieee80211_hw *hw, u8 hw_queue) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + if (hw_queue == BEACON_QUEUE) { + rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, BIT(4)); + } else { + rtl_write_word(rtlpriv, REG_PCIE_CTRL_REG, + BIT(0) << (hw_queue)); + } +} diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h new file mode 100644 index 00000000000..ad05b54bc0f --- /dev/null +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.h @@ -0,0 +1,725 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL8723E_TRX_H__ +#define __RTL8723E_TRX_H__ + +#define TX_DESC_SIZE 64 +#define TX_DESC_AGGR_SUBFRAME_SIZE 32 + +#define RX_DESC_SIZE 32 +#define RX_DRV_INFO_SIZE_UNIT 8 + +#define TX_DESC_NEXT_DESC_OFFSET 40 +#define USB_HWDESC_HEADER_LEN 32 +#define CRCLENGTH 4 + +#define SET_TX_DESC_PKT_SIZE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 0, 16, __val) +#define SET_TX_DESC_OFFSET(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 16, 8, __val) +#define SET_TX_DESC_BMC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 24, 1, __val) +#define SET_TX_DESC_HTC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 25, 1, __val) +#define SET_TX_DESC_LAST_SEG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 26, 1, __val) +#define SET_TX_DESC_FIRST_SEG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 27, 1, __val) +#define SET_TX_DESC_LINIP(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 28, 1, __val) +#define SET_TX_DESC_NO_ACM(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 29, 1, __val) +#define SET_TX_DESC_GF(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val) +#define SET_TX_DESC_OWN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val) + +#define GET_TX_DESC_PKT_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 0, 16) +#define GET_TX_DESC_OFFSET(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 16, 8) +#define GET_TX_DESC_BMC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 24, 1) +#define GET_TX_DESC_HTC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 25, 1) +#define GET_TX_DESC_LAST_SEG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 26, 1) +#define GET_TX_DESC_FIRST_SEG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 27, 1) +#define GET_TX_DESC_LINIP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 28, 1) +#define GET_TX_DESC_NO_ACM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 29, 1) +#define GET_TX_DESC_GF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 30, 1) +#define GET_TX_DESC_OWN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 31, 1) + +#define SET_TX_DESC_MACID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 0, 5, __val) +#define SET_TX_DESC_AGG_BREAK(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 5, 1, __val) +#define SET_TX_DESC_BK(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 6, 1, __val) +#define SET_TX_DESC_RDG_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 7, 1, __val) +#define SET_TX_DESC_QUEUE_SEL(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 8, 5, __val) +#define SET_TX_DESC_RDG_NAV_EXT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 13, 1, __val) +#define SET_TX_DESC_LSIG_TXOP_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 14, 1, __val) +#define SET_TX_DESC_PIFS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 15, 1, __val) +#define SET_TX_DESC_RATE_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 16, 4, __val) +#define SET_TX_DESC_NAV_USE_HDR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 20, 1, __val) +#define SET_TX_DESC_EN_DESC_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 21, 1, __val) +#define SET_TX_DESC_SEC_TYPE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 22, 2, __val) +#define SET_TX_DESC_PKT_OFFSET(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+4, 24, 8, __val) + +#define GET_TX_DESC_MACID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 0, 5) +#define GET_TX_DESC_AGG_ENABLE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 5, 1) +#define GET_TX_DESC_AGG_BREAK(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 6, 1) +#define GET_TX_DESC_RDG_ENABLE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 7, 1) +#define GET_TX_DESC_QUEUE_SEL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 8, 5) +#define GET_TX_DESC_RDG_NAV_EXT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 13, 1) +#define GET_TX_DESC_LSIG_TXOP_EN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 14, 1) +#define GET_TX_DESC_PIFS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 15, 1) +#define GET_TX_DESC_RATE_ID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 16, 4) +#define GET_TX_DESC_NAV_USE_HDR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 20, 1) +#define GET_TX_DESC_EN_DESC_ID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 21, 1) +#define GET_TX_DESC_SEC_TYPE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 22, 2) +#define GET_TX_DESC_PKT_OFFSET(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 24, 8) + +#define SET_TX_DESC_RTS_RC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 0, 6, __val) +#define SET_TX_DESC_DATA_RC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 6, 6, __val) +#define SET_TX_DESC_BAR_RTY_TH(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 14, 2, __val) +#define SET_TX_DESC_MORE_FRAG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 17, 1, __val) +#define SET_TX_DESC_RAW(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 18, 1, __val) +#define SET_TX_DESC_CCX(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 19, 1, __val) +#define SET_TX_DESC_AMPDU_DENSITY(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 20, 3, __val) +#define SET_TX_DESC_ANTSEL_A(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 24, 1, __val) +#define SET_TX_DESC_ANTSEL_B(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 25, 1, __val) +#define SET_TX_DESC_TX_ANT_CCK(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 26, 2, __val) +#define SET_TX_DESC_TX_ANTL(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 28, 2, __val) +#define SET_TX_DESC_TX_ANT_HT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+8, 30, 2, __val) + +#define GET_TX_DESC_RTS_RC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 0, 6) +#define GET_TX_DESC_DATA_RC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 6, 6) +#define GET_TX_DESC_BAR_RTY_TH(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 14, 2) +#define GET_TX_DESC_MORE_FRAG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 17, 1) +#define GET_TX_DESC_RAW(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 18, 1) +#define GET_TX_DESC_CCX(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 19, 1) +#define GET_TX_DESC_AMPDU_DENSITY(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 20, 3) +#define GET_TX_DESC_ANTSEL_A(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 24, 1) +#define GET_TX_DESC_ANTSEL_B(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 25, 1) +#define GET_TX_DESC_TX_ANT_CCK(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 26, 2) +#define GET_TX_DESC_TX_ANTL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 28, 2) +#define GET_TX_DESC_TX_ANT_HT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 30, 2) + +#define SET_TX_DESC_NEXT_HEAP_PAGE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 0, 8, __val) +#define SET_TX_DESC_TAIL_PAGE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 8, 8, __val) +#define SET_TX_DESC_SEQ(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 16, 12, __val) +#define SET_TX_DESC_PKT_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 28, 4, __val) + +#define GET_TX_DESC_NEXT_HEAP_PAGE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 0, 8) +#define GET_TX_DESC_TAIL_PAGE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 8, 8) +#define GET_TX_DESC_SEQ(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 16, 12) +#define GET_TX_DESC_PKT_ID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 28, 4) + +/* For RTL8723 */ +#define SET_TX_DESC_TRIGGER_INT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 30, 1, __val) +#define SET_TX_DESC_HWSEQ_EN_8723(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+12, 31, 1, __val) +#define SET_TX_DESC_HWSEQ_SEL_8723(__pTxDesc, __Value) \ + SET_BITS_TO_LE_4BYTE(__pTxDesc+16, 6, 2, __Value) + +#define SET_TX_DESC_RTS_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 0, 5, __val) +#define SET_TX_DESC_AP_DCFE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 5, 1, __val) +#define SET_TX_DESC_QOS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 6, 1, __val) +#define SET_TX_DESC_HWSEQ_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 7, 1, __val) +#define SET_TX_DESC_USE_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 8, 1, __val) +#define SET_TX_DESC_DISABLE_RTS_FB(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 9, 1, __val) +#define SET_TX_DESC_DISABLE_FB(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 10, 1, __val) +#define SET_TX_DESC_CTS2SELF(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 11, 1, __val) +#define SET_TX_DESC_RTS_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 12, 1, __val) +#define SET_TX_DESC_HW_RTS_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 13, 1, __val) +#define SET_TX_DESC_PORT_ID(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 14, 1, __val) +#define SET_TX_DESC_WAIT_DCTS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 18, 1, __val) +#define SET_TX_DESC_CTS2AP_EN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 19, 1, __val) +#define SET_TX_DESC_TX_SUB_CARRIER(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 20, 2, __val) +#define SET_TX_DESC_TX_STBC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 22, 2, __val) +#define SET_TX_DESC_DATA_SHORT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 24, 1, __val) +#define SET_TX_DESC_DATA_BW(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 25, 1, __val) +#define SET_TX_DESC_RTS_SHORT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 26, 1, __val) +#define SET_TX_DESC_RTS_BW(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 27, 1, __val) +#define SET_TX_DESC_RTS_SC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 28, 2, __val) +#define SET_TX_DESC_RTS_STBC(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+16, 30, 2, __val) + +#define GET_TX_DESC_RTS_RATE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 0, 5) +#define GET_TX_DESC_AP_DCFE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 5, 1) +#define GET_TX_DESC_QOS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 6, 1) +#define GET_TX_DESC_HWSEQ_EN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 7, 1) +#define GET_TX_DESC_USE_RATE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 8, 1) +#define GET_TX_DESC_DISABLE_RTS_FB(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 9, 1) +#define GET_TX_DESC_DISABLE_FB(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 10, 1) +#define GET_TX_DESC_CTS2SELF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 11, 1) +#define GET_TX_DESC_RTS_ENABLE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 12, 1) +#define GET_TX_DESC_HW_RTS_ENABLE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 13, 1) +#define GET_TX_DESC_PORT_ID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 14, 1) +#define GET_TX_DESC_WAIT_DCTS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 18, 1) +#define GET_TX_DESC_CTS2AP_EN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 19, 1) +#define GET_TX_DESC_TX_SUB_CARRIER(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 20, 2) +#define GET_TX_DESC_TX_STBC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 22, 2) +#define GET_TX_DESC_DATA_SHORT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 24, 1) +#define GET_TX_DESC_DATA_BW(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 25, 1) +#define GET_TX_DESC_RTS_SHORT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 26, 1) +#define GET_TX_DESC_RTS_BW(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 27, 1) +#define GET_TX_DESC_RTS_SC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 28, 2) +#define GET_TX_DESC_RTS_STBC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 30, 2) + +#define SET_TX_DESC_TX_RATE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 0, 6, __val) +#define SET_TX_DESC_DATA_SHORTGI(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 6, 1, __val) +#define SET_TX_DESC_CCX_TAG(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 7, 1, __val) +#define SET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 8, 5, __val) +#define SET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 13, 4, __val) +#define SET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 17, 1, __val) +#define SET_TX_DESC_DATA_RETRY_LIMIT(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 18, 6, __val) +#define SET_TX_DESC_USB_TXAGG_NUM(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+20, 24, 8, __val) + +#define GET_TX_DESC_TX_RATE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 0, 6) +#define GET_TX_DESC_DATA_SHORTGI(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 6, 1) +#define GET_TX_DESC_CCX_TAG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 7, 1) +#define GET_TX_DESC_DATA_RATE_FB_LIMIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 8, 5) +#define GET_TX_DESC_RTS_RATE_FB_LIMIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 13, 4) +#define GET_TX_DESC_RETRY_LIMIT_ENABLE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 17, 1) +#define GET_TX_DESC_DATA_RETRY_LIMIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 18, 6) +#define GET_TX_DESC_USB_TXAGG_NUM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 24, 8) + +#define SET_TX_DESC_TXAGC_A(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 5, __val) +#define SET_TX_DESC_TXAGC_B(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 5, 5, __val) +#define SET_TX_DESC_USE_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 10, 1, __val) +#define SET_TX_DESC_MAX_AGG_NUM(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 11, 5, __val) +#define SET_TX_DESC_MCSG1_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 16, 4, __val) +#define SET_TX_DESC_MCSG2_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 20, 4, __val) +#define SET_TX_DESC_MCSG3_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 24, 4, __val) +#define SET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc, __val)\ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 28, 4, __val) + +#define GET_TX_DESC_TXAGC_A(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 0, 5) +#define GET_TX_DESC_TXAGC_B(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 5, 5) +#define GET_TX_DESC_USE_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 10, 1) +#define GET_TX_DESC_MAX_AGG_NUM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 11, 5) +#define GET_TX_DESC_MCSG1_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 16, 4) +#define GET_TX_DESC_MCSG2_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 20, 4) +#define GET_TX_DESC_MCSG3_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 24, 4) +#define GET_TX_DESC_MCS7_SGI_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 28, 4) + +#define SET_TX_DESC_TX_BUFFER_SIZE(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 16, __val) +#define SET_TX_DESC_MCSG4_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 16, 4, __val) +#define SET_TX_DESC_MCSG5_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 20, 4, __val) +#define SET_TX_DESC_MCSG6_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 24, 4, __val) +#define SET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 28, 4, __val) + +#define GET_TX_DESC_TX_BUFFER_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 0, 16) +#define GET_TX_DESC_MCSG4_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 16, 4) +#define GET_TX_DESC_MCSG5_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 20, 4) +#define GET_TX_DESC_MCSG6_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 24, 4) +#define GET_TX_DESC_MCS15_SGI_MAX_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 28, 4) + +#define SET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+32, 0, 32, __val) +#define SET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+36, 0, 32, __val) + +#define GET_TX_DESC_TX_BUFFER_ADDRESS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+32, 0, 32) +#define GET_TX_DESC_TX_BUFFER_ADDRESS64(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+36, 0, 32) + +#define SET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+40, 0, 32, __val) +#define SET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+44, 0, 32, __val) + +#define GET_TX_DESC_NEXT_DESC_ADDRESS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+40, 0, 32) +#define GET_TX_DESC_NEXT_DESC_ADDRESS64(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+44, 0, 32) + +#define GET_RX_DESC_PKT_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 0, 14) +#define GET_RX_DESC_CRC32(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 14, 1) +#define GET_RX_DESC_ICV(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 15, 1) +#define GET_RX_DESC_DRV_INFO_SIZE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 16, 4) +#define GET_RX_DESC_SECURITY(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 20, 3) +#define GET_RX_DESC_QOS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 23, 1) +#define GET_RX_DESC_SHIFT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 24, 2) +#define GET_RX_DESC_PHYST(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 26, 1) +#define GET_RX_DESC_SWDEC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 27, 1) +#define GET_RX_DESC_LS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 28, 1) +#define GET_RX_DESC_FS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 29, 1) +#define GET_RX_DESC_EOR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 30, 1) +#define GET_RX_DESC_OWN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc, 31, 1) + +#define SET_RX_DESC_PKT_LEN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 0, 14, __val) +#define SET_RX_DESC_EOR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 30, 1, __val) +#define SET_RX_DESC_OWN(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc, 31, 1, __val) + +#define GET_RX_DESC_MACID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 0, 5) +#define GET_RX_DESC_TID(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 5, 4) +#define GET_RX_DESC_HWRSVD(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 9, 5) +#define GET_RX_DESC_PAGGR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 14, 1) +#define GET_RX_DESC_FAGGR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 15, 1) +#define GET_RX_DESC_A1_FIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 16, 4) +#define GET_RX_DESC_A2_FIT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 20, 4) +#define GET_RX_DESC_PAM(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 24, 1) +#define GET_RX_DESC_PWR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 25, 1) +#define GET_RX_DESC_MD(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 26, 1) +#define GET_RX_DESC_MF(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 27, 1) +#define GET_RX_DESC_TYPE(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 28, 2) +#define GET_RX_DESC_MC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 30, 1) +#define GET_RX_DESC_BC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+4, 31, 1) +#define GET_RX_DESC_SEQ(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 0, 12) +#define GET_RX_DESC_FRAG(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 12, 4) +#define GET_RX_DESC_NEXT_PKT_LEN(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 16, 14) +#define GET_RX_DESC_NEXT_IND(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 30, 1) +#define GET_RX_DESC_RSVD(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+8, 31, 1) + +#define GET_RX_DESC_RXMCS(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 0, 6) +#define GET_RX_DESC_RXHT(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 6, 1) +#define GET_RX_DESC_SPLCP(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 8, 1) +#define GET_RX_DESC_BW(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 9, 1) +#define GET_RX_DESC_HTC(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 10, 1) +#define GET_RX_DESC_HWPC_ERR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 14, 1) +#define GET_RX_DESC_HWPC_IND(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 15, 1) +#define GET_RX_DESC_IV0(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+12, 16, 16) + +#define GET_RX_DESC_IV1(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+16, 0, 32) +#define GET_RX_DESC_TSFL(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+20, 0, 32) + +#define GET_RX_DESC_BUFF_ADDR(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+24, 0, 32) +#define GET_RX_DESC_BUFF_ADDR64(__pdesc) \ + LE_BITS_TO_4BYTE(__pdesc+28, 0, 32) + +#define SET_RX_DESC_BUFF_ADDR(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+24, 0, 32, __val) +#define SET_RX_DESC_BUFF_ADDR64(__pdesc, __val) \ + SET_BITS_TO_LE_4BYTE(__pdesc+28, 0, 32, __val) + +#define CLEAR_PCI_TX_DESC_CONTENT(__pdesc, _size) \ +do { \ + if (_size > TX_DESC_NEXT_DESC_OFFSET) \ + memset(__pdesc, 0, TX_DESC_NEXT_DESC_OFFSET); \ + else \ + memset(__pdesc, 0, _size); \ +} while (0) + +#define RTL8723E_RX_HAL_IS_CCK_RATE(rxmcs) \ + ((rxmcs) == DESC92_RATE1M || \ + (rxmcs) == DESC92_RATE2M || \ + (rxmcs) == DESC92_RATE5_5M || \ + (rxmcs) == DESC92_RATE11M) + +struct rx_fwinfo_8723e { + u8 gain_trsw[4]; + u8 pwdb_all; + u8 cfosho[4]; + u8 cfotail[4]; + char rxevm[2]; + char rxsnr[4]; + u8 pdsnr[2]; + u8 csi_current[2]; + u8 csi_target[2]; + u8 sigevm; + u8 max_ex_pwr; + u8 ex_intf_flag:1; + u8 sgi_en:1; + u8 rxsc:2; + u8 reserve:4; +} __packed; + +struct tx_desc_8723e { + u32 pktsize:16; + u32 offset:8; + u32 bmc:1; + u32 htc:1; + u32 lastseg:1; + u32 firstseg:1; + u32 linip:1; + u32 noacm:1; + u32 gf:1; + u32 own:1; + + u32 macid:5; + u32 agg_en:1; + u32 bk:1; + u32 rdg_en:1; + u32 queuesel:5; + u32 rd_nav_ext:1; + u32 lsig_txop_en:1; + u32 pifs:1; + u32 rateid:4; + u32 nav_usehdr:1; + u32 en_descid:1; + u32 sectype:2; + u32 pktoffset:8; + + u32 rts_rc:6; + u32 data_rc:6; + u32 rsvd0:2; + u32 bar_retryht:2; + u32 rsvd1:1; + u32 morefrag:1; + u32 raw:1; + u32 ccx:1; + u32 ampdudensity:3; + u32 rsvd2:1; + u32 ant_sela:1; + u32 ant_selb:1; + u32 txant_cck:2; + u32 txant_l:2; + u32 txant_ht:2; + + u32 nextheadpage:8; + u32 tailpage:8; + u32 seq:12; + u32 pktid:4; + + u32 rtsrate:5; + u32 apdcfe:1; + u32 qos:1; + u32 hwseq_enable:1; + u32 userrate:1; + u32 dis_rtsfb:1; + u32 dis_datafb:1; + u32 cts2self:1; + u32 rts_en:1; + u32 hwrts_en:1; + u32 portid:1; + u32 rsvd3:3; + u32 waitdcts:1; + u32 cts2ap_en:1; + u32 txsc:2; + u32 stbc:2; + u32 txshort:1; + u32 txbw:1; + u32 rtsshort:1; + u32 rtsbw:1; + u32 rtssc:2; + u32 rtsstbc:2; + + u32 txrate:6; + u32 shortgi:1; + u32 ccxt:1; + u32 txrate_fb_lmt:5; + u32 rtsrate_fb_lmt:4; + u32 retrylmt_en:1; + u32 txretrylmt:6; + u32 usb_txaggnum:8; + + u32 txagca:5; + u32 txagcb:5; + u32 usemaxlen:1; + u32 maxaggnum:5; + u32 mcsg1maxlen:4; + u32 mcsg2maxlen:4; + u32 mcsg3maxlen:4; + u32 mcs7sgimaxlen:4; + + u32 txbuffersize:16; + u32 mcsg4maxlen:4; + u32 mcsg5maxlen:4; + u32 mcsg6maxlen:4; + u32 mcsg15sgimaxlen:4; + + u32 txbuffaddr; + u32 txbufferaddr64; + u32 nextdescaddress; + u32 nextdescaddress64; + + u32 reserve_pass_pcie_mm_limit[4]; +} __packed; + +struct rx_desc_8723e { + u32 length:14; + u32 crc32:1; + u32 icverror:1; + u32 drv_infosize:4; + u32 security:3; + u32 qos:1; + u32 shift:2; + u32 phystatus:1; + u32 swdec:1; + u32 lastseg:1; + u32 firstseg:1; + u32 eor:1; + u32 own:1; + + u32 macid:5; + u32 tid:4; + u32 hwrsvd:5; + u32 paggr:1; + u32 faggr:1; + u32 a1_fit:4; + u32 a2_fit:4; + u32 pam:1; + u32 pwr:1; + u32 moredata:1; + u32 morefrag:1; + u32 type:2; + u32 mc:1; + u32 bc:1; + + u32 seq:12; + u32 frag:4; + u32 nextpktlen:14; + u32 nextind:1; + u32 rsvd:1; + + u32 rxmcs:6; + u32 rxht:1; + u32 amsdu:1; + u32 splcp:1; + u32 bandwidth:1; + u32 htc:1; + u32 tcpchk_rpt:1; + u32 ipcchk_rpt:1; + u32 tcpchk_valid:1; + u32 hwpcerr:1; + u32 hwpcind:1; + u32 iv0:16; + + u32 iv1; + + u32 tsfl; + + u32 bufferaddress; + u32 bufferaddress64; + +} __packed; + +void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw, + struct ieee80211_hdr *hdr, u8 *pdesc_tx, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + struct sk_buff *skb, u8 hw_queue, + struct rtl_tcb_desc *ptcb_desc); +bool rtl8723ae_rx_query_desc(struct ieee80211_hw *hw, + struct rtl_stats *status, + struct ieee80211_rx_status *rx_status, + u8 *pdesc, struct sk_buff *skb); +void rtl8723ae_set_desc(u8 *pdesc, bool istx, u8 desc_name, u8 *val); +u32 rtl8723ae_get_desc(u8 *pdesc, bool istx, u8 desc_name); +void rtl8723ae_tx_polling(struct ieee80211_hw *hw, u8 hw_queue); +void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc, + bool b_firstseg, bool b_lastseg, + struct sk_buff *skb); + +#endif diff --git a/drivers/net/wireless/rtlwifi/stats.c b/drivers/net/wireless/rtlwifi/stats.c new file mode 100644 index 00000000000..8ed31744a05 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/stats.c @@ -0,0 +1,268 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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> + * + *****************************************************************************/ +#include "wifi.h" +#include "stats.h" +#include <linux/export.h> + +u8 rtl_query_rxpwrpercentage(char antpower) +{ + if ((antpower <= -100) || (antpower >= 20)) + return 0; + else if (antpower >= 0) + return 100; + else + return 100 + antpower; +} +EXPORT_SYMBOL(rtl_query_rxpwrpercentage); + +u8 rtl_evm_db_to_percentage(char value) +{ + char ret_val; + ret_val = value; + + if (ret_val >= 0) + ret_val = 0; + if (ret_val <= -33) + ret_val = -33; + ret_val = 0 - ret_val; + ret_val *= 3; + if (ret_val == 99) + ret_val = 100; + + return ret_val; +} +EXPORT_SYMBOL(rtl_evm_db_to_percentage); + +static long rtl_translate_todbm(struct ieee80211_hw *hw, + u8 signal_strength_index) +{ + long signal_power; + + signal_power = (long)((signal_strength_index + 1) >> 1); + signal_power -= 95; + return signal_power; +} + +long rtl_signal_scale_mapping(struct ieee80211_hw *hw, long currsig) +{ + long retsig; + + if (currsig >= 61 && currsig <= 100) + retsig = 90 + ((currsig - 60) / 4); + else if (currsig >= 41 && currsig <= 60) + retsig = 78 + ((currsig - 40) / 2); + else if (currsig >= 31 && currsig <= 40) + retsig = 66 + (currsig - 30); + else if (currsig >= 21 && currsig <= 30) + retsig = 54 + (currsig - 20); + else if (currsig >= 5 && currsig <= 20) + retsig = 42 + (((currsig - 5) * 2) / 3); + else if (currsig == 4) + retsig = 36; + else if (currsig == 3) + retsig = 27; + else if (currsig == 2) + retsig = 18; + else if (currsig == 1) + retsig = 9; + else + retsig = currsig; + + return retsig; +} +EXPORT_SYMBOL(rtl_signal_scale_mapping); + +static void rtl_process_ui_rssi(struct ieee80211_hw *hw, + struct rtl_stats *pstatus) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_phy *rtlphy = &(rtlpriv->phy); + u8 rfpath; + u32 last_rssi, tmpval; + + rtlpriv->stats.rssi_calculate_cnt++; + + if (rtlpriv->stats.ui_rssi.total_num++ >= PHY_RSSI_SLID_WIN_MAX) { + rtlpriv->stats.ui_rssi.total_num = PHY_RSSI_SLID_WIN_MAX; + last_rssi = rtlpriv->stats.ui_rssi.elements[ + rtlpriv->stats.ui_rssi.index]; + rtlpriv->stats.ui_rssi.total_val -= last_rssi; + } + rtlpriv->stats.ui_rssi.total_val += pstatus->signalstrength; + rtlpriv->stats.ui_rssi.elements[rtlpriv->stats.ui_rssi.index++] = + pstatus->signalstrength; + if (rtlpriv->stats.ui_rssi.index >= PHY_RSSI_SLID_WIN_MAX) + rtlpriv->stats.ui_rssi.index = 0; + tmpval = rtlpriv->stats.ui_rssi.total_val / + rtlpriv->stats.ui_rssi.total_num; + rtlpriv->stats.signal_strength = rtl_translate_todbm(hw, + (u8) tmpval); + pstatus->rssi = rtlpriv->stats.signal_strength; + + if (pstatus->is_cck) + return; + + for (rfpath = RF90_PATH_A; rfpath < rtlphy->num_total_rfpath; + rfpath++) { + if (rtlpriv->stats.rx_rssi_percentage[rfpath] == 0) { + rtlpriv->stats.rx_rssi_percentage[rfpath] = + pstatus->rx_mimo_signalstrength[rfpath]; + + } + if (pstatus->rx_mimo_signalstrength[rfpath] > + rtlpriv->stats.rx_rssi_percentage[rfpath]) { + rtlpriv->stats.rx_rssi_percentage[rfpath] = + ((rtlpriv->stats.rx_rssi_percentage[rfpath] * + (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_mimo_signalstrength[rfpath])) / + (RX_SMOOTH_FACTOR); + rtlpriv->stats.rx_rssi_percentage[rfpath] = + rtlpriv->stats.rx_rssi_percentage[rfpath] + 1; + } else { + rtlpriv->stats.rx_rssi_percentage[rfpath] = + ((rtlpriv->stats.rx_rssi_percentage[rfpath] * + (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_mimo_signalstrength[rfpath])) / + (RX_SMOOTH_FACTOR); + } + } +} + +static void rtl_update_rxsignalstatistics(struct ieee80211_hw *hw, + struct rtl_stats *pstatus) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + int weighting = 0; + + if (rtlpriv->stats.recv_signal_power == 0) + rtlpriv->stats.recv_signal_power = pstatus->recvsignalpower; + if (pstatus->recvsignalpower > rtlpriv->stats.recv_signal_power) + weighting = 5; + else if (pstatus->recvsignalpower < rtlpriv->stats.recv_signal_power) + weighting = (-5); + rtlpriv->stats.recv_signal_power = (rtlpriv->stats.recv_signal_power * + 5 + pstatus->recvsignalpower + weighting) / 6; +} + +static void rtl_process_pwdb(struct ieee80211_hw *hw, struct rtl_stats *pstatus) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + struct rtl_sta_info *drv_priv = NULL; + struct ieee80211_sta *sta = NULL; + long undec_sm_pwdb; + + rcu_read_lock(); + if (rtlpriv->mac80211.opmode != NL80211_IFTYPE_STATION) + sta = rtl_find_sta(hw, pstatus->psaddr); + + /* adhoc or ap mode */ + if (sta) { + drv_priv = (struct rtl_sta_info *) sta->drv_priv; + undec_sm_pwdb = drv_priv->rssi_stat.undec_sm_pwdb; + } else { + undec_sm_pwdb = rtlpriv->dm.undec_sm_pwdb; + } + + if (undec_sm_pwdb < 0) + undec_sm_pwdb = pstatus->rx_pwdb_all; + if (pstatus->rx_pwdb_all > (u32) undec_sm_pwdb) { + undec_sm_pwdb = (((undec_sm_pwdb) * + (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); + undec_sm_pwdb = undec_sm_pwdb + 1; + } else { + undec_sm_pwdb = (((undec_sm_pwdb) * (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_pwdb_all)) / (RX_SMOOTH_FACTOR); + } + + if (sta) { + drv_priv->rssi_stat.undec_sm_pwdb = undec_sm_pwdb; + } else { + rtlpriv->dm.undec_sm_pwdb = undec_sm_pwdb; + } + rcu_read_unlock(); + + rtl_update_rxsignalstatistics(hw, pstatus); +} + +static void rtl_process_ui_link_quality(struct ieee80211_hw *hw, + struct rtl_stats *pstatus) +{ + struct rtl_priv *rtlpriv = rtl_priv(hw); + u32 last_evm, n_stream, tmpval; + + if (pstatus->signalquality == 0) + return; + + if (rtlpriv->stats.ui_link_quality.total_num++ >= + PHY_LINKQUALITY_SLID_WIN_MAX) { + rtlpriv->stats.ui_link_quality.total_num = + PHY_LINKQUALITY_SLID_WIN_MAX; + last_evm = rtlpriv->stats.ui_link_quality.elements[ + rtlpriv->stats.ui_link_quality.index]; + rtlpriv->stats.ui_link_quality.total_val -= last_evm; + } + rtlpriv->stats.ui_link_quality.total_val += pstatus->signalquality; + rtlpriv->stats.ui_link_quality.elements[ + rtlpriv->stats.ui_link_quality.index++] = + pstatus->signalquality; + if (rtlpriv->stats.ui_link_quality.index >= + PHY_LINKQUALITY_SLID_WIN_MAX) + rtlpriv->stats.ui_link_quality.index = 0; + tmpval = rtlpriv->stats.ui_link_quality.total_val / + rtlpriv->stats.ui_link_quality.total_num; + rtlpriv->stats.signal_quality = tmpval; + rtlpriv->stats.last_sigstrength_inpercent = tmpval; + for (n_stream = 0; n_stream < 2; n_stream++) { + if (pstatus->rx_mimo_sig_qual[n_stream] != -1) { + if (rtlpriv->stats.rx_evm_percentage[n_stream] == 0) { + rtlpriv->stats.rx_evm_percentage[n_stream] = + pstatus->rx_mimo_sig_qual[n_stream]; + } + rtlpriv->stats.rx_evm_percentage[n_stream] = + ((rtlpriv->stats.rx_evm_percentage[n_stream] + * (RX_SMOOTH_FACTOR - 1)) + + (pstatus->rx_mimo_sig_qual[n_stream] * 1)) / + (RX_SMOOTH_FACTOR); + } + } +} + +void rtl_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, + struct rtl_stats *pstatus) +{ + + if (!pstatus->packet_matchbssid) + return; + + rtl_process_ui_rssi(hw, pstatus); + rtl_process_pwdb(hw, pstatus); + rtl_process_ui_link_quality(hw, pstatus); +} +EXPORT_SYMBOL(rtl_process_phyinfo); diff --git a/drivers/net/wireless/rtlwifi/stats.h b/drivers/net/wireless/rtlwifi/stats.h new file mode 100644 index 00000000000..0dbdc520383 --- /dev/null +++ b/drivers/net/wireless/rtlwifi/stats.h @@ -0,0 +1,46 @@ +/****************************************************************************** + * + * Copyright(c) 2009-2012 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 __RTL_STATS_H__ +#define __RTL_STATS_H__ + +#define PHY_RSSI_SLID_WIN_MAX 100 +#define PHY_LINKQUALITY_SLID_WIN_MAX 20 +#define PHY_BEACON_RSSI_SLID_WIN_MAX 10 + +/* Rx smooth factor */ +#define RX_SMOOTH_FACTOR 20 + +u8 rtl_query_rxpwrpercentage(char antpower); +u8 rtl_evm_db_to_percentage(char value); +long rtl_signal_scale_mapping(struct ieee80211_hw *hw, long currsig); +void rtl_process_phyinfo(struct ieee80211_hw *hw, u8 *buffer, + struct rtl_stats *pstatus); + +#endif diff --git a/drivers/net/wireless/rtlwifi/wifi.h b/drivers/net/wireless/rtlwifi/wifi.h index 6794b688dd7..21a5f4f4a13 100644 --- a/drivers/net/wireless/rtlwifi/wifi.h +++ b/drivers/net/wireless/rtlwifi/wifi.h @@ -350,6 +350,11 @@ enum rt_oem_id { RT_CID_819x_WNC_COREGA = 31, RT_CID_819x_Foxcoon = 32, RT_CID_819x_DELL = 33, + RT_CID_819x_PRONETS = 34, + RT_CID_819x_Edimax_ASUS = 35, + RT_CID_NETGEAR = 36, + RT_CID_PLANEX = 37, + RT_CID_CC_C = 38, }; enum hw_descs { @@ -505,6 +510,7 @@ enum rtl_var_map { RTL_IMR_ROK, /*Receive DMA OK Interrupt */ RTL_IBSS_INT_MASKS, /*(RTL_IMR_BcnInt | RTL_IMR_TBDOK | * RTL_IMR_TBDER) */ + RTL_IMR_C2HCMD, /*fw interrupt*/ /*CCK Rates, TxHT = 0 */ RTL_RC_CCK_RATE1M, @@ -661,6 +667,11 @@ enum ba_action { ACT_DELBA = 2, }; +enum rt_polarity_ctl { + RT_POLARITY_LOW_ACT = 0, + RT_POLARITY_HIGH_ACT = 1, +}; + struct octet_string { u8 *octet; u16 length; @@ -903,6 +914,8 @@ struct rtl_phy { u8 num_total_rfpath; struct phy_parameters hwparam_tables[MAX_TAB]; u16 rf_pathmap; + + enum rt_polarity_ctl polarity_ctl; }; #define MAX_TID_COUNT 9 @@ -1042,13 +1055,64 @@ struct rtl_mac { /*QOS & EDCA */ struct ieee80211_tx_queue_params edca_param[RTL_MAC80211_NUM_QUEUE]; struct rtl_qos_parameters ac[AC_MAX]; + + /* counters */ + u64 last_txok_cnt; + u64 last_rxok_cnt; + u32 last_bt_edca_ul; + u32 last_bt_edca_dl; +}; + +struct btdm_8723 { + bool all_off; + bool agc_table_en; + bool adc_back_off_on; + bool b2_ant_hid_en; + bool low_penalty_rate_adaptive; + bool rf_rx_lpf_shrink; + bool reject_aggre_pkt; + bool tra_tdma_on; + u8 tra_tdma_nav; + u8 tra_tdma_ant; + bool tdma_on; + u8 tdma_ant; + u8 tdma_nav; + u8 tdma_dac_swing; + u8 fw_dac_swing_lvl; + bool ps_tdma_on; + u8 ps_tdma_byte[5]; + bool pta_on; + u32 val_0x6c0; + u32 val_0x6c8; + u32 val_0x6cc; + bool sw_dac_swing_on; + u32 sw_dac_swing_lvl; + u32 wlan_act_hi; + u32 wlan_act_lo; + u32 bt_retry_index; + bool dec_bt_pwr; + bool ignore_wlan_act; +}; + +struct bt_coexist_8723 { + u32 high_priority_tx; + u32 high_priority_rx; + u32 low_priority_tx; + u32 low_priority_rx; + u8 c2h_bt_info; + bool c2h_bt_info_req_sent; + bool c2h_bt_inquiry_page; + u32 bt_inq_page_start_time; + u8 bt_retry_cnt; + u8 c2h_bt_info_original; + u8 bt_inquiry_page_cnt; + struct btdm_8723 btdm; }; struct rtl_hal { struct ieee80211_hw *hw; - + struct bt_coexist_8723 hal_coex_8723; bool up_first_time; - bool first_init; bool being_init_adapter; bool bbrf_ready; @@ -1312,6 +1376,7 @@ struct rtl_ps_ctl { }; struct rtl_stats { + u8 psaddr[ETH_ALEN]; u32 mac_time[2]; s8 rssi; u8 signal; @@ -1503,6 +1568,7 @@ struct rtl_hal_ops { 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); + void (*c2h_command_handle) (struct ieee80211_hw *hw); void (*bt_wifi_media_status_notify) (struct ieee80211_hw *hw, bool mstate); void (*bt_coex_off_before_lps) (struct ieee80211_hw *hw); @@ -1784,9 +1850,22 @@ struct rtl_priv { struct dig_t dm_digtable; struct ps_t dm_pstable; - /* data buffer pointer for USB reads */ - __le32 *usb_data; - int usb_data_index; + /* section shared by individual drivers */ + union { + struct { /* data buffer pointer for USB reads */ + __le32 *usb_data; + int usb_data_index; + bool initialized; + }; + struct { /* section for 8723ae */ + bool reg_init; /* true if regs saved */ + u32 reg_874; + u32 reg_c70; + u32 reg_85c; + u32 reg_a74; + bool bt_operation_on; + }; + }; /*This must be the last item so that it points to the data allocated @@ -1818,6 +1897,7 @@ enum bt_co_type { BT_CSR_BC4 = 3, BT_CSR_BC8 = 4, BT_RTL8756 = 5, + BT_RTL8723A = 6, }; enum bt_cur_state { @@ -1876,13 +1956,27 @@ struct bt_coexist_info { bool fw_coexist_all_off; bool sw_coexist_all_off; - u32 current_state; + bool hw_coexist_all_off; + u32 cstate; u32 previous_state; + u32 cstate_h; + u32 previous_state_h; + u8 bt_pre_rssi_state; + u8 bt_pre_rssi_state1; u8 reg_bt_iso; u8 reg_bt_sco; - + bool balance_on; + u8 bt_active_zero_cnt; + bool cur_bt_disabled; + bool pre_bt_disabled; + + u8 bt_profile_case; + u8 bt_profile_action; + bool bt_busy; + bool hold_for_bt_operation; + u8 lps_counter; }; diff --git a/drivers/net/wireless/ti/wl1251/rx.c b/drivers/net/wireless/ti/wl1251/rx.c index 6af35265c90..23289d49dd3 100644 --- a/drivers/net/wireless/ti/wl1251/rx.c +++ b/drivers/net/wireless/ti/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_MACTIME_MPDU; + status->flag |= RX_FLAG_MACTIME_START; if (desc->flags & RX_DESC_ENCRYPTION_MASK) { status->flag |= RX_FLAG_IV_STRIPPED | RX_FLAG_MMIC_STRIPPED; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 25530c8760c..4f1a05b92d2 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -677,7 +677,7 @@ static void wl12xx_get_vif_count(struct ieee80211_hw *hw, memset(data, 0, sizeof(*data)); data->cur_vif = cur_vif; - ieee80211_iterate_active_interfaces(hw, + ieee80211_iterate_active_interfaces(hw, IEEE80211_IFACE_ITER_RESUME_ALL, wl12xx_vif_count_iter, data); } @@ -3791,7 +3791,7 @@ static void wl1271_bss_info_changed_ap(struct wl1271 *wl, /* Handle HT information change */ if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { + (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { ret = wl1271_acx_set_ht_information(wl, wlvif, bss_conf->ht_operation_mode); if (ret < 0) { @@ -3905,7 +3905,8 @@ sta_not_found: u32 rates; int ieoffset; wlvif->aid = bss_conf->aid; - wlvif->channel_type = bss_conf->channel_type; + wlvif->channel_type = + cfg80211_get_chandef_type(&bss_conf->chandef); wlvif->beacon_int = bss_conf->beacon_int; do_join = true; set_assoc = true; @@ -4071,7 +4072,7 @@ sta_not_found: /* Handle new association with HT. Do this after join. */ if (sta_exists) { if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { + (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { ret = wl1271_acx_set_ht_capabilities(wl, &sta_ht_cap, true, @@ -4098,7 +4099,7 @@ sta_not_found: /* Handle HT information change. Done after join. */ if ((changed & BSS_CHANGED_HT) && - (bss_conf->channel_type != NL80211_CHAN_NO_HT)) { + (bss_conf->chandef.width != NL80211_CHAN_WIDTH_20_NOHT)) { ret = wl1271_acx_set_ht_information(wl, wlvif, bss_conf->ht_operation_mode); if (ret < 0) { diff --git a/drivers/nfc/pn544/i2c.c b/drivers/nfc/pn544/i2c.c index fb430d88235..7da9071b68b 100644 --- a/drivers/nfc/pn544/i2c.c +++ b/drivers/nfc/pn544/i2c.c @@ -26,7 +26,7 @@ #include <linux/interrupt.h> #include <linux/delay.h> -#include <linux/nfc/pn544.h> +#include <linux/platform_data/pn544.h> #include <net/nfc/hci.h> #include <net/nfc/llc.h> diff --git a/drivers/ptp/ptp_chardev.c b/drivers/ptp/ptp_chardev.c index 4f8ae8057a7..34a0c607318 100644 --- a/drivers/ptp/ptp_chardev.c +++ b/drivers/ptp/ptp_chardev.c @@ -21,6 +21,7 @@ #include <linux/posix-clock.h> #include <linux/poll.h> #include <linux/sched.h> +#include <linux/slab.h> #include "ptp_private.h" @@ -33,7 +34,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) { struct ptp_clock_caps caps; struct ptp_clock_request req; - struct ptp_sys_offset sysoff; + struct ptp_sys_offset *sysoff = NULL; struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct ptp_clock_info *ops = ptp->info; struct ptp_clock_time *pct; @@ -93,17 +94,22 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) break; case PTP_SYS_OFFSET: - if (copy_from_user(&sysoff, (void __user *)arg, - sizeof(sysoff))) { + sysoff = kmalloc(sizeof(*sysoff), GFP_KERNEL); + if (!sysoff) { + err = -ENOMEM; + break; + } + if (copy_from_user(sysoff, (void __user *)arg, + sizeof(*sysoff))) { err = -EFAULT; break; } - if (sysoff.n_samples > PTP_MAX_SAMPLES) { + if (sysoff->n_samples > PTP_MAX_SAMPLES) { err = -EINVAL; break; } - pct = &sysoff.ts[0]; - for (i = 0; i < sysoff.n_samples; i++) { + pct = &sysoff->ts[0]; + for (i = 0; i < sysoff->n_samples; i++) { getnstimeofday(&ts); pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; @@ -116,7 +122,7 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) getnstimeofday(&ts); pct->sec = ts.tv_sec; pct->nsec = ts.tv_nsec; - if (copy_to_user((void __user *)arg, &sysoff, sizeof(sysoff))) + if (copy_to_user((void __user *)arg, sysoff, sizeof(*sysoff))) err = -EFAULT; break; @@ -124,6 +130,8 @@ long ptp_ioctl(struct posix_clock *pc, unsigned int cmd, unsigned long arg) err = -ENOTTY; break; } + + kfree(sysoff); return err; } @@ -136,20 +144,23 @@ unsigned int ptp_poll(struct posix_clock *pc, struct file *fp, poll_table *wait) return queue_cnt(&ptp->tsevq) ? POLLIN : 0; } +#define EXTTS_BUFSIZE (PTP_BUF_TIMESTAMPS * sizeof(struct ptp_extts_event)) + ssize_t ptp_read(struct posix_clock *pc, uint rdflags, char __user *buf, size_t cnt) { struct ptp_clock *ptp = container_of(pc, struct ptp_clock, clock); struct timestamp_event_queue *queue = &ptp->tsevq; - struct ptp_extts_event event[PTP_BUF_TIMESTAMPS]; + struct ptp_extts_event *event; unsigned long flags; size_t qcnt, i; + int result; if (cnt % sizeof(struct ptp_extts_event) != 0) return -EINVAL; - if (cnt > sizeof(event)) - cnt = sizeof(event); + if (cnt > EXTTS_BUFSIZE) + cnt = EXTTS_BUFSIZE; cnt = cnt / sizeof(struct ptp_extts_event); @@ -167,6 +178,12 @@ ssize_t ptp_read(struct posix_clock *pc, return -ENODEV; } + event = kmalloc(EXTTS_BUFSIZE, GFP_KERNEL); + if (!event) { + mutex_unlock(&ptp->tsevq_mux); + return -ENOMEM; + } + spin_lock_irqsave(&queue->lock, flags); qcnt = queue_cnt(queue); @@ -185,8 +202,10 @@ ssize_t ptp_read(struct posix_clock *pc, mutex_unlock(&ptp->tsevq_mux); + result = cnt; if (copy_to_user(buf, event, cnt)) - return -EFAULT; + result = -EFAULT; - return cnt; + kfree(event); + return result; } diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index f87d7e8964b..4e0d0c3734b 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -539,25 +539,25 @@ static void insert_char(struct vc_data *vc, unsigned int nr) { unsigned short *p = (unsigned short *) vc->vc_pos; - scr_memmovew(p + nr, p, vc->vc_cols - vc->vc_x); + scr_memmovew(p + nr, p, (vc->vc_cols - vc->vc_x) * 2); scr_memsetw(p, vc->vc_video_erase_char, nr * 2); vc->vc_need_wrap = 0; if (DO_UPDATE(vc)) do_update_region(vc, (unsigned long) p, - (vc->vc_cols - vc->vc_x) / 2 + 1); + vc->vc_cols - vc->vc_x); } static void delete_char(struct vc_data *vc, unsigned int nr) { unsigned short *p = (unsigned short *) vc->vc_pos; - scr_memcpyw(p, p + nr, vc->vc_cols - vc->vc_x - nr); + scr_memcpyw(p, p + nr, (vc->vc_cols - vc->vc_x - nr) * 2); scr_memsetw(p + vc->vc_cols - vc->vc_x - nr, vc->vc_video_erase_char, nr * 2); vc->vc_need_wrap = 0; if (DO_UPDATE(vc)) do_update_region(vc, (unsigned long) p, - (vc->vc_cols - vc->vc_x) / 2); + vc->vc_cols - vc->vc_x); } static int softcursor_original; diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c index ef8f5988f85..5a3d0f1eaf9 100644 --- a/drivers/vhost/vhost.c +++ b/drivers/vhost/vhost.c @@ -1049,7 +1049,7 @@ static int translate_desc(struct vhost_dev *dev, u64 addr, u32 len, } _iov = iov + ret; size = reg->memory_size - addr + reg->guest_phys_addr; - _iov->iov_len = min((u64)len, size); + _iov->iov_len = min((u64)len - s, size); _iov->iov_base = (void __user *)(unsigned long) (reg->userspace_addr + addr - reg->guest_phys_addr); s += size; diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 51ea267d444..3e3422f7f0a 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -228,6 +228,8 @@ static void requeue_io(struct inode *inode, struct bdi_writeback *wb) static void inode_sync_complete(struct inode *inode) { inode->i_state &= ~I_SYNC; + /* If inode is clean an unused, put it into LRU now... */ + inode_add_lru(inode); /* Waiters must see I_SYNC cleared before being woken up */ smp_mb(); wake_up_bit(&inode->i_state, __I_SYNC); diff --git a/fs/inode.c b/fs/inode.c index b03c7195724..64999f14415 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -408,6 +408,19 @@ static void inode_lru_list_add(struct inode *inode) spin_unlock(&inode->i_sb->s_inode_lru_lock); } +/* + * Add inode to LRU if needed (inode is unused and clean). + * + * Needs inode->i_lock held. + */ +void inode_add_lru(struct inode *inode) +{ + if (!(inode->i_state & (I_DIRTY | I_SYNC | I_FREEING | I_WILL_FREE)) && + !atomic_read(&inode->i_count) && inode->i_sb->s_flags & MS_ACTIVE) + inode_lru_list_add(inode); +} + + static void inode_lru_list_del(struct inode *inode) { spin_lock(&inode->i_sb->s_inode_lru_lock); @@ -1390,8 +1403,7 @@ static void iput_final(struct inode *inode) if (!drop && (sb->s_flags & MS_ACTIVE)) { inode->i_state |= I_REFERENCED; - if (!(inode->i_state & (I_DIRTY|I_SYNC))) - inode_lru_list_add(inode); + inode_add_lru(inode); spin_unlock(&inode->i_lock); return; } diff --git a/fs/internal.h b/fs/internal.h index 916b7cbf3e3..2f6af7f645e 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -110,6 +110,7 @@ extern int open_check_o_direct(struct file *f); * inode.c */ extern spinlock_t inode_sb_list_lock; +extern void inode_add_lru(struct inode *inode); /* * fs-writeback.c diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c index 78b7f84241d..7f5120bf0ec 100644 --- a/fs/jbd/transaction.c +++ b/fs/jbd/transaction.c @@ -1961,7 +1961,9 @@ retry: spin_unlock(&journal->j_list_lock); jbd_unlock_bh_state(bh); spin_unlock(&journal->j_state_lock); + unlock_buffer(bh); log_wait_commit(journal, tid); + lock_buffer(bh); goto retry; } /* diff --git a/fs/proc/base.c b/fs/proc/base.c index 3c231adf845..9e28356a959 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -1877,8 +1877,9 @@ static struct dentry *proc_map_files_lookup(struct inode *dir, if (!vma) goto out_no_vma; - result = proc_map_files_instantiate(dir, dentry, task, - (void *)(unsigned long)vma->vm_file->f_mode); + if (vma->vm_file) + result = proc_map_files_instantiate(dir, dentry, task, + (void *)(unsigned long)vma->vm_file->f_mode); out_no_vma: up_read(&mm->mmap_sem); diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index fd15d982970..93b1e091b1e 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -157,6 +157,7 @@ struct bcma_host_ops { /* Chip IDs of SoCs */ #define BCMA_CHIP_ID_BCM4706 0x5300 +#define BCMA_PKG_ID_BCM4706L 1 #define BCMA_CHIP_ID_BCM4716 0x4716 #define BCMA_PKG_ID_BCM4716 8 #define BCMA_PKG_ID_BCM4717 9 @@ -166,7 +167,11 @@ struct bcma_host_ops { #define BCMA_CHIP_ID_BCM4749 0x4749 #define BCMA_CHIP_ID_BCM5356 0x5356 #define BCMA_CHIP_ID_BCM5357 0x5357 +#define BCMA_PKG_ID_BCM5358 9 +#define BCMA_PKG_ID_BCM47186 10 +#define BCMA_PKG_ID_BCM5357 11 #define BCMA_CHIP_ID_BCM53572 53572 +#define BCMA_PKG_ID_BCM47188 9 struct bcma_device { struct bcma_bus *bus; diff --git a/include/linux/bug.h b/include/linux/bug.h index aaac4bba6f5..b1cf40de847 100644 --- a/include/linux/bug.h +++ b/include/linux/bug.h @@ -15,6 +15,7 @@ struct pt_regs; #define BUILD_BUG_ON_NOT_POWER_OF_2(n) #define BUILD_BUG_ON_ZERO(e) (0) #define BUILD_BUG_ON_NULL(e) ((void*)0) +#define BUILD_BUG_ON_INVALID(e) (0) #define BUILD_BUG_ON(condition) #define BUILD_BUG() (0) #else /* __CHECKER__ */ diff --git a/include/linux/gfp.h b/include/linux/gfp.h index 02c1c9710be..d0a79678f16 100644 --- a/include/linux/gfp.h +++ b/include/linux/gfp.h @@ -31,6 +31,7 @@ struct vm_area_struct; #define ___GFP_THISNODE 0x40000u #define ___GFP_RECLAIMABLE 0x80000u #define ___GFP_NOTRACK 0x200000u +#define ___GFP_NO_KSWAPD 0x400000u #define ___GFP_OTHER_NODE 0x800000u #define ___GFP_WRITE 0x1000000u @@ -85,6 +86,7 @@ struct vm_area_struct; #define __GFP_RECLAIMABLE ((__force gfp_t)___GFP_RECLAIMABLE) /* Page is reclaimable */ #define __GFP_NOTRACK ((__force gfp_t)___GFP_NOTRACK) /* Don't track with kmemcheck */ +#define __GFP_NO_KSWAPD ((__force gfp_t)___GFP_NO_KSWAPD) #define __GFP_OTHER_NODE ((__force gfp_t)___GFP_OTHER_NODE) /* On behalf of other node */ #define __GFP_WRITE ((__force gfp_t)___GFP_WRITE) /* Allocator intends to dirty page */ @@ -114,7 +116,8 @@ struct vm_area_struct; __GFP_MOVABLE) #define GFP_IOFS (__GFP_IO | __GFP_FS) #define GFP_TRANSHUGE (GFP_HIGHUSER_MOVABLE | __GFP_COMP | \ - __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN) + __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_NOWARN | \ + __GFP_NO_KSWAPD) #ifdef CONFIG_NUMA #define GFP_THISNODE (__GFP_THISNODE | __GFP_NOWARN | __GFP_NORETRY) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 85764a90073..f9c5a787d35 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -131,6 +131,8 @@ #define IEEE80211_MAX_MESH_ID_LEN 32 +#define IEEE80211_NUM_TIDS 16 + #define IEEE80211_QOS_CTL_LEN 2 /* 1d tag mask */ #define IEEE80211_QOS_CTL_TAG1D_MASK 0x0007 @@ -666,6 +668,21 @@ struct ieee80211_meshconf_ie { } __attribute__ ((packed)); /** + * enum mesh_config_capab_flags - Mesh Configuration IE capability field flags + * + * @IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish + * additional mesh peerings with other mesh STAs + * @IEEE80211_MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs + * @IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure + * is ongoing + */ +enum mesh_config_capab_flags { + IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS = 0x01, + IEEE80211_MESHCONF_CAPAB_FORWARDING = 0x08, + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING = 0x20, +}; + +/** * struct ieee80211_rann_ie * * This structure refers to "Root Announcement information element" @@ -905,6 +922,38 @@ struct ieee80211_tdls_data { } u; } __packed; +/* + * Peer-to-Peer IE attribute related definitions. + */ +/** + * enum ieee80211_p2p_attr_id - identifies type of peer-to-peer attribute. + */ +enum ieee80211_p2p_attr_id { + IEEE80211_P2P_ATTR_STATUS = 0, + IEEE80211_P2P_ATTR_MINOR_REASON, + IEEE80211_P2P_ATTR_CAPABILITY, + IEEE80211_P2P_ATTR_DEVICE_ID, + IEEE80211_P2P_ATTR_GO_INTENT, + IEEE80211_P2P_ATTR_GO_CONFIG_TIMEOUT, + IEEE80211_P2P_ATTR_LISTEN_CHANNEL, + IEEE80211_P2P_ATTR_GROUP_BSSID, + IEEE80211_P2P_ATTR_EXT_LISTEN_TIMING, + IEEE80211_P2P_ATTR_INTENDED_IFACE_ADDR, + IEEE80211_P2P_ATTR_MANAGABILITY, + IEEE80211_P2P_ATTR_CHANNEL_LIST, + IEEE80211_P2P_ATTR_ABSENCE_NOTICE, + IEEE80211_P2P_ATTR_DEVICE_INFO, + IEEE80211_P2P_ATTR_GROUP_INFO, + IEEE80211_P2P_ATTR_GROUP_ID, + IEEE80211_P2P_ATTR_INTERFACE, + IEEE80211_P2P_ATTR_OPER_CHANNEL, + IEEE80211_P2P_ATTR_INVITE_FLAGS, + /* 19 - 220: Reserved */ + IEEE80211_P2P_ATTR_VENDOR_SPECIFIC = 221, + + IEEE80211_P2P_ATTR_MAX +}; + /** * struct ieee80211_bar - HT Block Ack Request * @@ -1114,11 +1163,13 @@ struct ieee80211_ht_operation { * STA can receive. Rate expressed in units of 1 Mbps. * If this field is 0 this value should not be used to * consider the highest RX data rate supported. + * The top 3 bits of this field are reserved. * @tx_mcs_map: TX MCS map 2 bits for each stream, total 8 streams * @tx_highest: Indicates highest long GI VHT PPDU data rate * STA can transmit. Rate expressed in units of 1 Mbps. * If this field is 0 this value should not be used to * consider the highest TX data rate supported. + * The top 3 bits of this field are reserved. */ struct ieee80211_vht_mcs_info { __le16 rx_mcs_map; @@ -1128,6 +1179,27 @@ struct ieee80211_vht_mcs_info { } __packed; /** + * enum ieee80211_vht_mcs_support - VHT MCS support definitions + * @IEEE80211_VHT_MCS_SUPPORT_0_7: MCSes 0-7 are supported for the + * number of streams + * @IEEE80211_VHT_MCS_SUPPORT_0_8: MCSes 0-8 are supported + * @IEEE80211_VHT_MCS_SUPPORT_0_9: MCSes 0-9 are supported + * @IEEE80211_VHT_MCS_NOT_SUPPORTED: This number of streams isn't supported + * + * These definitions are used in each 2-bit subfield of the @rx_mcs_map + * and @tx_mcs_map fields of &struct ieee80211_vht_mcs_info, which are + * both split into 8 subfields by number of streams. These values indicate + * which MCSes are supported for the number of streams the value appears + * for. + */ +enum ieee80211_vht_mcs_support { + IEEE80211_VHT_MCS_SUPPORT_0_7 = 0, + IEEE80211_VHT_MCS_SUPPORT_0_8 = 1, + IEEE80211_VHT_MCS_SUPPORT_0_9 = 2, + IEEE80211_VHT_MCS_NOT_SUPPORTED = 3, +}; + +/** * struct ieee80211_vht_cap - VHT capabilities * * This structure is the "VHT capabilities element" as diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 5e11905a4f0..12729e966dc 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -364,20 +364,22 @@ static inline struct raw6_sock *raw6_sk(const struct sock *sk) #define inet_v6_ipv6only(__sk) 0 #endif /* IS_ENABLED(CONFIG_IPV6) */ -#define INET6_MATCH(__sk, __net, __hash, __saddr, __daddr, __ports, __dif)\ - (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \ - ((*((__portpair *)&(inet_sk(__sk)->inet_dport))) == (__ports)) && \ - ((__sk)->sk_family == AF_INET6) && \ - ipv6_addr_equal(&inet6_sk(__sk)->daddr, (__saddr)) && \ - ipv6_addr_equal(&inet6_sk(__sk)->rcv_saddr, (__daddr)) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) - -#define INET6_TW_MATCH(__sk, __net, __hash, __saddr, __daddr, __ports, __dif) \ - (((__sk)->sk_hash == (__hash)) && sock_net((__sk)) == (__net) && \ - (*((__portpair *)&(inet_twsk(__sk)->tw_dport)) == (__ports)) && \ - ((__sk)->sk_family == PF_INET6) && \ - (ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_daddr, (__saddr))) && \ - (ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_rcv_saddr, (__daddr))) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) +#define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif) \ + ((inet_sk(__sk)->inet_portpair == (__ports)) && \ + ((__sk)->sk_family == AF_INET6) && \ + ipv6_addr_equal(&inet6_sk(__sk)->daddr, (__saddr)) && \ + ipv6_addr_equal(&inet6_sk(__sk)->rcv_saddr, (__daddr)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) + +#define INET6_TW_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif) \ + ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ + ((__sk)->sk_family == AF_INET6) && \ + ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_daddr, (__saddr)) && \ + ipv6_addr_equal(&inet6_twsk(__sk)->tw_v6_rcv_saddr, (__daddr)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) #endif /* _IPV6_H */ diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e46c830c88d..18c5dc98f6d 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1567,6 +1567,8 @@ extern int call_netdevice_notifiers(unsigned long val, struct net_device *dev); extern rwlock_t dev_base_lock; /* Device list lock */ +extern seqlock_t devnet_rename_seq; /* Device rename lock */ + #define for_each_netdev(net, d) \ list_for_each_entry(d, &(net)->dev_base_head, dev_list) @@ -2151,16 +2153,10 @@ extern void dev_kfree_skb_any(struct sk_buff *skb); extern int netif_rx(struct sk_buff *skb); extern int netif_rx_ni(struct sk_buff *skb); extern int netif_receive_skb(struct sk_buff *skb); -extern gro_result_t dev_gro_receive(struct napi_struct *napi, - struct sk_buff *skb); -extern gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb); extern gro_result_t napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb); extern void napi_gro_flush(struct napi_struct *napi, bool flush_old); extern struct sk_buff * napi_get_frags(struct napi_struct *napi); -extern gro_result_t napi_frags_finish(struct napi_struct *napi, - struct sk_buff *skb, - gro_result_t ret); extern gro_result_t napi_gro_frags(struct napi_struct *napi); static inline void napi_free_frags(struct napi_struct *napi) diff --git a/include/linux/netfilter_ipv6/ip6_tables.h b/include/linux/netfilter_ipv6/ip6_tables.h index 5f84c6229dc..610208b18c0 100644 --- a/include/linux/netfilter_ipv6/ip6_tables.h +++ b/include/linux/netfilter_ipv6/ip6_tables.h @@ -47,15 +47,6 @@ ip6t_ext_hdr(u8 nexthdr) (nexthdr == IPPROTO_DSTOPTS); } -enum { - IP6T_FH_F_FRAG = (1 << 0), - IP6T_FH_F_AUTH = (1 << 1), -}; - -/* find specified header and get offset to it */ -extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, - int target, unsigned short *fragoff, int *fragflg); - #ifdef CONFIG_COMPAT #include <net/compat.h> diff --git a/include/linux/nfc/pn544.h b/include/linux/nfc/pn544.h deleted file mode 100644 index 9890bbaf432..00000000000 --- a/include/linux/nfc/pn544.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Driver include for the PN544 NFC chip. - * - * Copyright (C) Nokia Corporation - * - * Author: Jari Vanhala <ext-jari.vanhala@nokia.com> - * Contact: Matti Aaltoenn <matti.j.aaltonen@nokia.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef _PN544_H_ -#define _PN544_H_ - -#include <linux/i2c.h> - -#define PN544_DRIVER_NAME "pn544" -#define PN544_MAXWINDOW_SIZE 7 -#define PN544_WINDOW_SIZE 4 -#define PN544_RETRIES 10 -#define PN544_MAX_I2C_TRANSFER 0x0400 -#define PN544_MSG_MAX_SIZE 0x21 /* at normal HCI mode */ - -/* ioctl */ -#define PN544_CHAR_BASE 'P' -#define PN544_IOR(num, dtype) _IOR(PN544_CHAR_BASE, num, dtype) -#define PN544_IOW(num, dtype) _IOW(PN544_CHAR_BASE, num, dtype) -#define PN544_GET_FW_MODE PN544_IOW(1, unsigned int) -#define PN544_SET_FW_MODE PN544_IOW(2, unsigned int) -#define PN544_GET_DEBUG PN544_IOW(3, unsigned int) -#define PN544_SET_DEBUG PN544_IOW(4, unsigned int) - -/* Timing restrictions (ms) */ -#define PN544_RESETVEN_TIME 30 /* 7 */ -#define PN544_PVDDVEN_TIME 0 -#define PN544_VBATVEN_TIME 0 -#define PN544_GPIO4VEN_TIME 0 -#define PN544_WAKEUP_ACK 5 -#define PN544_WAKEUP_GUARD (PN544_WAKEUP_ACK + 1) -#define PN544_INACTIVITY_TIME 1000 -#define PN544_INTERFRAME_DELAY 200 /* us */ -#define PN544_BAUDRATE_CHANGE 150 /* us */ - -/* Debug bits */ -#define PN544_DEBUG_BUF 0x01 -#define PN544_DEBUG_READ 0x02 -#define PN544_DEBUG_WRITE 0x04 -#define PN544_DEBUG_IRQ 0x08 -#define PN544_DEBUG_CALLS 0x10 -#define PN544_DEBUG_MODE 0x20 - -/* Normal (HCI) mode */ -#define PN544_LLC_HCI_OVERHEAD 3 /* header + crc (to length) */ -#define PN544_LLC_MIN_SIZE (1 + PN544_LLC_HCI_OVERHEAD) /* length + */ -#define PN544_LLC_MAX_DATA (PN544_MSG_MAX_SIZE - 2) -#define PN544_LLC_MAX_HCI_SIZE (PN544_LLC_MAX_DATA - 2) - -struct pn544_llc_packet { - unsigned char length; /* of rest of packet */ - unsigned char header; - unsigned char data[PN544_LLC_MAX_DATA]; /* includes crc-ccitt */ -}; - -/* Firmware upgrade mode */ -#define PN544_FW_HEADER_SIZE 3 -/* max fw transfer is 1024bytes, but I2C limits it to 0xC0 */ -#define PN544_MAX_FW_DATA (PN544_MAX_I2C_TRANSFER - PN544_FW_HEADER_SIZE) - -struct pn544_fw_packet { - unsigned char command; /* status in answer */ - unsigned char length[2]; /* big-endian order (msf) */ - unsigned char data[PN544_MAX_FW_DATA]; -}; - -#ifdef __KERNEL__ -enum { - NFC_GPIO_ENABLE, - NFC_GPIO_FW_RESET, - NFC_GPIO_IRQ -}; - -/* board config */ -struct pn544_nfc_platform_data { - int (*request_resources) (struct i2c_client *client); - void (*free_resources) (void); - void (*enable) (int fw); - int (*test) (void); - void (*disable) (void); - int (*get_gpio)(int type); -}; -#endif /* __KERNEL__ */ - -#endif /* _PN544_H_ */ diff --git a/include/linux/openvswitch.h b/include/linux/openvswitch.h index eb1efa54fe8..d42e174bd0c 100644 --- a/include/linux/openvswitch.h +++ b/include/linux/openvswitch.h @@ -243,6 +243,7 @@ enum ovs_key_attr { OVS_KEY_ATTR_ICMPV6, /* struct ovs_key_icmpv6 */ OVS_KEY_ATTR_ARP, /* struct ovs_key_arp */ OVS_KEY_ATTR_ND, /* struct ovs_key_nd */ + OVS_KEY_ATTR_SKB_MARK, /* u32 skb mark */ __OVS_KEY_ATTR_MAX }; diff --git a/include/linux/percpu-rwsem.h b/include/linux/percpu-rwsem.h index 250a4acddb2..bd1e86071e5 100644 --- a/include/linux/percpu-rwsem.h +++ b/include/linux/percpu-rwsem.h @@ -13,7 +13,7 @@ struct percpu_rw_semaphore { }; #define light_mb() barrier() -#define heavy_mb() synchronize_sched() +#define heavy_mb() synchronize_sched_expedited() static inline void percpu_down_read(struct percpu_rw_semaphore *p) { @@ -51,7 +51,7 @@ static inline void percpu_down_write(struct percpu_rw_semaphore *p) { mutex_lock(&p->mtx); p->locked = true; - synchronize_sched(); /* make sure that all readers exit the rcu_read_lock_sched region */ + synchronize_sched_expedited(); /* make sure that all readers exit the rcu_read_lock_sched region */ while (__percpu_count(p->counters)) msleep(1); heavy_mb(); /* C, between read of p->counter and write to data, paired with B */ diff --git a/include/linux/platform_data/pn544.h b/include/linux/platform_data/pn544.h new file mode 100644 index 00000000000..713bfd70334 --- /dev/null +++ b/include/linux/platform_data/pn544.h @@ -0,0 +1,44 @@ +/* + * Driver include for the PN544 NFC chip. + * + * Copyright (C) Nokia Corporation + * + * Author: Jari Vanhala <ext-jari.vanhala@nokia.com> + * Contact: Matti Aaltoenn <matti.j.aaltonen@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PN544_H_ +#define _PN544_H_ + +#include <linux/i2c.h> + +enum { + NFC_GPIO_ENABLE, + NFC_GPIO_FW_RESET, + NFC_GPIO_IRQ +}; + +/* board config */ +struct pn544_nfc_platform_data { + int (*request_resources) (struct i2c_client *client); + void (*free_resources) (void); + void (*enable) (int fw); + int (*test) (void); + void (*disable) (void); + int (*get_gpio)(int type); +}; + +#endif /* _PN544_H_ */ diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index a0525019e1d..6ecfa02ddba 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -485,7 +485,7 @@ #define SSB_SPROM8_HWIQ_IQSWP_IQCAL_SWP_SHIFT 4 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL 0x0020 #define SSB_SPROM8_HWIQ_IQSWP_HW_IQCAL_SHIFT 5 -#define SSB_SPROM8_TEMPDELTA 0x00BA +#define SSB_SPROM8_TEMPDELTA 0x00BC #define SSB_SPROM8_TEMPDELTA_PHYCAL 0x00ff #define SSB_SPROM8_TEMPDELTA_PHYCAL_SHIFT 0 #define SSB_SPROM8_TEMPDELTA_PERIOD 0x0f00 diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h index a1547ea3920..de5b2f8176c 100644 --- a/include/linux/stmmac.h +++ b/include/linux/stmmac.h @@ -104,6 +104,7 @@ struct plat_stmmacenet_data { int bugged_jumbo; int pmt; int force_sf_dma_mode; + int riwt_off; void (*fix_mac_speed)(void *priv, unsigned int speed); void (*bus_setup)(void __iomem *ioaddr); int (*init)(struct platform_device *pdev); diff --git a/include/media/adv7604.h b/include/media/adv7604.h index 171b957db74..dc004bc926c 100644 --- a/include/media/adv7604.h +++ b/include/media/adv7604.h @@ -40,14 +40,6 @@ enum adv7604_op_ch_sel { ADV7604_OP_CH_SEL_RBG = 5, }; -/* Primary mode (IO register 0x01, [3:0]) */ -enum adv7604_prim_mode { - ADV7604_PRIM_MODE_COMP = 1, - ADV7604_PRIM_MODE_RGB = 2, - ADV7604_PRIM_MODE_HDMI_COMP = 5, - ADV7604_PRIM_MODE_HDMI_GR = 6, -}; - /* Input Color Space (IO register 0x02, [7:4]) */ enum adv7604_inp_color_space { ADV7604_INP_COLOR_SPACE_LIM_RGB = 0, @@ -103,9 +95,6 @@ struct adv7604_platform_data { /* Bus rotation and reordering */ enum adv7604_op_ch_sel op_ch_sel; - /* Primary mode */ - enum adv7604_prim_mode prim_mode; - /* Select output format */ enum adv7604_op_format_sel op_format_sel; @@ -142,6 +131,16 @@ struct adv7604_platform_data { u8 i2c_vdp; }; +/* + * Mode of operation. + * This is used as the input argument of the s_routing video op. + */ +enum adv7604_mode { + ADV7604_MODE_COMP, + ADV7604_MODE_GR, + ADV7604_MODE_HDMI, +}; + #define V4L2_CID_ADV_RX_ANALOG_SAMPLING_PHASE (V4L2_CID_DV_CLASS_BASE + 0x1000) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR_MANUAL (V4L2_CID_DV_CLASS_BASE + 0x1001) #define V4L2_CID_ADV_RX_FREE_RUN_COLOR (V4L2_CID_DV_CLASS_BASE + 0x1002) diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h index 2e7c79ea046..7ea3db77ba8 100644 --- a/include/net/bluetooth/amp.h +++ b/include/net/bluetooth/amp.h @@ -46,5 +46,9 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, struct hci_conn *hcon); void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle); void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle); +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon); +void amp_create_logical_link(struct l2cap_chan *chan); +void amp_disconnect_logical_link(struct hci_chan *hchan); +void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason); #endif /* __AMP_H */ diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 88cbbda6102..45eee08157b 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -115,6 +115,7 @@ enum { HCI_SSP_ENABLED, HCI_HS_ENABLED, HCI_LE_ENABLED, + HCI_LE_PERIPHERAL, HCI_CONNECTABLE, HCI_DISCOVERABLE, HCI_LINK_SECURITY, @@ -153,7 +154,7 @@ enum { #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ #define HCI_INIT_TIMEOUT msecs_to_jiffies(10000) /* 10 seconds */ -#define HCI_CMD_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ +#define HCI_CMD_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_ACL_TX_TIMEOUT msecs_to_jiffies(45000) /* 45 seconds */ #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ @@ -318,6 +319,9 @@ enum { #define HCI_FLOW_CTL_MODE_PACKET_BASED 0x00 #define HCI_FLOW_CTL_MODE_BLOCK_BASED 0x01 +/* The core spec defines 127 as the "not available" value */ +#define HCI_TX_POWER_INVALID 127 + /* Extended Inquiry Response field types */ #define EIR_FLAGS 0x01 /* flags */ #define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */ @@ -334,6 +338,13 @@ enum { #define EIR_SSP_RAND_R 0x0F /* Simple Pairing Randomizer R */ #define EIR_DEVICE_ID 0x10 /* device ID */ +/* Low Energy Advertising Flags */ +#define LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define LE_AD_GENERAL 0x02 /* General Discoverable */ +#define LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ +#define LE_AD_SIM_LE_BREDR_CTRL 0x08 /* Simultaneous LE & BR/EDR Controller */ +#define LE_AD_SIM_LE_BREDR_HOST 0x10 /* Simultaneous LE & BR/EDR Host */ + /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 @@ -932,6 +943,22 @@ struct hci_rp_le_read_buffer_size { __u8 le_max_pkt; } __packed; +#define HCI_OP_LE_READ_ADV_TX_POWER 0x2007 +struct hci_rp_le_read_adv_tx_power { + __u8 status; + __s8 tx_power; +} __packed; + +#define HCI_MAX_AD_LENGTH 31 + +#define HCI_OP_LE_SET_ADV_DATA 0x2008 +struct hci_cp_le_set_adv_data { + __u8 length; + __u8 data[HCI_MAX_AD_LENGTH]; +} __packed; + +#define HCI_OP_LE_SET_ADV_ENABLE 0x200a + #define HCI_OP_LE_SET_SCAN_PARAM 0x200b struct hci_cp_le_set_scan_param { __u8 type; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9fe8e2dec87..ef5b85dac3f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -278,6 +278,10 @@ struct hci_dev { struct work_struct le_scan; struct le_scan_params le_scan_params; + __s8 adv_tx_power; + __u8 adv_data[HCI_MAX_AD_LENGTH]; + __u8 adv_data_len; + int (*open)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev); @@ -355,6 +359,7 @@ struct hci_chan { struct hci_conn *conn; struct sk_buff_head data_q; unsigned int sent; + __u8 state; }; extern struct list_head hci_dev_list; @@ -682,7 +687,7 @@ static inline uint8_t __hci_num_ctrl(void) } struct hci_dev *hci_dev_get(int index); -struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); +struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src); struct hci_dev *hci_alloc_dev(void); void hci_free_dev(struct hci_dev *hdev); @@ -731,6 +736,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, u8 *randomizer); int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr); +int hci_update_ad(struct hci_dev *hdev); + void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb); int hci_recv_frame(struct sk_buff *skb); @@ -747,18 +754,29 @@ void hci_conn_del_sysfs(struct hci_conn *conn); #define SET_HCIDEV_DEV(hdev, pdev) ((hdev)->dev.parent = (pdev)) /* ----- LMP capabilities ----- */ -#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH) #define lmp_encrypt_capable(dev) ((dev)->features[0] & LMP_ENCRYPT) +#define lmp_rswitch_capable(dev) ((dev)->features[0] & LMP_RSWITCH) +#define lmp_hold_capable(dev) ((dev)->features[0] & LMP_HOLD) #define lmp_sniff_capable(dev) ((dev)->features[0] & LMP_SNIFF) -#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) +#define lmp_park_capable(dev) ((dev)->features[1] & LMP_PARK) +#define lmp_inq_rssi_capable(dev) ((dev)->features[3] & LMP_RSSI_INQ) #define lmp_esco_capable(dev) ((dev)->features[3] & LMP_ESCO) +#define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR)) +#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) +#define lmp_sniffsubr_capable(dev) ((dev)->features[5] & LMP_SNIFF_SUBR) +#define lmp_pause_enc_capable(dev) ((dev)->features[5] & LMP_PAUSE_ENC) +#define lmp_ext_inq_capable(dev) ((dev)->features[6] & LMP_EXT_INQ) +#define lmp_le_br_capable(dev) ((dev)->features[6] & LMP_SIMUL_LE_BR) #define lmp_ssp_capable(dev) ((dev)->features[6] & LMP_SIMPLE_PAIR) #define lmp_no_flush_capable(dev) ((dev)->features[6] & LMP_NO_FLUSH) -#define lmp_le_capable(dev) ((dev)->features[4] & LMP_LE) -#define lmp_bredr_capable(dev) (!((dev)->features[4] & LMP_NO_BREDR)) +#define lmp_lsto_capable(dev) ((dev)->features[7] & LMP_LSTO) +#define lmp_inq_tx_pwr_capable(dev) ((dev)->features[7] & LMP_INQ_TX_PWR) +#define lmp_ext_feat_capable(dev) ((dev)->features[7] & LMP_EXTFEATURES) /* ----- Extended LMP capabilities ----- */ +#define lmp_host_ssp_capable(dev) ((dev)->host_features[0] & LMP_HOST_SSP) #define lmp_host_le_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE) +#define lmp_host_le_br_capable(dev) ((dev)->host_features[0] & LMP_HOST_LE_BREDR) /* ----- HCI protocols ----- */ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, @@ -877,7 +895,7 @@ struct hci_cb { static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) { - struct list_head *p; + struct hci_cb *cb; __u8 encrypt; hci_proto_auth_cfm(conn, status); @@ -888,8 +906,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; read_lock(&hci_cb_list_lock); - list_for_each(p, &hci_cb_list) { - struct hci_cb *cb = list_entry(p, struct hci_cb, list); + list_for_each_entry(cb, &hci_cb_list, list) { if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } @@ -899,7 +916,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt) { - struct list_head *p; + struct hci_cb *cb; if (conn->sec_level == BT_SECURITY_SDP) conn->sec_level = BT_SECURITY_LOW; @@ -910,8 +927,7 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, hci_proto_encrypt_cfm(conn, status, encrypt); read_lock(&hci_cb_list_lock); - list_for_each(p, &hci_cb_list) { - struct hci_cb *cb = list_entry(p, struct hci_cb, list); + list_for_each_entry(cb, &hci_cb_list, list) { if (cb->security_cfm) cb->security_cfm(conn, status, encrypt); } @@ -920,11 +936,10 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) { - struct list_head *p; + struct hci_cb *cb; read_lock(&hci_cb_list_lock); - list_for_each(p, &hci_cb_list) { - struct hci_cb *cb = list_entry(p, struct hci_cb, list); + list_for_each_entry(cb, &hci_cb_list, list) { if (cb->key_change_cfm) cb->key_change_cfm(conn, status); } @@ -934,11 +949,10 @@ static inline void hci_key_change_cfm(struct hci_conn *conn, __u8 status) static inline void hci_role_switch_cfm(struct hci_conn *conn, __u8 status, __u8 role) { - struct list_head *p; + struct hci_cb *cb; read_lock(&hci_cb_list_lock); - list_for_each(p, &hci_cb_list) { - struct hci_cb *cb = list_entry(p, struct hci_cb, list); + list_for_each_entry(cb, &hci_cb_list, list) { if (cb->role_switch_cfm) cb->role_switch_cfm(conn, status, role); } diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6e23afdf65c..f57fab04e7c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -52,6 +52,8 @@ #define L2CAP_ENC_TIMEOUT msecs_to_jiffies(5000) #define L2CAP_CONN_TIMEOUT msecs_to_jiffies(40000) #define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000) +#define L2CAP_MOVE_TIMEOUT msecs_to_jiffies(4000) +#define L2CAP_MOVE_ERTX_TIMEOUT msecs_to_jiffies(60000) #define L2CAP_A2MP_DEFAULT_MTU 670 @@ -434,6 +436,8 @@ struct l2cap_chan { struct sock *sk; struct l2cap_conn *conn; + struct hci_conn *hs_hcon; + struct hci_chan *hs_hchan; struct kref kref; __u8 state; @@ -477,6 +481,12 @@ struct l2cap_chan { unsigned long conn_state; unsigned long flags; + __u8 remote_amp_id; + __u8 local_amp_id; + __u8 move_id; + __u8 move_state; + __u8 move_role; + __u16 next_tx_seq; __u16 expected_ack_seq; __u16 expected_tx_seq; @@ -509,8 +519,6 @@ struct l2cap_chan { __u32 remote_acc_lat; __u32 remote_flush_to; - __u8 ctrl_id; - struct delayed_work chan_timer; struct delayed_work retrans_timer; struct delayed_work monitor_timer; @@ -644,6 +652,9 @@ enum { enum { L2CAP_RX_STATE_RECV, L2CAP_RX_STATE_SREJ_SENT, + L2CAP_RX_STATE_MOVE, + L2CAP_RX_STATE_WAIT_P, + L2CAP_RX_STATE_WAIT_F, }; enum { @@ -674,6 +685,25 @@ enum { L2CAP_EV_RECV_FRAME, }; +enum { + L2CAP_MOVE_ROLE_NONE, + L2CAP_MOVE_ROLE_INITIATOR, + L2CAP_MOVE_ROLE_RESPONDER, +}; + +enum { + L2CAP_MOVE_STABLE, + L2CAP_MOVE_WAIT_REQ, + L2CAP_MOVE_WAIT_RSP, + L2CAP_MOVE_WAIT_RSP_SUCCESS, + L2CAP_MOVE_WAIT_CONFIRM, + L2CAP_MOVE_WAIT_CONFIRM_RSP, + L2CAP_MOVE_WAIT_LOGICAL_COMP, + L2CAP_MOVE_WAIT_LOGICAL_CFM, + L2CAP_MOVE_WAIT_LOCAL_BUSY, + L2CAP_MOVE_WAIT_PREPARE, +}; + void l2cap_chan_hold(struct l2cap_chan *c); void l2cap_chan_put(struct l2cap_chan *c); @@ -778,5 +808,9 @@ void l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan); void l2cap_chan_del(struct l2cap_chan *chan, int err); void l2cap_send_conn_req(struct l2cap_chan *chan); +void l2cap_move_start(struct l2cap_chan *chan); +void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, + u8 status); +void __l2cap_physical_cfm(struct l2cap_chan *chan, int result); #endif /* __L2CAP_H */ diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index c6964572890..e78db2cf3d1 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -306,6 +306,88 @@ struct key_params { }; /** + * struct cfg80211_chan_def - channel definition + * @chan: the (control) channel + * @width: channel width + * @center_freq1: center frequency of first segment + * @center_freq2: center frequency of second segment + * (only with 80+80 MHz) + */ +struct cfg80211_chan_def { + struct ieee80211_channel *chan; + enum nl80211_chan_width width; + u32 center_freq1; + u32 center_freq2; +}; + +/** + * cfg80211_get_chandef_type - return old channel type from chandef + * @chandef: the channel definition + * + * Returns the old channel type (NOHT, HT20, HT40+/-) from a given + * chandef, which must have a bandwidth allowing this conversion. + */ +static inline enum nl80211_channel_type +cfg80211_get_chandef_type(const struct cfg80211_chan_def *chandef) +{ + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + return NL80211_CHAN_NO_HT; + case NL80211_CHAN_WIDTH_20: + return NL80211_CHAN_HT20; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + return NL80211_CHAN_HT40PLUS; + return NL80211_CHAN_HT40MINUS; + default: + WARN_ON(1); + return NL80211_CHAN_NO_HT; + } +} + +/** + * cfg80211_chandef_create - create channel definition using channel type + * @chandef: the channel definition struct to fill + * @channel: the control channel + * @chantype: the channel type + * + * Given a channel type, create a channel definition. + */ +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *channel, + enum nl80211_channel_type chantype); + +/** + * cfg80211_chandef_identical - check if two channel definitions are identical + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %true if the channels defined by the channel definitions are + * identical, %false otherwise. + */ +static inline bool +cfg80211_chandef_identical(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2) +{ + return (chandef1->chan == chandef2->chan && + chandef1->width == chandef2->width && + chandef1->center_freq1 == chandef2->center_freq1 && + chandef1->center_freq2 == chandef2->center_freq2); +} + +/** + * cfg80211_chandef_compatible - check if two channel definitions are compatible + * @chandef1: first channel definition + * @chandef2: second channel definition + * + * Returns %NULL if the given channel definitions are incompatible, + * chandef1 or chandef2 otherwise. + */ +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *chandef1, + const struct cfg80211_chan_def *chandef2); + +/** * enum survey_info_flags - survey information flags * * @SURVEY_INFO_NOISE_DBM: noise (in dBm) was filled in @@ -426,8 +508,7 @@ struct cfg80211_beacon_data { * * Used to configure an AP interface. * - * @channel: the channel to start the AP on - * @channel_type: the channel type to use + * @chandef: defines the channel to use * @beacon: beacon data * @beacon_interval: beacon interval * @dtim_period: DTIM period @@ -441,8 +522,7 @@ struct cfg80211_beacon_data { * @inactivity_timeout: time in seconds to determine station's inactivity. */ struct cfg80211_ap_settings { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; struct cfg80211_beacon_data beacon; @@ -582,16 +662,24 @@ enum station_info_flags { * Used by the driver to indicate the specific rate transmission * type for 802.11n transmissions. * - * @RATE_INFO_FLAGS_MCS: @tx_bitrate_mcs filled - * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 Mhz width transmission + * @RATE_INFO_FLAGS_MCS: mcs field filled with HT MCS + * @RATE_INFO_FLAGS_VHT_MCS: mcs field filled with VHT MCS + * @RATE_INFO_FLAGS_40_MHZ_WIDTH: 40 MHz width transmission + * @RATE_INFO_FLAGS_80_MHZ_WIDTH: 80 MHz width transmission + * @RATE_INFO_FLAGS_80P80_MHZ_WIDTH: 80+80 MHz width transmission + * @RATE_INFO_FLAGS_160_MHZ_WIDTH: 160 MHz width transmission * @RATE_INFO_FLAGS_SHORT_GI: 400ns guard interval - * @RATE_INFO_FLAGS_60G: 60gHz MCS + * @RATE_INFO_FLAGS_60G: 60GHz MCS */ enum rate_info_flags { - RATE_INFO_FLAGS_MCS = 1<<0, - RATE_INFO_FLAGS_40_MHZ_WIDTH = 1<<1, - RATE_INFO_FLAGS_SHORT_GI = 1<<2, - RATE_INFO_FLAGS_60G = 1<<3, + RATE_INFO_FLAGS_MCS = BIT(0), + RATE_INFO_FLAGS_VHT_MCS = BIT(1), + RATE_INFO_FLAGS_40_MHZ_WIDTH = BIT(2), + RATE_INFO_FLAGS_80_MHZ_WIDTH = BIT(3), + RATE_INFO_FLAGS_80P80_MHZ_WIDTH = BIT(4), + RATE_INFO_FLAGS_160_MHZ_WIDTH = BIT(5), + RATE_INFO_FLAGS_SHORT_GI = BIT(6), + RATE_INFO_FLAGS_60G = BIT(7), }; /** @@ -602,11 +690,13 @@ enum rate_info_flags { * @flags: bitflag of flags from &enum rate_info_flags * @mcs: mcs index if struct describes a 802.11n bitrate * @legacy: bitrate in 100kbit/s for 802.11abg + * @nss: number of streams (VHT only) */ struct rate_info { u8 flags; u8 mcs; u16 legacy; + u8 nss; }; /** @@ -909,8 +999,7 @@ struct mesh_config { /** * struct mesh_setup - 802.11s mesh setup configuration - * @channel: the channel to start the mesh network on - * @channel_type: the channel type to use + * @chandef: defines the channel to use * @mesh_id: the mesh ID * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes * @sync_method: which synchronization method to use @@ -925,8 +1014,7 @@ struct mesh_config { * These parameters are fixed when the mesh is created. */ struct mesh_setup { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; const u8 *mesh_id; u8 mesh_id_len; u8 sync_method; @@ -1266,8 +1354,7 @@ struct cfg80211_disassoc_request { * @ssid_len: The length of the SSID, will always be non-zero. * @bssid: Fixed BSSID requested, maybe be %NULL, if set do not * search for IBSSs with a different BSSID. - * @channel: The channel to use if no IBSS can be found to join. - * @channel_type: channel type (HT mode) + * @chandef: defines the channel to use if no other IBSS to join can be found * @channel_fixed: The channel should be fixed -- do not search for * IBSSs to join on other channels. * @ie: information element(s) to include in the beacon @@ -1285,8 +1372,7 @@ struct cfg80211_disassoc_request { struct cfg80211_ibss_params { u8 *ssid; u8 *bssid; - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; u8 *ie; u8 ssid_len, ie_len; u16 beacon_interval; @@ -1545,13 +1631,19 @@ struct cfg80211_gtk_rekey_data { * to a merge. * @leave_ibss: Leave the IBSS. * + * @set_mcast_rate: Set the specified multicast rate (only if vif is in ADHOC or + * MESH mode) + * * @set_wiphy_params: Notify that wiphy parameters have changed; * @changed bitfield (see &enum wiphy_params_flags) describes which values * have changed. The actual parameter values are available in * struct wiphy. If returning an error, no value should be changed. * * @set_tx_power: set the transmit power according to the parameters, - * the power passed is in mBm, to get dBm use MBM_TO_DBM(). + * the power passed is in mBm, to get dBm use MBM_TO_DBM(). The + * wdev may be %NULL if power was set for the wiphy, and will + * always be %NULL unless the driver supports per-vif TX power + * (as advertised by the nl80211 feature flag.) * @get_tx_power: store the current TX power into the dbm variable; * return 0 if successful * @@ -1722,8 +1814,7 @@ struct cfg80211_ops { struct ieee80211_channel *chan); int (*set_monitor_channel)(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); + struct cfg80211_chan_def *chandef); int (*scan)(struct wiphy *wiphy, struct cfg80211_scan_request *request); @@ -1746,11 +1837,15 @@ struct cfg80211_ops { struct cfg80211_ibss_params *params); int (*leave_ibss)(struct wiphy *wiphy, struct net_device *dev); + int (*set_mcast_rate)(struct wiphy *wiphy, struct net_device *dev, + int rate[IEEE80211_NUM_BANDS]); + int (*set_wiphy_params)(struct wiphy *wiphy, u32 changed); - int (*set_tx_power)(struct wiphy *wiphy, + int (*set_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm); - int (*get_tx_power)(struct wiphy *wiphy, int *dbm); + int (*get_tx_power)(struct wiphy *wiphy, struct wireless_dev *wdev, + int *dbm); int (*set_wds_peer)(struct wiphy *wiphy, struct net_device *dev, const u8 *addr); @@ -1781,7 +1876,6 @@ struct cfg80211_ops { int (*remain_on_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie); int (*cancel_remain_on_channel)(struct wiphy *wiphy, @@ -1790,10 +1884,8 @@ struct cfg80211_ops { int (*mgmt_tx)(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie); + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie); int (*mgmt_tx_cancel_wait)(struct wiphy *wiphy, struct wireless_dev *wdev, u64 cookie); @@ -1848,10 +1940,9 @@ struct cfg80211_ops { void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, u32 sset, u8 *data); - struct ieee80211_channel * - (*get_channel)(struct wiphy *wiphy, + int (*get_channel)(struct wiphy *wiphy, struct wireless_dev *wdev, - enum nl80211_channel_type *type); + struct cfg80211_chan_def *chandef); int (*start_p2p_device)(struct wiphy *wiphy, struct wireless_dev *wdev); @@ -2459,8 +2550,7 @@ struct wireless_dev { spinlock_t event_lock; struct cfg80211_internal_bss *current_bss; /* associated / joined */ - struct ieee80211_channel *preset_chan; - enum nl80211_channel_type preset_chantype; + struct cfg80211_chan_def preset_chandef; /* for AP and mesh channel tracking */ struct ieee80211_channel *channel; @@ -3340,14 +3430,12 @@ void cfg80211_disconnected(struct net_device *dev, u16 reason, * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) - * @channel_type: Channel type * @duration: Duration in milliseconds that the driver intents to remain on the * channel * @gfp: allocation flags */ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); /** @@ -3355,12 +3443,10 @@ void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, * @wdev: wireless device * @cookie: the request cookie * @chan: The current channel (from remain_on_channel request) - * @channel_type: Channel type * @gfp: allocation flags */ void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); @@ -3550,7 +3636,6 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, * @len: length of the frame * @freq: frequency the frame was received on * @sig_dbm: signal strength in mBm, or 0 if unknown - * @gfp: allocation flags * * Use this function to report to userspace when a beacon was * received. It is not useful to call this when there is no @@ -3558,31 +3643,47 @@ void cfg80211_probe_status(struct net_device *dev, const u8 *addr, */ void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, - int freq, int sig_dbm, gfp_t gfp); + int freq, int sig_dbm); /** - * cfg80211_can_beacon_sec_chan - test if ht40 on extension channel can be used + * cfg80211_reg_can_beacon - check if beaconing is allowed * @wiphy: the wiphy - * @chan: main channel - * @channel_type: HT mode + * @chandef: the channel definition * * This function returns true if there is no secondary channel or the secondary - * channel can be used for beaconing (i.e. is not a radar channel etc.) + * channel(s) can be used for beaconing (i.e. is not a radar channel etc.) */ -bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type); +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef); /* * cfg80211_ch_switch_notify - update wdev channel and notify userspace * @dev: the device which switched channels - * @freq: new channel frequency (in MHz) - * @type: channel type + * @chandef: the new channel definition * * Acquires wdev_lock, so must only be called from sleepable driver context! */ -void cfg80211_ch_switch_notify(struct net_device *dev, int freq, - enum nl80211_channel_type type); +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef); + +/* + * cfg80211_tdls_oper_request - request userspace to perform TDLS operation + * @dev: the device on which the operation is requested + * @peer: the MAC address of the peer device + * @oper: the requested TDLS operation (NL80211_TDLS_SETUP or + * NL80211_TDLS_TEARDOWN) + * @reason_code: the reason code for teardown request + * @gfp: allocation flags + * + * This function is used to request userspace to perform TDLS operation that + * requires knowledge of keys, i.e., link setup or teardown when the AP + * connection uses encryption. This is optional mechanism for the driver to use + * if it can automatically determine when a TDLS link could be useful (e.g., + * based on traffic and signal strength for a peer). + */ +void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp); /* * cfg80211_calculate_bitrate - calculate actual bitrate (in 100Kbps units) @@ -3608,6 +3709,26 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate); */ void cfg80211_unregister_wdev(struct wireless_dev *wdev); +/** + * cfg80211_get_p2p_attr - find and copy a P2P attribute from IE buffer + * @ies: the input IE buffer + * @len: the input length + * @attr: the attribute ID to find + * @buf: output buffer, can be %NULL if the data isn't needed, e.g. + * if the function is only called to get the needed buffer size + * @bufsize: size of the output buffer + * + * The function finds a given P2P attribute in the (vendor) IEs and + * copies its contents to the given buffer. + * + * The return value is a negative error code (-%EILSEQ or -%ENOENT) if + * the data is malformed or the attribute can't be found (respectively), + * or the length of the found attribute (which can be zero). + */ +int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, + enum ieee80211_p2p_attr_id attr, + u8 *buf, unsigned int bufsize); + /* Logging, debugging and troubleshooting/diagnostic helpers. */ /* wiphy_printk helpers, similar to dev_printk */ diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 54be0287eb9..d1de4fbd45c 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -299,30 +299,34 @@ typedef __u64 __bitwise __addrpair; (((__force __u64)(__be32)(__daddr)) << 32) | \ ((__force __u64)(__be32)(__saddr))); #endif /* __BIG_ENDIAN */ -#define INET_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif)\ - (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \ - ((*((__addrpair *)&(inet_sk(__sk)->inet_daddr))) == (__cookie)) && \ - ((*((__portpair *)&(inet_sk(__sk)->inet_dport))) == (__ports)) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) -#define INET_TW_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif)\ - (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \ - ((*((__addrpair *)&(inet_twsk(__sk)->tw_daddr))) == (__cookie)) && \ - ((*((__portpair *)&(inet_twsk(__sk)->tw_dport))) == (__ports)) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) +#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ + ((inet_sk(__sk)->inet_portpair == (__ports)) && \ + (inet_sk(__sk)->inet_addrpair == (__cookie)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) +#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif)\ + ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ + (inet_twsk(__sk)->tw_addrpair == (__cookie)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) #else /* 32-bit arch */ #define INET_ADDR_COOKIE(__name, __saddr, __daddr) -#define INET_MATCH(__sk, __net, __hash, __cookie, __saddr, __daddr, __ports, __dif) \ - (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \ - (inet_sk(__sk)->inet_daddr == (__saddr)) && \ - (inet_sk(__sk)->inet_rcv_saddr == (__daddr)) && \ - ((*((__portpair *)&(inet_sk(__sk)->inet_dport))) == (__ports)) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) -#define INET_TW_MATCH(__sk, __net, __hash,__cookie, __saddr, __daddr, __ports, __dif) \ - (((__sk)->sk_hash == (__hash)) && net_eq(sock_net(__sk), (__net)) && \ - (inet_twsk(__sk)->tw_daddr == (__saddr)) && \ - (inet_twsk(__sk)->tw_rcv_saddr == (__daddr)) && \ - ((*((__portpair *)&(inet_twsk(__sk)->tw_dport))) == (__ports)) && \ - (!((__sk)->sk_bound_dev_if) || ((__sk)->sk_bound_dev_if == (__dif)))) +#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ + ((inet_sk(__sk)->inet_portpair == (__ports)) && \ + (inet_sk(__sk)->inet_daddr == (__saddr)) && \ + (inet_sk(__sk)->inet_rcv_saddr == (__daddr)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) +#define INET_TW_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ + ((inet_twsk(__sk)->tw_portpair == (__ports)) && \ + (inet_twsk(__sk)->tw_daddr == (__saddr)) && \ + (inet_twsk(__sk)->tw_rcv_saddr == (__daddr)) && \ + (!(__sk)->sk_bound_dev_if || \ + ((__sk)->sk_bound_dev_if == (__dif))) && \ + net_eq(sock_net(__sk), (__net))) #endif /* 64-bit arch */ /* diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h index 256c1ed2d69..a4196cbc84e 100644 --- a/include/net/inet_sock.h +++ b/include/net/inet_sock.h @@ -144,9 +144,11 @@ struct inet_sock { /* Socket demultiplex comparisons on incoming packets. */ #define inet_daddr sk.__sk_common.skc_daddr #define inet_rcv_saddr sk.__sk_common.skc_rcv_saddr +#define inet_addrpair sk.__sk_common.skc_addrpair +#define inet_dport sk.__sk_common.skc_dport +#define inet_num sk.__sk_common.skc_num +#define inet_portpair sk.__sk_common.skc_portpair - __be16 inet_dport; - __u16 inet_num; __be32 inet_saddr; __s16 uc_ttl; __u16 cmsg_flags; @@ -154,6 +156,7 @@ struct inet_sock { __u16 inet_id; struct ip_options_rcu __rcu *inet_opt; + int rx_dst_ifindex; __u8 tos; __u8 min_ttl; __u8 mc_ttl; @@ -170,7 +173,6 @@ struct inet_sock { int uc_index; int mc_index; __be32 mc_addr; - int rx_dst_ifindex; struct ip_mc_socklist __rcu *mc_list; struct inet_cork_full cork; }; diff --git a/include/net/inet_timewait_sock.h b/include/net/inet_timewait_sock.h index ba52c830a7a..7d658d57736 100644 --- a/include/net/inet_timewait_sock.h +++ b/include/net/inet_timewait_sock.h @@ -112,6 +112,11 @@ struct inet_timewait_sock { #define tw_net __tw_common.skc_net #define tw_daddr __tw_common.skc_daddr #define tw_rcv_saddr __tw_common.skc_rcv_saddr +#define tw_addrpair __tw_common.skc_addrpair +#define tw_dport __tw_common.skc_dport +#define tw_num __tw_common.skc_num +#define tw_portpair __tw_common.skc_portpair + int tw_timeout; volatile unsigned char tw_substate; unsigned char tw_rcv_wscale; @@ -119,8 +124,6 @@ struct inet_timewait_sock { /* Socket demultiplex comparisons on incoming packets. */ /* these three are in inet_sock */ __be16 tw_sport; - __be16 tw_dport; - __u16 tw_num; kmemcheck_bitfield_begin(flags); /* And these are ours. */ unsigned int tw_ipv6only : 1, diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 979bf6c1314..5af66b26ebd 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -271,6 +271,15 @@ struct ipv6_txoptions *ipv6_fixup_options(struct ipv6_txoptions *opt_space, extern bool ipv6_opt_accepted(const struct sock *sk, const struct sk_buff *skb); +static inline bool ipv6_accept_ra(struct inet6_dev *idev) +{ + /* If forwarding is enabled, RA are not accepted unless the special + * hybrid mode (accept_ra=2) is enabled. + */ + return idev->cnf.forwarding ? idev->cnf.accept_ra == 2 : + idev->cnf.accept_ra; +} + #if IS_ENABLED(CONFIG_IPV6) static inline int ip6_frag_nqueues(struct net *net) { @@ -630,6 +639,16 @@ extern int ipv6_skip_exthdr(const struct sk_buff *, int start, extern bool ipv6_ext_hdr(u8 nexthdr); +enum { + IP6_FH_F_FRAG = (1 << 0), + IP6_FH_F_AUTH = (1 << 1), + IP6_FH_F_SKIP_RH = (1 << 2), +}; + +/* find specified header and get offset to it */ +extern int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff, int *fragflg); + extern int ipv6_find_tlv(struct sk_buff *skb, int offset, int type); extern struct in6_addr *fl6_update_dst(struct flowi6 *fl6, diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 00b7204708b..db7680acd0d 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -145,11 +145,11 @@ struct ieee80211_low_level_stats { /** * enum ieee80211_chanctx_change - change flag for channel context - * @IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE: The channel type was changed + * @IEEE80211_CHANCTX_CHANGE_WIDTH: The channel width changed * @IEEE80211_CHANCTX_CHANGE_RX_CHAINS: The number of RX chains changed */ enum ieee80211_chanctx_change { - IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE = BIT(0), + IEEE80211_CHANCTX_CHANGE_WIDTH = BIT(0), IEEE80211_CHANCTX_CHANGE_RX_CHAINS = BIT(1), }; @@ -159,8 +159,7 @@ enum ieee80211_chanctx_change { * This is the driver-visible part. The ieee80211_chanctx * that contains it is visible in mac80211 only. * - * @channel: the channel to tune to - * @channel_type: the channel (HT) type + * @def: the channel definition * @rx_chains_static: The number of RX chains that must always be * active on the channel to receive MIMO transmissions * @rx_chains_dynamic: The number of RX chains that must be enabled @@ -170,8 +169,7 @@ enum ieee80211_chanctx_change { * sizeof(void *), size is determined in hw information. */ struct ieee80211_chanctx_conf { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def def; u8 rx_chains_static, rx_chains_dynamic; @@ -207,6 +205,9 @@ struct ieee80211_chanctx_conf { * @BSS_CHANGED_SSID: SSID changed for this BSS (AP mode) * @BSS_CHANGED_AP_PROBE_RESP: Probe Response changed for this BSS (AP mode) * @BSS_CHANGED_PS: PS changed for this BSS (STA mode) + * @BSS_CHANGED_TXPOWER: TX power setting changed for this interface + * @BSS_CHANGED_P2P_PS: P2P powersave settings (CTWindow, opportunistic PS) + * changed (currently only in P2P client mode, GO mode will be later) */ enum ieee80211_bss_change { BSS_CHANGED_ASSOC = 1<<0, @@ -227,6 +228,8 @@ enum ieee80211_bss_change { BSS_CHANGED_SSID = 1<<15, BSS_CHANGED_AP_PROBE_RESP = 1<<16, BSS_CHANGED_PS = 1<<17, + BSS_CHANGED_TXPOWER = 1<<18, + BSS_CHANGED_P2P_PS = 1<<19, /* when adding here, make sure to change ieee80211_reconfig */ }; @@ -283,9 +286,8 @@ enum ieee80211_rssi_event { * @mcast_rate: per-band multicast rate index + 1 (0: disabled) * @bssid: The BSSID for this BSS * @enable_beacon: whether beaconing should be enabled or not - * @channel_type: Channel type for this BSS -- the hardware might be - * configured for HT40+ while this BSS only uses no-HT, for - * example. + * @chandef: Channel definition for this BSS -- the hardware might be + * configured a higher bandwidth than this BSS uses, for example. * @ht_operation_mode: HT operation mode like in &struct ieee80211_ht_operation. * This field is only valid when the channel type is one of the HT types. * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value @@ -309,6 +311,9 @@ enum ieee80211_rssi_event { * @ssid: The SSID of the current vif. Only valid in AP-mode. * @ssid_len: Length of SSID given in @ssid. * @hidden_ssid: The SSID of the current vif is hidden. Only valid in AP-mode. + * @txpower: TX power in dBm + * @p2p_ctwindow: P2P CTWindow, only for P2P client interfaces + * @p2p_oppps: P2P opportunistic PS is enabled */ struct ieee80211_bss_conf { const u8 *bssid; @@ -331,7 +336,7 @@ struct ieee80211_bss_conf { u16 ht_operation_mode; s32 cqm_rssi_thold; u32 cqm_rssi_hyst; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; __be32 arp_addr_list[IEEE80211_BSS_ARP_ADDR_LIST_LEN]; u8 arp_addr_cnt; bool arp_filter_enabled; @@ -341,6 +346,9 @@ struct ieee80211_bss_conf { u8 ssid[IEEE80211_MAX_SSID_LEN]; size_t ssid_len; bool hidden_ssid; + int txpower; + u8 p2p_ctwindow; + bool p2p_oppps; }; /** @@ -491,9 +499,14 @@ enum mac80211_tx_control_flags { * This is set if the current BSS requires ERP protection. * @IEEE80211_TX_RC_USE_SHORT_PREAMBLE: Use short preamble. * @IEEE80211_TX_RC_MCS: HT rate. + * @IEEE80211_TX_RC_VHT_MCS: VHT MCS rate, in this case the idx field is split + * into a higher 4 bits (Nss) and lower 4 bits (MCS number) * @IEEE80211_TX_RC_GREEN_FIELD: Indicates whether this rate should be used in * Greenfield mode. * @IEEE80211_TX_RC_40_MHZ_WIDTH: Indicates if the Channel Width should be 40 MHz. + * @IEEE80211_TX_RC_80_MHZ_WIDTH: Indicates 80 MHz transmission + * @IEEE80211_TX_RC_160_MHZ_WIDTH: Indicates 160 MHz transmission + * (80+80 isn't supported yet) * @IEEE80211_TX_RC_DUP_DATA: The frame should be transmitted on both of the * adjacent 20 MHz channels, if the current channel type is * NL80211_CHAN_HT40MINUS or NL80211_CHAN_HT40PLUS. @@ -504,12 +517,15 @@ enum mac80211_rate_control_flags { IEEE80211_TX_RC_USE_CTS_PROTECT = BIT(1), IEEE80211_TX_RC_USE_SHORT_PREAMBLE = BIT(2), - /* rate index is an MCS rate number instead of an index */ + /* rate index is an HT/VHT MCS instead of an index */ IEEE80211_TX_RC_MCS = BIT(3), IEEE80211_TX_RC_GREEN_FIELD = BIT(4), IEEE80211_TX_RC_40_MHZ_WIDTH = BIT(5), IEEE80211_TX_RC_DUP_DATA = BIT(6), IEEE80211_TX_RC_SHORT_GI = BIT(7), + IEEE80211_TX_RC_VHT_MCS = BIT(8), + IEEE80211_TX_RC_80_MHZ_WIDTH = BIT(9), + IEEE80211_TX_RC_160_MHZ_WIDTH = BIT(10), }; @@ -552,10 +568,32 @@ enum mac80211_rate_control_flags { */ struct ieee80211_tx_rate { s8 idx; - u8 count; - u8 flags; + u16 count:5, + flags:11; } __packed; +#define IEEE80211_MAX_TX_RETRY 31 + +static inline void ieee80211_rate_set_vht(struct ieee80211_tx_rate *rate, + u8 mcs, u8 nss) +{ + WARN_ON(mcs & ~0xF); + WARN_ON(nss & ~0x7); + rate->idx = (nss << 4) | mcs; +} + +static inline u8 +ieee80211_rate_get_vht_mcs(const struct ieee80211_tx_rate *rate) +{ + return rate->idx & 0xF; +} + +static inline u8 +ieee80211_rate_get_vht_nss(const struct ieee80211_tx_rate *rate) +{ + return rate->idx >> 4; +} + /** * struct ieee80211_tx_info - skb transmit information * @@ -700,13 +738,20 @@ 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_MACTIME_MPDU: The timestamp passed in the RX status (@mactime + * @RX_FLAG_MACTIME_START: 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_MACTIME_END: The timestamp passed in the RX status (@mactime + * field) is valid and contains the time the last symbol of the MPDU + * (including FCS) was received. * @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_VHT: VHT MCS was used and rate_index is MCS index * @RX_FLAG_40MHZ: HT40 (40 MHz) was used + * @RX_FLAG_80MHZ: 80 MHz was used + * @RX_FLAG_80P80MHZ: 80+80 MHz was used + * @RX_FLAG_160MHZ: 160 MHz was used * @RX_FLAG_SHORT_GI: Short guard interval was used * @RX_FLAG_NO_SIGNAL_VAL: The signal strength value is not present. * Valid only for data frames (mainly A-MPDU) @@ -734,7 +779,7 @@ enum mac80211_rx_flags { RX_FLAG_IV_STRIPPED = BIT(4), RX_FLAG_FAILED_FCS_CRC = BIT(5), RX_FLAG_FAILED_PLCP_CRC = BIT(6), - RX_FLAG_MACTIME_MPDU = BIT(7), + RX_FLAG_MACTIME_START = BIT(7), RX_FLAG_SHORTPRE = BIT(8), RX_FLAG_HT = BIT(9), RX_FLAG_40MHZ = BIT(10), @@ -748,6 +793,11 @@ enum mac80211_rx_flags { RX_FLAG_AMPDU_IS_LAST = BIT(18), RX_FLAG_AMPDU_DELIM_CRC_ERROR = BIT(19), RX_FLAG_AMPDU_DELIM_CRC_KNOWN = BIT(20), + RX_FLAG_MACTIME_END = BIT(21), + RX_FLAG_VHT = BIT(22), + RX_FLAG_80MHZ = BIT(23), + RX_FLAG_80P80MHZ = BIT(24), + RX_FLAG_160MHZ = BIT(25), }; /** @@ -768,25 +818,39 @@ enum mac80211_rx_flags { * @IEEE80211_HW_SIGNAL_* * @antenna: antenna used * @rate_idx: index of data rate into band's supported rates or MCS index if - * HT rates are use (RX_FLAG_HT) + * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) + * @vht_nss: number of streams (VHT only) * @flag: %RX_FLAG_* * @rx_flags: internal RX flags for mac80211 * @ampdu_reference: A-MPDU reference number, must be a different value for * each A-MPDU but the same for each subframe within one A-MPDU * @ampdu_delimiter_crc: A-MPDU delimiter CRC + * @vendor_radiotap_bitmap: radiotap vendor namespace presence bitmap + * @vendor_radiotap_len: radiotap vendor namespace length + * @vendor_radiotap_align: radiotap vendor namespace alignment. Note + * that the actual data must be at the start of the SKB data + * already. + * @vendor_radiotap_oui: radiotap vendor namespace OUI + * @vendor_radiotap_subns: radiotap vendor sub namespace */ struct ieee80211_rx_status { u64 mactime; u32 device_timestamp; u32 ampdu_reference; u32 flag; + u32 vendor_radiotap_bitmap; + u16 vendor_radiotap_len; u16 freq; u8 rate_idx; + u8 vht_nss; u8 rx_flags; u8 band; u8 antenna; s8 signal; u8 ampdu_delimiter_crc; + u8 vendor_radiotap_align; + u8 vendor_radiotap_oui[3]; + u8 vendor_radiotap_subns; }; /** @@ -884,7 +948,8 @@ enum ieee80211_smps_mode { * powersave documentation below. This variable is valid only when * the CONF_PS flag is set. * - * @power_level: requested transmit power (in dBm) + * @power_level: requested transmit power (in dBm), backward compatibility + * value only that is set to the minimum of all interfaces * * @channel: the channel to tune to * @channel_type: the channel (HT) type @@ -2180,6 +2245,14 @@ enum ieee80211_rate_control_changed { * @sta_remove: Notifies low level driver about removal of an associated * station, AP, IBSS/WDS/mesh peer etc. This callback can sleep. * + * @sta_add_debugfs: Drivers can use this callback to add debugfs files + * when a station is added to mac80211's station list. This callback + * and @sta_remove_debugfs should be within a CONFIG_MAC80211_DEBUGFS + * conditional. This callback can sleep. + * + * @sta_remove_debugfs: Remove the debugfs files which were added using + * @sta_add_debugfs. This callback can sleep. + * * @sta_notify: Notifies low level driver about power state transition of an * associated station, AP, IBSS/WDS/mesh peer etc. For a VIF operating * in AP mode, this callback will not be called when the flag @@ -2381,6 +2454,17 @@ enum ieee80211_rate_control_changed { * to vif. Possible use is for hw queue remapping. * @unassign_vif_chanctx: Notifies device driver about channel context being * unbound from vif. + * @start_ap: Start operation on the AP interface, this is called after all the + * information in bss_conf is set and beacon can be retrieved. A channel + * context is bound before this is called. Note that if the driver uses + * software scan or ROC, this (and @stop_ap) isn't called when the AP is + * just "paused" for scanning/ROC, which is indicated by the beacon being + * disabled/enabled via @bss_info_changed. + * @stop_ap: Stop operation on the AP interface. + * + * @restart_complete: Called after a call to ieee80211_restart_hw(), when the + * reconfiguration has completed. This can help the driver implement the + * reconfiguration step. This callback may sleep. */ struct ieee80211_ops { void (*tx)(struct ieee80211_hw *hw, @@ -2406,6 +2490,9 @@ struct ieee80211_ops { struct ieee80211_bss_conf *info, u32 changed); + int (*start_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + void (*stop_ap)(struct ieee80211_hw *hw, struct ieee80211_vif *vif); + u64 (*prepare_multicast)(struct ieee80211_hw *hw, struct netdev_hw_addr_list *mc_list); void (*configure_filter)(struct ieee80211_hw *hw, @@ -2447,6 +2534,16 @@ struct ieee80211_ops { struct ieee80211_sta *sta); int (*sta_remove)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta); +#ifdef CONFIG_MAC80211_DEBUGFS + void (*sta_add_debugfs)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir); + void (*sta_remove_debugfs)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct dentry *dir); +#endif void (*sta_notify)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum sta_notify_cmd, struct ieee80211_sta *sta); int (*sta_state)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -2488,8 +2585,8 @@ struct ieee80211_ops { int (*get_antenna)(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant); int (*remain_on_channel)(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, int duration); int (*cancel_remain_on_channel)(struct ieee80211_hw *hw); int (*set_ringparam)(struct ieee80211_hw *hw, u32 tx, u32 rx); @@ -2539,6 +2636,8 @@ struct ieee80211_ops { void (*unassign_vif_chanctx)(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_chanctx_conf *ctx); + + void (*restart_complete)(struct ieee80211_hw *hw); }; /** @@ -3385,6 +3484,21 @@ void ieee80211_sched_scan_results(struct ieee80211_hw *hw); void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); /** + * enum ieee80211_interface_iteration_flags - interface iteration flags + * @IEEE80211_IFACE_ITER_NORMAL: Iterate over all interfaces that have + * been added to the driver; However, note that during hardware + * reconfiguration (after restart_hw) it will iterate over a new + * interface and over all the existing interfaces even if they + * haven't been re-added to the driver yet. + * @IEEE80211_IFACE_ITER_RESUME_ALL: During resume, iterate over all + * interfaces, even if they haven't been re-added to the driver yet. + */ +enum ieee80211_interface_iteration_flags { + IEEE80211_IFACE_ITER_NORMAL = 0, + IEEE80211_IFACE_ITER_RESUME_ALL = BIT(0), +}; + +/** * ieee80211_iterate_active_interfaces - iterate active interfaces * * This function iterates over the interfaces associated with a given @@ -3392,13 +3506,15 @@ void ieee80211_sched_scan_stopped(struct ieee80211_hw *hw); * This function allows the iterator function to sleep, when the iterator * function is atomic @ieee80211_iterate_active_interfaces_atomic can * be used. - * Does not iterate over a new interface during add_interface() + * Does not iterate over a new interface during add_interface(). * * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags * @iterator: the iterator function to call * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, + u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data); @@ -3410,13 +3526,15 @@ void ieee80211_iterate_active_interfaces(struct ieee80211_hw *hw, * hardware that are currently active and calls the callback for them. * This function requires the iterator callback function to be atomic, * if that is not desired, use @ieee80211_iterate_active_interfaces instead. - * Does not iterate over a new interface during add_interface() + * Does not iterate over a new interface during add_interface(). * * @hw: the hardware struct of which the interfaces should be iterated over + * @iter_flags: iteration flags, see &enum ieee80211_interface_iteration_flags * @iterator: the iterator function to call, cannot sleep * @data: first argument of the iterator function */ void ieee80211_iterate_active_interfaces_atomic(struct ieee80211_hw *hw, + u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), diff --git a/include/net/nfc/hci.h b/include/net/nfc/hci.h index 639f50af42d..671953e1157 100644 --- a/include/net/nfc/hci.h +++ b/include/net/nfc/hci.h @@ -149,6 +149,8 @@ void *nfc_hci_get_clientdata(struct nfc_hci_dev *hdev); void nfc_hci_driver_failure(struct nfc_hci_dev *hdev, int err); +int nfc_hci_result_to_errno(u8 result); + /* Host IDs */ #define NFC_HCI_HOST_CONTROLLER_ID 0x00 #define NFC_HCI_TERMINAL_HOST_ID 0x01 @@ -235,5 +237,6 @@ int nfc_hci_send_response(struct nfc_hci_dev *hdev, u8 gate, u8 response, int nfc_hci_send_event(struct nfc_hci_dev *hdev, u8 gate, u8 event, const u8 *param, size_t param_len); int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate); +u32 nfc_hci_sak_to_protocol(u8 sak); #endif /* __NET_HCI_H */ diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index 6b00c4fc429..5a15fabd6a7 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -125,7 +125,7 @@ extern void rtnl_af_unregister(struct rtnl_af_ops *ops); extern struct net *rtnl_link_get_net(struct net *src_net, struct nlattr *tb[]); -extern struct net_device *rtnl_create_link(struct net *src_net, struct net *net, +extern struct net_device *rtnl_create_link(struct net *net, char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]); extern int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm); diff --git a/include/net/sock.h b/include/net/sock.h index c945fba4f54..c4132c1b63a 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -132,6 +132,8 @@ struct net; * @skc_rcv_saddr: Bound local IPv4 addr * @skc_hash: hash value used with various protocol lookup tables * @skc_u16hashes: two u16 hash values used by UDP lookup tables + * @skc_dport: placeholder for inet_dport/tw_dport + * @skc_num: placeholder for inet_num/tw_num * @skc_family: network address family * @skc_state: Connection state * @skc_reuse: %SO_REUSEADDR setting @@ -149,16 +151,29 @@ struct net; * for struct sock and struct inet_timewait_sock. */ struct sock_common { - /* skc_daddr and skc_rcv_saddr must be grouped : - * cf INET_MATCH() and INET_TW_MATCH() + /* skc_daddr and skc_rcv_saddr must be grouped on a 8 bytes aligned + * address on 64bit arches : cf INET_MATCH() and INET_TW_MATCH() */ - __be32 skc_daddr; - __be32 skc_rcv_saddr; - + union { + unsigned long skc_addrpair; + struct { + __be32 skc_daddr; + __be32 skc_rcv_saddr; + }; + }; union { unsigned int skc_hash; __u16 skc_u16hashes[2]; }; + /* skc_dport && skc_num must be grouped as well */ + union { + u32 skc_portpair; + struct { + __be16 skc_dport; + __u16 skc_num; + }; + }; + unsigned short skc_family; volatile unsigned char skc_state; unsigned char skc_reuse; diff --git a/include/trace/events/gfpflags.h b/include/trace/events/gfpflags.h index 9391706e925..d6fd8e5b14b 100644 --- a/include/trace/events/gfpflags.h +++ b/include/trace/events/gfpflags.h @@ -36,6 +36,7 @@ {(unsigned long)__GFP_RECLAIMABLE, "GFP_RECLAIMABLE"}, \ {(unsigned long)__GFP_MOVABLE, "GFP_MOVABLE"}, \ {(unsigned long)__GFP_NOTRACK, "GFP_NOTRACK"}, \ + {(unsigned long)__GFP_NO_KSWAPD, "GFP_NO_KSWAPD"}, \ {(unsigned long)__GFP_OTHER_NODE, "GFP_OTHER_NODE"} \ ) : "GFP_NOWAIT" diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h index 617d0fbfc96..33a417481ad 100644 --- a/include/uapi/linux/nl80211.h +++ b/include/uapi/linux/nl80211.h @@ -118,8 +118,9 @@ * to get a list of all present wiphys. * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, - * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ (and the + * attributes determining the channel width; this is used for setting + * monitor mode channel), %NL80211_ATTR_WIPHY_RETRY_SHORT, * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. * However, for setting the channel, see %NL80211_CMD_SET_CHANNEL @@ -171,7 +172,7 @@ * %NL80211_ATTR_AKM_SUITES, %NL80211_ATTR_PRIVACY, * %NL80211_ATTR_AUTH_TYPE and %NL80211_ATTR_INACTIVITY_TIMEOUT. * The channel to use can be set on the interface or be given using the - * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_WIPHY_CHANNEL_TYPE attrs. + * %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel width. * @NL80211_CMD_NEW_BEACON: old alias for %NL80211_CMD_START_AP * @NL80211_CMD_STOP_AP: Stop AP operation on the given interface * @NL80211_CMD_DEL_BEACON: old alias for %NL80211_CMD_STOP_AP @@ -401,8 +402,7 @@ * a response while being associated to an AP on another channel. * %NL80211_ATTR_IFINDEX is used to specify which interface (and thus * radio) is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the - * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be - * optionally used to specify additional channel parameters. + * frequency for the operation. * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds * to remain on the channel. This command is also used as an event to * notify when the requested duration starts (it may take a while for the @@ -440,12 +440,11 @@ * as an event indicating reception of a frame that was not processed in * kernel code, but is for us (i.e., which may need to be processed in a * user space application). %NL80211_ATTR_FRAME is used to specify the - * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and - * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on - * which channel the frame is to be transmitted or was received. If this - * channel is not the current channel (remain-on-channel or the - * operational channel) the device will switch to the given channel and - * transmit the frame, optionally waiting for a response for the time + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ is used + * to indicate on which channel the frame is to be transmitted or was + * received. If this channel is not the current channel (remain-on-channel + * or the operational channel) the device will switch to the given channel + * and transmit the frame, optionally waiting for a response for the time * specified using %NL80211_ATTR_DURATION. When called, this operation * returns a cookie (%NL80211_ATTR_COOKIE) that will be included with the * TX status event pertaining to the TX request. @@ -473,8 +472,8 @@ * command is used as an event to indicate the that a trigger level was * reached. * @NL80211_CMD_SET_CHANNEL: Set the channel (using %NL80211_ATTR_WIPHY_FREQ - * and %NL80211_ATTR_WIPHY_CHANNEL_TYPE) the given interface (identifed - * by %NL80211_ATTR_IFINDEX) shall operate on. + * and the attributes determining channel width) the given interface + * (identifed by %NL80211_ATTR_IFINDEX) shall operate on. * In case multiple channels are supported by the device, the mechanism * with which it switches channels is implementation-defined. * When a monitor interface is given, it can only switch channel while @@ -526,6 +525,12 @@ * of PMKSA caching dandidates. * * @NL80211_CMD_TDLS_OPER: Perform a high-level TDLS command (e.g. link setup). + * In addition, this can be used as an event to request userspace to take + * actions on TDLS links (set up a new link or tear down an existing one). + * In such events, %NL80211_ATTR_TDLS_OPERATION indicates the requested + * operation, %NL80211_ATTR_MAC contains the peer MAC address, and + * %NL80211_ATTR_REASON_CODE the reason code to be used (only with + * %NL80211_TDLS_TEARDOWN). * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. * * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP @@ -562,8 +567,8 @@ * * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels * independently of the userspace SME, send this event indicating - * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ with - * %NL80211_ATTR_WIPHY_CHANNEL_TYPE. + * %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the + * attributes determining channel width. * * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by * its %NL80211_ATTR_WDEV identifier. It must have been created with @@ -578,6 +583,9 @@ * station, due to particular reason. %NL80211_ATTR_CONN_FAILED_REASON * is used for this. * + * @NL80211_CMD_SET_MCAST_RATE: Change the rate used to send multicast frames + * for IBSS or MESH vif. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -726,6 +734,8 @@ enum nl80211_commands { NL80211_CMD_CONN_FAILED, + NL80211_CMD_SET_MCAST_RATE, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -762,14 +772,26 @@ enum nl80211_commands { * /sys/class/ieee80211/<phyname>/index * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters - * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz, + * defines the channel together with the (deprecated) + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE attribute or the attributes + * %NL80211_ATTR_CHANNEL_WIDTH and if needed %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 + * @NL80211_ATTR_CHANNEL_WIDTH: u32 attribute containing one of the values + * of &enum nl80211_chan_width, describing the channel width. See the + * documentation of the enum for more information. + * @NL80211_ATTR_CENTER_FREQ1: Center frequency of the first part of the + * channel, used for anything but 20 MHz bandwidth + * @NL80211_ATTR_CENTER_FREQ2: Center frequency of the second part of the + * channel, used only for 80+80 MHz bandwidth * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ - * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * if HT20 or HT40 are to be used (i.e., HT disabled if not included): * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including * this attribute) * NL80211_CHAN_HT20 = HT20 only * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * This attribute is now deprecated. * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is * less than or equal to the RTS threshold; allowed range: 1..255; * dot11ShortRetryLimit; u8 @@ -1544,6 +1566,10 @@ enum nl80211_attrs { NL80211_ATTR_SCAN_FLAGS, + NL80211_ATTR_CHANNEL_WIDTH, + NL80211_ATTR_CENTER_FREQ1, + NL80211_ATTR_CENTER_FREQ2, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1708,10 +1734,15 @@ struct nl80211_sta_flag_update { * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) - * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 MHz dualchannel bitrate * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval * @NL80211_RATE_INFO_BITRATE32: total bitrate (u32, 100kbit/s) * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @NL80211_RATE_INFO_VHT_MCS: MCS index for VHT (u8) + * @NL80211_RATE_INFO_VHT_NSS: number of streams in VHT (u8) + * @NL80211_RATE_INFO_80_MHZ_WIDTH: 80 MHz VHT rate + * @NL80211_RATE_INFO_80P80_MHZ_WIDTH: 80+80 MHz VHT rate + * @NL80211_RATE_INFO_160_MHZ_WIDTH: 160 MHz VHT rate * @__NL80211_RATE_INFO_AFTER_LAST: internal use */ enum nl80211_rate_info { @@ -1721,6 +1752,11 @@ enum nl80211_rate_info { NL80211_RATE_INFO_40_MHZ_WIDTH, NL80211_RATE_INFO_SHORT_GI, NL80211_RATE_INFO_BITRATE32, + NL80211_RATE_INFO_VHT_MCS, + NL80211_RATE_INFO_VHT_NSS, + NL80211_RATE_INFO_80_MHZ_WIDTH, + NL80211_RATE_INFO_80P80_MHZ_WIDTH, + NL80211_RATE_INFO_160_MHZ_WIDTH, /* keep last */ __NL80211_RATE_INFO_AFTER_LAST, @@ -2429,6 +2465,15 @@ enum nl80211_ac { #define NL80211_TXQ_Q_BE NL80211_AC_BE #define NL80211_TXQ_Q_BK NL80211_AC_BK +/** + * enum nl80211_channel_type - channel type + * @NL80211_CHAN_NO_HT: 20 MHz, non-HT channel + * @NL80211_CHAN_HT20: 20 MHz HT channel + * @NL80211_CHAN_HT40MINUS: HT40 channel, secondary channel + * below the control channel + * @NL80211_CHAN_HT40PLUS: HT40 channel, secondary channel + * above the control channel + */ enum nl80211_channel_type { NL80211_CHAN_NO_HT, NL80211_CHAN_HT20, @@ -2437,6 +2482,32 @@ enum nl80211_channel_type { }; /** + * enum nl80211_chan_width - channel width definitions + * + * These values are used with the %NL80211_ATTR_CHANNEL_WIDTH + * attribute. + * + * @NL80211_CHAN_WIDTH_20_NOHT: 20 MHz, non-HT channel + * @NL80211_CHAN_WIDTH_20: 20 MHz HT channel + * @NL80211_CHAN_WIDTH_40: 40 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80: 80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + * @NL80211_CHAN_WIDTH_80P80: 80+80 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well + * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1 + * attribute must be provided as well + */ +enum nl80211_chan_width { + NL80211_CHAN_WIDTH_20_NOHT, + NL80211_CHAN_WIDTH_20, + NL80211_CHAN_WIDTH_40, + NL80211_CHAN_WIDTH_80, + NL80211_CHAN_WIDTH_80P80, + NL80211_CHAN_WIDTH_160, +}; + +/** * enum nl80211_bss - netlink attributes for a BSS * * @__NL80211_BSS_INVALID: invalid @@ -3051,6 +3122,10 @@ enum nl80211_ap_sme_features { * @NL80211_FEATURE_LOW_PRIORITY_SCAN: This driver supports low priority scan * @NL80211_FEATURE_SCAN_FLUSH: Scan flush is supported * @NL80211_FEATURE_AP_SCAN: Support scanning using an AP vif + * @NL80211_FEATURE_VIF_TXPOWER: The driver supports per-vif TX power setting + * @NL80211_FEATURE_NEED_OBSS_SCAN: The driver expects userspace to perform + * OBSS scans and generate 20/40 BSS coex reports. This flag is used only + * for drivers implementing the CONNECT API, for AUTH/ASSOC it is implied. */ enum nl80211_feature_flags { NL80211_FEATURE_SK_TX_STATUS = 1 << 0, @@ -3062,6 +3137,8 @@ enum nl80211_feature_flags { NL80211_FEATURE_LOW_PRIORITY_SCAN = 1 << 6, NL80211_FEATURE_SCAN_FLUSH = 1 << 7, NL80211_FEATURE_AP_SCAN = 1 << 8, + NL80211_FEATURE_VIF_TXPOWER = 1 << 9, + NL80211_FEATURE_NEED_OBSS_SCAN = 1 << 10, }; /** diff --git a/kernel/futex.c b/kernel/futex.c index 20ef219bbe9..19eb089ca00 100644 --- a/kernel/futex.c +++ b/kernel/futex.c @@ -843,6 +843,9 @@ static void wake_futex(struct futex_q *q) { struct task_struct *p = q->task; + if (WARN(q->pi_state || q->rt_waiter, "refusing to wake PI futex\n")) + return; + /* * We set q->lock_ptr = NULL _before_ we wake up the task. If * a non-futex wake up happens on another CPU then the task @@ -1078,6 +1081,10 @@ retry_private: plist_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key1)) { + if (this->pi_state || this->rt_waiter) { + ret = -EINVAL; + goto out_unlock; + } wake_futex(this); if (++ret >= nr_wake) break; @@ -1090,6 +1097,10 @@ retry_private: op_ret = 0; plist_for_each_entry_safe(this, next, head, list) { if (match_futex (&this->key, &key2)) { + if (this->pi_state || this->rt_waiter) { + ret = -EINVAL; + goto out_unlock; + } wake_futex(this); if (++op_ret >= nr_wake2) break; @@ -1098,6 +1109,7 @@ retry_private: ret += op_ret; } +out_unlock: double_unlock_hb(hb1, hb2); out_put_keys: put_futex_key(&key2); @@ -1387,9 +1399,13 @@ retry_private: /* * FUTEX_WAIT_REQEUE_PI and FUTEX_CMP_REQUEUE_PI should always * be paired with each other and no other futex ops. + * + * We should never be requeueing a futex_q with a pi_state, + * which is awaiting a futex_unlock_pi(). */ if ((requeue_pi && !this->rt_waiter) || - (!requeue_pi && this->rt_waiter)) { + (!requeue_pi && this->rt_waiter) || + this->pi_state) { ret = -EINVAL; break; } diff --git a/kernel/watchdog.c b/kernel/watchdog.c index 9d4c8d5a1f5..dd4b80a9f1a 100644 --- a/kernel/watchdog.c +++ b/kernel/watchdog.c @@ -116,7 +116,7 @@ static unsigned long get_timestamp(int this_cpu) return cpu_clock(this_cpu) >> 30LL; /* 2^30 ~= 10^9 */ } -static unsigned long get_sample_period(void) +static u64 get_sample_period(void) { /* * convert watchdog_thresh from seconds to ns @@ -125,7 +125,7 @@ static unsigned long get_sample_period(void) * and hard thresholds) to increment before the * hardlockup detector generates a warning */ - return get_softlockup_thresh() * (NSEC_PER_SEC / 5); + return get_softlockup_thresh() * ((u64)NSEC_PER_SEC / 5); } /* Commands for resetting the watchdog */ diff --git a/lib/mpi/longlong.h b/lib/mpi/longlong.h index 678ce4f1e12..095ab157a52 100644 --- a/lib/mpi/longlong.h +++ b/lib/mpi/longlong.h @@ -641,7 +641,14 @@ do { \ ************** MIPS ***************** ***************************************/ #if defined(__mips__) && W_TYPE_SIZE == 32 -#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7 +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 +#define umul_ppmm(w1, w0, u, v) \ +do { \ + UDItype __ll = (UDItype)(u) * (v); \ + w1 = __ll >> 32; \ + w0 = __ll; \ +} while (0) +#elif __GNUC__ > 2 || __GNUC_MINOR__ >= 7 #define umul_ppmm(w1, w0, u, v) \ __asm__ ("multu %2,%3" \ : "=l" ((USItype)(w0)), \ @@ -666,7 +673,15 @@ do { \ ************** MIPS/64 ************** ***************************************/ #if (defined(__mips) && __mips >= 3) && W_TYPE_SIZE == 64 -#if __GNUC__ > 2 || __GNUC_MINOR__ >= 7 +#if __GNUC__ >= 4 && __GNUC_MINOR__ >= 4 +#define umul_ppmm(w1, w0, u, v) \ +do { \ + typedef unsigned int __ll_UTItype __attribute__((mode(TI))); \ + __ll_UTItype __ll = (__ll_UTItype)(u) * (v); \ + w1 = __ll >> 64; \ + w0 = __ll; \ +} while (0) +#elif __GNUC__ > 2 || __GNUC_MINOR__ >= 7 #define umul_ppmm(w1, w0, u, v) \ __asm__ ("dmultu %2,%3" \ : "=l" ((UDItype)(w0)), \ diff --git a/mm/page_alloc.c b/mm/page_alloc.c index bcb72c6e2b2..92871579cbe 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -2416,8 +2416,9 @@ __alloc_pages_slowpath(gfp_t gfp_mask, unsigned int order, goto nopage; restart: - wake_all_kswapd(order, zonelist, high_zoneidx, - zone_idx(preferred_zone)); + if (!(gfp_mask & __GFP_NO_KSWAPD)) + wake_all_kswapd(order, zonelist, high_zoneidx, + zone_idx(preferred_zone)); /* * OK, we're below the kswapd watermark and have kicked background @@ -2494,7 +2495,7 @@ rebalance: * system then fail the allocation instead of entering direct reclaim. */ if ((deferred_compaction || contended_compaction) && - (gfp_mask & (__GFP_MOVABLE|__GFP_REPEAT)) == __GFP_MOVABLE) + (gfp_mask & __GFP_NO_KSWAPD)) goto nopage; /* Try direct reclaim and then allocating */ diff --git a/mm/vmscan.c b/mm/vmscan.c index 48550c66f1f..cbf84e152f0 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2207,9 +2207,12 @@ static bool pfmemalloc_watermark_ok(pg_data_t *pgdat) * Throttle direct reclaimers if backing storage is backed by the network * and the PFMEMALLOC reserve for the preferred node is getting dangerously * depleted. kswapd will continue to make progress and wake the processes - * when the low watermark is reached + * when the low watermark is reached. + * + * Returns true if a fatal signal was delivered during throttling. If this + * happens, the page allocator should not consider triggering the OOM killer. */ -static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, +static bool throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, nodemask_t *nodemask) { struct zone *zone; @@ -2224,13 +2227,20 @@ static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, * processes to block on log_wait_commit(). */ if (current->flags & PF_KTHREAD) - return; + goto out; + + /* + * If a fatal signal is pending, this process should not throttle. + * It should return quickly so it can exit and free its memory + */ + if (fatal_signal_pending(current)) + goto out; /* Check if the pfmemalloc reserves are ok */ first_zones_zonelist(zonelist, high_zoneidx, NULL, &zone); pgdat = zone->zone_pgdat; if (pfmemalloc_watermark_ok(pgdat)) - return; + goto out; /* Account for the throttling */ count_vm_event(PGSCAN_DIRECT_THROTTLE); @@ -2246,12 +2256,20 @@ static void throttle_direct_reclaim(gfp_t gfp_mask, struct zonelist *zonelist, if (!(gfp_mask & __GFP_FS)) { wait_event_interruptible_timeout(pgdat->pfmemalloc_wait, pfmemalloc_watermark_ok(pgdat), HZ); - return; + + goto check_pending; } /* Throttle until kswapd wakes the process */ wait_event_killable(zone->zone_pgdat->pfmemalloc_wait, pfmemalloc_watermark_ok(pgdat)); + +check_pending: + if (fatal_signal_pending(current)) + return true; + +out: + return false; } unsigned long try_to_free_pages(struct zonelist *zonelist, int order, @@ -2273,13 +2291,12 @@ unsigned long try_to_free_pages(struct zonelist *zonelist, int order, .gfp_mask = sc.gfp_mask, }; - throttle_direct_reclaim(gfp_mask, zonelist, nodemask); - /* - * Do not enter reclaim if fatal signal is pending. 1 is returned so - * that the page allocator does not consider triggering OOM + * Do not enter reclaim if fatal signal was delivered while throttled. + * 1 is returned so that the page allocator does not OOM kill at this + * point. */ - if (fatal_signal_pending(current)) + if (throttle_direct_reclaim(gfp_mask, zonelist, nodemask)) return 1; trace_mm_vmscan_direct_reclaim_begin(order, diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index afba51e6031..a292e8050ef 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -242,6 +242,7 @@ static int register_vlan_device(struct net_device *real_dev, u16 vlan_id) * hope the underlying device can handle it. */ new_dev->mtu = real_dev->mtu; + new_dev->priv_flags |= (real_dev->priv_flags & IFF_UNICAST_FLT); vlan_dev_priv(new_dev)->vlan_id = vlan_id; vlan_dev_priv(new_dev)->real_dev = real_dev; diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index 365ed74f394..f1d37cd8181 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -30,6 +30,7 @@ #include "bridge_loop_avoidance.h" #include <linux/if_arp.h> +#include <linux/if_ether.h> void batadv_hardif_free_rcu(struct rcu_head *rcu) { @@ -311,7 +312,7 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface, { struct batadv_priv *bat_priv; struct net_device *soft_iface; - __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN); + __be16 ethertype = __constant_htons(ETH_P_BATMAN); int ret; if (hard_iface->if_status != BATADV_IF_NOT_IN_USE) diff --git a/net/batman-adv/packet.h b/net/batman-adv/packet.h index 1c5454d33f6..cb6405bf755 100644 --- a/net/batman-adv/packet.h +++ b/net/batman-adv/packet.h @@ -20,8 +20,6 @@ #ifndef _NET_BATMAN_ADV_PACKET_H_ #define _NET_BATMAN_ADV_PACKET_H_ -#define BATADV_ETH_P_BATMAN 0x4305 /* unofficial/not registered Ethertype */ - enum batadv_packettype { BATADV_IV_OGM = 0x01, BATADV_ICMP = 0x02, diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index c7f70237653..4425af9dad4 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -28,6 +28,8 @@ #include "gateway_common.h" #include "originator.h" +#include <linux/if_ether.h> + static void batadv_send_outstanding_bcast_packet(struct work_struct *work); /* send out an already prepared packet to the given address via the @@ -60,11 +62,11 @@ int batadv_send_skb_packet(struct sk_buff *skb, ethhdr = (struct ethhdr *)skb_mac_header(skb); 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(BATADV_ETH_P_BATMAN); + ethhdr->h_proto = __constant_htons(ETH_P_BATMAN); skb_set_network_header(skb, ETH_HLEN); skb->priority = TC_PRIO_CONTROL; - skb->protocol = __constant_htons(BATADV_ETH_P_BATMAN); + skb->protocol = __constant_htons(ETH_P_BATMAN); skb->dev = hard_iface->net_dev; diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index 54800c783f9..6b548fde8e0 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -34,6 +34,7 @@ #include <linux/ethtool.h> #include <linux/etherdevice.h> #include <linux/if_vlan.h> +#include <linux/if_ether.h> #include "unicast.h" #include "bridge_loop_avoidance.h" @@ -146,7 +147,7 @@ static int batadv_interface_tx(struct sk_buff *skb, struct batadv_hard_iface *primary_if = NULL; struct batadv_bcast_packet *bcast_packet; struct vlan_ethhdr *vhdr; - __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN); + __be16 ethertype = __constant_htons(ETH_P_BATMAN); static const uint8_t stp_addr[ETH_ALEN] = {0x01, 0x80, 0xC2, 0x00, 0x00, 0x00}; static const uint8_t ectp_addr[ETH_ALEN] = {0xCF, 0x00, 0x00, 0x00, @@ -172,7 +173,7 @@ static int batadv_interface_tx(struct sk_buff *skb, break; /* fall through */ - case BATADV_ETH_P_BATMAN: + case ETH_P_BATMAN: goto dropped; } @@ -302,7 +303,7 @@ void batadv_interface_rx(struct net_device *soft_iface, struct vlan_ethhdr *vhdr; struct batadv_header *batadv_header = (struct batadv_header *)skb->data; short vid __maybe_unused = -1; - __be16 ethertype = __constant_htons(BATADV_ETH_P_BATMAN); + __be16 ethertype = __constant_htons(ETH_P_BATMAN); bool is_bcast; is_bcast = (batadv_header->packet_type == BATADV_BCAST); @@ -325,7 +326,7 @@ void batadv_interface_rx(struct net_device *soft_iface, break; /* fall through */ - case BATADV_ETH_P_BATMAN: + case ETH_P_BATMAN: goto dropped; } diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 1c11d0dcd86..d3f3f7b1d32 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -48,4 +48,3 @@ source "net/bluetooth/cmtp/Kconfig" source "net/bluetooth/hidp/Kconfig" source "drivers/bluetooth/Kconfig" - diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index d5136cfb57e..2f67d5ecc90 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -423,7 +423,7 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); - mgr->bredr_chan->ctrl_id = rsp->id; + mgr->bredr_chan->remote_amp_id = rsp->id; amp_create_phylink(hdev, mgr, hcon); @@ -939,7 +939,7 @@ void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) goto clean; req->local_id = hdev->id; - req->remote_id = bredr_chan->ctrl_id; + req->remote_id = bredr_chan->remote_amp_id; memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c index 231d7ef53ec..1b0d92c0643 100644 --- a/net/bluetooth/amp.c +++ b/net/bluetooth/amp.c @@ -372,3 +372,100 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr, hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp); } + +void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon) +{ + struct hci_dev *bredr_hdev = hci_dev_hold(bredr_hcon->hdev); + struct amp_mgr *mgr = hs_hcon->amp_mgr; + struct l2cap_chan *bredr_chan; + + BT_DBG("bredr_hcon %p hs_hcon %p mgr %p", bredr_hcon, hs_hcon, mgr); + + if (!bredr_hdev || !mgr || !mgr->bredr_chan) + return; + + bredr_chan = mgr->bredr_chan; + + l2cap_chan_lock(bredr_chan); + + set_bit(FLAG_EFS_ENABLE, &bredr_chan->flags); + bredr_chan->remote_amp_id = hs_hcon->remote_id; + bredr_chan->local_amp_id = hs_hcon->hdev->id; + bredr_chan->hs_hcon = hs_hcon; + bredr_chan->conn->mtu = hs_hcon->hdev->block_mtu; + + __l2cap_physical_cfm(bredr_chan, 0); + + l2cap_chan_unlock(bredr_chan); + + hci_dev_put(bredr_hdev); +} + +void amp_create_logical_link(struct l2cap_chan *chan) +{ + struct hci_cp_create_accept_logical_link cp; + struct hci_conn *hcon; + struct hci_dev *hdev; + + BT_DBG("chan %p", chan); + + if (!chan->hs_hcon) + return; + + hdev = hci_dev_hold(chan->hs_hcon->hdev); + if (!hdev) + return; + + BT_DBG("chan %p dst %pMR", chan, chan->conn->dst); + + hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, chan->conn->dst); + if (!hcon) + goto done; + + cp.phy_handle = hcon->handle; + + cp.tx_flow_spec.id = chan->local_id; + cp.tx_flow_spec.stype = chan->local_stype; + cp.tx_flow_spec.msdu = cpu_to_le16(chan->local_msdu); + cp.tx_flow_spec.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + cp.tx_flow_spec.acc_lat = cpu_to_le32(chan->local_acc_lat); + cp.tx_flow_spec.flush_to = cpu_to_le32(chan->local_flush_to); + + cp.rx_flow_spec.id = chan->remote_id; + cp.rx_flow_spec.stype = chan->remote_stype; + cp.rx_flow_spec.msdu = cpu_to_le16(chan->remote_msdu); + cp.rx_flow_spec.sdu_itime = cpu_to_le32(chan->remote_sdu_itime); + cp.rx_flow_spec.acc_lat = cpu_to_le32(chan->remote_acc_lat); + cp.rx_flow_spec.flush_to = cpu_to_le32(chan->remote_flush_to); + + if (hcon->out) + hci_send_cmd(hdev, HCI_OP_CREATE_LOGICAL_LINK, sizeof(cp), + &cp); + else + hci_send_cmd(hdev, HCI_OP_ACCEPT_LOGICAL_LINK, sizeof(cp), + &cp); + +done: + hci_dev_put(hdev); +} + +void amp_disconnect_logical_link(struct hci_chan *hchan) +{ + struct hci_conn *hcon = hchan->conn; + struct hci_cp_disconn_logical_link cp; + + if (hcon->state != BT_CONNECTED) { + BT_DBG("hchan %p not connected", hchan); + return; + } + + cp.log_handle = cpu_to_le16(hchan->handle); + hci_send_cmd(hcon->hdev, HCI_OP_DISCONN_LOGICAL_LINK, sizeof(cp), &cp); +} + +void amp_destroy_logical_link(struct hci_chan *hchan, u8 reason) +{ + BT_DBG("hchan %p", hchan); + + hci_chan_del(hchan); +} diff --git a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c index 98f86f91d47..e58c8b32589 100644 --- a/net/bluetooth/bnep/netdev.c +++ b/net/bluetooth/bnep/netdev.c @@ -25,7 +25,6 @@ SOFTWARE IS DISCLAIMED. */ -#include <linux/export.h> #include <linux/etherdevice.h> #include <net/bluetooth/bluetooth.h> diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index 50f0d135eb8..a4a9d4b6816 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -20,7 +20,7 @@ SOFTWARE IS DISCLAIMED. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <linux/types.h> diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c index aacb802d1ee..1c57482112b 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -20,7 +20,7 @@ SOFTWARE IS DISCLAIMED. */ -#include <linux/module.h> +#include <linux/export.h> #include <linux/types.h> #include <linux/capability.h> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index fe646211c61..25bfce0666e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -502,6 +502,9 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, { struct hci_conn *le; + if (test_bit(HCI_LE_PERIPHERAL, &hdev->flags)) + return ERR_PTR(-ENOTSUPP); + le = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (!le) { le = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); @@ -959,6 +962,7 @@ struct hci_chan *hci_chan_create(struct hci_conn *conn) chan->conn = conn; skb_queue_head_init(&chan->data_q); + chan->state = BT_CONNECTED; list_add_rcu(&chan->list, &conn->chan_list); @@ -976,6 +980,8 @@ void hci_chan_del(struct hci_chan *chan) synchronize_rcu(); + hci_conn_put(conn); + skb_queue_purge(&chan->data_q); kfree(chan); } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f01e5e135b9..7140f83328a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -178,48 +178,13 @@ static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) static void bredr_init(struct hci_dev *hdev) { - struct hci_cp_delete_stored_link_key cp; - __le16 param; - __u8 flt_type; - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; - /* Mandatory initialization */ - /* Read Local Supported Features */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); - - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); - - /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); - - /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); - - /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); - - /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); - - /* Optional initialization */ - - /* Clear Event Filters */ - flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); - - /* Connection accept timeout ~20 secs */ - param = __constant_cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - bacpy(&cp.bdaddr, BDADDR_ANY); - cp.delete_all = 1; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } static void amp_init(struct hci_dev *hdev) @@ -273,14 +238,6 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) } } -static void hci_le_init_req(struct hci_dev *hdev, unsigned long opt) -{ - BT_DBG("%s", hdev->name); - - /* Read LE buffer size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); -} - static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) { __u8 scan = opt; @@ -477,6 +434,8 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, BT_DBG("cache %p, %pMR", cache, &data->bdaddr); + hci_remove_remote_oob_data(hdev, &data->bdaddr); + if (ssp) *ssp = data->ssp_mode; @@ -637,6 +596,99 @@ done: return err; } +static u8 create_ad(struct hci_dev *hdev, u8 *ptr) +{ + u8 ad_len = 0, flags = 0; + size_t name_len; + + if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) + flags |= LE_AD_GENERAL; + + if (!lmp_bredr_capable(hdev)) + flags |= LE_AD_NO_BREDR; + + if (lmp_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_CTRL; + + if (lmp_host_le_br_capable(hdev)) + flags |= LE_AD_SIM_LE_BREDR_HOST; + + if (flags) { + BT_DBG("adv flags 0x%02x", flags); + + ptr[0] = 2; + ptr[1] = EIR_FLAGS; + ptr[2] = flags; + + ad_len += 3; + ptr += 3; + } + + if (hdev->adv_tx_power != HCI_TX_POWER_INVALID) { + ptr[0] = 2; + ptr[1] = EIR_TX_POWER; + ptr[2] = (u8) hdev->adv_tx_power; + + ad_len += 3; + ptr += 3; + } + + name_len = strlen(hdev->dev_name); + if (name_len > 0) { + size_t max_len = HCI_MAX_AD_LENGTH - ad_len - 2; + + if (name_len > max_len) { + name_len = max_len; + ptr[1] = EIR_NAME_SHORT; + } else + ptr[1] = EIR_NAME_COMPLETE; + + ptr[0] = name_len + 1; + + memcpy(ptr + 2, hdev->dev_name, name_len); + + ad_len += (name_len + 2); + ptr += (name_len + 2); + } + + return ad_len; +} + +int hci_update_ad(struct hci_dev *hdev) +{ + struct hci_cp_le_set_adv_data cp; + u8 len; + int err; + + hci_dev_lock(hdev); + + if (!lmp_le_capable(hdev)) { + err = -EINVAL; + goto unlock; + } + + memset(&cp, 0, sizeof(cp)); + + len = create_ad(hdev, cp.data); + + if (hdev->adv_data_len == len && + memcmp(cp.data, hdev->adv_data, len) == 0) { + err = 0; + goto unlock; + } + + memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); + hdev->adv_data_len = len; + + cp.length = len; + err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); + +unlock: + hci_dev_unlock(hdev); + + return err; +} + /* ---- HCI ioctl helpers ---- */ int hci_dev_open(__u16 dev) @@ -687,10 +739,6 @@ int hci_dev_open(__u16 dev) ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); - if (lmp_host_le_capable(hdev)) - ret = __hci_request(hdev, hci_le_init_req, 0, - HCI_INIT_TIMEOUT); - clear_bit(HCI_INIT, &hdev->flags); } @@ -698,6 +746,7 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); + hci_update_ad(hdev); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); @@ -1039,10 +1088,17 @@ int hci_get_dev_info(void __user *arg) di.type = (hdev->bus & 0x0f) | (hdev->dev_type << 4); di.flags = hdev->flags; di.pkt_type = hdev->pkt_type; - di.acl_mtu = hdev->acl_mtu; - di.acl_pkts = hdev->acl_pkts; - di.sco_mtu = hdev->sco_mtu; - di.sco_pkts = hdev->sco_pkts; + if (lmp_bredr_capable(hdev)) { + di.acl_mtu = hdev->acl_mtu; + di.acl_pkts = hdev->acl_pkts; + di.sco_mtu = hdev->sco_mtu; + di.sco_pkts = hdev->sco_pkts; + } else { + di.acl_mtu = hdev->le_mtu; + di.acl_pkts = hdev->le_pkts; + di.sco_mtu = 0; + di.sco_pkts = 0; + } di.link_policy = hdev->link_policy; di.link_mode = hdev->link_mode; @@ -1617,6 +1673,9 @@ int hci_le_scan(struct hci_dev *hdev, u8 type, u16 interval, u16 window, BT_DBG("%s", hdev->name); + if (test_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags)) + return -ENOTSUPP; + if (work_busy(&hdev->le_scan)) return -EINPROGRESS; @@ -1643,6 +1702,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->esco_type = (ESCO_HV1); hdev->link_mode = (HCI_LM_ACCEPT); hdev->io_capability = 0x03; /* No Input No Output */ + hdev->inq_tx_power = HCI_TX_POWER_INVALID; + hdev->adv_tx_power = HCI_TX_POWER_INVALID; hdev->sniff_max_interval = 800; hdev->sniff_min_interval = 80; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0383635f91f..9f5c5f24450 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -24,7 +24,6 @@ /* Bluetooth HCI event handling. */ -#include <linux/export.h> #include <asm/unaligned.h> #include <net/bluetooth/bluetooth.h> @@ -203,6 +202,11 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) BIT(HCI_PERIODIC_INQ)); hdev->discovery.state = DISCOVERY_STOPPED; + hdev->inq_tx_power = HCI_TX_POWER_INVALID; + hdev->adv_tx_power = HCI_TX_POWER_INVALID; + + memset(hdev->adv_data, 0, sizeof(hdev->adv_data)); + hdev->adv_data_len = 0; } static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -225,6 +229,9 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); + if (!status && !test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); } @@ -440,7 +447,7 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); - void *sent; + struct hci_cp_write_ssp_mode *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -448,10 +455,17 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + if (!status) { + if (sent->mode) + hdev->host_features[0] |= LMP_HOST_SSP; + else + hdev->host_features[0] &= ~LMP_HOST_SSP; + } + if (test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_ssp_enable_complete(hdev, *((u8 *) sent), status); + mgmt_ssp_enable_complete(hdev, sent->mode, status); else if (!status) { - if (*((u8 *) sent)) + if (sent->mode) set_bit(HCI_SSP_ENABLED, &hdev->dev_flags); else clear_bit(HCI_SSP_ENABLED, &hdev->dev_flags); @@ -460,10 +474,10 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) static u8 hci_get_inquiry_mode(struct hci_dev *hdev) { - if (hdev->features[6] & LMP_EXT_INQ) + if (lmp_ext_inq_capable(hdev)) return 2; - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) return 1; if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && @@ -507,28 +521,30 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (hdev->hci_ver < BLUETOOTH_VER_1_2) return; - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ + if (lmp_bredr_capable(hdev)) { + events[4] |= 0x01; /* Flow Specification Complete */ + events[4] |= 0x02; /* Inquiry Result with RSSI */ + events[4] |= 0x04; /* Read Remote Extended Features Complete */ + events[5] |= 0x08; /* Synchronous Connection Complete */ + events[5] |= 0x10; /* Synchronous Connection Changed */ + } - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) events[4] |= 0x02; /* Inquiry Result with RSSI */ if (lmp_sniffsubr_capable(hdev)) events[5] |= 0x20; /* Sniff Subrating */ - if (hdev->features[5] & LMP_PAUSE_ENC) + if (lmp_pause_enc_capable(hdev)) events[5] |= 0x80; /* Encryption Key Refresh Complete */ - if (hdev->features[6] & LMP_EXT_INQ) + if (lmp_ext_inq_capable(hdev)) events[5] |= 0x40; /* Extended Inquiry Result */ if (lmp_no_flush_capable(hdev)) events[7] |= 0x01; /* Enhanced Flush Complete */ - if (hdev->features[7] & LMP_LSTO) + if (lmp_lsto_capable(hdev)) events[6] |= 0x80; /* Link Supervision Timeout Changed */ if (lmp_ssp_capable(hdev)) { @@ -548,6 +564,53 @@ static void hci_setup_event_mask(struct hci_dev *hdev) events[7] |= 0x20; /* LE Meta-Event */ hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); + + if (lmp_le_capable(hdev)) { + memset(events, 0, sizeof(events)); + events[0] = 0x1f; + hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, + sizeof(events), events); + } +} + +static void bredr_setup(struct hci_dev *hdev) +{ + struct hci_cp_delete_stored_link_key cp; + __le16 param; + __u8 flt_type; + + /* Read Buffer Size (ACL mtu, max pkt, etc.) */ + hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); + + /* Read Class of Device */ + hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + + /* Read Local Name */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); + + /* Read Voice Setting */ + hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); + + /* Clear Event Filters */ + flt_type = HCI_FLT_CLEAR_ALL; + hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + + /* Connection accept timeout ~20 secs */ + param = __constant_cpu_to_le16(0x7d00); + hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); + + bacpy(&cp.bdaddr, BDADDR_ANY); + cp.delete_all = 1; + hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); +} + +static void le_setup(struct hci_dev *hdev) +{ + /* Read LE Buffer Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + + /* Read LE Advertising Channel TX Power */ + hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); } static void hci_setup(struct hci_dev *hdev) @@ -555,6 +618,15 @@ static void hci_setup(struct hci_dev *hdev) if (hdev->dev_type != HCI_BREDR) return; + /* Read BD Address */ + hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); + + if (lmp_bredr_capable(hdev)) + bredr_setup(hdev); + + if (lmp_le_capable(hdev)) + le_setup(hdev); + hci_setup_event_mask(hdev); if (hdev->hci_ver > BLUETOOTH_VER_1_1) @@ -575,13 +647,13 @@ static void hci_setup(struct hci_dev *hdev) } } - if (hdev->features[3] & LMP_RSSI_INQ) + if (lmp_inq_rssi_capable(hdev)) hci_setup_inquiry_mode(hdev); - if (hdev->features[7] & LMP_INQ_TX_PWR) + if (lmp_inq_tx_pwr_capable(hdev)) hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); - if (hdev->features[7] & LMP_EXTFEATURES) { + if (lmp_ext_feat_capable(hdev)) { struct hci_cp_read_local_ext_features cp; cp.page = 0x01; @@ -628,11 +700,11 @@ static void hci_setup_link_policy(struct hci_dev *hdev) if (lmp_rswitch_capable(hdev)) link_policy |= HCI_LP_RSWITCH; - if (hdev->features[0] & LMP_HOLD) + if (lmp_hold_capable(hdev)) link_policy |= HCI_LP_HOLD; if (lmp_sniff_capable(hdev)) link_policy |= HCI_LP_SNIFF; - if (hdev->features[1] & LMP_PARK) + if (lmp_park_capable(hdev)) link_policy |= HCI_LP_PARK; cp.policy = cpu_to_le16(link_policy); @@ -722,10 +794,10 @@ static void hci_set_le_support(struct hci_dev *hdev) if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + cp.simul = !!lmp_le_br_capable(hdev); } - if (cp.le != !!(hdev->host_features[0] & LMP_HOST_LE)) + if (cp.le != !!lmp_host_le_capable(hdev)) hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), &cp); } @@ -1018,6 +1090,31 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } +static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_le_read_adv_tx_power *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (!rp->status) { + hdev->adv_tx_power = rp->tx_power; + if (!test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + } + + hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status); +} + +static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status); +} + static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; @@ -1093,6 +1190,33 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, hci_dev_unlock(hdev); } +static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) +{ + __u8 *sent, status = *((__u8 *) skb->data); + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE); + if (!sent) + return; + + hci_dev_lock(hdev); + + if (!status) { + if (*sent) + set_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); + else + clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); + } + + hci_dev_unlock(hdev); + + if (!test_bit(HCI_INIT, &hdev->flags)) + hci_update_ad(hdev); + + hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status); +} + static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -1207,6 +1331,11 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, hdev->host_features[0] |= LMP_HOST_LE; else hdev->host_features[0] &= ~LMP_HOST_LE; + + if (sent->simul) + hdev->host_features[0] |= LMP_HOST_LE_BREDR; + else + hdev->host_features[0] &= ~LMP_HOST_LE_BREDR; } if (test_bit(HCI_MGMT, &hdev->dev_flags) && @@ -1718,14 +1847,23 @@ static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, status); - if (status) - return; - cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK); if (!cp) return; - amp_write_remote_assoc(hdev, cp->phy_handle); + hci_dev_lock(hdev); + + if (status) { + struct hci_conn *hcon; + + hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle); + if (hcon) + hci_conn_del(hcon); + } else { + amp_write_remote_assoc(hdev, cp->phy_handle); + } + + hci_dev_unlock(hdev); } static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) @@ -1744,6 +1882,11 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } +static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status) +{ + BT_DBG("%s status 0x%2.2x", hdev->name, status); +} + static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2441,6 +2584,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_buffer_size(hdev, skb); break; + case HCI_OP_LE_READ_ADV_TX_POWER: + hci_cc_le_read_adv_tx_power(hdev, skb); + break; + + case HCI_OP_LE_SET_EVENT_MASK: + hci_cc_le_set_event_mask(hdev, skb); + break; + case HCI_OP_USER_CONFIRM_REPLY: hci_cc_user_confirm_reply(hdev, skb); break; @@ -2461,6 +2612,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_set_scan_param(hdev, skb); break; + case HCI_OP_LE_SET_ADV_ENABLE: + hci_cc_le_set_adv_enable(hdev, skb); + break; + case HCI_OP_LE_SET_SCAN_ENABLE: hci_cc_le_set_scan_enable(hdev, skb); break; @@ -2570,6 +2725,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_accept_phylink(hdev, ev->status); break; + case HCI_OP_CREATE_LOGICAL_LINK: + hci_cs_create_logical_link(hdev, ev->status); + break; + default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; @@ -3544,6 +3703,130 @@ unlock: hci_dev_unlock(hdev); } +static void hci_phy_link_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_phy_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon, *bredr_hcon; + + BT_DBG("%s handle 0x%2.2x status 0x%2.2x", hdev->name, ev->phy_handle, + ev->status); + + hci_dev_lock(hdev); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) { + hci_dev_unlock(hdev); + return; + } + + if (ev->status) { + hci_conn_del(hcon); + hci_dev_unlock(hdev); + return; + } + + bredr_hcon = hcon->amp_mgr->l2cap_conn->hcon; + + hcon->state = BT_CONNECTED; + bacpy(&hcon->dst, &bredr_hcon->dst); + + hci_conn_hold(hcon); + hcon->disc_timeout = HCI_DISCONN_TIMEOUT; + hci_conn_put(hcon); + + hci_conn_hold_device(hcon); + hci_conn_add_sysfs(hcon); + + amp_physical_cfm(bredr_hcon, hcon); + + hci_dev_unlock(hdev); +} + +static void hci_loglink_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_ev_logical_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon; + struct hci_chan *hchan; + struct amp_mgr *mgr; + + BT_DBG("%s log_handle 0x%4.4x phy_handle 0x%2.2x status 0x%2.2x", + hdev->name, le16_to_cpu(ev->handle), ev->phy_handle, + ev->status); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (!hcon) + return; + + /* Create AMP hchan */ + hchan = hci_chan_create(hcon); + if (!hchan) + return; + + hchan->handle = le16_to_cpu(ev->handle); + + BT_DBG("hcon %p mgr %p hchan %p", hcon, hcon->amp_mgr, hchan); + + mgr = hcon->amp_mgr; + if (mgr && mgr->bredr_chan) { + struct l2cap_chan *bredr_chan = mgr->bredr_chan; + + l2cap_chan_lock(bredr_chan); + + bredr_chan->conn->mtu = hdev->block_mtu; + l2cap_logical_cfm(bredr_chan, hchan, 0); + hci_conn_hold(hcon); + + l2cap_chan_unlock(bredr_chan); + } +} + +static void hci_disconn_loglink_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_disconn_logical_link_complete *ev = (void *) skb->data; + struct hci_chan *hchan; + + BT_DBG("%s log handle 0x%4.4x status 0x%2.2x", hdev->name, + le16_to_cpu(ev->handle), ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + hchan = hci_chan_lookup_handle(hdev, le16_to_cpu(ev->handle)); + if (!hchan) + goto unlock; + + amp_destroy_logical_link(hchan, ev->reason); + +unlock: + hci_dev_unlock(hdev); +} + +static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_disconn_phy_link_complete *ev = (void *) skb->data; + struct hci_conn *hcon; + + BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle); + if (hcon) { + hcon->state = BT_CLOSED; + hci_conn_del(hcon); + } + + hci_dev_unlock(hdev); +} + static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; @@ -3871,6 +4154,22 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_remote_oob_data_request_evt(hdev, skb); break; + case HCI_EV_PHY_LINK_COMPLETE: + hci_phy_link_complete_evt(hdev, skb); + break; + + case HCI_EV_LOGICAL_LINK_COMPLETE: + hci_loglink_complete_evt(hdev, skb); + break; + + case HCI_EV_DISCONN_LOGICAL_LINK_COMPLETE: + hci_disconn_loglink_complete_evt(hdev, skb); + break; + + case HCI_EV_DISCONN_PHY_LINK_COMPLETE: + hci_disconn_phylink_complete_evt(hdev, skb); + break; + case HCI_EV_NUM_COMP_BLOCKS: hci_num_comp_blocks_evt(hdev, skb); break; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 08efc256c93..b52f66d2243 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -38,6 +38,7 @@ #include <net/bluetooth/l2cap.h> #include <net/bluetooth/smp.h> #include <net/bluetooth/a2mp.h> +#include <net/bluetooth/amp.h> bool disable_ertm; @@ -100,6 +101,23 @@ static struct l2cap_chan *l2cap_get_chan_by_scid(struct l2cap_conn *conn, return c; } +/* Find channel with given DCID. + * Returns locked channel. + */ +static struct l2cap_chan *l2cap_get_chan_by_dcid(struct l2cap_conn *conn, + u16 cid) +{ + struct l2cap_chan *c; + + mutex_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_dcid(conn, cid); + if (c) + l2cap_chan_lock(c); + mutex_unlock(&conn->chan_lock); + + return c; +} + static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, u8 ident) { @@ -112,6 +130,20 @@ static struct l2cap_chan *__l2cap_get_chan_by_ident(struct l2cap_conn *conn, return NULL; } +static struct l2cap_chan *l2cap_get_chan_by_ident(struct l2cap_conn *conn, + u8 ident) +{ + struct l2cap_chan *c; + + mutex_lock(&conn->chan_lock); + c = __l2cap_get_chan_by_ident(conn, ident); + if (c) + l2cap_chan_lock(c); + mutex_unlock(&conn->chan_lock); + + return c; +} + static struct l2cap_chan *__l2cap_global_chan_by_addr(__le16 psm, bdaddr_t *src) { struct l2cap_chan *c; @@ -546,6 +578,13 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) mgr->bredr_chan = NULL; } + if (chan->hs_hchan) { + struct hci_chan *hs_hchan = chan->hs_hchan; + + BT_DBG("chan %p disconnect hs_hchan %p", chan, hs_hchan); + amp_disconnect_logical_link(hs_hchan); + } + chan->ops->teardown(chan, err); if (test_bit(CONF_NOT_COMPLETE, &chan->conf_state)) @@ -718,6 +757,12 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, hci_send_acl(conn->hchan, skb, flags); } +static bool __chan_is_moving(struct l2cap_chan *chan) +{ + return chan->move_state != L2CAP_MOVE_STABLE && + chan->move_state != L2CAP_MOVE_WAIT_PREPARE; +} + static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) { struct hci_conn *hcon = chan->conn->hcon; @@ -726,6 +771,15 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, skb->priority); + if (chan->hs_hcon && !__chan_is_moving(chan)) { + if (chan->hs_hchan) + hci_send_acl(chan->hs_hchan, skb, ACL_COMPLETE); + else + kfree_skb(skb); + + return; + } + if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; @@ -901,6 +955,9 @@ static void l2cap_send_sframe(struct l2cap_chan *chan, if (!control->sframe) return; + if (__chan_is_moving(chan)) + return; + if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state) && !control->poll) control->final = 1; @@ -964,6 +1021,12 @@ static bool __amp_capable(struct l2cap_chan *chan) return false; } +static bool l2cap_check_efs(struct l2cap_chan *chan) +{ + /* Check EFS parameters */ + return true; +} + void l2cap_send_conn_req(struct l2cap_chan *chan) { struct l2cap_conn *conn = chan->conn; @@ -979,6 +1042,76 @@ void l2cap_send_conn_req(struct l2cap_chan *chan) l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_REQ, sizeof(req), &req); } +static void l2cap_send_create_chan_req(struct l2cap_chan *chan, u8 amp_id) +{ + struct l2cap_create_chan_req req; + req.scid = cpu_to_le16(chan->scid); + req.psm = chan->psm; + req.amp_id = amp_id; + + chan->ident = l2cap_get_ident(chan->conn); + + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_REQ, + sizeof(req), &req); +} + +static void l2cap_move_setup(struct l2cap_chan *chan) +{ + struct sk_buff *skb; + + BT_DBG("chan %p", chan); + + if (chan->mode != L2CAP_MODE_ERTM) + return; + + __clear_retrans_timer(chan); + __clear_monitor_timer(chan); + __clear_ack_timer(chan); + + chan->retry_count = 0; + skb_queue_walk(&chan->tx_q, skb) { + if (bt_cb(skb)->control.retries) + bt_cb(skb)->control.retries = 1; + else + break; + } + + chan->expected_tx_seq = chan->buffer_seq; + + clear_bit(CONN_REJ_ACT, &chan->conn_state); + clear_bit(CONN_SREJ_ACT, &chan->conn_state); + l2cap_seq_list_clear(&chan->retrans_list); + l2cap_seq_list_clear(&chan->srej_list); + skb_queue_purge(&chan->srej_q); + + chan->tx_state = L2CAP_TX_STATE_XMIT; + chan->rx_state = L2CAP_RX_STATE_MOVE; + + set_bit(CONN_REMOTE_BUSY, &chan->conn_state); +} + +static void l2cap_move_done(struct l2cap_chan *chan) +{ + u8 move_role = chan->move_role; + BT_DBG("chan %p", chan); + + chan->move_state = L2CAP_MOVE_STABLE; + chan->move_role = L2CAP_MOVE_ROLE_NONE; + + if (chan->mode != L2CAP_MODE_ERTM) + return; + + switch (move_role) { + case L2CAP_MOVE_ROLE_INITIATOR: + l2cap_tx(chan, NULL, NULL, L2CAP_EV_EXPLICIT_POLL); + chan->rx_state = L2CAP_RX_STATE_WAIT_F; + break; + case L2CAP_MOVE_ROLE_RESPONDER: + chan->rx_state = L2CAP_RX_STATE_WAIT_P; + break; + } +} + static void l2cap_chan_ready(struct l2cap_chan *chan) { /* This clears all conf flags, including CONF_NOT_COMPLETE */ @@ -1695,6 +1828,9 @@ static void l2cap_streaming_send(struct l2cap_chan *chan, BT_DBG("chan %p, skbs %p", chan, skbs); + if (__chan_is_moving(chan)) + return; + skb_queue_splice_tail_init(skbs, &chan->tx_q); while (!skb_queue_empty(&chan->tx_q)) { @@ -1737,6 +1873,9 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return 0; + if (__chan_is_moving(chan)) + return 0; + while (chan->tx_send_head && chan->unacked_frames < chan->remote_tx_win && chan->tx_state == L2CAP_TX_STATE_XMIT) { @@ -1802,6 +1941,9 @@ static void l2cap_ertm_resend(struct l2cap_chan *chan) if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state)) return; + if (__chan_is_moving(chan)) + return; + while (chan->retrans_list.head != L2CAP_SEQ_LIST_CLEAR) { seq = l2cap_seq_list_pop(&chan->retrans_list); @@ -2144,7 +2286,9 @@ static int l2cap_segment_sdu(struct l2cap_chan *chan, /* PDU size is derived from the HCI MTU */ pdu_len = chan->conn->mtu; - pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); + /* Constrain PDU size for BR/EDR connections */ + if (!chan->hs_hcon) + pdu_len = min_t(size_t, pdu_len, L2CAP_BREDR_MAX_PAYLOAD); /* Adjust for largest possible L2CAP overhead. */ if (chan->fcs) @@ -2788,6 +2932,11 @@ int l2cap_ertm_init(struct l2cap_chan *chan) skb_queue_head_init(&chan->tx_q); + chan->local_amp_id = 0; + chan->move_id = 0; + chan->move_state = L2CAP_MOVE_STABLE; + chan->move_role = L2CAP_MOVE_ROLE_NONE; + if (chan->mode != L2CAP_MODE_ERTM) return 0; @@ -2834,6 +2983,44 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; } +static void __l2cap_set_ertm_timeouts(struct l2cap_chan *chan, + struct l2cap_conf_rfc *rfc) +{ + if (chan->local_amp_id && chan->hs_hcon) { + u64 ertm_to = chan->hs_hcon->hdev->amp_be_flush_to; + + /* Class 1 devices have must have ERTM timeouts + * exceeding the Link Supervision Timeout. The + * default Link Supervision Timeout for AMP + * controllers is 10 seconds. + * + * Class 1 devices use 0xffffffff for their + * best-effort flush timeout, so the clamping logic + * will result in a timeout that meets the above + * requirement. ERTM timeouts are 16-bit values, so + * the maximum timeout is 65.535 seconds. + */ + + /* Convert timeout to milliseconds and round */ + ertm_to = DIV_ROUND_UP_ULL(ertm_to, 1000); + + /* This is the recommended formula for class 2 devices + * that start ERTM timers when packets are sent to the + * controller. + */ + ertm_to = 3 * ertm_to + 500; + + if (ertm_to > 0xffff) + ertm_to = 0xffff; + + rfc->retrans_timeout = cpu_to_le16((u16) ertm_to); + rfc->monitor_timeout = rfc->retrans_timeout; + } else { + rfc->retrans_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); + rfc->monitor_timeout = __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + } +} + static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && @@ -2900,8 +3087,8 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; rfc.max_transmit = chan->max_tx; - rfc.retrans_timeout = 0; - rfc.monitor_timeout = 0; + + __l2cap_set_ertm_timeouts(chan, &rfc); size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - L2CAP_EXT_HDR_SIZE - L2CAP_SDULEN_SIZE - @@ -3129,10 +3316,7 @@ done: rfc.max_pdu_size = cpu_to_le16(size); chan->remote_mps = size; - rfc.retrans_timeout = - __constant_cpu_to_le16(L2CAP_DEFAULT_RETRANS_TO); - rfc.monitor_timeout = - __constant_cpu_to_le16(L2CAP_DEFAULT_MONITOR_TO); + __l2cap_set_ertm_timeouts(chan, &rfc); set_bit(CONF_MODE_DONE, &chan->conf_state); @@ -3308,12 +3492,21 @@ void __l2cap_connect_rsp_defer(struct l2cap_chan *chan) struct l2cap_conn_rsp rsp; struct l2cap_conn *conn = chan->conn; u8 buf[128]; + u8 rsp_code; rsp.scid = cpu_to_le16(chan->dcid); rsp.dcid = cpu_to_le16(chan->scid); rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); - l2cap_send_cmd(conn, chan->ident, L2CAP_CONN_RSP, sizeof(rsp), &rsp); + + if (chan->hs_hcon) + rsp_code = L2CAP_CREATE_CHAN_RSP; + else + rsp_code = L2CAP_CONN_RSP; + + BT_DBG("chan %p rsp_code %u", chan, rsp_code); + + l2cap_send_cmd(conn, chan->ident, rsp_code, sizeof(rsp), &rsp); if (test_and_set_bit(CONF_REQ_SENT, &chan->conf_state)) return; @@ -3395,8 +3588,9 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, return 0; } -static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, - u8 *data, u8 rsp_code, u8 amp_id) +static struct l2cap_chan *l2cap_connect(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u8 *data, u8 rsp_code, u8 amp_id) { struct l2cap_conn_req *req = (struct l2cap_conn_req *) data; struct l2cap_conn_rsp rsp; @@ -3447,6 +3641,7 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, bacpy(&bt_sk(sk)->dst, conn->dst); chan->psm = psm; chan->dcid = scid; + chan->local_amp_id = amp_id; __l2cap_chan_add(conn, chan); @@ -3464,8 +3659,17 @@ static void l2cap_connect(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, status = L2CAP_CS_AUTHOR_PEND; chan->ops->defer(chan); } else { - __l2cap_state_change(chan, BT_CONFIG); - result = L2CAP_CR_SUCCESS; + /* Force pending result for AMP controllers. + * The connection will succeed after the + * physical link is up. + */ + if (amp_id) { + __l2cap_state_change(chan, BT_CONNECT2); + result = L2CAP_CR_PEND; + } else { + __l2cap_state_change(chan, BT_CONFIG); + result = L2CAP_CR_SUCCESS; + } status = L2CAP_CS_NO_INFO; } } else { @@ -3511,6 +3715,8 @@ sendresp: l2cap_build_conf_req(chan, buf), buf); chan->num_conf_req++; } + + return chan; } static int l2cap_connect_req(struct l2cap_conn *conn, @@ -3520,7 +3726,7 @@ static int l2cap_connect_req(struct l2cap_conn *conn, return 0; } -static inline int l2cap_connect_rsp(struct l2cap_conn *conn, +static int l2cap_connect_create_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u8 *data) { struct l2cap_conn_rsp *rsp = (struct l2cap_conn_rsp *) data; @@ -3675,6 +3881,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, goto unlock; } + chan->ident = cmd->ident; l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, len, rsp); chan->num_conf_rsp++; @@ -3714,7 +3921,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, /* check compatibility */ /* Send rsp for BR/EDR channel */ - if (!chan->ctrl_id) + if (!chan->hs_hcon) l2cap_send_efs_conf_rsp(chan, rsp, cmd->ident, flags); else chan->ident = cmd->ident; @@ -3764,13 +3971,15 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, goto done; } - /* check compatibility */ - - if (!chan->ctrl_id) + if (!chan->hs_hcon) { l2cap_send_efs_conf_rsp(chan, buf, cmd->ident, 0); - else - chan->ident = cmd->ident; + } else { + if (l2cap_check_efs(chan)) { + amp_create_logical_link(chan); + chan->ident = cmd->ident; + } + } } goto done; @@ -4023,12 +4232,14 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, return 0; } -static inline int l2cap_create_channel_req(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static int l2cap_create_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_create_chan_req *req = data; struct l2cap_create_chan_rsp rsp; + struct l2cap_chan *chan; + struct hci_dev *hdev; u16 psm, scid; if (cmd_len != sizeof(*req)) @@ -4042,57 +4253,119 @@ static inline int l2cap_create_channel_req(struct l2cap_conn *conn, BT_DBG("psm 0x%2.2x, scid 0x%4.4x, amp_id %d", psm, scid, req->amp_id); - /* Placeholder: Always reject */ + /* For controller id 0 make BR/EDR connection */ + if (req->amp_id == HCI_BREDR_ID) { + l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, + req->amp_id); + return 0; + } + + /* Validate AMP controller id */ + hdev = hci_dev_get(req->amp_id); + if (!hdev) + goto error; + + if (hdev->dev_type != HCI_AMP || !test_bit(HCI_UP, &hdev->flags)) { + hci_dev_put(hdev); + goto error; + } + + chan = l2cap_connect(conn, cmd, data, L2CAP_CREATE_CHAN_RSP, + req->amp_id); + if (chan) { + struct amp_mgr *mgr = conn->hcon->amp_mgr; + struct hci_conn *hs_hcon; + + hs_hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, conn->dst); + if (!hs_hcon) { + hci_dev_put(hdev); + return -EFAULT; + } + + BT_DBG("mgr %p bredr_chan %p hs_hcon %p", mgr, chan, hs_hcon); + + mgr->bredr_chan = chan; + chan->hs_hcon = hs_hcon; + chan->fcs = L2CAP_FCS_NONE; + conn->mtu = hdev->block_mtu; + } + + hci_dev_put(hdev); + + return 0; + +error: rsp.dcid = 0; rsp.scid = cpu_to_le16(scid); - rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); + rsp.result = __constant_cpu_to_le16(L2CAP_CR_BAD_AMP); rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, sizeof(rsp), &rsp); - return 0; + return -EFAULT; } -static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - void *data) +static void l2cap_send_move_chan_req(struct l2cap_chan *chan, u8 dest_amp_id) { - BT_DBG("conn %p", conn); + struct l2cap_move_chan_req req; + u8 ident; + + BT_DBG("chan %p, dest_amp_id %d", chan, dest_amp_id); + + ident = l2cap_get_ident(chan->conn); + chan->ident = ident; - return l2cap_connect_rsp(conn, cmd, data); + req.icid = cpu_to_le16(chan->scid); + req.dest_amp_id = dest_amp_id; + + l2cap_send_cmd(chan->conn, ident, L2CAP_MOVE_CHAN_REQ, sizeof(req), + &req); + + __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT); } -static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, - u16 icid, u16 result) +static void l2cap_send_move_chan_rsp(struct l2cap_chan *chan, u16 result) { struct l2cap_move_chan_rsp rsp; - BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + BT_DBG("chan %p, result 0x%4.4x", chan, result); - rsp.icid = cpu_to_le16(icid); + rsp.icid = cpu_to_le16(chan->dcid); rsp.result = cpu_to_le16(result); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_RSP, + sizeof(rsp), &rsp); } -static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, - struct l2cap_chan *chan, - u16 icid, u16 result) +static void l2cap_send_move_chan_cfm(struct l2cap_chan *chan, u16 result) { struct l2cap_move_chan_cfm cfm; - u8 ident; - BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + BT_DBG("chan %p, result 0x%4.4x", chan, result); - ident = l2cap_get_ident(conn); - if (chan) - chan->ident = ident; + chan->ident = l2cap_get_ident(chan->conn); - cfm.icid = cpu_to_le16(icid); + cfm.icid = cpu_to_le16(chan->scid); cfm.result = cpu_to_le16(result); - l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_MOVE_CHAN_CFM, + sizeof(cfm), &cfm); + + __set_chan_timer(chan, L2CAP_MOVE_TIMEOUT); +} + +static void l2cap_send_move_chan_cfm_icid(struct l2cap_conn *conn, u16 icid) +{ + struct l2cap_move_chan_cfm cfm; + + BT_DBG("conn %p, icid 0x%4.4x", conn, icid); + + cfm.icid = cpu_to_le16(icid); + cfm.result = __constant_cpu_to_le16(L2CAP_MC_UNCONFIRMED); + + l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_MOVE_CHAN_CFM, + sizeof(cfm), &cfm); } static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, @@ -4106,11 +4379,289 @@ static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); } +static void __release_logical_link(struct l2cap_chan *chan) +{ + chan->hs_hchan = NULL; + chan->hs_hcon = NULL; + + /* Placeholder - release the logical link */ +} + +static void l2cap_logical_fail(struct l2cap_chan *chan) +{ + /* Logical link setup failed */ + if (chan->state != BT_CONNECTED) { + /* Create channel failure, disconnect */ + l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); + return; + } + + switch (chan->move_role) { + case L2CAP_MOVE_ROLE_RESPONDER: + l2cap_move_done(chan); + l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_SUPP); + break; + case L2CAP_MOVE_ROLE_INITIATOR: + if (chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_COMP || + chan->move_state == L2CAP_MOVE_WAIT_LOGICAL_CFM) { + /* Remote has only sent pending or + * success responses, clean up + */ + l2cap_move_done(chan); + } + + /* Other amp move states imply that the move + * has already aborted + */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + break; + } +} + +static void l2cap_logical_finish_create(struct l2cap_chan *chan, + struct hci_chan *hchan) +{ + struct l2cap_conf_rsp rsp; + + chan->hs_hchan = hchan; + chan->hs_hcon->l2cap_data = chan->conn; + + l2cap_send_efs_conf_rsp(chan, &rsp, chan->ident, 0); + + if (test_bit(CONF_INPUT_DONE, &chan->conf_state)) { + int err; + + set_default_fcs(chan); + + err = l2cap_ertm_init(chan); + if (err < 0) + l2cap_send_disconn_req(chan->conn, chan, -err); + else + l2cap_chan_ready(chan); + } +} + +static void l2cap_logical_finish_move(struct l2cap_chan *chan, + struct hci_chan *hchan) +{ + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + + BT_DBG("move_state %d", chan->move_state); + + switch (chan->move_state) { + case L2CAP_MOVE_WAIT_LOGICAL_COMP: + /* Move confirm will be sent after a success + * response is received + */ + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + break; + case L2CAP_MOVE_WAIT_LOGICAL_CFM: + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + } else if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP; + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } else if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); + } + break; + default: + /* Move was not in expected state, free the channel */ + __release_logical_link(chan); + + chan->move_state = L2CAP_MOVE_STABLE; + } +} + +/* Call with chan locked */ +void l2cap_logical_cfm(struct l2cap_chan *chan, struct hci_chan *hchan, + u8 status) +{ + BT_DBG("chan %p, hchan %p, status %d", chan, hchan, status); + + if (status) { + l2cap_logical_fail(chan); + __release_logical_link(chan); + return; + } + + if (chan->state != BT_CONNECTED) { + /* Ignore logical link if channel is on BR/EDR */ + if (chan->local_amp_id) + l2cap_logical_finish_create(chan, hchan); + } else { + l2cap_logical_finish_move(chan, hchan); + } +} + +void l2cap_move_start(struct l2cap_chan *chan) +{ + BT_DBG("chan %p", chan); + + if (chan->local_amp_id == HCI_BREDR_ID) { + if (chan->chan_policy != BT_CHANNEL_POLICY_AMP_PREFERRED) + return; + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR; + chan->move_state = L2CAP_MOVE_WAIT_PREPARE; + /* Placeholder - start physical link setup */ + } else { + chan->move_role = L2CAP_MOVE_ROLE_INITIATOR; + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + chan->move_id = 0; + l2cap_move_setup(chan); + l2cap_send_move_chan_req(chan, 0); + } +} + +static void l2cap_do_create(struct l2cap_chan *chan, int result, + u8 local_amp_id, u8 remote_amp_id) +{ + BT_DBG("chan %p state %s %u -> %u", chan, state_to_string(chan->state), + local_amp_id, remote_amp_id); + + chan->fcs = L2CAP_FCS_NONE; + + /* Outgoing channel on AMP */ + if (chan->state == BT_CONNECT) { + if (result == L2CAP_CR_SUCCESS) { + chan->local_amp_id = local_amp_id; + l2cap_send_create_chan_req(chan, remote_amp_id); + } else { + /* Revert to BR/EDR connect */ + l2cap_send_conn_req(chan); + } + + return; + } + + /* Incoming channel on AMP */ + if (__l2cap_no_conn_pending(chan)) { + struct l2cap_conn_rsp rsp; + char buf[128]; + rsp.scid = cpu_to_le16(chan->dcid); + rsp.dcid = cpu_to_le16(chan->scid); + + if (result == L2CAP_CR_SUCCESS) { + /* Send successful response */ + rsp.result = __constant_cpu_to_le16(L2CAP_CR_SUCCESS); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + } else { + /* Send negative response */ + rsp.result = __constant_cpu_to_le16(L2CAP_CR_NO_MEM); + rsp.status = __constant_cpu_to_le16(L2CAP_CS_NO_INFO); + } + + l2cap_send_cmd(chan->conn, chan->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + if (result == L2CAP_CR_SUCCESS) { + __l2cap_state_change(chan, BT_CONFIG); + set_bit(CONF_REQ_SENT, &chan->conf_state); + l2cap_send_cmd(chan->conn, l2cap_get_ident(chan->conn), + L2CAP_CONF_REQ, + l2cap_build_conf_req(chan, buf), buf); + chan->num_conf_req++; + } + } +} + +static void l2cap_do_move_initiate(struct l2cap_chan *chan, u8 local_amp_id, + u8 remote_amp_id) +{ + l2cap_move_setup(chan); + chan->move_id = local_amp_id; + chan->move_state = L2CAP_MOVE_WAIT_RSP; + + l2cap_send_move_chan_req(chan, remote_amp_id); +} + +static void l2cap_do_move_respond(struct l2cap_chan *chan, int result) +{ + struct hci_chan *hchan = NULL; + + /* Placeholder - get hci_chan for logical link */ + + if (hchan) { + if (hchan->state == BT_CONNECTED) { + /* Logical link is ready to go */ + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + l2cap_send_move_chan_rsp(chan, L2CAP_MR_SUCCESS); + + l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS); + } else { + /* Wait for logical link to be ready */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + } + } else { + /* Logical link not available */ + l2cap_send_move_chan_rsp(chan, L2CAP_MR_NOT_ALLOWED); + } +} + +static void l2cap_do_move_cancel(struct l2cap_chan *chan, int result) +{ + if (chan->move_role == L2CAP_MOVE_ROLE_RESPONDER) { + u8 rsp_result; + if (result == -EINVAL) + rsp_result = L2CAP_MR_BAD_ID; + else + rsp_result = L2CAP_MR_NOT_ALLOWED; + + l2cap_send_move_chan_rsp(chan, rsp_result); + } + + chan->move_role = L2CAP_MOVE_ROLE_NONE; + chan->move_state = L2CAP_MOVE_STABLE; + + /* Restart data transmission */ + l2cap_ertm_send(chan); +} + +/* Invoke with locked chan */ +void __l2cap_physical_cfm(struct l2cap_chan *chan, int result) +{ + u8 local_amp_id = chan->local_amp_id; + u8 remote_amp_id = chan->remote_amp_id; + + BT_DBG("chan %p, result %d, local_amp_id %d, remote_amp_id %d", + chan, result, local_amp_id, remote_amp_id); + + if (chan->state == BT_DISCONN || chan->state == BT_CLOSED) { + l2cap_chan_unlock(chan); + return; + } + + if (chan->state != BT_CONNECTED) { + l2cap_do_create(chan, result, local_amp_id, remote_amp_id); + } else if (result != L2CAP_MR_SUCCESS) { + l2cap_do_move_cancel(chan, result); + } else { + switch (chan->move_role) { + case L2CAP_MOVE_ROLE_INITIATOR: + l2cap_do_move_initiate(chan, local_amp_id, + remote_amp_id); + break; + case L2CAP_MOVE_ROLE_RESPONDER: + l2cap_do_move_respond(chan, result); + break; + default: + l2cap_do_move_cancel(chan, result); + break; + } + } +} + static inline int l2cap_move_channel_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) { struct l2cap_move_chan_req *req = data; + struct l2cap_move_chan_rsp rsp; + struct l2cap_chan *chan; u16 icid = 0; u16 result = L2CAP_MR_NOT_ALLOWED; @@ -4124,15 +4675,206 @@ static inline int l2cap_move_channel_req(struct l2cap_conn *conn, if (!enable_hs) return -EINVAL; - /* Placeholder: Always refuse */ - l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); + chan = l2cap_get_chan_by_dcid(conn, icid); + if (!chan) { + rsp.icid = cpu_to_le16(icid); + rsp.result = __constant_cpu_to_le16(L2CAP_MR_NOT_ALLOWED); + l2cap_send_cmd(conn, cmd->ident, L2CAP_MOVE_CHAN_RSP, + sizeof(rsp), &rsp); + return 0; + } + + chan->ident = cmd->ident; + + if (chan->scid < L2CAP_CID_DYN_START || + chan->chan_policy == BT_CHANNEL_POLICY_BREDR_ONLY || + (chan->mode != L2CAP_MODE_ERTM && + chan->mode != L2CAP_MODE_STREAMING)) { + result = L2CAP_MR_NOT_ALLOWED; + goto send_move_response; + } + + if (chan->local_amp_id == req->dest_amp_id) { + result = L2CAP_MR_SAME_ID; + goto send_move_response; + } + + if (req->dest_amp_id) { + struct hci_dev *hdev; + hdev = hci_dev_get(req->dest_amp_id); + if (!hdev || hdev->dev_type != HCI_AMP || + !test_bit(HCI_UP, &hdev->flags)) { + if (hdev) + hci_dev_put(hdev); + + result = L2CAP_MR_BAD_ID; + goto send_move_response; + } + hci_dev_put(hdev); + } + + /* Detect a move collision. Only send a collision response + * if this side has "lost", otherwise proceed with the move. + * The winner has the larger bd_addr. + */ + if ((__chan_is_moving(chan) || + chan->move_role != L2CAP_MOVE_ROLE_NONE) && + bacmp(conn->src, conn->dst) > 0) { + result = L2CAP_MR_COLLISION; + goto send_move_response; + } + + chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; + l2cap_move_setup(chan); + chan->move_id = req->dest_amp_id; + icid = chan->dcid; + + if (!req->dest_amp_id) { + /* Moving to BR/EDR */ + if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + result = L2CAP_MR_PEND; + } else { + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM; + result = L2CAP_MR_SUCCESS; + } + } else { + chan->move_state = L2CAP_MOVE_WAIT_PREPARE; + /* Placeholder - uncomment when amp functions are available */ + /*amp_accept_physical(chan, req->dest_amp_id);*/ + result = L2CAP_MR_PEND; + } + +send_move_response: + l2cap_send_move_chan_rsp(chan, result); + + l2cap_chan_unlock(chan); return 0; } -static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static void l2cap_move_continue(struct l2cap_conn *conn, u16 icid, u16 result) +{ + struct l2cap_chan *chan; + struct hci_chan *hchan = NULL; + + chan = l2cap_get_chan_by_scid(conn, icid); + if (!chan) { + l2cap_send_move_chan_cfm_icid(conn, icid); + return; + } + + __clear_chan_timer(chan); + if (result == L2CAP_MR_PEND) + __set_chan_timer(chan, L2CAP_MOVE_ERTX_TIMEOUT); + + switch (chan->move_state) { + case L2CAP_MOVE_WAIT_LOGICAL_COMP: + /* Move confirm will be sent when logical link + * is complete. + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + break; + case L2CAP_MOVE_WAIT_RSP_SUCCESS: + if (result == L2CAP_MR_PEND) { + break; + } else if (test_bit(CONN_LOCAL_BUSY, + &chan->conn_state)) { + chan->move_state = L2CAP_MOVE_WAIT_LOCAL_BUSY; + } else { + /* Logical link is up or moving to BR/EDR, + * proceed with move + */ + chan->move_state = L2CAP_MOVE_WAIT_CONFIRM_RSP; + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } + break; + case L2CAP_MOVE_WAIT_RSP: + /* Moving to AMP */ + if (result == L2CAP_MR_SUCCESS) { + /* Remote is ready, send confirm immediately + * after logical link is ready + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_CFM; + } else { + /* Both logical link and move success + * are required to confirm + */ + chan->move_state = L2CAP_MOVE_WAIT_LOGICAL_COMP; + } + + /* Placeholder - get hci_chan for logical link */ + if (!hchan) { + /* Logical link not available */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + break; + } + + /* If the logical link is not yet connected, do not + * send confirmation. + */ + if (hchan->state != BT_CONNECTED) + break; + + /* Logical link is already ready to go */ + + chan->hs_hcon = hchan->conn; + chan->hs_hcon->l2cap_data = chan->conn; + + if (result == L2CAP_MR_SUCCESS) { + /* Can confirm now */ + l2cap_send_move_chan_cfm(chan, L2CAP_MC_CONFIRMED); + } else { + /* Now only need move success + * to confirm + */ + chan->move_state = L2CAP_MOVE_WAIT_RSP_SUCCESS; + } + + l2cap_logical_cfm(chan, hchan, L2CAP_MR_SUCCESS); + break; + default: + /* Any other amp move state means the move failed. */ + chan->move_id = chan->local_amp_id; + l2cap_move_done(chan); + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + } + + l2cap_chan_unlock(chan); +} + +static void l2cap_move_fail(struct l2cap_conn *conn, u8 ident, u16 icid, + u16 result) +{ + struct l2cap_chan *chan; + + chan = l2cap_get_chan_by_ident(conn, ident); + if (!chan) { + /* Could not locate channel, icid is best guess */ + l2cap_send_move_chan_cfm_icid(conn, icid); + return; + } + + __clear_chan_timer(chan); + + if (chan->move_role == L2CAP_MOVE_ROLE_INITIATOR) { + if (result == L2CAP_MR_COLLISION) { + chan->move_role = L2CAP_MOVE_ROLE_RESPONDER; + } else { + /* Cleanup - cancel move */ + chan->move_id = chan->local_amp_id; + l2cap_move_done(chan); + } + } + + l2cap_send_move_chan_cfm(chan, L2CAP_MC_UNCONFIRMED); + + l2cap_chan_unlock(chan); +} + +static int l2cap_move_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_move_chan_rsp *rsp = data; u16 icid, result; @@ -4145,17 +4887,20 @@ static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); - /* Placeholder: Always unconfirmed */ - l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); + if (result == L2CAP_MR_SUCCESS || result == L2CAP_MR_PEND) + l2cap_move_continue(conn, icid, result); + else + l2cap_move_fail(conn, cmd->ident, icid, result); return 0; } -static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, - struct l2cap_cmd_hdr *cmd, - u16 cmd_len, void *data) +static int l2cap_move_channel_confirm(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, + u16 cmd_len, void *data) { struct l2cap_move_chan_cfm *cfm = data; + struct l2cap_chan *chan; u16 icid, result; if (cmd_len != sizeof(*cfm)) @@ -4166,8 +4911,29 @@ static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x, result 0x%4.4x", icid, result); + chan = l2cap_get_chan_by_dcid(conn, icid); + if (!chan) { + /* Spec requires a response even if the icid was not found */ + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + return 0; + } + + if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM) { + if (result == L2CAP_MC_CONFIRMED) { + chan->local_amp_id = chan->move_id; + if (!chan->local_amp_id) + __release_logical_link(chan); + } else { + chan->move_id = chan->local_amp_id; + } + + l2cap_move_done(chan); + } + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + l2cap_chan_unlock(chan); + return 0; } @@ -4176,6 +4942,7 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, u16 cmd_len, void *data) { struct l2cap_move_chan_cfm_rsp *rsp = data; + struct l2cap_chan *chan; u16 icid; if (cmd_len != sizeof(*rsp)) @@ -4185,6 +4952,23 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, BT_DBG("icid 0x%4.4x", icid); + chan = l2cap_get_chan_by_scid(conn, icid); + if (!chan) + return 0; + + __clear_chan_timer(chan); + + if (chan->move_state == L2CAP_MOVE_WAIT_CONFIRM_RSP) { + chan->local_amp_id = chan->move_id; + + if (!chan->local_amp_id && chan->hs_hchan) + __release_logical_link(chan); + + l2cap_move_done(chan); + } + + l2cap_chan_unlock(chan); + return 0; } @@ -4269,7 +5053,7 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, case L2CAP_CONN_RSP: case L2CAP_CREATE_CHAN_RSP: - err = l2cap_connect_rsp(conn, cmd, data); + err = l2cap_connect_create_rsp(conn, cmd, data); break; case L2CAP_CONF_REQ: @@ -4556,6 +5340,12 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, return err; } +static int l2cap_resegment(struct l2cap_chan *chan) +{ + /* Placeholder */ + return 0; +} + void l2cap_chan_busy(struct l2cap_chan *chan, int busy) { u8 event; @@ -4871,8 +5661,8 @@ static int l2cap_rx_state_recv(struct l2cap_chan *chan, if (control->final) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (!test_and_clear_bit(CONN_REJ_ACT, - &chan->conn_state)) { + if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state) && + !__chan_is_moving(chan)) { control->final = 0; l2cap_retransmit_all(chan, control); } @@ -5061,6 +5851,96 @@ static int l2cap_rx_state_srej_sent(struct l2cap_chan *chan, return err; } +static int l2cap_finish_move(struct l2cap_chan *chan) +{ + BT_DBG("chan %p", chan); + + chan->rx_state = L2CAP_RX_STATE_RECV; + + if (chan->hs_hcon) + chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; + else + chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + + return l2cap_resegment(chan); +} + +static int l2cap_rx_state_wait_p(struct l2cap_chan *chan, + struct l2cap_ctrl *control, + struct sk_buff *skb, u8 event) +{ + int err; + + BT_DBG("chan %p, control %p, skb %p, event %d", chan, control, skb, + event); + + if (!control->poll) + return -EPROTO; + + l2cap_process_reqseq(chan, control->reqseq); + + if (!skb_queue_empty(&chan->tx_q)) + chan->tx_send_head = skb_peek(&chan->tx_q); + else + chan->tx_send_head = NULL; + + /* Rewind next_tx_seq to the point expected + * by the receiver. + */ + chan->next_tx_seq = control->reqseq; + chan->unacked_frames = 0; + + err = l2cap_finish_move(chan); + if (err) + return err; + + set_bit(CONN_SEND_FBIT, &chan->conn_state); + l2cap_send_i_or_rr_or_rnr(chan); + + if (event == L2CAP_EV_RECV_IFRAME) + return -EPROTO; + + return l2cap_rx_state_recv(chan, control, NULL, event); +} + +static int l2cap_rx_state_wait_f(struct l2cap_chan *chan, + struct l2cap_ctrl *control, + struct sk_buff *skb, u8 event) +{ + int err; + + if (!control->final) + return -EPROTO; + + clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); + + chan->rx_state = L2CAP_RX_STATE_RECV; + l2cap_process_reqseq(chan, control->reqseq); + + if (!skb_queue_empty(&chan->tx_q)) + chan->tx_send_head = skb_peek(&chan->tx_q); + else + chan->tx_send_head = NULL; + + /* Rewind next_tx_seq to the point expected + * by the receiver. + */ + chan->next_tx_seq = control->reqseq; + chan->unacked_frames = 0; + + if (chan->hs_hcon) + chan->conn->mtu = chan->hs_hcon->hdev->block_mtu; + else + chan->conn->mtu = chan->conn->hcon->hdev->acl_mtu; + + err = l2cap_resegment(chan); + + if (!err) + err = l2cap_rx_state_recv(chan, control, skb, event); + + return err; +} + static bool __valid_reqseq(struct l2cap_chan *chan, u16 reqseq) { /* Make sure reqseq is for a packet that has been sent but not acked */ @@ -5087,6 +5967,12 @@ static int l2cap_rx(struct l2cap_chan *chan, struct l2cap_ctrl *control, err = l2cap_rx_state_srej_sent(chan, control, skb, event); break; + case L2CAP_RX_STATE_WAIT_P: + err = l2cap_rx_state_wait_p(chan, control, skb, event); + break; + case L2CAP_RX_STATE_WAIT_F: + err = l2cap_rx_state_wait_f(chan, control, skb, event); + break; default: /* shut it down */ break; @@ -5206,7 +6092,7 @@ static int l2cap_data_rcv(struct l2cap_chan *chan, struct sk_buff *skb) control->super); if (len != 0) { - BT_ERR("%d", len); + BT_ERR("Trailing bytes: %d in sframe", len); l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; } @@ -5422,9 +6308,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status) conn = l2cap_conn_add(hcon, status); if (conn) l2cap_conn_ready(conn); - } else + } else { l2cap_conn_del(hcon, bt_to_errno(status)); - + } } int l2cap_disconn_ind(struct hci_conn *hcon) @@ -5500,7 +6386,7 @@ int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt) continue; } - if (test_bit(CONF_CONNECT_PEND, &chan->conf_state)) { + if (!__l2cap_no_conn_pending(chan)) { l2cap_chan_unlock(chan); continue; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 89f1472939e..1bcfb8422fd 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -736,6 +736,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, } chan->chan_policy = (u8) opt; + + if (sk->sk_state == BT_CONNECTED && + chan->move_role == L2CAP_MOVE_ROLE_NONE) + l2cap_move_start(chan); + break; default: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 158a87bb0c0..142764aec2a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -222,7 +222,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_STATUS); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev)); @@ -253,7 +253,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, u8 status, hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); + hdr->opcode = __constant_cpu_to_le16(MGMT_EV_CMD_COMPLETE); hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); @@ -377,15 +377,15 @@ static u32 get_supported_settings(struct hci_dev *hdev) u32 settings = 0; settings |= MGMT_SETTING_POWERED; - settings |= MGMT_SETTING_CONNECTABLE; - settings |= MGMT_SETTING_FAST_CONNECTABLE; - settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_PAIRABLE; if (lmp_ssp_capable(hdev)) settings |= MGMT_SETTING_SSP; if (lmp_bredr_capable(hdev)) { + settings |= MGMT_SETTING_CONNECTABLE; + settings |= MGMT_SETTING_FAST_CONNECTABLE; + settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; } @@ -485,7 +485,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr += (name_len + 2); } - if (hdev->inq_tx_power) { + if (hdev->inq_tx_power != HCI_TX_POWER_INVALID) { ptr[0] = 2; ptr[1] = EIR_TX_POWER; ptr[2] = (u8) hdev->inq_tx_power; @@ -566,7 +566,7 @@ static int update_eir(struct hci_dev *hdev) if (!hdev_is_powered(hdev)) return 0; - if (!(hdev->features[6] & LMP_EXT_INQ)) + if (!lmp_ext_inq_capable(hdev)) return 0; if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) @@ -833,7 +833,7 @@ static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, if (hdev) hdr->index = cpu_to_le16(hdev->id); else - hdr->index = cpu_to_le16(MGMT_INDEX_NONE); + hdr->index = __constant_cpu_to_le16(MGMT_INDEX_NONE); hdr->len = cpu_to_le16(data_len); if (data) @@ -868,6 +868,10 @@ static int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, + MGMT_STATUS_NOT_SUPPORTED); + timeout = __le16_to_cpu(cp->timeout); if (!cp->val && timeout > 0) return cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, @@ -963,6 +967,10 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, + MGMT_STATUS_NOT_SUPPORTED); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1061,6 +1069,10 @@ static int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG("request for %s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, + MGMT_STATUS_NOT_SUPPORTED); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { @@ -1214,7 +1226,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } val = !!cp->val; - enabled = !!(hdev->host_features[0] & LMP_HOST_LE); + enabled = !!lmp_host_le_capable(hdev); if (!hdev_is_powered(hdev) || val == enabled) { bool changed = false; @@ -1250,7 +1262,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) if (val) { hci_cp.le = val; - hci_cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); + hci_cp.simul = !!lmp_le_br_capable(hdev); } err = hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), @@ -2596,6 +2608,10 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); + if (!lmp_bredr_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_NOT_SUPPORTED); + if (!hdev_is_powered(hdev)) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_POWERED); @@ -2873,6 +2889,21 @@ static void settings_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_free(cmd); } +static int set_bredr_scan(struct hci_dev *hdev) +{ + u8 scan = 0; + + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + scan |= SCAN_PAGE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + if (!scan) + return 0; + + return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct cmd_lookup match = { NULL, hdev }; @@ -2884,17 +2915,8 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); if (powered) { - u8 scan = 0; - - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) - scan |= SCAN_PAGE; - if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) - scan |= SCAN_INQUIRY; - - if (scan) - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + !lmp_host_ssp_capable(hdev)) { u8 ssp = 1; hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); @@ -2904,15 +2926,24 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered) struct hci_cp_write_le_host_supported cp; cp.le = 1; - cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR); - - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(cp), &cp); + cp.simul = !!lmp_le_br_capable(hdev); + + /* Check first if we already have the right + * host state (host features set) + */ + if (cp.le != !!lmp_host_le_capable(hdev) || + cp.simul != !!lmp_host_le_br_capable(hdev)) + hci_send_cmd(hdev, + HCI_OP_WRITE_LE_HOST_SUPPORTED, + sizeof(cp), &cp); } - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); + if (lmp_bredr_capable(hdev)) { + set_bredr_scan(hdev); + update_class(hdev); + update_name(hdev, hdev->dev_name); + update_eir(hdev); + } } else { u8 status = MGMT_STATUS_NOT_POWERED; mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); @@ -3361,7 +3392,7 @@ static int clear_eir(struct hci_dev *hdev) { struct hci_cp_write_eir cp; - if (!(hdev->features[6] & LMP_EXT_INQ)) + if (!lmp_ext_inq_capable(hdev)) return 0; memset(hdev->eir, 0, sizeof(hdev->eir)); @@ -3493,7 +3524,12 @@ send_event: err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); - update_eir(hdev); + /* EIR is taken care of separately when powering on the + * adapter so only update them here if this is a name change + * unrelated to power on. + */ + if (!test_bit(HCI_INIT, &hdev->flags)) + update_eir(hdev); failed: if (cmd) @@ -3588,9 +3624,9 @@ int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; if (cfm_name) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); + ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); if (!ssp) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); + ev->flags |= __constant_cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); if (eir_len > 0) memcpy(ev->eir, eir, eir_len); diff --git a/net/can/bcm.c b/net/can/bcm.c index 6f747582718..969b7cdff59 100644 --- a/net/can/bcm.c +++ b/net/can/bcm.c @@ -1084,6 +1084,9 @@ static int bcm_rx_setup(struct bcm_msg_head *msg_head, struct msghdr *msg, op->sk = sk; op->ifindex = ifindex; + /* ifindex for timeout events w/o previous frame reception */ + op->rx_ifindex = ifindex; + /* initialize uninitialized (kzalloc) structure */ hrtimer_init(&op->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); op->timer.function = bcm_rx_timeout_handler; diff --git a/net/core/dev.c b/net/core/dev.c index 7304ea8a1f1..2f94df257e5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -203,6 +203,8 @@ static struct list_head offload_base __read_mostly; DEFINE_RWLOCK(dev_base_lock); EXPORT_SYMBOL(dev_base_lock); +DEFINE_SEQLOCK(devnet_rename_seq); + static inline void dev_base_seq_inc(struct net *net) { while (++net->dev_base_seq == 0); @@ -1091,22 +1093,31 @@ int dev_change_name(struct net_device *dev, const char *newname) if (dev->flags & IFF_UP) return -EBUSY; - if (strncmp(newname, dev->name, IFNAMSIZ) == 0) + write_seqlock(&devnet_rename_seq); + + if (strncmp(newname, dev->name, IFNAMSIZ) == 0) { + write_sequnlock(&devnet_rename_seq); return 0; + } memcpy(oldname, dev->name, IFNAMSIZ); err = dev_get_valid_name(net, dev, newname); - if (err < 0) + if (err < 0) { + write_sequnlock(&devnet_rename_seq); return err; + } rollback: ret = device_rename(&dev->dev, dev->name); if (ret) { memcpy(dev->name, oldname, IFNAMSIZ); + write_sequnlock(&devnet_rename_seq); return ret; } + write_sequnlock(&devnet_rename_seq); + write_lock_bh(&dev_base_lock); hlist_del_rcu(&dev->name_hlist); write_unlock_bh(&dev_base_lock); @@ -1124,6 +1135,7 @@ rollback: /* err >= 0 after dev_alloc_name() or stores the first errno */ if (err >= 0) { err = ret; + write_seqlock(&devnet_rename_seq); memcpy(dev->name, oldname, IFNAMSIZ); goto rollback; } else { @@ -3580,7 +3592,7 @@ void napi_gro_flush(struct napi_struct *napi, bool flush_old) } EXPORT_SYMBOL(napi_gro_flush); -enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) +static enum gro_result dev_gro_receive(struct napi_struct *napi, struct sk_buff *skb) { struct sk_buff **pp = NULL; struct packet_offload *ptype; @@ -3671,7 +3683,6 @@ normal: ret = GRO_NORMAL; goto pull; } -EXPORT_SYMBOL(dev_gro_receive); static inline gro_result_t __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) @@ -3698,7 +3709,7 @@ __napi_gro_receive(struct napi_struct *napi, struct sk_buff *skb) return dev_gro_receive(napi, skb); } -gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) +static gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) { switch (ret) { case GRO_NORMAL: @@ -3724,7 +3735,6 @@ gro_result_t napi_skb_finish(gro_result_t ret, struct sk_buff *skb) return ret; } -EXPORT_SYMBOL(napi_skb_finish); static void skb_gro_reset_offset(struct sk_buff *skb) { @@ -3776,7 +3786,7 @@ struct sk_buff *napi_get_frags(struct napi_struct *napi) } EXPORT_SYMBOL(napi_get_frags); -gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, +static gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, gro_result_t ret) { switch (ret) { @@ -3801,7 +3811,6 @@ gro_result_t napi_frags_finish(struct napi_struct *napi, struct sk_buff *skb, return ret; } -EXPORT_SYMBOL(napi_frags_finish); static struct sk_buff *napi_frags_skb(struct napi_struct *napi) { @@ -4148,6 +4157,7 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg) { struct net_device *dev; struct ifreq ifr; + unsigned seq; /* * Fetch the caller's info block. @@ -4156,6 +4166,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg) if (copy_from_user(&ifr, arg, sizeof(struct ifreq))) return -EFAULT; +retry: + seq = read_seqbegin(&devnet_rename_seq); rcu_read_lock(); dev = dev_get_by_index_rcu(net, ifr.ifr_ifindex); if (!dev) { @@ -4165,6 +4177,8 @@ static int dev_ifname(struct net *net, struct ifreq __user *arg) strcpy(ifr.ifr_name, dev->name); rcu_read_unlock(); + if (read_seqretry(&devnet_rename_seq, seq)) + goto retry; if (copy_to_user(arg, &ifr, sizeof(struct ifreq))) return -EFAULT; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 575a6ee8994..1868625af25 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1642,7 +1642,7 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) } EXPORT_SYMBOL(rtnl_configure_link); -struct net_device *rtnl_create_link(struct net *src_net, struct net *net, +struct net_device *rtnl_create_link(struct net *net, char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) { int err; @@ -1840,7 +1840,7 @@ replay: if (IS_ERR(dest_net)) return PTR_ERR(dest_net); - dev = rtnl_create_link(net, dest_net, ifname, ops, tb); + dev = rtnl_create_link(dest_net, ifname, ops, tb); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out; diff --git a/net/core/sock.c b/net/core/sock.c index d4f7b58b386..a692ef49c9b 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -505,7 +505,8 @@ struct dst_entry *sk_dst_check(struct sock *sk, u32 cookie) } EXPORT_SYMBOL(sk_dst_check); -static int sock_bindtodevice(struct sock *sk, char __user *optval, int optlen) +static int sock_setbindtodevice(struct sock *sk, char __user *optval, + int optlen) { int ret = -ENOPROTOOPT; #ifdef CONFIG_NETDEVICES @@ -562,6 +563,59 @@ out: return ret; } +static int sock_getbindtodevice(struct sock *sk, char __user *optval, + int __user *optlen, int len) +{ + int ret = -ENOPROTOOPT; +#ifdef CONFIG_NETDEVICES + struct net *net = sock_net(sk); + struct net_device *dev; + char devname[IFNAMSIZ]; + unsigned seq; + + if (sk->sk_bound_dev_if == 0) { + len = 0; + goto zero; + } + + ret = -EINVAL; + if (len < IFNAMSIZ) + goto out; + +retry: + seq = read_seqbegin(&devnet_rename_seq); + rcu_read_lock(); + dev = dev_get_by_index_rcu(net, sk->sk_bound_dev_if); + ret = -ENODEV; + if (!dev) { + rcu_read_unlock(); + goto out; + } + + strcpy(devname, dev->name); + rcu_read_unlock(); + if (read_seqretry(&devnet_rename_seq, seq)) + goto retry; + + len = strlen(devname) + 1; + + ret = -EFAULT; + if (copy_to_user(optval, devname, len)) + goto out; + +zero: + ret = -EFAULT; + if (put_user(len, optlen)) + goto out; + + ret = 0; + +out: +#endif + + return ret; +} + static inline void sock_valbool_flag(struct sock *sk, int bit, int valbool) { if (valbool) @@ -589,7 +643,7 @@ int sock_setsockopt(struct socket *sock, int level, int optname, */ if (optname == SO_BINDTODEVICE) - return sock_bindtodevice(sk, optval, optlen); + return sock_setbindtodevice(sk, optval, optlen); if (optlen < sizeof(int)) return -EINVAL; @@ -1075,15 +1129,17 @@ int sock_getsockopt(struct socket *sock, int level, int optname, case SO_NOFCS: v.val = sock_flag(sk, SOCK_NOFCS); break; + case SO_BINDTODEVICE: - v.val = sk->sk_bound_dev_if; - break; + return sock_getbindtodevice(sk, optval, optlen, len); + case SO_GET_FILTER: len = sk_get_filter(sk, (struct sock_filter __user *)optval, len); if (len < 0) return len; goto lenout; + default: return -ENOPROTOOPT; } diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c index 6d42c17af96..f651da60f16 100644 --- a/net/ieee802154/6lowpan.c +++ b/net/ieee802154/6lowpan.c @@ -1047,7 +1047,8 @@ static netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *dev) goto error; } - if (skb->len <= IEEE802154_MTU) { + /* Send directly if less than the MTU minus the 2 checksum bytes. */ + if (skb->len <= IEEE802154_MTU - IEEE802154_MFR_SIZE) { err = dev_queue_xmit(skb); goto out; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index f2eccd53174..17ff9fd7cdd 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -257,7 +257,8 @@ static inline bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt, struct inet_peer *peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, 1); rc = inet_peer_xrlim_allow(peer, net->ipv4.sysctl_icmp_ratelimit); - inet_putpeer(peer); + if (peer) + inet_putpeer(peer); } out: return rc; diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 7880af97020..fa3ae814871 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -237,12 +237,14 @@ struct sock *__inet_lookup_established(struct net *net, rcu_read_lock(); begin: sk_nulls_for_each_rcu(sk, node, &head->chain) { - if (INET_MATCH(sk, net, hash, acookie, - saddr, daddr, ports, dif)) { + if (sk->sk_hash != hash) + continue; + if (likely(INET_MATCH(sk, net, acookie, + saddr, daddr, ports, dif))) { if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) goto begintw; - if (unlikely(!INET_MATCH(sk, net, hash, acookie, - saddr, daddr, ports, dif))) { + if (unlikely(!INET_MATCH(sk, net, acookie, + saddr, daddr, ports, dif))) { sock_put(sk); goto begin; } @@ -260,14 +262,18 @@ begin: begintw: /* Must check for a TIME_WAIT'er before going to listener hash. */ sk_nulls_for_each_rcu(sk, node, &head->twchain) { - if (INET_TW_MATCH(sk, net, hash, acookie, - saddr, daddr, ports, dif)) { + if (sk->sk_hash != hash) + continue; + if (likely(INET_TW_MATCH(sk, net, acookie, + saddr, daddr, ports, + dif))) { if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { sk = NULL; goto out; } - if (unlikely(!INET_TW_MATCH(sk, net, hash, acookie, - saddr, daddr, ports, dif))) { + if (unlikely(!INET_TW_MATCH(sk, net, acookie, + saddr, daddr, ports, + dif))) { sock_put(sk); goto begintw; } @@ -314,10 +320,12 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, /* Check TIME-WAIT sockets first. */ sk_nulls_for_each(sk2, node, &head->twchain) { - tw = inet_twsk(sk2); + if (sk2->sk_hash != hash) + continue; - if (INET_TW_MATCH(sk2, net, hash, acookie, - saddr, daddr, ports, dif)) { + if (likely(INET_TW_MATCH(sk2, net, acookie, + saddr, daddr, ports, dif))) { + tw = inet_twsk(sk2); if (twsk_unique(sk, sk2, twp)) goto unique; else @@ -328,8 +336,10 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, /* And established part... */ sk_nulls_for_each(sk2, node, &head->chain) { - if (INET_MATCH(sk2, net, hash, acookie, - saddr, daddr, ports, dif)) + if (sk2->sk_hash != hash) + continue; + if (likely(INET_MATCH(sk2, net, acookie, + saddr, daddr, ports, dif))) goto not_unique; } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index fc09ef93663..58e4160fdce 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1323,6 +1323,10 @@ int ip_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, unsi if (get_user(v, (u32 __user *)optval)) return -EFAULT; + /* "pimreg%u" should not exceed 16 bytes (IFNAMSIZ) */ + if (v != RT_TABLE_DEFAULT && v >= 1000000000) + return -EINVAL; + rtnl_lock(); ret = 0; if (sk == rtnl_dereference(mrt->mroute_sk)) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e6eace1c2bd..1aca02c9911 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -3590,8 +3590,7 @@ void __init tcp_init(void) alloc_large_system_hash("TCP established", sizeof(struct inet_ehash_bucket), thash_entries, - (totalram_pages >= 128 * 1024) ? - 13 : 15, + 17, /* one slot per 128 KB of memory */ 0, NULL, &tcp_hashinfo.ehash_mask, @@ -3607,8 +3606,7 @@ void __init tcp_init(void) alloc_large_system_hash("TCP bind", sizeof(struct inet_bind_hashbucket), tcp_hashinfo.ehash_mask + 1, - (totalram_pages >= 128 * 1024) ? - 13 : 15, + 17, /* one slot per 128 KB of memory */ 0, &tcp_hashinfo.bhash_size, NULL, diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index fc0e13ad633..4b644f656c4 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3150,8 +3150,7 @@ static void addrconf_dad_completed(struct inet6_ifaddr *ifp) router advertisements, start sending router solicitations. */ - if (((ifp->idev->cnf.accept_ra == 1 && !ifp->idev->cnf.forwarding) || - ifp->idev->cnf.accept_ra == 2) && + if (ipv6_accept_ra(ifp->idev) && ifp->idev->cnf.rtr_solicits > 0 && (dev->flags&IFF_LOOPBACK) == 0 && (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { diff --git a/net/ipv6/exthdrs_core.c b/net/ipv6/exthdrs_core.c index e7d756e19d1..c5e83fae4df 100644 --- a/net/ipv6/exthdrs_core.c +++ b/net/ipv6/exthdrs_core.c @@ -155,3 +155,127 @@ int ipv6_find_tlv(struct sk_buff *skb, int offset, int type) return -1; } EXPORT_SYMBOL_GPL(ipv6_find_tlv); + +/* + * find the offset to specified header or the protocol number of last header + * if target < 0. "last header" is transport protocol header, ESP, or + * "No next header". + * + * Note that *offset is used as input/output parameter. an if it is not zero, + * then it must be a valid offset to an inner IPv6 header. This can be used + * to explore inner IPv6 header, eg. ICMPv6 error messages. + * + * If target header is found, its offset is set in *offset and return protocol + * number. Otherwise, return -1. + * + * If the first fragment doesn't contain the final protocol header or + * NEXTHDR_NONE it is considered invalid. + * + * Note that non-1st fragment is special case that "the protocol number + * of last header" is "next header" field in Fragment header. In this case, + * *offset is meaningless and fragment offset is stored in *fragoff if fragoff + * isn't NULL. + * + * if flags is not NULL and it's a fragment, then the frag flag + * IP6_FH_F_FRAG will be set. If it's an AH header, the + * IP6_FH_F_AUTH flag is set and target < 0, then this function will + * stop at the AH header. If IP6_FH_F_SKIP_RH flag was passed, then this + * function will skip all those routing headers, where segements_left was 0. + */ +int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, + int target, unsigned short *fragoff, int *flags) +{ + unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); + u8 nexthdr = ipv6_hdr(skb)->nexthdr; + unsigned int len; + bool found; + + if (fragoff) + *fragoff = 0; + + if (*offset) { + struct ipv6hdr _ip6, *ip6; + + ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); + if (!ip6 || (ip6->version != 6)) { + printk(KERN_ERR "IPv6 header not found\n"); + return -EBADMSG; + } + start = *offset + sizeof(struct ipv6hdr); + nexthdr = ip6->nexthdr; + } + len = skb->len - start; + + do { + struct ipv6_opt_hdr _hdr, *hp; + unsigned int hdrlen; + found = (nexthdr == target); + + if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { + if (target < 0) + break; + return -ENOENT; + } + + hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); + if (hp == NULL) + return -EBADMSG; + + if (nexthdr == NEXTHDR_ROUTING) { + struct ipv6_rt_hdr _rh, *rh; + + rh = skb_header_pointer(skb, start, sizeof(_rh), + &_rh); + if (rh == NULL) + return -EBADMSG; + + if (flags && (*flags & IP6_FH_F_SKIP_RH) && + rh->segments_left == 0) + found = false; + } + + if (nexthdr == NEXTHDR_FRAGMENT) { + unsigned short _frag_off; + __be16 *fp; + + if (flags) /* Indicate that this is a fragment */ + *flags |= IP6_FH_F_FRAG; + fp = skb_header_pointer(skb, + start+offsetof(struct frag_hdr, + frag_off), + sizeof(_frag_off), + &_frag_off); + if (fp == NULL) + return -EBADMSG; + + _frag_off = ntohs(*fp) & ~0x7; + if (_frag_off) { + if (target < 0 && + ((!ipv6_ext_hdr(hp->nexthdr)) || + hp->nexthdr == NEXTHDR_NONE)) { + if (fragoff) + *fragoff = _frag_off; + return hp->nexthdr; + } + return -ENOENT; + } + hdrlen = 8; + } else if (nexthdr == NEXTHDR_AUTH) { + if (flags && (*flags & IP6_FH_F_AUTH) && (target < 0)) + break; + hdrlen = (hp->hdrlen + 2) << 2; + } else + hdrlen = ipv6_optlen(hp); + + if (!found) { + nexthdr = hp->nexthdr; + len -= hdrlen; + start += hdrlen; + } + } while (!found); + + *offset = start; + return nexthdr; +} +EXPORT_SYMBOL(ipv6_find_hdr); + diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index 73f1a00a96a..dea17fd28e5 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -87,11 +87,13 @@ struct sock *__inet6_lookup_established(struct net *net, rcu_read_lock(); begin: sk_nulls_for_each_rcu(sk, node, &head->chain) { - /* For IPV6 do the cheaper port and family tests first. */ - if (INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) { + if (sk->sk_hash != hash) + continue; + if (likely(INET6_MATCH(sk, net, saddr, daddr, ports, dif))) { if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) goto begintw; - if (!INET6_MATCH(sk, net, hash, saddr, daddr, ports, dif)) { + if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, + ports, dif))) { sock_put(sk); goto begin; } @@ -104,12 +106,16 @@ begin: begintw: /* Must check for a TIME_WAIT'er before going to listener hash. */ sk_nulls_for_each_rcu(sk, node, &head->twchain) { - if (INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) { + if (sk->sk_hash != hash) + continue; + if (likely(INET6_TW_MATCH(sk, net, saddr, daddr, + ports, dif))) { if (unlikely(!atomic_inc_not_zero(&sk->sk_refcnt))) { sk = NULL; goto out; } - if (!INET6_TW_MATCH(sk, net, hash, saddr, daddr, ports, dif)) { + if (unlikely(!INET6_TW_MATCH(sk, net, saddr, daddr, + ports, dif))) { sock_put(sk); goto begintw; } @@ -236,9 +242,12 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, /* Check TIME-WAIT sockets first. */ sk_nulls_for_each(sk2, node, &head->twchain) { - tw = inet_twsk(sk2); + if (sk2->sk_hash != hash) + continue; - if (INET6_TW_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) { + if (likely(INET6_TW_MATCH(sk2, net, saddr, daddr, + ports, dif))) { + tw = inet_twsk(sk2); if (twsk_unique(sk, sk2, twp)) goto unique; else @@ -249,7 +258,9 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, /* And established part... */ sk_nulls_for_each(sk2, node, &head->chain) { - if (INET6_MATCH(sk2, net, hash, saddr, daddr, ports, dif)) + if (sk2->sk_hash != hash) + continue; + if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) goto not_unique; } diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index fb828e9fe8e..a14f28b280f 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c @@ -74,6 +74,10 @@ MODULE_ALIAS_NETDEV("ip6tnl0"); #define HASH_SIZE_SHIFT 5 #define HASH_SIZE (1 << HASH_SIZE_SHIFT) +static bool log_ecn_error = true; +module_param(log_ecn_error, bool, 0644); +MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + static u32 HASH(const struct in6_addr *addr1, const struct in6_addr *addr2) { u32 hash = ipv6_addr_hash(addr1) ^ ipv6_addr_hash(addr2); @@ -683,28 +687,26 @@ ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, return 0; } -static void ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, - const struct ipv6hdr *ipv6h, - struct sk_buff *skb) +static int ip4ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, + struct sk_buff *skb) { __u8 dsfield = ipv6_get_dsfield(ipv6h) & ~INET_ECN_MASK; if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) ipv4_change_dsfield(ip_hdr(skb), INET_ECN_MASK, dsfield); - if (INET_ECN_is_ce(dsfield)) - IP_ECN_set_ce(ip_hdr(skb)); + return IP6_ECN_decapsulate(ipv6h, skb); } -static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, - const struct ipv6hdr *ipv6h, - struct sk_buff *skb) +static int ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, + struct sk_buff *skb) { if (t->parms.flags & IP6_TNL_F_RCV_DSCP_COPY) ipv6_copy_dscp(ipv6_get_dsfield(ipv6h), ipv6_hdr(skb)); - if (INET_ECN_is_ce(ipv6_get_dsfield(ipv6h))) - IP6_ECN_set_ce(ipv6_hdr(skb)); + return IP6_ECN_decapsulate(ipv6h, skb); } __u32 ip6_tnl_get_cap(struct ip6_tnl *t, @@ -768,12 +770,13 @@ EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, __u8 ipproto, - void (*dscp_ecn_decapsulate)(const struct ip6_tnl *t, - const struct ipv6hdr *ipv6h, - struct sk_buff *skb)) + int (*dscp_ecn_decapsulate)(const struct ip6_tnl *t, + const struct ipv6hdr *ipv6h, + struct sk_buff *skb)) { struct ip6_tnl *t; const struct ipv6hdr *ipv6h = ipv6_hdr(skb); + int err; rcu_read_lock(); @@ -803,14 +806,26 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol, skb->pkt_type = PACKET_HOST; memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); + __skb_tunnel_rx(skb, t->dev); + + err = dscp_ecn_decapsulate(t, ipv6h, skb); + if (unlikely(err)) { + if (log_ecn_error) + net_info_ratelimited("non-ECT from %pI6 with dsfield=%#x\n", + &ipv6h->saddr, + ipv6_get_dsfield(ipv6h)); + if (err > 1) { + ++t->dev->stats.rx_frame_errors; + ++t->dev->stats.rx_errors; + rcu_read_unlock(); + goto discard; + } + } + tstats = this_cpu_ptr(t->dev->tstats); tstats->rx_packets++; tstats->rx_bytes += skb->len; - __skb_tunnel_rx(skb, t->dev); - - dscp_ecn_decapsulate(t, ipv6h, skb); - netif_rx(skb); rcu_read_unlock(); diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 79bb49061ef..926ea544f49 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1646,6 +1646,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns case MRT6_ASSERT: { int v; + + if (optlen != sizeof(v)) + return -EINVAL; if (get_user(v, (int __user *)optval)) return -EFAULT; mrt->mroute_do_assert = v; @@ -1656,6 +1659,9 @@ int ip6_mroute_setsockopt(struct sock *sk, int optname, char __user *optval, uns case MRT6_PIM: { int v; + + if (optlen != sizeof(v)) + return -EINVAL; if (get_user(v, (int __user *)optval)) return -EFAULT; v = !!v; diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index f41853bca42..cf43b6550f5 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1032,18 +1032,6 @@ errout: rtnl_set_sk_err(net, RTNLGRP_ND_USEROPT, err); } -static inline int accept_ra(struct inet6_dev *in6_dev) -{ - /* - * If forwarding is enabled, RA are not accepted unless the special - * hybrid mode (accept_ra=2) is enabled. - */ - if (in6_dev->cnf.forwarding && in6_dev->cnf.accept_ra < 2) - return 0; - - return in6_dev->cnf.accept_ra; -} - static void ndisc_router_discovery(struct sk_buff *skb) { struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb); @@ -1091,7 +1079,7 @@ static void ndisc_router_discovery(struct sk_buff *skb) return; } - if (!accept_ra(in6_dev)) + if (!ipv6_accept_ra(in6_dev)) goto skip_linkparms; #ifdef CONFIG_IPV6_NDISC_NODETYPE @@ -1247,7 +1235,7 @@ skip_linkparms: NEIGH_UPDATE_F_ISROUTER); } - if (!accept_ra(in6_dev)) + if (!ipv6_accept_ra(in6_dev)) goto out; #ifdef CONFIG_IPV6_ROUTE_INFO diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 74cadd0719a..125a90d6a79 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -2271,112 +2271,9 @@ static void __exit ip6_tables_fini(void) unregister_pernet_subsys(&ip6_tables_net_ops); } -/* - * find the offset to specified header or the protocol number of last header - * if target < 0. "last header" is transport protocol header, ESP, or - * "No next header". - * - * Note that *offset is used as input/output parameter. an if it is not zero, - * then it must be a valid offset to an inner IPv6 header. This can be used - * to explore inner IPv6 header, eg. ICMPv6 error messages. - * - * If target header is found, its offset is set in *offset and return protocol - * number. Otherwise, return -1. - * - * If the first fragment doesn't contain the final protocol header or - * NEXTHDR_NONE it is considered invalid. - * - * Note that non-1st fragment is special case that "the protocol number - * of last header" is "next header" field in Fragment header. In this case, - * *offset is meaningless and fragment offset is stored in *fragoff if fragoff - * isn't NULL. - * - * if flags is not NULL and it's a fragment, then the frag flag IP6T_FH_F_FRAG - * will be set. If it's an AH header, the IP6T_FH_F_AUTH flag is set and - * target < 0, then this function will stop at the AH header. - */ -int ipv6_find_hdr(const struct sk_buff *skb, unsigned int *offset, - int target, unsigned short *fragoff, int *flags) -{ - unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr); - u8 nexthdr = ipv6_hdr(skb)->nexthdr; - unsigned int len; - - if (fragoff) - *fragoff = 0; - - if (*offset) { - struct ipv6hdr _ip6, *ip6; - - ip6 = skb_header_pointer(skb, *offset, sizeof(_ip6), &_ip6); - if (!ip6 || (ip6->version != 6)) { - printk(KERN_ERR "IPv6 header not found\n"); - return -EBADMSG; - } - start = *offset + sizeof(struct ipv6hdr); - nexthdr = ip6->nexthdr; - } - len = skb->len - start; - - while (nexthdr != target) { - struct ipv6_opt_hdr _hdr, *hp; - unsigned int hdrlen; - - if ((!ipv6_ext_hdr(nexthdr)) || nexthdr == NEXTHDR_NONE) { - if (target < 0) - break; - return -ENOENT; - } - - hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr); - if (hp == NULL) - return -EBADMSG; - if (nexthdr == NEXTHDR_FRAGMENT) { - unsigned short _frag_off; - __be16 *fp; - - if (flags) /* Indicate that this is a fragment */ - *flags |= IP6T_FH_F_FRAG; - fp = skb_header_pointer(skb, - start+offsetof(struct frag_hdr, - frag_off), - sizeof(_frag_off), - &_frag_off); - if (fp == NULL) - return -EBADMSG; - - _frag_off = ntohs(*fp) & ~0x7; - if (_frag_off) { - if (target < 0 && - ((!ipv6_ext_hdr(hp->nexthdr)) || - hp->nexthdr == NEXTHDR_NONE)) { - if (fragoff) - *fragoff = _frag_off; - return hp->nexthdr; - } - return -ENOENT; - } - hdrlen = 8; - } else if (nexthdr == NEXTHDR_AUTH) { - if (flags && (*flags & IP6T_FH_F_AUTH) && (target < 0)) - break; - hdrlen = (hp->hdrlen + 2) << 2; - } else - hdrlen = ipv6_optlen(hp); - - nexthdr = hp->nexthdr; - len -= hdrlen; - start += hdrlen; - } - - *offset = start; - return nexthdr; -} - EXPORT_SYMBOL(ip6t_register_table); EXPORT_SYMBOL(ip6t_unregister_table); EXPORT_SYMBOL(ip6t_do_table); -EXPORT_SYMBOL(ipv6_find_hdr); module_init(ip6_tables_init); module_exit(ip6_tables_fini); diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 80cb3829831..cfba99b2c2a 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c @@ -65,6 +65,10 @@ #define HASH_SIZE 16 #define HASH(addr) (((__force u32)addr^((__force u32)addr>>4))&0xF) +static bool log_ecn_error = true; +module_param(log_ecn_error, bool, 0644); +MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); + static int ipip6_tunnel_init(struct net_device *dev); static void ipip6_tunnel_setup(struct net_device *dev); static void ipip6_dev_free(struct net_device *dev); @@ -106,6 +110,7 @@ static struct rtnl_link_stats64 *ipip6_get_stats64(struct net_device *dev, } tot->rx_errors = dev->stats.rx_errors; + tot->rx_frame_errors = dev->stats.rx_frame_errors; tot->tx_fifo_errors = dev->stats.tx_fifo_errors; tot->tx_carrier_errors = dev->stats.tx_carrier_errors; tot->tx_dropped = dev->stats.tx_dropped; @@ -585,16 +590,11 @@ out: return err; } -static inline void ipip6_ecn_decapsulate(const struct iphdr *iph, struct sk_buff *skb) -{ - if (INET_ECN_is_ce(iph->tos)) - IP6_ECN_set_ce(ipv6_hdr(skb)); -} - static int ipip6_rcv(struct sk_buff *skb) { const struct iphdr *iph; struct ip_tunnel *tunnel; + int err; if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto out; @@ -616,18 +616,27 @@ static int ipip6_rcv(struct sk_buff *skb) if ((tunnel->dev->priv_flags & IFF_ISATAP) && !isatap_chksrc(skb, iph, tunnel)) { tunnel->dev->stats.rx_errors++; - kfree_skb(skb); - return 0; + goto out; + } + + __skb_tunnel_rx(skb, tunnel->dev); + + err = IP_ECN_decapsulate(iph, skb); + if (unlikely(err)) { + if (log_ecn_error) + net_info_ratelimited("non-ECT from %pI4 with TOS=%#x\n", + &iph->saddr, iph->tos); + if (err > 1) { + ++tunnel->dev->stats.rx_frame_errors; + ++tunnel->dev->stats.rx_errors; + goto out; + } } tstats = this_cpu_ptr(tunnel->dev->tstats); tstats->rx_packets++; tstats->rx_bytes += skb->len; - __skb_tunnel_rx(skb, tunnel->dev); - - ipip6_ecn_decapsulate(iph, skb); - netif_rx(skb); return 0; diff --git a/net/irda/irttp.c b/net/irda/irttp.c index 1002e3396f7..ae43c62f904 100644 --- a/net/irda/irttp.c +++ b/net/irda/irttp.c @@ -441,6 +441,7 @@ struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); if (lsap == NULL) { IRDA_DEBUG(0, "%s: unable to allocate LSAP!!\n", __func__); + __irttp_close_tsap(self); return NULL; } diff --git a/net/mac80211/aes_cmac.c b/net/mac80211/aes_cmac.c index 493353534a0..537488cbf94 100644 --- a/net/mac80211/aes_cmac.c +++ b/net/mac80211/aes_cmac.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/crypto.h> +#include <linux/export.h> #include <linux/err.h> #include <crypto/aes.h> diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 186d9919b04..808338a1bce 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -118,7 +118,7 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, return; } - for (i = 0; i < STA_TID_NUM; i++) + for (i = 0; i < IEEE80211_NUM_TIDS; i++) if (ba_rx_bitmap & BIT(i)) set_bit(i, sta->ampdu_mlme.tid_rx_stop_requested); diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 3195a6307f5..4152ed1034b 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -448,7 +448,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid, if (WARN_ON(!local->ops->ampdu_action)) return -EINVAL; - if ((tid >= STA_TID_NUM) || + if ((tid >= IEEE80211_NUM_TIDS) || !(local->hw.flags & IEEE80211_HW_AMPDU_AGGREGATION) || (local->hw.flags & IEEE80211_HW_TX_AMPDU_SETUP_IN_HW)) return -EINVAL; @@ -605,9 +605,9 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u16 tid) trace_api_start_tx_ba_cb(sdata, ra, tid); - if (tid >= STA_TID_NUM) { + if (tid >= IEEE80211_NUM_TIDS) { ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", - tid, STA_TID_NUM); + tid, IEEE80211_NUM_TIDS); return; } @@ -687,7 +687,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_sta *pubsta, u16 tid) if (!local->ops->ampdu_action) return -EINVAL; - if (tid >= STA_TID_NUM) + if (tid >= IEEE80211_NUM_TIDS) return -EINVAL; spin_lock_bh(&sta->lock); @@ -722,9 +722,9 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_vif *vif, u8 *ra, u8 tid) trace_api_stop_tx_ba_cb(sdata, ra, tid); - if (tid >= STA_TID_NUM) { + if (tid >= IEEE80211_NUM_TIDS) { ht_dbg(sdata, "Bad TID value: tid = %d (>= %d)\n", - tid, STA_TID_NUM); + tid, IEEE80211_NUM_TIDS); return; } diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 76690020d60..4965aa6424e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -370,30 +370,32 @@ 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) -{ - enum ieee80211_band band = ieee80211_get_sdata_band(sta->sdata); - - if (!(rate->flags & RATE_INFO_FLAGS_MCS)) { - struct ieee80211_supported_band *sband; - sband = sta->local->hw.wiphy->bands[band]; - rate->legacy = sband->bitrates[idx].bitrate; - } else - rate->mcs = idx; -} - void sta_set_rate_info_tx(struct sta_info *sta, const struct ieee80211_tx_rate *rate, struct rate_info *rinfo) { rinfo->flags = 0; - if (rate->flags & IEEE80211_TX_RC_MCS) + if (rate->flags & IEEE80211_TX_RC_MCS) { rinfo->flags |= RATE_INFO_FLAGS_MCS; + rinfo->mcs = rate->idx; + } else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; + rinfo->mcs = ieee80211_rate_get_vht_mcs(rate); + rinfo->nss = ieee80211_rate_get_vht_nss(rate); + } else { + struct ieee80211_supported_band *sband; + sband = sta->local->hw.wiphy->bands[ + ieee80211_get_sdata_band(sta->sdata)]; + rinfo->legacy = sband->bitrates[rate->idx].bitrate; + } if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) rinfo->flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + rinfo->flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + rinfo->flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; if (rate->flags & IEEE80211_TX_RC_SHORT_GI) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; - rate_idx_to_bitrate(rinfo, sta, rate->idx); } static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) @@ -444,13 +446,32 @@ static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) sta_set_rate_info_tx(sta, &sta->last_tx_rate, &sinfo->txrate); sinfo->rxrate.flags = 0; - if (sta->last_rx_rate_flag & RX_FLAG_HT) + if (sta->last_rx_rate_flag & RX_FLAG_HT) { sinfo->rxrate.flags |= RATE_INFO_FLAGS_MCS; + sinfo->rxrate.mcs = sta->last_rx_rate_idx; + } else if (sta->last_rx_rate_flag & RX_FLAG_VHT) { + sinfo->rxrate.flags |= RATE_INFO_FLAGS_VHT_MCS; + sinfo->rxrate.nss = sta->last_rx_rate_vht_nss; + sinfo->rxrate.mcs = sta->last_rx_rate_idx; + } else { + struct ieee80211_supported_band *sband; + + sband = sta->local->hw.wiphy->bands[ + ieee80211_get_sdata_band(sta->sdata)]; + sinfo->rxrate.legacy = + sband->bitrates[sta->last_rx_rate_idx].bitrate; + } + 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 (sta->last_rx_rate_flag & RX_FLAG_80MHZ) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_80P80MHZ) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; + if (sta->last_rx_rate_flag & RX_FLAG_160MHZ) + sinfo->rxrate.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; if (ieee80211_vif_is_mesh(&sdata->vif)) { #ifdef CONFIG_MAC80211_MESH @@ -615,7 +636,7 @@ do_survey: rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) - channel = chanctx_conf->channel; + channel = chanctx_conf->def.chan; else channel = NULL; rcu_read_unlock(); @@ -735,15 +756,13 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, } static int ieee80211_set_monitor_channel(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { struct ieee80211_local *local = wiphy_priv(wiphy); struct ieee80211_sub_if_data *sdata; int ret = 0; - if (local->monitor_channel == chan && - local->monitor_channel_type == channel_type) + if (cfg80211_chandef_identical(&local->monitor_chandef, chandef)) return 0; mutex_lock(&local->iflist_mtx); @@ -753,20 +772,17 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy, lockdep_is_held(&local->iflist_mtx)); if (sdata) { ieee80211_vif_release_channel(sdata); - ret = ieee80211_vif_use_channel( - sdata, chan, channel_type, + ret = ieee80211_vif_use_channel(sdata, chandef, IEEE80211_CHANCTX_EXCLUSIVE); } } else if (local->open_count == local->monitors) { - local->_oper_channel = chan; - local->_oper_channel_type = channel_type; + local->_oper_channel = chandef->chan; + local->_oper_channel_type = cfg80211_get_chandef_type(chandef); ieee80211_hw_config(local, 0); } - if (ret == 0) { - local->monitor_channel = chan; - local->monitor_channel_type = channel_type; - } + if (ret == 0) + local->monitor_chandef = *chandef; mutex_unlock(&local->iflist_mtx); return ret; @@ -888,8 +904,7 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_vif_use_channel(sdata, params->channel, - params->channel_type, + err = ieee80211_vif_use_channel(sdata, ¶ms->chandef, IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -922,6 +937,15 @@ static int ieee80211_start_ap(struct wiphy *wiphy, struct net_device *dev, return err; changed |= err; + err = drv_start_ap(sdata->local, sdata); + if (err) { + old = rtnl_dereference(sdata->u.ap.beacon); + if (old) + kfree_rcu(old, rcu_head); + RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); + return err; + } + ieee80211_bss_info_change_notify(sdata, changed); netif_carrier_on(dev); @@ -953,26 +977,38 @@ static int ieee80211_change_beacon(struct wiphy *wiphy, struct net_device *dev, static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) { - struct ieee80211_sub_if_data *sdata, *vlan; - struct beacon_data *old; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + struct ieee80211_sub_if_data *vlan; + struct ieee80211_local *local = sdata->local; + struct beacon_data *old_beacon; + struct probe_resp *old_probe_resp; - old = rtnl_dereference(sdata->u.ap.beacon); - if (!old) + old_beacon = rtnl_dereference(sdata->u.ap.beacon); + if (!old_beacon) return -ENOENT; + old_probe_resp = rtnl_dereference(sdata->u.ap.probe_resp); + /* turn off carrier for this interface and dependent VLANs */ list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) netif_carrier_off(vlan->dev); netif_carrier_off(dev); + /* remove beacon and probe response */ RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); + RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL); + kfree_rcu(old_beacon, rcu_head); + if (old_probe_resp) + kfree_rcu(old_probe_resp, rcu_head); - kfree_rcu(old, rcu_head); - - sta_info_flush(sdata->local, sdata); + sta_info_flush(local, sdata); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); + drv_stop_ap(sdata->local, sdata); + + /* free all potentially still buffered bcast frames */ + local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); + skb_queue_purge(&sdata->u.ap.ps.bc_buf); + ieee80211_vif_release_channel(sdata); return 0; @@ -1686,8 +1722,7 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev, sdata->smps_mode = IEEE80211_SMPS_OFF; sdata->needed_rx_chains = sdata->local->rx_chains; - err = ieee80211_vif_use_channel(sdata, setup->channel, - setup->channel_type, + err = ieee80211_vif_use_channel(sdata, &setup->chandef, IEEE80211_CHANCTX_SHARED); if (err) return err; @@ -1933,6 +1968,16 @@ static int ieee80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev) return ieee80211_ibss_leave(IEEE80211_DEV_TO_SUB_IF(dev)); } +static int ieee80211_set_mcast_rate(struct wiphy *wiphy, struct net_device *dev, + int rate[IEEE80211_NUM_BANDS]) +{ + struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + memcpy(sdata->vif.bss_conf.mcast_rate, rate, sizeof(rate)); + + return 0; +} + static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) { struct ieee80211_local *local = wiphy_priv(wiphy); @@ -1959,10 +2004,16 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) return err; } - if (changed & WIPHY_PARAM_RETRY_SHORT) + if (changed & WIPHY_PARAM_RETRY_SHORT) { + if (wiphy->retry_short > IEEE80211_MAX_TX_RETRY) + return -EINVAL; local->hw.conf.short_frame_max_tx_count = wiphy->retry_short; - if (changed & WIPHY_PARAM_RETRY_LONG) + } + if (changed & WIPHY_PARAM_RETRY_LONG) { + if (wiphy->retry_long > IEEE80211_MAX_TX_RETRY) + return -EINVAL; local->hw.conf.long_frame_max_tx_count = wiphy->retry_long; + } if (changed & (WIPHY_PARAM_RETRY_SHORT | WIPHY_PARAM_RETRY_LONG)) ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_RETRY_LIMITS); @@ -1971,45 +2022,65 @@ static int ieee80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) } static int ieee80211_set_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { struct ieee80211_local *local = wiphy_priv(wiphy); - struct ieee80211_channel *chan = local->_oper_channel; - u32 changes = 0; + struct ieee80211_sub_if_data *sdata; - /* FIXME */ - if (local->use_chanctx) - return -EOPNOTSUPP; + if (wdev) { + sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); + + switch (type) { + case NL80211_TX_POWER_AUTOMATIC: + sdata->user_power_level = IEEE80211_UNSET_POWER_LEVEL; + break; + case NL80211_TX_POWER_LIMITED: + case NL80211_TX_POWER_FIXED: + if (mbm < 0 || (mbm % 100)) + return -EOPNOTSUPP; + sdata->user_power_level = MBM_TO_DBM(mbm); + break; + } + + ieee80211_recalc_txpower(sdata); + + return 0; + } switch (type) { case NL80211_TX_POWER_AUTOMATIC: - local->user_power_level = -1; + local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; break; case NL80211_TX_POWER_LIMITED: - if (mbm < 0 || (mbm % 100)) - return -EOPNOTSUPP; - local->user_power_level = MBM_TO_DBM(mbm); - break; case NL80211_TX_POWER_FIXED: if (mbm < 0 || (mbm % 100)) return -EOPNOTSUPP; - /* TODO: move to cfg80211 when it knows the channel */ - if (MBM_TO_DBM(mbm) > chan->max_power) - return -EINVAL; local->user_power_level = MBM_TO_DBM(mbm); break; } - ieee80211_hw_config(local, changes); + mutex_lock(&local->iflist_mtx); + list_for_each_entry(sdata, &local->interfaces, list) + sdata->user_power_level = local->user_power_level; + list_for_each_entry(sdata, &local->interfaces, list) + ieee80211_recalc_txpower(sdata); + mutex_unlock(&local->iflist_mtx); return 0; } -static int ieee80211_get_tx_power(struct wiphy *wiphy, int *dbm) +static int ieee80211_get_tx_power(struct wiphy *wiphy, + struct wireless_dev *wdev, + int *dbm) { struct ieee80211_local *local = wiphy_priv(wiphy); + struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); - *dbm = local->hw.conf.power_level; + if (!local->use_chanctx) + *dbm = local->hw.conf.power_level; + else + *dbm = sdata->vif.bss_conf.txpower; return 0; } @@ -2078,7 +2149,7 @@ int __ieee80211_request_smps(struct ieee80211_sub_if_data *sdata, * the new value until we associate. */ if (!sdata->u.mgd.associated || - sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; ap = sdata->u.mgd.associated->bssid; @@ -2185,7 +2256,6 @@ static int ieee80211_set_bitrate_mask(struct wiphy *wiphy, static int ieee80211_start_roc_work(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie, struct sk_buff *txskb) { @@ -2203,7 +2273,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, return -ENOMEM; roc->chan = channel; - roc->chan_type = channel_type; roc->duration = duration; roc->req_duration = duration; roc->frame = txskb; @@ -2236,7 +2305,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, if (!duration) duration = 10; - ret = drv_remain_on_channel(local, channel, channel_type, duration); + ret = drv_remain_on_channel(local, sdata, channel, duration); if (ret) { kfree(roc); return ret; @@ -2247,7 +2316,7 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, out_check_combine: list_for_each_entry(tmp, &local->roc_list, list) { - if (tmp->chan != channel || tmp->chan_type != channel_type) + if (tmp->chan != channel || tmp->sdata != sdata) continue; /* @@ -2341,13 +2410,22 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, list_add_tail(&roc->list, &local->roc_list); /* - * cookie is either the roc (for normal roc) + * cookie is either the roc cookie (for normal roc) * or the SKB (for mgmt TX) */ - if (txskb) + if (!txskb) { + /* local->mtx protects this */ + local->roc_cookie_counter++; + roc->cookie = local->roc_cookie_counter; + /* wow, you wrapped 64 bits ... more likely a bug */ + if (WARN_ON(roc->cookie == 0)) { + roc->cookie = 1; + local->roc_cookie_counter++; + } + *cookie = roc->cookie; + } else { *cookie = (unsigned long)txskb; - else - *cookie = (unsigned long)roc; + } return 0; } @@ -2355,7 +2433,6 @@ static int ieee80211_start_roc_work(struct ieee80211_local *local, static int ieee80211_remain_on_channel(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { @@ -2364,7 +2441,7 @@ static int ieee80211_remain_on_channel(struct wiphy *wiphy, int ret; mutex_lock(&local->mtx); - ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + ret = ieee80211_start_roc_work(local, sdata, chan, duration, cookie, NULL); mutex_unlock(&local->mtx); @@ -2382,7 +2459,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, struct ieee80211_roc_work *dep, *tmp2; list_for_each_entry_safe(dep, tmp2, &roc->dependents, list) { - if (!mgmt_tx && (unsigned long)dep != cookie) + if (!mgmt_tx && dep->cookie != cookie) continue; else if (mgmt_tx && dep->mgmt_tx_cookie != cookie) continue; @@ -2394,7 +2471,7 @@ static int ieee80211_cancel_roc(struct ieee80211_local *local, return 0; } - if (!mgmt_tx && (unsigned long)roc != cookie) + if (!mgmt_tx && roc->cookie != cookie) continue; else if (mgmt_tx && roc->mgmt_tx_cookie != cookie) continue; @@ -2457,10 +2534,8 @@ static int ieee80211_cancel_remain_on_channel(struct wiphy *wiphy, static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_local *local = sdata->local; @@ -2529,14 +2604,10 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); - if (chanctx_conf) { - need_offchan = chan != chanctx_conf->channel; - if (channel_type_valid && - channel_type != chanctx_conf->channel_type) - need_offchan = true; - } else { + if (chanctx_conf) + need_offchan = chan != chanctx_conf->def.chan; + else need_offchan = true; - } rcu_read_unlock(); } @@ -2571,7 +2642,7 @@ static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, local->hw.offchannel_tx_hw_queue; /* This will handle all kinds of coalescing and immediate TX */ - ret = ieee80211_start_roc_work(local, sdata, chan, channel_type, + ret = ieee80211_start_roc_work(local, sdata, chan, wait, cookie, skb); if (ret) kfree_skb(skb); @@ -3005,7 +3076,7 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, rcu_read_unlock(); return -EINVAL; } - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; sta = sta_info_get(sdata, peer); if (sta) { qos = test_sta_flag(sta, WLAN_STA_WME); @@ -3062,23 +3133,23 @@ static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, return 0; } -static struct ieee80211_channel * -ieee80211_cfg_get_channel(struct wiphy *wiphy, struct wireless_dev *wdev, - enum nl80211_channel_type *type) +static int ieee80211_cfg_get_channel(struct wiphy *wiphy, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev); struct ieee80211_chanctx_conf *chanctx_conf; - struct ieee80211_channel *chan = NULL; + int ret = -ENODATA; rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf) { - *type = chanctx_conf->channel_type; - chan = chanctx_conf->channel; + *chandef = chanctx_conf->def; + ret = 0; } rcu_read_unlock(); - return chan; + return ret; } #ifdef CONFIG_PM @@ -3133,6 +3204,7 @@ struct cfg80211_ops mac80211_config_ops = { .disassoc = ieee80211_disassoc, .join_ibss = ieee80211_join_ibss, .leave_ibss = ieee80211_leave_ibss, + .set_mcast_rate = ieee80211_set_mcast_rate, .set_wiphy_params = ieee80211_set_wiphy_params, .set_tx_power = ieee80211_set_tx_power, .get_tx_power = ieee80211_get_tx_power, diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index f84b86028a9..53f03120db5 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -8,93 +8,47 @@ #include "ieee80211_i.h" #include "driver-ops.h" -static bool -ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1, - enum nl80211_channel_type chantype2, - enum nl80211_channel_type *compat) +static void ieee80211_change_chandef(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx, + const struct cfg80211_chan_def *chandef) { - /* - * start out with chantype1 being the result, - * overwriting later if needed - */ - if (compat) - *compat = chantype1; - - switch (chantype1) { - case NL80211_CHAN_NO_HT: - if (compat) - *compat = chantype2; - break; - case NL80211_CHAN_HT20: - /* - * allow any change that doesn't go to no-HT - * (if it already is no-HT no change is needed) - */ - if (chantype2 == NL80211_CHAN_NO_HT) - break; - if (compat) - *compat = chantype2; - break; - case NL80211_CHAN_HT40PLUS: - case NL80211_CHAN_HT40MINUS: - /* allow smaller bandwidth and same */ - if (chantype2 == NL80211_CHAN_NO_HT) - break; - if (chantype2 == NL80211_CHAN_HT20) - break; - if (chantype2 == chantype1) - break; - return false; - } - - return true; -} - -static void ieee80211_change_chantype(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx, - enum nl80211_channel_type chantype) -{ - if (chantype == ctx->conf.channel_type) + if (cfg80211_chandef_identical(&ctx->conf.def, chandef)) return; - ctx->conf.channel_type = chantype; - drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE); + WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef)); + + ctx->conf.def = *chandef; + drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH); if (!local->use_chanctx) { - local->_oper_channel_type = chantype; + local->_oper_channel_type = cfg80211_get_chandef_type(chandef); ieee80211_hw_config(local, 0); } } static struct ieee80211_chanctx * ieee80211_find_chanctx(struct ieee80211_local *local, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode) { struct ieee80211_chanctx *ctx; - enum nl80211_channel_type compat_type; lockdep_assert_held(&local->chanctx_mtx); if (mode == IEEE80211_CHANCTX_EXCLUSIVE) return NULL; - if (WARN_ON(!channel)) - return NULL; list_for_each_entry(ctx, &local->chanctx_list, list) { - compat_type = ctx->conf.channel_type; + const struct cfg80211_chan_def *compat; if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE) continue; - if (ctx->conf.channel != channel) - continue; - if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type, - channel_type, - &compat_type)) + + compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef); + if (!compat) continue; - ieee80211_change_chantype(local, ctx, compat_type); + ieee80211_change_chandef(local, ctx, compat); return ctx; } @@ -104,8 +58,7 @@ ieee80211_find_chanctx(struct ieee80211_local *local, static struct ieee80211_chanctx * ieee80211_new_chanctx(struct ieee80211_local *local, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode) { struct ieee80211_chanctx *ctx; @@ -117,15 +70,15 @@ ieee80211_new_chanctx(struct ieee80211_local *local, if (!ctx) return ERR_PTR(-ENOMEM); - ctx->conf.channel = channel; - ctx->conf.channel_type = channel_type; + ctx->conf.def = *chandef; ctx->conf.rx_chains_static = 1; ctx->conf.rx_chains_dynamic = 1; ctx->mode = mode; if (!local->use_chanctx) { - local->_oper_channel_type = channel_type; - local->_oper_channel = channel; + local->_oper_channel_type = + cfg80211_get_chandef_type(chandef); + local->_oper_channel = chandef->chan; ieee80211_hw_config(local, 0); } else { err = drv_add_chanctx(local, ctx); @@ -173,44 +126,42 @@ static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata, rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf); ctx->refcount++; + ieee80211_recalc_txpower(sdata); + return 0; } -static enum nl80211_channel_type -ieee80211_calc_chantype(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx) +static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, + struct ieee80211_chanctx *ctx) { struct ieee80211_chanctx_conf *conf = &ctx->conf; struct ieee80211_sub_if_data *sdata; - enum nl80211_channel_type result = NL80211_CHAN_NO_HT; + const struct cfg80211_chan_def *compat = NULL; lockdep_assert_held(&local->chanctx_mtx); rcu_read_lock(); list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!ieee80211_sdata_running(sdata)) continue; if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf) continue; - WARN_ON_ONCE(!ieee80211_channel_types_are_compatible( - sdata->vif.bss_conf.channel_type, - result, &result)); + if (!compat) + compat = &sdata->vif.bss_conf.chandef; + + compat = cfg80211_chandef_compatible( + &sdata->vif.bss_conf.chandef, compat); + if (!compat) + break; } rcu_read_unlock(); - return result; -} - -static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local, - struct ieee80211_chanctx *ctx) -{ - enum nl80211_channel_type chantype; - - lockdep_assert_held(&local->chanctx_mtx); + if (WARN_ON_ONCE(!compat)) + return; - chantype = ieee80211_calc_chantype(local, ctx); - ieee80211_change_chantype(local, ctx, chantype); + ieee80211_change_chandef(local, ctx, compat); } static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata, @@ -335,8 +286,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local, } int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode) { struct ieee80211_local *local = sdata->local; @@ -348,15 +298,15 @@ int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, mutex_lock(&local->chanctx_mtx); __ieee80211_vif_release_channel(sdata); - ctx = ieee80211_find_chanctx(local, channel, channel_type, mode); + ctx = ieee80211_find_chanctx(local, chandef, mode); if (!ctx) - ctx = ieee80211_new_chanctx(local, channel, channel_type, mode); + ctx = ieee80211_new_chanctx(local, chandef, mode); if (IS_ERR(ctx)) { ret = PTR_ERR(ctx); goto out; } - sdata->vif.bss_conf.channel_type = channel_type; + sdata->vif.bss_conf.chandef = *chandef; ret = ieee80211_assign_vif_chanctx(sdata, ctx); if (ret) { diff --git a/net/mac80211/debugfs_key.c b/net/mac80211/debugfs_key.c index 090d08ff22c..2d4235497f1 100644 --- a/net/mac80211/debugfs_key.c +++ b/net/mac80211/debugfs_key.c @@ -116,7 +116,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { struct ieee80211_key *key = file->private_data; - char buf[14*NUM_RX_DATA_QUEUES+1], *p = buf; + char buf[14*IEEE80211_NUM_TIDS+1], *p = buf; int i, len; const u8 *rpn; @@ -126,7 +126,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, len = scnprintf(buf, sizeof(buf), "\n"); break; case WLAN_CIPHER_SUITE_TKIP: - for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + for (i = 0; i < IEEE80211_NUM_TIDS; i++) p += scnprintf(p, sizeof(buf)+buf-p, "%08x %04x\n", key->u.tkip.rx[i].iv32, @@ -134,7 +134,7 @@ static ssize_t key_rx_spec_read(struct file *file, char __user *userbuf, len = p - buf; break; case WLAN_CIPHER_SUITE_CCMP: - for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) { rpn = key->u.ccmp.rx_pn[i]; p += scnprintf(p, sizeof(buf)+buf-p, "%02x%02x%02x%02x%02x%02x\n", diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index 3393ad5b8ab..cbde5cc49a4 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -10,6 +10,7 @@ #include <linux/kernel.h> #include <linux/device.h> #include <linux/if.h> +#include <linux/if_ether.h> #include <linux/interrupt.h> #include <linux/netdevice.h> #include <linux/rtnetlink.h> @@ -167,7 +168,29 @@ IEEE80211_IF_FILE(rc_rateidx_mcs_mask_5ghz, IEEE80211_IF_FILE(flags, flags, HEX); IEEE80211_IF_FILE(state, state, LHEX); -IEEE80211_IF_FILE(channel_type, vif.bss_conf.channel_type, DEC); +IEEE80211_IF_FILE(txpower, vif.bss_conf.txpower, DEC); +IEEE80211_IF_FILE(ap_power_level, ap_power_level, DEC); +IEEE80211_IF_FILE(user_power_level, user_power_level, DEC); + +static ssize_t +ieee80211_if_fmt_hw_queues(const struct ieee80211_sub_if_data *sdata, + char *buf, int buflen) +{ + int len; + + len = scnprintf(buf, buflen, "AC queues: VO:%d VI:%d BE:%d BK:%d\n", + sdata->vif.hw_queue[IEEE80211_AC_VO], + sdata->vif.hw_queue[IEEE80211_AC_VI], + sdata->vif.hw_queue[IEEE80211_AC_BE], + sdata->vif.hw_queue[IEEE80211_AC_BK]); + + if (sdata->vif.type == NL80211_IFTYPE_AP) + len += scnprintf(buf + len, buflen - len, "cab queue: %d\n", + sdata->vif.cab_queue); + + return len; +} +__IEEE80211_IF_FILE(hw_queues, NULL); /* STA attributes */ IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC); @@ -245,27 +268,6 @@ static ssize_t ieee80211_if_fmt_tkip_mic_test( return -EOPNOTSUPP; } -static int hwaddr_aton(const char *txt, u8 *addr) -{ - int i; - - for (i = 0; i < ETH_ALEN; i++) { - int a, b; - - a = hex_to_bin(*txt++); - if (a < 0) - return -1; - b = hex_to_bin(*txt++); - if (b < 0) - return -1; - *addr++ = (a << 4) | b; - if (i < 5 && *txt++ != ':') - return -1; - } - - return 0; -} - static ssize_t ieee80211_if_parse_tkip_mic_test( struct ieee80211_sub_if_data *sdata, const char *buf, int buflen) { @@ -275,13 +277,7 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( struct ieee80211_hdr *hdr; __le16 fc; - /* - * Assume colon-delimited MAC address with possible white space - * following. - */ - if (buflen < 3 * ETH_ALEN - 1) - return -EINVAL; - if (hwaddr_aton(buf, addr) < 0) + if (!mac_pton(buf, addr)) return -EINVAL; if (!ieee80211_sdata_running(sdata)) @@ -307,13 +303,16 @@ static ssize_t ieee80211_if_parse_tkip_mic_test( case NL80211_IFTYPE_STATION: fc |= cpu_to_le16(IEEE80211_FCTL_TODS); /* BSSID SA DA */ - if (sdata->vif.bss_conf.bssid == NULL) { + mutex_lock(&sdata->u.mgd.mtx); + if (!sdata->u.mgd.associated) { + mutex_unlock(&sdata->u.mgd.mtx); dev_kfree_skb(skb); return -ENOTCONN; } - memcpy(hdr->addr1, sdata->vif.bss_conf.bssid, ETH_ALEN); + memcpy(hdr->addr1, sdata->u.mgd.associated->bssid, ETH_ALEN); memcpy(hdr->addr2, sdata->vif.addr, ETH_ALEN); memcpy(hdr->addr3, addr, ETH_ALEN); + mutex_unlock(&sdata->u.mgd.mtx); break; default: dev_kfree_skb(skb); @@ -443,7 +442,7 @@ static ssize_t ieee80211_if_parse_tsf( } ret = kstrtoull(buf, 10, &tsf); if (ret < 0) - return -EINVAL; + return ret; if (tsf_is_delta) tsf = drv_get_tsf(local, sdata) + tsf_is_delta * tsf; if (local->ops->set_tsf) { @@ -531,6 +530,7 @@ static void add_common_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(rc_rateidx_mask_5ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_2ghz); DEBUGFS_ADD(rc_rateidx_mcs_mask_5ghz); + DEBUGFS_ADD(hw_queues); } static void add_sta_files(struct ieee80211_sub_if_data *sdata) @@ -631,7 +631,9 @@ static void add_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(flags); DEBUGFS_ADD(state); - DEBUGFS_ADD(channel_type); + DEBUGFS_ADD(txpower); + DEBUGFS_ADD(user_power_level); + DEBUGFS_ADD(ap_power_level); if (sdata->vif.type != NL80211_IFTYPE_MONITOR) add_common_files(sdata); diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index 5ccec2c1e9f..89281d24b09 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -14,6 +14,7 @@ #include "debugfs.h" #include "debugfs_sta.h" #include "sta_info.h" +#include "driver-ops.h" /* sta attributtes */ @@ -131,10 +132,10 @@ STA_OPS(connected_time); static ssize_t sta_last_seq_ctrl_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[15*NUM_RX_DATA_QUEUES], *p = buf; + char buf[15*IEEE80211_NUM_TIDS], *p = buf; int i; struct sta_info *sta = file->private_data; - for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + for (i = 0; i < IEEE80211_NUM_TIDS; i++) p += scnprintf(p, sizeof(buf)+buf-p, "%x ", le16_to_cpu(sta->last_seq_ctrl[i])); p += scnprintf(p, sizeof(buf)+buf-p, "\n"); @@ -145,7 +146,7 @@ STA_OPS(last_seq_ctrl); static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, size_t count, loff_t *ppos) { - char buf[71 + STA_TID_NUM * 40], *p = buf; + char buf[71 + IEEE80211_NUM_TIDS * 40], *p = buf; int i; struct sta_info *sta = file->private_data; struct tid_ampdu_rx *tid_rx; @@ -158,7 +159,7 @@ static ssize_t sta_agg_status_read(struct file *file, char __user *userbuf, p += scnprintf(p, sizeof(buf) + buf - p, "TID\t\tRX active\tDTKN\tSSN\t\tTX\tDTKN\tpending\n"); - for (i = 0; i < STA_TID_NUM; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { tid_rx = rcu_dereference(sta->ampdu_mlme.tid_rx[i]); tid_tx = rcu_dereference(sta->ampdu_mlme.tid_tx[i]); @@ -220,7 +221,7 @@ static ssize_t sta_agg_status_write(struct file *file, const char __user *userbu tid = simple_strtoul(buf, NULL, 0); - if (tid >= STA_TID_NUM) + if (tid >= IEEE80211_NUM_TIDS) return -EINVAL; if (tx) { @@ -334,6 +335,8 @@ STA_OPS(ht_capa); void ieee80211_sta_debugfs_add(struct sta_info *sta) { + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; struct dentry *stations_dir = sta->sdata->debugfs.subdir_stations; u8 mac[3*ETH_ALEN]; @@ -379,10 +382,16 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) DEBUGFS_ADD_COUNTER(tx_retry_failed, tx_retry_failed); DEBUGFS_ADD_COUNTER(tx_retry_count, tx_retry_count); DEBUGFS_ADD_COUNTER(wep_weak_iv_count, wep_weak_iv_count); + + drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); } void ieee80211_sta_debugfs_remove(struct sta_info *sta) { + struct ieee80211_local *local = sta->local; + struct ieee80211_sub_if_data *sdata = sta->sdata; + + drv_sta_remove_debugfs(local, sdata, &sta->sta, sta->debugfs.dir); debugfs_remove_recursive(sta->debugfs.dir); sta->debugfs.dir = NULL; } diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index 77407b31e1f..c6560cc7a9d 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h @@ -490,6 +490,38 @@ static inline void drv_sta_remove(struct ieee80211_local *local, trace_drv_return_void(local); } +#ifdef CONFIG_MAC80211_DEBUGFS +static inline void drv_sta_add_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + if (local->ops->sta_add_debugfs) + local->ops->sta_add_debugfs(&local->hw, &sdata->vif, + sta, dir); +} + +static inline void drv_sta_remove_debugfs(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_sta *sta, + struct dentry *dir) +{ + might_sleep(); + + sdata = get_bss_sdata(sdata); + check_sdata_in_driver(sdata); + + if (local->ops->sta_remove_debugfs) + local->ops->sta_remove_debugfs(&local->hw, &sdata->vif, + sta, dir); +} +#endif + static inline __must_check int drv_sta_state(struct ieee80211_local *local, struct ieee80211_sub_if_data *sdata, @@ -704,17 +736,17 @@ static inline int drv_get_antenna(struct ieee80211_local *local, } static inline int drv_remain_on_channel(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, struct ieee80211_channel *chan, - enum nl80211_channel_type chantype, unsigned int duration) { int ret; might_sleep(); - trace_drv_remain_on_channel(local, chan, chantype, duration); - ret = local->ops->remain_on_channel(&local->hw, chan, chantype, - duration); + trace_drv_remain_on_channel(local, sdata, chan, duration); + ret = local->ops->remain_on_channel(&local->hw, &sdata->vif, + chan, duration); trace_drv_return_int(local, ret); return ret; @@ -936,4 +968,39 @@ static inline void drv_unassign_vif_chanctx(struct ieee80211_local *local, trace_drv_return_void(local); } +static inline int drv_start_ap(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + int ret = 0; + + check_sdata_in_driver(sdata); + + trace_drv_start_ap(local, sdata, &sdata->vif.bss_conf); + if (local->ops->start_ap) + ret = local->ops->start_ap(&local->hw, &sdata->vif); + trace_drv_return_int(local, ret); + return ret; +} + +static inline void drv_stop_ap(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + check_sdata_in_driver(sdata); + + trace_drv_stop_ap(local, sdata); + if (local->ops->stop_ap) + local->ops->stop_ap(&local->hw, &sdata->vif); + trace_drv_return_void(local); +} + +static inline void drv_restart_complete(struct ieee80211_local *local) +{ + might_sleep(); + + trace_drv_restart_complete(local); + if (local->ops->restart_complete) + local->ops->restart_complete(&local->hw); + trace_drv_return_void(local); +} + #endif /* __MAC80211_DRIVER_OPS */ diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 4b4538d6392..a71d891794a 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -185,7 +185,7 @@ void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, bool tx) cancel_work_sync(&sta->ampdu_mlme.work); - for (i = 0; i < STA_TID_NUM; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { __ieee80211_stop_tx_ba_session(sta, i, WLAN_BACK_INITIATOR, tx); __ieee80211_stop_rx_ba_session(sta, i, WLAN_BACK_RECIPIENT, WLAN_REASON_QSTA_LEAVE_QBSS, tx); @@ -209,7 +209,7 @@ void ieee80211_ba_session_work(struct work_struct *work) return; mutex_lock(&sta->ampdu_mlme.mtx); - for (tid = 0; tid < STA_TID_NUM; tid++) { + for (tid = 0; tid < IEEE80211_NUM_TIDS; tid++) { if (test_and_clear_bit(tid, sta->ampdu_mlme.tid_rx_timer_expired)) ___ieee80211_stop_rx_ba_session( sta, tid, WLAN_BACK_RECIPIENT, diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 67774b05353..fa862b24a7e 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -51,7 +51,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, struct cfg80211_bss *bss; u32 bss_change; u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; lockdep_assert_held(&ifibss->mtx); @@ -79,12 +79,14 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, sdata->drop_unencrypted = capability & WLAN_CAPABILITY_PRIVACY ? 1 : 0; - channel_type = ifibss->channel_type; - if (!cfg80211_can_beacon_sec_chan(local->hw.wiphy, chan, channel_type)) - channel_type = NL80211_CHAN_HT20; + cfg80211_chandef_create(&chandef, chan, ifibss->channel_type); + if (!cfg80211_reg_can_beacon(local->hw.wiphy, &chandef)) { + chandef.width = NL80211_CHAN_WIDTH_20; + chandef.center_freq1 = chan->center_freq; + } ieee80211_vif_release_channel(sdata); - if (ieee80211_vif_use_channel(sdata, chan, channel_type, + if (ieee80211_vif_use_channel(sdata, &chandef, ifibss->fixed_channel ? IEEE80211_CHANCTX_SHARED : IEEE80211_CHANCTX_EXCLUSIVE)) { @@ -158,7 +160,8 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, ifibss->ie, ifibss->ie_len); /* add HT capability and information IEs */ - if (channel_type && sband->ht_cap.ht_supported) { + if (chandef.width != NL80211_CHAN_WIDTH_20_NOHT && + sband->ht_cap.ht_supported) { pos = skb_put(skb, 4 + sizeof(struct ieee80211_ht_cap) + sizeof(struct ieee80211_ht_operation)); @@ -170,7 +173,7 @@ static void __ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, * keep them at 0 */ pos = ieee80211_ie_build_ht_oper(pos, &sband->ht_cap, - chan, channel_type, 0); + &chandef, 0); } if (local->hw.queues >= IEEE80211_NUM_ACS) { @@ -326,7 +329,7 @@ ieee80211_ibss_add_sta(struct ieee80211_sub_if_data *sdata, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (WARN_ON_ONCE(!chanctx_conf)) return NULL; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; rcu_read_unlock(); sta = sta_info_alloc(sdata, addr, GFP_KERNEL); @@ -374,11 +377,13 @@ static void ieee80211_rx_mgmt_auth_ibss(struct ieee80211_sub_if_data *sdata, auth_alg = le16_to_cpu(mgmt->u.auth.auth_alg); auth_transaction = le16_to_cpu(mgmt->u.auth.auth_transaction); - if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) - return; ibss_dbg(sdata, "RX Auth SA=%pM DA=%pM BSSID=%pM (auth_transaction=%d)\n", mgmt->sa, mgmt->da, mgmt->bssid, auth_transaction); + + if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) + return; + sta_info_destroy_addr(sdata, mgmt->sa); sta = ieee80211_ibss_add_sta(sdata, mgmt->bssid, mgmt->sa, 0, false); rcu_read_unlock(); @@ -473,9 +478,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, sdata->u.ibss.channel_type != NL80211_CHAN_NO_HT) { /* we both use HT */ struct ieee80211_sta_ht_cap sta_ht_cap_new; - enum nl80211_channel_type channel_type = - ieee80211_ht_oper_to_channel_type( - elems->ht_operation); + struct cfg80211_chan_def chandef; + + ieee80211_ht_oper_to_chandef(channel, + elems->ht_operation, + &chandef); ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, @@ -485,9 +492,9 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, * fall back to HT20 if we don't use or use * the other extension channel */ - if (!(channel_type == NL80211_CHAN_HT40MINUS || - channel_type == NL80211_CHAN_HT40PLUS) || - channel_type != sdata->u.ibss.channel_type) + if (chandef.width != NL80211_CHAN_WIDTH_40 || + cfg80211_get_chandef_type(&chandef) != + sdata->u.ibss.channel_type) sta_ht_cap_new.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; @@ -543,30 +550,11 @@ static void ieee80211_rx_bss_info(struct ieee80211_sub_if_data *sdata, if (ether_addr_equal(cbss->bssid, sdata->u.ibss.bssid)) goto put_bss; - 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 - * the PHY, and the timestamp of the beacon is defined as "the - * time that the data symbol containing the first bit of the - * timestamp is transmitted to the PHY plus the transmitting - * STA's delays through its local PHY from the MAC-PHY - * interface to its interface with the WM" (802.11 11.1.2) - * - equals the time this bit arrives at the receiver - we have - * to take into account the offset between the two. - * - * E.g. at 1 MBit that means mactime is 192 usec earlier - * (=24 bytes * 8 usecs/byte) than the beacon timestamp. - */ - int rate; - - if (rx_status->flag & RX_FLAG_HT) - rate = 65; /* TODO: HT rates */ - else - rate = local->hw.wiphy->bands[band]-> - bitrates[rx_status->rate_idx].bitrate; - - rx_timestamp = rx_status->mactime + (24 * 8 * 10 / rate); + if (ieee80211_have_rx_timestamp(rx_status)) { + /* time when timestamp field was received */ + rx_timestamp = + ieee80211_calculate_rx_timestamp(local, rx_status, + len + FCS_LEN, 24); } else { /* * second best option: get current TSF @@ -630,7 +618,7 @@ void ieee80211_ibss_rx_no_sta(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); return; } - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; rcu_read_unlock(); sta = sta_info_alloc(sdata, addr, GFP_ATOMIC); @@ -1095,8 +1083,9 @@ int ieee80211_ibss_join(struct ieee80211_sub_if_data *sdata, sdata->vif.bss_conf.beacon_int = params->beacon_interval; - sdata->u.ibss.channel = params->channel; - sdata->u.ibss.channel_type = params->channel_type; + sdata->u.ibss.channel = params->chandef.chan; + sdata->u.ibss.channel_type = + cfg80211_get_chandef_type(¶ms->chandef); sdata->u.ibss.fixed_channel = params->channel_fixed; if (params->ie) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 32e47853f32..5c0d5a6946c 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -56,6 +56,9 @@ struct ieee80211_local; #define TU_TO_JIFFIES(x) (usecs_to_jiffies((x) * 1024)) #define TU_TO_EXP_TIME(x) (jiffies + TU_TO_JIFFIES(x)) +/* power level hasn't been configured (or set to automatic) */ +#define IEEE80211_UNSET_POWER_LEVEL INT_MIN + /* * Some APs experience problems when working with U-APSD. Decrease the * probability of that happening by using legacy mode for all ACs but VO. @@ -345,7 +348,6 @@ struct ieee80211_roc_work { struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *chan; - enum nl80211_channel_type chan_type; bool started, abort, hw_begun, notified; @@ -353,7 +355,7 @@ struct ieee80211_roc_work { u32 duration, req_duration; struct sk_buff *frame; - u64 mgmt_tx_cookie; + u64 cookie, mgmt_tx_cookie; }; /* flags used in struct ieee80211_if_managed.flags */ @@ -361,7 +363,7 @@ enum ieee80211_sta_flags { IEEE80211_STA_BEACON_POLL = BIT(0), IEEE80211_STA_CONNECTION_POLL = BIT(1), IEEE80211_STA_CONTROL_PORT = BIT(2), - IEEE80211_STA_DISABLE_11N = BIT(4), + IEEE80211_STA_DISABLE_HT = BIT(4), IEEE80211_STA_CSA_RECEIVED = BIT(5), IEEE80211_STA_MFP_ENABLED = BIT(6), IEEE80211_STA_UAPSD_ENABLED = BIT(7), @@ -470,6 +472,8 @@ struct ieee80211_if_managed { u8 use_4addr; + u8 p2p_noa_index; + /* Signal strength from the last Beacon frame in the current BSS. */ int last_beacon_signal; @@ -743,6 +747,9 @@ struct ieee80211_sub_if_data { u8 needed_rx_chains; enum ieee80211_smps_mode smps_mode; + int user_power_level; /* in dBm */ + int ap_power_level; /* in dBm */ + /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for @@ -792,7 +799,7 @@ ieee80211_get_sdata_band(struct ieee80211_sub_if_data *sdata) rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!WARN_ON(!chanctx_conf)) - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; rcu_read_unlock(); return band; @@ -1040,7 +1047,6 @@ struct ieee80211_local { /* Temporary remain-on-channel for off-channel operations */ struct ieee80211_channel *tmp_channel; - enum nl80211_channel_type tmp_channel_type; /* channel contexts */ struct list_head chanctx_list; @@ -1117,8 +1123,7 @@ struct ieee80211_local { int dynamic_ps_user_timeout; bool disable_dynamic_ps; - int user_power_level; /* in dBm */ - int ap_power_level; /* in dBm */ + int user_power_level; /* in dBm, for all interfaces */ enum ieee80211_smps_mode smps_mode; @@ -1137,6 +1142,7 @@ struct ieee80211_local { struct list_head roc_list; struct work_struct hw_roc_start, hw_roc_done; unsigned long hw_roc_start_time; + u64 roc_cookie_counter; struct idr ack_status_frames; spinlock_t ack_status_lock; @@ -1150,8 +1156,7 @@ struct ieee80211_local { /* virtual monitor interface */ struct ieee80211_sub_if_data __rcu *monitor_sdata; - struct ieee80211_channel *monitor_channel; - enum nl80211_channel_type monitor_channel_type; + struct cfg80211_chan_def monitor_chandef; }; static inline struct ieee80211_sub_if_data * @@ -1251,7 +1256,18 @@ static inline int ieee80211_bssid_match(const u8 *raddr, const u8 *addr) is_broadcast_ether_addr(raddr); } +static inline bool +ieee80211_have_rx_timestamp(struct ieee80211_rx_status *status) +{ + WARN_ON_ONCE(status->flag & RX_FLAG_MACTIME_START && + status->flag & RX_FLAG_MACTIME_END); + return status->flag & (RX_FLAG_MACTIME_START | RX_FLAG_MACTIME_END); +} +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset); int ieee80211_hw_config(struct ieee80211_local *local, u32 changed); void ieee80211_tx_set_protected(struct ieee80211_tx_data *tx); void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata, @@ -1365,6 +1381,9 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata, int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up); void ieee80211_sdata_stop(struct ieee80211_sub_if_data *sdata); +bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); +void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata); + static inline bool ieee80211_sdata_running(struct ieee80211_sub_if_data *sdata) { return test_bit(SDATA_STATE_RUNNING, &sdata->state); @@ -1496,7 +1515,7 @@ static inline void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata, } __ieee80211_tx_skb_tid_band(sdata, skb, tid, - chanctx_conf->channel->band); + chanctx_conf->def.chan->band); rcu_read_unlock(); } @@ -1585,8 +1604,7 @@ size_t ieee80211_ie_split_vendor(const u8 *ies, size_t ielen, size_t offset); u8 *ieee80211_ie_build_ht_cap(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, u16 cap); u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, u16 prot_mode); u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, u32 cap); @@ -1598,13 +1616,13 @@ int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, enum ieee80211_band band); /* channel management */ -enum nl80211_channel_type -ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper); +void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, + struct ieee80211_ht_operation *ht_oper, + struct cfg80211_chan_def *chandef); int __must_check ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, enum ieee80211_chanctx_mode mode); void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bc3e3e1db09..5331662489f 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -42,6 +42,41 @@ * by either the RTNL, the iflist_mtx or RCU. */ +bool __ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) +{ + struct ieee80211_chanctx_conf *chanctx_conf; + int power; + + rcu_read_lock(); + chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); + if (!chanctx_conf) { + rcu_read_unlock(); + return false; + } + + power = chanctx_conf->def.chan->max_power; + rcu_read_unlock(); + + if (sdata->user_power_level != IEEE80211_UNSET_POWER_LEVEL) + power = min(power, sdata->user_power_level); + + if (sdata->ap_power_level != IEEE80211_UNSET_POWER_LEVEL) + power = min(power, sdata->ap_power_level); + + if (power != sdata->vif.bss_conf.txpower) { + sdata->vif.bss_conf.txpower = power; + ieee80211_hw_config(sdata->local, 0); + return true; + } + + return false; +} + +void ieee80211_recalc_txpower(struct ieee80211_sub_if_data *sdata) +{ + if (__ieee80211_recalc_txpower(sdata)) + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_TXPOWER); +} static u32 ieee80211_idle_off(struct ieee80211_local *local, const char *reason) @@ -380,8 +415,7 @@ static int ieee80211_add_virtual_monitor(struct ieee80211_local *local) goto out_unlock; } - ret = ieee80211_vif_use_channel(sdata, local->monitor_channel, - local->monitor_channel_type, + ret = ieee80211_vif_use_channel(sdata, &local->monitor_chandef, IEEE80211_CHANCTX_EXCLUSIVE); if (ret) { drv_remove_interface(local, sdata); @@ -744,31 +778,12 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, /* APs need special treatment */ if (sdata->vif.type == NL80211_IFTYPE_AP) { struct ieee80211_sub_if_data *vlan, *tmpsdata; - struct beacon_data *old_beacon = - rtnl_dereference(sdata->u.ap.beacon); - struct probe_resp *old_probe_resp = - rtnl_dereference(sdata->u.ap.probe_resp); - - /* sdata_running will return false, so this will disable */ - ieee80211_bss_info_change_notify(sdata, - BSS_CHANGED_BEACON_ENABLED); - - /* remove beacon and probe response */ - RCU_INIT_POINTER(sdata->u.ap.beacon, NULL); - RCU_INIT_POINTER(sdata->u.ap.probe_resp, NULL); - synchronize_rcu(); - kfree(old_beacon); - kfree(old_probe_resp); /* down all dependent devices, that is VLANs */ list_for_each_entry_safe(vlan, tmpsdata, &sdata->u.ap.vlans, u.vlan.list) dev_close(vlan->dev); WARN_ON(!list_empty(&sdata->u.ap.vlans)); - - /* free all potentially still buffered bcast frames */ - local->total_ps_buffered -= skb_queue_len(&sdata->u.ap.ps.bc_buf); - skb_queue_purge(&sdata->u.ap.ps.bc_buf); } else if (sdata->vif.type == NL80211_IFTYPE_STATION) { ieee80211_mgd_stop(sdata); } @@ -1529,6 +1544,9 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_set_default_queues(sdata); + sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; + sdata->user_power_level = local->user_power_level; + /* setup type-dependent data */ ieee80211_setup_sdata(sdata, type); diff --git a/net/mac80211/key.c b/net/mac80211/key.c index d27e61aaa71..619c5d69799 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -339,7 +339,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, key->conf.iv_len = TKIP_IV_LEN; key->conf.icv_len = TKIP_ICV_LEN; if (seq) { - for (i = 0; i < NUM_RX_DATA_QUEUES; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { key->u.tkip.rx[i].iv32 = get_unaligned_le32(&seq[2]); key->u.tkip.rx[i].iv16 = @@ -352,7 +352,7 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, key->conf.iv_len = CCMP_HDR_LEN; key->conf.icv_len = CCMP_MIC_LEN; if (seq) { - for (i = 0; i < NUM_RX_DATA_QUEUES + 1; i++) + for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) for (j = 0; j < CCMP_PN_LEN; j++) key->u.ccmp.rx_pn[i][j] = seq[CCMP_PN_LEN - j - 1]; @@ -372,8 +372,9 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len, key->conf.iv_len = 0; key->conf.icv_len = sizeof(struct ieee80211_mmie); if (seq) - for (j = 0; j < 6; j++) - key->u.aes_cmac.rx_pn[j] = seq[6 - j - 1]; + for (j = 0; j < CMAC_PN_LEN; j++) + key->u.aes_cmac.rx_pn[j] = + seq[CMAC_PN_LEN - j - 1]; /* * Initialize AES key state here as an optimization so that * it does not need to be initialized for every packet. @@ -654,16 +655,16 @@ void ieee80211_get_key_rx_seq(struct ieee80211_key_conf *keyconf, switch (key->conf.cipher) { case WLAN_CIPHER_SUITE_TKIP: - if (WARN_ON(tid < 0 || tid >= NUM_RX_DATA_QUEUES)) + if (WARN_ON(tid < 0 || tid >= IEEE80211_NUM_TIDS)) return; seq->tkip.iv32 = key->u.tkip.rx[tid].iv32; seq->tkip.iv16 = key->u.tkip.rx[tid].iv16; break; case WLAN_CIPHER_SUITE_CCMP: - if (WARN_ON(tid < -1 || tid >= NUM_RX_DATA_QUEUES)) + if (WARN_ON(tid < -1 || tid >= IEEE80211_NUM_TIDS)) return; if (tid < 0) - pn = key->u.ccmp.rx_pn[NUM_RX_DATA_QUEUES]; + pn = key->u.ccmp.rx_pn[IEEE80211_NUM_TIDS]; else pn = key->u.ccmp.rx_pn[tid]; memcpy(seq->ccmp.pn, pn, CCMP_PN_LEN); diff --git a/net/mac80211/key.h b/net/mac80211/key.h index 7d4e31f037d..7cff0d3a519 100644 --- a/net/mac80211/key.h +++ b/net/mac80211/key.h @@ -30,8 +30,6 @@ #define TKIP_ICV_LEN 4 #define CMAC_PN_LEN 6 -#define NUM_RX_DATA_QUEUES 16 - struct ieee80211_local; struct ieee80211_sub_if_data; struct sta_info; @@ -82,17 +80,17 @@ struct ieee80211_key { struct tkip_ctx tx; /* last received RSC */ - struct tkip_ctx rx[NUM_RX_DATA_QUEUES]; + struct tkip_ctx rx[IEEE80211_NUM_TIDS]; } tkip; struct { atomic64_t tx_pn; /* * Last received packet number. The first - * NUM_RX_DATA_QUEUES counters are used with Data + * IEEE80211_NUM_TIDS counters are used with Data * frames and the last counter is used with Robust * Management frames. */ - u8 rx_pn[NUM_RX_DATA_QUEUES + 1][CCMP_PN_LEN]; + u8 rx_pn[IEEE80211_NUM_TIDS + 1][CCMP_PN_LEN]; struct crypto_cipher *tfm; u32 replays; /* dot11RSNAStatsCCMPReplays */ } ccmp; diff --git a/net/mac80211/main.c b/net/mac80211/main.c index d6e43b08d62..f5e4c1f24bf 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c @@ -95,11 +95,13 @@ static void ieee80211_reconfig_filter(struct work_struct *work) static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) { + struct ieee80211_sub_if_data *sdata; struct ieee80211_channel *chan; u32 changed = 0; int power; enum nl80211_channel_type channel_type; u32 offchannel_flag; + bool scanning = false; offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; if (local->scan_channel) { @@ -113,7 +115,7 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) channel_type = NL80211_CHAN_NO_HT; } else if (local->tmp_channel) { chan = local->tmp_channel; - channel_type = local->tmp_channel_type; + channel_type = NL80211_CHAN_NO_HT; } else { chan = local->_oper_channel; channel_type = local->_oper_channel_type; @@ -146,16 +148,18 @@ static u32 ieee80211_hw_conf_chan(struct ieee80211_local *local) changed |= IEEE80211_CONF_CHANGE_SMPS; } - if (test_bit(SCAN_SW_SCANNING, &local->scanning) || - test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || - test_bit(SCAN_HW_SCANNING, &local->scanning) || - !local->ap_power_level) - power = chan->max_power; - else - power = min(chan->max_power, local->ap_power_level); + scanning = test_bit(SCAN_SW_SCANNING, &local->scanning) || + test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning) || + test_bit(SCAN_HW_SCANNING, &local->scanning); + power = chan->max_power; - if (local->user_power_level >= 0) - power = min(power, local->user_power_level); + rcu_read_lock(); + list_for_each_entry_rcu(sdata, &local->interfaces, list) { + if (!rcu_access_pointer(sdata->vif.chanctx_conf)) + continue; + power = min(power, sdata->vif.bss_conf.txpower); + } + rcu_read_unlock(); if (local->hw.conf.power_level != power) { changed |= IEEE80211_CONF_CHANGE_POWER; @@ -600,7 +604,8 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, wiphy->features |= NL80211_FEATURE_SK_TX_STATUS | NL80211_FEATURE_SAE | - NL80211_FEATURE_HT_IBSS; + NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_VIF_TXPOWER; if (!ops->hw_scan) wiphy->features |= NL80211_FEATURE_LOW_PRIORITY_SCAN | @@ -633,7 +638,7 @@ struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len, local->hw.radiotap_mcs_details = IEEE80211_RADIOTAP_MCS_HAVE_MCS | IEEE80211_RADIOTAP_MCS_HAVE_GI | IEEE80211_RADIOTAP_MCS_HAVE_BW; - local->user_power_level = -1; + local->user_power_level = IEEE80211_UNSET_POWER_LEVEL; wiphy->ht_capa_mod_mask = &mac80211_ht_capa_mod_mask; INIT_LIST_HEAD(&local->interfaces); @@ -793,10 +798,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) local->_oper_channel = &sband->channels[0]; local->hw.conf.channel_type = NL80211_CHAN_NO_HT; } - if (!local->monitor_channel) { - local->monitor_channel = &sband->channels[0]; - local->monitor_channel_type = NL80211_CHAN_NO_HT; - } + cfg80211_chandef_create(&local->monitor_chandef, + &sband->channels[0], + NL80211_CHAN_NO_HT); channels += sband->n_channels; if (max_bitrates < sband->n_bitrates) @@ -879,10 +883,22 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) if (supp_ht) local->scan_ies_len += 2 + sizeof(struct ieee80211_ht_cap); - if (supp_vht) + if (supp_vht) { local->scan_ies_len += 2 + sizeof(struct ieee80211_vht_cap); + /* + * (for now at least), drivers wanting to use VHT must + * support channel contexts, as they contain all the + * necessary VHT information and the global hw config + * doesn't (yet) + */ + if (WARN_ON(!local->use_chanctx)) { + result = -EINVAL; + goto fail_wiphy_register; + } + } + if (!local->ops->hw_scan) { /* For hw_scan, driver needs to set these up. */ local->hw.wiphy->max_scan_ssids = 4; diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c index a350cab4b33..1bf03f9ff3b 100644 --- a/net/mac80211/mesh.c +++ b/net/mac80211/mesh.c @@ -76,7 +76,7 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh; struct ieee80211_local *local = sdata->local; u32 basic_rates = 0; - enum nl80211_channel_type sta_channel_type = NL80211_CHAN_NO_HT; + struct cfg80211_chan_def sta_chan_def; /* * As support for each feature is added, check for matching @@ -103,17 +103,11 @@ bool mesh_matches_local(struct ieee80211_sub_if_data *sdata, if (sdata->vif.bss_conf.basic_rates != basic_rates) goto mismatch; - if (ie->ht_operation) - sta_channel_type = - ieee80211_ht_oper_to_channel_type(ie->ht_operation); - - /* Disallow HT40+/- mismatch */ - if (ie->ht_operation && - (sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40MINUS || - sdata->vif.bss_conf.channel_type == NL80211_CHAN_HT40PLUS) && - (sta_channel_type == NL80211_CHAN_HT40MINUS || - sta_channel_type == NL80211_CHAN_HT40PLUS) && - sdata->vif.bss_conf.channel_type != sta_channel_type) + ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, + ie->ht_operation, &sta_chan_def); + + if (!cfg80211_chandef_compatible(&sdata->vif.bss_conf.chandef, + &sta_chan_def)) goto mismatch; return true; @@ -129,7 +123,7 @@ mismatch: bool mesh_peer_accepts_plinks(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & - MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; + IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS) != 0; } /** @@ -269,11 +263,11 @@ mesh_add_meshconf_ie(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata) neighbors = (neighbors > 15) ? 15 : neighbors; *pos++ = neighbors << 1; /* Mesh capability */ - *pos = MESHCONF_CAPAB_FORWARDING; + *pos = IEEE80211_MESHCONF_CAPAB_FORWARDING; *pos |= ifmsh->accepting_plinks ? - MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; + IEEE80211_MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00; *pos++ |= ifmsh->adjusting_tbtt ? - MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING : 0x00; *pos++ = 0x00; return 0; @@ -368,7 +362,7 @@ int mesh_add_ds_params_ie(struct sk_buff *skb, rcu_read_unlock(); return -EINVAL; } - chan = chanctx_conf->channel; + chan = chanctx_conf->def.chan; rcu_read_unlock(); sband = local->hw.wiphy->bands[chan->band]; @@ -392,7 +386,7 @@ int mesh_add_ht_cap_ie(struct sk_buff *skb, sband = local->hw.wiphy->bands[band]; if (!sband->ht_cap.ht_supported || - sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) + sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; if (skb_tailroom(skb) < 2 + sizeof(struct ieee80211_ht_cap)) @@ -411,7 +405,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, struct ieee80211_chanctx_conf *chanctx_conf; struct ieee80211_channel *channel; enum nl80211_channel_type channel_type = - sdata->vif.bss_conf.channel_type; + cfg80211_get_chandef_type(&sdata->vif.bss_conf.chandef); struct ieee80211_supported_band *sband; struct ieee80211_sta_ht_cap *ht_cap; u8 *pos; @@ -422,7 +416,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, rcu_read_unlock(); return -EINVAL; } - channel = chanctx_conf->channel; + channel = chanctx_conf->def.chan; rcu_read_unlock(); sband = local->hw.wiphy->bands[channel->band]; @@ -435,7 +429,7 @@ int mesh_add_ht_oper_ie(struct sk_buff *skb, return -ENOMEM; pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation)); - ieee80211_ie_build_ht_oper(pos, ht_cap, channel, channel_type, + ieee80211_ie_build_ht_oper(pos, ht_cap, &sdata->vif.bss_conf.chandef, sdata->vif.bss_conf.ht_operation_mode); return 0; diff --git a/net/mac80211/mesh.h b/net/mac80211/mesh.h index 9285f3f67e6..7c9215fb2ac 100644 --- a/net/mac80211/mesh.h +++ b/net/mac80211/mesh.h @@ -19,20 +19,6 @@ /* Data structures */ /** - * enum mesh_config_capab_flags - mesh config IE capability flags - * - * @MESHCONF_CAPAB_ACCEPT_PLINKS: STA is willing to establish - * additional mesh peerings with other mesh STAs - * @MESHCONF_CAPAB_FORWARDING: the STA forwards MSDUs - * @MESHCONF_CAPAB_TBTT_ADJUSTING: TBTT adjustment procedure is ongoing - */ -enum mesh_config_capab_flags { - MESHCONF_CAPAB_ACCEPT_PLINKS = BIT(0), - MESHCONF_CAPAB_FORWARDING = BIT(3), - MESHCONF_CAPAB_TBTT_ADJUSTING = BIT(5), -}; - -/** * enum mesh_path_flags - mac80211 mesh path flags * * diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 234fe755968..ca52dfdd537 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c @@ -19,12 +19,6 @@ #define mod_plink_timer(s, t) (mod_timer(&s->plink_timer, \ jiffies + HZ * t / 1000)) -#define dot11MeshMaxRetries(s) (s->u.mesh.mshcfg.dot11MeshMaxRetries) -#define dot11MeshRetryTimeout(s) (s->u.mesh.mshcfg.dot11MeshRetryTimeout) -#define dot11MeshConfirmTimeout(s) (s->u.mesh.mshcfg.dot11MeshConfirmTimeout) -#define dot11MeshHoldingTimeout(s) (s->u.mesh.mshcfg.dot11MeshHoldingTimeout) -#define dot11MeshMaxPeerLinks(s) (s->u.mesh.mshcfg.dot11MeshMaxPeerLinks) - /* We only need a valid sta if user configured a minimum rssi_threshold. */ #define rssi_threshold_check(sta, sdata) \ (sdata->u.mesh.mshcfg.rssi_threshold == 0 ||\ @@ -117,7 +111,7 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) u16 ht_opmode; bool non_ht_sta = false, ht20_sta = false; - if (sdata->vif.bss_conf.channel_type == NL80211_CHAN_NO_HT) + if (sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) return 0; rcu_read_lock(); @@ -126,14 +120,14 @@ static u32 mesh_set_ht_prot_mode(struct ieee80211_sub_if_data *sdata) sta->plink_state != NL80211_PLINK_ESTAB) continue; - switch (sta->ch_type) { - case NL80211_CHAN_NO_HT: + switch (sta->ch_width) { + case NL80211_CHAN_WIDTH_20_NOHT: mpl_dbg(sdata, "mesh_plink %pM: nonHT sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); non_ht_sta = true; goto out; - case NL80211_CHAN_HT20: + case NL80211_CHAN_WIDTH_20: mpl_dbg(sdata, "mesh_plink %pM: HT20 sta (%pM) is present\n", sdata->vif.addr, sta->sta.addr); @@ -148,7 +142,7 @@ out: if (non_ht_sta) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED; else if (ht20_sta && - sdata->vif.bss_conf.channel_type > NL80211_CHAN_HT20) + sdata->vif.bss_conf.chandef.width > NL80211_CHAN_WIDTH_20) ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_20MHZ; else ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONE; @@ -378,7 +372,7 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, sta->sta.supp_rates[band] = rates; if (elems->ht_cap_elem && - sdata->vif.bss_conf.channel_type != NL80211_CHAN_NO_HT) + sdata->vif.bss_conf.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems->ht_cap_elem, &sta->sta.ht_cap); @@ -386,12 +380,15 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); if (elems->ht_operation) { + struct cfg80211_chan_def chandef; + if (!(elems->ht_operation->ht_param & IEEE80211_HT_PARAM_CHAN_WIDTH_ANY)) sta->sta.ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; - sta->ch_type = - ieee80211_ht_oper_to_channel_type(elems->ht_operation); + ieee80211_ht_oper_to_chandef(sdata->vif.bss_conf.chandef.chan, + elems->ht_operation, &chandef); + sta->ch_width = chandef.width; } rate_control_rate_init(sta); @@ -430,6 +427,7 @@ static void mesh_plink_timer(unsigned long data) struct sta_info *sta; __le16 llid, plid, reason; struct ieee80211_sub_if_data *sdata; + struct mesh_config *mshcfg; /* * This STA is valid because sta_info_destroy() will @@ -456,12 +454,13 @@ static void mesh_plink_timer(unsigned long data) llid = sta->llid; plid = sta->plid; sdata = sta->sdata; + mshcfg = &sdata->u.mesh.mshcfg; switch (sta->plink_state) { case NL80211_PLINK_OPN_RCVD: case NL80211_PLINK_OPN_SNT: /* retry timer */ - if (sta->plink_retries < dot11MeshMaxRetries(sdata)) { + if (sta->plink_retries < mshcfg->dot11MeshMaxRetries) { u32 rand; mpl_dbg(sta->sdata, "Mesh plink for %pM (retry, timeout): %d %d\n", @@ -484,7 +483,7 @@ static void mesh_plink_timer(unsigned long data) if (!reason) reason = cpu_to_le16(WLAN_REASON_MESH_CONFIRM_TIMEOUT); sta->plink_state = NL80211_PLINK_HOLDING; - mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); + mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, sta->sta.addr, llid, plid, reason); @@ -543,7 +542,7 @@ int mesh_plink_open(struct sta_info *sta) return -EBUSY; } sta->plink_state = NL80211_PLINK_OPN_SNT; - mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); + mesh_plink_timer_set(sta, sdata->u.mesh.mshcfg.dot11MeshRetryTimeout); spin_unlock_bh(&sta->lock); mpl_dbg(sdata, "Mesh plink: starting establishment with %pM\n", @@ -570,6 +569,7 @@ void mesh_plink_block(struct sta_info *sta) void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt, size_t len, struct ieee80211_rx_status *rx_status) { + struct mesh_config *mshcfg = &sdata->u.mesh.mshcfg; struct ieee802_11_elems elems; struct sta_info *sta; enum plink_event event; @@ -777,7 +777,8 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->plid = plid; get_random_bytes(&llid, 2); sta->llid = llid; - mesh_plink_timer_set(sta, dot11MeshRetryTimeout(sdata)); + mesh_plink_timer_set(sta, + mshcfg->dot11MeshRetryTimeout); spin_unlock_bh(&sta->lock); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_OPEN, @@ -803,7 +804,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, - dot11MeshHoldingTimeout(sdata))) + mshcfg->dot11MeshHoldingTimeout)) sta->ignore_plink_timer = true; llid = sta->llid; @@ -825,7 +826,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m case CNF_ACPT: sta->plink_state = NL80211_PLINK_CNF_RCVD; if (!mod_plink_timer(sta, - dot11MeshConfirmTimeout(sdata))) + mshcfg->dot11MeshConfirmTimeout)) sta->ignore_plink_timer = true; spin_unlock_bh(&sta->lock); @@ -847,7 +848,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, - dot11MeshHoldingTimeout(sdata))) + mshcfg->dot11MeshHoldingTimeout)) sta->ignore_plink_timer = true; llid = sta->llid; @@ -888,7 +889,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m sta->reason = reason; sta->plink_state = NL80211_PLINK_HOLDING; if (!mod_plink_timer(sta, - dot11MeshHoldingTimeout(sdata))) + mshcfg->dot11MeshHoldingTimeout)) sta->ignore_plink_timer = true; llid = sta->llid; @@ -923,7 +924,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m changed |= __mesh_plink_deactivate(sta); sta->plink_state = NL80211_PLINK_HOLDING; llid = sta->llid; - mod_plink_timer(sta, dot11MeshHoldingTimeout(sdata)); + mod_plink_timer(sta, mshcfg->dot11MeshHoldingTimeout); spin_unlock_bh(&sta->lock); changed |= mesh_set_ht_prot_mode(sdata); mesh_plink_frame_tx(sdata, WLAN_SP_MESH_PEERING_CLOSE, diff --git a/net/mac80211/mesh_sync.c b/net/mac80211/mesh_sync.c index 407c8705e10..0f40086cce1 100644 --- a/net/mac80211/mesh_sync.c +++ b/net/mac80211/mesh_sync.c @@ -43,7 +43,7 @@ struct sync_method { static bool mesh_peer_tbtt_adjusting(struct ieee802_11_elems *ie) { return (ie->mesh_config->meshconf_cap & - MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; + IEEE80211_MESHCONF_CAPAB_TBTT_ADJUSTING) != 0; } void mesh_sync_adjust_tbtt(struct ieee80211_sub_if_data *sdata) @@ -116,43 +116,13 @@ static void mesh_sync_offset_rx_bcn_presp(struct ieee80211_sub_if_data *sdata, goto no_sync; } - if (rx_status->flag & RX_FLAG_MACTIME_MPDU && rx_status->mactime) { - /* - * The mactime is defined as the time the first data symbol - * of the frame hits the PHY, and the timestamp of the beacon - * is defined as "the time that the data symbol containing the - * first bit of the timestamp is transmitted to the PHY plus - * the transmitting STA's delays through its local PHY from the - * MAC-PHY interface to its interface with the WM" (802.11 - * 11.1.2) - * - * T_r, in 13.13.2.2.2, is just defined as "the frame reception - * time" but we unless we interpret that time to be the same - * time of the beacon timestamp, the offset calculation will be - * off. Below we adjust t_r to be "the time at which the first - * symbol of the timestamp element in the beacon is received". - * This correction depends on the rate. - * - * Based on similar code in ibss.c - */ - int rate; - - if (rx_status->flag & RX_FLAG_HT) { - /* TODO: - * In principle there could be HT-beacons (Dual Beacon - * HT Operation options), but for now ignore them and - * just use the primary (i.e. non-HT) beacons for - * synchronization. - * */ - goto no_sync; - } else - rate = local->hw.wiphy->bands[rx_status->band]-> - bitrates[rx_status->rate_idx].bitrate; - - /* 24 bytes of header * 8 bits/byte * - * 10*(100 Kbps)/Mbps / rate (100 Kbps)*/ - t_r = rx_status->mactime + (24 * 8 * 10 / rate); - } + if (ieee80211_have_rx_timestamp(rx_status)) + /* time when timestamp field was received */ + t_r = ieee80211_calculate_rx_timestamp(local, rx_status, + 24 + 12 + + elems->total_len + + FCS_LEN, + 24); /* Timing offset calculation (see 13.13.2.2.2) */ t_t = le64_to_cpu(mgmt->u.beacon.timestamp); diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 1d1fdf0791f..d2a4f78b4b0 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -191,17 +191,19 @@ static u32 ieee80211_config_ht_tx(struct ieee80211_sub_if_data *sdata, rcu_read_unlock(); return 0; } - chan = chanctx_conf->channel; + chan = chanctx_conf->def.chan; rcu_read_unlock(); sband = local->hw.wiphy->bands[chan->band]; - switch (sdata->vif.bss_conf.channel_type) { - case NL80211_CHAN_HT40PLUS: - if (chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + switch (sdata->vif.bss_conf.chandef.width) { + case NL80211_CHAN_WIDTH_40: + if (sdata->vif.bss_conf.chandef.chan->center_freq > + sdata->vif.bss_conf.chandef.center_freq1 && + chan->flags & IEEE80211_CHAN_NO_HT40PLUS) disable_40 = true; - break; - case NL80211_CHAN_HT40MINUS: - if (chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + if (sdata->vif.bss_conf.chandef.chan->center_freq < + sdata->vif.bss_conf.chandef.center_freq1 && + chan->flags & IEEE80211_CHAN_NO_HT40MINUS) disable_40 = true; break; default: @@ -381,7 +383,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) rcu_read_unlock(); return; } - chan = chanctx_conf->channel; + chan = chanctx_conf->def.chan; rcu_read_unlock(); sband = local->hw.wiphy->bands[chan->band]; @@ -541,7 +543,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) offset = noffset; } - if (!(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) + if (!(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) ieee80211_add_ht_ie(sdata, skb, assoc_data->ap_ht_param, sband, chan, sdata->smps_mode); @@ -820,10 +822,10 @@ void ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, cbss->beacon_interval)); } -static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, - struct ieee80211_channel *channel, - const u8 *country_ie, u8 country_ie_len, - const u8 *pwr_constr_elem) +static u32 ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *channel, + const u8 *country_ie, u8 country_ie_len, + const u8 *pwr_constr_elem) { struct ieee80211_country_ie_triplet *triplet; int chan = ieee80211_frequency_to_channel(channel->center_freq); @@ -832,7 +834,7 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, /* Invalid IE */ if (country_ie_len % 2 || country_ie_len < IEEE80211_COUNTRY_IE_MIN_LEN) - return; + return 0; triplet = (void *)(country_ie + 3); country_ie_len -= 3; @@ -873,19 +875,21 @@ static void ieee80211_handle_pwr_constr(struct ieee80211_sub_if_data *sdata, } if (!have_chan_pwr) - return; + return 0; new_ap_level = max_t(int, 0, chan_pwr - *pwr_constr_elem); - if (sdata->local->ap_power_level == new_ap_level) - return; + if (sdata->ap_power_level == new_ap_level) + return 0; sdata_info(sdata, "Limiting TX power to %d (%d - %d) dBm as advertised by %pM\n", new_ap_level, chan_pwr, *pwr_constr_elem, sdata->u.mgd.bssid); - sdata->local->ap_power_level = new_ap_level; - ieee80211_hw_config(sdata->local, 0); + sdata->ap_power_level = new_ap_level; + if (__ieee80211_recalc_txpower(sdata)) + return BSS_CHANGED_TXPOWER; + return 0; } void ieee80211_enable_dyn_ps(struct ieee80211_vif *vif) @@ -1363,6 +1367,22 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata, sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE; + if (sdata->vif.p2p) { + u8 noa[2]; + int ret; + + ret = cfg80211_get_p2p_attr(cbss->information_elements, + cbss->len_information_elements, + IEEE80211_P2P_ATTR_ABSENCE_NOTICE, + noa, sizeof(noa)); + if (ret >= 2) { + bss_conf->p2p_oppps = noa[1] & 0x80; + bss_conf->p2p_ctwindow = noa[1] & 0x7f; + bss_info_changed |= BSS_CHANGED_P2P_PS; + sdata->u.mgd.p2p_noa_index = noa[0]; + } + } + /* just to be sure */ ieee80211_stop_poll(sdata); @@ -1485,11 +1505,14 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_ASSOC; sdata->vif.bss_conf.assoc = false; + sdata->vif.bss_conf.p2p_ctwindow = 0; + sdata->vif.bss_conf.p2p_oppps = false; + /* on the next assoc, re-program HT parameters */ memset(&ifmgd->ht_capa, 0, sizeof(ifmgd->ht_capa)); memset(&ifmgd->ht_capa_mask, 0, sizeof(ifmgd->ht_capa_mask)); - local->ap_power_level = 0; + sdata->ap_power_level = IEEE80211_UNSET_POWER_LEVEL; del_timer_sync(&local->dynamic_ps_timer); cancel_work_sync(&local->dynamic_ps_enable_work); @@ -1507,8 +1530,6 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_BSSID | BSS_CHANGED_HT; ieee80211_bss_info_change_notify(sdata, changed); - ieee80211_vif_release_channel(sdata); - /* disassociated - set to defaults now */ ieee80211_set_wmm_default(sdata, false); @@ -1518,6 +1539,9 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, del_timer_sync(&sdata->u.mgd.chswitch_timer); sdata->u.mgd.timers_running = 0; + + ifmgd->flags = 0; + ieee80211_vif_release_channel(sdata); } void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata, @@ -1843,6 +1867,7 @@ static void ieee80211_destroy_auth_data(struct ieee80211_sub_if_data *sdata, memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + sdata->u.mgd.flags = 0; ieee80211_vif_release_channel(sdata); } @@ -2085,6 +2110,7 @@ static void ieee80211_destroy_assoc_data(struct ieee80211_sub_if_data *sdata, memset(sdata->u.mgd.bssid, 0, ETH_ALEN); ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BSSID); + sdata->u.mgd.flags = 0; ieee80211_vif_release_channel(sdata); } @@ -2149,7 +2175,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[ieee80211_get_sdata_band(sdata)]; - if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) + if (elems.ht_cap_elem && !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, elems.ht_cap_elem, &sta->sta.ht_cap); @@ -2201,7 +2227,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata, changed |= BSS_CHANGED_QOS; if (elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) + !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, cbss->bssid, false); @@ -2452,11 +2478,11 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, return; } - if (rx_status->freq != chanctx_conf->channel->center_freq) { + if (rx_status->freq != chanctx_conf->def.chan->center_freq) { rcu_read_unlock(); return; } - chan = chanctx_conf->channel; + chan = chanctx_conf->def.chan; rcu_read_unlock(); if (ifmgd->assoc_data && !ifmgd->assoc_data->have_beacon && @@ -2592,6 +2618,27 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, } } + if (sdata->vif.p2p) { + u8 noa[2]; + int ret; + + ret = cfg80211_get_p2p_attr(mgmt->u.beacon.variable, + len - baselen, + IEEE80211_P2P_ATTR_ABSENCE_NOTICE, + noa, sizeof(noa)); + if (ret >= 2 && sdata->u.mgd.p2p_noa_index != noa[0]) { + bss_conf->p2p_oppps = noa[1] & 0x80; + bss_conf->p2p_ctwindow = noa[1] & 0x7f; + changed |= BSS_CHANGED_P2P_PS; + sdata->u.mgd.p2p_noa_index = noa[0]; + /* + * make sure we update all information, the CRC + * mechanism doesn't look at P2P attributes. + */ + ifmgd->beacon_crc_valid = false; + } + } + if (ncrc == ifmgd->beacon_crc && ifmgd->beacon_crc_valid) return; ifmgd->beacon_crc = ncrc; @@ -2616,17 +2663,17 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata, if (elems.ht_cap_elem && elems.ht_operation && elems.wmm_param && - !(ifmgd->flags & IEEE80211_STA_DISABLE_11N)) + !(ifmgd->flags & IEEE80211_STA_DISABLE_HT)) changed |= ieee80211_config_ht_tx(sdata, elems.ht_operation, bssid, true); if (elems.country_elem && elems.pwr_constr_elem && mgmt->u.probe_resp.capab_info & cpu_to_le16(WLAN_CAPABILITY_SPECTRUM_MGMT)) - ieee80211_handle_pwr_constr(sdata, chan, - elems.country_elem, - elems.country_elem_len, - elems.pwr_constr_elem); + changed |= ieee80211_handle_pwr_constr(sdata, chan, + elems.country_elem, + elems.country_elem_len, + elems.pwr_constr_elem); ieee80211_bss_info_change_notify(sdata, changed); } @@ -3146,6 +3193,7 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, const u8 *ht_oper_ie; const struct ieee80211_ht_operation *ht_oper = NULL; struct ieee80211_supported_band *sband; + struct cfg80211_chan_def chandef; sband = local->hw.wiphy->bands[cbss->channel->band]; @@ -3177,12 +3225,10 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, ht_cfreq, ht_oper->primary_chan, cbss->channel->band); ht_oper = NULL; - } else { - channel_type = NL80211_CHAN_HT20; } } - if (ht_oper && sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + if (ht_oper) { /* * cfg80211 already verified that the channel itself can * be used, but it didn't check that we can do the right @@ -3195,19 +3241,26 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, channel_type = NL80211_CHAN_HT20; - switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { - case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: - if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40PLUS) - ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; - else - channel_type = NL80211_CHAN_HT40PLUS; - break; - case IEEE80211_HT_PARAM_CHA_SEC_BELOW: - if (cbss->channel->flags & IEEE80211_CHAN_NO_HT40MINUS) - ifmgd->flags |= IEEE80211_STA_DISABLE_40MHZ; - else - channel_type = NL80211_CHAN_HT40MINUS; - break; + if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + switch (ht_oper->ht_param & + IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { + case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: + if (cbss->channel->flags & + IEEE80211_CHAN_NO_HT40PLUS) + ifmgd->flags |= + IEEE80211_STA_DISABLE_40MHZ; + else + channel_type = NL80211_CHAN_HT40PLUS; + break; + case IEEE80211_HT_PARAM_CHA_SEC_BELOW: + if (cbss->channel->flags & + IEEE80211_CHAN_NO_HT40MINUS) + ifmgd->flags |= + IEEE80211_STA_DISABLE_40MHZ; + else + channel_type = NL80211_CHAN_HT40MINUS; + break; + } } ht_cap_ie = cfg80211_find_ie(WLAN_EID_HT_CAPABILITY, @@ -3220,13 +3273,15 @@ static int ieee80211_prep_channel(struct ieee80211_sub_if_data *sdata, sdata->needed_rx_chains = min(chains, local->rx_chains); } else { sdata->needed_rx_chains = 1; + sdata->u.mgd.flags |= IEEE80211_STA_DISABLE_HT; } /* will change later if needed */ sdata->smps_mode = IEEE80211_SMPS_OFF; ieee80211_vif_release_channel(sdata); - return ieee80211_vif_use_channel(sdata, cbss->channel, channel_type, + cfg80211_chandef_create(&chandef, cbss->channel, channel_type); + return ieee80211_vif_use_channel(sdata, &chandef, IEEE80211_CHANCTX_SHARED); } @@ -3488,13 +3543,6 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, /* prepare assoc data */ - /* - * keep only the 40 MHz disable bit set as it might have - * been set during authentication already, all other bits - * should be reset for a new connection - */ - ifmgd->flags &= IEEE80211_STA_DISABLE_40MHZ; - ifmgd->beacon_crc_valid = false; /* @@ -3508,7 +3556,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, if (req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP40 || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_TKIP || req->crypto.ciphers_pairwise[i] == WLAN_CIPHER_SUITE_WEP104) { - ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; netdev_info(sdata->dev, "disabling HT/VHT due to WEP/TKIP use\n"); @@ -3516,7 +3564,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, } if (req->flags & ASSOC_REQ_DISABLE_HT) { - ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; ifmgd->flags |= IEEE80211_STA_DISABLE_VHT; } @@ -3524,7 +3572,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, sband = local->hw.wiphy->bands[req->bss->channel->band]; if (!sband->ht_cap.ht_supported || local->hw.queues < IEEE80211_NUM_ACS || !bss->wmm_used) { - ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; if (!bss->wmm_used) netdev_info(sdata->dev, "disabling HT as WMM/QoS is not supported by the AP\n"); @@ -3569,7 +3617,7 @@ int ieee80211_mgd_assoc(struct ieee80211_sub_if_data *sdata, assoc_data->ap_ht_param = ((struct ieee80211_ht_operation *)(ht_ie + 2))->ht_param; else - ifmgd->flags |= IEEE80211_STA_DISABLE_11N; + ifmgd->flags |= IEEE80211_STA_DISABLE_HT; if (bss->wmm_used && bss->uapsd_supported && (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_UAPSD)) { @@ -3660,40 +3708,44 @@ int ieee80211_mgd_deauth(struct ieee80211_sub_if_data *sdata, struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; u8 frame_buf[IEEE80211_DEAUTH_FRAME_LEN]; bool tx = !req->local_state_change; + bool sent_frame = false; mutex_lock(&ifmgd->mtx); - if (ifmgd->auth_data) { - ieee80211_destroy_auth_data(sdata, false); - mutex_unlock(&ifmgd->mtx); - return 0; - } - sdata_info(sdata, "deauthenticating from %pM by local choice (reason=%d)\n", req->bssid, req->reason_code); - if (ifmgd->associated && - ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { - ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, - req->reason_code, tx, frame_buf); - } else { + if (ifmgd->auth_data) { drv_mgd_prepare_tx(sdata->local, sdata); ieee80211_send_deauth_disassoc(sdata, req->bssid, IEEE80211_STYPE_DEAUTH, req->reason_code, tx, frame_buf); + ieee80211_destroy_auth_data(sdata, false); + mutex_unlock(&ifmgd->mtx); + + sent_frame = tx; + goto out; } + if (ifmgd->associated && + ether_addr_equal(ifmgd->associated->bssid, req->bssid)) { + ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, + req->reason_code, tx, frame_buf); + sent_frame = tx; + } mutex_unlock(&ifmgd->mtx); - __cfg80211_send_deauth(sdata->dev, frame_buf, - IEEE80211_DEAUTH_FRAME_LEN); - + out: mutex_lock(&sdata->local->mtx); ieee80211_recalc_idle(sdata->local); mutex_unlock(&sdata->local->mtx); + if (sent_frame) + __cfg80211_send_deauth(sdata->dev, frame_buf, + IEEE80211_DEAUTH_FRAME_LEN); + return 0; } diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c index c349f3aaf59..5abddfe3e10 100644 --- a/net/mac80211/offchannel.c +++ b/net/mac80211/offchannel.c @@ -204,9 +204,9 @@ void ieee80211_handle_roc_started(struct ieee80211_roc_work *roc) roc->frame = NULL; } } else { - cfg80211_ready_on_channel(&roc->sdata->wdev, (unsigned long)roc, - roc->chan, roc->chan_type, - roc->req_duration, GFP_KERNEL); + cfg80211_ready_on_channel(&roc->sdata->wdev, roc->cookie, + roc->chan, roc->req_duration, + GFP_KERNEL); } roc->notified = true; @@ -283,8 +283,7 @@ void ieee80211_start_next_roc(struct ieee80211_local *local) if (!duration) duration = 10; - ret = drv_remain_on_channel(local, roc->chan, - roc->chan_type, + ret = drv_remain_on_channel(local, roc->sdata, roc->chan, duration); roc->started = true; @@ -320,8 +319,7 @@ void ieee80211_roc_notify_destroy(struct ieee80211_roc_work *roc) if (!roc->mgmt_tx_cookie) cfg80211_remain_on_channel_expired(&roc->sdata->wdev, - (unsigned long)roc, - roc->chan, roc->chan_type, + roc->cookie, roc->chan, GFP_KERNEL); list_for_each_entry_safe(dep, tmp, &roc->dependents, list) @@ -360,7 +358,6 @@ void ieee80211_sw_roc_work(struct work_struct *work) ieee80211_recalc_idle(local); local->tmp_channel = roc->chan; - local->tmp_channel_type = roc->chan_type; ieee80211_hw_config(local, 0); /* tell userspace or send frame */ diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 9f404ac901a..79a48f37d40 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -33,6 +33,7 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_sub_if_data *sdata; struct sta_info *sta; + struct ieee80211_chanctx *ctx; if (!local->open_count) goto suspend; @@ -135,14 +136,55 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); - /* the interface is leaving the channel and is removed */ - ieee80211_vif_release_channel(sdata); + if (sdata->vif.type == NL80211_IFTYPE_AP && + rcu_access_pointer(sdata->u.ap.beacon)) + drv_stop_ap(local, sdata); + + if (local->use_chanctx) { + struct ieee80211_chanctx_conf *conf; + + mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected( + sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (conf) { + ctx = container_of(conf, + struct ieee80211_chanctx, + conf); + drv_unassign_vif_chanctx(local, sdata, ctx); + } + + mutex_unlock(&local->chanctx_mtx); + } drv_remove_interface(local, sdata); } sdata = rtnl_dereference(local->monitor_sdata); - if (sdata) + if (sdata) { + if (local->use_chanctx) { + struct ieee80211_chanctx_conf *conf; + + mutex_lock(&local->chanctx_mtx); + conf = rcu_dereference_protected( + sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (conf) { + ctx = container_of(conf, + struct ieee80211_chanctx, + conf); + drv_unassign_vif_chanctx(local, sdata, ctx); + } + + mutex_unlock(&local->chanctx_mtx); + } + drv_remove_interface(local, sdata); + } + + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) + drv_remove_chanctx(local, ctx); + mutex_unlock(&local->chanctx_mtx); /* stop hardware - this must stop RX */ if (local->open_count) diff --git a/net/mac80211/rate.c b/net/mac80211/rate.c index 3313c117b32..dd88381c53b 100644 --- a/net/mac80211/rate.c +++ b/net/mac80211/rate.c @@ -391,7 +391,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, return; /* if HT BSS, and we handle a data frame, also try HT rates */ - if (txrc->bss_conf->channel_type == NL80211_CHAN_NO_HT) + if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_20_NOHT) return; fc = hdr->frame_control; @@ -408,8 +408,7 @@ static void rate_idx_match_mask(struct ieee80211_tx_rate *rate, alt_rate.flags |= IEEE80211_TX_RC_MCS; - if ((txrc->bss_conf->channel_type == NL80211_CHAN_HT40MINUS) || - (txrc->bss_conf->channel_type == NL80211_CHAN_HT40PLUS)) + if (txrc->bss_conf->chandef.width == NL80211_CHAN_WIDTH_40) alt_rate.flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; if (rate_idx_match_mcs_mask(&alt_rate, mcs_mask)) { diff --git a/net/mac80211/rate.h b/net/mac80211/rate.h index ec198ef6aa8..301386dabf8 100644 --- a/net/mac80211/rate.h +++ b/net/mac80211/rate.h @@ -65,7 +65,7 @@ static inline void rate_control_rate_init(struct sta_info *sta) return; } - sband = local->hw.wiphy->bands[chanctx_conf->channel->band]; + sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band]; rcu_read_unlock(); ref->ops->rate_init(ref->priv, sband, ista, priv_sta); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 8c1f1527d67..825f33cf7bb 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -40,6 +40,8 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, struct sk_buff *skb) { + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); + if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) { if (likely(skb->len > FCS_LEN)) __pskb_trim(skb, skb->len - FCS_LEN); @@ -51,20 +53,25 @@ static struct sk_buff *remove_monitor_info(struct ieee80211_local *local, } } + if (status->vendor_radiotap_len) + __pskb_pull(skb, status->vendor_radiotap_len); + return skb; } -static inline int should_drop_frame(struct sk_buff *skb, - int present_fcs_len) +static inline int should_drop_frame(struct sk_buff *skb, int present_fcs_len) { struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr; + + hdr = (void *)(skb->data + status->vendor_radiotap_len); if (status->flag & (RX_FLAG_FAILED_FCS_CRC | RX_FLAG_FAILED_PLCP_CRC | RX_FLAG_AMPDU_IS_ZEROLEN)) return 1; - if (unlikely(skb->len < 16 + present_fcs_len)) + if (unlikely(skb->len < 16 + present_fcs_len + + status->vendor_radiotap_len)) return 1; if (ieee80211_is_ctl(hdr->frame_control) && !ieee80211_is_pspoll(hdr->frame_control) && @@ -74,32 +81,48 @@ static inline int should_drop_frame(struct sk_buff *skb, } static int -ieee80211_rx_radiotap_len(struct ieee80211_local *local, - struct ieee80211_rx_status *status) +ieee80211_rx_radiotap_space(struct ieee80211_local *local, + struct ieee80211_rx_status *status) { int len; /* always present fields */ len = sizeof(struct ieee80211_radiotap_header) + 9; - if (status->flag & RX_FLAG_MACTIME_MPDU) + /* allocate extra bitmap */ + if (status->vendor_radiotap_len) + len += 4; + + if (ieee80211_have_rx_timestamp(status)) { + len = ALIGN(len, 8); len += 8; + } if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM) len += 1; - if (len & 1) /* padding for RX_FLAGS if necessary */ - len++; + /* padding for RX_FLAGS if necessary */ + len = ALIGN(len, 2); if (status->flag & RX_FLAG_HT) /* HT info */ len += 3; if (status->flag & RX_FLAG_AMPDU_DETAILS) { - /* padding */ - while (len & 3) - len++; + len = ALIGN(len, 4); len += 8; } + if (status->vendor_radiotap_len) { + if (WARN_ON_ONCE(status->vendor_radiotap_align == 0)) + status->vendor_radiotap_align = 1; + /* align standard part of vendor namespace */ + len = ALIGN(len, 2); + /* allocate standard part of vendor namespace */ + len += 6; + /* align vendor-defined part */ + len = ALIGN(len, status->vendor_radiotap_align); + /* vendor-defined part is already in skb */ + } + return len; } @@ -118,6 +141,11 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, struct ieee80211_radiotap_header *rthdr; unsigned char *pos; u16 rx_flags = 0; + int mpdulen; + + mpdulen = skb->len; + if (!(has_fcs && (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS))) + mpdulen += FCS_LEN; rthdr = (struct ieee80211_radiotap_header *)skb_push(skb, rtap_len); memset(rthdr, 0, rtap_len); @@ -128,17 +156,30 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, (1 << IEEE80211_RADIOTAP_CHANNEL) | (1 << IEEE80211_RADIOTAP_ANTENNA) | (1 << IEEE80211_RADIOTAP_RX_FLAGS)); - rthdr->it_len = cpu_to_le16(rtap_len); + rthdr->it_len = cpu_to_le16(rtap_len + status->vendor_radiotap_len); - pos = (unsigned char *)(rthdr+1); + pos = (unsigned char *)(rthdr + 1); + + if (status->vendor_radiotap_len) { + rthdr->it_present |= + cpu_to_le32(BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE)) | + cpu_to_le32(BIT(IEEE80211_RADIOTAP_EXT)); + put_unaligned_le32(status->vendor_radiotap_bitmap, pos); + pos += 4; + } /* the order of the following fields is important */ /* IEEE80211_RADIOTAP_TSFT */ - if (status->flag & RX_FLAG_MACTIME_MPDU) { - put_unaligned_le64(status->mactime, pos); - rthdr->it_present |= - cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); + if (ieee80211_have_rx_timestamp(status)) { + /* padding */ + while ((pos - (u8 *)rthdr) & 7) + *pos++ = 0; + put_unaligned_le64( + ieee80211_calculate_rx_timestamp(local, status, + mpdulen, 0), + pos); + rthdr->it_present |= cpu_to_le32(1 << IEEE80211_RADIOTAP_TSFT); pos += 8; } @@ -152,7 +193,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, pos++; /* IEEE80211_RADIOTAP_RATE */ - if (!rate || status->flag & RX_FLAG_HT) { + if (!rate || status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) { /* * Without rate information don't add it. If we have, * MCS information is a separate field in radiotap, @@ -172,7 +213,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, if (status->band == IEEE80211_BAND_5GHZ) put_unaligned_le16(IEEE80211_CHAN_OFDM | IEEE80211_CHAN_5GHZ, pos); - else if (status->flag & RX_FLAG_HT) + else if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) put_unaligned_le16(IEEE80211_CHAN_DYN | IEEE80211_CHAN_2GHZ, pos); else if (rate && rate->flags & IEEE80211_RATE_ERP_G) @@ -205,7 +246,7 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, /* IEEE80211_RADIOTAP_RX_FLAGS */ /* ensure 2 byte alignment for the 2 byte field as required */ if ((pos - (u8 *)rthdr) & 1) - pos++; + *pos++ = 0; if (status->flag & RX_FLAG_FAILED_PLCP_CRC) rx_flags |= IEEE80211_RADIOTAP_F_RX_BADPLCP; put_unaligned_le16(rx_flags, pos); @@ -255,6 +296,21 @@ ieee80211_add_rx_radiotap_header(struct ieee80211_local *local, *pos++ = 0; *pos++ = 0; } + + if (status->vendor_radiotap_len) { + /* ensure 2 byte alignment for the vendor field as required */ + if ((pos - (u8 *)rthdr) & 1) + *pos++ = 0; + *pos++ = status->vendor_radiotap_oui[0]; + *pos++ = status->vendor_radiotap_oui[1]; + *pos++ = status->vendor_radiotap_oui[2]; + *pos++ = status->vendor_radiotap_subns; + put_unaligned_le16(status->vendor_radiotap_len, pos); + pos += 2; + /* align the actual payload as requested */ + while ((pos - (u8 *)rthdr) & (status->vendor_radiotap_align - 1)) + *pos++ = 0; + } } /* @@ -283,13 +339,13 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, */ /* room for the radiotap header based on driver features */ - needed_headroom = ieee80211_rx_radiotap_len(local, status); + needed_headroom = ieee80211_rx_radiotap_space(local, status); if (local->hw.flags & IEEE80211_HW_RX_INCLUDES_FCS) present_fcs_len = FCS_LEN; - /* make sure hdr->frame_control is on the linear part */ - if (!pskb_may_pull(origskb, 2)) { + /* ensure hdr->frame_control and vendor radiotap data are in skb head */ + if (!pskb_may_pull(origskb, 2 + status->vendor_radiotap_len)) { dev_kfree_skb(origskb); return NULL; } @@ -374,7 +430,6 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb, return origskb; } - static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; @@ -403,10 +458,10 @@ static void ieee80211_parse_qos(struct ieee80211_rx_data *rx) * * We also use that counter for non-QoS STAs. */ - seqno_idx = NUM_RX_DATA_QUEUES; + seqno_idx = IEEE80211_NUM_TIDS; security_idx = 0; if (ieee80211_is_mgmt(hdr->frame_control)) - security_idx = NUM_RX_DATA_QUEUES; + security_idx = IEEE80211_NUM_TIDS; tid = 0; } @@ -481,8 +536,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) struct ieee80211_mgmt *hdr = (struct ieee80211_mgmt *) skb->data; struct ieee80211_mmie *mmie; - if (skb->len < 24 + sizeof(*mmie) || - !is_multicast_ether_addr(hdr->da)) + if (skb->len < 24 + sizeof(*mmie) || !is_multicast_ether_addr(hdr->da)) return -1; if (!ieee80211_is_robust_mgmt_frame((struct ieee80211_hdr *) hdr)) @@ -497,9 +551,7 @@ static int ieee80211_get_mmie_keyidx(struct sk_buff *skb) return le16_to_cpu(mmie->key_id); } - -static ieee80211_rx_result -ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) +static ieee80211_rx_result ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; char *dev_addr = rx->sdata->vif.addr; @@ -507,7 +559,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) if (ieee80211_is_data(hdr->frame_control)) { if (is_multicast_ether_addr(hdr->addr1)) { if (ieee80211_has_tods(hdr->frame_control) || - !ieee80211_has_fromds(hdr->frame_control)) + !ieee80211_has_fromds(hdr->frame_control)) return RX_DROP_MONITOR; if (ether_addr_equal(hdr->addr3, dev_addr)) return RX_DROP_MONITOR; @@ -539,7 +591,7 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) mgmt = (struct ieee80211_mgmt *)hdr; category = mgmt->u.action.category; if (category != WLAN_CATEGORY_MESH_ACTION && - category != WLAN_CATEGORY_SELF_PROTECTED) + category != WLAN_CATEGORY_SELF_PROTECTED) return RX_DROP_MONITOR; return RX_CONTINUE; } @@ -551,7 +603,6 @@ ieee80211_rx_mesh_check(struct ieee80211_rx_data *rx) return RX_CONTINUE; return RX_DROP_MONITOR; - } return RX_CONTINUE; @@ -575,7 +626,6 @@ static inline u16 seq_sub(u16 sq1, u16 sq2) return (sq1 - sq2) & SEQ_MASK; } - static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, int index) @@ -1291,17 +1341,22 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) /* * Update last_rx only for IBSS packets which are for the current - * BSSID to avoid keeping the current IBSS network alive in cases - * where other STAs start using different BSSID. + * BSSID and for station already AUTHORIZED to avoid keeping the + * current IBSS network alive in cases where other STAs start + * using different BSSID. This will also give the station another + * chance to restart the authentication/authorization in case + * something went wrong the first time. */ if (rx->sdata->vif.type == NL80211_IFTYPE_ADHOC) { u8 *bssid = ieee80211_get_bssid(hdr, rx->skb->len, NL80211_IFTYPE_ADHOC); - if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid)) { + if (ether_addr_equal(bssid, rx->sdata->u.ibss.bssid) && + test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { 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; + sta->last_rx_rate_vht_nss = status->vht_nss; } } } else if (!is_multicast_ether_addr(hdr->addr1)) { @@ -1313,6 +1368,7 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx) if (ieee80211_is_data(hdr->frame_control)) { sta->last_rx_rate_idx = status->rate_idx; sta->last_rx_rate_flag = status->flag; + sta->last_rx_rate_vht_nss = status->vht_nss; } } @@ -1585,18 +1641,15 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx) return RX_CONTINUE; } -static int -ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) +static int ieee80211_802_1x_port_control(struct ieee80211_rx_data *rx) { - if (unlikely(!rx->sta || - !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) + if (unlikely(!rx->sta || !test_sta_flag(rx->sta, WLAN_STA_AUTHORIZED))) return -EACCES; return 0; } -static int -ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) +static int ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) { struct sk_buff *skb = rx->skb; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); @@ -1618,8 +1671,7 @@ ieee80211_drop_unencrypted(struct ieee80211_rx_data *rx, __le16 fc) return 0; } -static int -ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) +static int ieee80211_drop_unencrypted_mgmt(struct ieee80211_rx_data *rx) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)rx->skb->data; struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb); @@ -2003,7 +2055,7 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx) } else { /* unable to resolve next hop */ mesh_path_error_tx(ifmsh->mshcfg.element_ttl, fwd_hdr->addr3, - 0, reason, fwd_hdr->addr2, sdata); + 0, reason, fwd_hdr->addr2, sdata); IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_no_route); kfree_skb(fwd_skb); return RX_DROP_MONITOR; @@ -2212,7 +2264,7 @@ ieee80211_rx_h_mgmt_check(struct ieee80211_rx_data *rx) cfg80211_report_obss_beacon(rx->local->hw.wiphy, rx->skb->data, rx->skb->len, - status->freq, sig, GFP_ATOMIC); + status->freq, sig); rx->flags |= IEEE80211_RX_BEACON_REPORTED; } @@ -2412,7 +2464,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx) if (!ieee80211_vif_is_mesh(&sdata->vif)) break; if (mesh_action_is_path_sel(mgmt) && - (!mesh_path_sel_is_hwmp(sdata))) + !mesh_path_sel_is_hwmp(sdata)) break; goto queue; } @@ -2468,7 +2520,6 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx) return RX_QUEUED; } - return RX_CONTINUE; } @@ -2598,7 +2649,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx, goto out_free_skb; /* room for the radiotap header based on driver features */ - needed_headroom = ieee80211_rx_radiotap_len(local, status); + needed_headroom = ieee80211_rx_radiotap_space(local, status); if (skb_headroom(skb) < needed_headroom && pskb_expand_head(skb, needed_headroom, 0, GFP_ATOMIC)) @@ -2661,7 +2712,8 @@ static void ieee80211_rx_handlers_result(struct ieee80211_rx_data *rx, status = IEEE80211_SKB_RXCB((rx->skb)); sband = rx->local->hw.wiphy->bands[status->band]; - if (!(status->flag & RX_FLAG_HT)) + if (!(status->flag & RX_FLAG_HT) && + !(status->flag & RX_FLAG_VHT)) rate = &sband->bitrates[status->rate_idx]; ieee80211_rx_cooked_monitor(rx, rate); @@ -2828,8 +2880,8 @@ static int prepare_for_handlers(struct ieee80211_rx_data *rx, status->rx_flags &= ~IEEE80211_RX_RA_MATCH; } else if (!rx->sta) { int rate_idx; - if (status->flag & RX_FLAG_HT) - rate_idx = 0; /* TODO: HT rates */ + if (status->flag & (RX_FLAG_HT | RX_FLAG_VHT)) + rate_idx = 0; /* TODO: HT/VHT rates */ else rate_idx = status->rate_idx; ieee80211_ibss_rx_no_sta(sdata, bssid, hdr->addr2, @@ -3105,6 +3157,13 @@ void ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb) status->rate_idx, status->rate_idx)) goto drop; + } else if (status->flag & RX_FLAG_VHT) { + if (WARN_ONCE(status->rate_idx > 9 || + !status->vht_nss || + status->vht_nss > 8, + "Rate marked as a VHT rate but data is invalid: MCS: %d, NSS: %d\n", + status->rate_idx, status->vht_nss)) + goto drop; } else { if (WARN_ON(status->rate_idx >= sband->n_bitrates)) goto drop; diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 8e9bb168b73..f3340279aba 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -174,7 +174,6 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) u8 *elements; struct ieee80211_channel *channel; size_t baselen; - int freq; bool beacon; struct ieee802_11_elems elems; @@ -209,13 +208,7 @@ void ieee80211_scan_rx(struct ieee80211_local *local, struct sk_buff *skb) ieee802_11_parse_elems(elements, skb->len - baselen, &elems); - if (elems.ds_params && elems.ds_params_len == 1) - freq = ieee80211_channel_to_frequency(elems.ds_params[0], - rx_status->band); - else - freq = rx_status->freq; - - channel = ieee80211_get_channel(local->hw.wiphy, freq); + channel = ieee80211_get_channel(local->hw.wiphy, rx_status->freq); if (!channel || channel->flags & IEEE80211_CHAN_DISABLED) return; diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index e9d57689c05..f3e502502fe 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -142,7 +142,7 @@ static void free_sta_work(struct work_struct *wk) * drivers have to handle aggregation stop being requested, followed * directly by station destruction. */ - for (i = 0; i < STA_TID_NUM; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); if (!tid_tx) continue; @@ -330,7 +330,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, return NULL; } - for (i = 0; i < STA_TID_NUM; i++) { + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { /* * timer_to_tid must be initialized with identity mapping * to enable session_timer's data differentiation. See @@ -343,7 +343,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata, skb_queue_head_init(&sta->tx_filtered[i]); } - for (i = 0; i < NUM_RX_DATA_QUEUES; i++) + for (i = 0; i < IEEE80211_NUM_TIDS; i++) sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX); sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr); @@ -986,7 +986,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta) clear_sta_flag(sta, WLAN_STA_SP); - BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1); + BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1); sta->driver_buffered_tids = 0; if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS)) @@ -1092,7 +1092,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata, return; } - ieee80211_xmit(sdata, skb, chanctx_conf->channel->band); + ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band); rcu_read_unlock(); } @@ -1374,7 +1374,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta, { struct sta_info *sta = container_of(pubsta, struct sta_info, sta); - if (WARN_ON(tid >= STA_TID_NUM)) + if (WARN_ON(tid >= IEEE80211_NUM_TIDS)) return; if (buffered) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c88f161f811..6835cea4e40 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -80,7 +80,6 @@ enum ieee80211_sta_info_flags { WLAN_STA_TOFFSET_KNOWN, }; -#define STA_TID_NUM 16 #define ADDBA_RESP_INTERVAL HZ #define HT_AGG_MAX_RETRIES 15 #define HT_AGG_BURST_RETRIES 3 @@ -197,15 +196,15 @@ struct tid_ampdu_rx { struct sta_ampdu_mlme { struct mutex mtx; /* rx */ - struct tid_ampdu_rx __rcu *tid_rx[STA_TID_NUM]; - unsigned long tid_rx_timer_expired[BITS_TO_LONGS(STA_TID_NUM)]; - unsigned long tid_rx_stop_requested[BITS_TO_LONGS(STA_TID_NUM)]; + struct tid_ampdu_rx __rcu *tid_rx[IEEE80211_NUM_TIDS]; + unsigned long tid_rx_timer_expired[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; + unsigned long tid_rx_stop_requested[BITS_TO_LONGS(IEEE80211_NUM_TIDS)]; /* tx */ struct work_struct work; - struct tid_ampdu_tx __rcu *tid_tx[STA_TID_NUM]; - struct tid_ampdu_tx *tid_start_tx[STA_TID_NUM]; - unsigned long last_addba_req_time[STA_TID_NUM]; - u8 addba_req_num[STA_TID_NUM]; + struct tid_ampdu_tx __rcu *tid_tx[IEEE80211_NUM_TIDS]; + struct tid_ampdu_tx *tid_start_tx[IEEE80211_NUM_TIDS]; + unsigned long last_addba_req_time[IEEE80211_NUM_TIDS]; + u8 addba_req_num[IEEE80211_NUM_TIDS]; u8 dialog_token_allocator; }; @@ -228,6 +227,7 @@ struct sta_ampdu_mlme { * "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 + * @last_rx_rate_vht_nss: rx status nss of last data packet * @lock: used for locking all fields that require locking, see comments * in the header file. * @drv_unblock_wk: used for driver PS unblocking @@ -273,7 +273,7 @@ struct sta_ampdu_mlme { * @t_offset: timing offset relative to this host * @t_offset_setpoint: reference timing offset of this sta to be used when * calculating clockdrift - * @ch_type: peer's channel type + * @ch_width: peer's channel width * @debugfs: debug filesystem info * @dead: set to true when sta is unlinked * @uploaded: set to true when sta is uploaded to the driver @@ -330,7 +330,7 @@ struct sta_info { int last_signal; struct ewma avg_signal; /* Plus 1 for non-QoS frames */ - __le16 last_seq_ctrl[NUM_RX_DATA_QUEUES + 1]; + __le16 last_seq_ctrl[IEEE80211_NUM_TIDS + 1]; /* Updated from TX status path only, no locking requirements */ unsigned long tx_filtered_count; @@ -344,14 +344,15 @@ struct sta_info { unsigned long tx_fragments; struct ieee80211_tx_rate last_tx_rate; int last_rx_rate_idx; - int last_rx_rate_flag; + u32 last_rx_rate_flag; + u8 last_rx_rate_vht_nss; u16 tid_seq[IEEE80211_QOS_CTL_TID_MASK + 1]; /* * Aggregation information, locked with lock. */ struct sta_ampdu_mlme ampdu_mlme; - u8 timer_to_tid[STA_TID_NUM]; + u8 timer_to_tid[IEEE80211_NUM_TIDS]; #ifdef CONFIG_MAC80211_MESH /* @@ -369,7 +370,7 @@ struct sta_info { struct timer_list plink_timer; s64 t_offset; s64 t_offset_setpoint; - enum nl80211_channel_type ch_type; + enum nl80211_chan_width ch_width; #endif #ifdef CONFIG_MAC80211_DEBUGFS diff --git a/net/mac80211/status.c b/net/mac80211/status.c index 248247877eb..ab63237107c 100644 --- a/net/mac80211/status.c +++ b/net/mac80211/status.c @@ -325,6 +325,75 @@ static void ieee80211_add_tx_radiotap_header(struct ieee80211_supported_band } +static void ieee80211_report_used_skb(struct ieee80211_local *local, + struct sk_buff *skb, bool dropped) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + bool acked = info->flags & IEEE80211_TX_STAT_ACK; + + if (dropped) + acked = false; + + if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { + struct ieee80211_sub_if_data *sdata = NULL; + struct ieee80211_sub_if_data *iter_sdata; + u64 cookie = (unsigned long)skb; + + rcu_read_lock(); + + if (skb->dev) { + list_for_each_entry_rcu(iter_sdata, &local->interfaces, + list) { + if (!iter_sdata->dev) + continue; + + if (skb->dev == iter_sdata->dev) { + sdata = iter_sdata; + break; + } + } + } else { + sdata = rcu_dereference(local->p2p_sdata); + } + + if (!sdata) + skb->dev = NULL; + else if (ieee80211_is_nullfunc(hdr->frame_control) || + ieee80211_is_qos_nullfunc(hdr->frame_control)) { + cfg80211_probe_status(sdata->dev, hdr->addr1, + cookie, acked, GFP_ATOMIC); + } else { + cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data, + skb->len, acked, GFP_ATOMIC); + } + + rcu_read_unlock(); + } + + if (unlikely(info->ack_frame_id)) { + struct sk_buff *ack_skb; + unsigned long flags; + + spin_lock_irqsave(&local->ack_status_lock, flags); + ack_skb = idr_find(&local->ack_status_frames, + info->ack_frame_id); + if (ack_skb) + idr_remove(&local->ack_status_frames, + info->ack_frame_id); + spin_unlock_irqrestore(&local->ack_status_lock, flags); + + if (ack_skb) { + if (!dropped) { + /* consumes ack_skb */ + skb_complete_wifi_ack(ack_skb, acked); + } else { + dev_kfree_skb_any(ack_skb); + } + } + } +} + /* * Use a static threshold for now, best value to be determined * by testing ... @@ -516,62 +585,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) msecs_to_jiffies(10)); } - if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX) { - u64 cookie = (unsigned long)skb; - bool found = false; - - acked = info->flags & IEEE80211_TX_STAT_ACK; - - rcu_read_lock(); - - list_for_each_entry_rcu(sdata, &local->interfaces, list) { - if (!sdata->dev) - continue; - - if (skb->dev != sdata->dev) - continue; - - found = true; - break; - } - - if (!skb->dev) { - sdata = rcu_dereference(local->p2p_sdata); - if (sdata) - found = true; - } - - if (!found) - skb->dev = NULL; - else if (ieee80211_is_nullfunc(hdr->frame_control) || - ieee80211_is_qos_nullfunc(hdr->frame_control)) { - cfg80211_probe_status(sdata->dev, hdr->addr1, - cookie, acked, GFP_ATOMIC); - } else { - cfg80211_mgmt_tx_status(&sdata->wdev, cookie, skb->data, - skb->len, acked, GFP_ATOMIC); - } - - rcu_read_unlock(); - } - - if (unlikely(info->ack_frame_id)) { - struct sk_buff *ack_skb; - unsigned long flags; - - spin_lock_irqsave(&local->ack_status_lock, flags); - ack_skb = idr_find(&local->ack_status_frames, - info->ack_frame_id); - if (ack_skb) - idr_remove(&local->ack_status_frames, - info->ack_frame_id); - spin_unlock_irqrestore(&local->ack_status_lock, flags); - - /* consumes ack_skb */ - if (ack_skb) - skb_complete_wifi_ack(ack_skb, - info->flags & IEEE80211_TX_STAT_ACK); - } + ieee80211_report_used_skb(local, skb, false); /* this was a transmitted frame, but now we want to reuse it */ skb_orphan(skb); @@ -647,25 +661,8 @@ EXPORT_SYMBOL(ieee80211_report_low_ack); void ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb) { struct ieee80211_local *local = hw_to_local(hw); - struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - - if (unlikely(info->ack_frame_id)) { - struct sk_buff *ack_skb; - unsigned long flags; - - spin_lock_irqsave(&local->ack_status_lock, flags); - ack_skb = idr_find(&local->ack_status_frames, - info->ack_frame_id); - if (ack_skb) - idr_remove(&local->ack_status_frames, - info->ack_frame_id); - spin_unlock_irqrestore(&local->ack_status_lock, flags); - - /* consumes ack_skb */ - if (ack_skb) - dev_kfree_skb_any(ack_skb); - } + ieee80211_report_used_skb(local, skb, true); dev_kfree_skb_any(skb); } EXPORT_SYMBOL(ieee80211_free_txskb); diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index 0638541b625..a8270b441a6 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h @@ -28,16 +28,21 @@ #define VIF_PR_FMT " vif:%s(%d%s)" #define VIF_PR_ARG __get_str(vif_name), __entry->vif_type, __entry->p2p ? "/p2p" : "" -#define CHANCTX_ENTRY __field(int, freq) \ - __field(int, chantype) \ +#define CHANCTX_ENTRY __field(u32, control_freq) \ + __field(u32, chan_width) \ + __field(u32, center_freq1) \ + __field(u32, center_freq2) \ __field(u8, rx_chains_static) \ __field(u8, rx_chains_dynamic) -#define CHANCTX_ASSIGN __entry->freq = ctx->conf.channel->center_freq; \ - __entry->chantype = ctx->conf.channel_type; \ +#define CHANCTX_ASSIGN __entry->control_freq = ctx->conf.def.chan->center_freq;\ + __entry->chan_width = ctx->conf.def.width; \ + __entry->center_freq1 = ctx->conf.def.center_freq1; \ + __entry->center_freq2 = ctx->conf.def.center_freq2; \ __entry->rx_chains_static = ctx->conf.rx_chains_static; \ __entry->rx_chains_dynamic = ctx->conf.rx_chains_dynamic -#define CHANCTX_PR_FMT " freq:%d MHz chantype:%d chains:%d/%d" -#define CHANCTX_PR_ARG __entry->freq, __entry->chantype, \ +#define CHANCTX_PR_FMT " control:%d MHz width:%d center: %d/%d MHz chains:%d/%d" +#define CHANCTX_PR_ARG __entry->control_freq, __entry->chan_width, \ + __entry->center_freq1, __entry->center_freq2, \ __entry->rx_chains_static, __entry->rx_chains_dynamic @@ -334,7 +339,8 @@ TRACE_EVENT(drv_bss_info_changed, __field(u16, ht_operation_mode) __field(s32, cqm_rssi_thold); __field(s32, cqm_rssi_hyst); - __field(u32, channel_type); + __field(u32, channel_width); + __field(u32, channel_cfreq1); __dynamic_array(u32, arp_addr_list, info->arp_addr_cnt); __field(bool, arp_filter_enabled); __field(bool, qos); @@ -342,6 +348,9 @@ TRACE_EVENT(drv_bss_info_changed, __field(bool, ps); __dynamic_array(u8, ssid, info->ssid_len); __field(bool, hidden_ssid); + __field(int, txpower) + __field(u8, p2p_ctwindow) + __field(bool, p2p_oppps) ), TP_fast_assign( @@ -367,7 +376,8 @@ TRACE_EVENT(drv_bss_info_changed, __entry->ht_operation_mode = info->ht_operation_mode; __entry->cqm_rssi_thold = info->cqm_rssi_thold; __entry->cqm_rssi_hyst = info->cqm_rssi_hyst; - __entry->channel_type = info->channel_type; + __entry->channel_width = info->chandef.width; + __entry->channel_cfreq1 = info->chandef.center_freq1; memcpy(__get_dynamic_array(arp_addr_list), info->arp_addr_list, sizeof(u32) * info->arp_addr_cnt); __entry->arp_filter_enabled = info->arp_filter_enabled; @@ -376,6 +386,9 @@ TRACE_EVENT(drv_bss_info_changed, __entry->ps = info->ps; memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); __entry->hidden_ssid = info->hidden_ssid; + __entry->txpower = info->txpower; + __entry->p2p_ctwindow = info->p2p_ctwindow; + __entry->p2p_oppps = info->p2p_oppps; ), TP_printk( @@ -1013,28 +1026,31 @@ TRACE_EVENT(drv_get_antenna, ); TRACE_EVENT(drv_remain_on_channel, - TP_PROTO(struct ieee80211_local *local, struct ieee80211_channel *chan, - enum nl80211_channel_type chantype, unsigned int duration), + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_channel *chan, + unsigned int duration), - TP_ARGS(local, chan, chantype, duration), + TP_ARGS(local, sdata, chan, duration), TP_STRUCT__entry( LOCAL_ENTRY + VIF_ENTRY __field(int, center_freq) - __field(int, channel_type) __field(unsigned int, duration) ), TP_fast_assign( LOCAL_ASSIGN; + VIF_ASSIGN; __entry->center_freq = chan->center_freq; - __entry->channel_type = chantype; __entry->duration = duration; ), TP_printk( - LOCAL_PR_FMT " freq:%dMHz duration:%dms", - LOCAL_PR_ARG, __entry->center_freq, __entry->duration + LOCAL_PR_FMT VIF_PR_FMT " freq:%dMHz duration:%dms", + LOCAL_PR_ARG, VIF_PR_ARG, + __entry->center_freq, __entry->duration ) ); @@ -1043,34 +1059,6 @@ 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 - ) -); - TRACE_EVENT(drv_set_ringparam, TP_PROTO(struct ieee80211_local *local, u32 tx, u32 rx), @@ -1396,6 +1384,48 @@ DEFINE_EVENT(local_sdata_chanctx, drv_unassign_vif_chanctx, TP_ARGS(local, sdata, ctx) ); +TRACE_EVENT(drv_start_ap, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata, + struct ieee80211_bss_conf *info), + + TP_ARGS(local, sdata, info), + + TP_STRUCT__entry( + LOCAL_ENTRY + VIF_ENTRY + __field(u8, dtimper) + __field(u16, bcnint) + __dynamic_array(u8, ssid, info->ssid_len); + __field(bool, hidden_ssid); + ), + + TP_fast_assign( + LOCAL_ASSIGN; + VIF_ASSIGN; + __entry->dtimper = info->dtim_period; + __entry->bcnint = info->beacon_int; + memcpy(__get_dynamic_array(ssid), info->ssid, info->ssid_len); + __entry->hidden_ssid = info->hidden_ssid; + ), + + TP_printk( + LOCAL_PR_FMT VIF_PR_FMT, + LOCAL_PR_ARG, VIF_PR_ARG + ) +); + +DEFINE_EVENT(local_sdata_evt, drv_stop_ap, + TP_PROTO(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata), + TP_ARGS(local, sdata) +); + +DEFINE_EVENT(local_only_evt, drv_restart_complete, + TP_PROTO(struct ieee80211_local *local), + TP_ARGS(local) +); + /* * Tracing for API calls that drivers call. */ diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c index 6f1981816dc..d287a4f2c01 100644 --- a/net/mac80211/tx.c +++ b/net/mac80211/tx.c @@ -1676,7 +1676,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb, if (!chanctx_conf) goto fail_rcu; - chan = chanctx_conf->channel; + chan = chanctx_conf->def.chan; /* * Frame injection is not allowed if beaconing is not allowed @@ -1779,7 +1779,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(ap_sdata->vif.chanctx_conf); if (!chanctx_conf) goto fail_rcu; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; if (sta) break; /* fall through */ @@ -1794,7 +1794,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) goto fail_rcu; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; break; case NL80211_IFTYPE_WDS: fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS); @@ -1871,7 +1871,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) goto fail_rcu; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; break; #endif case NL80211_IFTYPE_STATION: @@ -1930,7 +1930,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) goto fail_rcu; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; break; case NL80211_IFTYPE_ADHOC: /* DA SA BSSID */ @@ -1941,7 +1941,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (!chanctx_conf) goto fail_rcu; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; break; default: goto fail_rcu; @@ -2089,6 +2089,7 @@ netdev_tx_t ieee80211_subif_start_xmit(struct sk_buff *skb, head_need = max_t(int, 0, head_need); if (ieee80211_skb_resize(sdata, skb, head_need, true)) { ieee80211_free_txskb(&local->hw, skb); + skb = NULL; goto fail_rcu; } } @@ -2193,7 +2194,7 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local, return true; } result = ieee80211_tx(sdata, skb, true, - chanctx_conf->channel->band); + chanctx_conf->def.chan->band); } else { struct sk_buff_head skbs; @@ -2457,7 +2458,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, *pos++ = WLAN_EID_SSID; *pos++ = 0x0; - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; if (ieee80211_add_srates_ie(sdata, skb, true, band) || mesh_add_ds_params_ie(skb, sdata) || @@ -2476,7 +2477,7 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw, goto out; } - band = chanctx_conf->channel->band; + band = chanctx_conf->def.chan->band; info = IEEE80211_SKB_CB(skb); @@ -2756,7 +2757,7 @@ ieee80211_get_buffered_bc(struct ieee80211_hw *hw, info = IEEE80211_SKB_CB(skb); tx.flags |= IEEE80211_TX_PS_BUFFERED; - info->band = chanctx_conf->channel->band; + info->band = chanctx_conf->def.chan->band; if (invoke_tx_handlers(&tx)) skb = NULL; diff --git a/net/mac80211/util.c b/net/mac80211/util.c index acbb8c9bae2..08132ff9815 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -512,7 +512,7 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw) EXPORT_SYMBOL(ieee80211_wake_queues); void ieee80211_iterate_active_interfaces( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) @@ -530,6 +530,9 @@ void ieee80211_iterate_active_interfaces( default: break; } + if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + continue; if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); @@ -537,7 +540,9 @@ void ieee80211_iterate_active_interfaces( sdata = rcu_dereference_protected(local->monitor_sdata, lockdep_is_held(&local->iflist_mtx)); - if (sdata) + if (sdata && + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); mutex_unlock(&local->iflist_mtx); @@ -545,7 +550,7 @@ void ieee80211_iterate_active_interfaces( EXPORT_SYMBOL_GPL(ieee80211_iterate_active_interfaces); void ieee80211_iterate_active_interfaces_atomic( - struct ieee80211_hw *hw, + struct ieee80211_hw *hw, u32 iter_flags, void (*iterator)(void *data, u8 *mac, struct ieee80211_vif *vif), void *data) @@ -563,13 +568,18 @@ void ieee80211_iterate_active_interfaces_atomic( default: break; } + if (!(iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL) && + !(sdata->flags & IEEE80211_SDATA_IN_DRIVER)) + continue; if (ieee80211_sdata_running(sdata)) iterator(data, sdata->vif.addr, &sdata->vif); } sdata = rcu_dereference(local->monitor_sdata); - if (sdata) + if (sdata && + (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || + sdata->flags & IEEE80211_SDATA_IN_DRIVER)) iterator(data, sdata->vif.addr, &sdata->vif); rcu_read_unlock(); @@ -888,7 +898,7 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata, rcu_read_lock(); chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); use_11b = (chanctx_conf && - chanctx_conf->channel->band == IEEE80211_BAND_2GHZ) && + chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ) && !(sdata->flags & IEEE80211_SDATA_OPERATING_GMODE); rcu_read_unlock(); @@ -981,7 +991,7 @@ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata, chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf); if (chanctx_conf && - chanctx_conf->channel->band == IEEE80211_BAND_2GHZ && + chanctx_conf->def.chan->band == IEEE80211_BAND_2GHZ && have_higher_than_11mbit) sdata->flags |= IEEE80211_SDATA_OPERATING_GMODE; else @@ -1407,10 +1417,44 @@ int ieee80211_reconfig(struct ieee80211_local *local) } /* add channel contexts */ - mutex_lock(&local->chanctx_mtx); - list_for_each_entry(ctx, &local->chanctx_list, list) - WARN_ON(drv_add_chanctx(local, ctx)); - mutex_unlock(&local->chanctx_mtx); + if (local->use_chanctx) { + mutex_lock(&local->chanctx_mtx); + list_for_each_entry(ctx, &local->chanctx_list, list) + WARN_ON(drv_add_chanctx(local, ctx)); + mutex_unlock(&local->chanctx_mtx); + } + + list_for_each_entry(sdata, &local->interfaces, list) { + struct ieee80211_chanctx_conf *ctx_conf; + + if (!ieee80211_sdata_running(sdata)) + continue; + + mutex_lock(&local->chanctx_mtx); + ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (ctx_conf) { + ctx = container_of(ctx_conf, struct ieee80211_chanctx, + conf); + drv_assign_vif_chanctx(local, sdata, ctx); + } + mutex_unlock(&local->chanctx_mtx); + } + + sdata = rtnl_dereference(local->monitor_sdata); + if (sdata && local->use_chanctx && ieee80211_sdata_running(sdata)) { + struct ieee80211_chanctx_conf *ctx_conf; + + mutex_lock(&local->chanctx_mtx); + ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, + lockdep_is_held(&local->chanctx_mtx)); + if (ctx_conf) { + ctx = container_of(ctx_conf, struct ieee80211_chanctx, + conf); + drv_assign_vif_chanctx(local, sdata, ctx); + } + mutex_unlock(&local->chanctx_mtx); + } /* add STAs back */ mutex_lock(&local->sta_mtx); @@ -1452,22 +1496,11 @@ int ieee80211_reconfig(struct ieee80211_local *local) /* Finally also reconfigure all the BSS information */ list_for_each_entry(sdata, &local->interfaces, list) { - struct ieee80211_chanctx_conf *ctx_conf; u32 changed; if (!ieee80211_sdata_running(sdata)) continue; - mutex_lock(&local->chanctx_mtx); - ctx_conf = rcu_dereference_protected(sdata->vif.chanctx_conf, - lockdep_is_held(&local->chanctx_mtx)); - if (ctx_conf) { - ctx = container_of(ctx_conf, struct ieee80211_chanctx, - conf); - drv_assign_vif_chanctx(local, sdata, ctx); - } - mutex_unlock(&local->chanctx_mtx); - /* common change flags for all interface types */ changed = BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_ERP_PREAMBLE | @@ -1478,7 +1511,8 @@ int ieee80211_reconfig(struct ieee80211_local *local) BSS_CHANGED_BSSID | BSS_CHANGED_CQM | BSS_CHANGED_QOS | - BSS_CHANGED_IDLE; + BSS_CHANGED_IDLE | + BSS_CHANGED_TXPOWER; switch (sdata->vif.type) { case NL80211_IFTYPE_STATION: @@ -1495,9 +1529,13 @@ int ieee80211_reconfig(struct ieee80211_local *local) case NL80211_IFTYPE_AP: changed |= BSS_CHANGED_SSID; - if (sdata->vif.type == NL80211_IFTYPE_AP) + if (sdata->vif.type == NL80211_IFTYPE_AP) { changed |= BSS_CHANGED_AP_PROBE_RESP; + if (rcu_access_pointer(sdata->u.ap.beacon)) + drv_start_ap(local, sdata); + } + /* fall through */ case NL80211_IFTYPE_MESH_POINT: changed |= BSS_CHANGED_BEACON | @@ -1596,8 +1634,10 @@ int ieee80211_reconfig(struct ieee80211_local *local) * If this is for hw restart things are still running. * We may want to change that later, however. */ - if (!local->suspended) + if (!local->suspended) { + drv_restart_complete(local); return 0; + } #ifdef CONFIG_PM /* first set suspended false, then resuming */ @@ -1833,8 +1873,7 @@ u8 *ieee80211_ie_build_vht_cap(u8 *pos, struct ieee80211_sta_vht_cap *vht_cap, } u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, - struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type, + const struct cfg80211_chan_def *chandef, u16 prot_mode) { struct ieee80211_ht_operation *ht_oper; @@ -1842,23 +1881,25 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, *pos++ = WLAN_EID_HT_OPERATION; *pos++ = sizeof(struct ieee80211_ht_operation); ht_oper = (struct ieee80211_ht_operation *)pos; - ht_oper->primary_chan = - ieee80211_frequency_to_channel(channel->center_freq); - switch (channel_type) { - case NL80211_CHAN_HT40MINUS: - ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; - break; - case NL80211_CHAN_HT40PLUS: - ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + ht_oper->primary_chan = ieee80211_frequency_to_channel( + chandef->chan->center_freq); + switch (chandef->width) { + case NL80211_CHAN_WIDTH_160: + case NL80211_CHAN_WIDTH_80P80: + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 > chandef->chan->center_freq) + ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_ABOVE; + else + ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_BELOW; break; - case NL80211_CHAN_HT20: default: ht_oper->ht_param = IEEE80211_HT_PARAM_CHA_SEC_NONE; break; } if (ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && - channel_type != NL80211_CHAN_NO_HT && - channel_type != NL80211_CHAN_HT20) + chandef->width != NL80211_CHAN_WIDTH_20_NOHT && + chandef->width != NL80211_CHAN_WIDTH_20) ht_oper->ht_param |= IEEE80211_HT_PARAM_CHAN_WIDTH_ANY; ht_oper->operation_mode = cpu_to_le16(prot_mode); @@ -1872,13 +1913,17 @@ u8 *ieee80211_ie_build_ht_oper(u8 *pos, struct ieee80211_sta_ht_cap *ht_cap, return pos + sizeof(struct ieee80211_ht_operation); } -enum nl80211_channel_type -ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) +void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, + struct ieee80211_ht_operation *ht_oper, + struct cfg80211_chan_def *chandef) { enum nl80211_channel_type channel_type; - if (!ht_oper) - return NL80211_CHAN_NO_HT; + if (!ht_oper) { + cfg80211_chandef_create(chandef, control_chan, + NL80211_CHAN_NO_HT); + return; + } switch (ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET) { case IEEE80211_HT_PARAM_CHA_SEC_NONE: @@ -1894,7 +1939,7 @@ ieee80211_ht_oper_to_channel_type(struct ieee80211_ht_operation *ht_oper) channel_type = NL80211_CHAN_NO_HT; } - return channel_type; + cfg80211_chandef_create(chandef, control_chan, channel_type); } int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, @@ -1992,3 +2037,68 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs) return 2; return 1; } + +/** + * ieee80211_calculate_rx_timestamp - calculate timestamp in frame + * @local: mac80211 hw info struct + * @status: RX status + * @mpdu_len: total MPDU length (including FCS) + * @mpdu_offset: offset into MPDU to calculate timestamp at + * + * This function calculates the RX timestamp at the given MPDU offset, taking + * into account what the RX timestamp was. An offset of 0 will just normalize + * the timestamp to TSF at beginning of MPDU reception. + */ +u64 ieee80211_calculate_rx_timestamp(struct ieee80211_local *local, + struct ieee80211_rx_status *status, + unsigned int mpdu_len, + unsigned int mpdu_offset) +{ + u64 ts = status->mactime; + struct rate_info ri; + u16 rate; + + if (WARN_ON(!ieee80211_have_rx_timestamp(status))) + return 0; + + memset(&ri, 0, sizeof(ri)); + + /* Fill cfg80211 rate info */ + if (status->flag & RX_FLAG_HT) { + ri.mcs = status->rate_idx; + ri.flags |= RATE_INFO_FLAGS_MCS; + if (status->flag & RX_FLAG_40MHZ) + ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (status->flag & RX_FLAG_SHORT_GI) + ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else if (status->flag & RX_FLAG_VHT) { + ri.flags |= RATE_INFO_FLAGS_VHT_MCS; + ri.mcs = status->rate_idx; + ri.nss = status->vht_nss; + if (status->flag & RX_FLAG_40MHZ) + ri.flags |= RATE_INFO_FLAGS_40_MHZ_WIDTH; + if (status->flag & RX_FLAG_80MHZ) + ri.flags |= RATE_INFO_FLAGS_80_MHZ_WIDTH; + if (status->flag & RX_FLAG_80P80MHZ) + ri.flags |= RATE_INFO_FLAGS_80P80_MHZ_WIDTH; + if (status->flag & RX_FLAG_160MHZ) + ri.flags |= RATE_INFO_FLAGS_160_MHZ_WIDTH; + if (status->flag & RX_FLAG_SHORT_GI) + ri.flags |= RATE_INFO_FLAGS_SHORT_GI; + } else { + struct ieee80211_supported_band *sband; + + sband = local->hw.wiphy->bands[status->band]; + ri.legacy = sband->bitrates[status->rate_idx].bitrate; + } + + rate = cfg80211_calculate_bitrate(&ri); + + /* rewind from end of MPDU */ + if (status->flag & RX_FLAG_MACTIME_END) + ts -= mpdu_len * 8 * 10 / rate; + + ts += mpdu_offset * 8 * 10 / rate; + + return ts; +} diff --git a/net/mac80211/wme.c b/net/mac80211/wme.c index cea06e9f26f..906f00cd6d2 100644 --- a/net/mac80211/wme.c +++ b/net/mac80211/wme.c @@ -160,31 +160,37 @@ u16 ieee80211_select_queue(struct ieee80211_sub_if_data *sdata, return ieee80211_downgrade_queue(sdata, skb); } +/** + * ieee80211_set_qos_hdr - Fill in the QoS header if there is one. + * + * @sdata: local subif + * @skb: packet to be updated + */ void ieee80211_set_qos_hdr(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) { struct ieee80211_hdr *hdr = (void *)skb->data; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + u8 *p; + u8 ack_policy, tid; - /* Fill in the QoS header if there is one. */ - if (ieee80211_is_data_qos(hdr->frame_control)) { - u8 *p = ieee80211_get_qos_ctl(hdr); - u8 ack_policy, tid; - - tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; + if (!ieee80211_is_data_qos(hdr->frame_control)) + return; - /* preserve EOSP bit */ - ack_policy = *p & IEEE80211_QOS_CTL_EOSP; + p = ieee80211_get_qos_ctl(hdr); + tid = skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK; - if (is_multicast_ether_addr(hdr->addr1) || - sdata->noack_map & BIT(tid)) { - ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; - info->flags |= IEEE80211_TX_CTL_NO_ACK; - } + /* preserve EOSP bit */ + ack_policy = *p & IEEE80211_QOS_CTL_EOSP; - /* qos header is 2 bytes */ - *p++ = ack_policy | tid; - *p = ieee80211_vif_is_mesh(&sdata->vif) ? - (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; + if (is_multicast_ether_addr(hdr->addr1) || + sdata->noack_map & BIT(tid)) { + ack_policy |= IEEE80211_QOS_CTL_ACK_POLICY_NOACK; + info->flags |= IEEE80211_TX_CTL_NO_ACK; } + + /* qos header is 2 bytes */ + *p++ = ack_policy | tid; + *p = ieee80211_vif_is_mesh(&sdata->vif) ? + (IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT >> 8) : 0; } diff --git a/net/mac802154/tx.c b/net/mac802154/tx.c index 1a4df39c722..4e09d070995 100644 --- a/net/mac802154/tx.c +++ b/net/mac802154/tx.c @@ -85,6 +85,7 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, if (!(priv->phy->channels_supported[page] & (1 << chan))) { WARN_ON(1); + kfree_skb(skb); return NETDEV_TX_OK; } @@ -98,13 +99,15 @@ netdev_tx_t mac802154_tx(struct mac802154_priv *priv, struct sk_buff *skb, } if (skb_cow_head(skb, priv->hw.extra_tx_headroom)) { - dev_kfree_skb(skb); + kfree_skb(skb); return NETDEV_TX_OK; } work = kzalloc(sizeof(struct xmit_work), GFP_ATOMIC); - if (!work) + if (!work) { + kfree_skb(skb); return NETDEV_TX_BUSY; + } INIT_WORK(&work->work, mac802154_xmit_worker); work->skb = skb; diff --git a/net/mac802154/wpan.c b/net/mac802154/wpan.c index f30f6d4beea..1191039c2b1 100644 --- a/net/mac802154/wpan.c +++ b/net/mac802154/wpan.c @@ -327,8 +327,10 @@ mac802154_wpan_xmit(struct sk_buff *skb, struct net_device *dev) if (chan == MAC802154_CHAN_NONE || page >= WPAN_NUM_PAGES || - chan >= WPAN_NUM_CHANNELS) + chan >= WPAN_NUM_CHANNELS) { + kfree_skb(skb); return NETDEV_TX_OK; + } skb->skb_iif = dev->ifindex; dev->stats.tx_packets++; diff --git a/net/netfilter/ipset/ip_set_hash_netiface.c b/net/netfilter/ipset/ip_set_hash_netiface.c index b9a63381e34..45a101439bc 100644 --- a/net/netfilter/ipset/ip_set_hash_netiface.c +++ b/net/netfilter/ipset/ip_set_hash_netiface.c @@ -793,7 +793,7 @@ static struct ip_set_type hash_netiface_type __read_mostly = { [IPSET_ATTR_IP] = { .type = NLA_NESTED }, [IPSET_ATTR_IP_TO] = { .type = NLA_NESTED }, [IPSET_ATTR_IFACE] = { .type = NLA_NUL_STRING, - .len = IPSET_MAXNAMELEN - 1 }, + .len = IFNAMSIZ - 1 }, [IPSET_ATTR_CADT_FLAGS] = { .type = NLA_U32 }, [IPSET_ATTR_CIDR] = { .type = NLA_U8 }, [IPSET_ATTR_TIMEOUT] = { .type = NLA_U32 }, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index fb45640dc1f..47edf5a40a5 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -942,7 +942,7 @@ static int ip_vs_out_icmp_v6(struct sk_buff *skb, int *related, /* Fragment header that is before ICMP header tells us that: * it's not an error message since they can't be fragmented. */ - if (ipvsh->flags & IP6T_FH_F_FRAG) + if (ipvsh->flags & IP6_FH_F_FRAG) return NF_DROP; IP_VS_DBG(8, "Outgoing ICMPv6 (%d,%d) %pI6c->%pI6c\n", @@ -1475,7 +1475,7 @@ static int ip_vs_in_icmp_v6(struct sk_buff *skb, int *related, /* Fragment header that is before ICMP header tells us that: * it's not an error message since they can't be fragmented. */ - if (iph->flags & IP6T_FH_F_FRAG) + if (iph->flags & IP6_FH_F_FRAG) return NF_DROP; IP_VS_DBG(8, "Incoming ICMPv6 (%d,%d) %pI6c->%pI6c\n", diff --git a/net/netfilter/xt_HMARK.c b/net/netfilter/xt_HMARK.c index 1686ca1b53a..73b73f687c5 100644 --- a/net/netfilter/xt_HMARK.c +++ b/net/netfilter/xt_HMARK.c @@ -167,7 +167,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, const struct xt_hmark_info *info) { struct ipv6hdr *ip6, _ip6; - int flag = IP6T_FH_F_AUTH; + int flag = IP6_FH_F_AUTH; unsigned int nhoff = 0; u16 fragoff = 0; int nexthdr; @@ -177,7 +177,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, if (nexthdr < 0) return 0; /* No need to check for icmp errors on fragments */ - if ((flag & IP6T_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6)) + if ((flag & IP6_FH_F_FRAG) || (nexthdr != IPPROTO_ICMPV6)) goto noicmp; /* Use inner header in case of ICMP errors */ if (get_inner6_hdr(skb, &nhoff)) { @@ -185,7 +185,7 @@ hmark_pkt_set_htuple_ipv6(const struct sk_buff *skb, struct hmark_tuple *t, if (ip6 == NULL) return -1; /* If AH present, use SPI like in ESP. */ - flag = IP6T_FH_F_AUTH; + flag = IP6_FH_F_AUTH; nexthdr = ipv6_find_hdr(skb, &nhoff, -1, &fragoff, &flag); if (nexthdr < 0) return -1; @@ -201,7 +201,7 @@ noicmp: if (t->proto == IPPROTO_ICMPV6) return 0; - if (flag & IP6T_FH_F_FRAG) + if (flag & IP6_FH_F_FRAG) return 0; hmark_set_tuple_ports(skb, nhoff, t, info); diff --git a/net/nfc/hci/command.c b/net/nfc/hci/command.c index 07659cfd6d7..7d99410e6c1 100644 --- a/net/nfc/hci/command.c +++ b/net/nfc/hci/command.c @@ -344,7 +344,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, return -EADDRINUSE; if (pipe != NFC_HCI_INVALID_PIPE) - goto pipe_is_open; + goto open_pipe; switch (dest_gate) { case NFC_HCI_LINK_MGMT_GATE: @@ -361,6 +361,7 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, break; } +open_pipe: r = nfc_hci_open_pipe(hdev, pipe); if (r < 0) { if (pipe_created) @@ -371,7 +372,6 @@ int nfc_hci_connect_gate(struct nfc_hci_dev *hdev, u8 dest_host, u8 dest_gate, return r; } -pipe_is_open: hdev->gate2pipe[dest_gate] = pipe; return 0; diff --git a/net/nfc/hci/core.c b/net/nfc/hci/core.c index bc571b0efb9..7bea574d593 100644 --- a/net/nfc/hci/core.c +++ b/net/nfc/hci/core.c @@ -33,17 +33,20 @@ /* Largest headroom needed for outgoing HCI commands */ #define HCI_CMDS_HEADROOM 1 -static int nfc_hci_result_to_errno(u8 result) +int nfc_hci_result_to_errno(u8 result) { switch (result) { case NFC_HCI_ANY_OK: return 0; + case NFC_HCI_ANY_E_REG_PAR_UNKNOWN: + return -EOPNOTSUPP; case NFC_HCI_ANY_E_TIMEOUT: return -ETIME; default: return -1; } } +EXPORT_SYMBOL(nfc_hci_result_to_errno); static void nfc_hci_msg_tx_work(struct work_struct *work) { @@ -167,7 +170,7 @@ void nfc_hci_cmd_received(struct nfc_hci_dev *hdev, u8 pipe, u8 cmd, kfree_skb(skb); } -static u32 nfc_hci_sak_to_protocol(u8 sak) +u32 nfc_hci_sak_to_protocol(u8 sak) { switch (NFC_HCI_TYPE_A_SEL_PROT(sak)) { case NFC_HCI_TYPE_A_SEL_PROT_MIFARE: @@ -182,6 +185,7 @@ static u32 nfc_hci_sak_to_protocol(u8 sak) return 0xffffffff; } } +EXPORT_SYMBOL(nfc_hci_sak_to_protocol); int nfc_hci_target_discovered(struct nfc_hci_dev *hdev, u8 gate) { @@ -284,6 +288,12 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, struct sk_buff *skb) { int r = 0; + u8 gate = nfc_hci_pipe2gate(hdev, pipe); + + if (gate == 0xff) { + pr_err("Discarded event %x to unopened pipe %x\n", event, pipe); + goto exit; + } switch (event) { case NFC_HCI_EVT_TARGET_DISCOVERED: @@ -307,14 +317,11 @@ void nfc_hci_event_received(struct nfc_hci_dev *hdev, u8 pipe, u8 event, goto exit; } - r = nfc_hci_target_discovered(hdev, - nfc_hci_pipe2gate(hdev, pipe)); + r = nfc_hci_target_discovered(hdev, gate); break; default: if (hdev->ops->event_received) { - hdev->ops->event_received(hdev, - nfc_hci_pipe2gate(hdev, pipe), - event, skb); + hdev->ops->event_received(hdev, gate, event, skb); return; } @@ -419,6 +426,10 @@ static int hci_dev_version(struct nfc_hci_dev *hdev) r = nfc_hci_get_param(hdev, NFC_HCI_ID_MGMT_GATE, NFC_HCI_ID_MGMT_VERSION_SW, &skb); + if (r == -EOPNOTSUPP) { + pr_info("Software/Hardware info not available\n"); + return 0; + } if (r < 0) return r; diff --git a/net/nfc/llcp/commands.c b/net/nfc/llcp/commands.c index ed2d17312d6..df24be48d4d 100644 --- a/net/nfc/llcp/commands.c +++ b/net/nfc/llcp/commands.c @@ -528,6 +528,23 @@ int nfc_llcp_send_i_frame(struct nfc_llcp_sock *sock, if (local == NULL) return -ENODEV; + /* Remote is ready but has not acknowledged our frames */ + if((sock->remote_ready && + skb_queue_len(&sock->tx_pending_queue) >= sock->rw && + skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) { + pr_err("Pending queue is full %d frames\n", + skb_queue_len(&sock->tx_pending_queue)); + return -ENOBUFS; + } + + /* Remote is not ready and we've been queueing enough frames */ + if ((!sock->remote_ready && + skb_queue_len(&sock->tx_queue) >= 2 * sock->rw)) { + pr_err("Tx queue is full %d frames\n", + skb_queue_len(&sock->tx_queue)); + return -ENOBUFS; + } + msg_data = kzalloc(len, GFP_KERNEL); if (msg_data == NULL) return -ENOMEM; @@ -579,7 +596,7 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, struct sk_buff *pdu; struct nfc_llcp_local *local; size_t frag_len = 0, remaining_len; - u8 *msg_ptr; + u8 *msg_ptr, *msg_data; int err; pr_debug("Send UI frame len %zd\n", len); @@ -588,8 +605,17 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, if (local == NULL) return -ENODEV; + msg_data = kzalloc(len, GFP_KERNEL); + if (msg_data == NULL) + return -ENOMEM; + + if (memcpy_fromiovec(msg_data, msg->msg_iov, len)) { + kfree(msg_data); + return -EFAULT; + } + remaining_len = len; - msg_ptr = (u8 *) msg->msg_iov; + msg_ptr = msg_data; while (remaining_len > 0) { @@ -616,6 +642,8 @@ int nfc_llcp_send_ui_frame(struct nfc_llcp_sock *sock, u8 ssap, u8 dsap, msg_ptr += frag_len; } + kfree(msg_data); + return len; } diff --git a/net/nfc/llcp/llcp.c b/net/nfc/llcp/llcp.c index 2e9ddf34c09..2df87056c6d 100644 --- a/net/nfc/llcp/llcp.c +++ b/net/nfc/llcp/llcp.c @@ -656,6 +656,8 @@ static void nfc_llcp_tx_work(struct work_struct *work) if (llcp_sock == NULL && nfc_llcp_ptype(skb) == LLCP_PDU_I) { nfc_llcp_send_symm(local->dev); } else { + struct sk_buff *copy_skb = NULL; + u8 ptype = nfc_llcp_ptype(skb); int ret; pr_debug("Sending pending skb\n"); @@ -663,22 +665,29 @@ static void nfc_llcp_tx_work(struct work_struct *work) DUMP_PREFIX_OFFSET, 16, 1, skb->data, skb->len, true); + if (ptype == LLCP_PDU_I) + copy_skb = skb_copy(skb, GFP_ATOMIC); + nfc_llcp_send_to_raw_sock(local, skb, NFC_LLCP_DIRECTION_TX); ret = nfc_data_exchange(local->dev, local->target_idx, skb, nfc_llcp_recv, local); - if (!ret && nfc_llcp_ptype(skb) == LLCP_PDU_I) { - skb = skb_get(skb); - skb_queue_tail(&llcp_sock->tx_pending_queue, - skb); + if (ret) { + kfree_skb(copy_skb); + goto out; } + + if (ptype == LLCP_PDU_I && copy_skb) + skb_queue_tail(&llcp_sock->tx_pending_queue, + copy_skb); } } else { nfc_llcp_send_symm(local->dev); } +out: mod_timer(&local->link_timer, jiffies + msecs_to_jiffies(2 * local->remote_lto)); } diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 08114478cb8..ac2defeeba8 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -28,6 +28,7 @@ #include <linux/if_arp.h> #include <linux/if_vlan.h> #include <net/ip.h> +#include <net/ipv6.h> #include <net/checksum.h> #include <net/dsfield.h> @@ -162,6 +163,53 @@ static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, *addr = new_addr; } +static void update_ipv6_checksum(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4]) +{ + int transport_len = skb->len - skb_transport_offset(skb); + + if (l4_proto == IPPROTO_TCP) { + if (likely(transport_len >= sizeof(struct tcphdr))) + inet_proto_csum_replace16(&tcp_hdr(skb)->check, skb, + addr, new_addr, 1); + } else if (l4_proto == IPPROTO_UDP) { + if (likely(transport_len >= sizeof(struct udphdr))) { + struct udphdr *uh = udp_hdr(skb); + + if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { + inet_proto_csum_replace16(&uh->check, skb, + addr, new_addr, 1); + if (!uh->check) + uh->check = CSUM_MANGLED_0; + } + } + } +} + +static void set_ipv6_addr(struct sk_buff *skb, u8 l4_proto, + __be32 addr[4], const __be32 new_addr[4], + bool recalculate_csum) +{ + if (recalculate_csum) + update_ipv6_checksum(skb, l4_proto, addr, new_addr); + + skb->rxhash = 0; + memcpy(addr, new_addr, sizeof(__be32[4])); +} + +static void set_ipv6_tc(struct ipv6hdr *nh, u8 tc) +{ + nh->priority = tc >> 4; + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0x0F) | ((tc & 0x0F) << 4); +} + +static void set_ipv6_fl(struct ipv6hdr *nh, u32 fl) +{ + nh->flow_lbl[0] = (nh->flow_lbl[0] & 0xF0) | (fl & 0x000F0000) >> 16; + nh->flow_lbl[1] = (fl & 0x0000FF00) >> 8; + nh->flow_lbl[2] = fl & 0x000000FF; +} + static void set_ip_ttl(struct sk_buff *skb, struct iphdr *nh, u8 new_ttl) { csum_replace2(&nh->check, htons(nh->ttl << 8), htons(new_ttl << 8)); @@ -195,6 +243,47 @@ static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key) return 0; } +static int set_ipv6(struct sk_buff *skb, const struct ovs_key_ipv6 *ipv6_key) +{ + struct ipv6hdr *nh; + int err; + __be32 *saddr; + __be32 *daddr; + + err = make_writable(skb, skb_network_offset(skb) + + sizeof(struct ipv6hdr)); + if (unlikely(err)) + return err; + + nh = ipv6_hdr(skb); + saddr = (__be32 *)&nh->saddr; + daddr = (__be32 *)&nh->daddr; + + if (memcmp(ipv6_key->ipv6_src, saddr, sizeof(ipv6_key->ipv6_src))) + set_ipv6_addr(skb, ipv6_key->ipv6_proto, saddr, + ipv6_key->ipv6_src, true); + + if (memcmp(ipv6_key->ipv6_dst, daddr, sizeof(ipv6_key->ipv6_dst))) { + unsigned int offset = 0; + int flags = IP6_FH_F_SKIP_RH; + bool recalc_csum = true; + + if (ipv6_ext_hdr(nh->nexthdr)) + recalc_csum = ipv6_find_hdr(skb, &offset, + NEXTHDR_ROUTING, NULL, + &flags) != NEXTHDR_ROUTING; + + set_ipv6_addr(skb, ipv6_key->ipv6_proto, daddr, + ipv6_key->ipv6_dst, recalc_csum); + } + + set_ipv6_tc(nh, ipv6_key->ipv6_tclass); + set_ipv6_fl(nh, ntohl(ipv6_key->ipv6_label)); + nh->hop_limit = ipv6_key->ipv6_hlimit; + + return 0; +} + /* Must follow make_writable() since that can move the skb data. */ static void set_tp_port(struct sk_buff *skb, __be16 *port, __be16 new_port, __sum16 *check) @@ -339,6 +428,10 @@ static int execute_set_action(struct sk_buff *skb, skb->priority = nla_get_u32(nested_attr); break; + case OVS_KEY_ATTR_SKB_MARK: + skb->mark = nla_get_u32(nested_attr); + break; + case OVS_KEY_ATTR_ETHERNET: err = set_eth_addr(skb, nla_data(nested_attr)); break; @@ -347,6 +440,10 @@ static int execute_set_action(struct sk_buff *skb, err = set_ipv4(skb, nla_data(nested_attr)); break; + case OVS_KEY_ATTR_IPV6: + err = set_ipv6(skb, nla_data(nested_attr)); + break; + case OVS_KEY_ATTR_TCP: err = set_tcp(skb, nla_data(nested_attr)); break; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 4c4b62ccc7d..f996db34324 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -208,7 +208,7 @@ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) int error; int key_len; - stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id()); + stats = this_cpu_ptr(dp->stats_percpu); /* Extract flow from 'skb' into 'key'. */ error = ovs_flow_extract(skb, p->port_no, &key, &key_len); @@ -282,7 +282,7 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, return 0; err: - stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id()); + stats = this_cpu_ptr(dp->stats_percpu); u64_stats_update_begin(&stats->sync); stats->n_lost++; @@ -479,8 +479,10 @@ static int validate_set(const struct nlattr *a, switch (key_type) { const struct ovs_key_ipv4 *ipv4_key; + const struct ovs_key_ipv6 *ipv6_key; case OVS_KEY_ATTR_PRIORITY: + case OVS_KEY_ATTR_SKB_MARK: case OVS_KEY_ATTR_ETHERNET: break; @@ -500,6 +502,25 @@ static int validate_set(const struct nlattr *a, break; + case OVS_KEY_ATTR_IPV6: + if (flow_key->eth.type != htons(ETH_P_IPV6)) + return -EINVAL; + + if (!flow_key->ip.proto) + return -EINVAL; + + ipv6_key = nla_data(ovs_key); + if (ipv6_key->ipv6_proto != flow_key->ip.proto) + return -EINVAL; + + if (ipv6_key->ipv6_frag != flow_key->ip.frag) + return -EINVAL; + + if (ntohl(ipv6_key->ipv6_label) & 0xFFF00000) + return -EINVAL; + + break; + case OVS_KEY_ATTR_TCP: if (flow_key->ip.proto != IPPROTO_TCP) return -EINVAL; @@ -675,6 +696,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) goto err_flow_free; err = ovs_flow_metadata_from_nlattrs(&flow->key.phy.priority, + &flow->key.phy.skb_mark, &flow->key.phy.in_port, a[OVS_PACKET_ATTR_KEY]); if (err) @@ -694,6 +716,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) OVS_CB(packet)->flow = flow; packet->priority = flow->key.phy.priority; + packet->mark = flow->key.phy.skb_mark; rcu_read_lock(); dp = get_dp(sock_net(skb->sk), ovs_header->dp_ifindex); diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 98c70630ad0..c3294cebc4f 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -604,6 +604,7 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, key->phy.priority = skb->priority; key->phy.in_port = in_port; + key->phy.skb_mark = skb->mark; skb_reset_mac_header(skb); @@ -689,7 +690,8 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, } } - } else if (key->eth.type == htons(ETH_P_ARP) && arphdr_ok(skb)) { + } else if ((key->eth.type == htons(ETH_P_ARP) || + key->eth.type == htons(ETH_P_RARP)) && arphdr_ok(skb)) { struct arp_eth_header *arp; arp = (struct arp_eth_header *)skb_network_header(skb); @@ -702,15 +704,11 @@ int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, /* We only match on the lower 8 bits of the opcode. */ if (ntohs(arp->ar_op) <= 0xff) key->ip.proto = ntohs(arp->ar_op); - - if (key->ip.proto == ARPOP_REQUEST - || key->ip.proto == ARPOP_REPLY) { - memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src)); - memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst)); - memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN); - memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN); - key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); - } + memcpy(&key->ipv4.addr.src, arp->ar_sip, sizeof(key->ipv4.addr.src)); + memcpy(&key->ipv4.addr.dst, arp->ar_tip, sizeof(key->ipv4.addr.dst)); + memcpy(key->ipv4.arp.sha, arp->ar_sha, ETH_ALEN); + memcpy(key->ipv4.arp.tha, arp->ar_tha, ETH_ALEN); + key_len = SW_FLOW_KEY_OFFSET(ipv4.arp); } } else if (key->eth.type == htons(ETH_P_IPV6)) { int nh_len; /* IPv6 Header + Extensions */ @@ -806,6 +804,7 @@ const int ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = { [OVS_KEY_ATTR_ENCAP] = -1, [OVS_KEY_ATTR_PRIORITY] = sizeof(u32), [OVS_KEY_ATTR_IN_PORT] = sizeof(u32), + [OVS_KEY_ATTR_SKB_MARK] = sizeof(u32), [OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet), [OVS_KEY_ATTR_VLAN] = sizeof(__be16), [OVS_KEY_ATTR_ETHERTYPE] = sizeof(__be16), @@ -991,6 +990,10 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, } else { swkey->phy.in_port = DP_MAX_PORTS; } + if (attrs & (1 << OVS_KEY_ATTR_SKB_MARK)) { + swkey->phy.skb_mark = nla_get_u32(a[OVS_KEY_ATTR_SKB_MARK]); + attrs &= ~(1 << OVS_KEY_ATTR_SKB_MARK); + } /* Data attributes. */ if (!(attrs & (1 << OVS_KEY_ATTR_ETHERNET))) @@ -1090,7 +1093,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, if (err) return err; } - } else if (swkey->eth.type == htons(ETH_P_ARP)) { + } else if (swkey->eth.type == htons(ETH_P_ARP) || + swkey->eth.type == htons(ETH_P_RARP)) { const struct ovs_key_arp *arp_key; if (!(attrs & (1 << OVS_KEY_ATTR_ARP))) @@ -1117,6 +1121,8 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, /** * ovs_flow_metadata_from_nlattrs - parses Netlink attributes into a flow key. + * @priority: receives the skb priority + * @mark: receives the skb mark * @in_port: receives the extracted input port. * @key: Netlink attribute holding nested %OVS_KEY_ATTR_* Netlink attribute * sequence. @@ -1126,7 +1132,7 @@ int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, * get the metadata, that is, the parts of the flow key that cannot be * extracted from the packet itself. */ -int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, +int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port, const struct nlattr *attr) { const struct nlattr *nla; @@ -1134,6 +1140,7 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, *in_port = DP_MAX_PORTS; *priority = 0; + *mark = 0; nla_for_each_nested(nla, attr, rem) { int type = nla_type(nla); @@ -1152,6 +1159,10 @@ int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, return -EINVAL; *in_port = nla_get_u32(nla); break; + + case OVS_KEY_ATTR_SKB_MARK: + *mark = nla_get_u32(nla); + break; } } } @@ -1173,6 +1184,10 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) nla_put_u32(skb, OVS_KEY_ATTR_IN_PORT, swkey->phy.in_port)) goto nla_put_failure; + if (swkey->phy.skb_mark && + nla_put_u32(skb, OVS_KEY_ATTR_SKB_MARK, swkey->phy.skb_mark)) + goto nla_put_failure; + nla = nla_reserve(skb, OVS_KEY_ATTR_ETHERNET, sizeof(*eth_key)); if (!nla) goto nla_put_failure; @@ -1226,7 +1241,8 @@ int ovs_flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) ipv6_key->ipv6_tclass = swkey->ip.tos; ipv6_key->ipv6_hlimit = swkey->ip.ttl; ipv6_key->ipv6_frag = swkey->ip.frag; - } else if (swkey->eth.type == htons(ETH_P_ARP)) { + } else if (swkey->eth.type == htons(ETH_P_ARP) || + swkey->eth.type == htons(ETH_P_RARP)) { struct ovs_key_arp *arp_key; nla = nla_reserve(skb, OVS_KEY_ATTR_ARP, sizeof(*arp_key)); diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index 14a324eb017..a7bb60ff3b5 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -43,6 +43,7 @@ struct sw_flow_actions { struct sw_flow_key { struct { u32 priority; /* Packet QoS priority. */ + u32 skb_mark; /* SKB mark. */ u16 in_port; /* Input switch port (or DP_MAX_PORTS). */ } phy; struct { @@ -144,6 +145,7 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies); * ------ --- ------ ----- * OVS_KEY_ATTR_PRIORITY 4 -- 4 8 * OVS_KEY_ATTR_IN_PORT 4 -- 4 8 + * OVS_KEY_ATTR_SKB_MARK 4 -- 4 8 * OVS_KEY_ATTR_ETHERNET 12 -- 4 16 * OVS_KEY_ATTR_ETHERTYPE 2 2 4 8 (outer VLAN ethertype) * OVS_KEY_ATTR_8021Q 4 -- 4 8 @@ -153,14 +155,14 @@ u64 ovs_flow_used_time(unsigned long flow_jiffies); * OVS_KEY_ATTR_ICMPV6 2 2 4 8 * OVS_KEY_ATTR_ND 28 -- 4 32 * ------------------------------------------------- - * total 144 + * total 152 */ -#define FLOW_BUFSIZE 144 +#define FLOW_BUFSIZE 152 int ovs_flow_to_nlattrs(const struct sw_flow_key *, struct sk_buff *); int ovs_flow_from_nlattrs(struct sw_flow_key *swkey, int *key_lenp, const struct nlattr *); -int ovs_flow_metadata_from_nlattrs(u32 *priority, u16 *in_port, +int ovs_flow_metadata_from_nlattrs(u32 *priority, u32 *mark, u16 *in_port, const struct nlattr *); #define MAX_ACTIONS_BUFSIZE (16 * 1024) diff --git a/net/openvswitch/vport-netdev.c b/net/openvswitch/vport-netdev.c index 3c1e58ba714..a9327e2e48c 100644 --- a/net/openvswitch/vport-netdev.c +++ b/net/openvswitch/vport-netdev.c @@ -114,6 +114,15 @@ error: return ERR_PTR(err); } +static void free_port_rcu(struct rcu_head *rcu) +{ + struct netdev_vport *netdev_vport = container_of(rcu, + struct netdev_vport, rcu); + + dev_put(netdev_vport->dev); + ovs_vport_free(vport_from_priv(netdev_vport)); +} + static void netdev_destroy(struct vport *vport) { struct netdev_vport *netdev_vport = netdev_vport_priv(vport); @@ -122,10 +131,7 @@ static void netdev_destroy(struct vport *vport) netdev_rx_handler_unregister(netdev_vport->dev); dev_set_promiscuity(netdev_vport->dev, -1); - synchronize_rcu(); - - dev_put(netdev_vport->dev); - ovs_vport_free(vport); + call_rcu(&netdev_vport->rcu, free_port_rcu); } const char *ovs_netdev_get_name(const struct vport *vport) @@ -158,7 +164,7 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb) if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) { net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n", - ovs_dp_name(vport->dp), + netdev_vport->dev->name, packet_length(skb), mtu); goto error; } diff --git a/net/openvswitch/vport-netdev.h b/net/openvswitch/vport-netdev.h index f7072a25c60..6478079b341 100644 --- a/net/openvswitch/vport-netdev.h +++ b/net/openvswitch/vport-netdev.h @@ -20,12 +20,15 @@ #define VPORT_NETDEV_H 1 #include <linux/netdevice.h> +#include <linux/rcupdate.h> #include "vport.h" struct vport *ovs_netdev_get_vport(struct net_device *dev); struct netdev_vport { + struct rcu_head rcu; + struct net_device *dev; }; diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 03779e8a262..70af0bedbac 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c @@ -333,8 +333,7 @@ void ovs_vport_receive(struct vport *vport, struct sk_buff *skb) { struct vport_percpu_stats *stats; - stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id()); - + stats = this_cpu_ptr(vport->percpu_stats); u64_stats_update_begin(&stats->sync); stats->rx_packets++; stats->rx_bytes += skb->len; @@ -359,7 +358,7 @@ int ovs_vport_send(struct vport *vport, struct sk_buff *skb) if (likely(sent)) { struct vport_percpu_stats *stats; - stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id()); + stats = this_cpu_ptr(vport->percpu_stats); u64_stats_update_begin(&stats->sync); stats->tx_packets++; diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 9687fa1c227..6ed37652a4c 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1,7 +1,8 @@ /* - * net/sched/sch_qfq.c Quick Fair Queueing Scheduler. + * net/sched/sch_qfq.c Quick Fair Queueing Plus Scheduler. * * Copyright (c) 2009 Fabio Checconi, Luigi Rizzo, and Paolo Valente. + * Copyright (c) 2012 Paolo Valente. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -19,12 +20,18 @@ #include <net/pkt_cls.h> -/* Quick Fair Queueing - =================== +/* Quick Fair Queueing Plus + ======================== Sources: - Fabio Checconi, Luigi Rizzo, and Paolo Valente: "QFQ: Efficient + [1] Paolo Valente, + "Reducing the Execution Time of Fair-Queueing Schedulers." + http://algo.ing.unimo.it/people/paolo/agg-sched/agg-sched.pdf + + Sources for QFQ: + + [2] Fabio Checconi, Luigi Rizzo, and Paolo Valente: "QFQ: Efficient Packet Scheduling with Tight Bandwidth Distribution Guarantees." See also: @@ -33,6 +40,20 @@ /* + QFQ+ divides classes into aggregates of at most MAX_AGG_CLASSES + classes. Each aggregate is timestamped with a virtual start time S + and a virtual finish time F, and scheduled according to its + timestamps. S and F are computed as a function of a system virtual + time function V. The classes within each aggregate are instead + scheduled with DRR. + + To speed up operations, QFQ+ divides also aggregates into a limited + number of groups. Which group a class belongs to depends on the + ratio between the maximum packet length for the class and the weight + of the class. Groups have their own S and F. In the end, QFQ+ + schedules groups, then aggregates within groups, then classes within + aggregates. See [1] and [2] for a full description. + Virtual time computations. S, F and V are all computed in fixed point arithmetic with @@ -76,27 +97,28 @@ #define QFQ_MAX_SLOTS 32 /* - * Shifts used for class<->group mapping. We allow class weights that are - * in the range [1, 2^MAX_WSHIFT], and we try to map each class i to the + * Shifts used for aggregate<->group mapping. We allow class weights that are + * in the range [1, 2^MAX_WSHIFT], and we try to map each aggregate i to the * group with the smallest index that can support the L_i / r_i configured - * for the class. + * for the classes in the aggregate. * * grp->index is the index of the group; and grp->slot_shift * is the shift for the corresponding (scaled) sigma_i. */ #define QFQ_MAX_INDEX 24 -#define QFQ_MAX_WSHIFT 12 +#define QFQ_MAX_WSHIFT 10 -#define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT) -#define QFQ_MAX_WSUM (16*QFQ_MAX_WEIGHT) +#define QFQ_MAX_WEIGHT (1<<QFQ_MAX_WSHIFT) /* see qfq_slot_insert */ +#define QFQ_MAX_WSUM (64*QFQ_MAX_WEIGHT) #define FRAC_BITS 30 /* fixed point arithmetic */ #define ONE_FP (1UL << FRAC_BITS) #define IWSUM (ONE_FP/QFQ_MAX_WSUM) #define QFQ_MTU_SHIFT 16 /* to support TSO/GSO */ -#define QFQ_MIN_SLOT_SHIFT (FRAC_BITS + QFQ_MTU_SHIFT - QFQ_MAX_INDEX) -#define QFQ_MIN_LMAX 256 /* min possible lmax for a class */ +#define QFQ_MIN_LMAX 512 /* see qfq_slot_insert */ + +#define QFQ_MAX_AGG_CLASSES 8 /* max num classes per aggregate allowed */ /* * Possible group states. These values are used as indexes for the bitmaps @@ -106,6 +128,8 @@ enum qfq_state { ER, IR, EB, IB, QFQ_MAX_STATE }; struct qfq_group; +struct qfq_aggregate; + struct qfq_class { struct Qdisc_class_common common; @@ -116,7 +140,12 @@ struct qfq_class { struct gnet_stats_queue qstats; struct gnet_stats_rate_est rate_est; struct Qdisc *qdisc; + struct list_head alist; /* Link for active-classes list. */ + struct qfq_aggregate *agg; /* Parent aggregate. */ + int deficit; /* DRR deficit counter. */ +}; +struct qfq_aggregate { struct hlist_node next; /* Link for the slot list. */ u64 S, F; /* flow timestamps (exact) */ @@ -127,8 +156,18 @@ struct qfq_class { struct qfq_group *grp; /* these are copied from the flowset. */ - u32 inv_w; /* ONE_FP/weight */ - u32 lmax; /* Max packet size for this flow. */ + u32 class_weight; /* Weight of each class in this aggregate. */ + /* Max pkt size for the classes in this aggregate, DRR quantum. */ + int lmax; + + u32 inv_w; /* ONE_FP/(sum of weights of classes in aggr.). */ + u32 budgetmax; /* Max budget for this aggregate. */ + u32 initial_budget, budget; /* Initial and current budget. */ + + int num_classes; /* Number of classes in this aggr. */ + struct list_head active; /* DRR queue of active classes. */ + + struct hlist_node nonfull_next; /* See nonfull_aggs in qfq_sched. */ }; struct qfq_group { @@ -138,7 +177,7 @@ struct qfq_group { unsigned int front; /* Index of the front slot. */ unsigned long full_slots; /* non-empty slots */ - /* Array of RR lists of active classes. */ + /* Array of RR lists of active aggregates. */ struct hlist_head slots[QFQ_MAX_SLOTS]; }; @@ -146,13 +185,28 @@ struct qfq_sched { struct tcf_proto *filter_list; struct Qdisc_class_hash clhash; - u64 V; /* Precise virtual time. */ - u32 wsum; /* weight sum */ + u64 oldV, V; /* Precise virtual times. */ + struct qfq_aggregate *in_serv_agg; /* Aggregate being served. */ + u32 num_active_agg; /* Num. of active aggregates */ + u32 wsum; /* weight sum */ unsigned long bitmaps[QFQ_MAX_STATE]; /* Group bitmaps. */ struct qfq_group groups[QFQ_MAX_INDEX + 1]; /* The groups. */ + u32 min_slot_shift; /* Index of the group-0 bit in the bitmaps. */ + + u32 max_agg_classes; /* Max number of classes per aggr. */ + struct hlist_head nonfull_aggs; /* Aggs with room for more classes. */ }; +/* + * Possible reasons why the timestamps of an aggregate are updated + * enqueue: the aggregate switches from idle to active and must scheduled + * for service + * requeue: the aggregate finishes its budget, so it stops being served and + * must be rescheduled for service + */ +enum update_reason {enqueue, requeue}; + static struct qfq_class *qfq_find_class(struct Qdisc *sch, u32 classid) { struct qfq_sched *q = qdisc_priv(sch); @@ -182,18 +236,18 @@ static const struct nla_policy qfq_policy[TCA_QFQ_MAX + 1] = { * index = log_2(maxlen/weight) but we need to apply the scaling. * This is used only once at flow creation. */ -static int qfq_calc_index(u32 inv_w, unsigned int maxlen) +static int qfq_calc_index(u32 inv_w, unsigned int maxlen, u32 min_slot_shift) { u64 slot_size = (u64)maxlen * inv_w; unsigned long size_map; int index = 0; - size_map = slot_size >> QFQ_MIN_SLOT_SHIFT; + size_map = slot_size >> min_slot_shift; if (!size_map) goto out; index = __fls(size_map) + 1; /* basically a log_2 */ - index -= !(slot_size - (1ULL << (index + QFQ_MIN_SLOT_SHIFT - 1))); + index -= !(slot_size - (1ULL << (index + min_slot_shift - 1))); if (index < 0) index = 0; @@ -204,66 +258,150 @@ out: return index; } -/* Length of the next packet (0 if the queue is empty). */ -static unsigned int qdisc_peek_len(struct Qdisc *sch) +static void qfq_deactivate_agg(struct qfq_sched *, struct qfq_aggregate *); +static void qfq_activate_agg(struct qfq_sched *, struct qfq_aggregate *, + enum update_reason); + +static void qfq_init_agg(struct qfq_sched *q, struct qfq_aggregate *agg, + u32 lmax, u32 weight) { - struct sk_buff *skb; + INIT_LIST_HEAD(&agg->active); + hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs); + + agg->lmax = lmax; + agg->class_weight = weight; +} + +static struct qfq_aggregate *qfq_find_agg(struct qfq_sched *q, + u32 lmax, u32 weight) +{ + struct qfq_aggregate *agg; + struct hlist_node *n; + + hlist_for_each_entry(agg, n, &q->nonfull_aggs, nonfull_next) + if (agg->lmax == lmax && agg->class_weight == weight) + return agg; + + return NULL; +} + - skb = sch->ops->peek(sch); - return skb ? qdisc_pkt_len(skb) : 0; +/* Update aggregate as a function of the new number of classes. */ +static void qfq_update_agg(struct qfq_sched *q, struct qfq_aggregate *agg, + int new_num_classes) +{ + u32 new_agg_weight; + + if (new_num_classes == q->max_agg_classes) + hlist_del_init(&agg->nonfull_next); + + if (agg->num_classes > new_num_classes && + new_num_classes == q->max_agg_classes - 1) /* agg no more full */ + hlist_add_head(&agg->nonfull_next, &q->nonfull_aggs); + + agg->budgetmax = new_num_classes * agg->lmax; + new_agg_weight = agg->class_weight * new_num_classes; + agg->inv_w = ONE_FP/new_agg_weight; + + if (agg->grp == NULL) { + int i = qfq_calc_index(agg->inv_w, agg->budgetmax, + q->min_slot_shift); + agg->grp = &q->groups[i]; + } + + q->wsum += + (int) agg->class_weight * (new_num_classes - agg->num_classes); + + agg->num_classes = new_num_classes; +} + +/* Add class to aggregate. */ +static void qfq_add_to_agg(struct qfq_sched *q, + struct qfq_aggregate *agg, + struct qfq_class *cl) +{ + cl->agg = agg; + + qfq_update_agg(q, agg, agg->num_classes+1); + if (cl->qdisc->q.qlen > 0) { /* adding an active class */ + list_add_tail(&cl->alist, &agg->active); + if (list_first_entry(&agg->active, struct qfq_class, alist) == + cl && q->in_serv_agg != agg) /* agg was inactive */ + qfq_activate_agg(q, agg, enqueue); /* schedule agg */ + } } -static void qfq_deactivate_class(struct qfq_sched *, struct qfq_class *); -static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, - unsigned int len); +static struct qfq_aggregate *qfq_choose_next_agg(struct qfq_sched *); -static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl, - u32 lmax, u32 inv_w, int delta_w) +static void qfq_destroy_agg(struct qfq_sched *q, struct qfq_aggregate *agg) { - int i; + if (!hlist_unhashed(&agg->nonfull_next)) + hlist_del_init(&agg->nonfull_next); + if (q->in_serv_agg == agg) + q->in_serv_agg = qfq_choose_next_agg(q); + kfree(agg); +} - /* update qfq-specific data */ - cl->lmax = lmax; - cl->inv_w = inv_w; - i = qfq_calc_index(cl->inv_w, cl->lmax); +/* Deschedule class from within its parent aggregate. */ +static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl) +{ + struct qfq_aggregate *agg = cl->agg; - cl->grp = &q->groups[i]; - q->wsum += delta_w; + list_del(&cl->alist); /* remove from RR queue of the aggregate */ + if (list_empty(&agg->active)) /* agg is now inactive */ + qfq_deactivate_agg(q, agg); } -static void qfq_update_reactivate_class(struct qfq_sched *q, - struct qfq_class *cl, - u32 inv_w, u32 lmax, int delta_w) +/* Remove class from its parent aggregate. */ +static void qfq_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl) { - bool need_reactivation = false; - int i = qfq_calc_index(inv_w, lmax); + struct qfq_aggregate *agg = cl->agg; - if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) { - /* - * shift cl->F back, to not charge the - * class for the not-yet-served head - * packet - */ - cl->F = cl->S; - /* remove class from its slot in the old group */ - qfq_deactivate_class(q, cl); - need_reactivation = true; + cl->agg = NULL; + if (agg->num_classes == 1) { /* agg being emptied, destroy it */ + qfq_destroy_agg(q, agg); + return; } + qfq_update_agg(q, agg, agg->num_classes-1); +} - qfq_update_class_params(q, cl, lmax, inv_w, delta_w); +/* Deschedule class and remove it from its parent aggregate. */ +static void qfq_deact_rm_from_agg(struct qfq_sched *q, struct qfq_class *cl) +{ + if (cl->qdisc->q.qlen > 0) /* class is active */ + qfq_deactivate_class(q, cl); - if (need_reactivation) /* activate in new group */ - qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc)); + qfq_rm_from_agg(q, cl); } +/* Move class to a new aggregate, matching the new class weight and/or lmax */ +static int qfq_change_agg(struct Qdisc *sch, struct qfq_class *cl, u32 weight, + u32 lmax) +{ + struct qfq_sched *q = qdisc_priv(sch); + struct qfq_aggregate *new_agg = qfq_find_agg(q, lmax, weight); + + if (new_agg == NULL) { /* create new aggregate */ + new_agg = kzalloc(sizeof(*new_agg), GFP_ATOMIC); + if (new_agg == NULL) + return -ENOBUFS; + qfq_init_agg(q, new_agg, lmax, weight); + } + qfq_deact_rm_from_agg(q, cl); + qfq_add_to_agg(q, new_agg, cl); + + return 0; +} static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca, unsigned long *arg) { struct qfq_sched *q = qdisc_priv(sch); struct qfq_class *cl = (struct qfq_class *)*arg; + bool existing = false; struct nlattr *tb[TCA_QFQ_MAX + 1]; + struct qfq_aggregate *new_agg = NULL; u32 weight, lmax, inv_w; int err; int delta_w; @@ -286,15 +424,6 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, } else weight = 1; - inv_w = ONE_FP / weight; - weight = ONE_FP / inv_w; - delta_w = weight - (cl ? ONE_FP / cl->inv_w : 0); - if (q->wsum + delta_w > QFQ_MAX_WSUM) { - pr_notice("qfq: total weight out of range (%u + %u)\n", - delta_w, q->wsum); - return -EINVAL; - } - if (tb[TCA_QFQ_LMAX]) { lmax = nla_get_u32(tb[TCA_QFQ_LMAX]); if (lmax < QFQ_MIN_LMAX || lmax > (1UL << QFQ_MTU_SHIFT)) { @@ -304,7 +433,23 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, } else lmax = psched_mtu(qdisc_dev(sch)); - if (cl != NULL) { + inv_w = ONE_FP / weight; + weight = ONE_FP / inv_w; + + if (cl != NULL && + lmax == cl->agg->lmax && + weight == cl->agg->class_weight) + return 0; /* nothing to change */ + + delta_w = weight - (cl ? cl->agg->class_weight : 0); + + if (q->wsum + delta_w > QFQ_MAX_WSUM) { + pr_notice("qfq: total weight out of range (%d + %u)\n", + delta_w, q->wsum); + return -EINVAL; + } + + if (cl != NULL) { /* modify existing class */ if (tca[TCA_RATE]) { err = gen_replace_estimator(&cl->bstats, &cl->rate_est, qdisc_root_sleeping_lock(sch), @@ -312,25 +457,18 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (err) return err; } - - if (lmax == cl->lmax && inv_w == cl->inv_w) - return 0; /* nothing to update */ - - sch_tree_lock(sch); - qfq_update_reactivate_class(q, cl, inv_w, lmax, delta_w); - sch_tree_unlock(sch); - - return 0; + existing = true; + goto set_change_agg; } + /* create and init new class */ cl = kzalloc(sizeof(struct qfq_class), GFP_KERNEL); if (cl == NULL) return -ENOBUFS; cl->refcnt = 1; cl->common.classid = classid; - - qfq_update_class_params(q, cl, lmax, inv_w, delta_w); + cl->deficit = lmax; cl->qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid); @@ -341,11 +479,8 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, err = gen_new_estimator(&cl->bstats, &cl->rate_est, qdisc_root_sleeping_lock(sch), tca[TCA_RATE]); - if (err) { - qdisc_destroy(cl->qdisc); - kfree(cl); - return err; - } + if (err) + goto destroy_class; } sch_tree_lock(sch); @@ -354,19 +489,39 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, qdisc_class_hash_grow(sch, &q->clhash); +set_change_agg: + sch_tree_lock(sch); + new_agg = qfq_find_agg(q, lmax, weight); + if (new_agg == NULL) { /* create new aggregate */ + sch_tree_unlock(sch); + new_agg = kzalloc(sizeof(*new_agg), GFP_KERNEL); + if (new_agg == NULL) { + err = -ENOBUFS; + gen_kill_estimator(&cl->bstats, &cl->rate_est); + goto destroy_class; + } + sch_tree_lock(sch); + qfq_init_agg(q, new_agg, lmax, weight); + } + if (existing) + qfq_deact_rm_from_agg(q, cl); + qfq_add_to_agg(q, new_agg, cl); + sch_tree_unlock(sch); + *arg = (unsigned long)cl; return 0; + +destroy_class: + qdisc_destroy(cl->qdisc); + kfree(cl); + return err; } static void qfq_destroy_class(struct Qdisc *sch, struct qfq_class *cl) { struct qfq_sched *q = qdisc_priv(sch); - if (cl->inv_w) { - q->wsum -= ONE_FP / cl->inv_w; - cl->inv_w = 0; - } - + qfq_rm_from_agg(q, cl); gen_kill_estimator(&cl->bstats, &cl->rate_est); qdisc_destroy(cl->qdisc); kfree(cl); @@ -481,8 +636,8 @@ static int qfq_dump_class(struct Qdisc *sch, unsigned long arg, nest = nla_nest_start(skb, TCA_OPTIONS); if (nest == NULL) goto nla_put_failure; - if (nla_put_u32(skb, TCA_QFQ_WEIGHT, ONE_FP/cl->inv_w) || - nla_put_u32(skb, TCA_QFQ_LMAX, cl->lmax)) + if (nla_put_u32(skb, TCA_QFQ_WEIGHT, cl->agg->class_weight) || + nla_put_u32(skb, TCA_QFQ_LMAX, cl->agg->lmax)) goto nla_put_failure; return nla_nest_end(skb, nest); @@ -500,8 +655,8 @@ static int qfq_dump_class_stats(struct Qdisc *sch, unsigned long arg, memset(&xstats, 0, sizeof(xstats)); cl->qdisc->qstats.qlen = cl->qdisc->q.qlen; - xstats.weight = ONE_FP/cl->inv_w; - xstats.lmax = cl->lmax; + xstats.weight = cl->agg->class_weight; + xstats.lmax = cl->agg->lmax; if (gnet_stats_copy_basic(d, &cl->bstats) < 0 || gnet_stats_copy_rate_est(d, &cl->bstats, &cl->rate_est) < 0 || @@ -652,16 +807,16 @@ static void qfq_unblock_groups(struct qfq_sched *q, int index, u64 old_F) * perhaps * old_V ^= q->V; - old_V >>= QFQ_MIN_SLOT_SHIFT; + old_V >>= q->min_slot_shift; if (old_V) { ... } * */ -static void qfq_make_eligible(struct qfq_sched *q, u64 old_V) +static void qfq_make_eligible(struct qfq_sched *q) { - unsigned long vslot = q->V >> QFQ_MIN_SLOT_SHIFT; - unsigned long old_vslot = old_V >> QFQ_MIN_SLOT_SHIFT; + unsigned long vslot = q->V >> q->min_slot_shift; + unsigned long old_vslot = q->oldV >> q->min_slot_shift; if (vslot != old_vslot) { unsigned long mask = (1UL << fls(vslot ^ old_vslot)) - 1; @@ -672,34 +827,38 @@ static void qfq_make_eligible(struct qfq_sched *q, u64 old_V) /* - * If the weight and lmax (max_pkt_size) of the classes do not change, - * then QFQ guarantees that the slot index is never higher than - * 2 + ((1<<QFQ_MTU_SHIFT)/QFQ_MIN_LMAX) * (QFQ_MAX_WEIGHT/QFQ_MAX_WSUM). + * The index of the slot in which the aggregate is to be inserted must + * not be higher than QFQ_MAX_SLOTS-2. There is a '-2' and not a '-1' + * because the start time of the group may be moved backward by one + * slot after the aggregate has been inserted, and this would cause + * non-empty slots to be right-shifted by one position. * - * With the current values of the above constants, the index is - * then guaranteed to never be higher than 2 + 256 * (1 / 16) = 18. + * If the weight and lmax (max_pkt_size) of the classes do not change, + * then QFQ+ does meet the above contraint according to the current + * values of its parameters. In fact, if the weight and lmax of the + * classes do not change, then, from the theory, QFQ+ guarantees that + * the slot index is never higher than + * 2 + QFQ_MAX_AGG_CLASSES * ((1<<QFQ_MTU_SHIFT)/QFQ_MIN_LMAX) * + * (QFQ_MAX_WEIGHT/QFQ_MAX_WSUM) = 2 + 8 * 128 * (1 / 64) = 18 * * When the weight of a class is increased or the lmax of the class is - * decreased, a new class with smaller slot size may happen to be - * activated. The activation of this class should be properly delayed - * to when the service of the class has finished in the ideal system - * tracked by QFQ. If the activation of the class is not delayed to - * this reference time instant, then this class may be unjustly served - * before other classes waiting for service. This may cause - * (unfrequently) the above bound to the slot index to be violated for - * some of these unlucky classes. + * decreased, a new aggregate with smaller slot size than the original + * parent aggregate of the class may happen to be activated. The + * activation of this aggregate should be properly delayed to when the + * service of the class has finished in the ideal system tracked by + * QFQ+. If the activation of the aggregate is not delayed to this + * reference time instant, then this aggregate may be unjustly served + * before other aggregates waiting for service. This may cause the + * above bound to the slot index to be violated for some of these + * unlucky aggregates. * - * Instead of delaying the activation of the new class, which is quite - * complex, the following inaccurate but simple solution is used: if - * the slot index is higher than QFQ_MAX_SLOTS-2, then the timestamps - * of the class are shifted backward so as to let the slot index - * become equal to QFQ_MAX_SLOTS-2. This threshold is used because, if - * the slot index is above it, then the data structure implementing - * the bucket list either gets immediately corrupted or may get - * corrupted on a possible next packet arrival that causes the start - * time of the group to be shifted backward. + * Instead of delaying the activation of the new aggregate, which is + * quite complex, the following inaccurate but simple solution is used: + * if the slot index is higher than QFQ_MAX_SLOTS-2, then the + * timestamps of the aggregate are shifted backward so as to let the + * slot index become equal to QFQ_MAX_SLOTS-2. */ -static void qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, +static void qfq_slot_insert(struct qfq_group *grp, struct qfq_aggregate *agg, u64 roundedS) { u64 slot = (roundedS - grp->S) >> grp->slot_shift; @@ -708,22 +867,22 @@ static void qfq_slot_insert(struct qfq_group *grp, struct qfq_class *cl, if (unlikely(slot > QFQ_MAX_SLOTS - 2)) { u64 deltaS = roundedS - grp->S - ((u64)(QFQ_MAX_SLOTS - 2)<<grp->slot_shift); - cl->S -= deltaS; - cl->F -= deltaS; + agg->S -= deltaS; + agg->F -= deltaS; slot = QFQ_MAX_SLOTS - 2; } i = (grp->front + slot) % QFQ_MAX_SLOTS; - hlist_add_head(&cl->next, &grp->slots[i]); + hlist_add_head(&agg->next, &grp->slots[i]); __set_bit(slot, &grp->full_slots); } /* Maybe introduce hlist_first_entry?? */ -static struct qfq_class *qfq_slot_head(struct qfq_group *grp) +static struct qfq_aggregate *qfq_slot_head(struct qfq_group *grp) { return hlist_entry(grp->slots[grp->front].first, - struct qfq_class, next); + struct qfq_aggregate, next); } /* @@ -731,20 +890,20 @@ static struct qfq_class *qfq_slot_head(struct qfq_group *grp) */ static void qfq_front_slot_remove(struct qfq_group *grp) { - struct qfq_class *cl = qfq_slot_head(grp); + struct qfq_aggregate *agg = qfq_slot_head(grp); - BUG_ON(!cl); - hlist_del(&cl->next); + BUG_ON(!agg); + hlist_del(&agg->next); if (hlist_empty(&grp->slots[grp->front])) __clear_bit(0, &grp->full_slots); } /* - * Returns the first full queue in a group. As a side effect, - * adjust the bucket list so the first non-empty bucket is at - * position 0 in full_slots. + * Returns the first aggregate in the first non-empty bucket of the + * group. As a side effect, adjusts the bucket list so the first + * non-empty bucket is at position 0 in full_slots. */ -static struct qfq_class *qfq_slot_scan(struct qfq_group *grp) +static struct qfq_aggregate *qfq_slot_scan(struct qfq_group *grp) { unsigned int i; @@ -780,7 +939,7 @@ static void qfq_slot_rotate(struct qfq_group *grp, u64 roundedS) grp->front = (grp->front - i) % QFQ_MAX_SLOTS; } -static void qfq_update_eligible(struct qfq_sched *q, u64 old_V) +static void qfq_update_eligible(struct qfq_sched *q) { struct qfq_group *grp; unsigned long ineligible; @@ -792,137 +951,226 @@ static void qfq_update_eligible(struct qfq_sched *q, u64 old_V) if (qfq_gt(grp->S, q->V)) q->V = grp->S; } - qfq_make_eligible(q, old_V); + qfq_make_eligible(q); } } -/* - * Updates the class, returns true if also the group needs to be updated. - */ -static bool qfq_update_class(struct qfq_group *grp, struct qfq_class *cl) +/* Dequeue head packet of the head class in the DRR queue of the aggregate. */ +static void agg_dequeue(struct qfq_aggregate *agg, + struct qfq_class *cl, unsigned int len) { - unsigned int len = qdisc_peek_len(cl->qdisc); + qdisc_dequeue_peeked(cl->qdisc); - cl->S = cl->F; - if (!len) - qfq_front_slot_remove(grp); /* queue is empty */ - else { - u64 roundedS; + cl->deficit -= (int) len; - cl->F = cl->S + (u64)len * cl->inv_w; - roundedS = qfq_round_down(cl->S, grp->slot_shift); - if (roundedS == grp->S) - return false; - - qfq_front_slot_remove(grp); - qfq_slot_insert(grp, cl, roundedS); + if (cl->qdisc->q.qlen == 0) /* no more packets, remove from list */ + list_del(&cl->alist); + else if (cl->deficit < qdisc_pkt_len(cl->qdisc->ops->peek(cl->qdisc))) { + cl->deficit += agg->lmax; + list_move_tail(&cl->alist, &agg->active); } +} + +static inline struct sk_buff *qfq_peek_skb(struct qfq_aggregate *agg, + struct qfq_class **cl, + unsigned int *len) +{ + struct sk_buff *skb; - return true; + *cl = list_first_entry(&agg->active, struct qfq_class, alist); + skb = (*cl)->qdisc->ops->peek((*cl)->qdisc); + if (skb == NULL) + WARN_ONCE(1, "qfq_dequeue: non-workconserving leaf\n"); + else + *len = qdisc_pkt_len(skb); + + return skb; +} + +/* Update F according to the actual service received by the aggregate. */ +static inline void charge_actual_service(struct qfq_aggregate *agg) +{ + /* compute the service received by the aggregate */ + u32 service_received = agg->initial_budget - agg->budget; + + agg->F = agg->S + (u64)service_received * agg->inv_w; } static struct sk_buff *qfq_dequeue(struct Qdisc *sch) { struct qfq_sched *q = qdisc_priv(sch); - struct qfq_group *grp; + struct qfq_aggregate *in_serv_agg = q->in_serv_agg; struct qfq_class *cl; - struct sk_buff *skb; - unsigned int len; - u64 old_V; + struct sk_buff *skb = NULL; + /* next-packet len, 0 means no more active classes in in-service agg */ + unsigned int len = 0; - if (!q->bitmaps[ER]) + if (in_serv_agg == NULL) return NULL; - grp = qfq_ffs(q, q->bitmaps[ER]); + if (!list_empty(&in_serv_agg->active)) + skb = qfq_peek_skb(in_serv_agg, &cl, &len); - cl = qfq_slot_head(grp); - skb = qdisc_dequeue_peeked(cl->qdisc); - if (!skb) { - WARN_ONCE(1, "qfq_dequeue: non-workconserving leaf\n"); - return NULL; + /* + * If there are no active classes in the in-service aggregate, + * or if the aggregate has not enough budget to serve its next + * class, then choose the next aggregate to serve. + */ + if (len == 0 || in_serv_agg->budget < len) { + charge_actual_service(in_serv_agg); + + /* recharge the budget of the aggregate */ + in_serv_agg->initial_budget = in_serv_agg->budget = + in_serv_agg->budgetmax; + + if (!list_empty(&in_serv_agg->active)) + /* + * Still active: reschedule for + * service. Possible optimization: if no other + * aggregate is active, then there is no point + * in rescheduling this aggregate, and we can + * just keep it as the in-service one. This + * should be however a corner case, and to + * handle it, we would need to maintain an + * extra num_active_aggs field. + */ + qfq_activate_agg(q, in_serv_agg, requeue); + else if (sch->q.qlen == 0) { /* no aggregate to serve */ + q->in_serv_agg = NULL; + return NULL; + } + + /* + * If we get here, there are other aggregates queued: + * choose the new aggregate to serve. + */ + in_serv_agg = q->in_serv_agg = qfq_choose_next_agg(q); + skb = qfq_peek_skb(in_serv_agg, &cl, &len); } + if (!skb) + return NULL; sch->q.qlen--; qdisc_bstats_update(sch, skb); - old_V = q->V; - len = qdisc_pkt_len(skb); + agg_dequeue(in_serv_agg, cl, len); + in_serv_agg->budget -= len; q->V += (u64)len * IWSUM; pr_debug("qfq dequeue: len %u F %lld now %lld\n", - len, (unsigned long long) cl->F, (unsigned long long) q->V); + len, (unsigned long long) in_serv_agg->F, + (unsigned long long) q->V); - if (qfq_update_class(grp, cl)) { - u64 old_F = grp->F; + return skb; +} - cl = qfq_slot_scan(grp); - if (!cl) - __clear_bit(grp->index, &q->bitmaps[ER]); - else { - u64 roundedS = qfq_round_down(cl->S, grp->slot_shift); - unsigned int s; +static struct qfq_aggregate *qfq_choose_next_agg(struct qfq_sched *q) +{ + struct qfq_group *grp; + struct qfq_aggregate *agg, *new_front_agg; + u64 old_F; - if (grp->S == roundedS) - goto skip_unblock; - grp->S = roundedS; - grp->F = roundedS + (2ULL << grp->slot_shift); - __clear_bit(grp->index, &q->bitmaps[ER]); - s = qfq_calc_state(q, grp); - __set_bit(grp->index, &q->bitmaps[s]); - } + qfq_update_eligible(q); + q->oldV = q->V; + + if (!q->bitmaps[ER]) + return NULL; + + grp = qfq_ffs(q, q->bitmaps[ER]); + old_F = grp->F; + + agg = qfq_slot_head(grp); - qfq_unblock_groups(q, grp->index, old_F); + /* agg starts to be served, remove it from schedule */ + qfq_front_slot_remove(grp); + + new_front_agg = qfq_slot_scan(grp); + + if (new_front_agg == NULL) /* group is now inactive, remove from ER */ + __clear_bit(grp->index, &q->bitmaps[ER]); + else { + u64 roundedS = qfq_round_down(new_front_agg->S, + grp->slot_shift); + unsigned int s; + + if (grp->S == roundedS) + return agg; + grp->S = roundedS; + grp->F = roundedS + (2ULL << grp->slot_shift); + __clear_bit(grp->index, &q->bitmaps[ER]); + s = qfq_calc_state(q, grp); + __set_bit(grp->index, &q->bitmaps[s]); } -skip_unblock: - qfq_update_eligible(q, old_V); + qfq_unblock_groups(q, grp->index, old_F); - return skb; + return agg; } /* - * Assign a reasonable start time for a new flow k in group i. + * Assign a reasonable start time for a new aggregate in group i. * Admissible values for \hat(F) are multiples of \sigma_i * no greater than V+\sigma_i . Larger values mean that * we had a wraparound so we consider the timestamp to be stale. * * If F is not stale and F >= V then we set S = F. * Otherwise we should assign S = V, but this may violate - * the ordering in ER. So, if we have groups in ER, set S to - * the F_j of the first group j which would be blocking us. + * the ordering in EB (see [2]). So, if we have groups in ER, + * set S to the F_j of the first group j which would be blocking us. * We are guaranteed not to move S backward because * otherwise our group i would still be blocked. */ -static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl) +static void qfq_update_start(struct qfq_sched *q, struct qfq_aggregate *agg) { unsigned long mask; u64 limit, roundedF; - int slot_shift = cl->grp->slot_shift; + int slot_shift = agg->grp->slot_shift; - roundedF = qfq_round_down(cl->F, slot_shift); + roundedF = qfq_round_down(agg->F, slot_shift); limit = qfq_round_down(q->V, slot_shift) + (1ULL << slot_shift); - if (!qfq_gt(cl->F, q->V) || qfq_gt(roundedF, limit)) { + if (!qfq_gt(agg->F, q->V) || qfq_gt(roundedF, limit)) { /* timestamp was stale */ - mask = mask_from(q->bitmaps[ER], cl->grp->index); + mask = mask_from(q->bitmaps[ER], agg->grp->index); if (mask) { struct qfq_group *next = qfq_ffs(q, mask); if (qfq_gt(roundedF, next->F)) { if (qfq_gt(limit, next->F)) - cl->S = next->F; + agg->S = next->F; else /* preserve timestamp correctness */ - cl->S = limit; + agg->S = limit; return; } } - cl->S = q->V; + agg->S = q->V; } else /* timestamp is not stale */ - cl->S = cl->F; + agg->S = agg->F; } +/* + * Update the timestamps of agg before scheduling/rescheduling it for + * service. In particular, assign to agg->F its maximum possible + * value, i.e., the virtual finish time with which the aggregate + * should be labeled if it used all its budget once in service. + */ +static inline void +qfq_update_agg_ts(struct qfq_sched *q, + struct qfq_aggregate *agg, enum update_reason reason) +{ + if (reason != requeue) + qfq_update_start(q, agg); + else /* just charge agg for the service received */ + agg->S = agg->F; + + agg->F = agg->S + (u64)agg->budgetmax * agg->inv_w; +} + +static void qfq_schedule_agg(struct qfq_sched *, struct qfq_aggregate *); + static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) { struct qfq_sched *q = qdisc_priv(sch); struct qfq_class *cl; + struct qfq_aggregate *agg; int err = 0; cl = qfq_classify(skb, sch, &err); @@ -934,11 +1182,13 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) } pr_debug("qfq_enqueue: cl = %x\n", cl->common.classid); - if (unlikely(cl->lmax < qdisc_pkt_len(skb))) { + if (unlikely(cl->agg->lmax < qdisc_pkt_len(skb))) { pr_debug("qfq: increasing maxpkt from %u to %u for class %u", - cl->lmax, qdisc_pkt_len(skb), cl->common.classid); - qfq_update_reactivate_class(q, cl, cl->inv_w, - qdisc_pkt_len(skb), 0); + cl->agg->lmax, qdisc_pkt_len(skb), cl->common.classid); + err = qfq_change_agg(sch, cl, cl->agg->class_weight, + qdisc_pkt_len(skb)); + if (err) + return err; } err = qdisc_enqueue(skb, cl->qdisc); @@ -954,35 +1204,50 @@ static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch) bstats_update(&cl->bstats, skb); ++sch->q.qlen; - /* If the new skb is not the head of queue, then done here. */ - if (cl->qdisc->q.qlen != 1) + agg = cl->agg; + /* if the queue was not empty, then done here */ + if (cl->qdisc->q.qlen != 1) { + if (unlikely(skb == cl->qdisc->ops->peek(cl->qdisc)) && + list_first_entry(&agg->active, struct qfq_class, alist) + == cl && cl->deficit < qdisc_pkt_len(skb)) + list_move_tail(&cl->alist, &agg->active); + return err; + } + + /* schedule class for service within the aggregate */ + cl->deficit = agg->lmax; + list_add_tail(&cl->alist, &agg->active); - /* If reach this point, queue q was idle */ - qfq_activate_class(q, cl, qdisc_pkt_len(skb)); + if (list_first_entry(&agg->active, struct qfq_class, alist) != cl) + return err; /* aggregate was not empty, nothing else to do */ + + /* recharge budget */ + agg->initial_budget = agg->budget = agg->budgetmax; + + qfq_update_agg_ts(q, agg, enqueue); + if (q->in_serv_agg == NULL) + q->in_serv_agg = agg; + else if (agg != q->in_serv_agg) + qfq_schedule_agg(q, agg); return err; } /* - * Handle class switch from idle to backlogged. + * Schedule aggregate according to its timestamps. */ -static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, - unsigned int pkt_len) +static void qfq_schedule_agg(struct qfq_sched *q, struct qfq_aggregate *agg) { - struct qfq_group *grp = cl->grp; + struct qfq_group *grp = agg->grp; u64 roundedS; int s; - qfq_update_start(q, cl); - - /* compute new finish time and rounded start. */ - cl->F = cl->S + (u64)pkt_len * cl->inv_w; - roundedS = qfq_round_down(cl->S, grp->slot_shift); + roundedS = qfq_round_down(agg->S, grp->slot_shift); /* - * insert cl in the correct bucket. - * If cl->S >= grp->S we don't need to adjust the + * Insert agg in the correct bucket. + * If agg->S >= grp->S we don't need to adjust the * bucket list and simply go to the insertion phase. * Otherwise grp->S is decreasing, we must make room * in the bucket list, and also recompute the group state. @@ -990,10 +1255,10 @@ static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, * was in ER make sure to adjust V. */ if (grp->full_slots) { - if (!qfq_gt(grp->S, cl->S)) + if (!qfq_gt(grp->S, agg->S)) goto skip_update; - /* create a slot for this cl->S */ + /* create a slot for this agg->S */ qfq_slot_rotate(grp, roundedS); /* group was surely ineligible, remove */ __clear_bit(grp->index, &q->bitmaps[IR]); @@ -1008,46 +1273,61 @@ static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl, pr_debug("qfq enqueue: new state %d %#lx S %lld F %lld V %lld\n", s, q->bitmaps[s], - (unsigned long long) cl->S, - (unsigned long long) cl->F, + (unsigned long long) agg->S, + (unsigned long long) agg->F, (unsigned long long) q->V); skip_update: - qfq_slot_insert(grp, cl, roundedS); + qfq_slot_insert(grp, agg, roundedS); } +/* Update agg ts and schedule agg for service */ +static void qfq_activate_agg(struct qfq_sched *q, struct qfq_aggregate *agg, + enum update_reason reason) +{ + qfq_update_agg_ts(q, agg, reason); + qfq_schedule_agg(q, agg); +} + static void qfq_slot_remove(struct qfq_sched *q, struct qfq_group *grp, - struct qfq_class *cl) + struct qfq_aggregate *agg) { unsigned int i, offset; u64 roundedS; - roundedS = qfq_round_down(cl->S, grp->slot_shift); + roundedS = qfq_round_down(agg->S, grp->slot_shift); offset = (roundedS - grp->S) >> grp->slot_shift; + i = (grp->front + offset) % QFQ_MAX_SLOTS; - hlist_del(&cl->next); + hlist_del(&agg->next); if (hlist_empty(&grp->slots[i])) __clear_bit(offset, &grp->full_slots); } /* - * called to forcibly destroy a queue. - * If the queue is not in the front bucket, or if it has - * other queues in the front bucket, we can simply remove - * the queue with no other side effects. + * Called to forcibly deschedule an aggregate. If the aggregate is + * not in the front bucket, or if the latter has other aggregates in + * the front bucket, we can simply remove the aggregate with no other + * side effects. * Otherwise we must propagate the event up. */ -static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl) +static void qfq_deactivate_agg(struct qfq_sched *q, struct qfq_aggregate *agg) { - struct qfq_group *grp = cl->grp; + struct qfq_group *grp = agg->grp; unsigned long mask; u64 roundedS; int s; - cl->F = cl->S; - qfq_slot_remove(q, grp, cl); + if (agg == q->in_serv_agg) { + charge_actual_service(agg); + q->in_serv_agg = qfq_choose_next_agg(q); + return; + } + + agg->F = agg->S; + qfq_slot_remove(q, grp, agg); if (!grp->full_slots) { __clear_bit(grp->index, &q->bitmaps[IR]); @@ -1066,8 +1346,8 @@ static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl) } __clear_bit(grp->index, &q->bitmaps[ER]); } else if (hlist_empty(&grp->slots[grp->front])) { - cl = qfq_slot_scan(grp); - roundedS = qfq_round_down(cl->S, grp->slot_shift); + agg = qfq_slot_scan(grp); + roundedS = qfq_round_down(agg->S, grp->slot_shift); if (grp->S != roundedS) { __clear_bit(grp->index, &q->bitmaps[ER]); __clear_bit(grp->index, &q->bitmaps[IR]); @@ -1080,7 +1360,7 @@ static void qfq_deactivate_class(struct qfq_sched *q, struct qfq_class *cl) } } - qfq_update_eligible(q, q->V); + qfq_update_eligible(q); } static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg) @@ -1092,6 +1372,32 @@ static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg) qfq_deactivate_class(q, cl); } +static unsigned int qfq_drop_from_slot(struct qfq_sched *q, + struct hlist_head *slot) +{ + struct qfq_aggregate *agg; + struct hlist_node *n; + struct qfq_class *cl; + unsigned int len; + + hlist_for_each_entry(agg, n, slot, next) { + list_for_each_entry(cl, &agg->active, alist) { + + if (!cl->qdisc->ops->drop) + continue; + + len = cl->qdisc->ops->drop(cl->qdisc); + if (len > 0) { + if (cl->qdisc->q.qlen == 0) + qfq_deactivate_class(q, cl); + + return len; + } + } + } + return 0; +} + static unsigned int qfq_drop(struct Qdisc *sch) { struct qfq_sched *q = qdisc_priv(sch); @@ -1101,24 +1407,13 @@ static unsigned int qfq_drop(struct Qdisc *sch) for (i = 0; i <= QFQ_MAX_INDEX; i++) { grp = &q->groups[i]; for (j = 0; j < QFQ_MAX_SLOTS; j++) { - struct qfq_class *cl; - struct hlist_node *n; - - hlist_for_each_entry(cl, n, &grp->slots[j], next) { - - if (!cl->qdisc->ops->drop) - continue; - - len = cl->qdisc->ops->drop(cl->qdisc); - if (len > 0) { - sch->q.qlen--; - if (!cl->qdisc->q.qlen) - qfq_deactivate_class(q, cl); - - return len; - } + len = qfq_drop_from_slot(q, &grp->slots[j]); + if (len > 0) { + sch->q.qlen--; + return len; } } + } return 0; @@ -1129,44 +1424,51 @@ static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt) struct qfq_sched *q = qdisc_priv(sch); struct qfq_group *grp; int i, j, err; + u32 max_cl_shift, maxbudg_shift, max_classes; err = qdisc_class_hash_init(&q->clhash); if (err < 0) return err; + if (qdisc_dev(sch)->tx_queue_len + 1 > QFQ_MAX_AGG_CLASSES) + max_classes = QFQ_MAX_AGG_CLASSES; + else + max_classes = qdisc_dev(sch)->tx_queue_len + 1; + /* max_cl_shift = floor(log_2(max_classes)) */ + max_cl_shift = __fls(max_classes); + q->max_agg_classes = 1<<max_cl_shift; + + /* maxbudg_shift = log2(max_len * max_classes_per_agg) */ + maxbudg_shift = QFQ_MTU_SHIFT + max_cl_shift; + q->min_slot_shift = FRAC_BITS + maxbudg_shift - QFQ_MAX_INDEX; + for (i = 0; i <= QFQ_MAX_INDEX; i++) { grp = &q->groups[i]; grp->index = i; - grp->slot_shift = QFQ_MTU_SHIFT + FRAC_BITS - - (QFQ_MAX_INDEX - i); + grp->slot_shift = q->min_slot_shift + i; for (j = 0; j < QFQ_MAX_SLOTS; j++) INIT_HLIST_HEAD(&grp->slots[j]); } + INIT_HLIST_HEAD(&q->nonfull_aggs); + return 0; } static void qfq_reset_qdisc(struct Qdisc *sch) { struct qfq_sched *q = qdisc_priv(sch); - struct qfq_group *grp; struct qfq_class *cl; - struct hlist_node *n, *tmp; - unsigned int i, j; + struct hlist_node *n; + unsigned int i; - for (i = 0; i <= QFQ_MAX_INDEX; i++) { - grp = &q->groups[i]; - for (j = 0; j < QFQ_MAX_SLOTS; j++) { - hlist_for_each_entry_safe(cl, n, tmp, - &grp->slots[j], next) { + for (i = 0; i < q->clhash.hashsize; i++) { + hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) { + if (cl->qdisc->q.qlen > 0) qfq_deactivate_class(q, cl); - } - } - } - for (i = 0; i < q->clhash.hashsize; i++) { - hlist_for_each_entry(cl, n, &q->clhash.hash[i], common.hnode) qdisc_reset(cl->qdisc); + } } sch->q.qlen = 0; } diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 7c2df9c33df..69ce21e3716 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -183,7 +183,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, msg = sctp_datamsg_new(GFP_KERNEL); if (!msg) - return NULL; + return ERR_PTR(-ENOMEM); /* Note: Calculate this outside of the loop, so that all fragments * have the same expiration. @@ -280,11 +280,14 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0); - if (!chunk) + if (!chunk) { + err = -ENOMEM; goto errout; + } + err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov); if (err < 0) - goto errout; + goto errout_chunk_free; offset += len; @@ -315,8 +318,10 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0); - if (!chunk) + if (!chunk) { + err = -ENOMEM; goto errout; + } err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov); @@ -324,7 +329,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, __skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr - (__u8 *)chunk->skb->data); if (err < 0) - goto errout; + goto errout_chunk_free; sctp_datamsg_assign(msg, chunk); list_add_tail(&chunk->frag_list, &msg->chunks); @@ -332,6 +337,9 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, return msg; +errout_chunk_free: + sctp_chunk_free(chunk); + errout: list_for_each_safe(pos, temp, &msg->chunks) { list_del_init(pos); @@ -339,7 +347,7 @@ errout: sctp_chunk_free(chunk); } sctp_datamsg_put(msg); - return NULL; + return ERR_PTR(err); } /* Check whether this message has expired. */ diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index ea14cb44529..f3f0f4dc31d 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -345,7 +345,7 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, } out: - if (!IS_ERR(dst)) { + if (!IS_ERR_OR_NULL(dst)) { struct rt6_info *rt; rt = (struct rt6_info *)dst; t->dst = dst; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index b6adef8a1e9..e92079d27ea 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1055,6 +1055,7 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net, void *arg, sctp_cmd_seq_t *commands) { + sctp_paramhdr_t *param_hdr; struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; size_t paylen = 0; @@ -1072,12 +1073,17 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net, * Information field copied from the received HEARTBEAT chunk. */ chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data; + param_hdr = (sctp_paramhdr_t *) chunk->subh.hb_hdr; paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); + + if (ntohs(param_hdr->length) > paylen) + return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, + param_hdr, commands); + if (!pskb_pull(chunk->skb, paylen)) goto nomem; - reply = sctp_make_heartbeat_ack(asoc, chunk, - chunk->subh.hb_hdr, paylen); + reply = sctp_make_heartbeat_ack(asoc, chunk, param_hdr, paylen); if (!reply) goto nomem; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 2e897069310..bc1624913c4 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1916,8 +1916,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, /* Break the message into multiple chunks of maximum size. */ datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len); - if (!datamsg) { - err = -ENOMEM; + if (IS_ERR(datamsg)) { + err = PTR_ERR(datamsg); goto out_free; } diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 953c21e4af9..206cf5238fd 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -331,7 +331,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt) * 1/8, rto_alpha would be expressed as 3. */ tp->rttvar = tp->rttvar - (tp->rttvar >> net->sctp.rto_beta) - + ((abs(tp->srtt - rtt)) >> net->sctp.rto_beta); + + (((__u32)abs64((__s64)tp->srtt - (__s64)rtt)) >> net->sctp.rto_beta); tp->srtt = tp->srtt - (tp->srtt >> net->sctp.rto_alpha) + (rtt >> net->sctp.rto_alpha); } else { diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index fe4adb12b3e..16d08b39921 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -140,14 +140,13 @@ config CFG80211_WEXT extensions with cfg80211-based drivers. config LIB80211 - tristate "Common routines for IEEE802.11 drivers" + tristate default n help This options enables a library of common routines used by IEEE802.11 wireless LAN drivers. - Drivers should select this themselves if needed. Say Y if - you want this built into your kernel. + Drivers should select this themselves if needed. config LIB80211_CRYPT_WEP tristate diff --git a/net/wireless/ap.c b/net/wireless/ap.c index e143505f05b..324e8d851dc 100644 --- a/net/wireless/ap.c +++ b/net/wireless/ap.c @@ -28,6 +28,7 @@ static int __cfg80211_stop_ap(struct cfg80211_registered_device *rdev, if (!err) { wdev->beacon_interval = 0; wdev->channel = NULL; + wdev->ssid_len = 0; } return err; diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 48febd2160b..bf2dfd54ff3 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -11,96 +11,264 @@ #include "core.h" #include "rdev-ops.h" -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type) +void cfg80211_chandef_create(struct cfg80211_chan_def *chandef, + struct ieee80211_channel *chan, + enum nl80211_channel_type chan_type) { - struct ieee80211_channel *chan; - struct ieee80211_sta_ht_cap *ht_cap; + if (WARN_ON(!chan)) + return; - chan = ieee80211_get_channel(&rdev->wiphy, freq); + chandef->chan = chan; + chandef->center_freq2 = 0; - /* Primary channel not allowed */ - if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) - return NULL; + switch (chan_type) { + case NL80211_CHAN_NO_HT: + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = chan->center_freq; + break; + case NL80211_CHAN_HT20: + chandef->width = NL80211_CHAN_WIDTH_20; + chandef->center_freq1 = chan->center_freq; + break; + case NL80211_CHAN_HT40PLUS: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 = chan->center_freq + 10; + break; + case NL80211_CHAN_HT40MINUS: + chandef->width = NL80211_CHAN_WIDTH_40; + chandef->center_freq1 = chan->center_freq - 10; + break; + default: + WARN_ON(1); + } +} +EXPORT_SYMBOL(cfg80211_chandef_create); - if (channel_type == NL80211_CHAN_HT40MINUS && - chan->flags & IEEE80211_CHAN_NO_HT40MINUS) - return NULL; - else if (channel_type == NL80211_CHAN_HT40PLUS && - chan->flags & IEEE80211_CHAN_NO_HT40PLUS) - return NULL; +bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef) +{ + u32 control_freq; - ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; + if (!chandef->chan) + return false; - if (channel_type != NL80211_CHAN_NO_HT) { - if (!ht_cap->ht_supported) - return NULL; + control_freq = chandef->chan->center_freq; - if (channel_type != NL80211_CHAN_HT20 && - (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || - ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) - return NULL; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_20_NOHT: + if (chandef->center_freq1 != control_freq) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_40: + if (chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_80P80: + if (chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30) + return false; + if (!chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_80: + if (chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30) + return false; + if (chandef->center_freq2) + return false; + break; + case NL80211_CHAN_WIDTH_160: + if (chandef->center_freq1 != control_freq + 70 && + chandef->center_freq1 != control_freq + 50 && + chandef->center_freq1 != control_freq + 30 && + chandef->center_freq1 != control_freq + 10 && + chandef->center_freq1 != control_freq - 10 && + chandef->center_freq1 != control_freq - 30 && + chandef->center_freq1 != control_freq - 50 && + chandef->center_freq1 != control_freq - 70) + return false; + if (chandef->center_freq2) + return false; + break; + default: + return false; } - return chan; + return true; } -bool cfg80211_can_beacon_sec_chan(struct wiphy *wiphy, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) +static void chandef_primary_freqs(const struct cfg80211_chan_def *c, + int *pri40, int *pri80) { - struct ieee80211_channel *sec_chan; - int diff; - - trace_cfg80211_can_beacon_sec_chan(wiphy, chan, channel_type); + int tmp; - switch (channel_type) { - case NL80211_CHAN_HT40PLUS: - diff = 20; + switch (c->width) { + case NL80211_CHAN_WIDTH_40: + *pri40 = c->center_freq1; + *pri80 = 0; break; - case NL80211_CHAN_HT40MINUS: - diff = -20; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + *pri80 = c->center_freq1; + /* n_P20 */ + tmp = (30 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + *pri40 = c->center_freq1 - 20 + 40 * tmp; + break; + case NL80211_CHAN_WIDTH_160: + /* n_P20 */ + tmp = (70 + c->chan->center_freq - c->center_freq1)/20; + /* n_P40 */ + tmp /= 2; + /* freq_P40 */ + *pri40 = c->center_freq1 - 60 + 40 * tmp; + /* n_P80 */ + tmp /= 2; + *pri80 = c->center_freq1 - 40 + 80 * tmp; break; default: - trace_cfg80211_return_bool(true); - return true; + WARN_ON_ONCE(1); } +} + +const struct cfg80211_chan_def * +cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1, + const struct cfg80211_chan_def *c2) +{ + u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80; - sec_chan = ieee80211_get_channel(wiphy, chan->center_freq + diff); - if (!sec_chan) { + /* If they are identical, return */ + if (cfg80211_chandef_identical(c1, c2)) + return c1; + + /* otherwise, must have same control channel */ + if (c1->chan != c2->chan) + return NULL; + + /* + * If they have the same width, but aren't identical, + * then they can't be compatible. + */ + if (c1->width == c2->width) + return NULL; + + if (c1->width == NL80211_CHAN_WIDTH_20_NOHT || + c1->width == NL80211_CHAN_WIDTH_20) + return c2; + + if (c2->width == NL80211_CHAN_WIDTH_20_NOHT || + c2->width == NL80211_CHAN_WIDTH_20) + return c1; + + chandef_primary_freqs(c1, &c1_pri40, &c1_pri80); + chandef_primary_freqs(c2, &c2_pri40, &c2_pri80); + + if (c1_pri40 != c2_pri40) + return NULL; + + WARN_ON(!c1_pri80 && !c2_pri80); + if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80) + return NULL; + + if (c1->width > c2->width) + return c1; + return c2; +} +EXPORT_SYMBOL(cfg80211_chandef_compatible); + +bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, + u32 center_freq, u32 bandwidth, + u32 prohibited_flags) +{ + struct ieee80211_channel *c; + u32 freq; + + for (freq = center_freq - bandwidth/2 + 10; + freq <= center_freq + bandwidth/2 - 10; + freq += 20) { + c = ieee80211_get_channel(wiphy, freq); + if (!c || c->flags & prohibited_flags) + return false; + } + + return true; +} + +static bool cfg80211_check_beacon_chans(struct wiphy *wiphy, + u32 center_freq, u32 bw) +{ + return cfg80211_secondary_chans_ok(wiphy, center_freq, bw, + IEEE80211_CHAN_DISABLED | + IEEE80211_CHAN_PASSIVE_SCAN | + IEEE80211_CHAN_NO_IBSS | + IEEE80211_CHAN_RADAR); +} + +bool cfg80211_reg_can_beacon(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef) +{ + u32 width; + bool res; + + trace_cfg80211_reg_can_beacon(wiphy, chandef); + + if (WARN_ON(!cfg80211_chan_def_valid(chandef))) { trace_cfg80211_return_bool(false); return false; } - /* we'll need a DFS capability later */ - if (sec_chan->flags & (IEEE80211_CHAN_DISABLED | - IEEE80211_CHAN_PASSIVE_SCAN | - IEEE80211_CHAN_NO_IBSS | - IEEE80211_CHAN_RADAR)) { + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + width = 20; + break; + case NL80211_CHAN_WIDTH_40: + width = 40; + break; + case NL80211_CHAN_WIDTH_80: + case NL80211_CHAN_WIDTH_80P80: + width = 80; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + break; + default: + WARN_ON_ONCE(1); trace_cfg80211_return_bool(false); return false; } - trace_cfg80211_return_bool(true); - return true; + + res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width); + + if (res && chandef->center_freq2) + res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2, + width); + + trace_cfg80211_return_bool(res); + return res; } -EXPORT_SYMBOL(cfg80211_can_beacon_sec_chan); +EXPORT_SYMBOL(cfg80211_reg_can_beacon); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type chantype) + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *chan; - if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; if (!cfg80211_has_monitors_only(rdev)) return -EBUSY; - chan = rdev_freq_to_chan(rdev, freq, chantype); - if (!chan) - return -EINVAL; - - return rdev_set_monitor_channel(rdev, chan, chantype); + return rdev_set_monitor_channel(rdev, chandef); } void diff --git a/net/wireless/core.c b/net/wireless/core.c index 26711f46a3b..14d99040035 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -326,6 +326,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) mutex_init(&rdev->devlist_mtx); mutex_init(&rdev->sched_scan_mtx); INIT_LIST_HEAD(&rdev->wdev_list); + INIT_LIST_HEAD(&rdev->beacon_registrations); + spin_lock_init(&rdev->beacon_registrations_lock); spin_lock_init(&rdev->bss_lock); INIT_LIST_HEAD(&rdev->bss_list); INIT_WORK(&rdev->scan_done_wk, __cfg80211_scan_done); @@ -698,10 +700,15 @@ EXPORT_SYMBOL(wiphy_unregister); void cfg80211_dev_free(struct cfg80211_registered_device *rdev) { struct cfg80211_internal_bss *scan, *tmp; + struct cfg80211_beacon_registration *reg, *treg; rfkill_destroy(rdev->rfkill); mutex_destroy(&rdev->mtx); mutex_destroy(&rdev->devlist_mtx); mutex_destroy(&rdev->sched_scan_mtx); + list_for_each_entry_safe(reg, treg, &rdev->beacon_registrations, list) { + list_del(®->list); + kfree(reg); + } list_for_each_entry_safe(scan, tmp, &rdev->bss_list, list) cfg80211_put_bss(&scan->pub); kfree(rdev); diff --git a/net/wireless/core.h b/net/wireless/core.h index b8eb743fe7d..a0c8decf6a4 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -55,7 +55,8 @@ struct cfg80211_registered_device { int opencount; /* also protected by devlist_mtx */ wait_queue_head_t dev_wait; - u32 ap_beacons_nlportid; + struct list_head beacon_registrations; + spinlock_t beacon_registrations_lock; /* protected by RTNL only */ int num_running_ifaces; @@ -260,6 +261,10 @@ enum cfg80211_chan_mode { CHAN_MODE_EXCLUSIVE, }; +struct cfg80211_beacon_registration { + struct list_head list; + u32 nlportid; +}; /* free object */ extern void cfg80211_dev_free(struct cfg80211_registered_device *rdev); @@ -304,9 +309,9 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, const struct mesh_config *conf); int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, struct net_device *dev); -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type); +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef); /* AP */ int cfg80211_stop_ap(struct cfg80211_registered_device *rdev, @@ -373,10 +378,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie); + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie); void cfg80211_oper_and_ht_capa(struct ieee80211_ht_cap *ht_capa, const struct ieee80211_ht_cap *ht_capa_mask); @@ -467,11 +470,8 @@ cfg80211_get_chan_state(struct wireless_dev *wdev, struct ieee80211_channel **chan, enum cfg80211_chan_mode *chanmode); -struct ieee80211_channel * -rdev_freq_to_chan(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type channel_type); int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, - int freq, enum nl80211_channel_type chantype); + struct cfg80211_chan_def *chandef); int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, const u8 *rates, unsigned int n_rates, @@ -483,6 +483,12 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); +bool cfg80211_chan_def_valid(const struct cfg80211_chan_def *chandef); + +bool cfg80211_secondary_chans_ok(struct wiphy *wiphy, + u32 center_freq, u32 bandwidth, + u32 prohibited_flags); + #define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS diff --git a/net/wireless/ibss.c b/net/wireless/ibss.c index 27941d5db72..9b9551e4a6f 100644 --- a/net/wireless/ibss.c +++ b/net/wireless/ibss.c @@ -100,9 +100,9 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, * 11a for maximum compatibility. */ struct ieee80211_supported_band *sband = - rdev->wiphy.bands[params->channel->band]; + rdev->wiphy.bands[params->chandef.chan->band]; int j; - u32 flag = params->channel->band == IEEE80211_BAND_5GHZ ? + u32 flag = params->chandef.chan->band == IEEE80211_BAND_5GHZ ? IEEE80211_RATE_MANDATORY_A : IEEE80211_RATE_MANDATORY_B; @@ -118,11 +118,11 @@ int __cfg80211_join_ibss(struct cfg80211_registered_device *rdev, wdev->ibss_fixed = params->channel_fixed; #ifdef CONFIG_CFG80211_WEXT - wdev->wext.ibss.channel = params->channel; + wdev->wext.ibss.chandef = params->chandef; #endif wdev->sme_state = CFG80211_SME_CONNECTING; - err = cfg80211_can_use_chan(rdev, wdev, params->channel, + err = cfg80211_can_use_chan(rdev, wdev, params->chandef.chan, params->channel_fixed ? CHAN_MODE_SHARED : CHAN_MODE_EXCLUSIVE); @@ -251,7 +251,9 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, wdev->wext.ibss.beacon_interval = 100; /* try to find an IBSS channel if none requested ... */ - if (!wdev->wext.ibss.channel) { + if (!wdev->wext.ibss.chandef.chan) { + wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { struct ieee80211_supported_band *sband; struct ieee80211_channel *chan; @@ -266,15 +268,15 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, continue; if (chan->flags & IEEE80211_CHAN_DISABLED) continue; - wdev->wext.ibss.channel = chan; + wdev->wext.ibss.chandef.chan = chan; break; } - if (wdev->wext.ibss.channel) + if (wdev->wext.ibss.chandef.chan) break; } - if (!wdev->wext.ibss.channel) + if (!wdev->wext.ibss.chandef.chan) return -EINVAL; } @@ -336,7 +338,7 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return -EINVAL; } - if (wdev->wext.ibss.channel == chan) + if (wdev->wext.ibss.chandef.chan == chan) return 0; wdev_lock(wdev); @@ -349,7 +351,8 @@ int cfg80211_ibss_wext_siwfreq(struct net_device *dev, return err; if (chan) { - wdev->wext.ibss.channel = chan; + wdev->wext.ibss.chandef.chan = chan; + wdev->wext.ibss.chandef.width = NL80211_CHAN_WIDTH_20_NOHT; wdev->wext.ibss.channel_fixed = true; } else { /* cfg80211_ibss_wext_join will pick one if needed */ @@ -379,8 +382,8 @@ int cfg80211_ibss_wext_giwfreq(struct net_device *dev, wdev_lock(wdev); if (wdev->current_bss) chan = wdev->current_bss->pub.channel; - else if (wdev->wext.ibss.channel) - chan = wdev->wext.ibss.channel; + else if (wdev->wext.ibss.chandef.chan) + chan = wdev->wext.ibss.chandef.chan; wdev_unlock(wdev); if (chan) { diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c index 966cfc4cd79..3ee5a728228 100644 --- a/net/wireless/mesh.c +++ b/net/wireless/mesh.c @@ -73,8 +73,6 @@ const struct mesh_config default_mesh_config = { const struct mesh_setup default_mesh_setup = { /* cfg80211_join_mesh() will pick a channel if needed */ - .channel = NULL, - .channel_type = NL80211_CHAN_NO_HT, .sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET, .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, .path_metric = IEEE80211_PATH_METRIC_AIRTIME, @@ -111,13 +109,12 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!rdev->ops->join_mesh) return -EOPNOTSUPP; - if (!setup->channel) { + if (!setup->chandef.chan) { /* if no channel explicitly given, use preset channel */ - setup->channel = wdev->preset_chan; - setup->channel_type = wdev->preset_chantype; + setup->chandef = wdev->preset_chandef; } - if (!setup->channel) { + if (!setup->chandef.chan) { /* if we don't have that either, use the first usable channel */ enum ieee80211_band band; @@ -137,26 +134,25 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, IEEE80211_CHAN_DISABLED | IEEE80211_CHAN_RADAR)) continue; - setup->channel = chan; + setup->chandef.chan = chan; break; } - if (setup->channel) + if (setup->chandef.chan) break; } /* no usable channel ... */ - if (!setup->channel) + if (!setup->chandef.chan) return -EINVAL; - setup->channel_type = NL80211_CHAN_NO_HT; + setup->chandef.width = NL80211_CHAN_WIDTH_20_NOHT;; } - if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, setup->channel, - setup->channel_type)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &setup->chandef)) return -EINVAL; - err = cfg80211_can_use_chan(rdev, wdev, setup->channel, + err = cfg80211_can_use_chan(rdev, wdev, setup->chandef.chan, CHAN_MODE_SHARED); if (err) return err; @@ -165,7 +161,7 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, if (!err) { memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); wdev->mesh_id_len = setup->mesh_id_len; - wdev->channel = setup->channel; + wdev->channel = setup->chandef.chan; } return err; @@ -188,20 +184,12 @@ int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, return err; } -int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, int freq, - enum nl80211_channel_type channel_type) +int cfg80211_set_mesh_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *channel; int err; - channel = rdev_freq_to_chan(rdev, freq, channel_type); - if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, - channel, - channel_type)) { - return -EINVAL; - } - /* * Workaround for libertas (only!), it puts the interface * into mesh mode but doesn't implement join_mesh. Instead, @@ -210,21 +198,21 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, * compatible with 802.11 mesh. */ if (rdev->ops->libertas_set_mesh_channel) { - if (channel_type != NL80211_CHAN_NO_HT) + if (chandef->width != NL80211_CHAN_WIDTH_20_NOHT) return -EINVAL; if (!netif_running(wdev->netdev)) return -ENETDOWN; - err = cfg80211_can_use_chan(rdev, wdev, channel, + err = cfg80211_can_use_chan(rdev, wdev, chandef->chan, CHAN_MODE_SHARED); if (err) return err; err = rdev_libertas_set_mesh_channel(rdev, wdev->netdev, - channel); + chandef->chan); if (!err) - wdev->channel = channel; + wdev->channel = chandef->chan; return err; } @@ -232,8 +220,7 @@ int cfg80211_set_mesh_freq(struct cfg80211_registered_device *rdev, if (wdev->mesh_id_len) return -EBUSY; - wdev->preset_chan = channel; - wdev->preset_chantype = channel_type; + wdev->preset_chandef = *chandef; return 0; } diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 4bfd14f7c59..5e8123ee63f 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -579,31 +579,25 @@ void cfg80211_mlme_down(struct cfg80211_registered_device *rdev, void cfg80211_ready_on_channel(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - trace_cfg80211_ready_on_channel(wdev, cookie, chan, channel_type, - duration); - nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, channel_type, - duration, gfp); + trace_cfg80211_ready_on_channel(wdev, cookie, chan, duration); + nl80211_send_remain_on_channel(rdev, wdev, cookie, chan, duration, gfp); } EXPORT_SYMBOL(cfg80211_ready_on_channel); void cfg80211_remain_on_channel_expired(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) { struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan, - channel_type); - nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, - channel_type, gfp); + trace_cfg80211_ready_on_channel_expired(wdev, cookie, chan); + nl80211_send_remain_on_channel_cancel(rdev, wdev, cookie, chan, gfp); } EXPORT_SYMBOL(cfg80211_remain_on_channel_expired); @@ -758,10 +752,8 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { const struct ieee80211_mgmt *mgmt; u16 stype; @@ -855,7 +847,6 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, /* Transmit the Action frame as requested by user space */ return rdev_mgmt_tx(rdev, wdev, chan, offchan, - channel_type, channel_type_valid, wait, buf, len, no_cck, dont_wait_for_ack, cookie); } @@ -997,15 +988,14 @@ void cfg80211_pmksa_candidate_notify(struct net_device *dev, int index, } EXPORT_SYMBOL(cfg80211_pmksa_candidate_notify); -void cfg80211_ch_switch_notify(struct net_device *dev, int freq, - enum nl80211_channel_type type) +void cfg80211_ch_switch_notify(struct net_device *dev, + struct cfg80211_chan_def *chandef) { struct wireless_dev *wdev = dev->ieee80211_ptr; struct wiphy *wiphy = wdev->wiphy; struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); - struct ieee80211_channel *chan; - trace_cfg80211_ch_switch_notify(dev, freq, type); + trace_cfg80211_ch_switch_notify(dev, chandef); wdev_lock(wdev); @@ -1013,12 +1003,8 @@ void cfg80211_ch_switch_notify(struct net_device *dev, int freq, wdev->iftype != NL80211_IFTYPE_P2P_GO)) goto out; - chan = rdev_freq_to_chan(rdev, freq, type); - if (WARN_ON(!chan)) - goto out; - - wdev->channel = chan; - nl80211_ch_switch_notify(rdev, dev, freq, type, GFP_KERNEL); + wdev->channel = chandef->chan; + nl80211_ch_switch_notify(rdev, dev, chandef, GFP_KERNEL); out: wdev_unlock(wdev); return; diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 8c0857815a9..d038fa45ecd 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, .len = 20-1 }, [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, + [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 }, + [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 }, + [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 }, [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 }, @@ -1110,6 +1115,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; } CMD(start_p2p_device, START_P2P_DEVICE); + CMD(set_mcast_rate, SET_MCAST_RATE); #ifdef CONFIG_NL80211_TESTMODE CMD(testmode_cmd, TESTMODE); @@ -1359,51 +1365,139 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) wdev->iftype == NL80211_IFTYPE_P2P_GO; } -static bool nl80211_valid_channel_type(struct genl_info *info, - enum nl80211_channel_type *channel_type) +static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, + struct genl_info *info, + struct cfg80211_chan_def *chandef) { - enum nl80211_channel_type tmp; + struct ieee80211_sta_ht_cap *ht_cap; + struct ieee80211_sta_vht_cap *vht_cap; + u32 control_freq, width; - if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) - return false; + if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) + return -EINVAL; - tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); - if (tmp != NL80211_CHAN_NO_HT && - tmp != NL80211_CHAN_HT20 && - tmp != NL80211_CHAN_HT40PLUS && - tmp != NL80211_CHAN_HT40MINUS) - return false; + control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - if (channel_type) - *channel_type = tmp; + chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq); + chandef->width = NL80211_CHAN_WIDTH_20_NOHT; + chandef->center_freq1 = control_freq; + chandef->center_freq2 = 0; - return true; + /* Primary channel not allowed */ + if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED) + return -EINVAL; + + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { + enum nl80211_channel_type chantype; + + chantype = nla_get_u32( + info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); + + switch (chantype) { + case NL80211_CHAN_NO_HT: + case NL80211_CHAN_HT20: + case NL80211_CHAN_HT40PLUS: + case NL80211_CHAN_HT40MINUS: + cfg80211_chandef_create(chandef, chandef->chan, + chantype); + break; + default: + return -EINVAL; + } + } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) { + chandef->width = + nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]); + if (info->attrs[NL80211_ATTR_CENTER_FREQ1]) + chandef->center_freq1 = + nla_get_u32( + info->attrs[NL80211_ATTR_CENTER_FREQ1]); + if (info->attrs[NL80211_ATTR_CENTER_FREQ2]) + chandef->center_freq2 = + nla_get_u32( + info->attrs[NL80211_ATTR_CENTER_FREQ2]); + } + + ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap; + vht_cap = &rdev->wiphy.bands[chandef->chan->band]->vht_cap; + + if (!cfg80211_chan_def_valid(chandef)) + return -EINVAL; + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20: + if (!ht_cap->ht_supported) + return -EINVAL; + case NL80211_CHAN_WIDTH_20_NOHT: + width = 20; + break; + case NL80211_CHAN_WIDTH_40: + width = 40; + /* quick early regulatory check */ + if (chandef->center_freq1 < control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS) + return -EINVAL; + if (chandef->center_freq1 > control_freq && + chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS) + return -EINVAL; + if (!ht_cap->ht_supported) + return -EINVAL; + if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || + ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT) + return -EINVAL; + break; + case NL80211_CHAN_WIDTH_80: + width = 80; + if (!vht_cap->vht_supported) + return -EINVAL; + break; + case NL80211_CHAN_WIDTH_80P80: + width = 80; + if (!vht_cap->vht_supported) + return -EINVAL; + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) + return -EINVAL; + break; + case NL80211_CHAN_WIDTH_160: + width = 160; + if (!vht_cap->vht_supported) + return -EINVAL; + if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ)) + return -EINVAL; + break; + default: + return -EINVAL; + } + + if (!cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq1, + width, IEEE80211_CHAN_DISABLED)) + return -EINVAL; + if (chandef->center_freq2 && + !cfg80211_secondary_chans_ok(&rdev->wiphy, chandef->center_freq2, + width, IEEE80211_CHAN_DISABLED)) + return -EINVAL; + + /* TODO: missing regulatory check on bandwidth */ + + return 0; } static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct genl_info *info) { - struct ieee80211_channel *channel; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq; + struct cfg80211_chan_def chandef; int result; enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; if (wdev) iftype = wdev->iftype; - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) - return -EINVAL; - if (!nl80211_can_set_dev_channel(wdev)) return -EOPNOTSUPP; - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); + result = nl80211_parse_chandef(rdev, info, &chandef); + if (result) + return result; mutex_lock(&rdev->devlist_mtx); switch (iftype) { @@ -1413,22 +1507,18 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, result = -EBUSY; break; } - channel = rdev_freq_to_chan(rdev, freq, channel_type); - if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy, - channel, - channel_type)) { + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef)) { result = -EINVAL; break; } - wdev->preset_chan = channel; - wdev->preset_chantype = channel_type; + wdev->preset_chandef = chandef; result = 0; break; case NL80211_IFTYPE_MESH_POINT: - result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type); + result = cfg80211_set_mesh_channel(rdev, wdev, &chandef); break; case NL80211_IFTYPE_MONITOR: - result = cfg80211_set_monitor_channel(rdev, freq, channel_type); + result = cfg80211_set_monitor_channel(rdev, &chandef); break; default: result = -EINVAL; @@ -1516,10 +1606,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) result = 0; mutex_lock(&rdev->mtx); - } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) + } else wdev = netdev->ieee80211_ptr; - else - wdev = NULL; /* * end workaround code, by now the rdev is available @@ -1579,15 +1667,21 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - result = __nl80211_set_channel(rdev, wdev, info); + result = __nl80211_set_channel(rdev, + nl80211_can_set_dev_channel(wdev) ? wdev : NULL, + info); if (result) goto bad_res; } if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) { + struct wireless_dev *txp_wdev = wdev; enum nl80211_tx_power_setting type; int idx, mbm = 0; + if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER)) + txp_wdev = NULL; + if (!rdev->ops->set_tx_power) { result = -EOPNOTSUPP; goto bad_res; @@ -1607,7 +1701,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) mbm = nla_get_u32(info->attrs[idx]); } - result = rdev_set_tx_power(rdev, type, mbm); + result = rdev_set_tx_power(rdev, txp_wdev, type, mbm); if (result) goto bad_res; } @@ -1744,6 +1838,35 @@ static inline u64 wdev_id(struct wireless_dev *wdev) ((u64)wiphy_to_dev(wdev->wiphy)->wiphy_idx << 32); } +static int nl80211_send_chandef(struct sk_buff *msg, + struct cfg80211_chan_def *chandef) +{ + WARN_ON(!cfg80211_chan_def_valid(chandef)); + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, + chandef->chan->center_freq)) + return -ENOBUFS; + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + case NL80211_CHAN_WIDTH_40: + if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + cfg80211_get_chandef_type(chandef))) + return -ENOBUFS; + break; + default: + break; + } + if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width)) + return -ENOBUFS; + if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1)) + return -ENOBUFS; + if (chandef->center_freq2 && + nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2)) + return -ENOBUFS; + return 0; +} + static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev) @@ -1770,15 +1893,18 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flag goto nla_put_failure; if (rdev->ops->get_channel) { - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; - - chan = rdev_get_channel(rdev, wdev, &channel_type); - if (chan && - (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, - chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, - channel_type))) + int ret; + struct cfg80211_chan_def chandef; + + ret = rdev_get_channel(rdev, wdev, &chandef); + if (ret == 0) { + if (nl80211_send_chandef(msg, &chandef)) + goto nla_put_failure; + } + } + + if (wdev->ssid_len) { + if (nla_put(msg, NL80211_ATTR_SSID, wdev->ssid_len, wdev->ssid)) goto nla_put_failure; } @@ -2482,11 +2608,10 @@ static bool nl80211_get_ap_channel(struct cfg80211_registered_device *rdev, wdev->iftype != NL80211_IFTYPE_P2P_GO) continue; - if (!wdev->preset_chan) + if (!wdev->preset_chandef.chan) continue; - params->channel = wdev->preset_chan; - params->channel_type = wdev->preset_chantype; + params->chandef = wdev->preset_chandef; ret = true; break; } @@ -2608,30 +2733,19 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - params.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - channel_type); - if (!params.channel) - return -EINVAL; - params.channel_type = channel_type; - } else if (wdev->preset_chan) { - params.channel = wdev->preset_chan; - params.channel_type = wdev->preset_chantype; + err = nl80211_parse_chandef(rdev, info, ¶ms.chandef); + if (err) + return err; + } else if (wdev->preset_chandef.chan) { + params.chandef = wdev->preset_chandef; } else if (!nl80211_get_ap_channel(rdev, ¶ms)) return -EINVAL; - if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel, - params.channel_type)) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, ¶ms.chandef)) return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_can_use_chan(rdev, wdev, params.channel, + err = cfg80211_can_use_chan(rdev, wdev, params.chandef.chan, CHAN_MODE_SHARED); mutex_unlock(&rdev->devlist_mtx); @@ -2640,10 +2754,11 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) err = rdev_start_ap(rdev, dev, ¶ms); if (!err) { - wdev->preset_chan = params.channel; - wdev->preset_chantype = params.channel_type; + wdev->preset_chandef = params.chandef; wdev->beacon_interval = params.beacon_interval; - wdev->channel = params.channel; + wdev->channel = params.chandef.chan; + wdev->ssid_len = params.ssid_len; + memcpy(wdev->ssid, params.ssid, wdev->ssid_len); } return err; } @@ -2775,29 +2890,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info, rate = nla_nest_start(msg, attr); if (!rate) - goto nla_put_failure; + return false; /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */ bitrate = cfg80211_calculate_bitrate(info); /* report 16-bit bitrate only if we can */ bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0; - if ((bitrate > 0 && - nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) || - (bitrate_compat > 0 && - nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) || - ((info->flags & RATE_INFO_FLAGS_MCS) && - nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) || - ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) && - nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) || - ((info->flags & RATE_INFO_FLAGS_SHORT_GI) && - nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))) - goto nla_put_failure; + if (bitrate > 0 && + nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) + return false; + if (bitrate_compat > 0 && + nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) + return false; + + if (info->flags & RATE_INFO_FLAGS_MCS) { + if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) + return false; + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_SHORT_GI && + nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) + return false; + } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) { + if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs)) + return false; + if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss)) + return false; + if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH && + nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH)) + return false; + if (info->flags & RATE_INFO_FLAGS_SHORT_GI && + nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)) + return false; + } nla_nest_end(msg, rate); return true; - -nla_put_failure: - return false; } static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq, @@ -5318,8 +5456,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) return -EINVAL; - if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || - !info->attrs[NL80211_ATTR_SSID] || + if (!info->attrs[NL80211_ATTR_SSID] || !nla_len(info->attrs[NL80211_ATTR_SSID])) return -EINVAL; @@ -5354,35 +5491,17 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); } - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - enum nl80211_channel_type channel_type; - - if (!nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - if (channel_type != NL80211_CHAN_NO_HT && - !(wiphy->features & NL80211_FEATURE_HT_IBSS)) - return -EINVAL; - - ibss.channel_type = channel_type; - } else { - ibss.channel_type = NL80211_CHAN_NO_HT; - } + err = nl80211_parse_chandef(rdev, info, &ibss.chandef); + if (err) + return err; - ibss.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - ibss.channel_type); - if (!ibss.channel || - ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || - ibss.channel->flags & IEEE80211_CHAN_DISABLED) + if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef)) return -EINVAL; - /* Both channels should be able to initiate communication */ - if ((ibss.channel_type == NL80211_CHAN_HT40PLUS || - ibss.channel_type == NL80211_CHAN_HT40MINUS) && - !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel, - ibss.channel_type)) + if (ibss.chandef.width > NL80211_CHAN_WIDTH_40) return -EINVAL; + if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT && + !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)) ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY]; @@ -5393,7 +5512,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) int n_rates = nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); struct ieee80211_supported_band *sband = - wiphy->bands[ibss.channel->band]; + wiphy->bands[ibss.chandef.chan->band]; err = ieee80211_get_ratemask(sband, rates, n_rates, &ibss.basic_rates); @@ -5415,7 +5534,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) if (IS_ERR(connkeys)) return PTR_ERR(connkeys); - if ((ibss.channel_type != NL80211_CHAN_NO_HT) && no_ht) { + if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) && + no_ht) { kfree(connkeys); return -EINVAL; } @@ -5444,6 +5564,36 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) return cfg80211_leave_ibss(rdev, dev, false); } +static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + int mcast_rate[IEEE80211_NUM_BANDS]; + u32 nla_rate; + int err; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->set_mcast_rate) + return -EOPNOTSUPP; + + memset(mcast_rate, 0, sizeof(mcast_rate)); + + if (!info->attrs[NL80211_ATTR_MCAST_RATE]) + return -EINVAL; + + nla_rate = nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]); + if (!nl80211_parse_mcast_rate(rdev, mcast_rate, nla_rate)) + return -EINVAL; + + err = rdev->ops->set_mcast_rate(&rdev->wiphy, dev, mcast_rate); + + return err; +} + + #ifdef CONFIG_NL80211_TESTMODE static struct genl_multicast_group nl80211_testmode_mcgrp = { .name = "testmode", @@ -5906,12 +6056,11 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; - struct ieee80211_channel *chan; + struct cfg80211_chan_def chandef; struct sk_buff *msg; void *hdr; u64 cookie; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - u32 freq, duration; + u32 duration; int err; if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || @@ -5932,14 +6081,9 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!msg) @@ -5953,8 +6097,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, goto free_msg; } - err = rdev_remain_on_channel(rdev, wdev, chan, channel_type, duration, - &cookie); + err = rdev_remain_on_channel(rdev, wdev, chandef.chan, + duration, &cookie); if (err) goto free_msg; @@ -6173,10 +6317,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct wireless_dev *wdev = info->user_ptr[1]; - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - bool channel_type_valid = false; - u32 freq; + struct cfg80211_chan_def chandef; int err; void *hdr = NULL; u64 cookie; @@ -6186,8 +6327,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK]; - if (!info->attrs[NL80211_ATTR_FRAME] || - !info->attrs[NL80211_ATTR_WIPHY_FREQ]) + if (!info->attrs[NL80211_ATTR_FRAME]) return -EINVAL; if (!rdev->ops->mgmt_tx) @@ -6222,12 +6362,6 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { - if (!nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - channel_type_valid = true; - } - offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)) @@ -6235,10 +6369,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]); - freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); - chan = rdev_freq_to_chan(rdev, freq, channel_type); - if (chan == NULL) - return -EINVAL; + err = nl80211_parse_chandef(rdev, info, &chandef); + if (err) + return err; if (!dont_wait_for_ack) { msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); @@ -6254,8 +6387,7 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) } } - err = cfg80211_mlme_mgmt_tx(rdev, wdev, chan, offchan, channel_type, - channel_type_valid, wait, + err = cfg80211_mlme_mgmt_tx(rdev, wdev, chandef.chan, offchan, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), no_cck, dont_wait_for_ack, &cookie); @@ -6519,21 +6651,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { - enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; - - if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] && - !nl80211_valid_channel_type(info, &channel_type)) - return -EINVAL; - - setup.channel = rdev_freq_to_chan(rdev, - nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]), - channel_type); - if (!setup.channel) - return -EINVAL; - setup.channel_type = channel_type; + err = nl80211_parse_chandef(rdev, info, &setup.chandef); + if (err) + return err; } else { /* cfg80211_join_mesh() will sort it out */ - setup.channel = NULL; + setup.chandef.chan = NULL; } return cfg80211_join_mesh(rdev, dev, &setup, &cfg); @@ -6899,16 +7022,35 @@ static int nl80211_probe_client(struct sk_buff *skb, static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct cfg80211_beacon_registration *reg, *nreg; + int rv; if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS)) return -EOPNOTSUPP; - if (rdev->ap_beacons_nlportid) - return -EBUSY; + nreg = kzalloc(sizeof(*nreg), GFP_KERNEL); + if (!nreg) + return -ENOMEM; - rdev->ap_beacons_nlportid = info->snd_portid; + /* First, check if already registered. */ + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry(reg, &rdev->beacon_registrations, list) { + if (reg->nlportid == info->snd_portid) { + rv = -EALREADY; + goto out_err; + } + } + /* Add it to the list */ + nreg->nlportid = info->snd_portid; + list_add(&nreg->list, &rdev->beacon_registrations); + + spin_unlock_bh(&rdev->beacon_registrations_lock); return 0; +out_err: + spin_unlock_bh(&rdev->beacon_registrations_lock); + kfree(nreg); + return rv; } static int nl80211_start_p2p_device(struct sk_buff *skb, struct genl_info *info) @@ -7625,6 +7767,14 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_WDEV_UP | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_SET_MCAST_RATE, + .doit = nl80211_set_mcast_rate, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -8326,7 +8476,6 @@ static void nl80211_send_remain_on_chan_event( int cmd, struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { struct sk_buff *msg; @@ -8347,7 +8496,8 @@ static void nl80211_send_remain_on_chan_event( wdev->netdev->ifindex)) || nla_put_u64(msg, NL80211_ATTR_WDEV, wdev_id(wdev)) || nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) || + nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, + NL80211_CHAN_NO_HT) || nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie)) goto nla_put_failure; @@ -8369,23 +8519,20 @@ static void nl80211_send_remain_on_chan_event( void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, rdev, wdev, cookie, chan, - channel_type, duration, gfp); + duration, gfp); } void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp) + u64 cookie, struct ieee80211_channel *chan, gfp_t gfp) { nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, - rdev, wdev, cookie, chan, - channel_type, 0, gfp); + rdev, wdev, cookie, chan, 0, gfp); } void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, @@ -8741,8 +8888,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, } void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, - struct net_device *netdev, int freq, - enum nl80211_channel_type type, gfp_t gfp) + struct net_device *netdev, + struct cfg80211_chan_def *chandef, gfp_t gfp) { struct sk_buff *msg; void *hdr; @@ -8757,9 +8904,10 @@ void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, return; } - if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) || - nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type)) + if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex)) + goto nla_put_failure; + + if (nl80211_send_chandef(msg, chandef)) goto nla_put_failure; genlmsg_end(msg, hdr); @@ -8914,46 +9062,96 @@ EXPORT_SYMBOL(cfg80211_probe_status); void cfg80211_report_obss_beacon(struct wiphy *wiphy, const u8 *frame, size_t len, - int freq, int sig_dbm, gfp_t gfp) + int freq, int sig_dbm) { struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); struct sk_buff *msg; void *hdr; - u32 nlportid = ACCESS_ONCE(rdev->ap_beacons_nlportid); + struct cfg80211_beacon_registration *reg; trace_cfg80211_report_obss_beacon(wiphy, frame, len, freq, sig_dbm); - if (!nlportid) - return; + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry(reg, &rdev->beacon_registrations, list) { + msg = nlmsg_new(len + 100, GFP_ATOMIC); + if (!msg) { + spin_unlock_bh(&rdev->beacon_registrations_lock); + return; + } + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + if (!hdr) + goto nla_put_failure; + + if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || + (freq && + nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || + (sig_dbm && + nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || + nla_put(msg, NL80211_ATTR_FRAME, len, frame)) + goto nla_put_failure; + + genlmsg_end(msg, hdr); - msg = nlmsg_new(len + 100, gfp); + genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, reg->nlportid); + } + spin_unlock_bh(&rdev->beacon_registrations_lock); + return; + + nla_put_failure: + spin_unlock_bh(&rdev->beacon_registrations_lock); + if (hdr) + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} +EXPORT_SYMBOL(cfg80211_report_obss_beacon); + +void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer, + enum nl80211_tdls_operation oper, + u16 reason_code, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct sk_buff *msg; + void *hdr; + int err; + + trace_cfg80211_tdls_oper_request(wdev->wiphy, dev, peer, oper, + reason_code); + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); if (!msg) return; - hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME); + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER); if (!hdr) { nlmsg_free(msg); return; } if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) || - (freq && - nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) || - (sig_dbm && - nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) || - nla_put(msg, NL80211_ATTR_FRAME, len, frame)) + nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) || + nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) || + nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) || + (reason_code > 0 && + nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) goto nla_put_failure; - genlmsg_end(msg, hdr); + err = genlmsg_end(msg, hdr); + if (err < 0) { + nlmsg_free(msg); + return; + } - genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: genlmsg_cancel(msg, hdr); nlmsg_free(msg); } -EXPORT_SYMBOL(cfg80211_report_obss_beacon); +EXPORT_SYMBOL(cfg80211_tdls_oper_request); static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, @@ -8962,6 +9160,7 @@ static int nl80211_netlink_notify(struct notifier_block * nb, struct netlink_notify *notify = _notify; struct cfg80211_registered_device *rdev; struct wireless_dev *wdev; + struct cfg80211_beacon_registration *reg, *tmp; if (state != NETLINK_URELEASE) return NOTIFY_DONE; @@ -8971,8 +9170,17 @@ static int nl80211_netlink_notify(struct notifier_block * nb, list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) { list_for_each_entry_rcu(wdev, &rdev->wdev_list, list) cfg80211_mlme_unregister_socket(wdev, notify->portid); - if (rdev->ap_beacons_nlportid == notify->portid) - rdev->ap_beacons_nlportid = 0; + + spin_lock_bh(&rdev->beacon_registrations_lock); + list_for_each_entry_safe(reg, tmp, &rdev->beacon_registrations, + list) { + if (reg->nlportid == notify->portid) { + list_del(®->list); + kfree(reg); + break; + } + } + spin_unlock_bh(&rdev->beacon_registrations_lock); } rcu_read_unlock(); diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index f6153516068..2acba8477e9 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -76,13 +76,11 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, gfp_t gfp); void nl80211_send_remain_on_channel_cancel( struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, - u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, gfp_t gfp); + u64 cookie, struct ieee80211_channel *chan, gfp_t gfp); void nl80211_send_sta_event(struct cfg80211_registered_device *rdev, struct net_device *dev, const u8 *mac_addr, @@ -129,8 +127,8 @@ void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev, const u8 *bssid, bool preauth, gfp_t gfp); void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev, - struct net_device *dev, int freq, - enum nl80211_channel_type type, gfp_t gfp); + struct net_device *dev, + struct cfg80211_chan_def *chandef, gfp_t gfp); bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp); diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h index eb5f8974e14..6c0c8191f83 100644 --- a/net/wireless/rdev-ops.h +++ b/net/wireless/rdev-ops.h @@ -359,12 +359,11 @@ rdev_libertas_set_mesh_channel(struct cfg80211_registered_device *rdev, static inline int rdev_set_monitor_channel(struct cfg80211_registered_device *rdev, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type) + struct cfg80211_chan_def *chandef) { int ret; - trace_rdev_set_monitor_channel(&rdev->wiphy, chan, channel_type); - ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chan, channel_type); + trace_rdev_set_monitor_channel(&rdev->wiphy, chandef); + ret = rdev->ops->set_monitor_channel(&rdev->wiphy, chandef); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } @@ -476,21 +475,22 @@ rdev_set_wiphy_params(struct cfg80211_registered_device *rdev, u32 changed) } static inline int rdev_set_tx_power(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, enum nl80211_tx_power_setting type, int mbm) { int ret; - trace_rdev_set_tx_power(&rdev->wiphy, type, mbm); - ret = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm); + trace_rdev_set_tx_power(&rdev->wiphy, wdev, type, mbm); + ret = rdev->ops->set_tx_power(&rdev->wiphy, wdev, type, mbm); trace_rdev_return_int(&rdev->wiphy, ret); return ret; } static inline int rdev_get_tx_power(struct cfg80211_registered_device *rdev, - int *dbm) + struct wireless_dev *wdev, int *dbm) { int ret; - trace_rdev_get_tx_power(&rdev->wiphy); - ret = rdev->ops->get_tx_power(&rdev->wiphy, dbm); + trace_rdev_get_tx_power(&rdev->wiphy, wdev); + ret = rdev->ops->get_tx_power(&rdev->wiphy, wdev, dbm); trace_rdev_return_int_int(&rdev->wiphy, ret, *dbm); return ret; } @@ -599,14 +599,12 @@ static inline int rdev_remain_on_channel(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration, u64 *cookie) { int ret; - trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, channel_type, - duration); + trace_rdev_remain_on_channel(&rdev->wiphy, wdev, chan, duration); ret = rdev->ops->remain_on_channel(&rdev->wiphy, wdev, chan, - channel_type, duration, cookie); + duration, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } @@ -625,17 +623,15 @@ rdev_cancel_remain_on_channel(struct cfg80211_registered_device *rdev, static inline int rdev_mgmt_tx(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, - const u8 *buf, size_t len, bool no_cck, - bool dont_wait_for_ack, u64 *cookie) + unsigned int wait, const u8 *buf, size_t len, + bool no_cck, bool dont_wait_for_ack, u64 *cookie) { int ret; - trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, channel_type, - channel_type_valid, wait, no_cck, dont_wait_for_ack); + trace_rdev_mgmt_tx(&rdev->wiphy, wdev, chan, offchan, + wait, no_cck, dont_wait_for_ack); ret = rdev->ops->mgmt_tx(&rdev->wiphy, wdev, chan, offchan, - channel_type, channel_type_valid, wait, buf, - len, no_cck, dont_wait_for_ack, cookie); + wait, buf, len, no_cck, + dont_wait_for_ack, cookie); trace_rdev_return_int_cookie(&rdev->wiphy, ret, *cookie); return ret; } @@ -847,14 +843,17 @@ static inline void rdev_get_et_strings(struct cfg80211_registered_device *rdev, trace_rdev_return_void(&rdev->wiphy); } -static inline struct ieee80211_channel -*rdev_get_channel(struct cfg80211_registered_device *rdev, - struct wireless_dev *wdev, enum nl80211_channel_type *type) +static inline int +rdev_get_channel(struct cfg80211_registered_device *rdev, + struct wireless_dev *wdev, + struct cfg80211_chan_def *chandef) { - struct ieee80211_channel *ret; + int ret; + trace_rdev_get_channel(&rdev->wiphy, wdev); - ret = rdev->ops->get_channel(&rdev->wiphy, wdev, type); - trace_rdev_return_channel(&rdev->wiphy, ret, *type); + ret = rdev->ops->get_channel(&rdev->wiphy, wdev, chandef); + trace_rdev_return_chandef(&rdev->wiphy, ret, chandef); + return ret; } diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 7f97a087f45..9596015975d 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -771,6 +771,38 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, return found; } +static struct ieee80211_channel * +cfg80211_get_bss_channel(struct wiphy *wiphy, const u8 *ie, size_t ielen, + struct ieee80211_channel *channel) +{ + const u8 *tmp; + u32 freq; + int channel_number = -1; + + tmp = cfg80211_find_ie(WLAN_EID_DS_PARAMS, ie, ielen); + if (tmp && tmp[1] == 1) { + channel_number = tmp[2]; + } else { + tmp = cfg80211_find_ie(WLAN_EID_HT_OPERATION, ie, ielen); + if (tmp && tmp[1] >= sizeof(struct ieee80211_ht_operation)) { + struct ieee80211_ht_operation *htop = (void *)(tmp + 2); + + channel_number = htop->primary_chan; + } + } + + if (channel_number < 0) + return channel; + + freq = ieee80211_channel_to_frequency(channel_number, channel->band); + channel = ieee80211_get_channel(wiphy, freq); + if (!channel) + return NULL; + if (channel->flags & IEEE80211_CHAN_DISABLED) + return NULL; + return channel; +} + struct cfg80211_bss* cfg80211_inform_bss(struct wiphy *wiphy, struct ieee80211_channel *channel, @@ -790,6 +822,10 @@ cfg80211_inform_bss(struct wiphy *wiphy, (signal < 0 || signal > 100))) return NULL; + channel = cfg80211_get_bss_channel(wiphy, ie, ielen, channel); + if (!channel) + return NULL; + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); if (!res) return NULL; @@ -839,11 +875,13 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, s32 signal, gfp_t gfp) { struct cfg80211_internal_bss *res; - size_t ielen = len - offsetof(struct ieee80211_mgmt, u.probe_resp.variable); size_t privsz; + BUILD_BUG_ON(offsetof(struct ieee80211_mgmt, u.probe_resp.variable) != + offsetof(struct ieee80211_mgmt, u.beacon.variable)); + trace_cfg80211_inform_bss_frame(wiphy, channel, mgmt, len, signal); if (WARN_ON(!mgmt)) @@ -861,6 +899,11 @@ cfg80211_inform_bss_frame(struct wiphy *wiphy, privsz = wiphy->bss_priv_size; + channel = cfg80211_get_bss_channel(wiphy, mgmt->u.beacon.variable, + ielen, channel); + if (!channel) + return NULL; + res = kzalloc(sizeof(*res) + privsz + ielen, gfp); if (!res) return NULL; diff --git a/net/wireless/trace.h b/net/wireless/trace.h index 0ca71caf85f..2134576f426 100644 --- a/net/wireless/trace.h +++ b/net/wireless/trace.h @@ -20,29 +20,26 @@ #define MAC_PR_FMT "%pM" #define MAC_PR_ARG(entry_mac) (__entry->entry_mac) -#define WIPHY_ENTRY MAC_ENTRY(wiphy_mac) -#define WIPHY_ASSIGN MAC_ASSIGN(wiphy_mac, wiphy->perm_addr) -#define WIPHY_PR_FMT "wiphy " MAC_PR_FMT -#define WIPHY_PR_ARG MAC_PR_ARG(wiphy_mac) - -#define WDEV_ENTRY __field(u32, id) -#define WDEV_ASSIGN (__entry->id) = (wdev->identifier) -#define WDEV_PR_FMT ", wdev id: %u" -#define WDEV_PR_ARG (__entry->id) - -#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \ - MAC_ENTRY(netdev_addr) \ - __field(int, ifindex) +#define MAXNAME 32 +#define WIPHY_ENTRY __array(char, wiphy_name, 32) +#define WIPHY_ASSIGN strlcpy(__entry->wiphy_name, wiphy_name(wiphy), MAXNAME) +#define WIPHY_PR_FMT "%s" +#define WIPHY_PR_ARG __entry->wiphy_name + +#define WDEV_ENTRY __field(u32, id) +#define WDEV_ASSIGN (__entry->id) = (wdev ? wdev->identifier : 0) +#define WDEV_PR_FMT "wdev(%u)" +#define WDEV_PR_ARG (__entry->id) + +#define NETDEV_ENTRY __array(char, name, IFNAMSIZ) \ + __field(int, ifindex) #define NETDEV_ASSIGN \ do { \ memcpy(__entry->name, netdev->name, IFNAMSIZ); \ - MAC_ASSIGN(netdev_addr, netdev->dev_addr); \ (__entry->ifindex) = (netdev->ifindex); \ } while (0) -#define NETDEV_PR_FMT ", netdev - name: %s, addr: " MAC_PR_FMT \ - ", intf index: %d" -#define NETDEV_PR_ARG (__entry->name), MAC_PR_ARG(netdev_addr), \ - (__entry->ifindex) +#define NETDEV_PR_FMT "netdev:%s(%d)" +#define NETDEV_PR_ARG __entry->name, __entry->ifindex #define MESH_CFG_ENTRY __field(u16, dot11MeshRetryTimeout) \ __field(u16, dot11MeshConfirmTimeout) \ @@ -123,9 +120,37 @@ __entry->center_freq = 0; \ } \ } while (0) -#define CHAN_PR_FMT ", band: %d, freq: %u" +#define CHAN_PR_FMT "band: %d, freq: %u" #define CHAN_PR_ARG __entry->band, __entry->center_freq +#define CHAN_DEF_ENTRY __field(enum ieee80211_band, band) \ + __field(u32, control_freq) \ + __field(u32, width) \ + __field(u32, center_freq1) \ + __field(u32, center_freq2) +#define CHAN_DEF_ASSIGN(chandef) \ + do { \ + if ((chandef) && (chandef)->chan) { \ + __entry->band = (chandef)->chan->band; \ + __entry->control_freq = \ + (chandef)->chan->center_freq; \ + __entry->width = (chandef)->width; \ + __entry->center_freq1 = (chandef)->center_freq1;\ + __entry->center_freq2 = (chandef)->center_freq2;\ + } else { \ + __entry->band = 0; \ + __entry->control_freq = 0; \ + __entry->width = 0; \ + __entry->center_freq1 = 0; \ + __entry->center_freq2 = 0; \ + } \ + } while (0) +#define CHAN_DEF_PR_FMT \ + "band: %d, control freq: %u, width: %d, cf1: %u, cf2: %u" +#define CHAN_DEF_PR_ARG __entry->band, __entry->control_freq, \ + __entry->width, __entry->center_freq1, \ + __entry->center_freq2 + #define SINFO_ENTRY __field(int, generation) \ __field(u32, connected_time) \ __field(u32, inactive_time) \ @@ -260,11 +285,6 @@ DEFINE_EVENT(wiphy_only_evt, rdev_get_antenna, TP_ARGS(wiphy) ); -DEFINE_EVENT(wiphy_only_evt, rdev_get_tx_power, - TP_PROTO(struct wiphy *wiphy), - TP_ARGS(wiphy) -); - DEFINE_EVENT(wiphy_only_evt, rdev_rfkill_poll, TP_PROTO(struct wiphy *wiphy), TP_ARGS(wiphy) @@ -318,7 +338,7 @@ DECLARE_EVENT_CLASS(wiphy_wdev_evt, WIPHY_ASSIGN; WDEV_ASSIGN; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT, WIPHY_PR_ARG, WDEV_PR_ARG) ); DEFINE_EVENT(wiphy_wdev_evt, rdev_return_wdev, @@ -345,7 +365,7 @@ TRACE_EVENT(rdev_change_virtual_intf, NETDEV_ASSIGN; __entry->type = type; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", type: %d", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", type: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->type) ); @@ -367,7 +387,7 @@ DECLARE_EVENT_CLASS(key_handle, __entry->key_index = key_index; __entry->pairwise = pairwise; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key_index: %u, pairwise: %s, mac addr: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, BOOL_TO_STR(__entry->pairwise), MAC_PR_ARG(mac_addr)) ); @@ -408,7 +428,7 @@ TRACE_EVENT(rdev_set_default_key, __entry->unicast = unicast; __entry->multicast = multicast; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u, unicast: %s, multicast: %s", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index, BOOL_TO_STR(__entry->unicast), BOOL_TO_STR(__entry->multicast)) @@ -427,7 +447,7 @@ TRACE_EVENT(rdev_set_default_mgmt_key, NETDEV_ASSIGN; __entry->key_index = key_index; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", key index: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", key index: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->key_index) ); @@ -438,7 +458,7 @@ TRACE_EVENT(rdev_start_ap, TP_STRUCT__entry( WIPHY_ENTRY NETDEV_ENTRY - CHAN_ENTRY + CHAN_DEF_ENTRY __field(int, beacon_interval) __field(int, dtim_period) __array(char, ssid, IEEE80211_MAX_SSID_LEN + 1) @@ -451,7 +471,7 @@ TRACE_EVENT(rdev_start_ap, TP_fast_assign( WIPHY_ASSIGN; NETDEV_ASSIGN; - CHAN_ASSIGN(settings->channel); + CHAN_DEF_ASSIGN(&settings->chandef); __entry->beacon_interval = settings->beacon_interval; __entry->dtim_period = settings->dtim_period; __entry->hidden_ssid = settings->hidden_ssid; @@ -462,11 +482,11 @@ TRACE_EVENT(rdev_start_ap, memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); memcpy(__entry->ssid, settings->ssid, settings->ssid_len); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", AP settings - ssid: %s, " - CHAN_PR_FMT ", beacon interval: %d, dtim period: %d, " + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", AP settings - ssid: %s, " + CHAN_DEF_PR_FMT ", beacon interval: %d, dtim period: %d, " "hidden ssid: %d, wpa versions: %u, privacy: %s, " "auth type: %d, inactivity timeout: %d", - WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_PR_ARG, + WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ssid, CHAN_DEF_PR_ARG, __entry->beacon_interval, __entry->dtim_period, __entry->hidden_ssid, __entry->wpa_ver, BOOL_TO_STR(__entry->privacy), __entry->auth_type, @@ -515,7 +535,7 @@ TRACE_EVENT(rdev_change_beacon, info->probe_resp, info->probe_resp_len); } ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) ); DECLARE_EVENT_CLASS(wiphy_netdev_evt, @@ -529,7 +549,7 @@ DECLARE_EVENT_CLASS(wiphy_netdev_evt, WIPHY_ASSIGN; NETDEV_ASSIGN; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) ); DEFINE_EVENT(wiphy_netdev_evt, rdev_stop_ap, @@ -607,7 +627,7 @@ DECLARE_EVENT_CLASS(station_add_change, memcpy(__entry->ht_capa, params->ht_capa, sizeof(struct ieee80211_ht_cap)); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", station flags mask: %u, station flags set: %u, " "station modify mask: %u, listen interval: %d, aid: %u, " "plink action: %u, plink state: %u, uapsd queues: %u", @@ -643,7 +663,7 @@ DECLARE_EVENT_CLASS(wiphy_netdev_mac_evt, NETDEV_ASSIGN; MAC_ASSIGN(sta_mac, mac); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mac: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mac: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac)) ); @@ -683,7 +703,7 @@ TRACE_EVENT(rdev_dump_station, MAC_ASSIGN(sta_mac, mac); __entry->idx = idx; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", station mac: " MAC_PR_FMT ", idx: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(sta_mac), __entry->idx) ); @@ -721,7 +741,7 @@ DECLARE_EVENT_CLASS(mpath_evt, MAC_ASSIGN(dst, dst); MAC_ASSIGN(next_hop, next_hop); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(dst), MAC_PR_ARG(next_hop)) ); @@ -762,7 +782,7 @@ TRACE_EVENT(rdev_dump_mpath, MAC_ASSIGN(next_hop, next_hop); __entry->idx = idx; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d, destination: " + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d, destination: " MAC_PR_FMT ", next hop: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx, MAC_PR_ARG(dst), MAC_PR_ARG(next_hop)) @@ -839,7 +859,7 @@ TRACE_EVENT(rdev_update_mesh_config, MESH_CFG_ASSIGN; __entry->mask = mask; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", mask: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", mask: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->mask) ); @@ -858,7 +878,7 @@ TRACE_EVENT(rdev_join_mesh, NETDEV_ASSIGN; MESH_CFG_ASSIGN; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) ); @@ -884,7 +904,7 @@ TRACE_EVENT(rdev_change_bss, __entry->ap_isolate = params->ap_isolate; __entry->ht_opmode = params->ht_opmode; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", use cts prot: %d, " + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", use cts prot: %d, " "use short preamble: %d, use short slot time: %d, " "ap isolate: %d, ht opmode: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->use_cts_prot, @@ -914,7 +934,7 @@ TRACE_EVENT(rdev_set_txq_params, __entry->cwmax = params->cwmax; __entry->aifs = params->aifs; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", ac: %d, txop: %u, cwmin: %u, cwmax: %u, aifs: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->ac, __entry->txop, __entry->cwmin, __entry->cwmax, __entry->aifs) ); @@ -933,26 +953,24 @@ TRACE_EVENT(rdev_libertas_set_mesh_channel, NETDEV_ASSIGN; CHAN_ASSIGN(chan); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT CHAN_PR_FMT, WIPHY_PR_ARG, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " CHAN_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, CHAN_PR_ARG) ); TRACE_EVENT(rdev_set_monitor_channel, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_channel_type chan_type), - TP_ARGS(wiphy, chan, chan_type), + TP_PROTO(struct wiphy *wiphy, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, chan_type) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(chan); - __entry->chan_type = chan_type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type : %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->chan_type) + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ); TRACE_EVENT(rdev_auth, @@ -974,7 +992,7 @@ TRACE_EVENT(rdev_auth, memset(__entry->bssid, 0, ETH_ALEN); __entry->auth_type = req->auth_type; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", auth type: %d, bssid: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->auth_type, MAC_PR_ARG(bssid)) ); @@ -1002,7 +1020,7 @@ TRACE_EVENT(rdev_assoc, __entry->use_mfp = req->use_mfp; __entry->flags = req->flags; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", previous bssid: " MAC_PR_FMT ", use mfp: %s, flags: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), MAC_PR_ARG(prev_bssid), BOOL_TO_STR(__entry->use_mfp), @@ -1025,7 +1043,7 @@ TRACE_EVENT(rdev_deauth, MAC_ASSIGN(bssid, req->bssid); __entry->reason_code = req->reason_code; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->reason_code) ); @@ -1051,7 +1069,7 @@ TRACE_EVENT(rdev_disassoc, __entry->reason_code = req->reason_code; __entry->local_state_change = req->local_state_change; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", reason: %u, local state change: %s", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->reason_code, @@ -1072,7 +1090,7 @@ TRACE_EVENT(rdev_mgmt_tx_cancel_wait, WDEV_ASSIGN; __entry->cookie = cookie; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu ", + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu ", WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) ); @@ -1092,7 +1110,7 @@ TRACE_EVENT(rdev_set_power_mgmt, __entry->enabled = enabled; __entry->timeout = timeout; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", %senabled, timeout: %d ", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", %senabled, timeout: %d ", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->enabled ? "" : "not ", __entry->timeout) ); @@ -1122,7 +1140,7 @@ TRACE_EVENT(rdev_connect, __entry->wpa_versions = sme->crypto.wpa_versions; __entry->flags = sme->flags; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s, auth type: %d, privacy: %s, wpa versions: %u, " "flags: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid, @@ -1147,7 +1165,7 @@ TRACE_EVENT(rdev_set_cqm_rssi_config, __entry->rssi_thold = rssi_thold; __entry->rssi_hyst = rssi_hyst; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rssi_thold: %d, rssi_hyst: %u ", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rssi_thold, __entry->rssi_hyst) @@ -1171,7 +1189,7 @@ TRACE_EVENT(rdev_set_cqm_txe_config, __entry->pkts = pkts; __entry->intvl = intvl; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", rate: %u, packets: %u, interval: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->rate, __entry->pkts, __entry->intvl) ); @@ -1190,7 +1208,7 @@ TRACE_EVENT(rdev_disconnect, NETDEV_ASSIGN; __entry->reason_code = reason_code; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", reason code: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->reason_code) ); @@ -1211,7 +1229,7 @@ TRACE_EVENT(rdev_join_ibss, memset(__entry->ssid, 0, IEEE80211_MAX_SSID_LEN + 1); memcpy(__entry->ssid, params->ssid, params->ssid_len); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT ", ssid: %s", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid), __entry->ssid) ); @@ -1230,22 +1248,29 @@ TRACE_EVENT(rdev_set_wiphy_params, WIPHY_PR_ARG, __entry->changed) ); +DEFINE_EVENT(wiphy_wdev_evt, rdev_get_tx_power, + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev), + TP_ARGS(wiphy, wdev) +); + TRACE_EVENT(rdev_set_tx_power, - TP_PROTO(struct wiphy *wiphy, enum nl80211_tx_power_setting type, - int mbm), - TP_ARGS(wiphy, type, mbm), + TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, + enum nl80211_tx_power_setting type, int mbm), + TP_ARGS(wiphy, wdev, type, mbm), TP_STRUCT__entry( WIPHY_ENTRY + WDEV_ENTRY __field(enum nl80211_tx_power_setting, type) __field(int, mbm) ), TP_fast_assign( WIPHY_ASSIGN; + WDEV_ASSIGN; __entry->type = type; __entry->mbm = mbm; ), - TP_printk(WIPHY_PR_FMT ", type: %d, mbm: %d", - WIPHY_PR_ARG, __entry->type, __entry->mbm) + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", type: %u, mbm: %d", + WIPHY_PR_ARG, WDEV_PR_ARG,__entry->type, __entry->mbm) ); TRACE_EVENT(rdev_return_int_int, @@ -1305,7 +1330,7 @@ TRACE_EVENT(rdev_set_bitrate_mask, NETDEV_ASSIGN; MAC_ASSIGN(peer, peer); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", peer: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) ); @@ -1325,7 +1350,7 @@ TRACE_EVENT(rdev_mgmt_frame_register, __entry->frame_type = frame_type; __entry->reg = reg; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", frame_type: %u, reg: %s ", + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", frame_type: 0x%.2x, reg: %s ", WIPHY_PR_ARG, WDEV_PR_ARG, __entry->frame_type, __entry->reg ? "true" : "false") ); @@ -1411,7 +1436,7 @@ TRACE_EVENT(rdev_sched_scan_start, WIPHY_ASSIGN; NETDEV_ASSIGN; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG) ); @@ -1439,7 +1464,7 @@ TRACE_EVENT(rdev_tdls_mgmt, __entry->status_code = status_code; memcpy(__get_dynamic_array(buf), buf, len); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", action_code: %u, " + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", action_code: %u, " "dialog_token: %u, status_code: %u, buf: %#.2x ", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->action_code, __entry->dialog_token, @@ -1459,7 +1484,7 @@ TRACE_EVENT(rdev_dump_survey, NETDEV_ASSIGN; __entry->idx = idx; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", index: %d", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", index: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->idx) ); @@ -1516,7 +1541,7 @@ TRACE_EVENT(rdev_tdls_oper, MAC_ASSIGN(peer, peer); __entry->oper = oper; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT ", oper: %d", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT ", oper: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper) ); @@ -1534,7 +1559,7 @@ DECLARE_EVENT_CLASS(rdev_pmksa, NETDEV_ASSIGN; MAC_ASSIGN(bssid, pmksa->bssid); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", bssid: " MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", bssid: " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(bssid)) ); @@ -1552,7 +1577,7 @@ TRACE_EVENT(rdev_probe_client, NETDEV_ASSIGN; MAC_ASSIGN(peer, peer); ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT MAC_PR_FMT, + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", " MAC_PR_FMT, WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer)) ); @@ -1571,25 +1596,22 @@ DEFINE_EVENT(rdev_pmksa, rdev_del_pmksa, TRACE_EVENT(rdev_remain_on_channel, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration), - TP_ARGS(wiphy, wdev, chan, channel_type, duration), + unsigned int duration), + TP_ARGS(wiphy, wdev, chan, duration), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) __field(unsigned int, duration) ), TP_fast_assign( WIPHY_ASSIGN; WDEV_ASSIGN; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; __entry->duration = duration; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", channel type: %d, duration: %u", - WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->channel_type, - __entry->duration) + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", duration: %u", + WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, __entry->duration) ); TRACE_EVENT(rdev_return_int_cookie, @@ -1622,25 +1644,20 @@ TRACE_EVENT(rdev_cancel_remain_on_channel, WDEV_ASSIGN; __entry->cookie = cookie; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT ", cookie: %llu", + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", cookie: %llu", WIPHY_PR_ARG, WDEV_PR_ARG, __entry->cookie) ); TRACE_EVENT(rdev_mgmt_tx, TP_PROTO(struct wiphy *wiphy, struct wireless_dev *wdev, struct ieee80211_channel *chan, bool offchan, - enum nl80211_channel_type channel_type, - bool channel_type_valid, unsigned int wait, bool no_cck, - bool dont_wait_for_ack), - TP_ARGS(wiphy, wdev, chan, offchan, channel_type, channel_type_valid, - wait, no_cck, dont_wait_for_ack), + unsigned int wait, bool no_cck, bool dont_wait_for_ack), + TP_ARGS(wiphy, wdev, chan, offchan, wait, no_cck, dont_wait_for_ack), TP_STRUCT__entry( WIPHY_ENTRY WDEV_ENTRY CHAN_ENTRY __field(bool, offchan) - __field(enum nl80211_channel_type, channel_type) - __field(bool, channel_type_valid) __field(unsigned int, wait) __field(bool, no_cck) __field(bool, dont_wait_for_ack) @@ -1650,18 +1667,14 @@ TRACE_EVENT(rdev_mgmt_tx, WDEV_ASSIGN; CHAN_ASSIGN(chan); __entry->offchan = offchan; - __entry->channel_type = channel_type; - __entry->channel_type_valid = channel_type_valid; __entry->wait = wait; __entry->no_cck = no_cck; __entry->dont_wait_for_ack = dont_wait_for_ack; ), - TP_printk(WIPHY_PR_FMT WDEV_PR_FMT CHAN_PR_FMT ", offchan: %s, " - "channel type: %d, channel type valid: %s, wait: %u, " - "no cck: %s, dont wait for ack: %s", + TP_printk(WIPHY_PR_FMT ", " WDEV_PR_FMT ", " CHAN_PR_FMT ", offchan: %s," + " wait: %u, no cck: %s, dont wait for ack: %s", WIPHY_PR_ARG, WDEV_PR_ARG, CHAN_PR_ARG, - BOOL_TO_STR(__entry->offchan), __entry->channel_type, - BOOL_TO_STR(__entry->channel_type_valid), __entry->wait, + BOOL_TO_STR(__entry->offchan), __entry->wait, BOOL_TO_STR(__entry->no_cck), BOOL_TO_STR(__entry->dont_wait_for_ack)) ); @@ -1680,7 +1693,7 @@ TRACE_EVENT(rdev_set_noack_map, NETDEV_ASSIGN; __entry->noack_map = noack_map; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", noack_map: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", noack_map: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->noack_map) ); @@ -1697,7 +1710,7 @@ TRACE_EVENT(rdev_get_et_sset_count, NETDEV_ASSIGN; __entry->sset = sset; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %d", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %d", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) ); @@ -1714,7 +1727,7 @@ TRACE_EVENT(rdev_get_et_strings, NETDEV_ASSIGN; __entry->sset = sset; ), - TP_printk(WIPHY_PR_FMT NETDEV_PR_FMT ", sset: %u", + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", sset: %u", WIPHY_PR_ARG, NETDEV_PR_ARG, __entry->sset) ); @@ -1723,22 +1736,25 @@ DEFINE_EVENT(wiphy_wdev_evt, rdev_get_channel, TP_ARGS(wiphy, wdev) ); -TRACE_EVENT(rdev_return_channel, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *chan, - enum nl80211_channel_type type), - TP_ARGS(wiphy, chan, type), +TRACE_EVENT(rdev_return_chandef, + TP_PROTO(struct wiphy *wiphy, int ret, + struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, ret, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, type) + __field(int, ret) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(chan); - __entry->type = type; + if (ret == 0) + CHAN_DEF_ASSIGN(chandef); + else + CHAN_DEF_ASSIGN((struct cfg80211_chan_def *)NULL); + __entry->ret = ret; ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel type: %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->type) + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT ", ret: %d", + WIPHY_PR_ARG, CHAN_DEF_PR_ARG, __entry->ret) ); DEFINE_EVENT(wiphy_wdev_evt, rdev_start_p2p_device, @@ -1817,7 +1833,7 @@ TRACE_EVENT(cfg80211_send_rx_assoc, MAC_ASSIGN(bssid, bss->bssid); CHAN_ASSIGN(bss->channel); ), - TP_printk(NETDEV_PR_FMT MAC_PR_FMT CHAN_PR_FMT, + TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", " CHAN_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(bssid), CHAN_PR_ARG) ); @@ -1884,7 +1900,7 @@ TRACE_EVENT(cfg80211_michael_mic_failure, __entry->key_id = key_id; memcpy(__entry->tsc, tsc, 6); ), - TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm", + TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT ", key type: %d, key id: %d, tsc: %pm", NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->key_type, __entry->key_id, __entry->tsc) ); @@ -1892,47 +1908,41 @@ TRACE_EVENT(cfg80211_michael_mic_failure, TRACE_EVENT(cfg80211_ready_on_channel, TP_PROTO(struct wireless_dev *wdev, u64 cookie, struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type, unsigned int duration), - TP_ARGS(wdev, cookie, chan, channel_type, duration), + unsigned int duration), + TP_ARGS(wdev, cookie, chan, duration), TP_STRUCT__entry( WDEV_ENTRY __field(u64, cookie) CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) __field(unsigned int, duration) ), TP_fast_assign( WDEV_ASSIGN; __entry->cookie = cookie; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; __entry->duration = duration; ), - TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d, duration: %u", + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", duration: %u", WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, - __entry->channel_type, __entry->duration) + __entry->duration) ); TRACE_EVENT(cfg80211_ready_on_channel_expired, TP_PROTO(struct wireless_dev *wdev, u64 cookie, - struct ieee80211_channel *chan, - enum nl80211_channel_type channel_type), - TP_ARGS(wdev, cookie, chan, channel_type), + struct ieee80211_channel *chan), + TP_ARGS(wdev, cookie, chan), TP_STRUCT__entry( WDEV_ENTRY __field(u64, cookie) CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) ), TP_fast_assign( WDEV_ASSIGN; __entry->cookie = cookie; CHAN_ASSIGN(chan); - __entry->channel_type = channel_type; ), - TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT ", channel type: %d", - WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG, - __entry->channel_type) + TP_printk(WDEV_PR_FMT ", cookie: %llu, " CHAN_PR_FMT, + WDEV_PR_ARG, __entry->cookie, CHAN_PR_ARG) ); TRACE_EVENT(cfg80211_new_sta, @@ -1949,7 +1959,7 @@ TRACE_EVENT(cfg80211_new_sta, MAC_ASSIGN(mac_addr, mac_addr); SINFO_ASSIGN; ), - TP_printk(NETDEV_PR_FMT MAC_PR_FMT, + TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(mac_addr)) ); @@ -2008,40 +2018,35 @@ TRACE_EVENT(cfg80211_cqm_rssi_notify, NETDEV_PR_ARG, __entry->rssi_event) ); -TRACE_EVENT(cfg80211_can_beacon_sec_chan, - TP_PROTO(struct wiphy *wiphy, struct ieee80211_channel *channel, - enum nl80211_channel_type channel_type), - TP_ARGS(wiphy, channel, channel_type), +TRACE_EVENT(cfg80211_reg_can_beacon, + TP_PROTO(struct wiphy *wiphy, struct cfg80211_chan_def *chandef), + TP_ARGS(wiphy, chandef), TP_STRUCT__entry( WIPHY_ENTRY - CHAN_ENTRY - __field(enum nl80211_channel_type, channel_type) + CHAN_DEF_ENTRY ), TP_fast_assign( WIPHY_ASSIGN; - CHAN_ASSIGN(channel); - __entry->channel_type = channel_type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT ", channel_type: %d", - WIPHY_PR_ARG, CHAN_PR_ARG, __entry->channel_type) + TP_printk(WIPHY_PR_FMT ", " CHAN_DEF_PR_FMT, + WIPHY_PR_ARG, CHAN_DEF_PR_ARG) ); TRACE_EVENT(cfg80211_ch_switch_notify, - TP_PROTO(struct net_device *netdev, int freq, - enum nl80211_channel_type type), - TP_ARGS(netdev, freq, type), + TP_PROTO(struct net_device *netdev, + struct cfg80211_chan_def *chandef), + TP_ARGS(netdev, chandef), TP_STRUCT__entry( NETDEV_ENTRY - __field(int, freq) - __field(enum nl80211_channel_type, type) + CHAN_DEF_ENTRY ), TP_fast_assign( NETDEV_ASSIGN; - __entry->freq = freq; - __entry->type = type; + CHAN_DEF_ASSIGN(chandef); ), - TP_printk(NETDEV_PR_FMT ", freq: %d, type: %d", NETDEV_PR_ARG, - __entry->freq, __entry->type) + TP_printk(NETDEV_PR_FMT ", " CHAN_DEF_PR_FMT, + NETDEV_PR_ARG, CHAN_DEF_PR_ARG) ); DECLARE_EVENT_CLASS(cfg80211_rx_evt, @@ -2055,7 +2060,7 @@ DECLARE_EVENT_CLASS(cfg80211_rx_evt, NETDEV_ASSIGN; MAC_ASSIGN(addr, addr); ), - TP_printk(NETDEV_PR_FMT MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) + TP_printk(NETDEV_PR_FMT ", " MAC_PR_FMT, NETDEV_PR_ARG, MAC_PR_ARG(addr)) ); DEFINE_EVENT(cfg80211_rx_evt, cfg80211_ibss_joined, @@ -2089,7 +2094,7 @@ TRACE_EVENT(cfg80211_probe_status, __entry->cookie = cookie; __entry->acked = acked; ), - TP_printk(NETDEV_PR_FMT MAC_PR_FMT ", cookie: %llu, acked: %s", + TP_printk(NETDEV_PR_FMT " addr:" MAC_PR_FMT ", cookie: %llu, acked: %s", NETDEV_PR_ARG, MAC_PR_ARG(addr), __entry->cookie, BOOL_TO_STR(__entry->acked)) ); @@ -2155,6 +2160,29 @@ TRACE_EVENT(cfg80211_report_obss_beacon, WIPHY_PR_ARG, __entry->freq, __entry->sig_dbm) ); +TRACE_EVENT(cfg80211_tdls_oper_request, + TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, const u8 *peer, + enum nl80211_tdls_operation oper, u16 reason_code), + TP_ARGS(wiphy, netdev, peer, oper, reason_code), + TP_STRUCT__entry( + WIPHY_ENTRY + NETDEV_ENTRY + MAC_ENTRY(peer) + __field(enum nl80211_tdls_operation, oper) + __field(u16, reason_code) + ), + TP_fast_assign( + WIPHY_ASSIGN; + NETDEV_ASSIGN; + MAC_ASSIGN(peer, peer); + __entry->oper = oper; + __entry->reason_code = reason_code; + ), + TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT ", peer: " MAC_PR_FMT ", oper: %d, reason_code %u", + WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer), __entry->oper, + __entry->reason_code) + ); + TRACE_EVENT(cfg80211_scan_done, TP_PROTO(struct cfg80211_scan_request *request, bool aborted), TP_ARGS(request, aborted), @@ -2216,7 +2244,7 @@ TRACE_EVENT(cfg80211_get_bss, __entry->capa_mask = capa_mask; __entry->capa_val = capa_val; ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT MAC_PR_FMT ", buf: %#.2x, " + TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT ", " MAC_PR_FMT ", buf: %#.2x, " "capa_mask: %d, capa_val: %u", WIPHY_PR_ARG, CHAN_PR_ARG, MAC_PR_ARG(bssid), ((u8 *)__get_dynamic_array(ssid))[0], __entry->capa_mask, __entry->capa_val) @@ -2240,7 +2268,7 @@ TRACE_EVENT(cfg80211_inform_bss_frame, memcpy(__get_dynamic_array(mgmt), mgmt, len); __entry->signal = signal; ), - TP_printk(WIPHY_PR_FMT CHAN_PR_FMT "signal: %d", + TP_printk(WIPHY_PR_FMT ", " CHAN_PR_FMT "signal: %d", WIPHY_PR_ARG, CHAN_PR_ARG, __entry->signal) ); @@ -2255,7 +2283,7 @@ DECLARE_EVENT_CLASS(cfg80211_bss_evt, MAC_ASSIGN(bssid, pub->bssid); CHAN_ASSIGN(pub->channel); ), - TP_printk(MAC_PR_FMT CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG) + TP_printk(MAC_PR_FMT ", " CHAN_PR_FMT, MAC_PR_ARG(bssid), CHAN_PR_ARG) ); DEFINE_EVENT(cfg80211_bss_evt, cfg80211_return_bss, diff --git a/net/wireless/util.c b/net/wireless/util.c index 5b6c1df72f3..3cce6e48621 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -944,14 +944,86 @@ static u32 cfg80211_calculate_bitrate_60g(struct rate_info *rate) return __mcs2bitrate[rate->mcs]; } +static u32 cfg80211_calculate_bitrate_vht(struct rate_info *rate) +{ + static const u32 base[4][10] = { + { 6500000, + 13000000, + 19500000, + 26000000, + 39000000, + 52000000, + 58500000, + 65000000, + 78000000, + 0, + }, + { 13500000, + 27000000, + 40500000, + 54000000, + 81000000, + 108000000, + 121500000, + 135000000, + 162000000, + 180000000, + }, + { 29300000, + 58500000, + 87800000, + 117000000, + 175500000, + 234000000, + 263300000, + 292500000, + 351000000, + 390000000, + }, + { 58500000, + 117000000, + 175500000, + 234000000, + 351000000, + 468000000, + 526500000, + 585000000, + 702000000, + 780000000, + }, + }; + u32 bitrate; + int idx; + + if (WARN_ON_ONCE(rate->mcs > 9)) + return 0; + + idx = rate->flags & (RATE_INFO_FLAGS_160_MHZ_WIDTH | + RATE_INFO_FLAGS_80P80_MHZ_WIDTH) ? 3 : + rate->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH ? 2 : + rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH ? 1 : 0; + + bitrate = base[idx][rate->mcs]; + bitrate *= rate->nss; + + if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) + bitrate = (bitrate / 9) * 10; + + /* do NOT round down here */ + return (bitrate + 50000) / 100000; +} + u32 cfg80211_calculate_bitrate(struct rate_info *rate) { int modulation, streams, bitrate; - if (!(rate->flags & RATE_INFO_FLAGS_MCS)) + if (!(rate->flags & RATE_INFO_FLAGS_MCS) && + !(rate->flags & RATE_INFO_FLAGS_VHT_MCS)) return rate->legacy; if (rate->flags & RATE_INFO_FLAGS_60G) return cfg80211_calculate_bitrate_60g(rate); + if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) + return cfg80211_calculate_bitrate_vht(rate); /* the formula below does only work for MCS values smaller than 32 */ if (WARN_ON_ONCE(rate->mcs >= 32)) @@ -980,6 +1052,106 @@ u32 cfg80211_calculate_bitrate(struct rate_info *rate) } EXPORT_SYMBOL(cfg80211_calculate_bitrate); +int cfg80211_get_p2p_attr(const u8 *ies, unsigned int len, + enum ieee80211_p2p_attr_id attr, + u8 *buf, unsigned int bufsize) +{ + u8 *out = buf; + u16 attr_remaining = 0; + bool desired_attr = false; + u16 desired_len = 0; + + while (len > 0) { + unsigned int iedatalen; + unsigned int copy; + const u8 *iedata; + + if (len < 2) + return -EILSEQ; + iedatalen = ies[1]; + if (iedatalen + 2 > len) + return -EILSEQ; + + if (ies[0] != WLAN_EID_VENDOR_SPECIFIC) + goto cont; + + if (iedatalen < 4) + goto cont; + + iedata = ies + 2; + + /* check WFA OUI, P2P subtype */ + if (iedata[0] != 0x50 || iedata[1] != 0x6f || + iedata[2] != 0x9a || iedata[3] != 0x09) + goto cont; + + iedatalen -= 4; + iedata += 4; + + /* check attribute continuation into this IE */ + copy = min_t(unsigned int, attr_remaining, iedatalen); + if (copy && desired_attr) { + desired_len += copy; + if (out) { + memcpy(out, iedata, min(bufsize, copy)); + out += min(bufsize, copy); + bufsize -= min(bufsize, copy); + } + + + if (copy == attr_remaining) + return desired_len; + } + + attr_remaining -= copy; + if (attr_remaining) + goto cont; + + iedatalen -= copy; + iedata += copy; + + while (iedatalen > 0) { + u16 attr_len; + + /* P2P attribute ID & size must fit */ + if (iedatalen < 3) + return -EILSEQ; + desired_attr = iedata[0] == attr; + attr_len = get_unaligned_le16(iedata + 1); + iedatalen -= 3; + iedata += 3; + + copy = min_t(unsigned int, attr_len, iedatalen); + + if (desired_attr) { + desired_len += copy; + if (out) { + memcpy(out, iedata, min(bufsize, copy)); + out += min(bufsize, copy); + bufsize -= min(bufsize, copy); + } + + if (copy == attr_len) + return desired_len; + } + + iedata += copy; + iedatalen -= copy; + attr_remaining = attr_len - copy; + } + + cont: + len -= ies[1] + 2; + ies += ies[1] + 2; + } + + if (attr_remaining && desired_attr) + return -EILSEQ; + + return -ENOENT; +} +EXPORT_SYMBOL(cfg80211_get_p2p_attr); + int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, u32 beacon_int) { diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 6488d2dbc1d..f9680c9cf9b 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -784,6 +784,9 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); + struct cfg80211_chan_def chandef = { + .width = NL80211_CHAN_WIDTH_20_NOHT, + }; int freq, err; switch (wdev->iftype) { @@ -797,8 +800,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.center_freq1 = freq; + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (!chandef.chan) + return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); + err = cfg80211_set_monitor_channel(rdev, &chandef); mutex_unlock(&rdev->devlist_mtx); return err; case NL80211_IFTYPE_MESH_POINT: @@ -807,9 +814,12 @@ static int cfg80211_wext_siwfreq(struct net_device *dev, return freq; if (freq == 0) return -EINVAL; + chandef.center_freq1 = freq; + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (!chandef.chan) + return -EINVAL; mutex_lock(&rdev->devlist_mtx); - err = cfg80211_set_mesh_freq(rdev, wdev, freq, - NL80211_CHAN_NO_HT); + err = cfg80211_set_mesh_channel(rdev, wdev, &chandef); mutex_unlock(&rdev->devlist_mtx); return err; default: @@ -823,8 +833,8 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, { struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy); - struct ieee80211_channel *chan; - enum nl80211_channel_type channel_type; + struct cfg80211_chan_def chandef; + int ret; switch (wdev->iftype) { case NL80211_IFTYPE_STATION: @@ -835,10 +845,10 @@ static int cfg80211_wext_giwfreq(struct net_device *dev, if (!rdev->ops->get_channel) return -EINVAL; - chan = rdev_get_channel(rdev, wdev, &channel_type); - if (!chan) - return -EINVAL; - freq->m = chan->center_freq; + ret = rdev_get_channel(rdev, wdev, &chandef); + if (ret) + return ret; + freq->m = chandef.chan->center_freq; freq->e = 6; return 0; default: @@ -895,7 +905,7 @@ static int cfg80211_wext_siwtxpower(struct net_device *dev, return 0; } - return rdev_set_tx_power(rdev, type, DBM_TO_MBM(dbm)); + return rdev_set_tx_power(rdev, wdev, type, DBM_TO_MBM(dbm)); } static int cfg80211_wext_giwtxpower(struct net_device *dev, @@ -914,7 +924,7 @@ static int cfg80211_wext_giwtxpower(struct net_device *dev, if (!rdev->ops->get_tx_power) return -EOPNOTSUPP; - err = rdev_get_tx_power(rdev, &val); + err = rdev_get_tx_power(rdev, wdev, &val); if (err) return err; diff --git a/net/wireless/wext-sme.c b/net/wireless/wext-sme.c index 1f773f668d1..873af63187c 100644 --- a/net/wireless/wext-sme.c +++ b/net/wireless/wext-sme.c @@ -119,7 +119,16 @@ int cfg80211_mgd_wext_siwfreq(struct net_device *dev, * channel we disconnected above and reconnect below. */ if (chan && !wdev->wext.connect.ssid_len) { - err = cfg80211_set_monitor_channel(rdev, freq, NL80211_CHAN_NO_HT); + struct cfg80211_chan_def chandef = { + .width = NL80211_CHAN_WIDTH_20_NOHT, + .center_freq1 = freq, + }; + + chandef.chan = ieee80211_get_channel(&rdev->wiphy, freq); + if (chandef.chan) + err = cfg80211_set_monitor_channel(rdev, &chandef); + else + err = -EINVAL; goto out; } diff --git a/scripts/headers_install.pl b/scripts/headers_install.pl index 239d22d4207..6c353ae8a45 100644 --- a/scripts/headers_install.pl +++ b/scripts/headers_install.pl @@ -42,6 +42,9 @@ foreach my $filename (@files) { $line =~ s/(^|\s)(inline)\b/$1__$2__/g; $line =~ s/(^|\s)(asm)\b(\s|[(]|$)/$1__$2__$3/g; $line =~ s/(^|\s|[(])(volatile)\b(\s|[(]|$)/$1__$2__$3/g; + $line =~ s/#ifndef _UAPI/#ifndef /; + $line =~ s/#define _UAPI/#define /; + $line =~ s!#endif /[*] _UAPI!#endif /* !; printf {$out} "%s", $line; } close $out; |