bk://gkernel.bkbits.net/netdev-2.6 jgarzik@pobox.com|ChangeSet|20050315050552|15025 jgarzik # This is a BitKeeper generated diff -Nru style patch. # # ChangeSet # 2005/03/15 23:45:24-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # include/linux/pci_ids.h # 2005/03/15 23:45:20-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/14 23:35:31-08:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # net/Kconfig # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/pci_ids.h # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/wireless/orinoco.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tg3.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/r8169.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/pppoe.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Makefile # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2005/03/14 23:35:26-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/14 23:33:56-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/via-rhine.c # 2005/03/14 23:33:51-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/14 23:33:51-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/r8169.c # 2005/03/14 23:33:51-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/15 00:05:52-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/ALL # # net/Kconfig # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # include/linux/pci_ids.h # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/wireless/orinoco.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/tg3.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/r8169.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/pppoe.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Makefile # 2005/03/15 00:05:48-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/15 00:05:47-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/15 00:05:47-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/13 18:21:05-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # drivers/net/wireless/orinoco.c # 2005/03/13 18:21:00-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/13 18:21:00-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Makefile # 2005/03/13 18:21:00-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/13 18:21:00-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2005/03/13 18:20:59-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/12 12:47:53-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # include/linux/pci_ids.h # 2005/03/12 12:47:48-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/12 12:47:48-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/r8169.c # 2005/03/12 12:47:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2005/03/12 12:47:47-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/11 13:10:19-08:00 akpm@bix.(none) # Merge bix.(none):/usr/src/bk25 into bix.(none):/usr/src/bk-netdev # # net/Kconfig # 2005/03/11 13:10:09-08:00 akpm@bix.(none) +0 -0 # Auto merged # # include/linux/pci_ids.h # 2005/03/11 13:10:09-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/tg3.c # 2005/03/11 13:10:09-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/11 13:10:09-08:00 akpm@bix.(none) +0 -0 # Auto merged # # drivers/net/pppoe.c # 2005/03/11 13:10:09-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/10 12:32:09-08:00 akpm@bix.(none) # Merge bk://gkernel.bkbits.net/netdev-2.6 # into bix.(none):/usr/src/bk-netdev # # include/linux/pci_ids.h # 2005/03/10 12:32:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # # MAINTAINERS # 2005/03/10 12:32:05-08:00 akpm@bix.(none) +0 -0 # Auto merged # # ChangeSet # 2005/03/09 22:58:18-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/09 22:58:15-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2005/03/09 22:58:15-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/09 22:58:15-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/09 14:02:24-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/wifi # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/09 14:02:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/wireless/orinoco.c # 2005/03/09 14:02:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/wireless/atmel.c # 2005/03/09 14:02:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2005/03/09 14:02:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/09 14:02:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/09 13:58:51-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/ieee80211 # into pobox.com:/garz/repo/netdev-2.6/wifi # # MAINTAINERS # 2005/03/09 13:58:45-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/09 13:56:06-05:00 jgarzik@pobox.com # Merge # # drivers/net/wireless/atmel.c # 2005/03/09 13:56:05-05:00 jgarzik@pobox.com +0 -0 # SCCS merged # # drivers/net/wireless/orinoco.c # 2005/03/09 13:55:15-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/08 15:52:13-05:00 jgarzik@pobox.com # Merge # # drivers/net/Kconfig # 2005/03/08 15:52:12-05:00 jgarzik@pobox.com +0 -0 # SCCS merged # # include/linux/pci_ids.h # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # include/linux/mv643xx.h # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-velocity.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sis900.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/s2io.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/pppoe.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/mv643xx_eth.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ioc3-eth.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/eepro100.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/bonding/bond_alb.c # 2005/03/08 15:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/08 15:51:23-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/08 15:51:23-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/07 23:22:28-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-velocity.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/typhoon.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/tg3.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/s2io.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/eepro100.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/8139too.c # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/07 23:22:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/06 21:13:44-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/tg3.c # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/s2io.c # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/pppoe.c # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/06 21:13:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/06 21:13:40-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/05 17:51:28-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/smc91x # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # include/linux/mv643xx.h # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/wan/wanxl.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -16 # Auto merged # # drivers/net/via-velocity.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/typhoon.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/tulip/tulip.h # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/sk98lin/skethtool.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/s2io.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/r8169.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -2 # Auto merged # # drivers/net/defxx.c # 2005/03/05 17:51:25-05:00 jgarzik@pobox.com +0 -1 # Auto merged # # drivers/net/Kconfig # 2005/03/05 17:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/03/05 17:51:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/04 18:00:00-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/pci-slot-name # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/via-velocity.c # 2005/03/04 17:59:57-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/typhoon.c # 2005/03/04 17:59:57-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/sk98lin/skge.c # 2005/03/04 17:59:57-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/s2io.c # 2005/03/04 17:59:57-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/04 17:58:47-05:00 greg@kroah.com # [PATCH] net drivers: convert pci_dev->slot_name usage to pci_name() # # Prepare for removal of pci_dev->slot_name # # Signed-off-by: Dave Jones # Signed-off-by: Greg Kroah-Hartman # Signed-off-by: Jeff Garzik # # drivers/net/wan/wanxl.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +16 -22 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/via-velocity.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/typhoon.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/tulip/tulip.h # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/sk98lin/skge.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/sk98lin/skethtool.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/s2io.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/r8169.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +2 -2 # convert pci_dev->slot_name usage to pci_name() # # drivers/net/defxx.c # 2005/03/04 15:43:34-05:00 greg@kroah.com +1 -1 # convert pci_dev->slot_name usage to pci_name() # # ChangeSet # 2005/03/03 22:53:40-05:00 jgarzik@pobox.com # [netdrvr starfire] Add GPL'd firmware, remove compat code # # Contributed by Ion Badulescu , # further fixed up by me. # # drivers/net/starfire_firmware.h # 2005/03/03 22:53:34-05:00 jgarzik@pobox.com +3 -3 # [netdrvr starfire] Add GPL'd firmware, remove compat code # # Contributed by Ion Badulescu , # further fixed up by me. # # drivers/net/starfire.c # 2005/03/03 22:53:34-05:00 jgarzik@pobox.com +40 -102 # [netdrvr starfire] Add GPL'd firmware, remove compat code # # Contributed by Ion Badulescu , # further fixed up by me. # # drivers/net/starfire_firmware.h # 2005/03/03 22:43:01-05:00 jgarzik@pobox.com +346 -0 # # drivers/net/starfire_firmware.h # 2005/03/03 22:43:01-05:00 jgarzik@pobox.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/starfire/drivers/net/starfire_firmware.h # # ChangeSet # 2005/03/03 16:34:03-05:00 shemminger@osdl.org # [PATCH] skge: change driver version # # Change driver version to 0.6 # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 13:40:19-05:00 shemminger@osdl.org +1 -1 # (12/12) skge: change driver version # # ChangeSet # 2005/03/03 16:33:50-05:00 shemminger@osdl.org # [PATCH] skge: fix race with receive interrupt and NAPI # # Avoid possible race with receive interrupt and NAPI. # Move code for chip specific mac interrupt out of main path. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 13:32:58-05:00 shemminger@osdl.org +24 -18 # (11/12) skge: fix race with receive interrupt and NAPI # # ChangeSet # 2005/03/03 16:33:37-05:00 shemminger@osdl.org # [PATCH] skge: use lockless transmit # # Support lockless transmit, because there is existing transmit # lock. Also small declaration style fix. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 13:29:41-05:00 shemminger@osdl.org +10 -2 # (10/12) skge: use lockless transmit # # ChangeSet # 2005/03/03 16:33:24-05:00 shemminger@osdl.org # [PATCH] skge: interrupt coalecsing related fixes # # Avoid overflows (and 64 bit) in tha math for computing interrupt # coalesce values. Turn on transmit coalescing by default. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 13:26:58-05:00 shemminger@osdl.org +27 -16 # (9/12) skge: interrupt coalecsing related fixes # # ChangeSet # 2005/03/03 16:33:11-05:00 shemminger@osdl.org # [PATCH] skge: only allow tx/rx csum on Yukon chipset # # Only allow transmit and receive checksum to be set on Yukon chipsets # because the Genesis chipset support probably is broken based on the # comments in the sk98lin driver. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 12:51:52-05:00 shemminger@osdl.org +19 -4 # (8/12) skge: only allow tx/rx csum on Yukon chipset # # ChangeSet # 2005/03/03 16:32:59-05:00 shemminger@osdl.org # [PATCH] skge: use array for port irq masks # # Use array to represent the transmit and recieve irqmask per port. # And rename txq to txqaddr to be more descriptive # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/03 12:40:43-05:00 shemminger@osdl.org +25 -25 # (7/12) skge: use array for port irq masks # # ChangeSet # 2005/03/03 16:32:47-05:00 shemminger@osdl.org # [PATCH] skge: use chip MIB stats for packet and byte counts # # Use the chip MIB statistics to implement the packet and byte counts. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:29:30-05:00 shemminger@osdl.org +25 -14 # (6/12) skge: use chip MIB stats for packet and byte counts # # ChangeSet # 2005/03/03 16:32:35-05:00 shemminger@osdl.org # [PATCH] skge: simplify definition of wake on lan support # # Add a simple encapsulation of wake on lan support predicate. # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:24:23-05:00 shemminger@osdl.org +8 -9 # (5/12) skge: simplify definition of wake on lan support # # ChangeSet # 2005/03/03 16:32:22-05:00 shemminger@osdl.org # [PATCH] skge: suspend/resume related state management # # Fix suspend/resume logic. On suspend, shutdown board if it is already # up, and on resume only bring it up if it was up before the suspend # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:21:40-05:00 shemminger@osdl.org +11 -6 # (4/12) skge: suspend/resume related state management # # ChangeSet # 2005/03/03 16:32:10-05:00 shemminger@osdl.org # [PATCH] skge: remove unneeded include's # # Get rid of unnecessary include's # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:17:25-05:00 shemminger@osdl.org +0 -3 # (3/12) skge: remove unneeded include's # # ChangeSet # 2005/03/03 16:31:58-05:00 shemminger@osdl.org # [PATCH] skge: spelling and whitespace # # Fix spelling and whitespace issues # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:15:44-05:00 shemminger@osdl.org +5 -6 # (2/12) skge: spelling and whitespace # # ChangeSet # 2005/03/03 16:31:46-05:00 shemminger@osdl.org # [PATCH] skge: use PFX string # # Use the PFX string consistently for all messages # # Signed-off-by: Stephen Hemminger # Signed-off-by: Jeff Garzik # # drivers/net/skge.c # 2005/03/02 20:36:57-05:00 shemminger@osdl.org +7 -7 # (1/12) skge: use PFX string # # ChangeSet # 2005/03/03 02:32:24-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/via-rhine # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/via-rhine.c # 2005/03/03 02:32:21-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:43:01-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/typhoon # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/typhoon.c # 2005/03/03 01:42:58-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:34:47-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/sis900 # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/sis900.c # 2005/03/03 01:34:44-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:33:25-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/pm_message_t # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/via-velocity.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/via-rhine.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/natsemi.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/eepro100.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/8139too.c # 2005/03/03 01:33:22-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:32:18-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/pcnet32 # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/03 01:32:14-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:30:11-05:00 jgarzik@pobox.com # Hand-merge mv643xx conflicts. # # drivers/net/Kconfig # 2005/03/03 01:30:05-05:00 jgarzik@pobox.com +0 -1 # Hand-merge mv643xx conflicts. # # drivers/net/mv643xx_eth.c # 2005/03/03 01:29:02-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:27:44-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/s2io # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/s2io.c # 2005/03/03 01:27:41-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:25:35-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/mips # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/s2io.c # 2005/03/03 01:25:32-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ioc3-eth.c # 2005/03/03 01:25:32-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Makefile # 2005/03/03 01:25:32-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/Kconfig # 2005/03/03 01:25:32-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:17:54-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/janitor # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/via-rhine.c # 2005/03/03 01:17:50-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/ewrk3.c # 2005/03/03 01:17:50-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/eepro100.c # 2005/03/03 01:17:50-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/bonding/bond_alb.c # 2005/03/03 01:17:50-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/amd8111e.c # 2005/03/03 01:17:50-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:14:57-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/e100 # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/Kconfig # 2005/03/03 01:14:53-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:12:59-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/bonding # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/Kconfig # 2005/03/03 01:12:56-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:11:46-05:00 jgarzik@pobox.com # Merge # # drivers/net/wireless/atmel.c # 2005/03/03 01:11:45-05:00 jgarzik@pobox.com +0 -0 # SCCS merged # # ChangeSet # 2005/03/03 01:03:15-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/8139cp # into pobox.com:/garz/repo/netdev-2.6/ALL # # include/linux/pci_ids.h # 2005/03/03 01:03:12-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 01:01:02-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/viro-iomap-orinoco # into pobox.com:/garz/repo/netdev-2.6/ALL # # drivers/net/wireless/orinoco.c # 2005/03/03 01:00:59-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 00:59:59-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/wifi # into pobox.com:/garz/repo/netdev-2.6/ALL # # MAINTAINERS # 2005/03/03 00:59:55-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/03/03 00:25:28-05:00 mgalgoci@parcelfarce.linux.theplanet.co.uk # [PATCH] wireless-2.6 cleanup # # Cleanup drivers/net/wireless/* and remove unnecessary ieee802_11.h # # Signed-off-by: Jeff Garzik # # drivers/net/wireless/wl3501.h # 2005/02/13 19:18:22-05:00 mgalgoci@parcelfarce.linux.theplanet.co.uk +2 -2 # wireless-2.6 cleanup # # drivers/net/wireless/orinoco.c # 2005/02/13 19:18:22-05:00 mgalgoci@parcelfarce.linux.theplanet.co.uk +6 -5 # wireless-2.6 cleanup # # drivers/net/wireless/atmel.c # 2005/02/13 19:18:22-05:00 mgalgoci@parcelfarce.linux.theplanet.co.uk +31 -31 # wireless-2.6 cleanup # # BitKeeper/deleted/.del-ieee802_11.h~8db6b1017efe090e # 2005/03/03 00:25:19-05:00 mgalgoci@parcelfarce.linux.theplanet.co.uk +0 -0 # Delete: drivers/net/wireless/ieee802_11.h # # ChangeSet # 2005/03/02 23:59:30-05:00 okir@suse.de # [PATCH] natsemi: long cable, short cable # # The current version of the natsemi driver comes with a workaround # for a hardware problem that causes link failures on short cables. # Unfortunately, this workaround causes massive link failures on _long_ # cables (270-300ft). This patch adds a module parameter to disable # the workaround if required. # # Signed-off-by: Olaf Kirch # Signed-off-by: Jeff Garzik # # drivers/net/natsemi.c # 2005/03/02 23:59:23-05:00 okir@suse.de +11 -0 # [PATCH] natsemi: long cable, short cable # # The current version of the natsemi driver comes with a workaround # for a hardware problem that causes link failures on short cables. # Unfortunately, this workaround causes massive link failures on _long_ # cables (270-300ft). This patch adds a module parameter to disable # the workaround if required. # # Signed-off-by: Olaf Kirch # Signed-off-by: Jeff Garzik # # ChangeSet # 2005/02/26 03:59:22-05:00 bunk@stusta.de # [PATCH] net/ieee80211/Kconfig: don't describe what gets selected # # The information what gets selected doesn't belong into the help text # (and at least "make menuconfig" already displays this information). # # Signed-off-by: Adrian Bunk # Signed-off-by: Jeff Garzik # # net/ieee80211/Kconfig # 2005/02/24 16:32:02-05:00 bunk@stusta.de +4 -8 # net/ieee80211/Kconfig: don't describe what gets selected # # ChangeSet # 2005/02/24 01:30:33-05:00 viro@parcelfarce.linux.theplanet.co.uk # [PATCH] hostap __user annotations # # Signed-off-by: Al Viro # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_ioctl.c # 2005/02/02 16:31:30-05:00 viro@parcelfarce.linux.theplanet.co.uk +2 -2 # hostap __user annotations # # drivers/net/wireless/hostap/hostap_common.h # 2005/02/02 16:31:30-05:00 viro@parcelfarce.linux.theplanet.co.uk +1 -1 # hostap __user annotations # # ChangeSet # 2005/02/24 01:12:44-05:00 romieu@fr.zoreil.com # [PATCH] ieee80211: offset_in_page() removal # # offset_in_page() removal. # # Signed-off-by: Francois Romieu # Signed-off-by: Jeff Garzik # # include/net/ieee80211.h # 2005/02/13 18:10:56-05:00 romieu@fr.zoreil.com +0 -5 # ieee80211: offset_in_page() removal # # ChangeSet # 2005/02/24 01:12:33-05:00 romieu@fr.zoreil.com # [PATCH] ieee80211: C99 initialization for eap_types # # C99 initialization for eap_types. # # Signed-off-by: Francois Romieu # Signed-off-by: Jeff Garzik # # include/net/ieee80211.h # 2005/02/13 18:10:49-05:00 romieu@fr.zoreil.com +14 -5 # ieee80211: C99 initialization for eap_types # # ChangeSet # 2005/02/24 01:12:21-05:00 romieu@fr.zoreil.com # [PATCH] ieee80211: failure of ieee80211_crypto_init() # # Tell the world that ieee80211_crypto_init() failed and propagate its # status code. # # Signed-off-by: Francois Romieu # Signed-off-by: Jeff Garzik # # net/ieee80211/ieee80211_crypt_wep.c # 2005/02/13 17:38:23-05:00 romieu@fr.zoreil.com +1 -4 # ieee80211: failure of ieee80211_crypto_init() # # net/ieee80211/ieee80211_crypt_tkip.c # 2005/02/13 17:38:23-05:00 romieu@fr.zoreil.com +1 -4 # ieee80211: failure of ieee80211_crypto_init() # # net/ieee80211/ieee80211_crypt_ccmp.c # 2005/02/13 17:38:23-05:00 romieu@fr.zoreil.com +1 -4 # ieee80211: failure of ieee80211_crypto_init() # # net/ieee80211/ieee80211_crypt.c # 2005/02/13 17:38:23-05:00 romieu@fr.zoreil.com +11 -5 # ieee80211: failure of ieee80211_crypto_init() # # ChangeSet # 2005/02/11 16:00:54-05:00 jketreno@linux.intel.com # [PATCH] ieee80211 subsystem # # Signed-off-by: Jeff Garzik # # net/ieee80211/ieee80211_wx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +471 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_tx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +448 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_rx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +1206 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_module.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +268 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_crypt_wep.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +275 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_crypt_tkip.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +711 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_crypt_ccmp.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +473 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_crypt.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +253 -0 # ieee80211 subsystem # # net/ieee80211/Makefile # 2005/02/10 18:55:08-05:00 jketreno@linux.intel.com +11 -0 # ieee80211 subsystem # # net/ieee80211/Kconfig # 2005/02/10 18:48:14-05:00 jketreno@linux.intel.com +71 -0 # ieee80211 subsystem # # include/net/ieee80211_crypt.h # 2005/02/10 17:46:19-05:00 jketreno@linux.intel.com +86 -0 # ieee80211 subsystem # # include/net/ieee80211.h # 2005/02/10 17:46:19-05:00 jketreno@linux.intel.com +883 -0 # ieee80211 subsystem # # net/ieee80211/ieee80211_wx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_wx.c # # net/ieee80211/ieee80211_tx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_tx.c # # net/ieee80211/ieee80211_rx.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_rx.c # # net/ieee80211/ieee80211_module.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_module.c # # net/ieee80211/ieee80211_crypt_wep.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_crypt_wep.c # # net/ieee80211/ieee80211_crypt_tkip.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_crypt_tkip.c # # net/ieee80211/ieee80211_crypt_ccmp.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_crypt_ccmp.c # # net/ieee80211/ieee80211_crypt.c # 2005/02/10 18:07:47-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/ieee80211_crypt.c # # net/ieee80211/Makefile # 2005/02/10 18:55:08-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/Makefile # # net/ieee80211/Kconfig # 2005/02/10 18:48:14-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/net/ieee80211/Kconfig # # net/Makefile # 2005/02/10 18:40:04-05:00 jketreno@linux.intel.com +1 -0 # ieee80211 subsystem # # net/Kconfig # 2005/02/10 18:40:59-05:00 jketreno@linux.intel.com +2 -0 # ieee80211 subsystem # # include/net/ieee80211_crypt.h # 2005/02/10 17:46:19-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/include/net/ieee80211_crypt.h # # include/net/ieee80211.h # 2005/02/10 17:46:19-05:00 jketreno@linux.intel.com +0 -0 # BitKeeper file /garz/repo/netdev-2.6/ieee80211/include/net/ieee80211.h # # ChangeSet # 2005/02/11 15:35:28-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/wifi # # MAINTAINERS # 2005/02/11 15:35:24-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/01/27 17:10:05-05:00 shemminger@osdl.org # [PATCH] skge driver (0.5) # # Version 0.5 of the rewrite of syskonnect gigabit ethernet driver. # # This version 0.5 has: # * Build fixes for alpha and big endian # * Power management, and wake on lan support # * Better hardware error handling for flaky hw. # # Still not tested/debugged: # * Genesis chip support -- does anyone still have one of # these? May just drop it. # * Suspend/resume # # Still not implemented: # * Marvell Yukon2 88E8050 # * VLAN assist # # The patch should work on 2.6.8 or later, I am testing with 2.6.11-rc1. # Also available as download from http://developer.osdl.org/shemminger/skge # # Signed-off-by: Jeff Garzik # # drivers/net/skge.h # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +3005 -0 # skge driver (0.5) # # drivers/net/skge.c # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +3334 -0 # skge driver (0.5) # # drivers/net/skge.h # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +0 -0 # BitKeeper file /garz/repo/netdev-2.6/skge/drivers/net/skge.h # # drivers/net/skge.c # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +0 -0 # BitKeeper file /garz/repo/netdev-2.6/skge/drivers/net/skge.c # # drivers/net/Makefile # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +1 -0 # skge driver (0.5) # # drivers/net/Kconfig # 2005/01/25 16:51:57-05:00 shemminger@osdl.org +12 -0 # skge driver (0.5) # # ChangeSet # 2005/01/18 02:07:04-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/netdev-2.6/8139too # into pobox.com:/garz/repo/netdev-2.6/8139too-2 # # drivers/net/8139too.c # 2005/01/18 02:06:58-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/01/18 01:35:29-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/8139cp # # include/linux/pci_ids.h # 2005/01/18 01:35:20-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/01/18 00:58:46-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/wifi # # drivers/net/wireless/Kconfig # 2005/01/18 00:58:42-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2005/01/18 00:58:42-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/01/12 00:15:47-05:00 jgarzik@pobox.com # Manual 8139cp merge. # # drivers/net/8139cp.c # 2005/01/12 00:15:41-05:00 jgarzik@pobox.com +0 -2 # Manual 8139cp merge. # # include/linux/pci_ids.h # 2005/01/12 00:13:36-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2005/01/06 23:02:43-05:00 penberg@cs.helsinki.fi # [PATCH] 8139too: use iomap for pio/mmio # # Hi, # # This patch converts the 8139too driver to use the iomap infrastructure # for PIO and MMIO instead of playing macro tricks. I also had to fix # read_eeprom(), mdio_sync(), mdio_read(), and mdio_write() to not pass # PIO base address to MMIO read() and write() functions. In addition, # the patch adds proper __iomem annotations for the driver. # # Both modes, PIO and MMIO, were tested with a RealTel RTL8139 card on # an x86 box. The 8129 support remains untested due to lack of # hardware. # # Signed-off-by: Pekka Enberg # Signed-off-by: Jeff Garzik # # drivers/net/8139too.c # 2004/11/05 16:14:10-05:00 penberg@cs.helsinki.fi +87 -107 # 8139too: use iomap for pio/mmio # # ChangeSet # 2005/01/06 20:38:26-05:00 akpm@osdl.org # [PATCH] hostap: fix Kconfig typos and missing select CRYPTO # # From: Joshua Kwan # # CC [M] drivers/net/wireless/hostap/hostap_crypt_wep.o # drivers/net/wireless/hostap/hostap_crypt_wep.c:24:2: #error # CONFIG_CRYPTO is required to build this module. # make[4]: *** [drivers/net/wireless/hostap/hostap_crypt_wep.o] Error 1 # make[3]: *** [drivers/net/wireless/hostap] Error 2 # make[2]: *** [drivers/net/wireless] Error 2 # make[1]: *** [drivers/net] Error 2 # make: *** [drivers] Error 2 # # Here's a patchlet that fixes some Kconfig typos and adds missing select # CRYPTO for each hostap_crypt_* option. # # Signed-off-by: Joshua Kwan # Signed-off-by: Andrew Morton # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/Kconfig # 2004/12/03 08:42:01-05:00 akpm@osdl.org +6 -3 # hostap: fix Kconfig typos and missing select CRYPTO # # ChangeSet # 2005/01/06 19:51:03-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/wifi # # MAINTAINERS # 2005/01/06 19:50:58-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/11/14 18:42:43-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Replaced MODULE_PARM with module_param* # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:42:32-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:42:20-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Updated to use Linux wireless extensions v17 # # Patch from Jean Tourrilhes : # # HostAP WE-17 support: # - allow large scan requests # - export event capability # - new spy data handling # # jkm: removed support for old WE versions (ifdefs) # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:42:09-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: pci_register_driver() return value changes # # Changed pci_register_driver() calls to match with the new behavior # in Linux 2.6.10-rc1. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:41:57-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix netif_carrier_off() in non-client modes # # Connection status is reported properly only in client modes, so do not # try to set netif_carrier_off() in non-client modes. Previously, Master # mode may end up being in state where netif_carrier was left off and # this may break things like bridging. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:41:47-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix PRISM2_IO_DEBUG # # >From Mark Glines : # # I just noticed PRISM2_IO_DEBUG doesn't work. This patch gets it # working again. I checked the development CVS snapshot, looks like # its still broken there. # # jkm: in addition, fix the other PRISM2_IO_DEBUG function # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:41:35-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Use void __iomem * with {read,write}{b,w} # # Start using void __iomem * instead of unsigned long with {read,write}{b,w} # to silence compiler warning with Linux 2.6.9-rc2 and newer. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # ChangeSet # 2004/11/14 18:39:00-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/wifi # # drivers/net/wireless/hostap/hostap_plx.c # 2004/11/13 23:56:53-05:00 jkmaline@cc.hut.fi +2 -2 # Host AP: Replaced MODULE_PARM with module_param* # # drivers/net/wireless/hostap/hostap_hw.c # 2004/11/13 23:56:53-05:00 jkmaline@cc.hut.fi +13 -14 # Host AP: Replaced MODULE_PARM with module_param* # # drivers/net/wireless/hostap/hostap_cs.c # 2004/11/13 23:56:53-05:00 jkmaline@cc.hut.fi +5 -5 # Host AP: Replaced MODULE_PARM with module_param* # # drivers/net/wireless/hostap/hostap_ap.c # 2004/11/13 23:56:53-05:00 jkmaline@cc.hut.fi +4 -4 # Host AP: Replaced MODULE_PARM with module_param* # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +3 -2 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_plx.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +36 -15 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_pci.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +42 -19 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +220 -88 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_hw.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +130 -53 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_download.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +5 -2 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_cs.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +28 -14 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_ap.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +22 -10 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_80211_tx.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +20 -8 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +9 -5 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap.c # 2004/11/13 23:56:44-05:00 jkmaline@cc.hut.fi +44 -22 # Host AP: Replaced direct dev->priv references with netdev_priv(dev). # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/11/13 23:56:35-05:00 jkmaline@cc.hut.fi +1 -5 # Host AP: Updated to use Linux wireless extensions v17 # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/11/13 23:56:35-05:00 jkmaline@cc.hut.fi +26 -4 # Host AP: Updated to use Linux wireless extensions v17 # # drivers/net/wireless/hostap/hostap.c # 2004/11/13 23:56:35-05:00 jkmaline@cc.hut.fi +6 -2 # Host AP: Updated to use Linux wireless extensions v17 # # drivers/net/wireless/hostap/hostap_plx.c # 2004/11/13 23:56:25-05:00 jkmaline@cc.hut.fi +1 -8 # Host AP: pci_register_driver() return value changes # # drivers/net/wireless/hostap/hostap_pci.c # 2004/11/13 23:56:25-05:00 jkmaline@cc.hut.fi +1 -8 # Host AP: pci_register_driver() return value changes # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/11/13 23:56:17-05:00 jkmaline@cc.hut.fi +7 -0 # Host AP: Fix netif_carrier_off() in non-client modes # # drivers/net/wireless/hostap/hostap_hw.c # 2004/11/13 23:56:17-05:00 jkmaline@cc.hut.fi +5 -2 # Host AP: Fix netif_carrier_off() in non-client modes # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/11/13 23:56:09-05:00 jkmaline@cc.hut.fi +4 -2 # Host AP: Fix PRISM2_IO_DEBUG # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/11/13 23:55:58-05:00 jkmaline@cc.hut.fi +2 -1 # Host AP: Use void __iomem * with {read,write}{b,w} # # drivers/net/wireless/hostap/hostap_plx.c # 2004/11/13 23:55:58-05:00 jkmaline@cc.hut.fi +9 -10 # Host AP: Use void __iomem * with {read,write}{b,w} # # drivers/net/wireless/hostap/hostap_pci.c # 2004/11/13 23:55:58-05:00 jkmaline@cc.hut.fi +51 -24 # Host AP: Use void __iomem * with {read,write}{b,w} # # MAINTAINERS # 2004/11/14 18:38:57-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/11/09 02:36:23-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix card enabling after firmware download # # Fix card enabling after firmware download in case any of the # netdevs were up when the download was started. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_hw.c # 2004/11/08 01:39:00-05:00 jkmaline@cc.hut.fi +4 -1 # Host AP: Fix card enabling after firmware download # # ChangeSet # 2004/11/09 02:36:11-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Do not bridge packets to unauthorized ports # # Fix inner-BSS bridge (ap_bridge_packets=1) not to bridge packets to # unauthorized ports when IEEE 802.1X/WPA is used (i.e., require that # the STA completes authentication before capturing packets in the inner # bridge); previously, only association status was used and an attacker # could have capture packets to any MAC address even without having # proper credentials for using the network (although, the packets were # dropped because the controlled port for the STA was unauthorized). # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_ap.h # 2004/11/08 01:38:51-05:00 jkmaline@cc.hut.fi +1 -0 # Host AP: Do not bridge packets to unauthorized ports # # drivers/net/wireless/hostap/hostap_ap.c # 2004/11/08 01:38:51-05:00 jkmaline@cc.hut.fi +19 -0 # Host AP: Do not bridge packets to unauthorized ports # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/11/08 01:38:51-05:00 jkmaline@cc.hut.fi +1 -1 # Host AP: Do not bridge packets to unauthorized ports # # ChangeSet # 2004/11/09 02:35:58-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix compilation with PRISM2_NO_STATION_MODES defined. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/11/08 01:38:44-05:00 jkmaline@cc.hut.fi +4 -1 # Host AP: Fix compilation with PRISM2_NO_STATION_MODES defined. # # ChangeSet # 2004/11/09 02:35:45-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Prevent STAs from associating using AP address # # Prevent STAs from authenticating with AP address (i.e., spoofing AP # MAC address). The inner bridge implementation intercepts packets # before they are passed to Linux net stack, so using AP MAC address # would prevent AP from seeing the packet properly. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_ap.c # 2004/11/08 01:38:35-05:00 jkmaline@cc.hut.fi +2 -1 # Host AP: Prevent STAs from associating using AP address # # ChangeSet # 2004/11/09 02:35:32-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix hw address changing for wifi# interface # # Update wifi# interface MAC address when changing addresses. Without # this, MAC address change does not work correctly with the new # interface design (wifi#/wlan0#). # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap.c # 2004/11/08 01:38:26-05:00 jkmaline@cc.hut.fi +1 -0 # Host AP: Fix hw address changing for wifi# interface # # ChangeSet # 2004/11/09 02:35:21-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Remove ioctl debug messages # # Remove debug message from unsupported ioctls. Some of common ioctls # triggered these (e.g., SIOCGMIIPHY, SIOCGMIIREG, SIOCSMIIREG, # SIOCDEVPRIVATE) and filled debug logs with unwanted messages. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/11/08 01:38:18-05:00 jkmaline@cc.hut.fi +0 -8 # Host AP: Remove ioctl debug messages # # ChangeSet # 2004/11/09 02:35:08-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Ignore (Re)AssocResp messages silently # # Ignore (Re)AssocResp silently since these are not currently needed but # are still received when WPA/RSN mode is enabled. # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/11/08 01:38:11-05:00 jkmaline@cc.hut.fi +7 -0 # Host AP: Ignore (Re)AssocResp messages silently # # ChangeSet # 2004/11/09 02:34:56-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Fix interface packet counters # # Fix wlan#/wifi# interface packet counters (both are supposed to see # data packets once; wlan# was counting TX twice and wifi# did not count # TX or RX at all for most cases). # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_80211_tx.c # 2004/11/08 01:37:59-05:00 jkmaline@cc.hut.fi +6 -6 # Host AP: Fix interface packet counters # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/11/08 01:37:59-05:00 jkmaline@cc.hut.fi +3 -0 # Host AP: Fix interface packet counters # # ChangeSet # 2004/11/09 02:34:43-05:00 jkmaline@cc.hut.fi # [PATCH] Host AP: Disable EAPOL TX/RX debug messages # # Signed-off-by: Jouni Malinen # Signed-off-by: Jeff Garzik # # drivers/net/wireless/hostap/hostap_80211_tx.c # 2004/11/08 01:37:51-05:00 jkmaline@cc.hut.fi +1 -1 # Host AP: Disable EAPOL TX/RX debug messages # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/11/08 01:37:51-05:00 jkmaline@cc.hut.fi +2 -2 # Host AP: Disable EAPOL TX/RX debug messages # # ChangeSet # 2004/11/09 00:09:20-05:00 jgarzik@pobox.com # [wireless hostap] update for new pci_save_state() # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/11/09 00:08:32-05:00 jgarzik@pobox.com +0 -3 # [wireless hostap] update for new pci_save_state() # # drivers/net/wireless/hostap/hostap_pci.c # 2004/11/09 00:08:32-05:00 jgarzik@pobox.com +2 -6 # [wireless hostap] update for new pci_save_state() # # ChangeSet # 2004/11/09 00:01:02-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/wireless-2.6 # # MAINTAINERS # 2004/11/09 00:00:58-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/11/05 00:11:42-05:00 jgarzik@pobox.com # Merge pobox.com:/garz/repo/linux-2.6 # into pobox.com:/garz/repo/netdev-2.6/8139cp # # include/linux/pci_ids.h # 2004/11/05 00:11:37-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # drivers/net/8139cp.c # 2004/11/05 00:11:37-05:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/10/15 13:18:39-04:00 Philipp.Gortan@tttech.com # [netdrvr 8139cp] add PCI ID # # include/linux/pci_ids.h # 2004/10/15 13:18:32-04:00 Philipp.Gortan@tttech.com +3 -0 # [netdrvr 8139cp] add PCI ID # # drivers/net/8139cp.c # 2004/10/15 13:18:32-04:00 Philipp.Gortan@tttech.com +2 -0 # [netdrvr 8139cp] add PCI ID # # ChangeSet # 2004/10/01 00:16:39-04:00 felipewd@terra.com.br # [PATCH] 8139cp net driver: add MODULE_VERSION # # drivers/net/8139cp.c # 2004/09/23 00:02:58-04:00 felipewd@terra.com.br +1 -0 # 8139cp net driver: add MODULE_VERSION # # ChangeSet # 2004/09/30 23:24:55-04:00 klassert@mathematik.tu-chemnitz.de # [PATCH] 8139cp - add netpoll support # # Patch adds netpoll support to the 8139cp driver. # The patch needs some tests because I have no NIC of this type for testing. # # Applies against linux-2.6.9-rc2-mm3 # # Signed-off-by: Steffen Klassert # # drivers/net/8139cp.c # 2004/09/27 07:55:04-04:00 klassert@mathematik.tu-chemnitz.de +19 -0 # 8139cp - add netpoll support # # ChangeSet # 2004/09/20 14:48:28-04:00 shemminger@osdl.org # [PATCH] 8139cp - module_param # # Not sure if I sent this already... # Convert 8139cp to use new module_param() not old MODULE_PARM # # Signed-off-by: Stephen Hemminger # # drivers/net/8139cp.c # 2004/07/23 16:54:25-04:00 shemminger@osdl.org +3 -2 # 8139cp - module_param # # ChangeSet # 2004/08/31 14:24:59-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6 # into pobox.com:/spare/repo/wireless-2.6 # # MAINTAINERS # 2004/08/31 14:24:54-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/08/31 03:20:31-04:00 romieu@fr.zoreil.com # [PATCH] 8139cp: SG support fixes # # - suspicious length in pci_unmap_single; # - wait for the last frag before freeing the relevant skb; # - no need to crash when facing some unexpected csum combination. # # drivers/net/8139cp.c # 2004/08/30 15:21:23-04:00 romieu@fr.zoreil.com +13 -12 # 8139cp: SG support fixes # # ChangeSet # 2004/08/31 03:16:23-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/net-drivers-2.6 # into pobox.com:/spare/repo/netdev-2.6/8139cp # # drivers/net/8139cp.c # 2004/08/31 03:16:19-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/08/29 17:19:44-04:00 jgarzik@pobox.com # [netdrvr 8139cp] TSO support # # drivers/net/8139cp.c # 2004/08/29 17:19:37-04:00 jgarzik@pobox.com +33 -17 # [netdrvr 8139cp] TSO support # # ChangeSet # 2004/08/11 14:03:17-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6 # into pobox.com:/spare/repo/wireless-2.6 # # drivers/net/wireless/Kconfig # 2004/08/11 14:03:12-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # MAINTAINERS # 2004/08/11 14:03:12-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/16 22:14:18-04:00 jgarzik@pobox.com # Merge pobox.com:/spare/repo/linux-2.6.7 # into pobox.com:/spare/repo/wireless-2.6 # # MAINTAINERS # 2004/06/16 22:14:14-04:00 jgarzik@pobox.com +0 -0 # Auto merged # # ChangeSet # 2004/06/03 02:49:19-04:00 jkmaline@cc.hut.fi # [PATCH] fix hostap crypto bugs # # On Wed, Jun 02, 2004 at 09:36:34PM -0700, David S. Miller wrote: # # > You cannot invoke virt_to_page() on addresses on the kernel stack, # > and that is what the various HostAP crypto modules are doing. # > # > This happens to work on some platforms, but it is going to explode # > on others. # # Thanks! I have not updated my non-x86 platforms to 2.6 kernels, so I had # not yet had a change to explode anything with this.. # # > Allocate these little header scratch area blobs in the per-crypto-instance # > structs you kmalloc instead. # # This patch (for wireless-2.6) should do this. I used separate buffers # for RX and TX because they could be in theory called concurrently. # Better to get this first working, but it might be worthwhile to consider # the memory use at some point. This version uses 112 bytes of additional # scratch buffers per key for CCMP. # # drivers/net/wireless/hostap/hostap_crypt_tkip.c # 2004/06/03 02:01:06-04:00 jkmaline@cc.hut.fi +7 -6 # Re: hostap crypto bugs # # drivers/net/wireless/hostap/hostap_crypt_ccmp.c # 2004/06/03 01:51:28-04:00 jkmaline@cc.hut.fi +16 -7 # Re: hostap crypto bugs # # ChangeSet # 2004/06/02 23:24:41-04:00 jkmaline@cc.hut.fi # Add HostAP wireless driver. # # drivers/net/wireless/Makefile # 2004/06/02 23:24:35-04:00 jkmaline@cc.hut.fi +2 -0 # Add HostAP wireless driver. # # drivers/net/wireless/Kconfig # 2004/06/02 23:24:35-04:00 jkmaline@cc.hut.fi +2 -0 # Add HostAP wireless driver. # # MAINTAINERS # 2004/06/02 23:24:35-04:00 jkmaline@cc.hut.fi +7 -0 # Add HostAP wireless driver. # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +1074 -0 # # drivers/net/wireless/hostap/hostap_proc.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +466 -0 # # drivers/net/wireless/hostap/hostap_plx.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +598 -0 # # drivers/net/wireless/hostap/hostap_pci.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +413 -0 # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +3468 -0 # # drivers/net/wireless/hostap/hostap_info.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +469 -0 # # drivers/net/wireless/hostap/hostap_hw.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +3525 -0 # # drivers/net/wireless/hostap/hostap_download.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +758 -0 # # drivers/net/wireless/hostap/hostap_cs.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +771 -0 # # drivers/net/wireless/hostap/hostap_crypt_wep.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +281 -0 # # drivers/net/wireless/hostap/hostap.h # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +57 -0 # # drivers/net/wireless/hostap/Makefile # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +8 -0 # # drivers/net/wireless/hostap/Kconfig # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +101 -0 # # drivers/net/wireless/hostap/hostap_wlan.h # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_wlan.h # # drivers/net/wireless/hostap/hostap_proc.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_proc.c # # drivers/net/wireless/hostap/hostap_plx.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_plx.c # # drivers/net/wireless/hostap/hostap_pci.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_pci.c # # drivers/net/wireless/hostap/hostap_ioctl.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_ioctl.c # # drivers/net/wireless/hostap/hostap_info.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_info.c # # drivers/net/wireless/hostap/hostap_hw.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_hw.c # # drivers/net/wireless/hostap/hostap_download.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_download.c # # drivers/net/wireless/hostap/hostap_cs.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_cs.c # # drivers/net/wireless/hostap/hostap_crypt_wep.c # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_crypt_wep.c # # drivers/net/wireless/hostap/hostap_crypt_tkip.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +695 -0 # # drivers/net/wireless/hostap/hostap_crypt_ccmp.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +477 -0 # # drivers/net/wireless/hostap/hostap_crypt.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +50 -0 # # drivers/net/wireless/hostap/hostap_crypt.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +167 -0 # # drivers/net/wireless/hostap/hostap_config.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +86 -0 # # drivers/net/wireless/hostap/hostap_common.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +556 -0 # # drivers/net/wireless/hostap/hostap_ap.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +271 -0 # # drivers/net/wireless/hostap/hostap_ap.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +3227 -0 # # drivers/net/wireless/hostap/hostap_80211_tx.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +510 -0 # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +1066 -0 # # drivers/net/wireless/hostap/hostap_80211.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +107 -0 # # drivers/net/wireless/hostap/hostap.h # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap.h # # drivers/net/wireless/hostap/hostap.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +1178 -0 # # drivers/net/wireless/hostap/Makefile # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/Makefile # # drivers/net/wireless/hostap/Kconfig # 2004/06/02 23:17:30-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/Kconfig # # drivers/net/wireless/hostap/hostap_crypt_tkip.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_crypt_tkip.c # # drivers/net/wireless/hostap/hostap_crypt_ccmp.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_crypt_ccmp.c # # drivers/net/wireless/hostap/hostap_crypt.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_crypt.h # # drivers/net/wireless/hostap/hostap_crypt.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_crypt.c # # drivers/net/wireless/hostap/hostap_config.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_config.h # # drivers/net/wireless/hostap/hostap_common.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_common.h # # drivers/net/wireless/hostap/hostap_ap.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_ap.h # # drivers/net/wireless/hostap/hostap_ap.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_ap.c # # drivers/net/wireless/hostap/hostap_80211_tx.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_80211_tx.c # # drivers/net/wireless/hostap/hostap_80211_rx.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_80211_rx.c # # drivers/net/wireless/hostap/hostap_80211.h # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap_80211.h # # drivers/net/wireless/hostap/hostap.c # 2004/06/02 23:17:29-04:00 jgarzik@redhat.com +0 -0 # BitKeeper file /spare/repo/wireless-2.6/drivers/net/wireless/hostap/hostap.c # diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS 2005-03-15 23:46:53 -08:00 +++ b/MAINTAINERS 2005-03-15 23:46:53 -08:00 @@ -973,6 +973,13 @@ L: iss_storagedev@hp.com S: Supported +HOST AP DRIVER +P: Jouni Malinen +M: jkmaline@cc.hut.fi +L: hostap@shmoo.com +W: http://hostap.epitest.fi/ +S: Maintained + HP100: Driver for HP 10/100 Mbit/s Voice Grade Network Adapter Series P: Jaroslav Kysela M: perex@suse.cz diff -Nru a/drivers/net/8139cp.c b/drivers/net/8139cp.c --- a/drivers/net/8139cp.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/8139cp.c 2005-03-15 23:46:53 -08:00 @@ -54,6 +54,7 @@ #include #include +#include #include #include #include @@ -91,16 +92,17 @@ MODULE_AUTHOR("Jeff Garzik "); MODULE_DESCRIPTION("RealTek RTL-8139C+ series 10/100 PCI Ethernet driver"); +MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); static int debug = -1; -MODULE_PARM (debug, "i"); +module_param(debug, int, 0); MODULE_PARM_DESC (debug, "8139cp: bitmapped message enable number"); /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static int multicast_filter_limit = 32; -MODULE_PARM (multicast_filter_limit, "i"); +module_param(multicast_filter_limit, int, 0); MODULE_PARM_DESC (multicast_filter_limit, "8139cp: maximum number of filtered multicast addresses"); #define PFX DRV_NAME ": " @@ -186,6 +188,9 @@ RingEnd = (1 << 30), /* End of descriptor ring */ FirstFrag = (1 << 29), /* First segment of a packet */ LastFrag = (1 << 28), /* Final segment of a packet */ + LargeSend = (1 << 27), /* TCP Large Send Offload (TSO) */ + MSSShift = 16, /* MSS value position */ + MSSMask = 0xfff, /* MSS value: 11 bits */ TxError = (1 << 23), /* Tx error summary */ RxError = (1 << 20), /* Rx error summary */ IPCS = (1 << 18), /* Calculate IP checksum */ @@ -312,7 +317,7 @@ struct ring_info { struct sk_buff *skb; dma_addr_t mapping; - unsigned frag; + u32 len; }; struct cp_dma_stats { @@ -394,6 +399,9 @@ static void __cp_set_rx_mode (struct net_device *dev); static void cp_tx (struct cp_private *cp); static void cp_clean_rings (struct cp_private *cp); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void cp_poll_controller(struct net_device *dev); +#endif static struct pci_device_id cp_pci_tbl[] = { { PCI_VENDOR_ID_REALTEK, PCI_DEVICE_ID_REALTEK_8139, @@ -688,6 +696,19 @@ return IRQ_HANDLED; } +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling receive - used by netconsole and other diagnostic tools + * to allow network i/o with interrupts disabled. + */ +static void cp_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + cp_interrupt(dev->irq, dev, NULL); + enable_irq(dev->irq); +} +#endif + static void cp_tx (struct cp_private *cp) { unsigned tx_head = cp->tx_head; @@ -707,7 +728,7 @@ BUG(); pci_unmap_single(cp->pdev, cp->tx_skb[tx_tail].mapping, - skb->len, PCI_DMA_TODEVICE); + cp->tx_skb[tx_tail].len, PCI_DMA_TODEVICE); if (status & LastFrag) { if (status & (TxError | TxFIFOUnder)) { @@ -749,10 +770,11 @@ { struct cp_private *cp = netdev_priv(dev); unsigned entry; - u32 eor; + u32 eor, flags; #if CP_VLAN_TAG_USED u32 vlan_tag = 0; #endif + int mss = 0; spin_lock_irq(&cp->lock); @@ -772,6 +794,9 @@ entry = cp->tx_head; eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; + if (dev->features & NETIF_F_TSO) + mss = skb_shinfo(skb)->tso_size; + if (skb_shinfo(skb)->nr_frags == 0) { struct cp_desc *txd = &cp->tx_ring[entry]; u32 len; @@ -783,26 +808,26 @@ txd->addr = cpu_to_le64(mapping); wmb(); - if (skb->ip_summed == CHECKSUM_HW) { + flags = eor | len | DescOwn | FirstFrag | LastFrag; + + if (mss) + flags |= LargeSend | ((mss & MSSMask) << MSSShift); + else if (skb->ip_summed == CHECKSUM_HW) { const struct iphdr *ip = skb->nh.iph; if (ip->protocol == IPPROTO_TCP) - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag | - IPCS | TCPCS); + flags |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag | - IPCS | UDPCS); + flags |= IPCS | UDPCS; else - BUG(); - } else - txd->opts1 = cpu_to_le32(eor | len | DescOwn | - FirstFrag | LastFrag); + WARN_ON(1); /* we need a WARN() */ + } + + txd->opts1 = cpu_to_le32(flags); wmb(); cp->tx_skb[entry].skb = skb; cp->tx_skb[entry].mapping = mapping; - cp->tx_skb[entry].frag = 0; + cp->tx_skb[entry].len = len; entry = NEXT_TX(entry); } else { struct cp_desc *txd; @@ -820,7 +845,7 @@ first_len, PCI_DMA_TODEVICE); cp->tx_skb[entry].skb = skb; cp->tx_skb[entry].mapping = first_mapping; - cp->tx_skb[entry].frag = 1; + cp->tx_skb[entry].len = first_len; entry = NEXT_TX(entry); for (frag = 0; frag < skb_shinfo(skb)->nr_frags; frag++) { @@ -836,16 +861,19 @@ len, PCI_DMA_TODEVICE); eor = (entry == (CP_TX_RING_SIZE - 1)) ? RingEnd : 0; - if (skb->ip_summed == CHECKSUM_HW) { - ctrl = eor | len | DescOwn | IPCS; + ctrl = eor | len | DescOwn; + + if (mss) + ctrl |= LargeSend | + ((mss & MSSMask) << MSSShift); + else if (skb->ip_summed == CHECKSUM_HW) { if (ip->protocol == IPPROTO_TCP) - ctrl |= TCPCS; + ctrl |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) - ctrl |= UDPCS; + ctrl |= IPCS | UDPCS; else BUG(); - } else - ctrl = eor | len | DescOwn; + } if (frag == skb_shinfo(skb)->nr_frags - 1) ctrl |= LastFrag; @@ -860,7 +888,7 @@ cp->tx_skb[entry].skb = skb; cp->tx_skb[entry].mapping = mapping; - cp->tx_skb[entry].frag = frag + 2; + cp->tx_skb[entry].len = len; entry = NEXT_TX(entry); } @@ -1074,7 +1102,6 @@ cp->rx_skb[i].mapping = pci_map_single(cp->pdev, skb->tail, cp->rx_buf_sz, PCI_DMA_FROMDEVICE); cp->rx_skb[i].skb = skb; - cp->rx_skb[i].frag = 0; cp->rx_ring[i].opts2 = 0; cp->rx_ring[i].addr = cpu_to_le64(cp->rx_skb[i].mapping); @@ -1126,9 +1153,6 @@ { unsigned i; - memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); - memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); - for (i = 0; i < CP_RX_RING_SIZE; i++) { if (cp->rx_skb[i].skb) { pci_unmap_single(cp->pdev, cp->rx_skb[i].mapping, @@ -1140,13 +1164,18 @@ for (i = 0; i < CP_TX_RING_SIZE; i++) { if (cp->tx_skb[i].skb) { struct sk_buff *skb = cp->tx_skb[i].skb; + pci_unmap_single(cp->pdev, cp->tx_skb[i].mapping, - skb->len, PCI_DMA_TODEVICE); - dev_kfree_skb(skb); + cp->tx_skb[i].len, PCI_DMA_TODEVICE); + if (le32_to_cpu(cp->tx_ring[i].opts1) & LastFrag) + dev_kfree_skb(skb); cp->net_stats.tx_dropped++; } } + memset(cp->rx_ring, 0, sizeof(struct cp_desc) * CP_RX_RING_SIZE); + memset(cp->tx_ring, 0, sizeof(struct cp_desc) * CP_TX_RING_SIZE); + memset(&cp->rx_skb, 0, sizeof(struct ring_info) * CP_RX_RING_SIZE); memset(&cp->tx_skb, 0, sizeof(struct ring_info) * CP_TX_RING_SIZE); } @@ -1538,6 +1567,8 @@ .set_tx_csum = ethtool_op_set_tx_csum, /* local! */ .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, + .get_tso = ethtool_op_get_tso, + .set_tso = ethtool_op_set_tso, .get_regs = cp_get_regs, .get_wol = cp_get_wol, .set_wol = cp_set_wol, @@ -1749,6 +1780,9 @@ dev->get_stats = cp_get_stats; dev->do_ioctl = cp_ioctl; dev->poll = cp_rx_poll; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = cp_poll_controller; +#endif dev->weight = 16; /* arbitrary? from NAPI_HOWTO.txt. */ #ifdef BROKEN dev->change_mtu = cp_change_mtu; @@ -1767,6 +1801,10 @@ if (pci_using_dac) dev->features |= NETIF_F_HIGHDMA; + +#if 0 /* disabled by default until verified */ + dev->features |= NETIF_F_TSO; +#endif dev->irq = pdev->irq; diff -Nru a/drivers/net/8139too.c b/drivers/net/8139too.c --- a/drivers/net/8139too.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/8139too.c 2005-03-15 23:46:53 -08:00 @@ -569,7 +569,7 @@ }; struct rtl8139_private { - void *mmio_addr; + void __iomem *mmio_addr; int drv_flags; struct pci_dev *pci_dev; u32 msg_enable; @@ -614,7 +614,7 @@ MODULE_PARM_DESC (media, "8139too: Bits 4+9: force full duplex, bit 5: 100Mbps"); MODULE_PARM_DESC (full_duplex, "8139too: Force full duplex for board(s) (1)"); -static int read_eeprom (void *ioaddr, int location, int addr_len); +static int read_eeprom (void __iomem *ioaddr, int location, int addr_len); static int rtl8139_open (struct net_device *dev); static int mdio_read (struct net_device *dev, int phy_id, int location); static void mdio_write (struct net_device *dev, int phy_id, int location, @@ -638,46 +638,20 @@ static void rtl8139_hw_start (struct net_device *dev); static struct ethtool_ops rtl8139_ethtool_ops; -#ifdef USE_IO_OPS - -#define RTL_R8(reg) inb (((unsigned long)ioaddr) + (reg)) -#define RTL_R16(reg) inw (((unsigned long)ioaddr) + (reg)) -#define RTL_R32(reg) ((unsigned long) inl (((unsigned long)ioaddr) + (reg))) -#define RTL_W8(reg, val8) outb ((val8), ((unsigned long)ioaddr) + (reg)) -#define RTL_W16(reg, val16) outw ((val16), ((unsigned long)ioaddr) + (reg)) -#define RTL_W32(reg, val32) outl ((val32), ((unsigned long)ioaddr) + (reg)) -#define RTL_W8_F RTL_W8 -#define RTL_W16_F RTL_W16 -#define RTL_W32_F RTL_W32 -#undef readb -#undef readw -#undef readl -#undef writeb -#undef writew -#undef writel -#define readb(addr) inb((unsigned long)(addr)) -#define readw(addr) inw((unsigned long)(addr)) -#define readl(addr) inl((unsigned long)(addr)) -#define writeb(val,addr) outb((val),(unsigned long)(addr)) -#define writew(val,addr) outw((val),(unsigned long)(addr)) -#define writel(val,addr) outl((val),(unsigned long)(addr)) - -#else - /* write MMIO register, with flush */ /* Flush avoids rtl8139 bug w/ posted MMIO writes */ -#define RTL_W8_F(reg, val8) do { writeb ((val8), ioaddr + (reg)); readb (ioaddr + (reg)); } while (0) -#define RTL_W16_F(reg, val16) do { writew ((val16), ioaddr + (reg)); readw (ioaddr + (reg)); } while (0) -#define RTL_W32_F(reg, val32) do { writel ((val32), ioaddr + (reg)); readl (ioaddr + (reg)); } while (0) +#define RTL_W8_F(reg, val8) do { iowrite8 ((val8), ioaddr + (reg)); ioread8 (ioaddr + (reg)); } while (0) +#define RTL_W16_F(reg, val16) do { iowrite16 ((val16), ioaddr + (reg)); ioread16 (ioaddr + (reg)); } while (0) +#define RTL_W32_F(reg, val32) do { iowrite32 ((val32), ioaddr + (reg)); ioread32 (ioaddr + (reg)); } while (0) #define MMIO_FLUSH_AUDIT_COMPLETE 1 #if MMIO_FLUSH_AUDIT_COMPLETE /* write MMIO register */ -#define RTL_W8(reg, val8) writeb ((val8), ioaddr + (reg)) -#define RTL_W16(reg, val16) writew ((val16), ioaddr + (reg)) -#define RTL_W32(reg, val32) writel ((val32), ioaddr + (reg)) +#define RTL_W8(reg, val8) iowrite8 ((val8), ioaddr + (reg)) +#define RTL_W16(reg, val16) iowrite16 ((val16), ioaddr + (reg)) +#define RTL_W32(reg, val32) iowrite32 ((val32), ioaddr + (reg)) #else @@ -689,11 +663,9 @@ #endif /* MMIO_FLUSH_AUDIT_COMPLETE */ /* read MMIO register */ -#define RTL_R8(reg) readb (ioaddr + (reg)) -#define RTL_R16(reg) readw (ioaddr + (reg)) -#define RTL_R32(reg) ((unsigned long) readl (ioaddr + (reg))) - -#endif /* USE_IO_OPS */ +#define RTL_R8(reg) ioread8 (ioaddr + (reg)) +#define RTL_R16(reg) ioread16 (ioaddr + (reg)) +#define RTL_R32(reg) ((unsigned long) ioread32 (ioaddr + (reg))) static const u16 rtl8139_intr_mask = @@ -740,10 +712,13 @@ assert (tp->pci_dev != NULL); pdev = tp->pci_dev; -#ifndef USE_IO_OPS +#ifdef USE_IO_OPS + if (tp->mmio_addr) + ioport_unmap (tp->mmio_addr); +#else if (tp->mmio_addr) - iounmap (tp->mmio_addr); -#endif /* !USE_IO_OPS */ + pci_iounmap (pdev, tp->mmio_addr); +#endif /* USE_IO_OPS */ /* it's ok to call this even if we have no regions to free */ pci_release_regions (pdev); @@ -754,7 +729,7 @@ } -static void rtl8139_chip_reset (void *ioaddr) +static void rtl8139_chip_reset (void __iomem *ioaddr) { int i; @@ -774,7 +749,7 @@ static int __devinit rtl8139_init_board (struct pci_dev *pdev, struct net_device **dev_out) { - void *ioaddr; + void __iomem *ioaddr; struct net_device *dev; struct rtl8139_private *tp; u8 tmp8; @@ -855,13 +830,18 @@ pci_set_master (pdev); #ifdef USE_IO_OPS - ioaddr = (void *) pio_start; + ioaddr = ioport_map(pio_start, pio_len); + if (!ioaddr) { + printk (KERN_ERR PFX "%s: cannot map PIO, aborting\n", pci_name(pdev)); + rc = -EIO; + goto err_out; + } dev->base_addr = pio_start; tp->mmio_addr = ioaddr; tp->regs_len = pio_len; #else /* ioremap MMIO region */ - ioaddr = ioremap (mmio_start, mmio_len); + ioaddr = pci_iomap(pdev, 1, 0); if (ioaddr == NULL) { printk (KERN_ERR PFX "%s: cannot remap MMIO, aborting\n", pci_name(pdev)); rc = -EIO; @@ -945,7 +925,7 @@ struct net_device *dev = NULL; struct rtl8139_private *tp; int i, addr_len, option; - void *ioaddr; + void __iomem *ioaddr; static int board_idx = -1; u8 pci_rev; @@ -1143,47 +1123,46 @@ No extra delay is needed with 33Mhz PCI, but 66Mhz may change this. */ -#define eeprom_delay() readl(ee_addr) +#define eeprom_delay() RTL_R32(Cfg9346) /* The EEPROM commands include the alway-set leading bit. */ #define EE_WRITE_CMD (5) #define EE_READ_CMD (6) #define EE_ERASE_CMD (7) -static int __devinit read_eeprom (void *ioaddr, int location, int addr_len) +static int __devinit read_eeprom (void __iomem *ioaddr, int location, int addr_len) { int i; unsigned retval = 0; - void *ee_addr = ioaddr + Cfg9346; int read_cmd = location | (EE_READ_CMD << addr_len); - writeb (EE_ENB & ~EE_CS, ee_addr); - writeb (EE_ENB, ee_addr); + RTL_W8 (Cfg9346, EE_ENB & ~EE_CS); + RTL_W8 (Cfg9346, EE_ENB); eeprom_delay (); /* Shift the read command bits out. */ for (i = 4 + addr_len; i >= 0; i--) { int dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; - writeb (EE_ENB | dataval, ee_addr); + RTL_W8 (Cfg9346, EE_ENB | dataval); eeprom_delay (); - writeb (EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + RTL_W8 (Cfg9346, EE_ENB | dataval | EE_SHIFT_CLK); eeprom_delay (); } - writeb (EE_ENB, ee_addr); + RTL_W8 (Cfg9346, EE_ENB); eeprom_delay (); for (i = 16; i > 0; i--) { - writeb (EE_ENB | EE_SHIFT_CLK, ee_addr); + RTL_W8 (Cfg9346, EE_ENB | EE_SHIFT_CLK); eeprom_delay (); retval = - (retval << 1) | ((readb (ee_addr) & EE_DATA_READ) ? 1 : + (retval << 1) | ((RTL_R8 (Cfg9346) & EE_DATA_READ) ? 1 : 0); - writeb (EE_ENB, ee_addr); + RTL_W8 (Cfg9346, EE_ENB); eeprom_delay (); } /* Terminate the EEPROM access. */ - writeb (~EE_CS, ee_addr); + RTL_W8 (Cfg9346, ~EE_CS); eeprom_delay (); return retval; @@ -1202,7 +1181,7 @@ #define MDIO_WRITE0 (MDIO_DIR) #define MDIO_WRITE1 (MDIO_DIR | MDIO_DATA_OUT) -#define mdio_delay(mdio_addr) readb(mdio_addr) +#define mdio_delay() RTL_R8(Config4) static char mii_2_8139_map[8] = { @@ -1219,15 +1198,15 @@ #ifdef CONFIG_8139TOO_8129 /* Syncronize the MII management interface by shifting 32 one bits out. */ -static void mdio_sync (void *mdio_addr) +static void mdio_sync (void __iomem *ioaddr) { int i; for (i = 32; i >= 0; i--) { - writeb (MDIO_WRITE1, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_WRITE1 | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); + RTL_W8 (Config4, MDIO_WRITE1); + mdio_delay (); + RTL_W8 (Config4, MDIO_WRITE1 | MDIO_CLK); + mdio_delay (); } } #endif @@ -1237,35 +1216,36 @@ struct rtl8139_private *tp = netdev_priv(dev); int retval = 0; #ifdef CONFIG_8139TOO_8129 - void *mdio_addr = tp->mmio_addr + Config4; + void __iomem *ioaddr = tp->mmio_addr; int mii_cmd = (0xf6 << 10) | (phy_id << 5) | location; int i; #endif if (phy_id > 31) { /* Really a 8139. Use internal registers. */ + void __iomem *ioaddr = tp->mmio_addr; return location < 8 && mii_2_8139_map[location] ? - readw (tp->mmio_addr + mii_2_8139_map[location]) : 0; + RTL_R16 (mii_2_8139_map[location]) : 0; } #ifdef CONFIG_8139TOO_8129 - mdio_sync (mdio_addr); + mdio_sync (ioaddr); /* Shift the read command bits out. */ for (i = 15; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_DATA_OUT : 0; - writeb (MDIO_DIR | dataval, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_DIR | dataval | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); + RTL_W8 (Config4, MDIO_DIR | dataval); + mdio_delay (); + RTL_W8 (Config4, MDIO_DIR | dataval | MDIO_CLK); + mdio_delay (); } /* Read the two transition, 16 data, and wire-idle bits. */ for (i = 19; i > 0; i--) { - writeb (0, mdio_addr); - mdio_delay (mdio_addr); - retval = (retval << 1) | ((readb (mdio_addr) & MDIO_DATA_IN) ? 1 : 0); - writeb (MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); + RTL_W8 (Config4, 0); + mdio_delay (); + retval = (retval << 1) | ((RTL_R8 (Config4) & MDIO_DATA_IN) ? 1 : 0); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); } #endif @@ -1278,13 +1258,13 @@ { struct rtl8139_private *tp = netdev_priv(dev); #ifdef CONFIG_8139TOO_8129 - void *mdio_addr = tp->mmio_addr + Config4; + void __iomem *ioaddr = tp->mmio_addr; int mii_cmd = (0x5002 << 16) | (phy_id << 23) | (location << 18) | value; int i; #endif if (phy_id > 31) { /* Really a 8139. Use internal registers. */ - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; if (location == 0) { RTL_W8 (Cfg9346, Cfg9346_Unlock); RTL_W16 (BasicModeCtrl, value); @@ -1295,23 +1275,23 @@ } #ifdef CONFIG_8139TOO_8129 - mdio_sync (mdio_addr); + mdio_sync (ioaddr); /* Shift the command bits out. */ for (i = 31; i >= 0; i--) { int dataval = (mii_cmd & (1 << i)) ? MDIO_WRITE1 : MDIO_WRITE0; - writeb (dataval, mdio_addr); - mdio_delay (mdio_addr); - writeb (dataval | MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); + RTL_W8 (Config4, dataval); + mdio_delay (); + RTL_W8 (Config4, dataval | MDIO_CLK); + mdio_delay (); } /* Clear out extra bits. */ for (i = 2; i > 0; i--) { - writeb (0, mdio_addr); - mdio_delay (mdio_addr); - writeb (MDIO_CLK, mdio_addr); - mdio_delay (mdio_addr); + RTL_W8 (Config4, 0); + mdio_delay (); + RTL_W8 (Config4, MDIO_CLK); + mdio_delay (); } #endif } @@ -1321,7 +1301,7 @@ { struct rtl8139_private *tp = netdev_priv(dev); int retval; - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; retval = request_irq (dev->irq, rtl8139_interrupt, SA_SHIRQ, dev->name, dev); if (retval) @@ -1378,7 +1358,7 @@ static void rtl8139_hw_start (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; u32 i; u8 tmp; @@ -1480,7 +1460,7 @@ struct rtl8139_private *tp) { int linkcase; - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; /* This is a complicated state machine to configure the "twister" for impedance/echos based on the cable length. @@ -1564,7 +1544,7 @@ static inline void rtl8139_thread_iter (struct net_device *dev, struct rtl8139_private *tp, - void *ioaddr) + void __iomem *ioaddr) { int mii_lpa; @@ -1672,7 +1652,7 @@ static void rtl8139_tx_timeout (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; int i; u8 tmp8; unsigned long flags; @@ -1717,7 +1697,7 @@ static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; unsigned int entry; unsigned int len = skb->len; @@ -1759,7 +1739,7 @@ static void rtl8139_tx_interrupt (struct net_device *dev, struct rtl8139_private *tp, - void *ioaddr) + void __iomem *ioaddr) { unsigned long dirty_tx, tx_left; @@ -1829,7 +1809,7 @@ /* TODO: clean this up! Rx reset need not be this intensive */ static void rtl8139_rx_err (u32 rx_status, struct net_device *dev, - struct rtl8139_private *tp, void *ioaddr) + struct rtl8139_private *tp, void __iomem *ioaddr) { u8 tmp8; #ifdef CONFIG_8139_OLD_RX_RESET @@ -1926,7 +1906,7 @@ static void rtl8139_isr_ack(struct rtl8139_private *tp) { - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; u16 status; status = RTL_R16 (IntrStatus) & RxAckBits; @@ -1945,7 +1925,7 @@ static int rtl8139_rx(struct net_device *dev, struct rtl8139_private *tp, int budget) { - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; int received = 0; unsigned char *rx_ring = tp->rx_ring; unsigned int cur_rx = tp->cur_rx; @@ -2083,7 +2063,7 @@ static void rtl8139_weird_interrupt (struct net_device *dev, struct rtl8139_private *tp, - void *ioaddr, + void __iomem *ioaddr, int status, int link_changed) { DPRINTK ("%s: Abnormal interrupt, status %8.8x.\n", @@ -2123,7 +2103,7 @@ static int rtl8139_poll(struct net_device *dev, int *budget) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; int orig_budget = min(*budget, dev->quota); int done = 1; @@ -2161,7 +2141,7 @@ { struct net_device *dev = (struct net_device *) dev_instance; struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; u16 status, ackstat; int link_changed = 0; /* avoid bogus "uninit" warning */ int handled = 0; @@ -2237,7 +2217,7 @@ static int rtl8139_close (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; int ret = 0; unsigned long flags; @@ -2300,7 +2280,7 @@ static void rtl8139_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8139_private *np = netdev_priv(dev); - void *ioaddr = np->mmio_addr; + void __iomem *ioaddr = np->mmio_addr; spin_lock_irq(&np->lock); if (rtl_chip_info[np->chipset].flags & HasLWake) { @@ -2334,7 +2314,7 @@ static int rtl8139_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) { struct rtl8139_private *np = netdev_priv(dev); - void *ioaddr = np->mmio_addr; + void __iomem *ioaddr = np->mmio_addr; u32 support; u8 cfg3, cfg5; @@ -2502,7 +2482,7 @@ static struct net_device_stats *rtl8139_get_stats (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; unsigned long flags; if (netif_running(dev)) { @@ -2521,7 +2501,7 @@ static void __set_rx_mode (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; u32 mc_filter[2]; /* Multicast hash filter */ int i, rx_mode; u32 tmp; @@ -2582,7 +2562,7 @@ { struct net_device *dev = pci_get_drvdata (pdev); struct rtl8139_private *tp = netdev_priv(dev); - void *ioaddr = tp->mmio_addr; + void __iomem *ioaddr = tp->mmio_addr; unsigned long flags; pci_save_state (pdev); diff -Nru a/drivers/net/Kconfig b/drivers/net/Kconfig --- a/drivers/net/Kconfig 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/Kconfig 2005-03-15 23:46:53 -08:00 @@ -1953,6 +1953,18 @@ If in doubt, say Y. +config SKGE + tristate "New SysKonnect GigaEthernet support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL + select CRC32 + ---help--- + This driver support the Marvell Yukon or SysKonnect SK-98xx/SK-95xx + and related Gigabit Ethernet adapters. It is a new smaller driver + driver with better performance and more complete ethtool support. + + It does not support the link failover and network management + features that "portable" vendor supplied sk98lin driver does. + config SK98LIN tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support" depends on PCI diff -Nru a/drivers/net/Makefile b/drivers/net/Makefile --- a/drivers/net/Makefile 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/Makefile 2005-03-15 23:46:53 -08:00 @@ -52,6 +52,7 @@ obj-$(CONFIG_FEALNX) += fealnx.o obj-$(CONFIG_TIGON3) += tg3.o obj-$(CONFIG_TC35815) += tc35815.o +obj-$(CONFIG_SKGE) += skge.o obj-$(CONFIG_SK98LIN) += sk98lin/ obj-$(CONFIG_SKFP) += skfp/ obj-$(CONFIG_VIA_RHINE) += via-rhine.o diff -Nru a/drivers/net/natsemi.c b/drivers/net/natsemi.c --- a/drivers/net/natsemi.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/natsemi.c 2005-03-15 23:46:53 -08:00 @@ -186,6 +186,7 @@ /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ static int max_interrupt_work = 20; static int mtu; +static int no_cable_magic = 0; /* Maximum number of multicast addresses to filter (vs. rx-all-multicast). This chip uses a 512 element hash table based on the Ethernet CRC. */ @@ -257,6 +258,7 @@ module_param(rx_copybreak, int, 0); module_param_array(options, int, NULL, 0); module_param_array(full_duplex, int, NULL, 0); +module_param(no_cable_magic, int, 0); MODULE_PARM_DESC(max_interrupt_work, "DP8381x maximum events handled per interrupt"); MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)"); @@ -266,6 +268,9 @@ MODULE_PARM_DESC(options, "DP8381x: Bits 0-3: media type, bit 17: full duplex"); MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)"); +MODULE_PARM_DESC(no_cable_magic, + "DP8381x: set no_cable_magic=1 to disable magic workaround for short cables " + "(may help with long cables:-)"); /* Theory of Operation @@ -1580,6 +1585,9 @@ struct netdev_private *np = netdev_priv(dev); void __iomem *ioaddr = ns_ioaddr(dev); + if (no_cable_magic) + return; + if (dev->if_port != PORT_TP) return; @@ -1624,6 +1632,9 @@ u16 data; struct netdev_private *np = netdev_priv(dev); void __iomem * ioaddr = ns_ioaddr(dev); + + if (no_cable_magic) + return; if (dev->if_port != PORT_TP) return; diff -Nru a/drivers/net/skge.c b/drivers/net/skge.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/skge.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,3385 @@ +/* + * New driver for Marvell Yukon chipset and SysKonnect Gigabit + * Ethernet adapters. Based on earlier sk98lin, e100 and + * FreeBSD if_sk drivers. + * + * This driver intentionally does not support all the features + * of the original driver such as link fail-over and link management because + * those should be done at higher levels. + * + * Copyright (C) 2004, Stephen Hemminger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "skge.h" + +#define DRV_NAME "skge" +#define DRV_VERSION "0.6" +#define PFX DRV_NAME " " + +#define DEFAULT_TX_RING_SIZE 128 +#define DEFAULT_RX_RING_SIZE 512 +#define MAX_TX_RING_SIZE 1024 +#define MAX_RX_RING_SIZE 4096 +#define PHY_RETRIES 1000 +#define ETH_JUMBO_MTU 9000 +#define TX_WATCHDOG (5 * HZ) +#define NAPI_WEIGHT 64 +#define BLINK_HZ (HZ/4) +#define LINK_POLL_HZ (HZ/10) + +MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver"); +MODULE_AUTHOR("Stephen Hemminger "); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +static const u32 default_msg + = NETIF_MSG_DRV| NETIF_MSG_PROBE| NETIF_MSG_LINK + | NETIF_MSG_IFUP| NETIF_MSG_IFDOWN; + +static int debug = -1; /* defaults above */ +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); + +static const struct pci_device_id skge_id_table[] = { + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_SYSKONNECT, 0x9E00, /* SK-9Exx */ + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_MARVELL, 0x4320, /* Gigabit Ethernet Controller */ + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_MARVELL, 0x5005, /* Marvell (11ab), Belkin */ + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1032, + PCI_ANY_ID, PCI_ANY_ID }, + { PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064, + PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, skge_id_table); + +static int skge_up(struct net_device *dev); +static int skge_down(struct net_device *dev); +static void skge_tx_clean(struct skge_port *skge); +static void skge_xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val); +static void skge_gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val); +static void genesis_get_stats(struct skge_port *skge, u64 *data); +static void yukon_get_stats(struct skge_port *skge, u64 *data); +static void yukon_init(struct skge_hw *hw, int port); +static void yukon_reset(struct skge_hw *hw, int port); +static void genesis_mac_init(struct skge_hw *hw, int port); +static void genesis_reset(struct skge_hw *hw, int port); + +static const int txqaddr[] = { Q_XA1, Q_XA2 }; +static const int rxqaddr[] = { Q_R1, Q_R2 }; +static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F }; +static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F }; + +/* Don't need to look at whole 16K. + * last interesting register is descriptor poll timer. + */ +#define SKGE_REGS_LEN (29*128) + +static int skge_get_regs_len(struct net_device *dev) +{ + return SKGE_REGS_LEN; +} + +/* + * Returns copy of control register region + * I/O region is divided into banks and certain regions are unreadable + */ +static void skge_get_regs(struct net_device *dev, struct ethtool_regs *regs, + void *p) +{ + const struct skge_port *skge = netdev_priv(dev); + unsigned long offs; + const void __iomem *io = skge->hw->regs; + static const unsigned long bankmap + = (1<<0) | (1<<2) | (1<<8) | (1<<9) + | (1<<12) | (1<<13) | (1<<14) | (1<<15) | (1<<16) + | (1<<17) | (1<<20) | (1<<21) | (1<<22) | (1<<23) + | (1<<24) | (1<<25) | (1<<26) | (1<<27) | (1<<28); + + regs->version = 1; + for (offs = 0; offs < regs->len; offs += 128) { + u32 len = min_t(u32, 128, regs->len - offs); + + if (bankmap & (1<<(offs/128))) + memcpy_fromio(p + offs, io + offs, len); + else + memset(p + offs, 0, len); + } +} + +/* Wake on Lan only supported on Yukon chps with rev 1 or above */ +static int wol_supported(const struct skge_hw *hw) +{ + return !((hw->chip_id == CHIP_ID_GENESIS || + (hw->chip_id == CHIP_ID_YUKON && chip_rev(hw) == 0))); +} + +static void skge_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct skge_port *skge = netdev_priv(dev); + + wol->supported = wol_supported(skge->hw) ? WAKE_MAGIC : 0; + wol->wolopts = skge->wol ? WAKE_MAGIC : 0; +} + +static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + + if(wol->wolopts != WAKE_MAGIC && wol->wolopts != 0) + return -EOPNOTSUPP; + + if (wol->wolopts == WAKE_MAGIC && !wol_supported(hw)) + return -EOPNOTSUPP; + + skge->wol = wol->wolopts == WAKE_MAGIC; + + if (skge->wol) { + memcpy_toio(hw->regs + WOL_MAC_ADDR, dev->dev_addr, ETH_ALEN); + + skge_write16(hw, WOL_CTRL_STAT, + WOL_CTL_ENA_PME_ON_MAGIC_PKT | + WOL_CTL_ENA_MAGIC_PKT_UNIT); + } else + skge_write16(hw, WOL_CTRL_STAT, WOL_CTL_DEFAULT); + + return 0; +} + + +static int skge_get_settings(struct net_device *dev, + struct ethtool_cmd *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + + ecmd->transceiver = XCVR_INTERNAL; + + if (iscopper(hw)) { + if (hw->chip_id == CHIP_ID_GENESIS) + ecmd->supported = SUPPORTED_1000baseT_Full + | SUPPORTED_1000baseT_Half + | SUPPORTED_Autoneg | SUPPORTED_TP; + else { + ecmd->supported = SUPPORTED_10baseT_Half + | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half + | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full + | SUPPORTED_Autoneg| SUPPORTED_TP; + + if (hw->chip_id == CHIP_ID_YUKON) + ecmd->supported &= ~SUPPORTED_1000baseT_Half; + + else if (hw->chip_id == CHIP_ID_YUKON_FE) + ecmd->supported &= ~(SUPPORTED_1000baseT_Half + | SUPPORTED_1000baseT_Full); + } + + ecmd->port = PORT_TP; + ecmd->phy_address = hw->phy_addr; + } else { + ecmd->supported = SUPPORTED_1000baseT_Full + | SUPPORTED_FIBRE + | SUPPORTED_Autoneg; + + ecmd->port = PORT_FIBRE; + } + + ecmd->advertising = skge->advertising; + ecmd->autoneg = skge->autoneg; + ecmd->speed = skge->speed; + ecmd->duplex = skge->duplex; + return 0; +} + +static u32 skge_modes(const struct skge_hw *hw) +{ + u32 modes = ADVERTISED_Autoneg + | ADVERTISED_1000baseT_Full | ADVERTISED_1000baseT_Half + | ADVERTISED_100baseT_Full | ADVERTISED_100baseT_Half + | ADVERTISED_10baseT_Full | ADVERTISED_10baseT_Half; + + if (iscopper(hw)) { + modes |= ADVERTISED_TP; + switch(hw->chip_id) { + case CHIP_ID_GENESIS: + modes &= ~(ADVERTISED_100baseT_Full + | ADVERTISED_100baseT_Half + | ADVERTISED_10baseT_Full + | ADVERTISED_10baseT_Half); + break; + + case CHIP_ID_YUKON: + modes &= ~ADVERTISED_1000baseT_Half; + break; + + case CHIP_ID_YUKON_FE: + modes &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full); + break; + } + } else { + modes |= ADVERTISED_FIBRE; + modes &= ~ADVERTISED_1000baseT_Half; + } + return modes; +} + +static int skge_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + const struct skge_hw *hw = skge->hw; + + if (ecmd->autoneg == AUTONEG_ENABLE) { + if (ecmd->advertising & skge_modes(hw)) + return -EINVAL; + } else { + switch(ecmd->speed) { + case SPEED_1000: + if (hw->chip_id == CHIP_ID_YUKON_FE) + return -EINVAL; + break; + case SPEED_100: + case SPEED_10: + if (iscopper(hw) || hw->chip_id == CHIP_ID_GENESIS) + return -EINVAL; + break; + default: + return -EINVAL; + } + } + + skge->autoneg = ecmd->autoneg; + skge->speed = ecmd->speed; + skge->duplex = ecmd->duplex; + skge->advertising = ecmd->advertising; + + if (netif_running(dev)) { + skge_down(dev); + skge_up(dev); + } + return (0); +} + +static void skge_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + struct skge_port *skge = netdev_priv(dev); + + strcpy(info->driver, DRV_NAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->fw_version, "N/A"); + strcpy(info->bus_info, pci_name(skge->hw->pdev)); +} + +static const struct skge_stat { + char name[ETH_GSTRING_LEN]; + u16 xmac_offset; + u16 gma_offset; +} skge_stats[] = { + { "tx_bytes", XM_TXO_OK_HI, GM_TXO_OK_HI }, + { "rx_bytes", XM_RXO_OK_HI, GM_RXO_OK_HI }, + + { "tx_broadcast", XM_TXF_BC_OK, GM_TXF_BC_OK }, + { "rx_broadcast", XM_RXF_BC_OK, GM_RXF_BC_OK }, + { "tx_multicast", XM_TXF_MC_OK, GM_TXF_MC_OK }, + { "rx_multicast", XM_RXF_MC_OK, GM_RXF_MC_OK }, + { "tx_unicast", XM_TXF_UC_OK, GM_TXF_UC_OK }, + { "rx_unicast", XM_RXF_UC_OK, GM_RXF_UC_OK }, + { "tx_mac_pause", XM_TXF_MPAUSE, GM_TXF_MPAUSE }, + { "rx_mac_pause", XM_RXF_MPAUSE, GM_RXF_MPAUSE }, + + { "collisions", XM_TXF_SNG_COL, GM_TXF_SNG_COL }, + { "multi_collisions", XM_TXF_MUL_COL, GM_TXF_MUL_COL }, + { "aborted", XM_TXF_ABO_COL, GM_TXF_ABO_COL }, + { "late_collision", XM_TXF_LAT_COL, GM_TXF_LAT_COL }, + { "fifo_underrun", XM_TXE_FIFO_UR, GM_TXE_FIFO_UR }, + { "fifo_overflow", XM_RXE_FIFO_OV, GM_RXE_FIFO_OV }, + + { "rx_toolong", XM_RXF_LNG_ERR, GM_RXF_LNG_ERR }, + { "rx_jabber", XM_RXF_JAB_PKT, GM_RXF_JAB_PKT }, + { "rx_runt", XM_RXE_RUNT, GM_RXE_FRAG }, + { "rx_too_long", XM_RXF_LNG_ERR, GM_RXF_LNG_ERR }, + { "rx_fcs_error", XM_RXF_FCS_ERR, GM_RXF_FCS_ERR }, +}; + +static int skge_get_stats_count(struct net_device *dev) +{ + return ARRAY_SIZE(skge_stats); +} + +static void skge_get_ethtool_stats(struct net_device *dev, + struct ethtool_stats *stats, u64 *data) +{ + struct skge_port *skge = netdev_priv(dev); + + if (skge->hw->chip_id == CHIP_ID_GENESIS) + genesis_get_stats(skge, data); + else + yukon_get_stats(skge, data); +} + +/* Use hardware MIB variables for critical path statistics and + * transmit feedback not reported at interrupt. + * Other errors are accounted for in interrupt handler. + */ +static struct net_device_stats *skge_get_stats(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + u64 data[ARRAY_SIZE(skge_stats)]; + + if (skge->hw->chip_id == CHIP_ID_GENESIS) + genesis_get_stats(skge, data); + else + yukon_get_stats(skge, data); + + skge->net_stats.tx_bytes = data[0]; + skge->net_stats.rx_bytes = data[1]; + skge->net_stats.tx_packets = data[2] + data[4] + data[6]; + skge->net_stats.rx_packets = data[3] + data[5] + data[7]; + skge->net_stats.multicast = data[5] + data[7]; + skge->net_stats.collisions = data[10]; + skge->net_stats.tx_aborted_errors = data[12]; + + return &skge->net_stats; +} + +static void skge_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + int i; + + switch(stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(skge_stats); i++) + memcpy(data + i * ETH_GSTRING_LEN, + skge_stats[i].name, ETH_GSTRING_LEN); + break; + } +} + +static void skge_get_ring_param(struct net_device *dev, + struct ethtool_ringparam *p) +{ + struct skge_port *skge = netdev_priv(dev); + + p->rx_max_pending = MAX_RX_RING_SIZE; + p->tx_max_pending = MAX_TX_RING_SIZE; + p->rx_mini_max_pending = 0; + p->rx_jumbo_max_pending = 0; + + p->rx_pending = skge->rx_ring.count; + p->tx_pending = skge->tx_ring.count; + p->rx_mini_pending = 0; + p->rx_jumbo_pending = 0; +} + +static int skge_set_ring_param(struct net_device *dev, + struct ethtool_ringparam *p) +{ + struct skge_port *skge = netdev_priv(dev); + + if (p->rx_pending == 0 || p->rx_pending > MAX_RX_RING_SIZE || + p->tx_pending == 0 || p->tx_pending > MAX_TX_RING_SIZE) + return -EINVAL; + + skge->rx_ring.count = p->rx_pending; + skge->tx_ring.count = p->tx_pending; + + if (netif_running(dev)) { + skge_down(dev); + skge_up(dev); + } + + return 0; +} + +static u32 skge_get_msglevel(struct net_device *netdev) +{ + struct skge_port *skge = netdev_priv(netdev); + return skge->msg_enable; +} + +static void skge_set_msglevel(struct net_device *netdev, u32 value) +{ + struct skge_port *skge = netdev_priv(netdev); + skge->msg_enable = value; +} + +static int skge_nway_reset(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + + if (skge->autoneg != AUTONEG_ENABLE || !netif_running(dev)) + return -EINVAL; + + spin_lock_bh(&hw->phy_lock); + if (hw->chip_id == CHIP_ID_GENESIS) { + genesis_reset(hw, port); + genesis_mac_init(hw, port); + } else { + yukon_reset(hw, port); + yukon_init(hw, port); + } + spin_unlock_bh(&hw->phy_lock); + return 0; +} + +static int skge_set_sg(struct net_device *dev, u32 data) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + + if (hw->chip_id == CHIP_ID_GENESIS && data) + return -EOPNOTSUPP; + return ethtool_op_set_sg(dev, data); +} + +static int skge_set_tx_csum(struct net_device *dev, u32 data) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + + if (hw->chip_id == CHIP_ID_GENESIS && data) + return -EOPNOTSUPP; + + return ethtool_op_set_tx_csum(dev, data); +} + +static u32 skge_get_rx_csum(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + + return skge->rx_csum; +} + +/* Only Yukon supports checksum offload. */ +static int skge_set_rx_csum(struct net_device *dev, u32 data) +{ + struct skge_port *skge = netdev_priv(dev); + + if (skge->hw->chip_id == CHIP_ID_GENESIS && data) + return -EOPNOTSUPP; + + skge->rx_csum = data; + return 0; +} + +/* Only Yukon II supports TSO (not implemented yet) */ +static int skge_set_tso(struct net_device *dev, u32 data) +{ + if (data) + return -EOPNOTSUPP; + return 0; +} + +static void skge_get_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + + ecmd->tx_pause = (skge->flow_control == FLOW_MODE_LOC_SEND) + || (skge->flow_control == FLOW_MODE_SYMMETRIC); + ecmd->rx_pause = (skge->flow_control == FLOW_MODE_REM_SEND) + || (skge->flow_control == FLOW_MODE_SYMMETRIC); + + ecmd->autoneg = skge->autoneg; +} + +static int skge_set_pauseparam(struct net_device *dev, + struct ethtool_pauseparam *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + + skge->autoneg = ecmd->autoneg; + if (ecmd->rx_pause && ecmd->tx_pause) + skge->flow_control = FLOW_MODE_SYMMETRIC; + else if(ecmd->rx_pause && !ecmd->tx_pause) + skge->flow_control = FLOW_MODE_REM_SEND; + else if(!ecmd->rx_pause && ecmd->tx_pause) + skge->flow_control = FLOW_MODE_LOC_SEND; + else + skge->flow_control = FLOW_MODE_NONE; + + if (netif_running(dev)) { + skge_down(dev); + skge_up(dev); + } + return 0; +} + +/* Chip internal frequency for clock calculations */ +static inline u32 hwkhz(const struct skge_hw *hw) +{ + if (hw->chip_id == CHIP_ID_GENESIS) + return 53215; /* or: 53.125 MHz */ + else if (hw->chip_id == CHIP_ID_YUKON_EC) + return 125000; /* or: 125.000 MHz */ + else + return 78215; /* or: 78.125 MHz */ +} + +/* Chip hz to microseconds */ +static inline u32 skge_clk2usec(const struct skge_hw *hw, u32 ticks) +{ + return (ticks * 1000) / hwkhz(hw); +} + +/* Microseconds to chip hz */ +static inline u32 skge_usecs2clk(const struct skge_hw *hw, u32 usec) +{ + return hwkhz(hw) * usec / 1000; +} + +static int skge_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + + ecmd->rx_coalesce_usecs = 0; + ecmd->tx_coalesce_usecs = 0; + + if (skge_read32(hw, B2_IRQM_CTRL) & TIM_START) { + u32 delay = skge_clk2usec(hw, skge_read32(hw, B2_IRQM_INI)); + u32 msk = skge_read32(hw, B2_IRQM_MSK); + + if (msk & rxirqmask[port]) + ecmd->rx_coalesce_usecs = delay; + if (msk & txirqmask[port]) + ecmd->tx_coalesce_usecs = delay; + } + + return 0; +} + +/* Note: interrupt timer is per board, but can turn on/off per port */ +static int skge_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ecmd) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + u32 msk = skge_read32(hw, B2_IRQM_MSK); + u32 delay = 25; + + if (ecmd->rx_coalesce_usecs == 0) + msk &= ~rxirqmask[port]; + else if (ecmd->rx_coalesce_usecs < 25 || + ecmd->rx_coalesce_usecs > 33333) + return -EINVAL; + else { + msk |= rxirqmask[port]; + delay = ecmd->rx_coalesce_usecs; + } + + if (ecmd->tx_coalesce_usecs == 0) + msk &= ~txirqmask[port]; + else if (ecmd->tx_coalesce_usecs < 25 || + ecmd->tx_coalesce_usecs > 33333) + return -EINVAL; + else { + msk |= txirqmask[port]; + delay = min(delay, ecmd->rx_coalesce_usecs); + } + + skge_write32(hw, B2_IRQM_MSK, msk); + if (msk == 0) + skge_write32(hw, B2_IRQM_CTRL, TIM_STOP); + else { + skge_write32(hw, B2_IRQM_INI, skge_usecs2clk(hw, delay)); + skge_write32(hw, B2_IRQM_CTRL, TIM_START); + } + return 0; +} + +static void skge_led_on(struct skge_hw *hw, int port) +{ + if (hw->chip_id == CHIP_ID_GENESIS) { + skge_write8(hw, SKGEMAC_REG(port, LNK_LED_REG), LINKLED_ON); + skge_write8(hw, B0_LED, LED_STAT_ON); + + skge_write8(hw, SKGEMAC_REG(port, RX_LED_TST), LED_T_ON); + skge_write32(hw, SKGEMAC_REG(port, RX_LED_VAL), 100); + skge_write8(hw, SKGEMAC_REG(port, RX_LED_CTRL), LED_START); + + switch (hw->phy_type) { + case SK_PHY_BCOM: + skge_xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, + PHY_B_PEC_LED_ON); + break; + case SK_PHY_LONE: + skge_xm_phy_write(hw, port, PHY_LONE_LED_CFG, + 0x0800); + break; + default: + skge_write8(hw, SKGEMAC_REG(port, TX_LED_TST), LED_T_ON); + skge_write32(hw, SKGEMAC_REG(port, TX_LED_VAL), 100); + skge_write8(hw, SKGEMAC_REG(port, TX_LED_CTRL), LED_START); + } + } else { + skge_gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0); + skge_gm_phy_write(hw, port, PHY_MARV_LED_OVER, + PHY_M_LED_MO_DUP(MO_LED_ON) | + PHY_M_LED_MO_10(MO_LED_ON) | + PHY_M_LED_MO_100(MO_LED_ON) | + PHY_M_LED_MO_1000(MO_LED_ON) | + PHY_M_LED_MO_RX(MO_LED_ON)); + } +} + +static void skge_led_off(struct skge_hw *hw, int port) +{ + if (hw->chip_id == CHIP_ID_GENESIS) { + skge_write8(hw, SKGEMAC_REG(port, LNK_LED_REG), LINKLED_OFF); + skge_write8(hw, B0_LED, LED_STAT_OFF); + + skge_write32(hw, SKGEMAC_REG(port, RX_LED_VAL), 0); + skge_write8(hw, SKGEMAC_REG(port, RX_LED_CTRL), LED_T_OFF); + + switch (hw->phy_type) { + case SK_PHY_BCOM: + skge_xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, + PHY_B_PEC_LED_OFF); + break; + case SK_PHY_LONE: + skge_xm_phy_write(hw, port, PHY_LONE_LED_CFG, + PHY_L_LC_LEDT); + break; + default: + skge_write32(hw, SKGEMAC_REG(port, TX_LED_VAL), 0); + skge_write8(hw, SKGEMAC_REG(port, TX_LED_CTRL), LED_T_OFF); + } + } else { + skge_gm_phy_write(hw, port, PHY_MARV_LED_CTRL, 0); + skge_gm_phy_write(hw, port, PHY_MARV_LED_OVER, + PHY_M_LED_MO_DUP(MO_LED_OFF) | + PHY_M_LED_MO_10(MO_LED_OFF) | + PHY_M_LED_MO_100(MO_LED_OFF) | + PHY_M_LED_MO_1000(MO_LED_OFF) | + PHY_M_LED_MO_RX(MO_LED_OFF)); + } +} + +static void skge_blink_timer(unsigned long data) +{ + struct skge_port *skge = (struct skge_port *) data; + struct skge_hw *hw = skge->hw; + unsigned long flags; + + spin_lock_irqsave(&hw->phy_lock, flags); + if (skge->blink_on) + skge_led_on(hw, skge->port); + else + skge_led_off(hw, skge->port); + spin_unlock_irqrestore(&hw->phy_lock, flags); + + skge->blink_on = !skge->blink_on; + mod_timer(&skge->led_blink, jiffies + BLINK_HZ); +} + +/* blink LED's for finding board */ +static int skge_phys_id(struct net_device *dev, u32 data) +{ + struct skge_port *skge = netdev_priv(dev); + + if(!data || data > (u32)(MAX_SCHEDULE_TIMEOUT / HZ)) + data = (u32)(MAX_SCHEDULE_TIMEOUT / HZ); + + /* start blinking */ + skge->blink_on = 1; + mod_timer(&skge->led_blink, jiffies+1); + + msleep_interruptible(data * 1000); + del_timer_sync(&skge->led_blink); + + skge_led_off(skge->hw, skge->port); + + return 0; +} + +static struct ethtool_ops skge_ethtool_ops = { + .get_settings = skge_get_settings, + .set_settings = skge_set_settings, + .get_drvinfo = skge_get_drvinfo, + .get_regs_len = skge_get_regs_len, + .get_regs = skge_get_regs, + .get_wol = skge_get_wol, + .set_wol = skge_set_wol, + .get_msglevel = skge_get_msglevel, + .set_msglevel = skge_set_msglevel, + .nway_reset = skge_nway_reset, + .get_link = ethtool_op_get_link, + .get_ringparam = skge_get_ring_param, + .set_ringparam = skge_set_ring_param, + .get_pauseparam = skge_get_pauseparam, + .set_pauseparam = skge_set_pauseparam, + .get_coalesce = skge_get_coalesce, + .set_coalesce = skge_set_coalesce, + .get_tso = ethtool_op_get_tso, + .set_tso = skge_set_tso, + .get_sg = ethtool_op_get_sg, + .set_sg = skge_set_sg, + .get_tx_csum = ethtool_op_get_tx_csum, + .set_tx_csum = skge_set_tx_csum, + .get_rx_csum = skge_get_rx_csum, + .set_rx_csum = skge_set_rx_csum, + .get_strings = skge_get_strings, + .phys_id = skge_phys_id, + .get_stats_count = skge_get_stats_count, + .get_ethtool_stats = skge_get_ethtool_stats, +}; + +/* + * Allocate ring elements and chain them together + * One-to-one association of board descriptors with ring elements + */ +static int skge_ring_alloc(struct skge_ring *ring, void *vaddr, u64 base) +{ + struct skge_tx_desc *d; + struct skge_element *e; + int i; + + ring->start = kmalloc(sizeof(*e)*ring->count, GFP_KERNEL); + if (!ring->start) + return -ENOMEM; + + for (i = 0, e = ring->start, d = vaddr; i < ring->count; i++, e++, d++) { + e->desc = d; + if (i == ring->count - 1) { + e->next = ring->start; + d->next_offset = base; + } else { + e->next = e + 1; + d->next_offset = base + (i+1) * sizeof(*d); + } + } + ring->to_use = ring->to_clean = ring->start; + + return 0; +} + +/* Setup buffer for receiving */ +static inline int skge_rx_alloc(struct skge_port *skge, + struct skge_element *e) +{ + unsigned long bufsize = skge->netdev->mtu + ETH_HLEN; /* VLAN? */ + struct skge_rx_desc *rd = e->desc; + struct sk_buff *skb; + u64 map; + + skb = dev_alloc_skb(bufsize + NET_IP_ALIGN); + if (unlikely(!skb)) { + printk(KERN_DEBUG PFX "%s: out of memory for receive\n", + skge->netdev->name); + return -ENOMEM; + } + + skb->dev = skge->netdev; + skb_reserve(skb, NET_IP_ALIGN); + + map = pci_map_single(skge->hw->pdev, skb->data, bufsize, + PCI_DMA_FROMDEVICE); + + rd->dma_lo = map; + rd->dma_hi = map >> 32; + e->skb = skb; + rd->csum1_start = ETH_HLEN; + rd->csum2_start = ETH_HLEN; + rd->csum1 = 0; + rd->csum2 = 0; + + wmb(); + + rd->control = BMU_OWN | BMU_STF | BMU_IRQ_EOF | BMU_TCP_CHECK | bufsize; + pci_unmap_addr_set(e, mapaddr, map); + pci_unmap_len_set(e, maplen, bufsize); + return 0; +} + +/* Free all unused buffers in receive ring, assumes receiver stopped */ +static void skge_rx_clean(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + struct skge_ring *ring = &skge->rx_ring; + struct skge_element *e; + + for (e = ring->to_clean; e != ring->to_use; e = e->next) { + struct skge_rx_desc *rd = e->desc; + rd->control = 0; + + pci_unmap_single(hw->pdev, + pci_unmap_addr(e, mapaddr), + pci_unmap_len(e, maplen), + PCI_DMA_FROMDEVICE); + dev_kfree_skb(e->skb); + e->skb = NULL; + } + ring->to_clean = e; +} + +/* Allocate buffers for receive ring + * For receive: to_use is refill location + * to_clean is next received frame. + * + * if (to_use == to_clean) + * then ring all frames in ring need buffers + * if (to_use->next == to_clean) + * then ring all frames in ring have buffers + */ +static int skge_rx_fill(struct skge_port *skge) +{ + struct skge_ring *ring = &skge->rx_ring; + struct skge_element *e; + int ret = 0; + + for (e = ring->to_use; e->next != ring->to_clean; e = e->next) { + if (skge_rx_alloc(skge, e)) { + ret = 1; + break; + } + + } + ring->to_use = e; + + return ret; +} + +static void skge_link_up(struct skge_port *skge) +{ + netif_carrier_on(skge->netdev); + if (skge->tx_avail > MAX_SKB_FRAGS + 1) + netif_wake_queue(skge->netdev); + + if (netif_msg_link(skge)) + printk(KERN_INFO PFX + "%s: Link is up at %d Mbps, %s duplex, flow control %s\n", + skge->netdev->name, skge->speed, + skge->duplex == DUPLEX_FULL ? "full" : "half", + (skge->flow_control == FLOW_MODE_NONE) ? "none" : + (skge->flow_control == FLOW_MODE_LOC_SEND) ? "tx only" : + (skge->flow_control == FLOW_MODE_REM_SEND) ? "rx only" : + (skge->flow_control == FLOW_MODE_SYMMETRIC) ? "tx and rx" : + "unknown"); +} + +static void skge_link_down(struct skge_port *skge) +{ + netif_carrier_off(skge->netdev); + netif_stop_queue(skge->netdev); + + if (netif_msg_link(skge)) + printk(KERN_INFO PFX "%s: Link is down.\n", skge->netdev->name); +} + +static u16 skge_xm_phy_read(struct skge_hw *hw, int port, u16 reg) +{ + int i; + u16 v; + + skge_xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr); + v = skge_xm_read16(hw, port, XM_PHY_DATA); + if (hw->phy_type != SK_PHY_XMAC) { + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + if (skge_xm_read16(hw, port, XM_MMU_CMD) + & XM_MMU_PHY_RDY) + goto ready; + } + + printk(KERN_WARNING PFX "%s: phy read timed out\n", + hw->dev[port]->name); + return 0; + ready: + v = skge_xm_read16(hw, port, XM_PHY_DATA); + } + + return v; +} + +static void skge_xm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val) +{ + int i; + + skge_xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr); + for (i = 0; i < PHY_RETRIES; i++) { + if (!(skge_xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY)) + goto ready; + cpu_relax(); + } + printk(KERN_WARNING PFX "%s: phy write failed to come ready\n", + hw->dev[port]->name); + + + ready: + skge_xm_write16(hw, port, XM_PHY_DATA, val); + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + if (!(skge_xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_BUSY)) + return; + } + printk(KERN_WARNING PFX "%s: phy write timed out\n", + hw->dev[port]->name); +} + +static void genesis_init(struct skge_hw *hw) +{ + /* set blink source counter */ + skge_write32(hw, B2_BSC_INI, (SK_BLK_DUR * SK_FACT_53) / 100); + skge_write8(hw, B2_BSC_CTRL, BSC_START); + + /* configure mac arbiter */ + skge_write16(hw, B3_MA_TO_CTRL, MA_RST_CLR); + + /* configure mac arbiter timeout values */ + skge_write8(hw, B3_MA_TOINI_RX1, SK_MAC_TO_53); + skge_write8(hw, B3_MA_TOINI_RX2, SK_MAC_TO_53); + skge_write8(hw, B3_MA_TOINI_TX1, SK_MAC_TO_53); + skge_write8(hw, B3_MA_TOINI_TX2, SK_MAC_TO_53); + + skge_write8(hw, B3_MA_RCINI_RX1, 0); + skge_write8(hw, B3_MA_RCINI_RX2, 0); + skge_write8(hw, B3_MA_RCINI_TX1, 0); + skge_write8(hw, B3_MA_RCINI_TX2, 0); + + /* configure packet arbiter timeout */ + skge_write16(hw, B3_PA_CTRL, PA_RST_CLR); + skge_write16(hw, B3_PA_TOINI_RX1, SK_PKT_TO_MAX); + skge_write16(hw, B3_PA_TOINI_TX1, SK_PKT_TO_MAX); + skge_write16(hw, B3_PA_TOINI_RX2, SK_PKT_TO_MAX); + skge_write16(hw, B3_PA_TOINI_TX2, SK_PKT_TO_MAX); +} + +static void genesis_reset(struct skge_hw *hw, int port) +{ + int i; + u64 zero = 0; + + /* reset the statistics module */ + skge_xm_write32(hw, port, XM_GP_PORT, XM_GP_RES_STAT); + skge_xm_write16(hw, port, XM_IMSK, 0xffff); /* disable XMAC IRQs */ + skge_xm_write32(hw, port, XM_MODE, 0); /* clear Mode Reg */ + skge_xm_write16(hw, port, XM_TX_CMD, 0); /* reset TX CMD Reg */ + skge_xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */ + + /* disable all PHY IRQs */ + if (hw->phy_type == SK_PHY_BCOM) + skge_xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff); + + skge_xm_outhash(hw, port, XM_HSM, (u8 *) &zero); + for (i = 0; i < 15; i++) + skge_xm_outaddr(hw, port, XM_EXM(i), (u8 *) &zero); + skge_xm_outhash(hw, port, XM_SRC_CHK, (u8 *) &zero); +} + + +static void genesis_mac_init(struct skge_hw *hw, int port) +{ + struct skge_port *skge = netdev_priv(hw->dev[port]); + int i; + u32 r; + u16 id1; + u16 ctrl1, ctrl2, ctrl3, ctrl4, ctrl5; + + /* magic workaround patterns for Broadcom */ + static const struct { + u16 reg; + u16 val; + } A1hack[] = { + { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1104 }, + { 0x17, 0x0013 }, { 0x15, 0x0404 }, { 0x17, 0x8006 }, + { 0x15, 0x0132 }, { 0x17, 0x8006 }, { 0x15, 0x0232 }, + { 0x17, 0x800D }, { 0x15, 0x000F }, { 0x18, 0x0420 }, + }, C0hack[] = { + { 0x18, 0x0c20 }, { 0x17, 0x0012 }, { 0x15, 0x1204 }, + { 0x17, 0x0013 }, { 0x15, 0x0A04 }, { 0x18, 0x0420 }, + }; + + + /* initialize Rx, Tx and Link LED */ + skge_write8(hw, SKGEMAC_REG(port, LNK_LED_REG), LINKLED_ON); + skge_write8(hw, SKGEMAC_REG(port, LNK_LED_REG), LINKLED_LINKSYNC_ON); + + skge_write8(hw, SKGEMAC_REG(port, RX_LED_CTRL), LED_START); + skge_write8(hw, SKGEMAC_REG(port, TX_LED_CTRL), LED_START); + + /* Unreset the XMAC. */ + skge_write16(hw, SKGEMAC_REG(port, TX_MFF_CTRL1), MFF_CLR_MAC_RST); + + /* + * Perform additional initialization for external PHYs, + * namely for the 1000baseTX cards that use the XMAC's + * GMII mode. + */ + spin_lock_bh(&hw->phy_lock); + if (hw->phy_type != SK_PHY_XMAC) { + /* Take PHY out of reset. */ + r = skge_read32(hw, B2_GP_IO); + if (port == 0) + r |= GP_DIR_0|GP_IO_0; + else + r |= GP_DIR_2|GP_IO_2; + + skge_write32(hw, B2_GP_IO, r); + skge_read32(hw, B2_GP_IO); + + /* Enable GMII mode on the XMAC. */ + skge_xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD); + + id1 = skge_xm_phy_read(hw, port, PHY_XMAC_ID1); + + /* Optimize MDIO transfer by suppressing preamble. */ + skge_xm_write16(hw, port, XM_MMU_CMD, + skge_xm_read16(hw, port, XM_MMU_CMD) + | XM_MMU_NO_PRE); + + if (id1 == PHY_BCOM_ID1_C0) { + /* + * Workaround BCOM Errata for the C0 type. + * Write magic patterns to reserved registers. + */ + for (i = 0; i < ARRAY_SIZE(C0hack); i++) + skge_xm_phy_write(hw, port, + C0hack[i].reg, C0hack[i].val); + + } else if (id1 == PHY_BCOM_ID1_A1) { + /* + * Workaround BCOM Errata for the A1 type. + * Write magic patterns to reserved registers. + */ + for (i = 0; i < ARRAY_SIZE(A1hack); i++) + skge_xm_phy_write(hw, port, + A1hack[i].reg, A1hack[i].val); + } + + /* + * Workaround BCOM Errata (#10523) for all BCom PHYs. + * Disable Power Management after reset. + */ + r = skge_xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL); + skge_xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL, r | PHY_B_AC_DIS_PM); + } + + /* Dummy read */ + skge_xm_read16(hw, port, XM_ISRC); + + r = skge_xm_read32(hw, port, XM_MODE); + skge_xm_write32(hw, port, XM_MODE, r|XM_MD_CSA); + + /* We don't need the FCS appended to the packet. */ + r = skge_xm_read16(hw, port, XM_RX_CMD); + skge_xm_write16(hw, port, XM_RX_CMD, r | XM_RX_STRIP_FCS); + + /* We want short frames padded to 60 bytes. */ + r = skge_xm_read16(hw, port, XM_TX_CMD); + skge_xm_write16(hw, port, XM_TX_CMD, r | XM_TX_AUTO_PAD); + + /* + * Enable the reception of all error frames. This is is + * a necessary evil due to the design of the XMAC. The + * XMAC's receive FIFO is only 8K in size, however jumbo + * frames can be up to 9000 bytes in length. When bad + * frame filtering is enabled, the XMAC's RX FIFO operates + * in 'store and forward' mode. For this to work, the + * entire frame has to fit into the FIFO, but that means + * that jumbo frames larger than 8192 bytes will be + * truncated. Disabling all bad frame filtering causes + * the RX FIFO to operate in streaming mode, in which + * case the XMAC will start transfering frames out of the + * RX FIFO as soon as the FIFO threshold is reached. + */ + r = skge_xm_read32(hw, port, XM_MODE); + skge_xm_write32(hw, port, XM_MODE, + XM_MD_RX_CRCE|XM_MD_RX_LONG|XM_MD_RX_RUNT| + XM_MD_RX_ERR|XM_MD_RX_IRLE); + + skge_xm_outaddr(hw, port, XM_SA, hw->dev[port]->dev_addr); + skge_xm_outaddr(hw, port, XM_EXM(0), hw->dev[port]->dev_addr); + + /* + * Bump up the transmit threshold. This helps hold off transmit + * underruns when we're blasting traffic from both ports at once. + */ + skge_xm_write16(hw, port, XM_TX_THR, 512); + + /* Configure MAC arbiter */ + skge_write16(hw, B3_MA_TO_CTRL, MA_RST_CLR); + + /* configure timeout values */ + skge_write8(hw, B3_MA_TOINI_RX1, 72); + skge_write8(hw, B3_MA_TOINI_RX2, 72); + skge_write8(hw, B3_MA_TOINI_TX1, 72); + skge_write8(hw, B3_MA_TOINI_TX2, 72); + + skge_write8(hw, B3_MA_RCINI_RX1, 0); + skge_write8(hw, B3_MA_RCINI_RX2, 0); + skge_write8(hw, B3_MA_RCINI_TX1, 0); + skge_write8(hw, B3_MA_RCINI_TX2, 0); + + /* Configure Rx MAC FIFO */ + skge_write8(hw, SKGEMAC_REG(port, RX_MFF_CTRL2), MFF_RST_CLR); + skge_write16(hw, SKGEMAC_REG(port, RX_MFF_CTRL1), MFF_ENA_TIM_PAT); + skge_write8(hw, SKGEMAC_REG(port, RX_MFF_CTRL2), MFF_ENA_OP_MD); + + /* Configure Tx MAC FIFO */ + skge_write8(hw, SKGEMAC_REG(port, TX_MFF_CTRL2), MFF_RST_CLR); + skge_write16(hw, SKGEMAC_REG(port, TX_MFF_CTRL1), MFF_TX_CTRL_DEF); + skge_write8(hw, SKGEMAC_REG(port, TX_MFF_CTRL2), MFF_ENA_OP_MD); + + if (hw->dev[port]->mtu > ETH_DATA_LEN) { + /* Enable frame flushing if jumbo frames used */ + skge_write16(hw, SKGEMAC_REG(port,RX_MFF_CTRL1), MFF_ENA_FLUSH); + } else { + /* enable timeout timers if normal frames */ + skge_write16(hw, B3_PA_CTRL, + port == 0 ? PA_ENA_TO_TX1 : PA_ENA_TO_TX2); + } + + + r = skge_xm_read16(hw, port, XM_RX_CMD); + if (hw->dev[port]->mtu > ETH_DATA_LEN) + skge_xm_write16(hw, port, XM_RX_CMD, r | XM_RX_BIG_PK_OK); + else + skge_xm_write16(hw, port, XM_RX_CMD, r & ~(XM_RX_BIG_PK_OK)); + + switch (hw->phy_type) { + case SK_PHY_XMAC: + if (skge->autoneg == AUTONEG_ENABLE) { + ctrl1 = PHY_X_AN_FD | PHY_X_AN_HD; + + switch (skge->flow_control) { + case FLOW_MODE_NONE: + ctrl1 |= PHY_X_P_NO_PAUSE; + break; + case FLOW_MODE_LOC_SEND: + ctrl1 |= PHY_X_P_ASYM_MD; + break; + case FLOW_MODE_SYMMETRIC: + ctrl1 |= PHY_X_P_SYM_MD; + break; + case FLOW_MODE_REM_SEND: + ctrl1 |= PHY_X_P_BOTH_MD; + break; + } + + skge_xm_phy_write(hw, port, PHY_XMAC_AUNE_ADV, ctrl1); + ctrl2 = PHY_CT_ANE | PHY_CT_RE_CFG; + } else { + ctrl2 = 0; + if (skge->duplex == DUPLEX_FULL) + ctrl2 |= PHY_CT_DUP_MD; + } + + skge_xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl2); + break; + + case SK_PHY_BCOM: + ctrl1 = PHY_CT_SP1000; + ctrl2 = 0; + ctrl3 = PHY_SEL_TYPE; + ctrl4 = PHY_B_PEC_EN_LTR; + ctrl5 = PHY_B_AC_TX_TST; + + if (skge->autoneg == AUTONEG_ENABLE) { + /* + * Workaround BCOM Errata #1 for the C5 type. + * 1000Base-T Link Acquisition Failure in Slave Mode + * Set Repeater/DTE bit 10 of the 1000Base-T Control Register + */ + ctrl2 |= PHY_B_1000C_RD; + if (skge->advertising & ADVERTISED_1000baseT_Half) + ctrl2 |= PHY_B_1000C_AHD; + if (skge->advertising & ADVERTISED_1000baseT_Full) + ctrl2 |= PHY_B_1000C_AFD; + + /* Set Flow-control capabilities */ + switch (skge->flow_control) { + case FLOW_MODE_NONE: + ctrl3 |= PHY_B_P_NO_PAUSE; + break; + case FLOW_MODE_LOC_SEND: + ctrl3 |= PHY_B_P_ASYM_MD; + break; + case FLOW_MODE_SYMMETRIC: + ctrl3 |= PHY_B_P_SYM_MD; + break; + case FLOW_MODE_REM_SEND: + ctrl3 |= PHY_B_P_BOTH_MD; + break; + } + + /* Restart Auto-negotiation */ + ctrl1 |= PHY_CT_ANE | PHY_CT_RE_CFG; + } else { + if (skge->duplex == DUPLEX_FULL) + ctrl1 |= PHY_CT_DUP_MD; + + ctrl2 |= PHY_B_1000C_MSE; /* set it to Slave */ + } + + skge_xm_phy_write(hw, port, PHY_BCOM_1000T_CTRL, ctrl2); + skge_xm_phy_write(hw, port, PHY_BCOM_AUNE_ADV, ctrl3); + + if (skge->netdev->mtu > ETH_DATA_LEN) { + ctrl4 |= PHY_B_PEC_HIGH_LA; + ctrl5 |= PHY_B_AC_LONG_PACK; + + skge_xm_phy_write(hw, port,PHY_BCOM_AUX_CTRL, ctrl5); + } + + skge_xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, ctrl4); + skge_xm_phy_write(hw, port, PHY_BCOM_CTRL, ctrl1); + break; + } + spin_unlock_bh(&hw->phy_lock); + + /* Clear MIB counters */ + skge_xm_write16(hw, port, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + /* Clear two times according to Errata #3 */ + skge_xm_write16(hw, port, XM_STAT_CMD, + XM_SC_CLR_RXC | XM_SC_CLR_TXC); + + /* Start polling for link status */ + mod_timer(&skge->link_check, jiffies + LINK_POLL_HZ); +} + +static void genesis_stop(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + + /* Clear Tx packet arbiter timeout IRQ */ + skge_write16(hw, B3_PA_CTRL, + port == 0 ? PA_CLR_TO_TX1 : PA_CLR_TO_TX2); + + /* + * If the transfer stucks at the MAC the STOP command will not + * terminate if we don't flush the XMAC's transmit FIFO ! + */ + skge_xm_write32(hw, port, XM_MODE, + skge_xm_read32(hw, port, XM_MODE)|XM_MD_FTF); + + + /* Reset the MAC */ + skge_write16(hw, SKGEMAC_REG(port, TX_MFF_CTRL1), MFF_SET_MAC_RST); + + /* For external PHYs there must be special handling */ + if (hw->phy_type != SK_PHY_XMAC) { + u32 reg = skge_read32(hw, B2_GP_IO); + + if (port == 0) { + reg |= GP_DIR_0; + reg &= ~GP_IO_0; + } else { + reg |= GP_DIR_2; + reg &= ~GP_IO_2; + } + skge_write32(hw, B2_GP_IO, reg); + skge_read32(hw, B2_GP_IO); + } + + skge_xm_write16(hw, port, XM_MMU_CMD, + skge_xm_read16(hw, port, XM_MMU_CMD) + & ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX)); + + skge_xm_read16(hw, port, XM_MMU_CMD); +} + + +static void genesis_get_stats(struct skge_port *skge, u64 *data) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + int i; + unsigned long timeout = jiffies + HZ; + + skge_xm_write16(hw, port, + XM_STAT_CMD, XM_SC_SNP_TXC | XM_SC_SNP_RXC); + + /* wait for update to complete */ + while (skge_xm_read16(hw, port, XM_STAT_CMD) + & (XM_SC_SNP_TXC | XM_SC_SNP_RXC)) { + if (time_after(jiffies, timeout)) + break; + udelay(10); + } + + /* special case for 64 bit octet counter */ + data[0] = (u64) skge_xm_read32(hw, port, XM_TXO_OK_HI) << 32 + | skge_xm_read32(hw, port, XM_TXO_OK_LO); + data[1] = (u64) skge_xm_read32(hw, port, XM_RXO_OK_HI) << 32 + | skge_xm_read32(hw, port, XM_RXO_OK_LO); + + for (i = 2; i < ARRAY_SIZE(skge_stats); i++) + data[i] = skge_xm_read32(hw, port, skge_stats[i].xmac_offset); +} + +static void genesis_mac_intr(struct skge_hw *hw, int port) +{ + struct skge_port *skge = netdev_priv(hw->dev[port]); + u16 status = skge_xm_read16(hw, port, XM_ISRC); + + pr_debug("genesis_intr status %x\n", status); + if (hw->phy_type == SK_PHY_XMAC) { + /* LInk down, start polling for state change */ + if (status & XM_IS_INP_ASS) { + skge_xm_write16(hw, port, XM_IMSK, + skge_xm_read16(hw, port, XM_IMSK) | XM_IS_INP_ASS); + mod_timer(&skge->link_check, jiffies + LINK_POLL_HZ); + } + else if (status & XM_IS_AND) + mod_timer(&skge->link_check, jiffies + LINK_POLL_HZ); + } + + if (status & XM_IS_TXF_UR) { + skge_xm_write32(hw, port, XM_MODE, XM_MD_FTF); + ++skge->net_stats.tx_fifo_errors; + } + if (status & XM_IS_RXF_OV) { + skge_xm_write32(hw, port, XM_MODE, XM_MD_FRF); + ++skge->net_stats.rx_fifo_errors; + } +} + +static void skge_gm_phy_write(struct skge_hw *hw, int port, u16 reg, u16 val) +{ + int i; + + skge_gma_write16(hw, port, GM_SMI_DATA, val); + skge_gma_write16(hw, port, GM_SMI_CTRL, + GM_SMI_CT_PHY_AD(hw->phy_addr) | GM_SMI_CT_REG_AD(reg)); + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + + if (!(skge_gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_BUSY)) + break; + } +} + +static u16 skge_gm_phy_read(struct skge_hw *hw, int port, u16 reg) +{ + int i; + + skge_gma_write16(hw, port, GM_SMI_CTRL, + GM_SMI_CT_PHY_AD(hw->phy_addr) + | GM_SMI_CT_REG_AD(reg) | GM_SMI_CT_OP_RD); + + for (i = 0; i < PHY_RETRIES; i++) { + udelay(1); + if (skge_gma_read16(hw, port, GM_SMI_CTRL) & GM_SMI_CT_RD_VAL) + goto ready; + } + + printk(KERN_WARNING PFX "%s: phy read timeout\n", + hw->dev[port]->name); + return 0; + ready: + return skge_gma_read16(hw, port, GM_SMI_DATA); +} + +static void genesis_link_down(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + + pr_debug("genesis_link_down\n"); + + skge_xm_write16(hw, port, XM_MMU_CMD, + skge_xm_read16(hw, port, XM_MMU_CMD) + & ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX)); + + /* dummy read to ensure writing */ + (void) skge_xm_read16(hw, port, XM_MMU_CMD); + + skge_link_down(skge); +} + +static void genesis_link_up(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + u16 cmd; + u32 mode, msk; + + pr_debug("genesis_link_up\n"); + cmd = skge_xm_read16(hw, port, XM_MMU_CMD); + + /* + * enabling pause frame reception is required for 1000BT + * because the XMAC is not reset if the link is going down + */ + if (skge->flow_control == FLOW_MODE_NONE || + skge->flow_control == FLOW_MODE_LOC_SEND) + cmd |= XM_MMU_IGN_PF; + else + /* Enable Pause Frame Reception */ + cmd &= ~XM_MMU_IGN_PF; + + skge_xm_write16(hw, port, XM_MMU_CMD, cmd); + + mode = skge_xm_read32(hw, port, XM_MODE); + if (skge->flow_control == FLOW_MODE_SYMMETRIC || + skge->flow_control == FLOW_MODE_LOC_SEND) { + /* + * Configure Pause Frame Generation + * Use internal and external Pause Frame Generation. + * Sending pause frames is edge triggered. + * Send a Pause frame with the maximum pause time if + * internal oder external FIFO full condition occurs. + * Send a zero pause time frame to re-start transmission. + */ + /* XM_PAUSE_DA = '010000C28001' (default) */ + /* XM_MAC_PTIME = 0xffff (maximum) */ + /* remember this value is defined in big endian (!) */ + skge_xm_write16(hw, port, XM_MAC_PTIME, 0xffff); + + mode |= XM_PAUSE_MODE; + skge_write16(hw, SKGEMAC_REG(port, RX_MFF_CTRL1), MFF_ENA_PAUSE); + } else { + /* + * disable pause frame generation is required for 1000BT + * because the XMAC is not reset if the link is going down + */ + /* Disable Pause Mode in Mode Register */ + mode &= ~XM_PAUSE_MODE; + + skge_write16(hw, SKGEMAC_REG(port, RX_MFF_CTRL1), MFF_DIS_PAUSE); + } + + skge_xm_write32(hw, port, XM_MODE, mode); + + msk = XM_DEF_MSK; + if (hw->phy_type != SK_PHY_XMAC) + msk |= XM_IS_INP_ASS; /* disable GP0 interrupt bit */ + + skge_xm_write16(hw, port, XM_IMSK, msk); + skge_xm_read16(hw, port, XM_ISRC); + + /* get MMU Command Reg. */ + cmd = skge_xm_read16(hw, port, XM_MMU_CMD); + if (hw->phy_type != SK_PHY_XMAC && skge->duplex == DUPLEX_FULL) + cmd |= XM_MMU_GMII_FD; + + if (hw->phy_type == SK_PHY_BCOM) { + /* + * Workaround BCOM Errata (#10523) for all BCom Phys + * Enable Power Management after link up + */ + skge_xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL, + skge_xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL) + & ~PHY_B_AC_DIS_PM); + skge_xm_phy_write(hw, port, PHY_BCOM_INT_MASK, + PHY_B_DEF_MSK); + } + + /* enable Rx/Tx */ + skge_xm_write16(hw, port, XM_MMU_CMD, + cmd | XM_MMU_ENA_RX | XM_MMU_ENA_TX); + skge_link_up(skge); +} + + +static void genesis_bcom_intr(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + u16 stat = skge_xm_phy_read(hw, port, PHY_BCOM_INT_STAT); + + pr_debug("genesis_bcom intr stat=%x\n", stat); + + /* Workaround BCom Errata: + * enable and disable loopback mode if "NO HCD" occurs. + */ + if (stat & PHY_B_IS_NO_HDCL) { + u16 ctrl = skge_xm_phy_read(hw, port, PHY_BCOM_CTRL); + skge_xm_phy_write(hw, port, PHY_BCOM_CTRL, + ctrl | PHY_CT_LOOP); + skge_xm_phy_write(hw, port, PHY_BCOM_CTRL, + ctrl & ~PHY_CT_LOOP); + } + + stat = skge_xm_phy_read(hw, port, PHY_BCOM_STAT); + if (stat & (PHY_B_IS_AN_PR | PHY_B_IS_LST_CHANGE)) { + u16 aux = skge_xm_phy_read(hw, port, PHY_BCOM_AUX_STAT); + if ( !(aux & PHY_B_AS_LS) && netif_carrier_ok(skge->netdev)) + genesis_link_down(skge); + + else if (stat & PHY_B_IS_LST_CHANGE) { + if (aux & PHY_B_AS_AN_C) { + switch (aux & PHY_B_AS_AN_RES_MSK) { + case PHY_B_RES_1000FD: + skge->duplex = DUPLEX_FULL; + break; + case PHY_B_RES_1000HD: + skge->duplex = DUPLEX_HALF; + break; + } + + switch (aux & PHY_B_AS_PAUSE_MSK) { + case PHY_B_AS_PAUSE_MSK: + skge->flow_control = FLOW_MODE_SYMMETRIC; + break; + case PHY_B_AS_PRR: + skge->flow_control = FLOW_MODE_REM_SEND; + break; + case PHY_B_AS_PRT: + skge->flow_control = FLOW_MODE_LOC_SEND; + break; + default: + skge->flow_control = FLOW_MODE_NONE; + } + skge->speed = SPEED_1000; + } + genesis_link_up(skge); + } + else + mod_timer(&skge->link_check, jiffies + LINK_POLL_HZ); + } +} + +/* Perodic poll of phy status to check for link transistion */ +static void skge_link_timer(unsigned long __arg) +{ + struct skge_port *skge = (struct skge_port *) __arg; + struct skge_hw *hw = skge->hw; + int port = skge->port; + + if (hw->chip_id != CHIP_ID_GENESIS || !netif_running(skge->netdev)) + return; + + spin_lock_bh(&hw->phy_lock); + if (hw->phy_type == SK_PHY_BCOM) + genesis_bcom_intr(skge); + else { + int i; + for (i = 0; i < 3; i++) + if (skge_xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS) + break; + + if (i == 3) + mod_timer(&skge->link_check, jiffies + LINK_POLL_HZ); + else + genesis_link_up(skge); + } + spin_unlock_bh(&hw->phy_lock); +} + +/* Marvell Phy Initailization */ +static void yukon_init(struct skge_hw *hw, int port) +{ + struct skge_port *skge = netdev_priv(hw->dev[port]); + u16 ctrl, ct1000, adv; + u16 ledctrl, ledover; + + pr_debug("yukon_init\n"); + if (skge->autoneg == AUTONEG_ENABLE) { + u16 ectrl = skge_gm_phy_read(hw, port, PHY_MARV_EXT_CTRL); + + ectrl &= ~(PHY_M_EC_M_DSC_MSK | PHY_M_EC_S_DSC_MSK | + PHY_M_EC_MAC_S_MSK); + ectrl |= PHY_M_EC_MAC_S(MAC_TX_CLK_25_MHZ); + + /* on PHY 88E1111 there is a change for downshift control */ + if (hw->chip_id == CHIP_ID_YUKON_EC) + ectrl |= PHY_M_EC_M_DSC_2(0) | PHY_M_EC_DOWN_S_ENA; + else + ectrl |= PHY_M_EC_M_DSC(0) | PHY_M_EC_S_DSC(1); + + skge_gm_phy_write(hw, port, PHY_MARV_EXT_CTRL, ectrl); + } + + ctrl = skge_gm_phy_read(hw, port, PHY_MARV_CTRL); + if (skge->autoneg == AUTONEG_DISABLE) + ctrl &= ~PHY_CT_ANE; + + ctrl |= PHY_CT_RESET; + skge_gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl); + + ctrl = 0; + ct1000 = 0; + adv = PHY_SEL_TYPE; + + if (skge->autoneg == AUTONEG_ENABLE) { + if (iscopper(hw)) { + if (skge->advertising & ADVERTISED_1000baseT_Full) + ct1000 |= PHY_M_1000C_AFD; + if (skge->advertising & ADVERTISED_1000baseT_Half) + ct1000 |= PHY_M_1000C_AHD; + if (skge->advertising & ADVERTISED_100baseT_Full) + adv |= PHY_M_AN_100_FD; + if (skge->advertising & ADVERTISED_100baseT_Half) + adv |= PHY_M_AN_100_HD; + if (skge->advertising & ADVERTISED_10baseT_Full) + adv |= PHY_M_AN_10_FD; + if (skge->advertising & ADVERTISED_10baseT_Half) + adv |= PHY_M_AN_10_HD; + + /* Set Flow-control capabilities */ + switch (skge->flow_control) { + case FLOW_MODE_NONE: + adv |= PHY_B_P_NO_PAUSE; + break; + case FLOW_MODE_LOC_SEND: + adv |= PHY_B_P_ASYM_MD; + break; + case FLOW_MODE_SYMMETRIC: + adv |= PHY_B_P_SYM_MD; + break; + case FLOW_MODE_REM_SEND: + adv |= PHY_B_P_BOTH_MD; + break; + } + } else { /* special defines for FIBER (88E1011S only) */ + adv |= PHY_M_AN_1000X_AHD | PHY_M_AN_1000X_AFD; + + /* Set Flow-control capabilities */ + switch (skge->flow_control) { + case FLOW_MODE_NONE: + adv |= PHY_M_P_NO_PAUSE_X; + break; + case FLOW_MODE_LOC_SEND: + adv |= PHY_M_P_ASYM_MD_X; + break; + case FLOW_MODE_SYMMETRIC: + adv |= PHY_M_P_SYM_MD_X; + break; + case FLOW_MODE_REM_SEND: + adv |= PHY_M_P_BOTH_MD_X; + break; + } + } + /* Restart Auto-negotiation */ + ctrl |= PHY_CT_ANE | PHY_CT_RE_CFG; + } else { + /* forced speed/duplex settings */ + ct1000 = PHY_M_1000C_MSE; + + if (skge->duplex == DUPLEX_FULL) + ctrl |= PHY_CT_DUP_MD; + + switch (skge->speed) { + case SPEED_1000: + ctrl |= PHY_CT_SP1000; + break; + case SPEED_100: + ctrl |= PHY_CT_SP100; + break; + } + + ctrl |= PHY_CT_RESET; + } + + if (hw->chip_id != CHIP_ID_YUKON_FE) + skge_gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, ct1000); + + skge_gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, adv); + skge_gm_phy_write(hw, port, PHY_MARV_CTRL, ctrl); + + /* Setup Phy LED's */ + ledctrl = PHY_M_LED_PULS_DUR(PULS_170MS); + ledover = 0; + + if (hw->chip_id == CHIP_ID_YUKON_FE) { + /* on 88E3082 these bits are at 11..9 (shifted left) */ + ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) << 1; + + skge_gm_phy_write(hw, port, PHY_MARV_FE_LED_PAR, + ((skge_gm_phy_read(hw, port, PHY_MARV_FE_LED_PAR) + + & ~PHY_M_FELP_LED1_MSK) + | PHY_M_FELP_LED1_CTRL(LED_PAR_CTRL_ACT_BL))); + } else { + /* set Tx LED (LED_TX) to blink mode on Rx OR Tx activity */ + ledctrl |= PHY_M_LED_BLINK_RT(BLINK_84MS) | PHY_M_LEDC_TX_CTRL; + + /* turn off the Rx LED (LED_RX) */ + ledover |= PHY_M_LED_MO_RX(MO_LED_OFF); + } + + /* disable blink mode (LED_DUPLEX) on collisions */ + ctrl |= PHY_M_LEDC_DP_CTRL; + skge_gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); + + if (skge->autoneg == AUTONEG_DISABLE || skge->speed == SPEED_100) { + /* turn on 100 Mbps LED (LED_LINK100) */ + ledover |= PHY_M_LED_MO_100(MO_LED_ON); + } + + if (ledover) + skge_gm_phy_write(hw, port, PHY_MARV_LED_OVER, ledover); + + /* Enable phy interrupt on autonegotiation complete (or link up) */ + if (skge->autoneg == AUTONEG_ENABLE) + skge_gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_IS_AN_COMPL); + else + skge_gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); +} + +static void yukon_reset(struct skge_hw *hw, int port) +{ + skge_gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0);/* disable PHY IRQs */ + skge_gma_write16(hw, port, GM_MC_ADDR_H1, 0); /* clear MC hash */ + skge_gma_write16(hw, port, GM_MC_ADDR_H2, 0); + skge_gma_write16(hw, port, GM_MC_ADDR_H3, 0); + skge_gma_write16(hw, port, GM_MC_ADDR_H4, 0); + + skge_gma_write16(hw, port, GM_RX_CTRL, + skge_gma_read16(hw, port, GM_RX_CTRL) + | GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); +} + +static void yukon_mac_init(struct skge_hw *hw, int port) +{ + struct skge_port *skge = netdev_priv(hw->dev[port]); + int i; + u32 reg; + const u8 *addr = hw->dev[port]->dev_addr; + + /* WA code for COMA mode -- set PHY reset */ + if (hw->chip_id == CHIP_ID_YUKON_LITE && + chip_rev(hw) == CHIP_REV_YU_LITE_A3) + skge_write32(hw, B2_GP_IO, + (skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9)); + + /* hard reset */ + skge_write32(hw, SKGEMAC_REG(port, GPHY_CTRL), GPC_RST_SET); + skge_write32(hw, SKGEMAC_REG(port, GMAC_CTRL), GMC_RST_SET); + + /* WA code for COMA mode -- clear PHY reset */ + if (hw->chip_id == CHIP_ID_YUKON_LITE && + chip_rev(hw) == CHIP_REV_YU_LITE_A3) + skge_write32(hw, B2_GP_IO, + (skge_read32(hw, B2_GP_IO) | GP_DIR_9) + & ~GP_IO_9); + + /* Set hardware config mode */ + reg = GPC_INT_POL_HI | GPC_DIS_FC | GPC_DIS_SLEEP | + GPC_ENA_XC | GPC_ANEG_ADV_ALL_M | GPC_ENA_PAUSE; + reg |= iscopper(hw) ? GPC_HWCFG_GMII_COP : GPC_HWCFG_GMII_FIB; + + /* Clear GMC reset */ + skge_write32(hw, SKGEMAC_REG(port, GPHY_CTRL), reg | GPC_RST_SET); + skge_write32(hw, SKGEMAC_REG(port, GPHY_CTRL), reg | GPC_RST_CLR); + skge_write32(hw, SKGEMAC_REG(port, GMAC_CTRL), GMC_PAUSE_ON | GMC_RST_CLR); + if (skge->autoneg == AUTONEG_DISABLE) { + reg = GM_GPCR_AU_ALL_DIS; + skge_gma_write16(hw, port, GM_GP_CTRL, + skge_gma_read16(hw, port, GM_GP_CTRL) | reg); + + switch (skge->speed) { + case SPEED_1000: + reg |= GM_GPCR_SPEED_1000; + /* fallthru */ + case SPEED_100: + reg |= GM_GPCR_SPEED_100; + } + + if (skge->duplex == DUPLEX_FULL) + reg |= GM_GPCR_DUP_FULL; + } else + reg = GM_GPCR_SPEED_1000 | GM_GPCR_SPEED_100 | GM_GPCR_DUP_FULL; + switch (skge->flow_control) { + case FLOW_MODE_NONE: + skge_write32(hw, SKGEMAC_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); + reg |= GM_GPCR_FC_TX_DIS | GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; + break; + case FLOW_MODE_LOC_SEND: + /* disable Rx flow-control */ + reg |= GM_GPCR_FC_RX_DIS | GM_GPCR_AU_FCT_DIS; + } + + skge_gma_write16(hw, port, GM_GP_CTRL, reg); + skge_read16(hw, GMAC_IRQ_SRC); + + spin_lock_bh(&hw->phy_lock); + yukon_init(hw, port); + spin_unlock_bh(&hw->phy_lock); + + /* MIB clear */ + reg = skge_gma_read16(hw, port, GM_PHY_ADDR); + skge_gma_write16(hw, port, GM_PHY_ADDR, reg | GM_PAR_MIB_CLR); + + for (i = 0; i < GM_MIB_CNT_SIZE; i++) + skge_gma_read16(hw, port, GM_MIB_CNT_BASE + 8*i); + skge_gma_write16(hw, port, GM_PHY_ADDR, reg); + + /* transmit control */ + skge_gma_write16(hw, port, GM_TX_CTRL, TX_COL_THR(TX_COL_DEF)); + + /* receive control reg: unicast + multicast + no FCS */ + skge_gma_write16(hw, port, GM_RX_CTRL, + GM_RXCR_UCF_ENA | GM_RXCR_CRC_DIS | GM_RXCR_MCF_ENA); + + /* transmit flow control */ + skge_gma_write16(hw, port, GM_TX_FLOW_CTRL, 0xffff); + + /* transmit parameter */ + skge_gma_write16(hw, port, GM_TX_PARAM, + TX_JAM_LEN_VAL(TX_JAM_LEN_DEF) | + TX_JAM_IPG_VAL(TX_JAM_IPG_DEF) | + TX_IPG_JAM_DATA(TX_IPG_JAM_DEF)); + + /* serial mode register */ + reg = GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF); + if (hw->dev[port]->mtu > 1500) + reg |= GM_SMOD_JUMBO_ENA; + + skge_gma_write16(hw, port, GM_SERIAL_MODE, reg); + + /* physical address: used for pause frames */ + skge_gm_set_addr(hw, port, GM_SRC_ADDR_1L, addr); + /* virtual address for data */ + skge_gm_set_addr(hw, port, GM_SRC_ADDR_2L, addr); + + /* enable interrupt mask for counter overflows */ + skge_gma_write16(hw, port, GM_TX_IRQ_MSK, 0); + skge_gma_write16(hw, port, GM_RX_IRQ_MSK, 0); + skge_gma_write16(hw, port, GM_TR_IRQ_MSK, 0); + + /* Initialize Mac Fifo */ + + /* Configure Rx MAC FIFO */ + skge_write16(hw, SKGEMAC_REG(port, RX_GMF_FL_MSK), RX_FF_FL_DEF_MSK); + reg = GMF_OPER_ON | GMF_RX_F_FL_ON; + if (hw->chip_id == CHIP_ID_YUKON_LITE && + chip_rev(hw) == CHIP_REV_YU_LITE_A3) + reg &= ~GMF_RX_F_FL_ON; + skge_write8(hw, SKGEMAC_REG(port, RX_GMF_CTRL_T), GMF_RST_CLR); + skge_write16(hw, SKGEMAC_REG(port, RX_GMF_CTRL_T), reg); + skge_write16(hw, SKGEMAC_REG(port, RX_GMF_FL_THR), RX_GMF_FL_THR_DEF); + + /* Configure Tx MAC FIFO */ + skge_write8(hw, SKGEMAC_REG(port, TX_GMF_CTRL_T), GMF_RST_CLR); + skge_write16(hw, SKGEMAC_REG(port, TX_GMF_CTRL_T), GMF_OPER_ON); +} + +static void yukon_stop(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + + if (hw->chip_id == CHIP_ID_YUKON_LITE && + chip_rev(hw) == CHIP_REV_YU_LITE_A3) { + skge_write32(hw, B2_GP_IO, + skge_read32(hw, B2_GP_IO) | GP_DIR_9 | GP_IO_9); + } + + skge_gma_write16(hw, port, GM_GP_CTRL, + skge_gma_read16(hw, port, GM_GP_CTRL) + & ~(GM_GPCR_RX_ENA|GM_GPCR_RX_ENA)); + skge_gma_read16(hw, port, GM_GP_CTRL); + + /* set GPHY Control reset */ + skge_gma_write32(hw, port, GPHY_CTRL, GPC_RST_SET); + skge_gma_write32(hw, port, GMAC_CTRL, GMC_RST_SET); +} + +static void yukon_get_stats(struct skge_port *skge, u64 *data) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + int i; + + data[0] = (u64) skge_gma_read32(hw, port, GM_TXO_OK_HI) << 32 + | skge_gma_read32(hw, port, GM_TXO_OK_LO); + data[1] = (u64) skge_gma_read32(hw, port, GM_RXO_OK_HI) << 32 + | skge_gma_read32(hw, port, GM_RXO_OK_LO); + + for (i = 2; i < ARRAY_SIZE(skge_stats); i++) + data[i] = skge_gma_read32(hw, port, + skge_stats[i].gma_offset); +} + +static void yukon_mac_intr(struct skge_hw *hw, int port) +{ + struct skge_port *skge = netdev_priv(hw->dev[port]); + u8 status = skge_read8(hw, SKGEMAC_REG(port, GMAC_IRQ_SRC)); + + pr_debug("yukon_intr status %x\n", status); + if (status & GM_IS_RX_FF_OR) { + ++skge->net_stats.rx_fifo_errors; + skge_gma_write8(hw, port, RX_GMF_CTRL_T, GMF_CLI_RX_FO); + } + if (status & GM_IS_TX_FF_UR) { + ++skge->net_stats.tx_fifo_errors; + skge_gma_write8(hw, port, TX_GMF_CTRL_T, GMF_CLI_TX_FU); + } + +} + +static u16 yukon_speed(const struct skge_hw *hw, u16 aux) +{ + if (hw->chip_id == CHIP_ID_YUKON_FE) + return (aux & PHY_M_PS_SPEED_100) ? SPEED_100 : SPEED_10; + + switch(aux & PHY_M_PS_SPEED_MSK) { + case PHY_M_PS_SPEED_1000: + return SPEED_1000; + case PHY_M_PS_SPEED_100: + return SPEED_100; + default: + return SPEED_10; + } +} + +static void yukon_link_up(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + u16 reg; + + pr_debug("yukon_link_up\n"); + + /* Enable Transmit FIFO Underrun */ + skge_write8(hw, GMAC_IRQ_MSK, GMAC_DEF_MSK); + + reg = skge_gma_read16(hw, port, GM_GP_CTRL); + if (skge->duplex == DUPLEX_FULL || skge->autoneg == AUTONEG_ENABLE) + reg |= GM_GPCR_DUP_FULL; + + /* enable Rx/Tx */ + reg |= GM_GPCR_RX_ENA | GM_GPCR_TX_ENA; + skge_gma_write16(hw, port, GM_GP_CTRL, reg); + + skge_gm_phy_write(hw, port, PHY_MARV_INT_MASK, PHY_M_DEF_MSK); + skge_link_up(skge); +} + +static void yukon_link_down(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + + pr_debug("yukon_link_down\n"); + skge_gm_phy_write(hw, port, PHY_MARV_INT_MASK, 0); + skge_gm_phy_write(hw, port, GM_GP_CTRL, + skge_gm_phy_read(hw, port, GM_GP_CTRL) + & ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA)); + + if (hw->chip_id != CHIP_ID_YUKON_FE && + skge->flow_control == FLOW_MODE_REM_SEND) { + /* restore Asymmetric Pause bit */ + skge_gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, + skge_gm_phy_read(hw, port, + PHY_MARV_AUNE_ADV) + | PHY_M_AN_ASP); + + } + + yukon_reset(hw, port); + skge_link_down(skge); + + yukon_init(hw, port); +} + +static void yukon_phy_intr(struct skge_port *skge) +{ + struct skge_hw *hw = skge->hw; + int port = skge->port; + const char *reason = NULL; + u16 istatus, phystat; + + istatus = skge_gm_phy_read(hw, port, PHY_MARV_INT_STAT); + phystat = skge_gm_phy_read(hw, port, PHY_MARV_PHY_STAT); + pr_debug("yukon phy intr istat=%x phy_stat=%x\n", istatus, phystat); + + if (istatus & PHY_M_IS_AN_COMPL) { + if (skge_gm_phy_read(hw, port, PHY_MARV_AUNE_LP) + & PHY_M_AN_RF) { + reason = "remote fault"; + goto failed; + } + + if (!(hw->chip_id == CHIP_ID_YUKON_FE || hw->chip_id == CHIP_ID_YUKON_EC) + && (skge_gm_phy_read(hw, port, PHY_MARV_1000T_STAT) + & PHY_B_1000S_MSF)) { + reason = "master/slave fault"; + goto failed; + } + + if (!(phystat & PHY_M_PS_SPDUP_RES)) { + reason = "speed/duplex"; + goto failed; + } + + skge->duplex = (phystat & PHY_M_PS_FULL_DUP) + ? DUPLEX_FULL : DUPLEX_HALF; + skge->speed = yukon_speed(hw, phystat); + + /* Tx & Rx Pause Enabled bits are at 9..8 */ + if (hw->chip_id == CHIP_ID_YUKON_XL) + phystat >>= 6; + + /* We are using IEEE 802.3z/D5.0 Table 37-4 */ + switch (phystat & PHY_M_PS_PAUSE_MSK) { + case PHY_M_PS_PAUSE_MSK: + skge->flow_control = FLOW_MODE_SYMMETRIC; + break; + case PHY_M_PS_RX_P_EN: + skge->flow_control = FLOW_MODE_REM_SEND; + break; + case PHY_M_PS_TX_P_EN: + skge->flow_control = FLOW_MODE_LOC_SEND; + break; + default: + skge->flow_control = FLOW_MODE_NONE; + } + + if (skge->flow_control == FLOW_MODE_NONE || + (skge->speed < SPEED_1000 && skge->duplex == DUPLEX_HALF)) + skge_write8(hw, SKGEMAC_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); + else + skge_write8(hw, SKGEMAC_REG(port, GMAC_CTRL), GMC_PAUSE_ON); + yukon_link_up(skge); + return; + } + + if (istatus & PHY_M_IS_LSP_CHANGE) + skge->speed = yukon_speed(hw, phystat); + + if (istatus & PHY_M_IS_DUP_CHANGE) + skge->duplex = (phystat & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; + if (istatus & PHY_M_IS_LST_CHANGE) { + if (phystat & PHY_M_PS_LINK_UP) + yukon_link_up(skge); + else + yukon_link_down(skge); + } + return; + failed: + printk(KERN_ERR PFX "%s: autonegotiation failed (%s)\n", + skge->netdev->name, reason); + + /* XXX restart autonegotiation? */ +} + +static void skge_ramset(struct skge_hw *hw, u16 q, u32 start, size_t len) +{ + u32 end; + + start /= 8; + len /= 8; + end = start + len - 1; + + skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_RST_CLR); + skge_write32(hw, RB_ADDR(q, RB_START), start); + skge_write32(hw, RB_ADDR(q, RB_WP), start); + skge_write32(hw, RB_ADDR(q, RB_RP), start); + skge_write32(hw, RB_ADDR(q, RB_END), end); + + if (q == Q_R1 || q == Q_R2) { + /* Set thresholds on receive queue's */ + skge_write32(hw, RB_ADDR(q, RB_RX_UTPP), + start + (2*len)/3); + skge_write32(hw, RB_ADDR(q, RB_RX_LTPP), + start + (len/3)); + } else { + /* Enable store & forward on Tx queue's because + * Tx FIFO is only 4K on Genesis and 1K on Yukon + */ + skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_STFWD); + } + + skge_write8(hw, RB_ADDR(q, RB_CTRL), RB_ENA_OP_MD); +} + +/* Setup Bus Memory Interface */ +static void skge_qset(struct skge_port *skge, u16 q, + const struct skge_element *e) +{ + struct skge_hw *hw = skge->hw; + u32 watermark = 0x600; + u64 base = skge->dma + (e->desc - skge->mem); + + /* optimization to reduce window on 32bit/33mhz */ + if ((skge_read16(hw, B0_CTST) & (CS_BUS_CLOCK | CS_BUS_SLOT_SZ)) == 0) + watermark /= 2; + + skge_write32(hw, Q_ADDR(q, Q_CSR), CSR_CLR_RESET); + skge_write32(hw, Q_ADDR(q, Q_F), watermark); + skge_write32(hw, Q_ADDR(q, Q_DA_H), (u32)(base >> 32)); + skge_write32(hw, Q_ADDR(q, Q_DA_L), (u32)base); +} + +static int skge_up(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + u32 chunk, ram_addr; + size_t rx_size, tx_size; + int err; + + if (netif_msg_ifup(skge)) + printk(KERN_INFO PFX "%s: enabling interface\n", dev->name); + + rx_size = skge->rx_ring.count * sizeof(struct skge_rx_desc); + tx_size = skge->tx_ring.count * sizeof(struct skge_tx_desc); + skge->mem_size = tx_size + rx_size; + skge->mem = pci_alloc_consistent(hw->pdev, skge->mem_size, &skge->dma); + if (!skge->mem) + return -ENOMEM; + + memset(skge->mem, 0, skge->mem_size); + + if ((err = skge_ring_alloc(&skge->rx_ring, skge->mem, skge->dma))) + goto free_pci_mem; + + if (skge_rx_fill(skge)) + goto free_rx_ring; + + if ((err = skge_ring_alloc(&skge->tx_ring, skge->mem + rx_size, + skge->dma + rx_size))) + goto free_rx_ring; + + skge->tx_avail = skge->tx_ring.count - 1; + + /* Initialze MAC */ + if (hw->chip_id == CHIP_ID_GENESIS) + genesis_mac_init(hw, port); + else + yukon_mac_init(hw, port); + + /* Configure RAMbuffers */ + chunk = hw->ram_size / (isdualport(hw) ? 4 : 2); + ram_addr = hw->ram_offset + 2 * chunk * port; + + skge_ramset(hw, rxqaddr[port], ram_addr, chunk); + skge_qset(skge, rxqaddr[port], skge->rx_ring.to_clean); + + BUG_ON(skge->tx_ring.to_use != skge->tx_ring.to_clean); + skge_ramset(hw, txqaddr[port], ram_addr+chunk, chunk); + skge_qset(skge, txqaddr[port], skge->tx_ring.to_use); + + /* Start receiver BMU */ + wmb(); + skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F); + + pr_debug("skge_up completed\n"); + return 0; + + free_rx_ring: + skge_rx_clean(skge); + kfree(skge->rx_ring.start); + free_pci_mem: + pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma); + + return err; +} + +static int skge_down(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + + if (netif_msg_ifdown(skge)) + printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); + + netif_stop_queue(dev); + + del_timer_sync(&skge->led_blink); + del_timer_sync(&skge->link_check); + + /* Stop transmitter */ + skge_write8(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_STOP); + skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), + RB_RST_SET|RB_DIS_OP_MD); + + if (hw->chip_id == CHIP_ID_GENESIS) + genesis_stop(skge); + else + yukon_stop(skge); + + /* Disable Force Sync bit and Enable Alloc bit */ + skge_write8(hw, SKGEMAC_REG(port, TXA_CTRL), + TXA_DIS_FSYNC | TXA_DIS_ALLOC | TXA_STOP_RC); + + /* Stop Interval Timer and Limit Counter of Tx Arbiter */ + skge_write32(hw, SKGEMAC_REG(port, TXA_ITI_INI), 0L); + skge_write32(hw, SKGEMAC_REG(port, TXA_LIM_INI), 0L); + + /* Reset PCI FIFO */ + skge_write32(hw, Q_ADDR(txqaddr[port], Q_CSR), CSR_SET_RESET); + skge_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET); + + /* Reset the RAM Buffer async Tx queue */ + skge_write8(hw, RB_ADDR(port == 0 ? Q_XA1 : Q_XA2, RB_CTRL), RB_RST_SET); + /* stop receiver */ + skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_STOP); + skge_write32(hw, RB_ADDR(port ? Q_R2 : Q_R1, RB_CTRL), + RB_RST_SET|RB_DIS_OP_MD); + skge_write32(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_SET_RESET); + + if (hw->chip_id == CHIP_ID_GENESIS) { + skge_write8(hw, SKGEMAC_REG(port, TX_MFF_CTRL2), MFF_RST_SET); + skge_write8(hw, SKGEMAC_REG(port, RX_MFF_CTRL2), MFF_RST_SET); + skge_write8(hw, SKGEMAC_REG(port, TX_LED_CTRL), LED_STOP); + skge_write8(hw, SKGEMAC_REG(port, RX_LED_CTRL), LED_STOP); + } else { + skge_write8(hw, SKGEMAC_REG(port, RX_GMF_CTRL_T), GMF_RST_SET); + skge_write8(hw, SKGEMAC_REG(port, TX_GMF_CTRL_T), GMF_RST_SET); + } + + /* turn off led's */ + skge_write16(hw, B0_LED, LED_STAT_OFF); + + skge_tx_clean(skge); + skge_rx_clean(skge); + + kfree(skge->rx_ring.start); + kfree(skge->tx_ring.start); + pci_free_consistent(hw->pdev, skge->mem_size, skge->mem, skge->dma); + return 0; +} + +static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + struct skge_ring *ring = &skge->tx_ring; + struct skge_element *e; + struct skge_tx_desc *td; + int i; + u32 control, len; + u64 map; + unsigned long flags; + + skb = skb_padto(skb, ETH_ZLEN); + if (!skb) + return NETDEV_TX_OK; + + local_irq_save(flags); + if (!spin_trylock(&skge->tx_lock)) { + /* Collision - tell upper layer to requeue */ + local_irq_restore(flags); + return NETDEV_TX_LOCKED; + } + + if (unlikely(skge->tx_avail < skb_shinfo(skb)->nr_frags +1)) { + netif_stop_queue(dev); + spin_unlock_irqrestore(&skge->tx_lock, flags); + + printk(KERN_WARNING PFX "%s: ring full when queue awake!\n", + dev->name); + return NETDEV_TX_BUSY; + } + + e = ring->to_use; + td = e->desc; + e->skb = skb; + len = skb_headlen(skb); + map = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE); + pci_unmap_addr_set(e, mapaddr, map); + pci_unmap_len_set(e, maplen, len); + + td->dma_lo = map; + td->dma_hi = map >> 32; + + if (skb->ip_summed == CHECKSUM_HW) { + const struct iphdr *ip + = (const struct iphdr *) (skb->data + ETH_HLEN); + int offset = skb->h.raw - skb->data; + + /* This seems backwards, but it is what the sk98lin + * does. Looks like hardware is wrong? + */ + if (ip->protocol == IPPROTO_UDP + && chip_rev(hw) == 0 && hw->chip_id == CHIP_ID_YUKON) + control = BMU_TCP_CHECK; + else + control = BMU_UDP_CHECK; + + td->csum_offs = 0; + td->csum_start = offset; + td->csum_write = offset + skb->csum; + } else + control = BMU_CHECK; + + if (!skb_shinfo(skb)->nr_frags) /* single buffer i.e. no fragments */ + control |= BMU_EOF| BMU_IRQ_EOF; + else { + struct skge_tx_desc *tf = td; + + control |= BMU_STFWD; + for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + + map = pci_map_page(hw->pdev, frag->page, frag->page_offset, + frag->size, PCI_DMA_TODEVICE); + + e = e->next; + e->skb = NULL; + tf = e->desc; + tf->dma_lo = map; + tf->dma_hi = (u64) map >> 32; + pci_unmap_addr_set(e, mapaddr, map); + pci_unmap_len_set(e, maplen, frag->size); + + tf->control = BMU_OWN | BMU_SW | control | frag->size; + } + tf->control |= BMU_EOF | BMU_IRQ_EOF; + } + /* Make sure all the descriptors written */ + wmb(); + td->control = BMU_OWN | BMU_SW | BMU_STF | control | len; + wmb(); + + skge_write8(hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_START); + + if (netif_msg_tx_queued(skge)) + printk(KERN_DEBUG "%s: tx queued, slot %d, len %d\n", + dev->name, e - ring->start, skb->len); + + ring->to_use = e->next; + skge->tx_avail -= skb_shinfo(skb)->nr_frags + 1; + if (skge->tx_avail <= MAX_SKB_FRAGS + 1) { + pr_debug("%s: transmit queue full\n", dev->name); + netif_stop_queue(dev); + } + + dev->trans_start = jiffies; + spin_unlock_irqrestore(&skge->tx_lock, flags); + + return NETDEV_TX_OK; +} + +static inline void skge_tx_free(struct skge_hw *hw, struct skge_element *e) +{ + if (e->skb) { + pci_unmap_single(hw->pdev, + pci_unmap_addr(e, mapaddr), + pci_unmap_len(e, maplen), + PCI_DMA_TODEVICE); + dev_kfree_skb_any(e->skb); + e->skb = NULL; + } else { + pci_unmap_page(hw->pdev, + pci_unmap_addr(e, mapaddr), + pci_unmap_len(e, maplen), + PCI_DMA_TODEVICE); + } +} + +static void skge_tx_clean(struct skge_port *skge) +{ + struct skge_ring *ring = &skge->tx_ring; + struct skge_element *e; + unsigned long flags; + + spin_lock_irqsave(&skge->tx_lock, flags); + for (e = ring->to_clean; e != ring->to_use; e = e->next) { + ++skge->tx_avail; + skge_tx_free(skge->hw, e); + } + ring->to_clean = e; + spin_unlock_irqrestore(&skge->tx_lock, flags); +} + +static void skge_tx_timeout(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + + if (netif_msg_timer(skge)) + printk(KERN_DEBUG PFX "%s: tx timeout\n", dev->name); + + skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_STOP); + skge_tx_clean(skge); +} + +static int skge_change_mtu(struct net_device *dev, int new_mtu) +{ + int err = 0; + + if(new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + + if (netif_running(dev)) { + skge_down(dev); + skge_up(dev); + } + + return err; +} + +static void genesis_set_multicast(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + int i, count = dev->mc_count; + struct dev_mc_list *list = dev->mc_list; + u32 mode; + u8 filter[8]; + + mode = skge_xm_read32(hw, port, XM_MODE); + mode |= XM_MD_ENA_HASH; + if (dev->flags & IFF_PROMISC) + mode |= XM_MD_ENA_PROM; + else + mode &= ~XM_MD_ENA_PROM; + + if (dev->flags & IFF_ALLMULTI) + memset(filter, 0xff, sizeof(filter)); + else { + memset(filter, 0, sizeof(filter)); + for(i = 0; list && i < count; i++, list = list->next) { + u32 crc = crc32_le(~0, list->dmi_addr, ETH_ALEN); + u8 bit = 63 - (crc & 63); + + filter[bit/8] |= 1 << (bit%8); + } + } + + skge_xm_outhash(hw, port, XM_HSM, filter); + + skge_xm_write32(hw, port, XM_MODE, mode); +} + +static void yukon_set_multicast(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + int port = skge->port; + struct dev_mc_list *list = dev->mc_list; + u16 reg; + u8 filter[8]; + + memset(filter, 0, sizeof(filter)); + + reg = skge_gma_read16(hw, port, GM_RX_CTRL); + reg |= GM_RXCR_UCF_ENA; + + if (dev->flags & IFF_PROMISC) /* promiscious */ + reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); + else if (dev->flags & IFF_ALLMULTI) /* all multicast */ + memset(filter, 0xff, sizeof(filter)); + else if (dev->mc_count == 0) /* no multicast */ + reg &= ~GM_RXCR_MCF_ENA; + else { + int i; + reg |= GM_RXCR_MCF_ENA; + + for(i = 0; list && i < dev->mc_count; i++, list = list->next) { + u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f; + filter[bit/8] |= 1 << (bit%8); + } + } + + + skge_gma_write16(hw, port, GM_MC_ADDR_H1, + (u16)filter[0] | ((u16)filter[1] << 8)); + skge_gma_write16(hw, port, GM_MC_ADDR_H2, + (u16)filter[2] | ((u16)filter[3] << 8)); + skge_gma_write16(hw, port, GM_MC_ADDR_H3, + (u16)filter[4] | ((u16)filter[5] << 8)); + skge_gma_write16(hw, port, GM_MC_ADDR_H4, + (u16)filter[6] | ((u16)filter[7] << 8)); + + skge_gma_write16(hw, port, GM_RX_CTRL, reg); +} + +static inline int bad_phy_status(const struct skge_hw *hw, u32 status) +{ + if (hw->chip_id == CHIP_ID_GENESIS) + return (status & (XMR_FS_ERR | XMR_FS_2L_VLAN)) != 0; + else + return (status & GMR_FS_ANY_ERR) || + (status & GMR_FS_RX_OK) == 0; +} + +static void skge_rx_error(struct skge_port *skge, int slot, + u32 control, u32 status) +{ + if (netif_msg_rx_err(skge)) + printk(KERN_DEBUG PFX "%s: rx err, slot %d control 0x%x status 0x%x\n", + skge->netdev->name, slot, control, status); + + if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF) + || (control & BMU_BBC) > skge->netdev->mtu + VLAN_ETH_HLEN) + skge->net_stats.rx_length_errors++; + else { + if (skge->hw->chip_id == CHIP_ID_GENESIS) { + if (status & (XMR_FS_RUNT|XMR_FS_LNG_ERR)) + skge->net_stats.rx_length_errors++; + if (status & XMR_FS_FRA_ERR) + skge->net_stats.rx_frame_errors++; + if (status & XMR_FS_FCS_ERR) + skge->net_stats.rx_crc_errors++; + } else { + if (status & (GMR_FS_LONG_ERR|GMR_FS_UN_SIZE)) + skge->net_stats.rx_length_errors++; + if (status & GMR_FS_FRAGMENT) + skge->net_stats.rx_frame_errors++; + if (status & GMR_FS_CRC_ERR) + skge->net_stats.rx_crc_errors++; + } + } +} + +static int skge_poll(struct net_device *dev, int *budget) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + struct skge_ring *ring = &skge->rx_ring; + struct skge_element *e; + unsigned int to_do = min(dev->quota, *budget); + unsigned int work_done = 0; + int done; + static const u32 irqmask[] = { IS_PORT_1, IS_PORT_2 }; + + for (e = ring->to_clean; e != ring->to_use && work_done < to_do; + e = e->next) { + struct skge_rx_desc *rd = e->desc; + struct sk_buff *skb = e->skb; + u32 control, len, status; + + rmb(); + control = rd->control; + if (control & BMU_OWN) + break; + + len = control & BMU_BBC; + e->skb = NULL; + + pci_unmap_single(hw->pdev, + pci_unmap_addr(e, mapaddr), + pci_unmap_len(e, maplen), + PCI_DMA_FROMDEVICE); + + status = rd->status; + if ((control & (BMU_EOF|BMU_STF)) != (BMU_STF|BMU_EOF) + || len > dev->mtu + VLAN_ETH_HLEN + || bad_phy_status(hw, status)) { + skge_rx_error(skge, e - ring->start, control, status); + dev_kfree_skb(skb); + continue; + } + + if (netif_msg_rx_status(skge)) + printk(KERN_DEBUG PFX "%s: rx slot %d status 0x%x len %d\n", + dev->name, e - ring->start, rd->status, len); + + skb_put(skb, len); + skb->protocol = eth_type_trans(skb, dev); + + if (skge->rx_csum) { + skb->csum = le16_to_cpu(rd->csum2); + skb->ip_summed = CHECKSUM_HW; + } + + dev->last_rx = jiffies; + netif_receive_skb(skb); + + ++work_done; + } + ring->to_clean = e; + + *budget -= work_done; + dev->quota -= work_done; + done = work_done < to_do; + + if (skge_rx_fill(skge)) + done = 0; + + /* restart receiver */ + wmb(); + skge_write8(hw, Q_ADDR(rxqaddr[skge->port], Q_CSR), + CSR_START | CSR_IRQ_CL_F); + + if (done) { + local_irq_disable(); + hw->intr_mask |= irqmask[skge->port]; + /* Order is important since data can get interrupted */ + skge_write32(hw, B0_IMSK, hw->intr_mask); + __netif_rx_complete(dev); + local_irq_enable(); + } + + return !done; +} + +static inline void skge_tx_intr(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + struct skge_hw *hw = skge->hw; + struct skge_ring *ring = &skge->tx_ring; + struct skge_element *e; + + spin_lock(&skge->tx_lock); + for(e = ring->to_clean; e != ring->to_use; e = e->next) { + struct skge_tx_desc *td = e->desc; + u32 control; + + rmb(); + control = td->control; + if (control & BMU_OWN) + break; + + if (unlikely(netif_msg_tx_done(skge))) + printk(KERN_DEBUG PFX "%s: tx done slot %d status 0x%x\n", + dev->name, e - ring->start, td->status); + + skge_tx_free(hw, e); + e->skb = NULL; + ++skge->tx_avail; + } + ring->to_clean = e; + skge_write8(hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F); + + if (skge->tx_avail > MAX_SKB_FRAGS + 1) + netif_wake_queue(dev); + + spin_unlock(&skge->tx_lock); +} + +static void skge_mac_parity(struct skge_hw *hw, int port) +{ + printk(KERN_ERR PFX "%s: mac data parity error\n", + hw->dev[port] ? hw->dev[port]->name + : (port == 0 ? "(port A)": "(port B")); + + if (hw->chip_id == CHIP_ID_GENESIS) + skge_write16(hw, SKGEMAC_REG(port, TX_MFF_CTRL1), + MFF_CLR_PERR); + else + /* HW-Bug #8: cleared by GMF_CLI_TX_FC instead of GMF_CLI_TX_PE */ + skge_write8(hw, SKGEMAC_REG(port, TX_GMF_CTRL_T), + (hw->chip_id == CHIP_ID_YUKON && chip_rev(hw) == 0) + ? GMF_CLI_TX_FC : GMF_CLI_TX_PE); +} + +static void skge_pci_clear(struct skge_hw *hw) +{ + u16 status; + + status = skge_read16(hw, SKGEPCI_REG(PCI_STATUS)); + skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON); + skge_write16(hw, SKGEPCI_REG(PCI_STATUS), + status | PCI_STATUS_ERROR_BITS); + skge_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF); +} + +static void skge_mac_intr(struct skge_hw *hw, int port) +{ + if (hw->chip_id == CHIP_ID_GENESIS) + genesis_mac_intr(hw, port); + else + yukon_mac_intr(hw, port); +} + +/* Handle device specific framing and timeout interrupts */ +static void skge_error_irq(struct skge_hw *hw) +{ + u32 hwstatus = skge_read32(hw, B0_HWE_ISRC); + + if (hw->chip_id == CHIP_ID_GENESIS) { + /* clear xmac errors */ + if (hwstatus & (IS_NO_STAT_M1|IS_NO_TIST_M1)) + skge_write16(hw, SKGEMAC_REG(0, RX_MFF_CTRL1), MFF_CLR_INSTAT); + if (hwstatus & (IS_NO_STAT_M2|IS_NO_TIST_M2)) + skge_write16(hw, SKGEMAC_REG(0, RX_MFF_CTRL2), MFF_CLR_INSTAT); + } else { + /* Timestamp (unused) overflow */ + if (hwstatus & IS_IRQ_TIST_OV) + skge_write8(hw, GMAC_TI_ST_CTRL, GMT_ST_CLR_IRQ); + + if (hwstatus & IS_IRQ_SENSOR) { + /* no sensors on 32-bit Yukon */ + if (!(skge_read16(hw, B0_CTST) & CS_BUS_SLOT_SZ)) { + printk(KERN_ERR PFX "ignoring bogus sensor interrups\n"); + skge_write32(hw, B0_HWE_IMSK, + IS_ERR_MSK & ~IS_IRQ_SENSOR); + } else + printk(KERN_WARNING PFX "sensor interrupt\n"); + } + + + } + + if (hwstatus & IS_RAM_RD_PAR) { + printk(KERN_ERR PFX "Ram read data parity error\n"); + skge_write16(hw, B3_RI_CTRL, RI_CLR_RD_PERR); + } + + if (hwstatus & IS_RAM_WR_PAR) { + printk(KERN_ERR PFX "Ram write data parity error\n"); + skge_write16(hw, B3_RI_CTRL, RI_CLR_WR_PERR); + } + + if (hwstatus & IS_M1_PAR_ERR) + skge_mac_parity(hw, 0); + + if (hwstatus & IS_M2_PAR_ERR) + skge_mac_parity(hw, 1); + + if (hwstatus & IS_R1_PAR_ERR) + skge_write32(hw, B0_R1_CSR, CSR_IRQ_CL_P); + + if (hwstatus & IS_R2_PAR_ERR) + skge_write32(hw, B0_R2_CSR, CSR_IRQ_CL_P); + + if (hwstatus & (IS_IRQ_MST_ERR|IS_IRQ_STAT)) { + printk(KERN_ERR PFX "hardware error detected (status 0x%x)\n", + hwstatus); + + skge_pci_clear(hw); + + hwstatus = skge_read32(hw, B0_HWE_ISRC); + if (hwstatus & IS_IRQ_STAT) { + printk(KERN_WARNING PFX "IRQ status %x: still set ignoring hardware errors\n", + hwstatus); + hw->intr_mask &= ~IS_HW_ERR; + } + } +} + +/* + * Interrrupt from PHY are handled in tasklet (soft irq) + * because accessing phy registers requires spin wait which might + * cause excess interrupt latency. + */ +static void skge_extirq(unsigned long data) +{ + struct skge_hw *hw = (struct skge_hw *) data; + int port; + + spin_lock(&hw->phy_lock); + for (port = 0; port < 2; port++) { + struct net_device *dev = hw->dev[port]; + + if (dev && netif_running(dev)) { + struct skge_port *skge = netdev_priv(dev); + + if (hw->chip_id != CHIP_ID_GENESIS) + yukon_phy_intr(skge); + else if (hw->phy_type == SK_PHY_BCOM) + genesis_bcom_intr(skge); + } + } + spin_unlock(&hw->phy_lock); + + local_irq_disable(); + hw->intr_mask |= IS_EXT_REG; + skge_write32(hw, B0_IMSK, hw->intr_mask); + local_irq_enable(); +} + +static irqreturn_t skge_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + struct skge_hw *hw = dev_id; + u32 status = skge_read32(hw, B0_SP_ISRC); + + if (status == 0 || status == ~0) /* hotplug or shared irq */ + return IRQ_NONE; + + status &= hw->intr_mask; + + if ((status & IS_R1_F) && netif_rx_schedule_prep(hw->dev[0])) { + status &= ~IS_R1_F; + hw->intr_mask &= ~IS_R1_F; + skge_write32(hw, B0_IMSK, hw->intr_mask); + __netif_rx_schedule(hw->dev[0]); + } + + if ((status & IS_R2_F) && netif_rx_schedule_prep(hw->dev[1])) { + status &= ~IS_R2_F; + hw->intr_mask &= ~IS_R2_F; + skge_write32(hw, B0_IMSK, hw->intr_mask); + __netif_rx_schedule(hw->dev[1]); + } + + if (status & IS_XA1_F) + skge_tx_intr(hw->dev[0]); + + if (status & IS_XA2_F) + skge_tx_intr(hw->dev[1]); + + if (status & IS_MAC1) + skge_mac_intr(hw, 0); + + if (status & IS_MAC2) + skge_mac_intr(hw, 1); + + if (status & IS_HW_ERR) + skge_error_irq(hw); + + if (status & IS_EXT_REG) { + hw->intr_mask &= ~IS_EXT_REG; + tasklet_schedule(&hw->ext_tasklet); + } + + if (status) + skge_write32(hw, B0_IMSK, hw->intr_mask); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void skge_netpoll(struct net_device *dev) +{ + struct skge_port *skge = netdev_priv(dev); + + disable_irq(dev->irq); + skge_intr(dev->irq, skge->hw, NULL); + enable_irq(dev->irq); +} +#endif + +static int skge_set_mac_address(struct net_device *dev, void *p) +{ + struct skge_port *skge = netdev_priv(dev); + struct sockaddr *addr = p; + int err = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + skge_down(dev); + memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); + memcpy_toio(skge->hw->regs + B2_MAC_1 + skge->port*8, + dev->dev_addr, ETH_ALEN); + memcpy_toio(skge->hw->regs + B2_MAC_2 + skge->port*8, + dev->dev_addr, ETH_ALEN); + if (dev->flags & IFF_UP) + err = skge_up(dev); + return err; +} + +static const struct { + u8 id; + const char *name; +} skge_chips[] = { + { CHIP_ID_GENESIS, "Genesis" }, + { CHIP_ID_YUKON, "Yukon" }, + { CHIP_ID_YUKON_LITE, "Yukon-Lite"}, + { CHIP_ID_YUKON_LP, "Yukon-LP"}, + { CHIP_ID_YUKON_XL, "Yukon-2 XL"}, + { CHIP_ID_YUKON_EC, "YUKON-2 EC"}, + { CHIP_ID_YUKON_FE, "YUKON-2 FE"}, +}; + +static const char *skge_board_name(const struct skge_hw *hw) +{ + int i; + static char buf[16]; + + for (i = 0; i < ARRAY_SIZE(skge_chips); i++) + if (skge_chips[i].id == hw->chip_id) + return skge_chips[i].name; + + snprintf(buf, sizeof buf, "chipid 0x%x", hw->chip_id); + return buf; +} + + +/* + * Setup the board data structure, but don't bring up + * the port(s) + */ +static int skge_reset(struct skge_hw *hw) +{ + u16 ctst; + u8 t8; + int i, ports; + + ctst = skge_read16(hw, B0_CTST); + + /* do a SW reset */ + skge_write8(hw, B0_CTST, CS_RST_SET); + skge_write8(hw, B0_CTST, CS_RST_CLR); + + /* clear PCI errors, if any */ + skge_pci_clear(hw); + + skge_write8(hw, B0_CTST, CS_MRST_CLR); + + /* restore CLK_RUN bits (for Yukon-Lite) */ + skge_write16(hw, B0_CTST, + ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA)); + + hw->chip_id = skge_read8(hw, B2_CHIP_ID); + hw->phy_type = skge_read8(hw, B2_E_1) & 0xf; + hw->pmd_type = skge_read8(hw, B2_PMD_TYP); + + switch(hw->chip_id) { + case CHIP_ID_GENESIS: + switch (hw->phy_type) { + case SK_PHY_XMAC: + hw->phy_addr = PHY_ADDR_XMAC; + break; + case SK_PHY_BCOM: + hw->phy_addr = PHY_ADDR_BCOM; + break; + default: + printk(KERN_ERR PFX "%s: unsupported phy type 0x%x\n", + pci_name(hw->pdev), hw->phy_type); + return -EOPNOTSUPP; + } + break; + + case CHIP_ID_YUKON: + case CHIP_ID_YUKON_LITE: + case CHIP_ID_YUKON_LP: + if (hw->phy_type < SK_PHY_MARV_COPPER && hw->pmd_type != 'S') + hw->phy_type = SK_PHY_MARV_COPPER; + + hw->phy_addr = PHY_ADDR_MARV; + if (!iscopper(hw)) + hw->phy_type = SK_PHY_MARV_FIBER; + + break; + + default: + printk(KERN_ERR PFX "%s: unsupported chip type 0x%x\n", + pci_name(hw->pdev), hw->chip_id); + return -EOPNOTSUPP; + } + + hw->mac_cfg = skge_read8(hw, B2_MAC_CFG); + ports = isdualport(hw) ? 2 : 1; + + /* read the adapters RAM size */ + t8 = skge_read8(hw, B2_E_0); + if (hw->chip_id == CHIP_ID_GENESIS) { + if (t8 == 3) { + /* special case: 4 x 64k x 36, offset = 0x80000 */ + hw->ram_size = 0x100000; + hw->ram_offset = 0x80000; + } else + hw->ram_size = t8 * 512; + } + else if (t8 == 0) + hw->ram_size = 0x20000; + else + hw->ram_size = t8 * 4096; + + if (hw->chip_id == CHIP_ID_GENESIS) + genesis_init(hw); + else { + /* switch power to VCC (WA for VAUX problem) */ + skge_write8(hw, B0_POWER_CTRL, + PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_OFF | PC_VCC_ON); + for (i = 0; i < ports; i++) { + skge_write16(hw, SKGEMAC_REG(i, GMAC_LINK_CTRL), GMLC_RST_SET); + skge_write16(hw, SKGEMAC_REG(i, GMAC_LINK_CTRL), GMLC_RST_CLR); + } + } + + /* turn off hardware timer (unused) */ + skge_write8(hw, B2_TI_CTRL, TIM_STOP); + skge_write8(hw, B2_TI_CTRL, TIM_CLR_IRQ); + skge_write8(hw, B0_LED, LED_STAT_ON); + + /* enable the Tx Arbiters */ + for (i = 0; i < ports; i++) + skge_write8(hw, SKGEMAC_REG(i, TXA_CTRL), TXA_ENA_ARB); + + /* Initialize ram interface */ + skge_write16(hw, B3_RI_CTRL, RI_RST_CLR); + + skge_write8(hw, B3_RI_WTO_R1, SK_RI_TO_53); + skge_write8(hw, B3_RI_WTO_XA1, SK_RI_TO_53); + skge_write8(hw, B3_RI_WTO_XS1, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_R1, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_XA1, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_XS1, SK_RI_TO_53); + skge_write8(hw, B3_RI_WTO_R2, SK_RI_TO_53); + skge_write8(hw, B3_RI_WTO_XA2, SK_RI_TO_53); + skge_write8(hw, B3_RI_WTO_XS2, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_R2, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_XA2, SK_RI_TO_53); + skge_write8(hw, B3_RI_RTO_XS2, SK_RI_TO_53); + + skge_write32(hw, B0_HWE_IMSK, IS_ERR_MSK); + + /* Set interrupt moderation for Transmit only + * Receive interrupts avoided by NAPI + */ + skge_write32(hw, B2_IRQM_MSK, IS_XA1_F|IS_XA2_F); + skge_write32(hw, B2_IRQM_INI, skge_usecs2clk(hw, 100)); + skge_write32(hw, B2_IRQM_CTRL, TIM_START); + + hw->intr_mask = IS_HW_ERR | IS_EXT_REG | IS_PORT_1; + if (isdualport(hw)) + hw->intr_mask |= IS_PORT_2; + skge_write32(hw, B0_IMSK, hw->intr_mask); + + if (hw->chip_id != CHIP_ID_GENESIS) + skge_write8(hw, GMAC_IRQ_MSK, 0); + + spin_lock_bh(&hw->phy_lock); + for (i = 0; i < ports; i++) { + if (hw->chip_id == CHIP_ID_GENESIS) + genesis_reset(hw, i); + else + yukon_reset(hw, i); + } + spin_unlock_bh(&hw->phy_lock); + + return 0; +} + +/* Initialize network device */ +static struct net_device *skge_devinit(struct skge_hw *hw, int port) +{ + struct skge_port *skge; + struct net_device *dev = alloc_etherdev(sizeof(*skge)); + + if (!dev) { + printk(KERN_ERR "skge etherdev alloc failed"); + return NULL; + } + + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &hw->pdev->dev); + dev->open = skge_up; + dev->stop = skge_down; + dev->hard_start_xmit = skge_xmit_frame; + dev->get_stats = skge_get_stats; + if (hw->chip_id == CHIP_ID_GENESIS) + dev->set_multicast_list = genesis_set_multicast; + else + dev->set_multicast_list = yukon_set_multicast; + + dev->set_mac_address = skge_set_mac_address; + dev->change_mtu = skge_change_mtu; + SET_ETHTOOL_OPS(dev, &skge_ethtool_ops); + dev->tx_timeout = skge_tx_timeout; + dev->watchdog_timeo = TX_WATCHDOG; + dev->poll = skge_poll; + dev->weight = NAPI_WEIGHT; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = skge_netpoll; +#endif + dev->irq = hw->pdev->irq; + dev->features = NETIF_F_LLTX; + + skge = netdev_priv(dev); + skge->netdev = dev; + skge->hw = hw; + skge->msg_enable = netif_msg_init(debug, default_msg); + skge->tx_ring.count = DEFAULT_TX_RING_SIZE; + skge->rx_ring.count = DEFAULT_RX_RING_SIZE; + + /* Auto speed and flow control */ + skge->autoneg = AUTONEG_ENABLE; + skge->flow_control = FLOW_MODE_SYMMETRIC; + skge->duplex = -1; + skge->speed = -1; + skge->advertising = skge_modes(hw); + + hw->dev[port] = dev; + + skge->port = port; + + spin_lock_init(&skge->tx_lock); + + init_timer(&skge->link_check); + skge->link_check.function = skge_link_timer; + skge->link_check.data = (unsigned long) skge; + + init_timer(&skge->led_blink); + skge->led_blink.function = skge_blink_timer; + skge->led_blink.data = (unsigned long) skge; + + if (hw->chip_id != CHIP_ID_GENESIS) { + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; + skge->rx_csum = 1; + } + + /* read the mac address */ + memcpy_fromio(dev->dev_addr, hw->regs + B2_MAC_1 + port*8, ETH_ALEN); + + /* device is off until link detection */ + netif_carrier_off(dev); + netif_stop_queue(dev); + + return dev; +} + +static void __devinit skge_show_addr(struct net_device *dev) +{ + const struct skge_port *skge = netdev_priv(dev); + + if (netif_msg_probe(skge)) + printk(KERN_INFO PFX "%s: addr %02x:%02x:%02x:%02x:%02x:%02x\n", + dev->name, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); +} + +static int __devinit skge_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct net_device *dev, *dev1; + struct skge_hw *hw; + int err, using_dac = 0; + + if ((err = pci_enable_device(pdev))) { + printk(KERN_ERR PFX "%s cannot enable PCI device\n", + pci_name(pdev)); + goto err_out; + } + + if ((err = pci_request_regions(pdev, DRV_NAME))) { + printk(KERN_ERR PFX "%s cannot obtain PCI resources\n", + pci_name(pdev)); + goto err_out_disable_pdev; + } + + pci_set_master(pdev); + + if (!(err = pci_set_dma_mask(pdev, DMA_64BIT_MASK))) + using_dac = 1; + else if (!(err = pci_set_dma_mask(pdev, DMA_32BIT_MASK))) { + printk(KERN_ERR PFX "%s no usable DMA configuration\n", + pci_name(pdev)); + goto err_out_free_regions; + } + +#ifdef __BIG_ENDIAN + /* byte swap decriptors in hardware */ + { + u32 reg; + + pci_read_config_dword(pdev, PCI_DEV_REG2, ®); + reg |= PCI_REV_DESC; + pci_write_config_dword(pdev, PCI_DEV_REG2, reg); + } +#endif + + err = -ENOMEM; + hw = kmalloc(sizeof(*hw), GFP_KERNEL); + if (!hw) { + printk(KERN_ERR PFX "%s: cannot allocate hardware struct\n", + pci_name(pdev)); + goto err_out_free_regions; + } + + memset(hw, 0, sizeof(*hw)); + hw->pdev = pdev; + spin_lock_init(&hw->phy_lock); + tasklet_init(&hw->ext_tasklet, skge_extirq, (unsigned long) hw); + + hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); + if (!hw->regs) { + printk(KERN_ERR PFX "%s: cannot map device registers\n", + pci_name(pdev)); + goto err_out_free_hw; + } + + if ((err = request_irq(pdev->irq, skge_intr, SA_SHIRQ, DRV_NAME, hw))) { + printk(KERN_ERR PFX "%s: cannot assign irq %d\n", + pci_name(pdev), pdev->irq); + goto err_out_iounmap; + } + pci_set_drvdata(pdev, hw); + + err = skge_reset(hw); + if (err) + goto err_out_free_irq; + + printk(KERN_INFO PFX "addr 0x%lx irq %d chip %s rev %d\n", + pci_resource_start(pdev, 0), pdev->irq, + skge_board_name(hw), chip_rev(hw)); + + if ((dev = skge_devinit(hw, 0)) == NULL) + goto err_out_led_off; + + if (using_dac) + dev->features |= NETIF_F_HIGHDMA; + + if ((err = register_netdev(dev))) { + printk(KERN_ERR PFX "%s: cannot register net device\n", + pci_name(pdev)); + goto err_out_free_netdev; + } + + skge_show_addr(dev); + + if (isdualport(hw) && (dev1 = skge_devinit(hw, 1))) { + if (using_dac) + dev1->features |= NETIF_F_HIGHDMA; + + if (register_netdev(dev1) == 0) + skge_show_addr(dev1); + else { + /* Failure to register second port need not be fatal */ + printk(KERN_WARNING PFX "register of second port failed\n"); + hw->dev[1] = NULL; + free_netdev(dev1); + } + } + + return 0; + +err_out_free_netdev: + free_netdev(dev); +err_out_led_off: + skge_write16(hw, B0_LED, LED_STAT_OFF); +err_out_free_irq: + free_irq(pdev->irq, hw); +err_out_iounmap: + iounmap(hw->regs); +err_out_free_hw: + kfree(hw); +err_out_free_regions: + pci_release_regions(pdev); +err_out_disable_pdev: + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); +err_out: + return err; +} + +static void __devexit skge_remove(struct pci_dev *pdev) +{ + struct skge_hw *hw = pci_get_drvdata(pdev); + struct net_device *dev0, *dev1; + + if(!hw) + return; + + if ((dev1 = hw->dev[1])) + unregister_netdev(dev1); + dev0 = hw->dev[0]; + unregister_netdev(dev0); + + tasklet_kill(&hw->ext_tasklet); + + free_irq(pdev->irq, hw); + pci_release_regions(pdev); + pci_disable_device(pdev); + if (dev1) + free_netdev(dev1); + free_netdev(dev0); + skge_write16(hw, B0_LED, LED_STAT_OFF); + iounmap(hw->regs); + kfree(hw); + pci_set_drvdata(pdev, NULL); +} + +#ifdef CONFIG_PM +static int skge_suspend(struct pci_dev *pdev, u32 state) +{ + struct skge_hw *hw = pci_get_drvdata(pdev); + int i, wol = 0; + + for(i = 0; i < 2; i++) { + struct net_device *dev = hw->dev[i]; + + if (dev) { + struct skge_port *skge = netdev_priv(dev); + if (netif_running(dev)) { + netif_carrier_off(dev); + skge_down(dev); + } + netif_device_detach(dev); + wol |= skge->wol; + } + } + + pci_save_state(pdev); + pci_enable_wake(pdev, state, wol); + pci_disable_device(pdev); + pci_set_power_state(pdev, pci_choose_state(pdev, state)); + + return 0; +} + +static int skge_resume(struct pci_dev *pdev) +{ + struct skge_hw *hw = pci_get_drvdata(pdev); + int i; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + + skge_reset(hw); + + for(i = 0; i < 2; i++) { + struct net_device *dev = hw->dev[i]; + if (dev) { + netif_device_attach(dev); + if(netif_running(dev)) + skge_up(dev); + } + } + return 0; +} +#endif + +static struct pci_driver skge_driver = { + .name = DRV_NAME, + .id_table = skge_id_table, + .probe = skge_probe, + .remove = __devexit_p(skge_remove), +#ifdef CONFIG_PM + .suspend = skge_suspend, + .resume = skge_resume, +#endif +}; + +static int __init skge_init_module(void) +{ + return pci_module_init(&skge_driver); +} + +static void __exit skge_cleanup_module(void) +{ + pci_unregister_driver(&skge_driver); +} + +module_init(skge_init_module); +module_exit(skge_cleanup_module); diff -Nru a/drivers/net/skge.h b/drivers/net/skge.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/skge.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,3005 @@ +/* + * Definitions for the new Marvell Yukon / SysKonenct driver. + */ +#ifndef _SKGE_H +#define _SKGE_H + +/* PCI config registers */ +#define PCI_DEV_REG1 0x40 +#define PCI_DEV_REG2 0x44 +#ifndef PCI_VPD +#define PCI_VPD 0x50 +#endif + +/* PCI_OUR_REG_2 32 bit Our Register 2 */ +enum { + PCI_VPD_WR_THR = 0xff<<24, /* Bit 31..24: VPD Write Threshold */ + PCI_DEV_SEL = 0x7f<<17, /* Bit 23..17: EEPROM Device Select */ + PCI_VPD_ROM_SZ = 7 <<14, /* Bit 16..14: VPD ROM Size */ + /* Bit 13..12: reserved */ + PCI_EN_DUMMY_RD = 1<<3, /* Enable Dummy Read */ + PCI_REV_DESC = 1<<2, /* Reverse Desc. Bytes */ + PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */ +}; + +/* PCI_VPD_ADR_REG 16 bit VPD Address Register */ +enum { + PCI_VPD_FLAG = 1<<15, /* starts VPD rd/wr cycle */ + PCI_VPD_ADR_MSK =0x7fffL, /* Bit 14.. 0: VPD Address Mask */ + VPD_RES_ID = 0x82, + VPD_RES_READ = 0x90, + VPD_RES_WRITE = 0x81, + VPD_RES_END = 0x78, +}; + + +#define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \ + PCI_STATUS_SIG_SYSTEM_ERROR | \ + PCI_STATUS_REC_MASTER_ABORT | \ + PCI_STATUS_REC_TARGET_ABORT | \ + PCI_STATUS_PARITY) + + +enum csr_regs { + B0_RAP = 0x0000, + B0_CTST = 0x0004, + B0_LED = 0x0006, + B0_POWER_CTRL = 0x0007, + B0_ISRC = 0x0008, + B0_IMSK = 0x000c, + B0_HWE_ISRC = 0x0010, + B0_HWE_IMSK = 0x0014, + B0_SP_ISRC = 0x0018, + B0_XM1_IMSK = 0x0020, + B0_XM1_ISRC = 0x0028, + B0_XM1_PHY_ADDR = 0x0030, + B0_XM1_PHY_DATA = 0x0034, + B0_XM2_IMSK = 0x0040, + B0_XM2_ISRC = 0x0048, + B0_XM2_PHY_ADDR = 0x0050, + B0_XM2_PHY_DATA = 0x0054, + B0_R1_CSR = 0x0060, + B0_R2_CSR = 0x0064, + B0_XS1_CSR = 0x0068, + B0_XA1_CSR = 0x006c, + B0_XS2_CSR = 0x0070, + B0_XA2_CSR = 0x0074, + + B2_MAC_1 = 0x0100, + B2_MAC_2 = 0x0108, + B2_MAC_3 = 0x0110, + B2_CONN_TYP = 0x0118, + B2_PMD_TYP = 0x0119, + B2_MAC_CFG = 0x011a, + B2_CHIP_ID = 0x011b, + B2_E_0 = 0x011c, + B2_E_1 = 0x011d, + B2_E_2 = 0x011e, + B2_E_3 = 0x011f, + B2_FAR = 0x0120, + B2_FDP = 0x0124, + B2_LD_CTRL = 0x0128, + B2_LD_TEST = 0x0129, + B2_TI_INI = 0x0130, + B2_TI_VAL = 0x0134, + B2_TI_CTRL = 0x0138, + B2_TI_TEST = 0x0139, + B2_IRQM_INI = 0x0140, + B2_IRQM_VAL = 0x0144, + B2_IRQM_CTRL = 0x0148, + B2_IRQM_TEST = 0x0149, + B2_IRQM_MSK = 0x014c, + B2_IRQM_HWE_MSK = 0x0150, + B2_TST_CTRL1 = 0x0158, + B2_TST_CTRL2 = 0x0159, + B2_GP_IO = 0x015c, + B2_I2C_CTRL = 0x0160, + B2_I2C_DATA = 0x0164, + B2_I2C_IRQ = 0x0168, + B2_I2C_SW = 0x016c, + B2_BSC_INI = 0x0170, + B2_BSC_VAL = 0x0174, + B2_BSC_CTRL = 0x0178, + B2_BSC_STAT = 0x0179, + B2_BSC_TST = 0x017a, + + B3_RAM_ADDR = 0x0180, + B3_RAM_DATA_LO = 0x0184, + B3_RAM_DATA_HI = 0x0188, + B3_RI_WTO_R1 = 0x0190, + B3_RI_WTO_XA1 = 0x0191, + B3_RI_WTO_XS1 = 0x0192, + B3_RI_RTO_R1 = 0x0193, + B3_RI_RTO_XA1 = 0x0194, + B3_RI_RTO_XS1 = 0x0195, + B3_RI_WTO_R2 = 0x0196, + B3_RI_WTO_XA2 = 0x0197, + B3_RI_WTO_XS2 = 0x0198, + B3_RI_RTO_R2 = 0x0199, + B3_RI_RTO_XA2 = 0x019a, + B3_RI_RTO_XS2 = 0x019b, + B3_RI_TO_VAL = 0x019c, + B3_RI_CTRL = 0x01a0, + B3_RI_TEST = 0x01a2, + B3_MA_TOINI_RX1 = 0x01b0, + B3_MA_TOINI_RX2 = 0x01b1, + B3_MA_TOINI_TX1 = 0x01b2, + B3_MA_TOINI_TX2 = 0x01b3, + B3_MA_TOVAL_RX1 = 0x01b4, + B3_MA_TOVAL_RX2 = 0x01b5, + B3_MA_TOVAL_TX1 = 0x01b6, + B3_MA_TOVAL_TX2 = 0x01b7, + B3_MA_TO_CTRL = 0x01b8, + B3_MA_TO_TEST = 0x01ba, + B3_MA_RCINI_RX1 = 0x01c0, + B3_MA_RCINI_RX2 = 0x01c1, + B3_MA_RCINI_TX1 = 0x01c2, + B3_MA_RCINI_TX2 = 0x01c3, + B3_MA_RCVAL_RX1 = 0x01c4, + B3_MA_RCVAL_RX2 = 0x01c5, + B3_MA_RCVAL_TX1 = 0x01c6, + B3_MA_RCVAL_TX2 = 0x01c7, + B3_MA_RC_CTRL = 0x01c8, + B3_MA_RC_TEST = 0x01ca, + B3_PA_TOINI_RX1 = 0x01d0, + B3_PA_TOINI_RX2 = 0x01d4, + B3_PA_TOINI_TX1 = 0x01d8, + B3_PA_TOINI_TX2 = 0x01dc, + B3_PA_TOVAL_RX1 = 0x01e0, + B3_PA_TOVAL_RX2 = 0x01e4, + B3_PA_TOVAL_TX1 = 0x01e8, + B3_PA_TOVAL_TX2 = 0x01ec, + B3_PA_CTRL = 0x01f0, + B3_PA_TEST = 0x01f2, +}; + +/* B0_CTST 16 bit Control/Status register */ +enum { + CS_CLK_RUN_HOT = 1<<13,/* CLK_RUN hot m. (YUKON-Lite only) */ + CS_CLK_RUN_RST = 1<<12,/* CLK_RUN reset (YUKON-Lite only) */ + CS_CLK_RUN_ENA = 1<<11,/* CLK_RUN enable (YUKON-Lite only) */ + CS_VAUX_AVAIL = 1<<10,/* VAUX available (YUKON only) */ + CS_BUS_CLOCK = 1<<9, /* Bus Clock 0/1 = 33/66 MHz */ + CS_BUS_SLOT_SZ = 1<<8, /* Slot Size 0/1 = 32/64 bit slot */ + CS_ST_SW_IRQ = 1<<7, /* Set IRQ SW Request */ + CS_CL_SW_IRQ = 1<<6, /* Clear IRQ SW Request */ + CS_STOP_DONE = 1<<5, /* Stop Master is finished */ + CS_STOP_MAST = 1<<4, /* Command Bit to stop the master */ + CS_MRST_CLR = 1<<3, /* Clear Master reset */ + CS_MRST_SET = 1<<2, /* Set Master reset */ + CS_RST_CLR = 1<<1, /* Clear Software reset */ + CS_RST_SET = 1, /* Set Software reset */ + +/* B0_LED 8 Bit LED register */ +/* Bit 7.. 2: reserved */ + LED_STAT_ON = 1<<1, /* Status LED on */ + LED_STAT_OFF = 1, /* Status LED off */ + +/* B0_POWER_CTRL 8 Bit Power Control reg (YUKON only) */ + PC_VAUX_ENA = 1<<7, /* Switch VAUX Enable */ + PC_VAUX_DIS = 1<<6, /* Switch VAUX Disable */ + PC_VCC_ENA = 1<<5, /* Switch VCC Enable */ + PC_VCC_DIS = 1<<4, /* Switch VCC Disable */ + PC_VAUX_ON = 1<<3, /* Switch VAUX On */ + PC_VAUX_OFF = 1<<2, /* Switch VAUX Off */ + PC_VCC_ON = 1<<1, /* Switch VCC On */ + PC_VCC_OFF = 1<<0, /* Switch VCC Off */ +}; + +/* B2_IRQM_MSK 32 bit IRQ Moderation Mask */ +enum { + IS_ALL_MSK = 0xbffffffful, /* All Interrupt bits */ + IS_HW_ERR = 1<<31, /* Interrupt HW Error */ + /* Bit 30: reserved */ + IS_PA_TO_RX1 = 1<<29, /* Packet Arb Timeout Rx1 */ + IS_PA_TO_RX2 = 1<<28, /* Packet Arb Timeout Rx2 */ + IS_PA_TO_TX1 = 1<<27, /* Packet Arb Timeout Tx1 */ + IS_PA_TO_TX2 = 1<<26, /* Packet Arb Timeout Tx2 */ + IS_I2C_READY = 1<<25, /* IRQ on end of I2C Tx */ + IS_IRQ_SW = 1<<24, /* SW forced IRQ */ + IS_EXT_REG = 1<<23, /* IRQ from LM80 or PHY (GENESIS only) */ + /* IRQ from PHY (YUKON only) */ + IS_TIMINT = 1<<22, /* IRQ from Timer */ + IS_MAC1 = 1<<21, /* IRQ from MAC 1 */ + IS_LNK_SYNC_M1 = 1<<20, /* Link Sync Cnt wrap MAC 1 */ + IS_MAC2 = 1<<19, /* IRQ from MAC 2 */ + IS_LNK_SYNC_M2 = 1<<18, /* Link Sync Cnt wrap MAC 2 */ +/* Receive Queue 1 */ + IS_R1_B = 1<<17, /* Q_R1 End of Buffer */ + IS_R1_F = 1<<16, /* Q_R1 End of Frame */ + IS_R1_C = 1<<15, /* Q_R1 Encoding Error */ +/* Receive Queue 2 */ + IS_R2_B = 1<<14, /* Q_R2 End of Buffer */ + IS_R2_F = 1<<13, /* Q_R2 End of Frame */ + IS_R2_C = 1<<12, /* Q_R2 Encoding Error */ +/* Synchronous Transmit Queue 1 */ + IS_XS1_B = 1<<11, /* Q_XS1 End of Buffer */ + IS_XS1_F = 1<<10, /* Q_XS1 End of Frame */ + IS_XS1_C = 1<<9, /* Q_XS1 Encoding Error */ +/* Asynchronous Transmit Queue 1 */ + IS_XA1_B = 1<<8, /* Q_XA1 End of Buffer */ + IS_XA1_F = 1<<7, /* Q_XA1 End of Frame */ + IS_XA1_C = 1<<6, /* Q_XA1 Encoding Error */ +/* Synchronous Transmit Queue 2 */ + IS_XS2_B = 1<<5, /* Q_XS2 End of Buffer */ + IS_XS2_F = 1<<4, /* Q_XS2 End of Frame */ + IS_XS2_C = 1<<3, /* Q_XS2 Encoding Error */ +/* Asynchronous Transmit Queue 2 */ + IS_XA2_B = 1<<2, /* Q_XA2 End of Buffer */ + IS_XA2_F = 1<<1, /* Q_XA2 End of Frame */ + IS_XA2_C = 1<<0, /* Q_XA2 Encoding Error */ + + IS_PORT_1 = IS_XA1_F| IS_R1_F| IS_MAC1, + IS_PORT_2 = IS_XA2_F| IS_R2_F| IS_MAC2, +}; + + +/* B2_IRQM_HWE_MSK 32 bit IRQ Moderation HW Error Mask */ +enum { + IS_ERR_MSK = 0x00003fff,/* All Error bits */ + + IS_IRQ_TIST_OV = 1<<13, /* Time Stamp Timer Overflow (YUKON only) */ + IS_IRQ_SENSOR = 1<<12, /* IRQ from Sensor (YUKON only) */ + IS_IRQ_MST_ERR = 1<<11, /* IRQ master error detected */ + IS_IRQ_STAT = 1<<10, /* IRQ status exception */ + IS_NO_STAT_M1 = 1<<9, /* No Rx Status from MAC 1 */ + IS_NO_STAT_M2 = 1<<8, /* No Rx Status from MAC 2 */ + IS_NO_TIST_M1 = 1<<7, /* No Time Stamp from MAC 1 */ + IS_NO_TIST_M2 = 1<<6, /* No Time Stamp from MAC 2 */ + IS_RAM_RD_PAR = 1<<5, /* RAM Read Parity Error */ + IS_RAM_WR_PAR = 1<<4, /* RAM Write Parity Error */ + IS_M1_PAR_ERR = 1<<3, /* MAC 1 Parity Error */ + IS_M2_PAR_ERR = 1<<2, /* MAC 2 Parity Error */ + IS_R1_PAR_ERR = 1<<1, /* Queue R1 Parity Error */ + IS_R2_PAR_ERR = 1<<0, /* Queue R2 Parity Error */ +}; + +/* B2_TST_CTRL1 8 bit Test Control Register 1 */ +enum { + TST_FRC_DPERR_MR = 1<<7, /* force DATAPERR on MST RD */ + TST_FRC_DPERR_MW = 1<<6, /* force DATAPERR on MST WR */ + TST_FRC_DPERR_TR = 1<<5, /* force DATAPERR on TRG RD */ + TST_FRC_DPERR_TW = 1<<4, /* force DATAPERR on TRG WR */ + TST_FRC_APERR_M = 1<<3, /* force ADDRPERR on MST */ + TST_FRC_APERR_T = 1<<2, /* force ADDRPERR on TRG */ + TST_CFG_WRITE_ON = 1<<1, /* Enable Config Reg WR */ + TST_CFG_WRITE_OFF= 1<<0, /* Disable Config Reg WR */ +}; + +/* B2_MAC_CFG 8 bit MAC Configuration / Chip Revision */ +enum { + CFG_CHIP_R_MSK = 0xf<<4, /* Bit 7.. 4: Chip Revision */ + /* Bit 3.. 2: reserved */ + CFG_DIS_M2_CLK = 1<<1, /* Disable Clock for 2nd MAC */ + CFG_SNG_MAC = 1<<0, /* MAC Config: 0=2 MACs / 1=1 MAC*/ +}; + +/* B2_CHIP_ID 8 bit Chip Identification Number */ +enum { + CHIP_ID_GENESIS = 0x0a, /* Chip ID for GENESIS */ + CHIP_ID_YUKON = 0xb0, /* Chip ID for YUKON */ + CHIP_ID_YUKON_LITE = 0xb1, /* Chip ID for YUKON-Lite (Rev. A1-A3) */ + CHIP_ID_YUKON_LP = 0xb2, /* Chip ID for YUKON-LP */ + CHIP_ID_YUKON_XL = 0xb3, /* Chip ID for YUKON-2 XL */ + CHIP_ID_YUKON_EC = 0xb6, /* Chip ID for YUKON-2 EC */ + CHIP_ID_YUKON_FE = 0xb7, /* Chip ID for YUKON-2 FE */ + + CHIP_REV_YU_LITE_A1 = 3, /* Chip Rev. for YUKON-Lite A1,A2 */ + CHIP_REV_YU_LITE_A3 = 7, /* Chip Rev. for YUKON-Lite A3 */ +}; + +/* B2_LD_TEST 8 bit EPROM loader test register */ +enum { + LD_T_ON = 1<<3, /* Loader Test mode on */ + LD_T_OFF = 1<<2, /* Loader Test mode off */ + LD_T_STEP = 1<<1, /* Decrement FPROM addr. Counter */ + LD_START = 1<<0, /* Start loading FPROM */ +}; + +/* B2_TI_CTRL 8 bit Timer control */ +/* B2_IRQM_CTRL 8 bit IRQ Moderation Timer Control */ +enum { + TIM_START = 1<<2, /* Start Timer */ + TIM_STOP = 1<<1, /* Stop Timer */ + TIM_CLR_IRQ = 1<<0, /* Clear Timer IRQ (!IRQM) */ +}; + +/* B2_TI_TEST 8 Bit Timer Test */ +/* B2_IRQM_TEST 8 bit IRQ Moderation Timer Test */ +/* B28_DPT_TST 8 bit Descriptor Poll Timer Test Reg */ +enum { + TIM_T_ON = 1<<2, /* Test mode on */ + TIM_T_OFF = 1<<1, /* Test mode off */ + TIM_T_STEP = 1<<0, /* Test step */ +}; + +/* B28_DPT_INI 32 bit Descriptor Poll Timer Init Val */ +/* B28_DPT_VAL 32 bit Descriptor Poll Timer Curr Val */ +/* B28_DPT_CTRL 8 bit Descriptor Poll Timer Ctrl Reg */ +enum { + DPT_MSK = 0x00ffffffL, /* Bit 23.. 0: Desc Poll Timer Bits */ + + DPT_START = 1<<1, /* Start Descriptor Poll Timer */ + DPT_STOP = 1<<0, /* Stop Descriptor Poll Timer */ +}; + +/* B2_GP_IO 32 bit General Purpose I/O Register */ +enum { + GP_DIR_9 = 1<<25, /* IO_9 direct, 0=In/1=Out */ + GP_DIR_8 = 1<<24, /* IO_8 direct, 0=In/1=Out */ + GP_DIR_7 = 1<<23, /* IO_7 direct, 0=In/1=Out */ + GP_DIR_6 = 1<<22, /* IO_6 direct, 0=In/1=Out */ + GP_DIR_5 = 1<<21, /* IO_5 direct, 0=In/1=Out */ + GP_DIR_4 = 1<<20, /* IO_4 direct, 0=In/1=Out */ + GP_DIR_3 = 1<<19, /* IO_3 direct, 0=In/1=Out */ + GP_DIR_2 = 1<<18, /* IO_2 direct, 0=In/1=Out */ + GP_DIR_1 = 1<<17, /* IO_1 direct, 0=In/1=Out */ + GP_DIR_0 = 1<<16, /* IO_0 direct, 0=In/1=Out */ + + GP_IO_9 = 1<<9, /* IO_9 pin */ + GP_IO_8 = 1<<8, /* IO_8 pin */ + GP_IO_7 = 1<<7, /* IO_7 pin */ + GP_IO_6 = 1<<6, /* IO_6 pin */ + GP_IO_5 = 1<<5, /* IO_5 pin */ + GP_IO_4 = 1<<4, /* IO_4 pin */ + GP_IO_3 = 1<<3, /* IO_3 pin */ + GP_IO_2 = 1<<2, /* IO_2 pin */ + GP_IO_1 = 1<<1, /* IO_1 pin */ + GP_IO_0 = 1<<0, /* IO_0 pin */ +}; + +/* Rx/Tx Path related Arbiter Test Registers */ +/* B3_MA_TO_TEST 16 bit MAC Arbiter Timeout Test Reg */ +/* B3_MA_RC_TEST 16 bit MAC Arbiter Recovery Test Reg */ +/* B3_PA_TEST 16 bit Packet Arbiter Test Register */ +/* Bit 15, 11, 7, and 3 are reserved in B3_PA_TEST */ +enum { + TX2_T_EV = 1<<15,/* TX2 Timeout/Recv Event occured */ + TX2_T_ON = 1<<14,/* TX2 Timeout/Recv Timer Test On */ + TX2_T_OFF = 1<<13,/* TX2 Timeout/Recv Timer Tst Off */ + TX2_T_STEP = 1<<12,/* TX2 Timeout/Recv Timer Step */ + TX1_T_EV = 1<<11,/* TX1 Timeout/Recv Event occured */ + TX1_T_ON = 1<<10,/* TX1 Timeout/Recv Timer Test On */ + TX1_T_OFF = 1<<9, /* TX1 Timeout/Recv Timer Tst Off */ + TX1_T_STEP = 1<<8, /* TX1 Timeout/Recv Timer Step */ + RX2_T_EV = 1<<7, /* RX2 Timeout/Recv Event occured */ + RX2_T_ON = 1<<6, /* RX2 Timeout/Recv Timer Test On */ + RX2_T_OFF = 1<<5, /* RX2 Timeout/Recv Timer Tst Off */ + RX2_T_STEP = 1<<4, /* RX2 Timeout/Recv Timer Step */ + RX1_T_EV = 1<<3, /* RX1 Timeout/Recv Event occured */ + RX1_T_ON = 1<<2, /* RX1 Timeout/Recv Timer Test On */ + RX1_T_OFF = 1<<1, /* RX1 Timeout/Recv Timer Tst Off */ + RX1_T_STEP = 1<<0, /* RX1 Timeout/Recv Timer Step */ +}; + +/* Descriptor Bit Definition */ +/* TxCtrl Transmit Buffer Control Field */ +/* RxCtrl Receive Buffer Control Field */ +enum { + BMU_OWN = 1<<31, /* OWN bit: 0=host/1=BMU */ + BMU_STF = 1<<30, /* Start of Frame */ + BMU_EOF = 1<<29, /* End of Frame */ + BMU_IRQ_EOB = 1<<28, /* Req "End of Buffer" IRQ */ + BMU_IRQ_EOF = 1<<27, /* Req "End of Frame" IRQ */ + /* TxCtrl specific bits */ + BMU_STFWD = 1<<26, /* (Tx) Store & Forward Frame */ + BMU_NO_FCS = 1<<25, /* (Tx) Disable MAC FCS (CRC) generation */ + BMU_SW = 1<<24, /* (Tx) 1 bit res. for SW use */ + /* RxCtrl specific bits */ + BMU_DEV_0 = 1<<26, /* (Rx) Transfer data to Dev0 */ + BMU_STAT_VAL = 1<<25, /* (Rx) Rx Status Valid */ + BMU_TIST_VAL = 1<<24, /* (Rx) Rx TimeStamp Valid */ + /* Bit 23..16: BMU Check Opcodes */ + BMU_CHECK = 0x55<<16, /* Default BMU check */ + BMU_TCP_CHECK = 0x56<<16, /* Descr with TCP ext */ + BMU_UDP_CHECK = 0x57<<16, /* Descr with UDP ext (YUKON only) */ + BMU_BBC = 0xffffL, /* Bit 15.. 0: Buffer Byte Counter */ +}; + +/* B2_BSC_CTRL 8 bit Blink Source Counter Control */ +enum { + BSC_START = 1<<1, /* Start Blink Source Counter */ + BSC_STOP = 1<<0, /* Stop Blink Source Counter */ +}; + +/* B2_BSC_STAT 8 bit Blink Source Counter Status */ +enum { + BSC_SRC = 1<<0, /* Blink Source, 0=Off / 1=On */ +}; + +/* B2_BSC_TST 16 bit Blink Source Counter Test Reg */ +enum { + BSC_T_ON = 1<<2, /* Test mode on */ + BSC_T_OFF = 1<<1, /* Test mode off */ + BSC_T_STEP = 1<<0, /* Test step */ +}; + +/* B3_RAM_ADDR 32 bit RAM Address, to read or write */ + /* Bit 31..19: reserved */ +#define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */ +/* RAM Interface Registers */ + +/* B3_RI_CTRL 16 bit RAM Iface Control Register */ +enum { + RI_CLR_RD_PERR = 1<<9, /* Clear IRQ RAM Read Parity Err */ + RI_CLR_WR_PERR = 1<<8, /* Clear IRQ RAM Write Parity Err*/ + + RI_RST_CLR = 1<<1, /* Clear RAM Interface Reset */ + RI_RST_SET = 1<<0, /* Set RAM Interface Reset */ +}; + +/* B3_RI_TEST 8 bit RAM Iface Test Register */ +enum { + RI_T_EV = 1<<3, /* Timeout Event occured */ + RI_T_ON = 1<<2, /* Timeout Timer Test On */ + RI_T_OFF = 1<<1, /* Timeout Timer Test Off */ + RI_T_STEP = 1<<0, /* Timeout Timer Step */ +}; + +/* MAC Arbiter Registers */ +/* B3_MA_TO_CTRL 16 bit MAC Arbiter Timeout Ctrl Reg */ +enum { + MA_FOE_ON = 1<<3, /* XMAC Fast Output Enable ON */ + MA_FOE_OFF = 1<<2, /* XMAC Fast Output Enable OFF */ + MA_RST_CLR = 1<<1, /* Clear MAC Arbiter Reset */ + MA_RST_SET = 1<<0, /* Set MAC Arbiter Reset */ + +}; + +/* Timeout values */ +#define SK_MAC_TO_53 72 /* MAC arbiter timeout */ +#define SK_PKT_TO_53 0x2000 /* Packet arbiter timeout */ +#define SK_PKT_TO_MAX 0xffff /* Maximum value */ +#define SK_RI_TO_53 36 /* RAM interface timeout */ + + +/* B3_MA_RC_CTRL 16 bit MAC Arbiter Recovery Ctrl Reg */ +enum { + MA_ENA_REC_TX2 = 1<<7, /* Enable Recovery Timer TX2 */ + MA_DIS_REC_TX2 = 1<<6, /* Disable Recovery Timer TX2 */ + MA_ENA_REC_TX1 = 1<<5, /* Enable Recovery Timer TX1 */ + MA_DIS_REC_TX1 = 1<<4, /* Disable Recovery Timer TX1 */ + MA_ENA_REC_RX2 = 1<<3, /* Enable Recovery Timer RX2 */ + MA_DIS_REC_RX2 = 1<<2, /* Disable Recovery Timer RX2 */ + MA_ENA_REC_RX1 = 1<<1, /* Enable Recovery Timer RX1 */ + MA_DIS_REC_RX1 = 1<<0, /* Disable Recovery Timer RX1 */ +}; + +/* Packet Arbiter Registers */ +/* B3_PA_CTRL 16 bit Packet Arbiter Ctrl Register */ +enum { + PA_CLR_TO_TX2 = 1<<13, /* Clear IRQ Packet Timeout TX2 */ + PA_CLR_TO_TX1 = 1<<12, /* Clear IRQ Packet Timeout TX1 */ + PA_CLR_TO_RX2 = 1<<11, /* Clear IRQ Packet Timeout RX2 */ + PA_CLR_TO_RX1 = 1<<10, /* Clear IRQ Packet Timeout RX1 */ + PA_ENA_TO_TX2 = 1<<9, /* Enable Timeout Timer TX2 */ + PA_DIS_TO_TX2 = 1<<8, /* Disable Timeout Timer TX2 */ + PA_ENA_TO_TX1 = 1<<7, /* Enable Timeout Timer TX1 */ + PA_DIS_TO_TX1 = 1<<6, /* Disable Timeout Timer TX1 */ + PA_ENA_TO_RX2 = 1<<5, /* Enable Timeout Timer RX2 */ + PA_DIS_TO_RX2 = 1<<4, /* Disable Timeout Timer RX2 */ + PA_ENA_TO_RX1 = 1<<3, /* Enable Timeout Timer RX1 */ + PA_DIS_TO_RX1 = 1<<2, /* Disable Timeout Timer RX1 */ + PA_RST_CLR = 1<<1, /* Clear MAC Arbiter Reset */ + PA_RST_SET = 1<<0, /* Set MAC Arbiter Reset */ +}; + +#define PA_ENA_TO_ALL (PA_ENA_TO_RX1 | PA_ENA_TO_RX2 |\ + PA_ENA_TO_TX1 | PA_ENA_TO_TX2) + + +/* Transmit Arbiter Registers MAC 1 and 2, use MR_ADDR() to access */ +/* TXA_ITI_INI 32 bit Tx Arb Interval Timer Init Val */ +/* TXA_ITI_VAL 32 bit Tx Arb Interval Timer Value */ +/* TXA_LIM_INI 32 bit Tx Arb Limit Counter Init Val */ +/* TXA_LIM_VAL 32 bit Tx Arb Limit Counter Value */ + +#define TXA_MAX_VAL 0x00ffffffUL /* Bit 23.. 0: Max TXA Timer/Cnt Val */ + +/* TXA_CTRL 8 bit Tx Arbiter Control Register */ +enum { + TXA_ENA_FSYNC = 1<<7, /* Enable force of sync Tx queue */ + TXA_DIS_FSYNC = 1<<6, /* Disable force of sync Tx queue */ + TXA_ENA_ALLOC = 1<<5, /* Enable alloc of free bandwidth */ + TXA_DIS_ALLOC = 1<<4, /* Disable alloc of free bandwidth */ + TXA_START_RC = 1<<3, /* Start sync Rate Control */ + TXA_STOP_RC = 1<<2, /* Stop sync Rate Control */ + TXA_ENA_ARB = 1<<1, /* Enable Tx Arbiter */ + TXA_DIS_ARB = 1<<0, /* Disable Tx Arbiter */ +}; + +/* + * Bank 4 - 5 + */ +/* Transmit Arbiter Registers MAC 1 and 2, use MR_ADDR() to access */ +enum { + TXA_ITI_INI = 0x0200,/* 32 bit Tx Arb Interval Timer Init Val*/ + TXA_ITI_VAL = 0x0204,/* 32 bit Tx Arb Interval Timer Value */ + TXA_LIM_INI = 0x0208,/* 32 bit Tx Arb Limit Counter Init Val */ + TXA_LIM_VAL = 0x020c,/* 32 bit Tx Arb Limit Counter Value */ + TXA_CTRL = 0x0210,/* 8 bit Tx Arbiter Control Register */ + TXA_TEST = 0x0211,/* 8 bit Tx Arbiter Test Register */ + TXA_STAT = 0x0212,/* 8 bit Tx Arbiter Status Register */ +}; + + +enum { + B6_EXT_REG = 0x0300,/* External registers (GENESIS only) */ + B7_CFG_SPC = 0x0380,/* copy of the Configuration register */ + B8_RQ1_REGS = 0x0400,/* Receive Queue 1 */ + B8_RQ2_REGS = 0x0480,/* Receive Queue 2 */ + B8_TS1_REGS = 0x0600,/* Transmit sync queue 1 */ + B8_TA1_REGS = 0x0680,/* Transmit async queue 1 */ + B8_TS2_REGS = 0x0700,/* Transmit sync queue 2 */ + B8_TA2_REGS = 0x0780,/* Transmit sync queue 2 */ + B16_RAM_REGS = 0x0800,/* RAM Buffer Registers */ +}; + +/* Queue Register Offsets, use Q_ADDR() to access */ +enum { + B8_Q_REGS = 0x0400, /* base of Queue registers */ + Q_D = 0x00, /* 8*32 bit Current Descriptor */ + Q_DA_L = 0x20, /* 32 bit Current Descriptor Address Low dWord */ + Q_DA_H = 0x24, /* 32 bit Current Descriptor Address High dWord */ + Q_AC_L = 0x28, /* 32 bit Current Address Counter Low dWord */ + Q_AC_H = 0x2c, /* 32 bit Current Address Counter High dWord */ + Q_BC = 0x30, /* 32 bit Current Byte Counter */ + Q_CSR = 0x34, /* 32 bit BMU Control/Status Register */ + Q_F = 0x38, /* 32 bit Flag Register */ + Q_T1 = 0x3c, /* 32 bit Test Register 1 */ + Q_T1_TR = 0x3c, /* 8 bit Test Register 1 Transfer SM */ + Q_T1_WR = 0x3d, /* 8 bit Test Register 1 Write Descriptor SM */ + Q_T1_RD = 0x3e, /* 8 bit Test Register 1 Read Descriptor SM */ + Q_T1_SV = 0x3f, /* 8 bit Test Register 1 Supervisor SM */ + Q_T2 = 0x40, /* 32 bit Test Register 2 */ + Q_T3 = 0x44, /* 32 bit Test Register 3 */ + +/* Yukon-2 */ + Q_DONE = 0x24, /* 16 bit Done Index (Yukon-2 only) */ + Q_WM = 0x40, /* 16 bit FIFO Watermark */ + Q_AL = 0x42, /* 8 bit FIFO Alignment */ + Q_RSP = 0x44, /* 16 bit FIFO Read Shadow Pointer */ + Q_RSL = 0x46, /* 8 bit FIFO Read Shadow Level */ + Q_RP = 0x48, /* 8 bit FIFO Read Pointer */ + Q_RL = 0x4a, /* 8 bit FIFO Read Level */ + Q_WP = 0x4c, /* 8 bit FIFO Write Pointer */ + Q_WSP = 0x4d, /* 8 bit FIFO Write Shadow Pointer */ + Q_WL = 0x4e, /* 8 bit FIFO Write Level */ + Q_WSL = 0x4f, /* 8 bit FIFO Write Shadow Level */ +}; +#define Q_ADDR(reg, offs) (B8_Q_REGS + (reg) + (offs)) + +/* RAM Buffer Register Offsets */ +enum { + + RB_START = 0x00,/* 32 bit RAM Buffer Start Address */ + RB_END = 0x04,/* 32 bit RAM Buffer End Address */ + RB_WP = 0x08,/* 32 bit RAM Buffer Write Pointer */ + RB_RP = 0x0c,/* 32 bit RAM Buffer Read Pointer */ + RB_RX_UTPP = 0x10,/* 32 bit Rx Upper Threshold, Pause Packet */ + RB_RX_LTPP = 0x14,/* 32 bit Rx Lower Threshold, Pause Packet */ + RB_RX_UTHP = 0x18,/* 32 bit Rx Upper Threshold, High Prio */ + RB_RX_LTHP = 0x1c,/* 32 bit Rx Lower Threshold, High Prio */ + /* 0x10 - 0x1f: reserved at Tx RAM Buffer Registers */ + RB_PC = 0x20,/* 32 bit RAM Buffer Packet Counter */ + RB_LEV = 0x24,/* 32 bit RAM Buffer Level Register */ + RB_CTRL = 0x28,/* 32 bit RAM Buffer Control Register */ + RB_TST1 = 0x29,/* 8 bit RAM Buffer Test Register 1 */ + RB_TST2 = 0x2a,/* 8 bit RAM Buffer Test Register 2 */ +}; + +/* Receive and Transmit Queues */ +enum { + Q_R1 = 0x0000, /* Receive Queue 1 */ + Q_R2 = 0x0080, /* Receive Queue 2 */ + Q_XS1 = 0x0200, /* Synchronous Transmit Queue 1 */ + Q_XA1 = 0x0280, /* Asynchronous Transmit Queue 1 */ + Q_XS2 = 0x0300, /* Synchronous Transmit Queue 2 */ + Q_XA2 = 0x0380, /* Asynchronous Transmit Queue 2 */ +}; + +/* Different MAC Types */ +enum { + SK_MAC_XMAC = 0, /* Xaqti XMAC II */ + SK_MAC_GMAC = 1, /* Marvell GMAC */ +}; + +/* Different PHY Types */ +enum { + SK_PHY_XMAC = 0,/* integrated in XMAC II */ + SK_PHY_BCOM = 1,/* Broadcom BCM5400 */ + SK_PHY_LONE = 2,/* Level One LXT1000 [not supported]*/ + SK_PHY_NAT = 3,/* National DP83891 [not supported] */ + SK_PHY_MARV_COPPER= 4,/* Marvell 88E1011S */ + SK_PHY_MARV_FIBER = 5,/* Marvell 88E1011S working on fiber */ +}; + +/* PHY addresses (bits 12..8 of PHY address reg) */ +enum { + PHY_ADDR_XMAC = 0<<8, + PHY_ADDR_BCOM = 1<<8, + PHY_ADDR_LONE = 3<<8, + PHY_ADDR_NAT = 0<<8, +/* GPHY address (bits 15..11 of SMI control reg) */ + PHY_ADDR_MARV = 0, +}; + +#define RB_ADDR(offs, queue) (B16_RAM_REGS + (queue) + (offs)) + +/* Receive MAC FIFO, Receive LED, and Link_Sync regs (GENESIS only) */ +enum { + RX_MFF_EA = 0x0c00,/* 32 bit Receive MAC FIFO End Address */ + RX_MFF_WP = 0x0c04,/* 32 bit Receive MAC FIFO Write Pointer */ + + RX_MFF_RP = 0x0c0c,/* 32 bit Receive MAC FIFO Read Pointer */ + RX_MFF_PC = 0x0c10,/* 32 bit Receive MAC FIFO Packet Cnt */ + RX_MFF_LEV = 0x0c14,/* 32 bit Receive MAC FIFO Level */ + RX_MFF_CTRL1 = 0x0c18,/* 16 bit Receive MAC FIFO Control Reg 1*/ + RX_MFF_STAT_TO = 0x0c1a,/* 8 bit Receive MAC Status Timeout */ + RX_MFF_TIST_TO = 0x0c1b,/* 8 bit Receive MAC Time Stamp Timeout */ + RX_MFF_CTRL2 = 0x0c1c,/* 8 bit Receive MAC FIFO Control Reg 2*/ + RX_MFF_TST1 = 0x0c1d,/* 8 bit Receive MAC FIFO Test Reg 1 */ + RX_MFF_TST2 = 0x0c1e,/* 8 bit Receive MAC FIFO Test Reg 2 */ + + RX_LED_INI = 0x0c20,/* 32 bit Receive LED Cnt Init Value */ + RX_LED_VAL = 0x0c24,/* 32 bit Receive LED Cnt Current Value */ + RX_LED_CTRL = 0x0c28,/* 8 bit Receive LED Cnt Control Reg */ + RX_LED_TST = 0x0c29,/* 8 bit Receive LED Cnt Test Register */ + + LNK_SYNC_INI = 0x0c30,/* 32 bit Link Sync Cnt Init Value */ + LNK_SYNC_VAL = 0x0c34,/* 32 bit Link Sync Cnt Current Value */ + LNK_SYNC_CTRL = 0x0c38,/* 8 bit Link Sync Cnt Control Register */ + LNK_SYNC_TST = 0x0c39,/* 8 bit Link Sync Cnt Test Register */ + LNK_LED_REG = 0x0c3c,/* 8 bit Link LED Register */ +}; + +/* Receive and Transmit MAC FIFO Registers (GENESIS only) */ +/* RX_MFF_CTRL1 16 bit Receive MAC FIFO Control Reg 1 */ +enum { + MFF_ENA_RDY_PAT = 1<<13, /* Enable Ready Patch */ + MFF_DIS_RDY_PAT = 1<<12, /* Disable Ready Patch */ + MFF_ENA_TIM_PAT = 1<<11, /* Enable Timing Patch */ + MFF_DIS_TIM_PAT = 1<<10, /* Disable Timing Patch */ + MFF_ENA_ALM_FUL = 1<<9, /* Enable AlmostFull Sign */ + MFF_DIS_ALM_FUL = 1<<8, /* Disable AlmostFull Sign */ + MFF_ENA_PAUSE = 1<<7, /* Enable Pause Signaling */ + MFF_DIS_PAUSE = 1<<6, /* Disable Pause Signaling */ + MFF_ENA_FLUSH = 1<<5, /* Enable Frame Flushing */ + MFF_DIS_FLUSH = 1<<4, /* Disable Frame Flushing */ + MFF_ENA_TIST = 1<<3, /* Enable Time Stamp Gener */ + MFF_DIS_TIST = 1<<2, /* Disable Time Stamp Gener */ + MFF_CLR_INTIST = 1<<1, /* Clear IRQ No Time Stamp */ + MFF_CLR_INSTAT = 1<<0, /* Clear IRQ No Status */ +#define MFF_RX_CTRL_DEF MFF_ENA_TIM_PAT +}; + +/* TX_MFF_CTRL1 16 bit Transmit MAC FIFO Control Reg 1 */ +enum { + MFF_CLR_PERR = 1<<15, /* Clear Parity Error IRQ */ + /* Bit 14: reserved */ + MFF_ENA_PKT_REC = 1<<13, /* Enable Packet Recovery */ + MFF_DIS_PKT_REC = 1<<12, /* Disable Packet Recovery */ + + MFF_ENA_W4E = 1<<7, /* Enable Wait for Empty */ + MFF_DIS_W4E = 1<<6, /* Disable Wait for Empty */ + + MFF_ENA_LOOPB = 1<<3, /* Enable Loopback */ + MFF_DIS_LOOPB = 1<<2, /* Disable Loopback */ + MFF_CLR_MAC_RST = 1<<1, /* Clear XMAC Reset */ + MFF_SET_MAC_RST = 1<<0, /* Set XMAC Reset */ +}; + +#define MFF_TX_CTRL_DEF (MFF_ENA_PKT_REC | MFF_ENA_TIM_PAT | MFF_ENA_FLUSH) + +/* RX_MFF_TST2 8 bit Receive MAC FIFO Test Register 2 */ +/* TX_MFF_TST2 8 bit Transmit MAC FIFO Test Register 2 */ +enum { + MFF_WSP_T_ON = 1<<6, /* Tx: Write Shadow Ptr TestOn */ + MFF_WSP_T_OFF = 1<<5, /* Tx: Write Shadow Ptr TstOff */ + MFF_WSP_INC = 1<<4, /* Tx: Write Shadow Ptr Increment */ + MFF_PC_DEC = 1<<3, /* Packet Counter Decrement */ + MFF_PC_T_ON = 1<<2, /* Packet Counter Test On */ + MFF_PC_T_OFF = 1<<1, /* Packet Counter Test Off */ + MFF_PC_INC = 1<<0, /* Packet Counter Increment */ +}; + +/* RX_MFF_TST1 8 bit Receive MAC FIFO Test Register 1 */ +/* TX_MFF_TST1 8 bit Transmit MAC FIFO Test Register 1 */ +enum { + MFF_WP_T_ON = 1<<6, /* Write Pointer Test On */ + MFF_WP_T_OFF = 1<<5, /* Write Pointer Test Off */ + MFF_WP_INC = 1<<4, /* Write Pointer Increm */ + + MFF_RP_T_ON = 1<<2, /* Read Pointer Test On */ + MFF_RP_T_OFF = 1<<1, /* Read Pointer Test Off */ + MFF_RP_DEC = 1<<0, /* Read Pointer Decrement */ +}; + +/* RX_MFF_CTRL2 8 bit Receive MAC FIFO Control Reg 2 */ +/* TX_MFF_CTRL2 8 bit Transmit MAC FIFO Control Reg 2 */ +enum { + MFF_ENA_OP_MD = 1<<3, /* Enable Operation Mode */ + MFF_DIS_OP_MD = 1<<2, /* Disable Operation Mode */ + MFF_RST_CLR = 1<<1, /* Clear MAC FIFO Reset */ + MFF_RST_SET = 1<<0, /* Set MAC FIFO Reset */ +}; + + +/* Link LED Counter Registers (GENESIS only) */ + +/* RX_LED_CTRL 8 bit Receive LED Cnt Control Reg */ +/* TX_LED_CTRL 8 bit Transmit LED Cnt Control Reg */ +/* LNK_SYNC_CTRL 8 bit Link Sync Cnt Control Register */ +enum { + LED_START = 1<<2, /* Start Timer */ + LED_STOP = 1<<1, /* Stop Timer */ + LED_STATE = 1<<0, /* Rx/Tx: LED State, 1=LED on */ +}; + +/* RX_LED_TST 8 bit Receive LED Cnt Test Register */ +/* TX_LED_TST 8 bit Transmit LED Cnt Test Register */ +/* LNK_SYNC_TST 8 bit Link Sync Cnt Test Register */ +enum { + LED_T_ON = 1<<2, /* LED Counter Test mode On */ + LED_T_OFF = 1<<1, /* LED Counter Test mode Off */ + LED_T_STEP = 1<<0, /* LED Counter Step */ +}; + +/* LNK_LED_REG 8 bit Link LED Register */ +enum { + LED_BLK_ON = 1<<5, /* Link LED Blinking On */ + LED_BLK_OFF = 1<<4, /* Link LED Blinking Off */ + LED_SYNC_ON = 1<<3, /* Use Sync Wire to switch LED */ + LED_SYNC_OFF = 1<<2, /* Disable Sync Wire Input */ + LED_ON = 1<<1, /* switch LED on */ + LED_OFF = 1<<0, /* switch LED off */ +}; + +/* Receive GMAC FIFO (YUKON and Yukon-2) */ +enum { + RX_GMF_EA = 0x0c40,/* 32 bit Rx GMAC FIFO End Address */ + RX_GMF_AF_THR = 0x0c44,/* 32 bit Rx GMAC FIFO Almost Full Thresh. */ + RX_GMF_CTRL_T = 0x0c48,/* 32 bit Rx GMAC FIFO Control/Test */ + RX_GMF_FL_MSK = 0x0c4c,/* 32 bit Rx GMAC FIFO Flush Mask */ + RX_GMF_FL_THR = 0x0c50,/* 32 bit Rx GMAC FIFO Flush Threshold */ + RX_GMF_TR_THR = 0x0c54,/* 32 bit Rx Truncation Threshold (Yukon-2) */ + + RX_GMF_VLAN = 0x0c5c,/* 32 bit Rx VLAN Type Register (Yukon-2) */ + RX_GMF_WP = 0x0c60,/* 32 bit Rx GMAC FIFO Write Pointer */ + + RX_GMF_WLEV = 0x0c68,/* 32 bit Rx GMAC FIFO Write Level */ + + RX_GMF_RP = 0x0c70,/* 32 bit Rx GMAC FIFO Read Pointer */ + + RX_GMF_RLEV = 0x0c78,/* 32 bit Rx GMAC FIFO Read Level */ +}; + + +/* TXA_TEST 8 bit Tx Arbiter Test Register */ +enum { + TXA_INT_T_ON = 1<<5, /* Tx Arb Interval Timer Test On */ + TXA_INT_T_OFF = 1<<4, /* Tx Arb Interval Timer Test Off */ + TXA_INT_T_STEP = 1<<3, /* Tx Arb Interval Timer Step */ + TXA_LIM_T_ON = 1<<2, /* Tx Arb Limit Timer Test On */ + TXA_LIM_T_OFF = 1<<1, /* Tx Arb Limit Timer Test Off */ + TXA_LIM_T_STEP = 1<<0, /* Tx Arb Limit Timer Step */ +}; + +/* TXA_STAT 8 bit Tx Arbiter Status Register */ +enum { + TXA_PRIO_XS = 1<<0, /* sync queue has prio to send */ +}; + + +/* Q_BC 32 bit Current Byte Counter */ + +/* BMU Control Status Registers */ +/* B0_R1_CSR 32 bit BMU Ctrl/Stat Rx Queue 1 */ +/* B0_R2_CSR 32 bit BMU Ctrl/Stat Rx Queue 2 */ +/* B0_XA1_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 1 */ +/* B0_XS1_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 1 */ +/* B0_XA2_CSR 32 bit BMU Ctrl/Stat Sync Tx Queue 2 */ +/* B0_XS2_CSR 32 bit BMU Ctrl/Stat Async Tx Queue 2 */ +/* Q_CSR 32 bit BMU Control/Status Register */ + +enum { + CSR_SV_IDLE = 1<<24, /* BMU SM Idle */ + + CSR_DESC_CLR = 1<<21, /* Clear Reset for Descr */ + CSR_DESC_SET = 1<<20, /* Set Reset for Descr */ + CSR_FIFO_CLR = 1<<19, /* Clear Reset for FIFO */ + CSR_FIFO_SET = 1<<18, /* Set Reset for FIFO */ + CSR_HPI_RUN = 1<<17, /* Release HPI SM */ + CSR_HPI_RST = 1<<16, /* Reset HPI SM to Idle */ + CSR_SV_RUN = 1<<15, /* Release Supervisor SM */ + CSR_SV_RST = 1<<14, /* Reset Supervisor SM */ + CSR_DREAD_RUN = 1<<13, /* Release Descr Read SM */ + CSR_DREAD_RST = 1<<12, /* Reset Descr Read SM */ + CSR_DWRITE_RUN = 1<<11, /* Release Descr Write SM */ + CSR_DWRITE_RST = 1<<10, /* Reset Descr Write SM */ + CSR_TRANS_RUN = 1<<9, /* Release Transfer SM */ + CSR_TRANS_RST = 1<<8, /* Reset Transfer SM */ + CSR_ENA_POL = 1<<7, /* Enable Descr Polling */ + CSR_DIS_POL = 1<<6, /* Disable Descr Polling */ + CSR_STOP = 1<<5, /* Stop Rx/Tx Queue */ + CSR_START = 1<<4, /* Start Rx/Tx Queue */ + CSR_IRQ_CL_P = 1<<3, /* (Rx) Clear Parity IRQ */ + CSR_IRQ_CL_B = 1<<2, /* Clear EOB IRQ */ + CSR_IRQ_CL_F = 1<<1, /* Clear EOF IRQ */ + CSR_IRQ_CL_C = 1<<0, /* Clear ERR IRQ */ +}; + +#define CSR_SET_RESET (CSR_DESC_SET | CSR_FIFO_SET | CSR_HPI_RST |\ + CSR_SV_RST | CSR_DREAD_RST | CSR_DWRITE_RST |\ + CSR_TRANS_RST) +#define CSR_CLR_RESET (CSR_DESC_CLR | CSR_FIFO_CLR | CSR_HPI_RUN |\ + CSR_SV_RUN | CSR_DREAD_RUN | CSR_DWRITE_RUN |\ + CSR_TRANS_RUN) + +/* Q_F 32 bit Flag Register */ +enum { + F_ALM_FULL = 1<<27, /* Rx FIFO: almost full */ + F_EMPTY = 1<<27, /* Tx FIFO: empty flag */ + F_FIFO_EOF = 1<<26, /* Tag (EOF Flag) bit in FIFO */ + F_WM_REACHED = 1<<25, /* Watermark reached */ + + F_FIFO_LEVEL = 0x1fL<<16, /* Bit 23..16: # of Qwords in FIFO */ + F_WATER_MARK = 0x0007ffL, /* Bit 10.. 0: Watermark */ +}; + +/* RAM Buffer Register Offsets, use RB_ADDR(Queue, Offs) to access */ +/* RB_START 32 bit RAM Buffer Start Address */ +/* RB_END 32 bit RAM Buffer End Address */ +/* RB_WP 32 bit RAM Buffer Write Pointer */ +/* RB_RP 32 bit RAM Buffer Read Pointer */ +/* RB_RX_UTPP 32 bit Rx Upper Threshold, Pause Pack */ +/* RB_RX_LTPP 32 bit Rx Lower Threshold, Pause Pack */ +/* RB_RX_UTHP 32 bit Rx Upper Threshold, High Prio */ +/* RB_RX_LTHP 32 bit Rx Lower Threshold, High Prio */ +/* RB_PC 32 bit RAM Buffer Packet Counter */ +/* RB_LEV 32 bit RAM Buffer Level Register */ + +#define RB_MSK 0x0007ffff /* Bit 18.. 0: RAM Buffer Pointer Bits */ +/* RB_TST2 8 bit RAM Buffer Test Register 2 */ +/* RB_TST1 8 bit RAM Buffer Test Register 1 */ + +/* RB_CTRL 8 bit RAM Buffer Control Register */ +enum { + RB_ENA_STFWD = 1<<5, /* Enable Store & Forward */ + RB_DIS_STFWD = 1<<4, /* Disable Store & Forward */ + RB_ENA_OP_MD = 1<<3, /* Enable Operation Mode */ + RB_DIS_OP_MD = 1<<2, /* Disable Operation Mode */ + RB_RST_CLR = 1<<1, /* Clear RAM Buf STM Reset */ + RB_RST_SET = 1<<0, /* Set RAM Buf STM Reset */ +}; + +/* Transmit MAC FIFO and Transmit LED Registers (GENESIS only), */ +enum { + TX_MFF_EA = 0x0d00,/* 32 bit Transmit MAC FIFO End Address */ + TX_MFF_WP = 0x0d04,/* 32 bit Transmit MAC FIFO WR Pointer */ + TX_MFF_WSP = 0x0d08,/* 32 bit Transmit MAC FIFO WR Shadow Ptr */ + TX_MFF_RP = 0x0d0c,/* 32 bit Transmit MAC FIFO RD Pointer */ + TX_MFF_PC = 0x0d10,/* 32 bit Transmit MAC FIFO Packet Cnt */ + TX_MFF_LEV = 0x0d14,/* 32 bit Transmit MAC FIFO Level */ + TX_MFF_CTRL1 = 0x0d18,/* 16 bit Transmit MAC FIFO Ctrl Reg 1 */ + TX_MFF_WAF = 0x0d1a,/* 8 bit Transmit MAC Wait after flush */ + + TX_MFF_CTRL2 = 0x0d1c,/* 8 bit Transmit MAC FIFO Ctrl Reg 2 */ + TX_MFF_TST1 = 0x0d1d,/* 8 bit Transmit MAC FIFO Test Reg 1 */ + TX_MFF_TST2 = 0x0d1e,/* 8 bit Transmit MAC FIFO Test Reg 2 */ + + TX_LED_INI = 0x0d20,/* 32 bit Transmit LED Cnt Init Value */ + TX_LED_VAL = 0x0d24,/* 32 bit Transmit LED Cnt Current Val */ + TX_LED_CTRL = 0x0d28,/* 8 bit Transmit LED Cnt Control Reg */ + TX_LED_TST = 0x0d29,/* 8 bit Transmit LED Cnt Test Reg */ +}; + +/* Counter and Timer constants, for a host clock of 62.5 MHz */ +#define SK_XMIT_DUR 0x002faf08UL /* 50 ms */ +#define SK_BLK_DUR 0x01dcd650UL /* 500 ms */ + +#define SK_DPOLL_DEF 0x00ee6b28UL /* 250 ms at 62.5 MHz */ + +#define SK_DPOLL_MAX 0x00ffffffUL /* 268 ms at 62.5 MHz */ + /* 215 ms at 78.12 MHz */ + +#define SK_FACT_62 100 /* is given in percent */ +#define SK_FACT_53 85 /* on GENESIS: 53.12 MHz */ +#define SK_FACT_78 125 /* on YUKON: 78.12 MHz */ + + +/* Transmit GMAC FIFO (YUKON only) */ +enum { + TX_GMF_EA = 0x0d40,/* 32 bit Tx GMAC FIFO End Address */ + TX_GMF_AE_THR = 0x0d44,/* 32 bit Tx GMAC FIFO Almost Empty Thresh.*/ + TX_GMF_CTRL_T = 0x0d48,/* 32 bit Tx GMAC FIFO Control/Test */ + + TX_GMF_WP = 0x0d60,/* 32 bit Tx GMAC FIFO Write Pointer */ + TX_GMF_WSP = 0x0d64,/* 32 bit Tx GMAC FIFO Write Shadow Ptr. */ + TX_GMF_WLEV = 0x0d68,/* 32 bit Tx GMAC FIFO Write Level */ + + TX_GMF_RP = 0x0d70,/* 32 bit Tx GMAC FIFO Read Pointer */ + TX_GMF_RSTP = 0x0d74,/* 32 bit Tx GMAC FIFO Restart Pointer */ + TX_GMF_RLEV = 0x0d78,/* 32 bit Tx GMAC FIFO Read Level */ + + /* Descriptor Poll Timer Registers */ + B28_DPT_INI = 0x0e00,/* 24 bit Descriptor Poll Timer Init Val */ + B28_DPT_VAL = 0x0e04,/* 24 bit Descriptor Poll Timer Curr Val */ + B28_DPT_CTRL = 0x0e08,/* 8 bit Descriptor Poll Timer Ctrl Reg */ + + B28_DPT_TST = 0x0e0a,/* 8 bit Descriptor Poll Timer Test Reg */ + + /* Time Stamp Timer Registers (YUKON only) */ + GMAC_TI_ST_VAL = 0x0e14,/* 32 bit Time Stamp Timer Curr Val */ + GMAC_TI_ST_CTRL = 0x0e18,/* 8 bit Time Stamp Timer Ctrl Reg */ + GMAC_TI_ST_TST = 0x0e1a,/* 8 bit Time Stamp Timer Test Reg */ +}; + +/* Status BMU Registers (Yukon-2 only)*/ +enum { + STAT_CTRL = 0x0e80,/* 32 bit Status BMU Control Reg */ + STAT_LAST_IDX = 0x0e84,/* 16 bit Status BMU Last Index */ + /* 0x0e85 - 0x0e86: reserved */ + STAT_LIST_ADDR_LO = 0x0e88,/* 32 bit Status List Start Addr (low) */ + STAT_LIST_ADDR_HI = 0x0e8c,/* 32 bit Status List Start Addr (high) */ + STAT_TXA1_RIDX = 0x0e90,/* 16 bit Status TxA1 Report Index Reg */ + STAT_TXS1_RIDX = 0x0e92,/* 16 bit Status TxS1 Report Index Reg */ + STAT_TXA2_RIDX = 0x0e94,/* 16 bit Status TxA2 Report Index Reg */ + STAT_TXS2_RIDX = 0x0e96,/* 16 bit Status TxS2 Report Index Reg */ + STAT_TX_IDX_TH = 0x0e98,/* 16 bit Status Tx Index Threshold Reg */ + STAT_PUT_IDX = 0x0e9c,/* 16 bit Status Put Index Reg */ + +/* FIFO Control/Status Registers (Yukon-2 only)*/ + STAT_FIFO_WP = 0x0ea0,/* 8 bit Status FIFO Write Pointer Reg */ + STAT_FIFO_RP = 0x0ea4,/* 8 bit Status FIFO Read Pointer Reg */ + STAT_FIFO_RSP = 0x0ea6,/* 8 bit Status FIFO Read Shadow Ptr */ + STAT_FIFO_LEVEL = 0x0ea8,/* 8 bit Status FIFO Level Reg */ + STAT_FIFO_SHLVL = 0x0eaa,/* 8 bit Status FIFO Shadow Level Reg */ + STAT_FIFO_WM = 0x0eac,/* 8 bit Status FIFO Watermark Reg */ + STAT_FIFO_ISR_WM = 0x0ead,/* 8 bit Status FIFO ISR Watermark Reg */ + +/* Level and ISR Timer Registers (Yukon-2 only)*/ + STAT_LEV_TIMER_INI = 0x0eb0,/* 32 bit Level Timer Init. Value Reg */ + STAT_LEV_TIMER_CNT = 0x0eb4,/* 32 bit Level Timer Counter Reg */ + STAT_LEV_TIMER_CTRL = 0x0eb8,/* 8 bit Level Timer Control Reg */ + STAT_LEV_TIMER_TEST = 0x0eb9,/* 8 bit Level Timer Test Reg */ + STAT_TX_TIMER_INI = 0x0ec0,/* 32 bit Tx Timer Init. Value Reg */ + STAT_TX_TIMER_CNT = 0x0ec4,/* 32 bit Tx Timer Counter Reg */ + STAT_TX_TIMER_CTRL = 0x0ec8,/* 8 bit Tx Timer Control Reg */ + STAT_TX_TIMER_TEST = 0x0ec9,/* 8 bit Tx Timer Test Reg */ + STAT_ISR_TIMER_INI = 0x0ed0,/* 32 bit ISR Timer Init. Value Reg */ + STAT_ISR_TIMER_CNT = 0x0ed4,/* 32 bit ISR Timer Counter Reg */ + STAT_ISR_TIMER_CTRL = 0x0ed8,/* 8 bit ISR Timer Control Reg */ + STAT_ISR_TIMER_TEST = 0x0ed9,/* 8 bit ISR Timer Test Reg */ + + ST_LAST_IDX_MASK = 0x007f,/* Last Index Mask */ + ST_TXRP_IDX_MASK = 0x0fff,/* Tx Report Index Mask */ + ST_TXTH_IDX_MASK = 0x0fff,/* Tx Threshold Index Mask */ + ST_WM_IDX_MASK = 0x3f,/* FIFO Watermark Index Mask */ +}; + +enum { + LINKLED_OFF = 0x01, + LINKLED_ON = 0x02, + LINKLED_LINKSYNC_OFF = 0x04, + LINKLED_LINKSYNC_ON = 0x08, + LINKLED_BLINK_OFF = 0x10, + LINKLED_BLINK_ON = 0x20, +}; + +/* GMAC and GPHY Control Registers (YUKON only) */ +enum { + GMAC_CTRL = 0x0f00,/* 32 bit GMAC Control Reg */ + GPHY_CTRL = 0x0f04,/* 32 bit GPHY Control Reg */ + GMAC_IRQ_SRC = 0x0f08,/* 8 bit GMAC Interrupt Source Reg */ + GMAC_IRQ_MSK = 0x0f0c,/* 8 bit GMAC Interrupt Mask Reg */ + GMAC_LINK_CTRL = 0x0f10,/* 16 bit Link Control Reg */ + +/* Wake-up Frame Pattern Match Control Registers (YUKON only) */ + + WOL_REG_OFFS = 0x20,/* HW-Bug: Address is + 0x20 against spec. */ + + WOL_CTRL_STAT = 0x0f20,/* 16 bit WOL Control/Status Reg */ + WOL_MATCH_CTL = 0x0f22,/* 8 bit WOL Match Control Reg */ + WOL_MATCH_RES = 0x0f23,/* 8 bit WOL Match Result Reg */ + WOL_MAC_ADDR = 0x0f24,/* 32 bit WOL MAC Address */ + WOL_PATT_PME = 0x0f2a,/* 8 bit WOL PME Match Enable (Yukon-2) */ + WOL_PATT_ASFM = 0x0f2b,/* 8 bit WOL ASF Match Enable (Yukon-2) */ + WOL_PATT_RPTR = 0x0f2c,/* 8 bit WOL Pattern Read Pointer */ + +/* WOL Pattern Length Registers (YUKON only) */ + + WOL_PATT_LEN_LO = 0x0f30,/* 32 bit WOL Pattern Length 3..0 */ + WOL_PATT_LEN_HI = 0x0f34,/* 24 bit WOL Pattern Length 6..4 */ + +/* WOL Pattern Counter Registers (YUKON only) */ + + WOL_PATT_CNT_0 = 0x0f38,/* 32 bit WOL Pattern Counter 3..0 */ + WOL_PATT_CNT_4 = 0x0f3c,/* 24 bit WOL Pattern Counter 6..4 */ +}; + +enum { + WOL_PATT_RAM_1 = 0x1000,/* WOL Pattern RAM Link 1 */ + WOL_PATT_RAM_2 = 0x1400,/* WOL Pattern RAM Link 2 */ +}; + +enum { + BASE_XMAC_1 = 0x2000,/* XMAC 1 registers */ + BASE_GMAC_1 = 0x2800,/* GMAC 1 registers */ + BASE_XMAC_2 = 0x3000,/* XMAC 2 registers */ + BASE_GMAC_2 = 0x3800,/* GMAC 2 registers */ +}; + +/* + * Receive Frame Status Encoding + */ +enum { + XMR_FS_LEN = 0x3fff<<18, /* Bit 31..18: Rx Frame Length */ + XMR_FS_2L_VLAN = 1<<17, /* Bit 17: tagged wh 2Lev VLAN ID*/ + XMR_FS_1_VLAN = 1<<16, /* Bit 16: tagged wh 1ev VLAN ID*/ + XMR_FS_BC = 1<<15, /* Bit 15: Broadcast Frame */ + XMR_FS_MC = 1<<14, /* Bit 14: Multicast Frame */ + XMR_FS_UC = 1<<13, /* Bit 13: Unicast Frame */ + + XMR_FS_BURST = 1<<11, /* Bit 11: Burst Mode */ + XMR_FS_CEX_ERR = 1<<10, /* Bit 10: Carrier Ext. Error */ + XMR_FS_802_3 = 1<<9, /* Bit 9: 802.3 Frame */ + XMR_FS_COL_ERR = 1<<8, /* Bit 8: Collision Error */ + XMR_FS_CAR_ERR = 1<<7, /* Bit 7: Carrier Event Error */ + XMR_FS_LEN_ERR = 1<<6, /* Bit 6: In-Range Length Error */ + XMR_FS_FRA_ERR = 1<<5, /* Bit 5: Framing Error */ + XMR_FS_RUNT = 1<<4, /* Bit 4: Runt Frame */ + XMR_FS_LNG_ERR = 1<<3, /* Bit 3: Giant (Jumbo) Frame */ + XMR_FS_FCS_ERR = 1<<2, /* Bit 2: Frame Check Sequ Err */ + XMR_FS_ERR = 1<<1, /* Bit 1: Frame Error */ + XMR_FS_MCTRL = 1<<0, /* Bit 0: MAC Control Packet */ + +/* + * XMR_FS_ERR will be set if + * XMR_FS_FCS_ERR, XMR_FS_LNG_ERR, XMR_FS_RUNT, + * XMR_FS_FRA_ERR, XMR_FS_LEN_ERR, or XMR_FS_CEX_ERR + * is set. XMR_FS_LNG_ERR and XMR_FS_LEN_ERR will issue + * XMR_FS_ERR unless the corresponding bit in the Receive Command + * Register is set. + */ +}; + +/* +,* XMAC-PHY Registers, indirect addressed over the XMAC + */ +enum { + PHY_XMAC_CTRL = 0x00,/* 16 bit r/w PHY Control Register */ + PHY_XMAC_STAT = 0x01,/* 16 bit r/w PHY Status Register */ + PHY_XMAC_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */ + PHY_XMAC_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */ + PHY_XMAC_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */ + PHY_XMAC_AUNE_LP = 0x05,/* 16 bit r/o Link Partner Abi Reg */ + PHY_XMAC_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */ + PHY_XMAC_NEPG = 0x07,/* 16 bit r/w Next Page Register */ + PHY_XMAC_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */ + + PHY_XMAC_EXT_STAT = 0x0f,/* 16 bit r/o Ext Status Register */ + PHY_XMAC_RES_ABI = 0x10,/* 16 bit r/o PHY Resolved Ability */ +}; +/* + * Broadcom-PHY Registers, indirect addressed over XMAC + */ +enum { + PHY_BCOM_CTRL = 0x00,/* 16 bit r/w PHY Control Register */ + PHY_BCOM_STAT = 0x01,/* 16 bit r/o PHY Status Register */ + PHY_BCOM_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */ + PHY_BCOM_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */ + PHY_BCOM_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */ + PHY_BCOM_AUNE_LP = 0x05,/* 16 bit r/o Link Part Ability Reg */ + PHY_BCOM_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */ + PHY_BCOM_NEPG = 0x07,/* 16 bit r/w Next Page Register */ + PHY_BCOM_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */ + /* Broadcom-specific registers */ + PHY_BCOM_1000T_CTRL = 0x09,/* 16 bit r/w 1000Base-T Control Reg */ + PHY_BCOM_1000T_STAT = 0x0a,/* 16 bit r/o 1000Base-T Status Reg */ + PHY_BCOM_EXT_STAT = 0x0f,/* 16 bit r/o Extended Status Reg */ + PHY_BCOM_P_EXT_CTRL = 0x10,/* 16 bit r/w PHY Extended Ctrl Reg */ + PHY_BCOM_P_EXT_STAT = 0x11,/* 16 bit r/o PHY Extended Stat Reg */ + PHY_BCOM_RE_CTR = 0x12,/* 16 bit r/w Receive Error Counter */ + PHY_BCOM_FC_CTR = 0x13,/* 16 bit r/w False Carrier Sense Cnt */ + PHY_BCOM_RNO_CTR = 0x14,/* 16 bit r/w Receiver NOT_OK Cnt */ + + PHY_BCOM_AUX_CTRL = 0x18,/* 16 bit r/w Auxiliary Control Reg */ + PHY_BCOM_AUX_STAT = 0x19,/* 16 bit r/o Auxiliary Stat Summary */ + PHY_BCOM_INT_STAT = 0x1a,/* 16 bit r/o Interrupt Status Reg */ + PHY_BCOM_INT_MASK = 0x1b,/* 16 bit r/w Interrupt Mask Reg */ +}; + +/* + * Marvel-PHY Registers, indirect addressed over GMAC + */ +enum { + PHY_MARV_CTRL = 0x00,/* 16 bit r/w PHY Control Register */ + PHY_MARV_STAT = 0x01,/* 16 bit r/o PHY Status Register */ + PHY_MARV_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */ + PHY_MARV_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */ + PHY_MARV_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */ + PHY_MARV_AUNE_LP = 0x05,/* 16 bit r/o Link Part Ability Reg */ + PHY_MARV_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */ + PHY_MARV_NEPG = 0x07,/* 16 bit r/w Next Page Register */ + PHY_MARV_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */ + /* Marvel-specific registers */ + PHY_MARV_1000T_CTRL = 0x09,/* 16 bit r/w 1000Base-T Control Reg */ + PHY_MARV_1000T_STAT = 0x0a,/* 16 bit r/o 1000Base-T Status Reg */ + PHY_MARV_EXT_STAT = 0x0f,/* 16 bit r/o Extended Status Reg */ + PHY_MARV_PHY_CTRL = 0x10,/* 16 bit r/w PHY Specific Ctrl Reg */ + PHY_MARV_PHY_STAT = 0x11,/* 16 bit r/o PHY Specific Stat Reg */ + PHY_MARV_INT_MASK = 0x12,/* 16 bit r/w Interrupt Mask Reg */ + PHY_MARV_INT_STAT = 0x13,/* 16 bit r/o Interrupt Status Reg */ + PHY_MARV_EXT_CTRL = 0x14,/* 16 bit r/w Ext. PHY Specific Ctrl */ + PHY_MARV_RXE_CNT = 0x15,/* 16 bit r/w Receive Error Counter */ + PHY_MARV_EXT_ADR = 0x16,/* 16 bit r/w Ext. Ad. for Cable Diag. */ + PHY_MARV_PORT_IRQ = 0x17,/* 16 bit r/o Port 0 IRQ (88E1111 only) */ + PHY_MARV_LED_CTRL = 0x18,/* 16 bit r/w LED Control Reg */ + PHY_MARV_LED_OVER = 0x19,/* 16 bit r/w Manual LED Override Reg */ + PHY_MARV_EXT_CTRL_2 = 0x1a,/* 16 bit r/w Ext. PHY Specific Ctrl 2 */ + PHY_MARV_EXT_P_STAT = 0x1b,/* 16 bit r/w Ext. PHY Spec. Stat Reg */ + PHY_MARV_CABLE_DIAG = 0x1c,/* 16 bit r/o Cable Diagnostic Reg */ + PHY_MARV_PAGE_ADDR = 0x1d,/* 16 bit r/w Extended Page Address Reg */ + PHY_MARV_PAGE_DATA = 0x1e,/* 16 bit r/w Extended Page Data Reg */ + +/* for 10/100 Fast Ethernet PHY (88E3082 only) */ + PHY_MARV_FE_LED_PAR = 0x16,/* 16 bit r/w LED Parallel Select Reg. */ + PHY_MARV_FE_LED_SER = 0x17,/* 16 bit r/w LED Stream Select S. LED */ + PHY_MARV_FE_VCT_TX = 0x1a,/* 16 bit r/w VCT Reg. for TXP/N Pins */ + PHY_MARV_FE_VCT_RX = 0x1b,/* 16 bit r/o VCT Reg. for RXP/N Pins */ + PHY_MARV_FE_SPEC_2 = 0x1c,/* 16 bit r/w Specific Control Reg. 2 */ +}; + +/* Level One-PHY Registers, indirect addressed over XMAC */ +enum { + PHY_LONE_CTRL = 0x00,/* 16 bit r/w PHY Control Register */ + PHY_LONE_STAT = 0x01,/* 16 bit r/o PHY Status Register */ + PHY_LONE_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */ + PHY_LONE_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */ + PHY_LONE_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */ + PHY_LONE_AUNE_LP = 0x05,/* 16 bit r/o Link Part Ability Reg */ + PHY_LONE_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */ + PHY_LONE_NEPG = 0x07,/* 16 bit r/w Next Page Register */ + PHY_LONE_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */ + /* Level One-specific registers */ + PHY_LONE_1000T_CTRL = 0x09,/* 16 bit r/w 1000Base-T Control Reg */ + PHY_LONE_1000T_STAT = 0x0a,/* 16 bit r/o 1000Base-T Status Reg */ + PHY_LONE_EXT_STAT = 0x0f,/* 16 bit r/o Extended Status Reg */ + PHY_LONE_PORT_CFG = 0x10,/* 16 bit r/w Port Configuration Reg*/ + PHY_LONE_Q_STAT = 0x11,/* 16 bit r/o Quick Status Reg */ + PHY_LONE_INT_ENAB = 0x12,/* 16 bit r/w Interrupt Enable Reg */ + PHY_LONE_INT_STAT = 0x13,/* 16 bit r/o Interrupt Status Reg */ + PHY_LONE_LED_CFG = 0x14,/* 16 bit r/w LED Configuration Reg */ + PHY_LONE_PORT_CTRL = 0x15,/* 16 bit r/w Port Control Reg */ + PHY_LONE_CIM = 0x16,/* 16 bit r/o CIM Reg */ +}; + +/* National-PHY Registers, indirect addressed over XMAC */ +enum { + PHY_NAT_CTRL = 0x00,/* 16 bit r/w PHY Control Register */ + PHY_NAT_STAT = 0x01,/* 16 bit r/w PHY Status Register */ + PHY_NAT_ID0 = 0x02,/* 16 bit r/o PHY ID0 Register */ + PHY_NAT_ID1 = 0x03,/* 16 bit r/o PHY ID1 Register */ + PHY_NAT_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */ + PHY_NAT_AUNE_LP = 0x05,/* 16 bit r/o Link Partner Ability Reg */ + PHY_NAT_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */ + PHY_NAT_NEPG = 0x07,/* 16 bit r/w Next Page Register */ + PHY_NAT_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner Reg */ + /* National-specific registers */ + PHY_NAT_1000T_CTRL = 0x09,/* 16 bit r/w 1000Base-T Control Reg */ + PHY_NAT_1000T_STAT = 0x0a,/* 16 bit r/o 1000Base-T Status Reg */ + PHY_NAT_EXT_STAT = 0x0f,/* 16 bit r/o Extended Status Register */ + PHY_NAT_EXT_CTRL1 = 0x10,/* 16 bit r/o Extended Control Reg1 */ + PHY_NAT_Q_STAT1 = 0x11,/* 16 bit r/o Quick Status Reg1 */ + PHY_NAT_10B_OP = 0x12,/* 16 bit r/o 10Base-T Operations Reg */ + PHY_NAT_EXT_CTRL2 = 0x13,/* 16 bit r/o Extended Control Reg1 */ + PHY_NAT_Q_STAT2 = 0x14,/* 16 bit r/o Quick Status Reg2 */ + + PHY_NAT_PHY_ADDR = 0x19,/* 16 bit r/o PHY Address Register */ +}; + +enum { + PHY_CT_RESET = 1<<15, /* Bit 15: (sc) clear all PHY related regs */ + PHY_CT_LOOP = 1<<14, /* Bit 14: enable Loopback over PHY */ + PHY_CT_SPS_LSB = 1<<13, /* Bit 13: Speed select, lower bit */ + PHY_CT_ANE = 1<<12, /* Bit 12: Auto-Negotiation Enabled */ + PHY_CT_PDOWN = 1<<11, /* Bit 11: Power Down Mode */ + PHY_CT_ISOL = 1<<10, /* Bit 10: Isolate Mode */ + PHY_CT_RE_CFG = 1<<9, /* Bit 9: (sc) Restart Auto-Negotiation */ + PHY_CT_DUP_MD = 1<<8, /* Bit 8: Duplex Mode */ + PHY_CT_COL_TST = 1<<7, /* Bit 7: Collision Test enabled */ + PHY_CT_SPS_MSB = 1<<6, /* Bit 6: Speed select, upper bit */ +}; + +enum { + PHY_CT_SP1000 = PHY_CT_SPS_MSB, /* enable speed of 1000 Mbps */ + PHY_CT_SP100 = PHY_CT_SPS_LSB, /* enable speed of 100 Mbps */ + PHY_CT_SP10 = 0, /* enable speed of 10 Mbps */ +}; + +enum { + PHY_ST_EXT_ST = 1<<8, /* Bit 8: Extended Status Present */ + + PHY_ST_PRE_SUP = 1<<6, /* Bit 6: Preamble Suppression */ + PHY_ST_AN_OVER = 1<<5, /* Bit 5: Auto-Negotiation Over */ + PHY_ST_REM_FLT = 1<<4, /* Bit 4: Remote Fault Condition Occured */ + PHY_ST_AN_CAP = 1<<3, /* Bit 3: Auto-Negotiation Capability */ + PHY_ST_LSYNC = 1<<2, /* Bit 2: Link Synchronized */ + PHY_ST_JAB_DET = 1<<1, /* Bit 1: Jabber Detected */ + PHY_ST_EXT_REG = 1<<0, /* Bit 0: Extended Register available */ +}; + +enum { + PHY_I1_OUI_MSK = 0x3f<<10, /* Bit 15..10: Organization Unique ID */ + PHY_I1_MOD_NUM = 0x3f<<4, /* Bit 9.. 4: Model Number */ + PHY_I1_REV_MSK = 0xf, /* Bit 3.. 0: Revision Number */ +}; + +/* different Broadcom PHY Ids */ +enum { + PHY_BCOM_ID1_A1 = 0x6041, + PHY_BCOM_ID1_B2 = 0x6043, + PHY_BCOM_ID1_C0 = 0x6044, + PHY_BCOM_ID1_C5 = 0x6047, +}; + +/* different Marvell PHY Ids */ +enum { + PHY_MARV_ID0_VAL= 0x0141, /* Marvell Unique Identifier */ + PHY_MARV_ID1_B0 = 0x0C23, /* Yukon (PHY 88E1011) */ + PHY_MARV_ID1_B2 = 0x0C25, /* Yukon-Plus (PHY 88E1011) */ + PHY_MARV_ID1_C2 = 0x0CC2, /* Yukon-EC (PHY 88E1111) */ + PHY_MARV_ID1_Y2 = 0x0C91, /* Yukon-2 (PHY 88E1112) */ +}; + +enum { + PHY_AN_NXT_PG = 1<<15, /* Bit 15: Request Next Page */ + PHY_X_AN_ACK = 1<<14, /* Bit 14: (ro) Acknowledge Received */ + PHY_X_AN_RFB = 3<<12,/* Bit 13..12: Remote Fault Bits */ + + PHY_X_AN_PAUSE = 3<<7,/* Bit 8.. 7: Pause Bits */ + PHY_X_AN_HD = 1<<6, /* Bit 6: Half Duplex */ + PHY_X_AN_FD = 1<<5, /* Bit 5: Full Duplex */ +}; + +enum { + PHY_B_AN_RF = 1<<13, /* Bit 13: Remote Fault */ + + PHY_B_AN_ASP = 1<<11, /* Bit 11: Asymmetric Pause */ + PHY_B_AN_PC = 1<<10, /* Bit 10: Pause Capable */ + PHY_B_AN_SEL = 0x1f, /* Bit 4..0: Selector Field, 00001=Ethernet*/ +}; + +enum { + PHY_L_AN_RF = 1<<13, /* Bit 13: Remote Fault */ + /* Bit 12: reserved */ + PHY_L_AN_ASP = 1<<11, /* Bit 11: Asymmetric Pause */ + PHY_L_AN_PC = 1<<10, /* Bit 10: Pause Capable */ + + PHY_L_AN_SEL = 0x1f, /* Bit 4..0: Selector Field, 00001=Ethernet*/ +}; + +/* PHY_NAT_AUNE_ADV 16 bit r/w Auto-Negotiation Advertisement */ +/* PHY_NAT_AUNE_LP 16 bit r/o Link Partner Ability Reg *****/ +/* PHY_AN_NXT_PG (see XMAC) Bit 15: Request Next Page */ +enum { + PHY_N_AN_RF = 1<<13, /* Bit 13: Remote Fault */ + + PHY_N_AN_100F = 1<<11, /* Bit 11: 100Base-T2 FD Support */ + PHY_N_AN_100H = 1<<10, /* Bit 10: 100Base-T2 HD Support */ + + PHY_N_AN_SEL = 0x1f, /* Bit 4..0: Selector Field, 00001=Ethernet*/ +}; + +/* field type definition for PHY_x_AN_SEL */ +enum { + PHY_SEL_TYPE = 1, /* 00001 = Ethernet */ +}; + +enum { + PHY_ANE_LP_NP = 1<<3, /* Bit 3: Link Partner can Next Page */ + PHY_ANE_LOC_NP = 1<<2, /* Bit 2: Local PHY can Next Page */ + PHY_ANE_RX_PG = 1<<1, /* Bit 1: Page Received */ +}; + +enum { + PHY_ANE_PAR_DF = 1<<4, /* Bit 4: Parallel Detection Fault */ + + PHY_ANE_LP_CAP = 1<<0, /* Bit 0: Link Partner Auto-Neg. Cap. */ +}; + +enum { + PHY_NP_MORE = 1<<15, /* Bit 15: More, Next Pages to follow */ + PHY_NP_ACK1 = 1<<14, /* Bit 14: (ro) Ack1, for receiving a message */ + PHY_NP_MSG_VAL = 1<<13, /* Bit 13: Message Page valid */ + PHY_NP_ACK2 = 1<<12, /* Bit 12: Ack2, comply with msg content */ + PHY_NP_TOG = 1<<11, /* Bit 11: Toggle Bit, ensure sync */ + PHY_NP_MSG = 0x07ff, /* Bit 10..0: Message from/to Link Partner */ +}; + +enum { + PHY_X_EX_FD = 1<<15, /* Bit 15: Device Supports Full Duplex */ + PHY_X_EX_HD = 1<<14, /* Bit 14: Device Supports Half Duplex */ +}; + +enum { + PHY_X_RS_PAUSE = 3<<7,/* Bit 8..7: selected Pause Mode */ + PHY_X_RS_HD = 1<<6, /* Bit 6: Half Duplex Mode selected */ + PHY_X_RS_FD = 1<<5, /* Bit 5: Full Duplex Mode selected */ + PHY_X_RS_ABLMIS = 1<<4, /* Bit 4: duplex or pause cap mismatch */ + PHY_X_RS_PAUMIS = 1<<3, /* Bit 3: pause capability mismatch */ +}; + +/** Remote Fault Bits (PHY_X_AN_RFB) encoding */ +enum { + X_RFB_OK = 0<<12,/* Bit 13..12 No errors, Link OK */ + X_RFB_LF = 1<<12, /* Bit 13..12 Link Failure */ + X_RFB_OFF = 2<<12,/* Bit 13..12 Offline */ + X_RFB_AN_ERR = 3<<12,/* Bit 13..12 Auto-Negotiation Error */ +}; + +/* Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding */ +enum { + PHY_X_P_NO_PAUSE = 0<<7,/* Bit 8..7: no Pause Mode */ + PHY_X_P_SYM_MD = 1<<7, /* Bit 8..7: symmetric Pause Mode */ + PHY_X_P_ASYM_MD = 2<<7,/* Bit 8..7: asymmetric Pause Mode */ + PHY_X_P_BOTH_MD = 3<<7,/* Bit 8..7: both Pause Mode */ +}; + + +/* Broadcom-Specific */ +/***** PHY_BCOM_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +enum { + PHY_B_1000C_TEST = 7<<13,/* Bit 15..13: Test Modes */ + PHY_B_1000C_MSE = 1<<12, /* Bit 12: Master/Slave Enable */ + PHY_B_1000C_MSC = 1<<11, /* Bit 11: M/S Configuration */ + PHY_B_1000C_RD = 1<<10, /* Bit 10: Repeater/DTE */ + PHY_B_1000C_AFD = 1<<9, /* Bit 9: Advertise Full Duplex */ + PHY_B_1000C_AHD = 1<<8, /* Bit 8: Advertise Half Duplex */ +}; + +/***** PHY_BCOM_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/ +/***** PHY_MARV_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/ +enum { + PHY_B_1000S_MSF = 1<<15, /* Bit 15: Master/Slave Fault */ + PHY_B_1000S_MSR = 1<<14, /* Bit 14: Master/Slave Result */ + PHY_B_1000S_LRS = 1<<13, /* Bit 13: Local Receiver Status */ + PHY_B_1000S_RRS = 1<<12, /* Bit 12: Remote Receiver Status */ + PHY_B_1000S_LP_FD = 1<<11, /* Bit 11: Link Partner can FD */ + PHY_B_1000S_LP_HD = 1<<10, /* Bit 10: Link Partner can HD */ + /* Bit 9..8: reserved */ + PHY_B_1000S_IEC = 0xff, /* Bit 7..0: Idle Error Count */ +}; + +/***** PHY_BCOM_EXT_STAT 16 bit r/o Extended Status Register *****/ +enum { + PHY_B_ES_X_FD_CAP = 1<<15, /* Bit 15: 1000Base-X FD capable */ + PHY_B_ES_X_HD_CAP = 1<<14, /* Bit 14: 1000Base-X HD capable */ + PHY_B_ES_T_FD_CAP = 1<<13, /* Bit 13: 1000Base-T FD capable */ + PHY_B_ES_T_HD_CAP = 1<<12, /* Bit 12: 1000Base-T HD capable */ +}; + +/***** PHY_BCOM_P_EXT_CTRL 16 bit r/w PHY Extended Control Reg *****/ +enum { + PHY_B_PEC_MAC_PHY = 1<<15, /* Bit 15: 10BIT/GMI-Interface */ + PHY_B_PEC_DIS_CROSS = 1<<14, /* Bit 14: Disable MDI Crossover */ + PHY_B_PEC_TX_DIS = 1<<13, /* Bit 13: Tx output Disabled */ + PHY_B_PEC_INT_DIS = 1<<12, /* Bit 12: Interrupts Disabled */ + PHY_B_PEC_F_INT = 1<<11, /* Bit 11: Force Interrupt */ + PHY_B_PEC_BY_45 = 1<<10, /* Bit 10: Bypass 4B5B-Decoder */ + PHY_B_PEC_BY_SCR = 1<<9, /* Bit 9: Bypass Scrambler */ + PHY_B_PEC_BY_MLT3 = 1<<8, /* Bit 8: Bypass MLT3 Encoder */ + PHY_B_PEC_BY_RXA = 1<<7, /* Bit 7: Bypass Rx Alignm. */ + PHY_B_PEC_RES_SCR = 1<<6, /* Bit 6: Reset Scrambler */ + PHY_B_PEC_EN_LTR = 1<<5, /* Bit 5: Ena LED Traffic Mode */ + PHY_B_PEC_LED_ON = 1<<4, /* Bit 4: Force LED's on */ + PHY_B_PEC_LED_OFF = 1<<3, /* Bit 3: Force LED's off */ + PHY_B_PEC_EX_IPG = 1<<2, /* Bit 2: Extend Tx IPG Mode */ + PHY_B_PEC_3_LED = 1<<1, /* Bit 1: Three Link LED mode */ + PHY_B_PEC_HIGH_LA = 1<<0, /* Bit 0: GMII FIFO Elasticy */ +}; + +/***** PHY_BCOM_P_EXT_STAT 16 bit r/o PHY Extended Status Reg *****/ +enum { + PHY_B_PES_CROSS_STAT = 1<<13, /* Bit 13: MDI Crossover Status */ + PHY_B_PES_INT_STAT = 1<<12, /* Bit 12: Interrupt Status */ + PHY_B_PES_RRS = 1<<11, /* Bit 11: Remote Receiver Stat. */ + PHY_B_PES_LRS = 1<<10, /* Bit 10: Local Receiver Stat. */ + PHY_B_PES_LOCKED = 1<<9, /* Bit 9: Locked */ + PHY_B_PES_LS = 1<<8, /* Bit 8: Link Status */ + PHY_B_PES_RF = 1<<7, /* Bit 7: Remote Fault */ + PHY_B_PES_CE_ER = 1<<6, /* Bit 6: Carrier Ext Error */ + PHY_B_PES_BAD_SSD = 1<<5, /* Bit 5: Bad SSD */ + PHY_B_PES_BAD_ESD = 1<<4, /* Bit 4: Bad ESD */ + PHY_B_PES_RX_ER = 1<<3, /* Bit 3: Receive Error */ + PHY_B_PES_TX_ER = 1<<2, /* Bit 2: Transmit Error */ + PHY_B_PES_LOCK_ER = 1<<1, /* Bit 1: Lock Error */ + PHY_B_PES_MLT3_ER = 1<<0, /* Bit 0: MLT3 code Error */ +}; + +/***** PHY_BCOM_FC_CTR 16 bit r/w False Carrier Counter *****/ +enum { + PHY_B_FC_CTR = 0xff, /* Bit 7..0: False Carrier Counter */ + +/***** PHY_BCOM_RNO_CTR 16 bit r/w Receive NOT_OK Counter *****/ + PHY_B_RC_LOC_MSK = 0xff00, /* Bit 15..8: Local Rx NOT_OK cnt */ + PHY_B_RC_REM_MSK = 0x00ff, /* Bit 7..0: Remote Rx NOT_OK cnt */ + +/***** PHY_BCOM_AUX_CTRL 16 bit r/w Auxiliary Control Reg *****/ + PHY_B_AC_L_SQE = 1<<15, /* Bit 15: Low Squelch */ + PHY_B_AC_LONG_PACK = 1<<14, /* Bit 14: Rx Long Packets */ + PHY_B_AC_ER_CTRL = 3<<12,/* Bit 13..12: Edgerate Control */ + /* Bit 11: reserved */ + PHY_B_AC_TX_TST = 1<<10, /* Bit 10: Tx test bit, always 1 */ + /* Bit 9.. 8: reserved */ + PHY_B_AC_DIS_PRF = 1<<7, /* Bit 7: dis part resp filter */ + /* Bit 6: reserved */ + PHY_B_AC_DIS_PM = 1<<5, /* Bit 5: dis power management */ + /* Bit 4: reserved */ + PHY_B_AC_DIAG = 1<<3, /* Bit 3: Diagnostic Mode */ +}; + +/***** PHY_BCOM_AUX_STAT 16 bit r/o Auxiliary Status Reg *****/ +enum { + PHY_B_AS_AN_C = 1<<15, /* Bit 15: AutoNeg complete */ + PHY_B_AS_AN_CA = 1<<14, /* Bit 14: AN Complete Ack */ + PHY_B_AS_ANACK_D = 1<<13, /* Bit 13: AN Ack Detect */ + PHY_B_AS_ANAB_D = 1<<12, /* Bit 12: AN Ability Detect */ + PHY_B_AS_NPW = 1<<11, /* Bit 11: AN Next Page Wait */ + PHY_B_AS_AN_RES_MSK = 7<<8,/* Bit 10..8: AN HDC */ + PHY_B_AS_PDF = 1<<7, /* Bit 7: Parallel Detect. Fault */ + PHY_B_AS_RF = 1<<6, /* Bit 6: Remote Fault */ + PHY_B_AS_ANP_R = 1<<5, /* Bit 5: AN Page Received */ + PHY_B_AS_LP_ANAB = 1<<4, /* Bit 4: LP AN Ability */ + PHY_B_AS_LP_NPAB = 1<<3, /* Bit 3: LP Next Page Ability */ + PHY_B_AS_LS = 1<<2, /* Bit 2: Link Status */ + PHY_B_AS_PRR = 1<<1, /* Bit 1: Pause Resolution-Rx */ + PHY_B_AS_PRT = 1<<0, /* Bit 0: Pause Resolution-Tx */ +}; +#define PHY_B_AS_PAUSE_MSK (PHY_B_AS_PRR | PHY_B_AS_PRT) + +/***** PHY_BCOM_INT_STAT 16 bit r/o Interrupt Status Reg *****/ +/***** PHY_BCOM_INT_MASK 16 bit r/w Interrupt Mask Reg *****/ +enum { + PHY_B_IS_PSE = 1<<14, /* Bit 14: Pair Swap Error */ + PHY_B_IS_MDXI_SC = 1<<13, /* Bit 13: MDIX Status Change */ + PHY_B_IS_HCT = 1<<12, /* Bit 12: counter above 32k */ + PHY_B_IS_LCT = 1<<11, /* Bit 11: counter above 128 */ + PHY_B_IS_AN_PR = 1<<10, /* Bit 10: Page Received */ + PHY_B_IS_NO_HDCL = 1<<9, /* Bit 9: No HCD Link */ + PHY_B_IS_NO_HDC = 1<<8, /* Bit 8: No HCD */ + PHY_B_IS_NEG_USHDC = 1<<7, /* Bit 7: Negotiated Unsup. HCD */ + PHY_B_IS_SCR_S_ER = 1<<6, /* Bit 6: Scrambler Sync Error */ + PHY_B_IS_RRS_CHANGE = 1<<5, /* Bit 5: Remote Rx Stat Change */ + PHY_B_IS_LRS_CHANGE = 1<<4, /* Bit 4: Local Rx Stat Change */ + PHY_B_IS_DUP_CHANGE = 1<<3, /* Bit 3: Duplex Mode Change */ + PHY_B_IS_LSP_CHANGE = 1<<2, /* Bit 2: Link Speed Change */ + PHY_B_IS_LST_CHANGE = 1<<1, /* Bit 1: Link Status Changed */ + PHY_B_IS_CRC_ER = 1<<0, /* Bit 0: CRC Error */ +}; +#define PHY_B_DEF_MSK (~(PHY_B_IS_AN_PR | PHY_B_IS_LST_CHANGE)) + +/* Pause Bits (PHY_B_AN_ASP and PHY_B_AN_PC) encoding */ +enum { + PHY_B_P_NO_PAUSE = 0<<10,/* Bit 11..10: no Pause Mode */ + PHY_B_P_SYM_MD = 1<<10, /* Bit 11..10: symmetric Pause Mode */ + PHY_B_P_ASYM_MD = 2<<10,/* Bit 11..10: asymmetric Pause Mode */ + PHY_B_P_BOTH_MD = 3<<10,/* Bit 11..10: both Pause Mode */ +}; +/* + * Resolved Duplex mode and Capabilities (Aux Status Summary Reg) + */ +enum { + PHY_B_RES_1000FD = 7<<8,/* Bit 10..8: 1000Base-T Full Dup. */ + PHY_B_RES_1000HD = 6<<8,/* Bit 10..8: 1000Base-T Half Dup. */ +}; + +/* + * Level One-Specific + */ +/***** PHY_LONE_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +enum { + PHY_L_1000C_TEST = 7<<13,/* Bit 15..13: Test Modes */ + PHY_L_1000C_MSE = 1<<12, /* Bit 12: Master/Slave Enable */ + PHY_L_1000C_MSC = 1<<11, /* Bit 11: M/S Configuration */ + PHY_L_1000C_RD = 1<<10, /* Bit 10: Repeater/DTE */ + PHY_L_1000C_AFD = 1<<9, /* Bit 9: Advertise Full Duplex */ + PHY_L_1000C_AHD = 1<<8, /* Bit 8: Advertise Half Duplex */ +}; + +/***** PHY_LONE_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/ +enum { + PHY_L_1000S_MSF = 1<<15, /* Bit 15: Master/Slave Fault */ + PHY_L_1000S_MSR = 1<<14, /* Bit 14: Master/Slave Result */ + PHY_L_1000S_LRS = 1<<13, /* Bit 13: Local Receiver Status */ + PHY_L_1000S_RRS = 1<<12, /* Bit 12: Remote Receiver Status */ + PHY_L_1000S_LP_FD = 1<<11, /* Bit 11: Link Partner can FD */ + PHY_L_1000S_LP_HD = 1<<10, /* Bit 10: Link Partner can HD */ + + PHY_L_1000S_IEC = 0xff, /* Bit 7..0: Idle Error Count */ + +/***** PHY_LONE_EXT_STAT 16 bit r/o Extended Status Register *****/ + PHY_L_ES_X_FD_CAP = 1<<15, /* Bit 15: 1000Base-X FD capable */ + PHY_L_ES_X_HD_CAP = 1<<14, /* Bit 14: 1000Base-X HD capable */ + PHY_L_ES_T_FD_CAP = 1<<13, /* Bit 13: 1000Base-T FD capable */ + PHY_L_ES_T_HD_CAP = 1<<12, /* Bit 12: 1000Base-T HD capable */ +}; + +/***** PHY_LONE_PORT_CFG 16 bit r/w Port Configuration Reg *****/ +enum { + PHY_L_PC_REP_MODE = 1<<15, /* Bit 15: Repeater Mode */ + + PHY_L_PC_TX_DIS = 1<<13, /* Bit 13: Tx output Disabled */ + PHY_L_PC_BY_SCR = 1<<12, /* Bit 12: Bypass Scrambler */ + PHY_L_PC_BY_45 = 1<<11, /* Bit 11: Bypass 4B5B-Decoder */ + PHY_L_PC_JAB_DIS = 1<<10, /* Bit 10: Jabber Disabled */ + PHY_L_PC_SQE = 1<<9, /* Bit 9: Enable Heartbeat */ + PHY_L_PC_TP_LOOP = 1<<8, /* Bit 8: TP Loopback */ + PHY_L_PC_SSS = 1<<7, /* Bit 7: Smart Speed Selection */ + PHY_L_PC_FIFO_SIZE = 1<<6, /* Bit 6: FIFO Size */ + PHY_L_PC_PRE_EN = 1<<5, /* Bit 5: Preamble Enable */ + PHY_L_PC_CIM = 1<<4, /* Bit 4: Carrier Integrity Mon */ + PHY_L_PC_10_SER = 1<<3, /* Bit 3: Use Serial Output */ + PHY_L_PC_ANISOL = 1<<2, /* Bit 2: Unisolate Port */ + PHY_L_PC_TEN_BIT = 1<<1, /* Bit 1: 10bit iface mode on */ + PHY_L_PC_ALTCLOCK = 1<<0, /* Bit 0: (ro) ALTCLOCK Mode on */ +}; + +/***** PHY_LONE_Q_STAT 16 bit r/o Quick Status Reg *****/ +enum { + PHY_L_QS_D_RATE = 3<<14,/* Bit 15..14: Data Rate */ + PHY_L_QS_TX_STAT = 1<<13, /* Bit 13: Transmitting */ + PHY_L_QS_RX_STAT = 1<<12, /* Bit 12: Receiving */ + PHY_L_QS_COL_STAT = 1<<11, /* Bit 11: Collision */ + PHY_L_QS_L_STAT = 1<<10, /* Bit 10: Link is up */ + PHY_L_QS_DUP_MOD = 1<<9, /* Bit 9: Full/Half Duplex */ + PHY_L_QS_AN = 1<<8, /* Bit 8: AutoNeg is On */ + PHY_L_QS_AN_C = 1<<7, /* Bit 7: AN is Complete */ + PHY_L_QS_LLE = 7<<4,/* Bit 6..4: Line Length Estim. */ + PHY_L_QS_PAUSE = 1<<3, /* Bit 3: LP advertised Pause */ + PHY_L_QS_AS_PAUSE = 1<<2, /* Bit 2: LP adv. asym. Pause */ + PHY_L_QS_ISOLATE = 1<<1, /* Bit 1: CIM Isolated */ + PHY_L_QS_EVENT = 1<<0, /* Bit 0: Event has occurred */ +}; + +/***** PHY_LONE_INT_ENAB 16 bit r/w Interrupt Enable Reg *****/ +/***** PHY_LONE_INT_STAT 16 bit r/o Interrupt Status Reg *****/ +enum { + PHY_L_IS_AN_F = 1<<13, /* Bit 13: Auto-Negotiation fault */ + PHY_L_IS_CROSS = 1<<11, /* Bit 11: Crossover used */ + PHY_L_IS_POL = 1<<10, /* Bit 10: Polarity correct. used */ + PHY_L_IS_SS = 1<<9, /* Bit 9: Smart Speed Downgrade */ + PHY_L_IS_CFULL = 1<<8, /* Bit 8: Counter Full */ + PHY_L_IS_AN_C = 1<<7, /* Bit 7: AutoNeg Complete */ + PHY_L_IS_SPEED = 1<<6, /* Bit 6: Speed Changed */ + PHY_L_IS_DUP = 1<<5, /* Bit 5: Duplex Changed */ + PHY_L_IS_LS = 1<<4, /* Bit 4: Link Status Changed */ + PHY_L_IS_ISOL = 1<<3, /* Bit 3: Isolate Occured */ + PHY_L_IS_MDINT = 1<<2, /* Bit 2: (ro) STAT: MII Int Pending */ + PHY_L_IS_INTEN = 1<<1, /* Bit 1: ENAB: Enable IRQs */ + PHY_L_IS_FORCE = 1<<0, /* Bit 0: ENAB: Force Interrupt */ +}; + +/* int. mask */ +#define PHY_L_DEF_MSK (PHY_L_IS_LS | PHY_L_IS_ISOL | PHY_L_IS_INTEN) + +/***** PHY_LONE_LED_CFG 16 bit r/w LED Configuration Reg *****/ +enum { + PHY_L_LC_LEDC = 3<<14,/* Bit 15..14: Col/Blink/On/Off */ + PHY_L_LC_LEDR = 3<<12,/* Bit 13..12: Rx/Blink/On/Off */ + PHY_L_LC_LEDT = 3<<10,/* Bit 11..10: Tx/Blink/On/Off */ + PHY_L_LC_LEDG = 3<<8,/* Bit 9..8: Giga/Blink/On/Off */ + PHY_L_LC_LEDS = 3<<6,/* Bit 7..6: 10-100/Blink/On/Off */ + PHY_L_LC_LEDL = 3<<4,/* Bit 5..4: Link/Blink/On/Off */ + PHY_L_LC_LEDF = 3<<2,/* Bit 3..2: Duplex/Blink/On/Off */ + PHY_L_LC_PSTRECH= 1<<1, /* Bit 1: Strech LED Pulses */ + PHY_L_LC_FREQ = 1<<0, /* Bit 0: 30/100 ms */ +}; + +/***** PHY_LONE_PORT_CTRL 16 bit r/w Port Control Reg *****/ +enum { + PHY_L_PC_TX_TCLK = 1<<15, /* Bit 15: Enable TX_TCLK */ + PHY_L_PC_ALT_NP = 1<<13, /* Bit 14: Alternate Next Page */ + PHY_L_PC_GMII_ALT= 1<<12, /* Bit 13: Alternate GMII driver */ + PHY_L_PC_TEN_CRS = 1<<10, /* Bit 10: Extend CRS*/ +}; + +/***** PHY_LONE_CIM 16 bit r/o CIM Reg *****/ +enum { + PHY_L_CIM_ISOL = 0xff<<8,/* Bit 15..8: Isolate Count */ + PHY_L_CIM_FALSE_CAR = 0xff, /* Bit 7..0: False Carrier Count */ +}; + +/* + * Pause Bits (PHY_L_AN_ASP and PHY_L_AN_PC) encoding + */ +enum { + PHY_L_P_NO_PAUSE= 0<<10,/* Bit 11..10: no Pause Mode */ + PHY_L_P_SYM_MD = 1<<10, /* Bit 11..10: symmetric Pause Mode */ + PHY_L_P_ASYM_MD = 2<<10,/* Bit 11..10: asymmetric Pause Mode */ + PHY_L_P_BOTH_MD = 3<<10,/* Bit 11..10: both Pause Mode */ +}; + +/* + * National-Specific + */ +/***** PHY_NAT_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +enum { + PHY_N_1000C_TEST= 7<<13,/* Bit 15..13: Test Modes */ + PHY_N_1000C_MSE = 1<<12, /* Bit 12: Master/Slave Enable */ + PHY_N_1000C_MSC = 1<<11, /* Bit 11: M/S Configuration */ + PHY_N_1000C_RD = 1<<10, /* Bit 10: Repeater/DTE */ + PHY_N_1000C_AFD = 1<<9, /* Bit 9: Advertise Full Duplex */ + PHY_N_1000C_AHD = 1<<8, /* Bit 8: Advertise Half Duplex */ + PHY_N_1000C_APC = 1<<7, /* Bit 7: Asymmetric Pause Cap. */}; + + +/***** PHY_NAT_1000T_STAT 16 bit r/o 1000Base-T Status Reg *****/ +enum { + PHY_N_1000S_MSF = 1<<15, /* Bit 15: Master/Slave Fault */ + PHY_N_1000S_MSR = 1<<14, /* Bit 14: Master/Slave Result */ + PHY_N_1000S_LRS = 1<<13, /* Bit 13: Local Receiver Status */ + PHY_N_1000S_RRS = 1<<12, /* Bit 12: Remote Receiver Status*/ + PHY_N_1000S_LP_FD= 1<<11, /* Bit 11: Link Partner can FD */ + PHY_N_1000S_LP_HD= 1<<10, /* Bit 10: Link Partner can HD */ + PHY_N_1000C_LP_APC= 1<<9, /* Bit 9: LP Asym. Pause Cap. */ + PHY_N_1000S_IEC = 0xff, /* Bit 7..0: Idle Error Count */ +}; + +/***** PHY_NAT_EXT_STAT 16 bit r/o Extended Status Register *****/ +enum { + PHY_N_ES_X_FD_CAP= 1<<15, /* Bit 15: 1000Base-X FD capable */ + PHY_N_ES_X_HD_CAP= 1<<14, /* Bit 14: 1000Base-X HD capable */ + PHY_N_ES_T_FD_CAP= 1<<13, /* Bit 13: 1000Base-T FD capable */ + PHY_N_ES_T_HD_CAP= 1<<12, /* Bit 12: 1000Base-T HD capable */ +}; + +/** Marvell-Specific */ +enum { + PHY_M_AN_NXT_PG = 1<<15, /* Request Next Page */ + PHY_M_AN_ACK = 1<<14, /* (ro) Acknowledge Received */ + PHY_M_AN_RF = 1<<13, /* Remote Fault */ + + PHY_M_AN_ASP = 1<<11, /* Asymmetric Pause */ + PHY_M_AN_PC = 1<<10, /* MAC Pause implemented */ + PHY_M_AN_100_T4 = 1<<9, /* Not cap. 100Base-T4 (always 0) */ + PHY_M_AN_100_FD = 1<<8, /* Advertise 100Base-TX Full Duplex */ + PHY_M_AN_100_HD = 1<<7, /* Advertise 100Base-TX Half Duplex */ + PHY_M_AN_10_FD = 1<<6, /* Advertise 10Base-TX Full Duplex */ + PHY_M_AN_10_HD = 1<<5, /* Advertise 10Base-TX Half Duplex */ + PHY_M_AN_SEL_MSK =0x1f<<4, /* Bit 4.. 0: Selector Field Mask */ +}; + +/* special defines for FIBER (88E1011S only) */ +enum { + PHY_M_AN_ASP_X = 1<<8, /* Asymmetric Pause */ + PHY_M_AN_PC_X = 1<<7, /* MAC Pause implemented */ + PHY_M_AN_1000X_AHD = 1<<6, /* Advertise 10000Base-X Half Duplex */ + PHY_M_AN_1000X_AFD = 1<<5, /* Advertise 10000Base-X Full Duplex */ +}; + +/* Pause Bits (PHY_M_AN_ASP_X and PHY_M_AN_PC_X) encoding */ +enum { + PHY_M_P_NO_PAUSE_X = 0<<7,/* Bit 8.. 7: no Pause Mode */ + PHY_M_P_SYM_MD_X = 1<<7, /* Bit 8.. 7: symmetric Pause Mode */ + PHY_M_P_ASYM_MD_X = 2<<7,/* Bit 8.. 7: asymmetric Pause Mode */ + PHY_M_P_BOTH_MD_X = 3<<7,/* Bit 8.. 7: both Pause Mode */ +}; + +/***** PHY_MARV_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/ +enum { + PHY_M_1000C_TEST = 7<<13,/* Bit 15..13: Test Modes */ + PHY_M_1000C_MSE = 1<<12, /* Manual Master/Slave Enable */ + PHY_M_1000C_MSC = 1<<11, /* M/S Configuration (1=Master) */ + PHY_M_1000C_MPD = 1<<10, /* Multi-Port Device */ + PHY_M_1000C_AFD = 1<<9, /* Advertise Full Duplex */ + PHY_M_1000C_AHD = 1<<8, /* Advertise Half Duplex */ +}; + +/***** PHY_MARV_PHY_CTRL 16 bit r/w PHY Specific Ctrl Reg *****/ +enum { + PHY_M_PC_TX_FFD_MSK = 3<<14,/* Bit 15..14: Tx FIFO Depth Mask */ + PHY_M_PC_RX_FFD_MSK = 3<<12,/* Bit 13..12: Rx FIFO Depth Mask */ + PHY_M_PC_ASS_CRS_TX = 1<<11, /* Assert CRS on Transmit */ + PHY_M_PC_FL_GOOD = 1<<10, /* Force Link Good */ + PHY_M_PC_EN_DET_MSK = 3<<8,/* Bit 9.. 8: Energy Detect Mask */ + PHY_M_PC_ENA_EXT_D = 1<<7, /* Enable Ext. Distance (10BT) */ + PHY_M_PC_MDIX_MSK = 3<<5,/* Bit 6.. 5: MDI/MDIX Config. Mask */ + PHY_M_PC_DIS_125CLK = 1<<4, /* Disable 125 CLK */ + PHY_M_PC_MAC_POW_UP = 1<<3, /* MAC Power up */ + PHY_M_PC_SQE_T_ENA = 1<<2, /* SQE Test Enabled */ + PHY_M_PC_POL_R_DIS = 1<<1, /* Polarity Reversal Disabled */ + PHY_M_PC_DIS_JABBER = 1<<0, /* Disable Jabber */ +}; + +enum { + PHY_M_PC_EN_DET = 2<<8, /* Energy Detect (Mode 1) */ + PHY_M_PC_EN_DET_PLUS = 3<<8, /* Energy Detect Plus (Mode 2) */ +}; + +#define PHY_M_PC_MDI_XMODE(x) (((x)<<5) & PHY_M_PC_MDIX_MSK) + +enum { + PHY_M_PC_MAN_MDI = 0, /* 00 = Manual MDI configuration */ + PHY_M_PC_MAN_MDIX = 1, /* 01 = Manual MDIX configuration */ + PHY_M_PC_ENA_AUTO = 3, /* 11 = Enable Automatic Crossover */ +}; + +/* for 10/100 Fast Ethernet PHY (88E3082 only) */ +enum { + PHY_M_PC_ENA_DTE_DT = 1<<15, /* Enable Data Terminal Equ. (DTE) Detect */ + PHY_M_PC_ENA_ENE_DT = 1<<14, /* Enable Energy Detect (sense & pulse) */ + PHY_M_PC_DIS_NLP_CK = 1<<13, /* Disable Normal Link Puls (NLP) Check */ + PHY_M_PC_ENA_LIP_NP = 1<<12, /* Enable Link Partner Next Page Reg. */ + PHY_M_PC_DIS_NLP_GN = 1<<11, /* Disable Normal Link Puls Generation */ + + PHY_M_PC_DIS_SCRAMB = 1<<9, /* Disable Scrambler */ + PHY_M_PC_DIS_FEFI = 1<<8, /* Disable Far End Fault Indic. (FEFI) */ + + PHY_M_PC_SH_TP_SEL = 1<<6, /* Shielded Twisted Pair Select */ + PHY_M_PC_RX_FD_MSK = 3<<2,/* Bit 3.. 2: Rx FIFO Depth Mask */ +}; + +/***** PHY_MARV_PHY_STAT 16 bit r/o PHY Specific Status Reg *****/ +enum { + PHY_M_PS_SPEED_MSK = 3<<14, /* Bit 15..14: Speed Mask */ + PHY_M_PS_SPEED_1000 = 1<<15, /* 10 = 1000 Mbps */ + PHY_M_PS_SPEED_100 = 1<<14, /* 01 = 100 Mbps */ + PHY_M_PS_SPEED_10 = 0, /* 00 = 10 Mbps */ + PHY_M_PS_FULL_DUP = 1<<13, /* Full Duplex */ + PHY_M_PS_PAGE_REC = 1<<12, /* Page Received */ + PHY_M_PS_SPDUP_RES = 1<<11, /* Speed & Duplex Resolved */ + PHY_M_PS_LINK_UP = 1<<10, /* Link Up */ + PHY_M_PS_CABLE_MSK = 7<<7, /* Bit 9.. 7: Cable Length Mask */ + PHY_M_PS_MDI_X_STAT = 1<<6, /* MDI Crossover Stat (1=MDIX) */ + PHY_M_PS_DOWNS_STAT = 1<<5, /* Downshift Status (1=downsh.) */ + PHY_M_PS_ENDET_STAT = 1<<4, /* Energy Detect Status (1=act) */ + PHY_M_PS_TX_P_EN = 1<<3, /* Tx Pause Enabled */ + PHY_M_PS_RX_P_EN = 1<<2, /* Rx Pause Enabled */ + PHY_M_PS_POL_REV = 1<<1, /* Polarity Reversed */ + PHY_M_PS_JABBER = 1<<0, /* Jabber */ +}; + +#define PHY_M_PS_PAUSE_MSK (PHY_M_PS_TX_P_EN | PHY_M_PS_RX_P_EN) + +/* for 10/100 Fast Ethernet PHY (88E3082 only) */ +enum { + PHY_M_PS_DTE_DETECT = 1<<15, /* Data Terminal Equipment (DTE) Detected */ + PHY_M_PS_RES_SPEED = 1<<14, /* Resolved Speed (1=100 Mbps, 0=10 Mbps */ +}; + +enum { + PHY_M_IS_AN_ERROR = 1<<15, /* Auto-Negotiation Error */ + PHY_M_IS_LSP_CHANGE = 1<<14, /* Link Speed Changed */ + PHY_M_IS_DUP_CHANGE = 1<<13, /* Duplex Mode Changed */ + PHY_M_IS_AN_PR = 1<<12, /* Page Received */ + PHY_M_IS_AN_COMPL = 1<<11, /* Auto-Negotiation Completed */ + PHY_M_IS_LST_CHANGE = 1<<10, /* Link Status Changed */ + PHY_M_IS_SYMB_ERROR = 1<<9, /* Symbol Error */ + PHY_M_IS_FALSE_CARR = 1<<8, /* False Carrier */ + PHY_M_IS_FIFO_ERROR = 1<<7, /* FIFO Overflow/Underrun Error */ + PHY_M_IS_MDI_CHANGE = 1<<6, /* MDI Crossover Changed */ + PHY_M_IS_DOWNSH_DET = 1<<5, /* Downshift Detected */ + PHY_M_IS_END_CHANGE = 1<<4, /* Energy Detect Changed */ + + PHY_M_IS_DTE_CHANGE = 1<<2, /* DTE Power Det. Status Changed */ + PHY_M_IS_POL_CHANGE = 1<<1, /* Polarity Changed */ + PHY_M_IS_JABBER = 1<<0, /* Jabber */ +}; + +#define PHY_M_DEF_MSK ( PHY_M_IS_AN_ERROR | PHY_M_IS_LSP_CHANGE | \ + PHY_M_IS_LST_CHANGE | PHY_M_IS_FIFO_ERROR) + +/***** PHY_MARV_EXT_CTRL 16 bit r/w Ext. PHY Specific Ctrl *****/ +enum { + PHY_M_EC_ENA_BC_EXT = 1<<15, /* Enable Block Carr. Ext. (88E1111 only) */ + PHY_M_EC_ENA_LIN_LB = 1<<14, /* Enable Line Loopback (88E1111 only) */ + + PHY_M_EC_DIS_LINK_P = 1<<12, /* Disable Link Pulses (88E1111 only) */ + PHY_M_EC_M_DSC_MSK = 3<<10, /* Bit 11..10: Master Downshift Counter */ + /* (88E1011 only) */ + PHY_M_EC_S_DSC_MSK = 3<<8,/* Bit 9.. 8: Slave Downshift Counter */ + /* (88E1011 only) */ + PHY_M_EC_M_DSC_MSK2 = 7<<9,/* Bit 11.. 9: Master Downshift Counter */ + /* (88E1111 only) */ + PHY_M_EC_DOWN_S_ENA = 1<<8, /* Downshift Enable (88E1111 only) */ + /* !!! Errata in spec. (1 = disable) */ + PHY_M_EC_RX_TIM_CT = 1<<7, /* RGMII Rx Timing Control*/ + PHY_M_EC_MAC_S_MSK = 7<<4,/* Bit 6.. 4: Def. MAC interface speed */ + PHY_M_EC_FIB_AN_ENA = 1<<3, /* Fiber Auto-Neg. Enable (88E1011S only) */ + PHY_M_EC_DTE_D_ENA = 1<<2, /* DTE Detect Enable (88E1111 only) */ + PHY_M_EC_TX_TIM_CT = 1<<1, /* RGMII Tx Timing Control */ + PHY_M_EC_TRANS_DIS = 1<<0, /* Transmitter Disable (88E1111 only) */}; + +#define PHY_M_EC_M_DSC(x) ((x)<<10) /* 00=1x; 01=2x; 10=3x; 11=4x */ +#define PHY_M_EC_S_DSC(x) ((x)<<8) /* 00=dis; 01=1x; 10=2x; 11=3x */ +#define PHY_M_EC_MAC_S(x) ((x)<<4) /* 01X=0; 110=2.5; 111=25 (MHz) */ + +#define PHY_M_EC_M_DSC_2(x) ((x)<<9) /* 000=1x; 001=2x; 010=3x; 011=4x */ + /* 100=5x; 101=6x; 110=7x; 111=8x */ +enum { + MAC_TX_CLK_0_MHZ = 2, + MAC_TX_CLK_2_5_MHZ = 6, + MAC_TX_CLK_25_MHZ = 7, +}; + +/***** PHY_MARV_LED_CTRL 16 bit r/w LED Control Reg *****/ +enum { + PHY_M_LEDC_DIS_LED = 1<<15, /* Disable LED */ + PHY_M_LEDC_PULS_MSK = 7<<12,/* Bit 14..12: Pulse Stretch Mask */ + PHY_M_LEDC_F_INT = 1<<11, /* Force Interrupt */ + PHY_M_LEDC_BL_R_MSK = 7<<8,/* Bit 10.. 8: Blink Rate Mask */ + PHY_M_LEDC_DP_C_LSB = 1<<7, /* Duplex Control (LSB, 88E1111 only) */ + PHY_M_LEDC_TX_C_LSB = 1<<6, /* Tx Control (LSB, 88E1111 only) */ + PHY_M_LEDC_LK_C_MSK = 7<<3,/* Bit 5.. 3: Link Control Mask */ + /* (88E1111 only) */ +}; + +enum { + PHY_M_LEDC_LINK_MSK = 3<<3,/* Bit 4.. 3: Link Control Mask */ + /* (88E1011 only) */ + PHY_M_LEDC_DP_CTRL = 1<<2, /* Duplex Control */ + PHY_M_LEDC_DP_C_MSB = 1<<2, /* Duplex Control (MSB, 88E1111 only) */ + PHY_M_LEDC_RX_CTRL = 1<<1, /* Rx Activity / Link */ + PHY_M_LEDC_TX_CTRL = 1<<0, /* Tx Activity / Link */ + PHY_M_LEDC_TX_C_MSB = 1<<0, /* Tx Control (MSB, 88E1111 only) */ +}; + +#define PHY_M_LED_PULS_DUR(x) ( ((x)<<12) & PHY_M_LEDC_PULS_MSK) + +enum { + PULS_NO_STR = 0,/* no pulse stretching */ + PULS_21MS = 1,/* 21 ms to 42 ms */ + PULS_42MS = 2,/* 42 ms to 84 ms */ + PULS_84MS = 3,/* 84 ms to 170 ms */ + PULS_170MS = 4,/* 170 ms to 340 ms */ + PULS_340MS = 5,/* 340 ms to 670 ms */ + PULS_670MS = 6,/* 670 ms to 1.3 s */ + PULS_1300MS = 7,/* 1.3 s to 2.7 s */ +}; + +#define PHY_M_LED_BLINK_RT(x) ( ((x)<<8) & PHY_M_LEDC_BL_R_MSK) + +enum { + BLINK_42MS = 0,/* 42 ms */ + BLINK_84MS = 1,/* 84 ms */ + BLINK_170MS = 2,/* 170 ms */ + BLINK_340MS = 3,/* 340 ms */ + BLINK_670MS = 4,/* 670 ms */ +}; + +/***** PHY_MARV_LED_OVER 16 bit r/w Manual LED Override Reg *****/ +#define PHY_M_LED_MO_SGMII(x) ((x)<<14) /* Bit 15..14: SGMII AN Timer */ + /* Bit 13..12: reserved */ +#define PHY_M_LED_MO_DUP(x) ((x)<<10) /* Bit 11..10: Duplex */ +#define PHY_M_LED_MO_10(x) ((x)<<8) /* Bit 9.. 8: Link 10 */ +#define PHY_M_LED_MO_100(x) ((x)<<6) /* Bit 7.. 6: Link 100 */ +#define PHY_M_LED_MO_1000(x) ((x)<<4) /* Bit 5.. 4: Link 1000 */ +#define PHY_M_LED_MO_RX(x) ((x)<<2) /* Bit 3.. 2: Rx */ +#define PHY_M_LED_MO_TX(x) ((x)<<0) /* Bit 1.. 0: Tx */ + +enum { + MO_LED_NORM = 0, + MO_LED_BLINK = 1, + MO_LED_OFF = 2, + MO_LED_ON = 3, +}; + +/***** PHY_MARV_EXT_CTRL_2 16 bit r/w Ext. PHY Specific Ctrl 2 *****/ +enum { + PHY_M_EC2_FI_IMPED = 1<<6, /* Fiber Input Impedance */ + PHY_M_EC2_FO_IMPED = 1<<5, /* Fiber Output Impedance */ + PHY_M_EC2_FO_M_CLK = 1<<4, /* Fiber Mode Clock Enable */ + PHY_M_EC2_FO_BOOST = 1<<3, /* Fiber Output Boost */ + PHY_M_EC2_FO_AM_MSK = 7,/* Bit 2.. 0: Fiber Output Amplitude */ +}; + +/***** PHY_MARV_EXT_P_STAT 16 bit r/w Ext. PHY Specific Status *****/ +enum { + PHY_M_FC_AUTO_SEL = 1<<15, /* Fiber/Copper Auto Sel. Dis. */ + PHY_M_FC_AN_REG_ACC = 1<<14, /* Fiber/Copper AN Reg. Access */ + PHY_M_FC_RESOLUTION = 1<<13, /* Fiber/Copper Resolution */ + PHY_M_SER_IF_AN_BP = 1<<12, /* Ser. IF AN Bypass Enable */ + PHY_M_SER_IF_BP_ST = 1<<11, /* Ser. IF AN Bypass Status */ + PHY_M_IRQ_POLARITY = 1<<10, /* IRQ polarity */ + PHY_M_DIS_AUT_MED = 1<<9, /* Disable Aut. Medium Reg. Selection */ + /* (88E1111 only) */ + /* Bit 9.. 4: reserved (88E1011 only) */ + PHY_M_UNDOC1 = 1<<7, /* undocumented bit !! */ + PHY_M_DTE_POW_STAT = 1<<4, /* DTE Power Status (88E1111 only) */ + PHY_M_MODE_MASK = 0xf, /* Bit 3.. 0: copy of HWCFG MODE[3:0] */ +}; + +/***** PHY_MARV_CABLE_DIAG 16 bit r/o Cable Diagnostic Reg *****/ +enum { + PHY_M_CABD_ENA_TEST = 1<<15, /* Enable Test (Page 0) */ + PHY_M_CABD_DIS_WAIT = 1<<15, /* Disable Waiting Period (Page 1) */ + /* (88E1111 only) */ + PHY_M_CABD_STAT_MSK = 3<<13, /* Bit 14..13: Status Mask */ + PHY_M_CABD_AMPL_MSK = 0x1f<<8,/* Bit 12.. 8: Amplitude Mask */ + /* (88E1111 only) */ + PHY_M_CABD_DIST_MSK = 0xff, /* Bit 7.. 0: Distance Mask */ +}; + +/* values for Cable Diagnostic Status (11=fail; 00=OK; 10=open; 01=short) */ +enum { + CABD_STAT_NORMAL= 0, + CABD_STAT_SHORT = 1, + CABD_STAT_OPEN = 2, + CABD_STAT_FAIL = 3, +}; + +/* for 10/100 Fast Ethernet PHY (88E3082 only) */ +/***** PHY_MARV_FE_LED_PAR 16 bit r/w LED Parallel Select Reg. *****/ + /* Bit 15..12: reserved (used internally) */ +enum { + PHY_M_FELP_LED2_MSK = 0xf<<8, /* Bit 11.. 8: LED2 Mask (LINK) */ + PHY_M_FELP_LED1_MSK = 0xf<<4, /* Bit 7.. 4: LED1 Mask (ACT) */ + PHY_M_FELP_LED0_MSK = 0xf, /* Bit 3.. 0: LED0 Mask (SPEED) */ +}; + +#define PHY_M_FELP_LED2_CTRL(x) ( ((x)<<8) & PHY_M_FELP_LED2_MSK) +#define PHY_M_FELP_LED1_CTRL(x) ( ((x)<<4) & PHY_M_FELP_LED1_MSK) +#define PHY_M_FELP_LED0_CTRL(x) ( ((x)<<0) & PHY_M_FELP_LED0_MSK) + +enum { + LED_PAR_CTRL_COLX = 0x00, + LED_PAR_CTRL_ERROR = 0x01, + LED_PAR_CTRL_DUPLEX = 0x02, + LED_PAR_CTRL_DP_COL = 0x03, + LED_PAR_CTRL_SPEED = 0x04, + LED_PAR_CTRL_LINK = 0x05, + LED_PAR_CTRL_TX = 0x06, + LED_PAR_CTRL_RX = 0x07, + LED_PAR_CTRL_ACT = 0x08, + LED_PAR_CTRL_LNK_RX = 0x09, + LED_PAR_CTRL_LNK_AC = 0x0a, + LED_PAR_CTRL_ACT_BL = 0x0b, + LED_PAR_CTRL_TX_BL = 0x0c, + LED_PAR_CTRL_RX_BL = 0x0d, + LED_PAR_CTRL_COL_BL = 0x0e, + LED_PAR_CTRL_INACT = 0x0f +}; + +/*****,PHY_MARV_FE_SPEC_2 16 bit r/w Specific Control Reg. 2 *****/ +enum { + PHY_M_FESC_DIS_WAIT = 1<<2, /* Disable TDR Waiting Period */ + PHY_M_FESC_ENA_MCLK = 1<<1, /* Enable MAC Rx Clock in sleep mode */ + PHY_M_FESC_SEL_CL_A = 1<<0, /* Select Class A driver (100B-TX) */ +}; + +/* for Yukon-2 Gigabit Ethernet PHY (88E1112 only) */ +/***** PHY_MARV_PHY_CTRL (page 2) 16 bit r/w MAC Specific Ctrl *****/ +enum { + PHY_M_MAC_MD_MSK = 7<<7, /* Bit 9.. 7: Mode Select Mask */ + PHY_M_MAC_MD_AUTO = 3,/* Auto Copper/1000Base-X */ + PHY_M_MAC_MD_COPPER = 5,/* Copper only */ + PHY_M_MAC_MD_1000BX = 7,/* 1000Base-X only */ +}; +#define PHY_M_MAC_MODE_SEL(x) ( ((x)<<7) & PHY_M_MAC_MD_MSK) + +/***** PHY_MARV_PHY_CTRL (page 3) 16 bit r/w LED Control Reg. *****/ +enum { + PHY_M_LEDC_LOS_MSK = 0xf<<12,/* Bit 15..12: LOS LED Ctrl. Mask */ + PHY_M_LEDC_INIT_MSK = 0xf<<8, /* Bit 11.. 8: INIT LED Ctrl. Mask */ + PHY_M_LEDC_STA1_MSK = 0xf<<4,/* Bit 7.. 4: STAT1 LED Ctrl. Mask */ + PHY_M_LEDC_STA0_MSK = 0xf, /* Bit 3.. 0: STAT0 LED Ctrl. Mask */ +}; + +#define PHY_M_LEDC_LOS_CTRL(x) ( ((x)<<12) & PHY_M_LEDC_LOS_MSK) +#define PHY_M_LEDC_INIT_CTRL(x) ( ((x)<<8) & PHY_M_LEDC_INIT_MSK) +#define PHY_M_LEDC_STA1_CTRL(x) ( ((x)<<4) & PHY_M_LEDC_STA1_MSK) +#define PHY_M_LEDC_STA0_CTRL(x) ( ((x)<<0) & PHY_M_LEDC_STA0_MSK) + +/* GMAC registers */ +/* Port Registers */ +enum { + GM_GP_STAT = 0x0000, /* 16 bit r/o General Purpose Status */ + GM_GP_CTRL = 0x0004, /* 16 bit r/w General Purpose Control */ + GM_TX_CTRL = 0x0008, /* 16 bit r/w Transmit Control Reg. */ + GM_RX_CTRL = 0x000c, /* 16 bit r/w Receive Control Reg. */ + GM_TX_FLOW_CTRL = 0x0010, /* 16 bit r/w Transmit Flow-Control */ + GM_TX_PARAM = 0x0014, /* 16 bit r/w Transmit Parameter Reg. */ + GM_SERIAL_MODE = 0x0018, /* 16 bit r/w Serial Mode Register */ +/* Source Address Registers */ + GM_SRC_ADDR_1L = 0x001c, /* 16 bit r/w Source Address 1 (low) */ + GM_SRC_ADDR_1M = 0x0020, /* 16 bit r/w Source Address 1 (middle) */ + GM_SRC_ADDR_1H = 0x0024, /* 16 bit r/w Source Address 1 (high) */ + GM_SRC_ADDR_2L = 0x0028, /* 16 bit r/w Source Address 2 (low) */ + GM_SRC_ADDR_2M = 0x002c, /* 16 bit r/w Source Address 2 (middle) */ + GM_SRC_ADDR_2H = 0x0030, /* 16 bit r/w Source Address 2 (high) */ + +/* Multicast Address Hash Registers */ + GM_MC_ADDR_H1 = 0x0034, /* 16 bit r/w Multicast Address Hash 1 */ + GM_MC_ADDR_H2 = 0x0038, /* 16 bit r/w Multicast Address Hash 2 */ + GM_MC_ADDR_H3 = 0x003c, /* 16 bit r/w Multicast Address Hash 3 */ + GM_MC_ADDR_H4 = 0x0040, /* 16 bit r/w Multicast Address Hash 4 */ + +/* Interrupt Source Registers */ + GM_TX_IRQ_SRC = 0x0044, /* 16 bit r/o Tx Overflow IRQ Source */ + GM_RX_IRQ_SRC = 0x0048, /* 16 bit r/o Rx Overflow IRQ Source */ + GM_TR_IRQ_SRC = 0x004c, /* 16 bit r/o Tx/Rx Over. IRQ Source */ + +/* Interrupt Mask Registers */ + GM_TX_IRQ_MSK = 0x0050, /* 16 bit r/w Tx Overflow IRQ Mask */ + GM_RX_IRQ_MSK = 0x0054, /* 16 bit r/w Rx Overflow IRQ Mask */ + GM_TR_IRQ_MSK = 0x0058, /* 16 bit r/w Tx/Rx Over. IRQ Mask */ + +/* Serial Management Interface (SMI) Registers */ + GM_SMI_CTRL = 0x0080, /* 16 bit r/w SMI Control Register */ + GM_SMI_DATA = 0x0084, /* 16 bit r/w SMI Data Register */ + GM_PHY_ADDR = 0x0088, /* 16 bit r/w GPHY Address Register */ +}; + +/* MIB Counters */ +#define GM_MIB_CNT_BASE 0x0100 /* Base Address of MIB Counters */ +#define GM_MIB_CNT_SIZE 44 /* Number of MIB Counters */ + +/* + * MIB Counters base address definitions (low word) - + * use offset 4 for access to high word (32 bit r/o) + */ +enum { + GM_RXF_UC_OK = GM_MIB_CNT_BASE + 0, /* Unicast Frames Received OK */ + GM_RXF_BC_OK = GM_MIB_CNT_BASE + 8, /* Broadcast Frames Received OK */ + GM_RXF_MPAUSE = GM_MIB_CNT_BASE + 16, /* Pause MAC Ctrl Frames Received */ + GM_RXF_MC_OK = GM_MIB_CNT_BASE + 24, /* Multicast Frames Received OK */ + GM_RXF_FCS_ERR = GM_MIB_CNT_BASE + 32, /* Rx Frame Check Seq. Error */ + /* GM_MIB_CNT_BASE + 40: reserved */ + GM_RXO_OK_LO = GM_MIB_CNT_BASE + 48, /* Octets Received OK Low */ + GM_RXO_OK_HI = GM_MIB_CNT_BASE + 56, /* Octets Received OK High */ + GM_RXO_ERR_LO = GM_MIB_CNT_BASE + 64, /* Octets Received Invalid Low */ + GM_RXO_ERR_HI = GM_MIB_CNT_BASE + 72, /* Octets Received Invalid High */ + GM_RXF_SHT = GM_MIB_CNT_BASE + 80, /* Frames <64 Byte Received OK */ + GM_RXE_FRAG = GM_MIB_CNT_BASE + 88, /* Frames <64 Byte Received with FCS Err */ + GM_RXF_64B = GM_MIB_CNT_BASE + 96, /* 64 Byte Rx Frame */ + GM_RXF_127B = GM_MIB_CNT_BASE + 104, /* 65-127 Byte Rx Frame */ + GM_RXF_255B = GM_MIB_CNT_BASE + 112, /* 128-255 Byte Rx Frame */ + GM_RXF_511B = GM_MIB_CNT_BASE + 120, /* 256-511 Byte Rx Frame */ + GM_RXF_1023B = GM_MIB_CNT_BASE + 128, /* 512-1023 Byte Rx Frame */ + GM_RXF_1518B = GM_MIB_CNT_BASE + 136, /* 1024-1518 Byte Rx Frame */ + GM_RXF_MAX_SZ = GM_MIB_CNT_BASE + 144, /* 1519-MaxSize Byte Rx Frame */ + GM_RXF_LNG_ERR = GM_MIB_CNT_BASE + 152, /* Rx Frame too Long Error */ + GM_RXF_JAB_PKT = GM_MIB_CNT_BASE + 160, /* Rx Jabber Packet Frame */ + /* GM_MIB_CNT_BASE + 168: reserved */ + GM_RXE_FIFO_OV = GM_MIB_CNT_BASE + 176, /* Rx FIFO overflow Event */ + /* GM_MIB_CNT_BASE + 184: reserved */ + GM_TXF_UC_OK = GM_MIB_CNT_BASE + 192, /* Unicast Frames Xmitted OK */ + GM_TXF_BC_OK = GM_MIB_CNT_BASE + 200, /* Broadcast Frames Xmitted OK */ + GM_TXF_MPAUSE = GM_MIB_CNT_BASE + 208, /* Pause MAC Ctrl Frames Xmitted */ + GM_TXF_MC_OK = GM_MIB_CNT_BASE + 216, /* Multicast Frames Xmitted OK */ + GM_TXO_OK_LO = GM_MIB_CNT_BASE + 224, /* Octets Transmitted OK Low */ + GM_TXO_OK_HI = GM_MIB_CNT_BASE + 232, /* Octets Transmitted OK High */ + GM_TXF_64B = GM_MIB_CNT_BASE + 240, /* 64 Byte Tx Frame */ + GM_TXF_127B = GM_MIB_CNT_BASE + 248, /* 65-127 Byte Tx Frame */ + GM_TXF_255B = GM_MIB_CNT_BASE + 256, /* 128-255 Byte Tx Frame */ + GM_TXF_511B = GM_MIB_CNT_BASE + 264, /* 256-511 Byte Tx Frame */ + GM_TXF_1023B = GM_MIB_CNT_BASE + 272, /* 512-1023 Byte Tx Frame */ + GM_TXF_1518B = GM_MIB_CNT_BASE + 280, /* 1024-1518 Byte Tx Frame */ + GM_TXF_MAX_SZ = GM_MIB_CNT_BASE + 288, /* 1519-MaxSize Byte Tx Frame */ + + GM_TXF_COL = GM_MIB_CNT_BASE + 304, /* Tx Collision */ + GM_TXF_LAT_COL = GM_MIB_CNT_BASE + 312, /* Tx Late Collision */ + GM_TXF_ABO_COL = GM_MIB_CNT_BASE + 320, /* Tx aborted due to Exces. Col. */ + GM_TXF_MUL_COL = GM_MIB_CNT_BASE + 328, /* Tx Multiple Collision */ + GM_TXF_SNG_COL = GM_MIB_CNT_BASE + 336, /* Tx Single Collision */ + GM_TXE_FIFO_UR = GM_MIB_CNT_BASE + 344, /* Tx FIFO Underrun Event */ +}; + +/* GMAC Bit Definitions */ +/* GM_GP_STAT 16 bit r/o General Purpose Status Register */ +enum { + GM_GPSR_SPEED = 1<<15, /* Bit 15: Port Speed (1 = 100 Mbps) */ + GM_GPSR_DUPLEX = 1<<14, /* Bit 14: Duplex Mode (1 = Full) */ + GM_GPSR_FC_TX_DIS = 1<<13, /* Bit 13: Tx Flow-Control Mode Disabled */ + GM_GPSR_LINK_UP = 1<<12, /* Bit 12: Link Up Status */ + GM_GPSR_PAUSE = 1<<11, /* Bit 11: Pause State */ + GM_GPSR_TX_ACTIVE = 1<<10, /* Bit 10: Tx in Progress */ + GM_GPSR_EXC_COL = 1<<9, /* Bit 9: Excessive Collisions Occured */ + GM_GPSR_LAT_COL = 1<<8, /* Bit 8: Late Collisions Occured */ + + GM_GPSR_PHY_ST_CH = 1<<5, /* Bit 5: PHY Status Change */ + GM_GPSR_GIG_SPEED = 1<<4, /* Bit 4: Gigabit Speed (1 = 1000 Mbps) */ + GM_GPSR_PART_MODE = 1<<3, /* Bit 3: Partition mode */ + GM_GPSR_FC_RX_DIS = 1<<2, /* Bit 2: Rx Flow-Control Mode Disabled */ + GM_GPSR_PROM_EN = 1<<1, /* Bit 1: Promiscuous Mode Enabled */ +}; + +/* GM_GP_CTRL 16 bit r/w General Purpose Control Register */ +enum { + GM_GPCR_PROM_ENA = 1<<14, /* Bit 14: Enable Promiscuous Mode */ + GM_GPCR_FC_TX_DIS = 1<<13, /* Bit 13: Disable Tx Flow-Control Mode */ + GM_GPCR_TX_ENA = 1<<12, /* Bit 12: Enable Transmit */ + GM_GPCR_RX_ENA = 1<<11, /* Bit 11: Enable Receive */ + GM_GPCR_BURST_ENA = 1<<10, /* Bit 10: Enable Burst Mode */ + GM_GPCR_LOOP_ENA = 1<<9, /* Bit 9: Enable MAC Loopback Mode */ + GM_GPCR_PART_ENA = 1<<8, /* Bit 8: Enable Partition Mode */ + GM_GPCR_GIGS_ENA = 1<<7, /* Bit 7: Gigabit Speed (1000 Mbps) */ + GM_GPCR_FL_PASS = 1<<6, /* Bit 6: Force Link Pass */ + GM_GPCR_DUP_FULL = 1<<5, /* Bit 5: Full Duplex Mode */ + GM_GPCR_FC_RX_DIS = 1<<4, /* Bit 4: Disable Rx Flow-Control Mode */ + GM_GPCR_SPEED_100 = 1<<3, /* Bit 3: Port Speed 100 Mbps */ + GM_GPCR_AU_DUP_DIS = 1<<2, /* Bit 2: Disable Auto-Update Duplex */ + GM_GPCR_AU_FCT_DIS = 1<<1, /* Bit 1: Disable Auto-Update Flow-C. */ + GM_GPCR_AU_SPD_DIS = 1<<0, /* Bit 0: Disable Auto-Update Speed */ +}; + +#define GM_GPCR_SPEED_1000 (GM_GPCR_GIGS_ENA | GM_GPCR_SPEED_100) +#define GM_GPCR_AU_ALL_DIS (GM_GPCR_AU_DUP_DIS | GM_GPCR_AU_FCT_DIS|GM_GPCR_AU_SPD_DIS) + +/* GM_TX_CTRL 16 bit r/w Transmit Control Register */ +enum { + GM_TXCR_FORCE_JAM = 1<<15, /* Bit 15: Force Jam / Flow-Control */ + GM_TXCR_CRC_DIS = 1<<14, /* Bit 14: Disable insertion of CRC */ + GM_TXCR_PAD_DIS = 1<<13, /* Bit 13: Disable padding of packets */ + GM_TXCR_COL_THR_MSK = 1<<10, /* Bit 12..10: Collision Threshold */ +}; + +#define TX_COL_THR(x) (((x)<<10) & GM_TXCR_COL_THR_MSK) +#define TX_COL_DEF 0x04 + +/* GM_RX_CTRL 16 bit r/w Receive Control Register */ +enum { + GM_RXCR_UCF_ENA = 1<<15, /* Bit 15: Enable Unicast filtering */ + GM_RXCR_MCF_ENA = 1<<14, /* Bit 14: Enable Multicast filtering */ + GM_RXCR_CRC_DIS = 1<<13, /* Bit 13: Remove 4-byte CRC */ + GM_RXCR_PASS_FC = 1<<12, /* Bit 12: Pass FC packets to FIFO */ +}; + +/* GM_TX_PARAM 16 bit r/w Transmit Parameter Register */ +enum { + GM_TXPA_JAMLEN_MSK = 0x03<<14, /* Bit 15..14: Jam Length */ + GM_TXPA_JAMIPG_MSK = 0x1f<<9, /* Bit 13..9: Jam IPG */ + GM_TXPA_JAMDAT_MSK = 0x1f<<4, /* Bit 8..4: IPG Jam to Data */ + + TX_JAM_LEN_DEF = 0x03, + TX_JAM_IPG_DEF = 0x0b, + TX_IPG_JAM_DEF = 0x1c, +}; + +#define TX_JAM_LEN_VAL(x) (((x)<<14) & GM_TXPA_JAMLEN_MSK) +#define TX_JAM_IPG_VAL(x) (((x)<<9) & GM_TXPA_JAMIPG_MSK) +#define TX_IPG_JAM_DATA(x) (((x)<<4) & GM_TXPA_JAMDAT_MSK) + + +/* GM_SERIAL_MODE 16 bit r/w Serial Mode Register */ +enum { + GM_SMOD_DATABL_MSK = 0x1f<<11, /* Bit 15..11: Data Blinder (r/o) */ + GM_SMOD_LIMIT_4 = 1<<10, /* Bit 10: 4 consecutive Tx trials */ + GM_SMOD_VLAN_ENA = 1<<9, /* Bit 9: Enable VLAN (Max. Frame Len) */ + GM_SMOD_JUMBO_ENA = 1<<8, /* Bit 8: Enable Jumbo (Max. Frame Len) */ + GM_SMOD_IPG_MSK = 0x1f /* Bit 4..0: Inter-Packet Gap (IPG) */ +}; + +#define DATA_BLIND_VAL(x) (((x)<<11) & GM_SMOD_DATABL_MSK) +#define DATA_BLIND_DEF 0x04 + +#define IPG_DATA_VAL(x) (x & GM_SMOD_IPG_MSK) +#define IPG_DATA_DEF 0x1e + +/* GM_SMI_CTRL 16 bit r/w SMI Control Register */ +enum { + GM_SMI_CT_PHY_A_MSK = 0x1f<<11,/* Bit 15..11: PHY Device Address */ + GM_SMI_CT_REG_A_MSK = 0x1f<<6,/* Bit 10.. 6: PHY Register Address */ + GM_SMI_CT_OP_RD = 1<<5, /* Bit 5: OpCode Read (0=Write)*/ + GM_SMI_CT_RD_VAL = 1<<4, /* Bit 4: Read Valid (Read completed) */ + GM_SMI_CT_BUSY = 1<<3, /* Bit 3: Busy (Operation in progress) */ +}; + +#define GM_SMI_CT_PHY_AD(x) (((x)<<11) & GM_SMI_CT_PHY_A_MSK) +#define GM_SMI_CT_REG_AD(x) (((x)<<6) & GM_SMI_CT_REG_A_MSK) + +/* GM_PHY_ADDR 16 bit r/w GPHY Address Register */ +enum { + GM_PAR_MIB_CLR = 1<<5, /* Bit 5: Set MIB Clear Counter Mode */ + GM_PAR_MIB_TST = 1<<4, /* Bit 4: MIB Load Counter (Test Mode) */ +}; + +/* Receive Frame Status Encoding */ +enum { + GMR_FS_LEN = 0xffff<<16, /* Bit 31..16: Rx Frame Length */ + GMR_FS_VLAN = 1<<13, /* Bit 13: VLAN Packet */ + GMR_FS_JABBER = 1<<12, /* Bit 12: Jabber Packet */ + GMR_FS_UN_SIZE = 1<<11, /* Bit 11: Undersize Packet */ + GMR_FS_MC = 1<<10, /* Bit 10: Multicast Packet */ + GMR_FS_BC = 1<<9, /* Bit 9: Broadcast Packet */ + GMR_FS_RX_OK = 1<<8, /* Bit 8: Receive OK (Good Packet) */ + GMR_FS_GOOD_FC = 1<<7, /* Bit 7: Good Flow-Control Packet */ + GMR_FS_BAD_FC = 1<<6, /* Bit 6: Bad Flow-Control Packet */ + GMR_FS_MII_ERR = 1<<5, /* Bit 5: MII Error */ + GMR_FS_LONG_ERR = 1<<4, /* Bit 4: Too Long Packet */ + GMR_FS_FRAGMENT = 1<<3, /* Bit 3: Fragment */ + + GMR_FS_CRC_ERR = 1<<1, /* Bit 1: CRC Error */ + GMR_FS_RX_FF_OV = 1<<0, /* Bit 0: Rx FIFO Overflow */ + +/* + * GMR_FS_ANY_ERR (analogous to XMR_FS_ANY_ERR) + */ + GMR_FS_ANY_ERR = GMR_FS_CRC_ERR | GMR_FS_LONG_ERR | + GMR_FS_MII_ERR | GMR_FS_BAD_FC | GMR_FS_GOOD_FC | + GMR_FS_JABBER, +/* Rx GMAC FIFO Flush Mask (default) */ + RX_FF_FL_DEF_MSK = GMR_FS_CRC_ERR | GMR_FS_RX_FF_OV |GMR_FS_MII_ERR | + GMR_FS_BAD_FC | GMR_FS_GOOD_FC | GMR_FS_UN_SIZE | + GMR_FS_JABBER, +}; + +/* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */ +enum { + GMF_WP_TST_ON = 1<<14, /* Write Pointer Test On */ + GMF_WP_TST_OFF = 1<<13, /* Write Pointer Test Off */ + GMF_WP_STEP = 1<<12, /* Write Pointer Step/Increment */ + + GMF_RP_TST_ON = 1<<10, /* Read Pointer Test On */ + GMF_RP_TST_OFF = 1<<9, /* Read Pointer Test Off */ + GMF_RP_STEP = 1<<8, /* Read Pointer Step/Increment */ + GMF_RX_F_FL_ON = 1<<7, /* Rx FIFO Flush Mode On */ + GMF_RX_F_FL_OFF = 1<<6, /* Rx FIFO Flush Mode Off */ + GMF_CLI_RX_FO = 1<<5, /* Clear IRQ Rx FIFO Overrun */ + GMF_CLI_RX_FC = 1<<4, /* Clear IRQ Rx Frame Complete */ + GMF_OPER_ON = 1<<3, /* Operational Mode On */ + GMF_OPER_OFF = 1<<2, /* Operational Mode Off */ + GMF_RST_CLR = 1<<1, /* Clear GMAC FIFO Reset */ + GMF_RST_SET = 1<<0, /* Set GMAC FIFO Reset */ + + RX_GMF_FL_THR_DEF = 0xa, /* flush threshold (default) */ +}; + + +/* TX_GMF_CTRL_T 32 bit Tx GMAC FIFO Control/Test */ +enum { + GMF_WSP_TST_ON = 1<<18,/* Write Shadow Pointer Test On */ + GMF_WSP_TST_OFF = 1<<17,/* Write Shadow Pointer Test Off */ + GMF_WSP_STEP = 1<<16,/* Write Shadow Pointer Step/Increment */ + + GMF_CLI_TX_FU = 1<<6, /* Clear IRQ Tx FIFO Underrun */ + GMF_CLI_TX_FC = 1<<5, /* Clear IRQ Tx Frame Complete */ + GMF_CLI_TX_PE = 1<<4, /* Clear IRQ Tx Parity Error */ +}; + +/* GMAC_TI_ST_CTRL 8 bit Time Stamp Timer Ctrl Reg (YUKON only) */ +enum { + GMT_ST_START = 1<<2, /* Start Time Stamp Timer */ + GMT_ST_STOP = 1<<1, /* Stop Time Stamp Timer */ + GMT_ST_CLR_IRQ = 1<<0, /* Clear Time Stamp Timer IRQ */ +}; + +/* GMAC_CTRL 32 bit GMAC Control Reg (YUKON only) */ +enum { + GMC_H_BURST_ON = 1<<7, /* Half Duplex Burst Mode On */ + GMC_H_BURST_OFF = 1<<6, /* Half Duplex Burst Mode Off */ + GMC_F_LOOPB_ON = 1<<5, /* FIFO Loopback On */ + GMC_F_LOOPB_OFF = 1<<4, /* FIFO Loopback Off */ + GMC_PAUSE_ON = 1<<3, /* Pause On */ + GMC_PAUSE_OFF = 1<<2, /* Pause Off */ + GMC_RST_CLR = 1<<1, /* Clear GMAC Reset */ + GMC_RST_SET = 1<<0, /* Set GMAC Reset */ +}; + +/* GPHY_CTRL 32 bit GPHY Control Reg (YUKON only) */ +enum { + GPC_SEL_BDT = 1<<28, /* Select Bi-Dir. Transfer for MDC/MDIO */ + GPC_INT_POL_HI = 1<<27, /* IRQ Polarity is Active HIGH */ + GPC_75_OHM = 1<<26, /* Use 75 Ohm Termination instead of 50 */ + GPC_DIS_FC = 1<<25, /* Disable Automatic Fiber/Copper Detection */ + GPC_DIS_SLEEP = 1<<24, /* Disable Energy Detect */ + GPC_HWCFG_M_3 = 1<<23, /* HWCFG_MODE[3] */ + GPC_HWCFG_M_2 = 1<<22, /* HWCFG_MODE[2] */ + GPC_HWCFG_M_1 = 1<<21, /* HWCFG_MODE[1] */ + GPC_HWCFG_M_0 = 1<<20, /* HWCFG_MODE[0] */ + GPC_ANEG_0 = 1<<19, /* ANEG[0] */ + GPC_ENA_XC = 1<<18, /* Enable MDI crossover */ + GPC_DIS_125 = 1<<17, /* Disable 125 MHz clock */ + GPC_ANEG_3 = 1<<16, /* ANEG[3] */ + GPC_ANEG_2 = 1<<15, /* ANEG[2] */ + GPC_ANEG_1 = 1<<14, /* ANEG[1] */ + GPC_ENA_PAUSE = 1<<13, /* Enable Pause (SYM_OR_REM) */ + GPC_PHYADDR_4 = 1<<12, /* Bit 4 of Phy Addr */ + GPC_PHYADDR_3 = 1<<11, /* Bit 3 of Phy Addr */ + GPC_PHYADDR_2 = 1<<10, /* Bit 2 of Phy Addr */ + GPC_PHYADDR_1 = 1<<9, /* Bit 1 of Phy Addr */ + GPC_PHYADDR_0 = 1<<8, /* Bit 0 of Phy Addr */ + /* Bits 7..2: reserved */ + GPC_RST_CLR = 1<<1, /* Clear GPHY Reset */ + GPC_RST_SET = 1<<0, /* Set GPHY Reset */ +}; + +#define GPC_HWCFG_GMII_COP (GPC_HWCFG_M_3|GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0) +#define GPC_HWCFG_GMII_FIB (GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0) +#define GPC_ANEG_ADV_ALL_M (GPC_ANEG_3 | GPC_ANEG_2 | GPC_ANEG_1 | GPC_ANEG_0) + +/* forced speed and duplex mode (don't mix with other ANEG bits) */ +#define GPC_FRC10MBIT_HALF 0 +#define GPC_FRC10MBIT_FULL GPC_ANEG_0 +#define GPC_FRC100MBIT_HALF GPC_ANEG_1 +#define GPC_FRC100MBIT_FULL (GPC_ANEG_0 | GPC_ANEG_1) + +/* auto-negotiation with limited advertised speeds */ +/* mix only with master/slave settings (for copper) */ +#define GPC_ADV_1000_HALF GPC_ANEG_2 +#define GPC_ADV_1000_FULL GPC_ANEG_3 +#define GPC_ADV_ALL (GPC_ANEG_2 | GPC_ANEG_3) + +/* master/slave settings */ +/* only for copper with 1000 Mbps */ +#define GPC_FORCE_MASTER 0 +#define GPC_FORCE_SLAVE GPC_ANEG_0 +#define GPC_PREF_MASTER GPC_ANEG_1 +#define GPC_PREF_SLAVE (GPC_ANEG_1 | GPC_ANEG_0) + +/* GMAC_IRQ_SRC 8 bit GMAC Interrupt Source Reg (YUKON only) */ +/* GMAC_IRQ_MSK 8 bit GMAC Interrupt Mask Reg (YUKON only) */ +enum { + GM_IS_TX_CO_OV = 1<<5, /* Transmit Counter Overflow IRQ */ + GM_IS_RX_CO_OV = 1<<4, /* Receive Counter Overflow IRQ */ + GM_IS_TX_FF_UR = 1<<3, /* Transmit FIFO Underrun */ + GM_IS_TX_COMPL = 1<<2, /* Frame Transmission Complete */ + GM_IS_RX_FF_OR = 1<<1, /* Receive FIFO Overrun */ + GM_IS_RX_COMPL = 1<<0, /* Frame Reception Complete */ + +#define GMAC_DEF_MSK (GM_IS_TX_CO_OV | GM_IS_RX_CO_OV | GM_IS_TX_FF_UR) + +/* GMAC_LINK_CTRL 16 bit GMAC Link Control Reg (YUKON only) */ + /* Bits 15.. 2: reserved */ + GMLC_RST_CLR = 1<<1, /* Clear GMAC Link Reset */ + GMLC_RST_SET = 1<<0, /* Set GMAC Link Reset */ + + +/* WOL_CTRL_STAT 16 bit WOL Control/Status Reg */ + WOL_CTL_LINK_CHG_OCC = 1<<15, + WOL_CTL_MAGIC_PKT_OCC = 1<<14, + WOL_CTL_PATTERN_OCC = 1<<13, + WOL_CTL_CLEAR_RESULT = 1<<12, + WOL_CTL_ENA_PME_ON_LINK_CHG = 1<<11, + WOL_CTL_DIS_PME_ON_LINK_CHG = 1<<10, + WOL_CTL_ENA_PME_ON_MAGIC_PKT = 1<<9, + WOL_CTL_DIS_PME_ON_MAGIC_PKT = 1<<8, + WOL_CTL_ENA_PME_ON_PATTERN = 1<<7, + WOL_CTL_DIS_PME_ON_PATTERN = 1<<6, + WOL_CTL_ENA_LINK_CHG_UNIT = 1<<5, + WOL_CTL_DIS_LINK_CHG_UNIT = 1<<4, + WOL_CTL_ENA_MAGIC_PKT_UNIT = 1<<3, + WOL_CTL_DIS_MAGIC_PKT_UNIT = 1<<2, + WOL_CTL_ENA_PATTERN_UNIT = 1<<1, + WOL_CTL_DIS_PATTERN_UNIT = 1<<0, +}; + +#define WOL_CTL_DEFAULT \ + (WOL_CTL_DIS_PME_ON_LINK_CHG | \ + WOL_CTL_DIS_PME_ON_PATTERN | \ + WOL_CTL_DIS_PME_ON_MAGIC_PKT | \ + WOL_CTL_DIS_LINK_CHG_UNIT | \ + WOL_CTL_DIS_PATTERN_UNIT | \ + WOL_CTL_DIS_MAGIC_PKT_UNIT) + +/* WOL_MATCH_CTL 8 bit WOL Match Control Reg */ +#define WOL_CTL_PATT_ENA(x) (1 << (x)) + + +/* XMAC II registers */ +enum { + XM_MMU_CMD = 0x0000, /* 16 bit r/w MMU Command Register */ + XM_POFF = 0x0008, /* 32 bit r/w Packet Offset Register */ + XM_BURST = 0x000c, /* 32 bit r/w Burst Register for half duplex*/ + XM_1L_VLAN_TAG = 0x0010, /* 16 bit r/w One Level VLAN Tag ID */ + XM_2L_VLAN_TAG = 0x0014, /* 16 bit r/w Two Level VLAN Tag ID */ + XM_TX_CMD = 0x0020, /* 16 bit r/w Transmit Command Register */ + XM_TX_RT_LIM = 0x0024, /* 16 bit r/w Transmit Retry Limit Register */ + XM_TX_STIME = 0x0028, /* 16 bit r/w Transmit Slottime Register */ + XM_TX_IPG = 0x002c, /* 16 bit r/w Transmit Inter Packet Gap */ + XM_RX_CMD = 0x0030, /* 16 bit r/w Receive Command Register */ + XM_PHY_ADDR = 0x0034, /* 16 bit r/w PHY Address Register */ + XM_PHY_DATA = 0x0038, /* 16 bit r/w PHY Data Register */ + XM_GP_PORT = 0x0040, /* 32 bit r/w General Purpose Port Register */ + XM_IMSK = 0x0044, /* 16 bit r/w Interrupt Mask Register */ + XM_ISRC = 0x0048, /* 16 bit r/o Interrupt Status Register */ + XM_HW_CFG = 0x004c, /* 16 bit r/w Hardware Config Register */ + XM_TX_LO_WM = 0x0060, /* 16 bit r/w Tx FIFO Low Water Mark */ + XM_TX_HI_WM = 0x0062, /* 16 bit r/w Tx FIFO High Water Mark */ + XM_TX_THR = 0x0064, /* 16 bit r/w Tx Request Threshold */ + XM_HT_THR = 0x0066, /* 16 bit r/w Host Request Threshold */ + XM_PAUSE_DA = 0x0068, /* NA reg r/w Pause Destination Address */ + XM_CTL_PARA = 0x0070, /* 32 bit r/w Control Parameter Register */ + XM_MAC_OPCODE = 0x0074, /* 16 bit r/w Opcode for MAC control frames */ + XM_MAC_PTIME = 0x0076, /* 16 bit r/w Pause time for MAC ctrl frames*/ + XM_TX_STAT = 0x0078, /* 32 bit r/o Tx Status LIFO Register */ + + XM_EXM_START = 0x0080, /* r/w Start Address of the EXM Regs */ +#define XM_EXM(reg) (XM_EXM_START + ((reg) << 3)) +}; + +enum { + XM_SRC_CHK = 0x0100, /* NA reg r/w Source Check Address Register */ + XM_SA = 0x0108, /* NA reg r/w Station Address Register */ + XM_HSM = 0x0110, /* 64 bit r/w Hash Match Address Registers */ + XM_RX_LO_WM = 0x0118, /* 16 bit r/w Receive Low Water Mark */ + XM_RX_HI_WM = 0x011a, /* 16 bit r/w Receive High Water Mark */ + XM_RX_THR = 0x011c, /* 32 bit r/w Receive Request Threshold */ + XM_DEV_ID = 0x0120, /* 32 bit r/o Device ID Register */ + XM_MODE = 0x0124, /* 32 bit r/w Mode Register */ + XM_LSA = 0x0128, /* NA reg r/o Last Source Register */ + XM_TS_READ = 0x0130, /* 32 bit r/o Time Stamp Read Register */ + XM_TS_LOAD = 0x0134, /* 32 bit r/o Time Stamp Load Value */ + XM_STAT_CMD = 0x0200, /* 16 bit r/w Statistics Command Register */ + XM_RX_CNT_EV = 0x0204, /* 32 bit r/o Rx Counter Event Register */ + XM_TX_CNT_EV = 0x0208, /* 32 bit r/o Tx Counter Event Register */ + XM_RX_EV_MSK = 0x020c, /* 32 bit r/w Rx Counter Event Mask */ + XM_TX_EV_MSK = 0x0210, /* 32 bit r/w Tx Counter Event Mask */ + XM_TXF_OK = 0x0280, /* 32 bit r/o Frames Transmitted OK Conuter */ + XM_TXO_OK_HI = 0x0284, /* 32 bit r/o Octets Transmitted OK High Cnt*/ + XM_TXO_OK_LO = 0x0288, /* 32 bit r/o Octets Transmitted OK Low Cnt */ + XM_TXF_BC_OK = 0x028c, /* 32 bit r/o Broadcast Frames Xmitted OK */ + XM_TXF_MC_OK = 0x0290, /* 32 bit r/o Multicast Frames Xmitted OK */ + XM_TXF_UC_OK = 0x0294, /* 32 bit r/o Unicast Frames Xmitted OK */ + XM_TXF_LONG = 0x0298, /* 32 bit r/o Tx Long Frame Counter */ + XM_TXE_BURST = 0x029c, /* 32 bit r/o Tx Burst Event Counter */ + XM_TXF_MPAUSE = 0x02a0, /* 32 bit r/o Tx Pause MAC Ctrl Frame Cnt */ + XM_TXF_MCTRL = 0x02a4, /* 32 bit r/o Tx MAC Ctrl Frame Counter */ + XM_TXF_SNG_COL = 0x02a8, /* 32 bit r/o Tx Single Collision Counter */ + XM_TXF_MUL_COL = 0x02ac, /* 32 bit r/o Tx Multiple Collision Counter */ + XM_TXF_ABO_COL = 0x02b0, /* 32 bit r/o Tx aborted due to Exces. Col. */ + XM_TXF_LAT_COL = 0x02b4, /* 32 bit r/o Tx Late Collision Counter */ + XM_TXF_DEF = 0x02b8, /* 32 bit r/o Tx Deferred Frame Counter */ + XM_TXF_EX_DEF = 0x02bc, /* 32 bit r/o Tx Excessive Deferall Counter */ + XM_TXE_FIFO_UR = 0x02c0, /* 32 bit r/o Tx FIFO Underrun Event Cnt */ + XM_TXE_CS_ERR = 0x02c4, /* 32 bit r/o Tx Carrier Sense Error Cnt */ + XM_TXP_UTIL = 0x02c8, /* 32 bit r/o Tx Utilization in % */ + XM_TXF_64B = 0x02d0, /* 32 bit r/o 64 Byte Tx Frame Counter */ + XM_TXF_127B = 0x02d4, /* 32 bit r/o 65-127 Byte Tx Frame Counter */ + XM_TXF_255B = 0x02d8, /* 32 bit r/o 128-255 Byte Tx Frame Counter */ + XM_TXF_511B = 0x02dc, /* 32 bit r/o 256-511 Byte Tx Frame Counter */ + XM_TXF_1023B = 0x02e0, /* 32 bit r/o 512-1023 Byte Tx Frame Counter*/ + XM_TXF_MAX_SZ = 0x02e4, /* 32 bit r/o 1024-MaxSize Byte Tx Frame Cnt*/ + XM_RXF_OK = 0x0300, /* 32 bit r/o Frames Received OK */ + XM_RXO_OK_HI = 0x0304, /* 32 bit r/o Octets Received OK High Cnt */ + XM_RXO_OK_LO = 0x0308, /* 32 bit r/o Octets Received OK Low Counter*/ + XM_RXF_BC_OK = 0x030c, /* 32 bit r/o Broadcast Frames Received OK */ + XM_RXF_MC_OK = 0x0310, /* 32 bit r/o Multicast Frames Received OK */ + XM_RXF_UC_OK = 0x0314, /* 32 bit r/o Unicast Frames Received OK */ + XM_RXF_MPAUSE = 0x0318, /* 32 bit r/o Rx Pause MAC Ctrl Frame Cnt */ + XM_RXF_MCTRL = 0x031c, /* 32 bit r/o Rx MAC Ctrl Frame Counter */ + XM_RXF_INV_MP = 0x0320, /* 32 bit r/o Rx invalid Pause Frame Cnt */ + XM_RXF_INV_MOC = 0x0324, /* 32 bit r/o Rx Frames with inv. MAC Opcode*/ + XM_RXE_BURST = 0x0328, /* 32 bit r/o Rx Burst Event Counter */ + XM_RXE_FMISS = 0x032c, /* 32 bit r/o Rx Missed Frames Event Cnt */ + XM_RXF_FRA_ERR = 0x0330, /* 32 bit r/o Rx Framing Error Counter */ + XM_RXE_FIFO_OV = 0x0334, /* 32 bit r/o Rx FIFO overflow Event Cnt */ + XM_RXF_JAB_PKT = 0x0338, /* 32 bit r/o Rx Jabber Packet Frame Cnt */ + XM_RXE_CAR_ERR = 0x033c, /* 32 bit r/o Rx Carrier Event Error Cnt */ + XM_RXF_LEN_ERR = 0x0340, /* 32 bit r/o Rx in Range Length Error */ + XM_RXE_SYM_ERR = 0x0344, /* 32 bit r/o Rx Symbol Error Counter */ + XM_RXE_SHT_ERR = 0x0348, /* 32 bit r/o Rx Short Event Error Cnt */ + XM_RXE_RUNT = 0x034c, /* 32 bit r/o Rx Runt Event Counter */ + XM_RXF_LNG_ERR = 0x0350, /* 32 bit r/o Rx Frame too Long Error Cnt */ + XM_RXF_FCS_ERR = 0x0354, /* 32 bit r/o Rx Frame Check Seq. Error Cnt */ + XM_RXF_CEX_ERR = 0x035c, /* 32 bit r/o Rx Carrier Ext Error Frame Cnt*/ + XM_RXP_UTIL = 0x0360, /* 32 bit r/o Rx Utilization in % */ + XM_RXF_64B = 0x0368, /* 32 bit r/o 64 Byte Rx Frame Counter */ + XM_RXF_127B = 0x036c, /* 32 bit r/o 65-127 Byte Rx Frame Counter */ + XM_RXF_255B = 0x0370, /* 32 bit r/o 128-255 Byte Rx Frame Counter */ + XM_RXF_511B = 0x0374, /* 32 bit r/o 256-511 Byte Rx Frame Counter */ + XM_RXF_1023B = 0x0378, /* 32 bit r/o 512-1023 Byte Rx Frame Counter*/ + XM_RXF_MAX_SZ = 0x037c, /* 32 bit r/o 1024-MaxSize Byte Rx Frame Cnt*/ +}; + +/* XM_MMU_CMD 16 bit r/w MMU Command Register */ +enum { + XM_MMU_PHY_RDY = 1<<12,/* Bit 12: PHY Read Ready */ + XM_MMU_PHY_BUSY = 1<<11,/* Bit 11: PHY Busy */ + XM_MMU_IGN_PF = 1<<10,/* Bit 10: Ignore Pause Frame */ + XM_MMU_MAC_LB = 1<<9, /* Bit 9: Enable MAC Loopback */ + XM_MMU_FRC_COL = 1<<7, /* Bit 7: Force Collision */ + XM_MMU_SIM_COL = 1<<6, /* Bit 6: Simulate Collision */ + XM_MMU_NO_PRE = 1<<5, /* Bit 5: No MDIO Preamble */ + XM_MMU_GMII_FD = 1<<4, /* Bit 4: GMII uses Full Duplex */ + XM_MMU_RAT_CTRL = 1<<3, /* Bit 3: Enable Rate Control */ + XM_MMU_GMII_LOOP= 1<<2, /* Bit 2: PHY is in Loopback Mode */ + XM_MMU_ENA_RX = 1<<1, /* Bit 1: Enable Receiver */ + XM_MMU_ENA_TX = 1<<0, /* Bit 0: Enable Transmitter */ +}; + + +/* XM_TX_CMD 16 bit r/w Transmit Command Register */ +enum { + XM_TX_BK2BK = 1<<6, /* Bit 6: Ignor Carrier Sense (Tx Bk2Bk)*/ + XM_TX_ENC_BYP = 1<<5, /* Bit 5: Set Encoder in Bypass Mode */ + XM_TX_SAM_LINE = 1<<4, /* Bit 4: (sc) Start utilization calculation */ + XM_TX_NO_GIG_MD = 1<<3, /* Bit 3: Disable Carrier Extension */ + XM_TX_NO_PRE = 1<<2, /* Bit 2: Disable Preamble Generation */ + XM_TX_NO_CRC = 1<<1, /* Bit 1: Disable CRC Generation */ + XM_TX_AUTO_PAD = 1<<0, /* Bit 0: Enable Automatic Padding */ +}; + +/* XM_TX_RT_LIM 16 bit r/w Transmit Retry Limit Register */ +#define XM_RT_LIM_MSK 0x1f /* Bit 4..0: Tx Retry Limit */ + + +/* XM_TX_STIME 16 bit r/w Transmit Slottime Register */ +#define XM_STIME_MSK 0x7f /* Bit 6..0: Tx Slottime bits */ + + +/* XM_TX_IPG 16 bit r/w Transmit Inter Packet Gap */ +#define XM_IPG_MSK 0xff /* Bit 7..0: IPG value bits */ + + +/* XM_RX_CMD 16 bit r/w Receive Command Register */ +enum { + XM_RX_LENERR_OK = 1<<8, /* Bit 8 don't set Rx Err bit for */ + /* inrange error packets */ + XM_RX_BIG_PK_OK = 1<<7, /* Bit 7 don't set Rx Err bit for */ + /* jumbo packets */ + XM_RX_IPG_CAP = 1<<6, /* Bit 6 repl. type field with IPG */ + XM_RX_TP_MD = 1<<5, /* Bit 5: Enable transparent Mode */ + XM_RX_STRIP_FCS = 1<<4, /* Bit 4: Enable FCS Stripping */ + XM_RX_SELF_RX = 1<<3, /* Bit 3: Enable Rx of own packets */ + XM_RX_SAM_LINE = 1<<2, /* Bit 2: (sc) Start utilization calculation */ + XM_RX_STRIP_PAD = 1<<1, /* Bit 1: Strip pad bytes of Rx frames */ + XM_RX_DIS_CEXT = 1<<0, /* Bit 0: Disable carrier ext. check */ +}; + + +/* XM_PHY_ADDR 16 bit r/w PHY Address Register */ +#define XM_PHY_ADDR_SZ 0x1f /* Bit 4..0: PHY Address bits */ + + +/* XM_GP_PORT 32 bit r/w General Purpose Port Register */ +enum { + XM_GP_ANIP = 1<<6, /* Bit 6: (ro) Auto-Neg. in progress */ + XM_GP_FRC_INT = 1<<5, /* Bit 5: (sc) Force Interrupt */ + XM_GP_RES_MAC = 1<<3, /* Bit 3: (sc) Reset MAC and FIFOs */ + XM_GP_RES_STAT = 1<<2, /* Bit 2: (sc) Reset the statistics module */ + XM_GP_INP_ASS = 1<<0, /* Bit 0: (ro) GP Input Pin asserted */ +}; + + +/* XM_IMSK 16 bit r/w Interrupt Mask Register */ +/* XM_ISRC 16 bit r/o Interrupt Status Register */ +enum { + XM_IS_LNK_AE = 1<<14, /* Bit 14: Link Asynchronous Event */ + XM_IS_TX_ABORT = 1<<13, /* Bit 13: Transmit Abort, late Col. etc */ + XM_IS_FRC_INT = 1<<12, /* Bit 12: Force INT bit set in GP */ + XM_IS_INP_ASS = 1<<11, /* Bit 11: Input Asserted, GP bit 0 set */ + XM_IS_LIPA_RC = 1<<10, /* Bit 10: Link Partner requests config */ + XM_IS_RX_PAGE = 1<<9, /* Bit 9: Page Received */ + XM_IS_TX_PAGE = 1<<8, /* Bit 8: Next Page Loaded for Transmit */ + XM_IS_AND = 1<<7, /* Bit 7: Auto-Negotiation Done */ + XM_IS_TSC_OV = 1<<6, /* Bit 6: Time Stamp Counter Overflow */ + XM_IS_RXC_OV = 1<<5, /* Bit 5: Rx Counter Event Overflow */ + XM_IS_TXC_OV = 1<<4, /* Bit 4: Tx Counter Event Overflow */ + XM_IS_RXF_OV = 1<<3, /* Bit 3: Receive FIFO Overflow */ + XM_IS_TXF_UR = 1<<2, /* Bit 2: Transmit FIFO Underrun */ + XM_IS_TX_COMP = 1<<1, /* Bit 1: Frame Tx Complete */ + XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */ +}; + +#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | \ + XM_IS_AND | XM_IS_RXC_OV | XM_IS_TXC_OV | \ + XM_IS_RXF_OV | XM_IS_TXF_UR)) + + +/* XM_HW_CFG 16 bit r/w Hardware Config Register */ +enum { + XM_HW_GEN_EOP = 1<<3, /* Bit 3: generate End of Packet pulse */ + XM_HW_COM4SIG = 1<<2, /* Bit 2: use Comma Detect for Sig. Det.*/ + XM_HW_GMII_MD = 1<<0, /* Bit 0: GMII Interface selected */ +}; + + +/* XM_TX_LO_WM 16 bit r/w Tx FIFO Low Water Mark */ +/* XM_TX_HI_WM 16 bit r/w Tx FIFO High Water Mark */ +#define XM_TX_WM_MSK 0x01ff /* Bit 9.. 0 Tx FIFO Watermark bits */ + +/* XM_TX_THR 16 bit r/w Tx Request Threshold */ +/* XM_HT_THR 16 bit r/w Host Request Threshold */ +/* XM_RX_THR 16 bit r/w Rx Request Threshold */ +#define XM_THR_MSK 0x03ff /* Bit 10.. 0 Rx/Tx Request Threshold bits */ + + +/* XM_TX_STAT 32 bit r/o Tx Status LIFO Register */ +enum { + XM_ST_VALID = (1UL<<31), /* Bit 31: Status Valid */ + XM_ST_BYTE_CNT = (0x3fffL<<17), /* Bit 30..17: Tx frame Length */ + XM_ST_RETRY_CNT = (0x1fL<<12), /* Bit 16..12: Retry Count */ + XM_ST_EX_COL = 1<<11, /* Bit 11: Excessive Collisions */ + XM_ST_EX_DEF = 1<<10, /* Bit 10: Excessive Deferral */ + XM_ST_BURST = 1<<9, /* Bit 9: p. xmitted in burst md*/ + XM_ST_DEFER = 1<<8, /* Bit 8: packet was defered */ + XM_ST_BC = 1<<7, /* Bit 7: Broadcast packet */ + XM_ST_MC = 1<<6, /* Bit 6: Multicast packet */ + XM_ST_UC = 1<<5, /* Bit 5: Unicast packet */ + XM_ST_TX_UR = 1<<4, /* Bit 4: FIFO Underrun occured */ + XM_ST_CS_ERR = 1<<3, /* Bit 3: Carrier Sense Error */ + XM_ST_LAT_COL = 1<<2, /* Bit 2: Late Collision Error */ + XM_ST_MUL_COL = 1<<1, /* Bit 1: Multiple Collisions */ + XM_ST_SGN_COL = 1<<0, /* Bit 0: Single Collision */ +}; + +/* XM_RX_LO_WM 16 bit r/w Receive Low Water Mark */ +/* XM_RX_HI_WM 16 bit r/w Receive High Water Mark */ +#define XM_RX_WM_MSK 0x03ff /* Bit 11.. 0: Rx FIFO Watermark bits */ + + +/* XM_DEV_ID 32 bit r/o Device ID Register */ +#define XM_DEV_OUI (0x00ffffffUL<<8) /* Bit 31..8: Device OUI */ +#define XM_DEV_REV (0x07L << 5) /* Bit 7..5: Chip Rev Num */ + + +/* XM_MODE 32 bit r/w Mode Register */ +enum { + XM_MD_ENA_REJ = 1<<26, /* Bit 26: Enable Frame Reject */ + XM_MD_SPOE_E = 1<<25, /* Bit 25: Send Pause on Edge */ + /* extern generated */ + XM_MD_TX_REP = 1<<24, /* Bit 24: Transmit Repeater Mode */ + XM_MD_SPOFF_I = 1<<23, /* Bit 23: Send Pause on FIFO full */ + /* intern generated */ + XM_MD_LE_STW = 1<<22, /* Bit 22: Rx Stat Word in Little Endian */ + XM_MD_TX_CONT = 1<<21, /* Bit 21: Send Continuous */ + XM_MD_TX_PAUSE = 1<<20, /* Bit 20: (sc) Send Pause Frame */ + XM_MD_ATS = 1<<19, /* Bit 19: Append Time Stamp */ + XM_MD_SPOL_I = 1<<18, /* Bit 18: Send Pause on Low */ + /* intern generated */ + XM_MD_SPOH_I = 1<<17, /* Bit 17: Send Pause on High */ + /* intern generated */ + XM_MD_CAP = 1<<16, /* Bit 16: Check Address Pair */ + XM_MD_ENA_HASH = 1<<15, /* Bit 15: Enable Hashing */ + XM_MD_CSA = 1<<14, /* Bit 14: Check Station Address */ + XM_MD_CAA = 1<<13, /* Bit 13: Check Address Array */ + XM_MD_RX_MCTRL = 1<<12, /* Bit 12: Rx MAC Control Frame */ + XM_MD_RX_RUNT = 1<<11, /* Bit 11: Rx Runt Frames */ + XM_MD_RX_IRLE = 1<<10, /* Bit 10: Rx in Range Len Err Frame */ + XM_MD_RX_LONG = 1<<9, /* Bit 9: Rx Long Frame */ + XM_MD_RX_CRCE = 1<<8, /* Bit 8: Rx CRC Error Frame */ + XM_MD_RX_ERR = 1<<7, /* Bit 7: Rx Error Frame */ + XM_MD_DIS_UC = 1<<6, /* Bit 6: Disable Rx Unicast */ + XM_MD_DIS_MC = 1<<5, /* Bit 5: Disable Rx Multicast */ + XM_MD_DIS_BC = 1<<4, /* Bit 4: Disable Rx Broadcast */ + XM_MD_ENA_PROM = 1<<3, /* Bit 3: Enable Promiscuous */ + XM_MD_ENA_BE = 1<<2, /* Bit 2: Enable Big Endian */ + XM_MD_FTF = 1<<1, /* Bit 1: (sc) Flush Tx FIFO */ + XM_MD_FRF = 1<<0, /* Bit 0: (sc) Flush Rx FIFO */ +}; + +#define XM_PAUSE_MODE (XM_MD_SPOE_E | XM_MD_SPOL_I | XM_MD_SPOH_I) +#define XM_DEF_MODE (XM_MD_RX_RUNT | XM_MD_RX_IRLE | XM_MD_RX_LONG |\ + XM_MD_RX_CRCE | XM_MD_RX_ERR | XM_MD_CSA | XM_MD_CAA) + +/* XM_STAT_CMD 16 bit r/w Statistics Command Register */ +enum { + XM_SC_SNP_RXC = 1<<5, /* Bit 5: (sc) Snap Rx Counters */ + XM_SC_SNP_TXC = 1<<4, /* Bit 4: (sc) Snap Tx Counters */ + XM_SC_CP_RXC = 1<<3, /* Bit 3: Copy Rx Counters Continuously */ + XM_SC_CP_TXC = 1<<2, /* Bit 2: Copy Tx Counters Continuously */ + XM_SC_CLR_RXC = 1<<1, /* Bit 1: (sc) Clear Rx Counters */ + XM_SC_CLR_TXC = 1<<0, /* Bit 0: (sc) Clear Tx Counters */ +}; + + +/* XM_RX_CNT_EV 32 bit r/o Rx Counter Event Register */ +/* XM_RX_EV_MSK 32 bit r/w Rx Counter Event Mask */ +enum { + XMR_MAX_SZ_OV = 1<<31, /* Bit 31: 1024-MaxSize Rx Cnt Ov*/ + XMR_1023B_OV = 1<<30, /* Bit 30: 512-1023Byte Rx Cnt Ov*/ + XMR_511B_OV = 1<<29, /* Bit 29: 256-511 Byte Rx Cnt Ov*/ + XMR_255B_OV = 1<<28, /* Bit 28: 128-255 Byte Rx Cnt Ov*/ + XMR_127B_OV = 1<<27, /* Bit 27: 65-127 Byte Rx Cnt Ov */ + XMR_64B_OV = 1<<26, /* Bit 26: 64 Byte Rx Cnt Ov */ + XMR_UTIL_OV = 1<<25, /* Bit 25: Rx Util Cnt Overflow */ + XMR_UTIL_UR = 1<<24, /* Bit 24: Rx Util Cnt Underrun */ + XMR_CEX_ERR_OV = 1<<23, /* Bit 23: CEXT Err Cnt Ov */ + XMR_FCS_ERR_OV = 1<<21, /* Bit 21: Rx FCS Error Cnt Ov */ + XMR_LNG_ERR_OV = 1<<20, /* Bit 20: Rx too Long Err Cnt Ov*/ + XMR_RUNT_OV = 1<<19, /* Bit 19: Runt Event Cnt Ov */ + XMR_SHT_ERR_OV = 1<<18, /* Bit 18: Rx Short Ev Err Cnt Ov*/ + XMR_SYM_ERR_OV = 1<<17, /* Bit 17: Rx Sym Err Cnt Ov */ + XMR_CAR_ERR_OV = 1<<15, /* Bit 15: Rx Carr Ev Err Cnt Ov */ + XMR_JAB_PKT_OV = 1<<14, /* Bit 14: Rx Jabb Packet Cnt Ov */ + XMR_FIFO_OV = 1<<13, /* Bit 13: Rx FIFO Ov Ev Cnt Ov */ + XMR_FRA_ERR_OV = 1<<12, /* Bit 12: Rx Framing Err Cnt Ov */ + XMR_FMISS_OV = 1<<11, /* Bit 11: Rx Missed Ev Cnt Ov */ + XMR_BURST = 1<<10, /* Bit 10: Rx Burst Event Cnt Ov */ + XMR_INV_MOC = 1<<9, /* Bit 9: Rx with inv. MAC OC Ov*/ + XMR_INV_MP = 1<<8, /* Bit 8: Rx inv Pause Frame Ov */ + XMR_MCTRL_OV = 1<<7, /* Bit 7: Rx MAC Ctrl-F Cnt Ov */ + XMR_MPAUSE_OV = 1<<6, /* Bit 6: Rx Pause MAC Ctrl-F Ov*/ + XMR_UC_OK_OV = 1<<5, /* Bit 5: Rx Unicast Frame CntOv*/ + XMR_MC_OK_OV = 1<<4, /* Bit 4: Rx Multicast Cnt Ov */ + XMR_BC_OK_OV = 1<<3, /* Bit 3: Rx Broadcast Cnt Ov */ + XMR_OK_LO_OV = 1<<2, /* Bit 2: Octets Rx OK Low CntOv*/ + XMR_OK_HI_OV = 1<<1, /* Bit 1: Octets Rx OK Hi Cnt Ov*/ + XMR_OK_OV = 1<<0, /* Bit 0: Frames Received Ok Ov */ +}; + +#define XMR_DEF_MSK (XMR_OK_LO_OV | XMR_OK_HI_OV) + +/* XM_TX_CNT_EV 32 bit r/o Tx Counter Event Register */ +/* XM_TX_EV_MSK 32 bit r/w Tx Counter Event Mask */ +enum { + XMT_MAX_SZ_OV = 1<<25, /* Bit 25: 1024-MaxSize Tx Cnt Ov*/ + XMT_1023B_OV = 1<<24, /* Bit 24: 512-1023Byte Tx Cnt Ov*/ + XMT_511B_OV = 1<<23, /* Bit 23: 256-511 Byte Tx Cnt Ov*/ + XMT_255B_OV = 1<<22, /* Bit 22: 128-255 Byte Tx Cnt Ov*/ + XMT_127B_OV = 1<<21, /* Bit 21: 65-127 Byte Tx Cnt Ov */ + XMT_64B_OV = 1<<20, /* Bit 20: 64 Byte Tx Cnt Ov */ + XMT_UTIL_OV = 1<<19, /* Bit 19: Tx Util Cnt Overflow */ + XMT_UTIL_UR = 1<<18, /* Bit 18: Tx Util Cnt Underrun */ + XMT_CS_ERR_OV = 1<<17, /* Bit 17: Tx Carr Sen Err Cnt Ov*/ + XMT_FIFO_UR_OV = 1<<16, /* Bit 16: Tx FIFO Ur Ev Cnt Ov */ + XMT_EX_DEF_OV = 1<<15, /* Bit 15: Tx Ex Deferall Cnt Ov */ + XMT_DEF = 1<<14, /* Bit 14: Tx Deferred Cnt Ov */ + XMT_LAT_COL_OV = 1<<13, /* Bit 13: Tx Late Col Cnt Ov */ + XMT_ABO_COL_OV = 1<<12, /* Bit 12: Tx abo dueto Ex Col Ov*/ + XMT_MUL_COL_OV = 1<<11, /* Bit 11: Tx Mult Col Cnt Ov */ + XMT_SNG_COL = 1<<10, /* Bit 10: Tx Single Col Cnt Ov */ + XMT_MCTRL_OV = 1<<9, /* Bit 9: Tx MAC Ctrl Counter Ov*/ + XMT_MPAUSE = 1<<8, /* Bit 8: Tx Pause MAC Ctrl-F Ov*/ + XMT_BURST = 1<<7, /* Bit 7: Tx Burst Event Cnt Ov */ + XMT_LONG = 1<<6, /* Bit 6: Tx Long Frame Cnt Ov */ + XMT_UC_OK_OV = 1<<5, /* Bit 5: Tx Unicast Cnt Ov */ + XMT_MC_OK_OV = 1<<4, /* Bit 4: Tx Multicast Cnt Ov */ + XMT_BC_OK_OV = 1<<3, /* Bit 3: Tx Broadcast Cnt Ov */ + XMT_OK_LO_OV = 1<<2, /* Bit 2: Octets Tx OK Low CntOv*/ + XMT_OK_HI_OV = 1<<1, /* Bit 1: Octets Tx OK Hi Cnt Ov*/ + XMT_OK_OV = 1<<0, /* Bit 0: Frames Tx Ok Ov */ +}; + +#define XMT_DEF_MSK (XMT_OK_LO_OV | XMT_OK_HI_OV) + +struct skge_rx_desc { + u32 control; + u32 next_offset; + u32 dma_lo; + u32 dma_hi; + u32 status; + u32 timestamp; + u16 csum2; + u16 csum1; + u16 csum2_start; + u16 csum1_start; +}; + +struct skge_tx_desc { + u32 control; + u32 next_offset; + u32 dma_lo; + u32 dma_hi; + u32 status; + u32 csum_offs; + u16 csum_write; + u16 csum_start; + u32 rsvd; +}; + +struct skge_element { + struct skge_element *next; + void *desc; + struct sk_buff *skb; + DECLARE_PCI_UNMAP_ADDR(mapaddr); + DECLARE_PCI_UNMAP_LEN(maplen); +}; + +struct skge_ring { + struct skge_element *to_clean; + struct skge_element *to_use; + struct skge_element *start; + unsigned long count; +}; + + +struct skge_hw { + void __iomem *regs; + struct pci_dev *pdev; + u32 intr_mask; + struct net_device *dev[2]; + + u8 mac_cfg; + u8 chip_id; + u8 phy_type; + u8 pmd_type; + u16 phy_addr; + + u32 ram_size; + u32 ram_offset; + + struct tasklet_struct ext_tasklet; + spinlock_t phy_lock; +}; + +static inline int isdualport(const struct skge_hw *hw) +{ + return !(hw->mac_cfg & CFG_SNG_MAC); +} + +static inline u8 chip_rev(const struct skge_hw *hw) +{ + return (hw->mac_cfg & CFG_CHIP_R_MSK) >> 4; +} + +static inline int iscopper(const struct skge_hw *hw) +{ + return (hw->pmd_type == 'T'); +} + +enum { + FLOW_MODE_NONE = 0, /* No Flow-Control */ + FLOW_MODE_LOC_SEND = 1, /* Local station sends PAUSE */ + FLOW_MODE_REM_SEND = 2, /* Symmetric or just remote */ + FLOW_MODE_SYMMETRIC = 3, /* Both stations may send PAUSE */ +}; + +struct skge_port { + u32 msg_enable; + struct skge_hw *hw; + struct net_device *netdev; + int port; + + spinlock_t tx_lock; + u32 tx_avail; + struct skge_ring tx_ring; + struct skge_ring rx_ring; + + struct net_device_stats net_stats; + + u8 rx_csum; + u8 blink_on; + u8 flow_control; + u8 wol; + u8 autoneg; /* AUTONEG_ENABLE, AUTONEG_DISABLE */ + u8 duplex; /* DUPLEX_HALF, DUPLEX_FULL */ + u16 speed; /* SPEED_1000, SPEED_100, ... */ + u32 advertising; + + void *mem; /* PCI memory for rings */ + dma_addr_t dma; + unsigned long mem_size; + + struct timer_list link_check; + struct timer_list led_blink; +}; + + +/* Register accessor for memory mapped device */ +static inline u32 skge_read32(const struct skge_hw *hw, int reg) +{ + return readl(hw->regs + reg); + +} + +static inline u16 skge_read16(const struct skge_hw *hw, int reg) +{ + return readw(hw->regs + reg); +} + +static inline u8 skge_read8(const struct skge_hw *hw, int reg) +{ + return readb(hw->regs + reg); +} + +static inline void skge_write32(const struct skge_hw *hw, int reg, u32 val) +{ + writel(val, hw->regs + reg); +} + +static inline void skge_write16(const struct skge_hw *hw, int reg, u16 val) +{ + writew(val, hw->regs + reg); +} + +static inline void skge_write8(const struct skge_hw *hw, int reg, u8 val) +{ + writeb(val, hw->regs + reg); +} + +/* MAC Related Registers inside the device. */ +#define SKGEMAC_REG(port,reg) (((port)<<7)+(reg)) + +/* PCI config space can be accessed via memory mapped space */ +#define SKGEPCI_REG(reg) ((reg)+ 0x380) + +#define SKGEXM_REG(port, reg) \ + ((BASE_XMAC_1 + (port) * (BASE_XMAC_2 - BASE_XMAC_1)) | (reg) << 1) + +static inline u32 skge_xm_read32(const struct skge_hw *hw, int port, int reg) +{ + return skge_read32(hw, SKGEXM_REG(port,reg)); +} + +static inline u16 skge_xm_read16(const struct skge_hw *hw, int port, int reg) +{ + return skge_read16(hw, SKGEXM_REG(port,reg)); +} + +static inline u8 skge_xm_read8(const struct skge_hw *hw, int port, int reg) +{ + return skge_read8(hw, SKGEXM_REG(port,reg)); +} + +static inline void skge_xm_write32(const struct skge_hw *hw, int port, int r, u32 v) +{ + skge_write32(hw, SKGEXM_REG(port,r), v); +} + +static inline void skge_xm_write16(const struct skge_hw *hw, int port, int r, u16 v) +{ + skge_write16(hw, SKGEXM_REG(port,r), v); +} + +static inline void skge_xm_write8(const struct skge_hw *hw, int port, int r, u8 v) +{ + skge_write8(hw, SKGEXM_REG(port,r), v); +} + +static inline void skge_xm_outhash(const struct skge_hw *hw, int port, int reg, + const u8 *hash) +{ + skge_xm_write16(hw, port, reg, + (u16)hash[0] | ((u16)hash[1] << 8)); + skge_xm_write16(hw, port, reg+2, + (u16)hash[2] | ((u16)hash[3] << 8)); + skge_xm_write16(hw, port, reg+4, + (u16)hash[4] | ((u16)hash[5] << 8)); + skge_xm_write16(hw, port, reg+6, + (u16)hash[6] | ((u16)hash[7] << 8)); +} + +static inline void skge_xm_outaddr(const struct skge_hw *hw, int port, int reg, + const u8 *addr) +{ + skge_xm_write16(hw, port, reg, + (u16)addr[0] | ((u16)addr[1] << 8)); + skge_xm_write16(hw, port, reg, + (u16)addr[2] | ((u16)addr[3] << 8)); + skge_xm_write16(hw, port, reg, + (u16)addr[4] | ((u16)addr[5] << 8)); +} + + +#define SKGEGMA_REG(port,reg) \ + ((reg) + BASE_GMAC_1 + \ + (port) * (BASE_GMAC_2-BASE_GMAC_1)) + +static inline u16 skge_gma_read16(const struct skge_hw *hw, int port, int reg) +{ + return skge_read16(hw, SKGEGMA_REG(port,reg)); +} + +static inline u32 skge_gma_read32(const struct skge_hw *hw, int port, int reg) +{ + return (u32) skge_read16(hw, SKGEGMA_REG(port,reg)) + | ((u32)skge_read16(hw, SKGEGMA_REG(port,reg+4)) << 16); +} + +static inline u8 skge_gma_read8(const struct skge_hw *hw, int port, int reg) +{ + return skge_read8(hw, SKGEGMA_REG(port,reg)); +} + +static inline void skge_gma_write16(const struct skge_hw *hw, int port, int r, u16 v) +{ + skge_write16(hw, SKGEGMA_REG(port,r), v); +} + +static inline void skge_gma_write32(const struct skge_hw *hw, int port, int r, u32 v) +{ + skge_write16(hw, SKGEGMA_REG(port, r), (u16) v); + skge_write32(hw, SKGEGMA_REG(port, r+4), (u16)(v >> 16)); +} + +static inline void skge_gma_write8(const struct skge_hw *hw, int port, int r, u8 v) +{ + skge_write8(hw, SKGEGMA_REG(port,r), v); +} + +static inline void skge_gm_set_addr(struct skge_hw *hw, int port, int reg, + const u8 *addr) +{ + skge_gma_write16(hw, port, reg, + (u16) addr[0] | ((u16) addr[1] << 8)); + skge_gma_write16(hw, port, reg+4, + (u16) addr[2] | ((u16) addr[3] << 8)); + skge_gma_write16(hw, port, reg+8, + (u16) addr[4] | ((u16) addr[5] << 8)); +} + +#endif diff -Nru a/drivers/net/starfire.c b/drivers/net/starfire.c --- a/drivers/net/starfire.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/starfire.c 2005-03-15 23:46:53 -08:00 @@ -2,7 +2,7 @@ /* Written 1998-2000 by Donald Becker. - Current maintainer is Ion Badulescu . Please + Current maintainer is Ion Badulescu . Please send all bug reports to me, and not to Donald Becker, as this code has been heavily modified from Donald's original version. @@ -129,12 +129,18 @@ - put the chip to a D3 slumber on driver unload - added config option to enable/disable NAPI -TODO: bugfixes (no bugs known as of right now) + LK1.4.2 (Ion Badulescu) + - finally added firmware (GPL'ed by Adaptec) + - removed compatibility code for 2.2.x + +TODO: - fix forced speed/duplexing code (broken a long time ago, when + somebody converted the driver to use the generic MII code) + - fix VLAN support */ #define DRV_NAME "starfire" -#define DRV_VERSION "1.03+LK1.4.1" -#define DRV_RELDATE "February 10, 2002" +#define DRV_VERSION "1.03+LK1.4.2" +#define DRV_RELDATE "January 19, 2005" #include #include @@ -145,25 +151,15 @@ #include #include #include +#include +#include +#include +#include #include /* Processor type for cache alignment. */ #include #include -/* - * Adaptec's license for their drivers (which is where I got the - * firmware files) does not allow one to redistribute them. Thus, we can't - * include the firmware with this driver. - * - * However, should a legal-to-distribute firmware become available, - * the driver developer would need only to obtain the firmware in the - * form of a C header file. - * Once that's done, the #undef below must be changed into a #define - * for this driver to really use the firmware. Note that Rx/Tx - * hardware TCP checksumming is not possible without the firmware. - * - * WANTED: legal firmware to include with this GPL'd driver. - */ -#undef HAS_FIRMWARE +#include "starfire_firmware.h" /* * The current frame processor firmware fails to checksum a fragment * of length 1. If and when this is fixed, the #define below can be removed. @@ -172,13 +168,7 @@ /* * Define this if using the driver with the zero-copy patch */ -#if defined(HAS_FIRMWARE) && defined(MAX_SKB_FRAGS) #define ZEROCOPY -#endif - -#ifdef HAS_FIRMWARE -#include "starfire_firmware.h" -#endif /* HAS_FIRMWARE */ #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #define VLAN_SUPPORT @@ -202,11 +192,7 @@ The Starfire has a 512 element hash table based on the Ethernet CRC. */ static int multicast_filter_limit = 512; /* Whether to do TCP/UDP checksums in hardware */ -#ifdef HAS_FIRMWARE static int enable_hw_cksum = 1; -#else -static int enable_hw_cksum = 0; -#endif #define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/ /* @@ -291,43 +277,15 @@ #define RX_DESC_ADDR_SIZE RxDescAddr32bit #endif -#ifdef MAX_SKB_FRAGS #define skb_first_frag_len(skb) skb_headlen(skb) #define skb_num_frags(skb) (skb_shinfo(skb)->nr_frags + 1) -#else /* not MAX_SKB_FRAGS */ -#define skb_first_frag_len(skb) (skb->len) -#define skb_num_frags(skb) 1 -#endif /* not MAX_SKB_FRAGS */ - -/* 2.2.x compatibility code */ -#if LINUX_VERSION_CODE < 0x20300 - -#include "starfire-kcomp22.h" - -#else /* LINUX_VERSION_CODE > 0x20300 */ - -#include -#include -#include - -#include - -#define init_tx_timer(dev, func, timeout) \ - dev->tx_timeout = func; \ - dev->watchdog_timeo = timeout; -#define kick_tx_timer(dev, func, timeout) - -#define netif_start_if(dev) -#define netif_stop_if(dev) - -#define PCI_SLOT_NAME(pci_dev) pci_name(pci_dev) - -#endif /* LINUX_VERSION_CODE > 0x20300 */ #ifdef HAVE_NETDEV_POLL #define init_poll(dev) \ +do { \ dev->poll = &netdev_poll; \ - dev->weight = max_interrupt_work; + dev->weight = max_interrupt_work; \ +} while (0) #define netdev_rx(dev, ioaddr) \ do { \ u32 intr_enable; \ @@ -341,7 +299,7 @@ /* Paranoia check */ \ intr_enable = readl(ioaddr + IntrEnable); \ if (intr_enable & (IntrRxDone | IntrRxEmpty)) { \ - printk("%s: interrupt while in polling mode!\n", dev->name); \ + printk(KERN_INFO "%s: interrupt while in polling mode!\n", dev->name); \ intr_enable &= ~(IntrRxDone | IntrRxEmpty); \ writel(intr_enable, ioaddr + IntrEnable); \ } \ @@ -371,6 +329,7 @@ MODULE_AUTHOR("Donald Becker "); MODULE_DESCRIPTION("Adaptec Starfire Ethernet driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); module_param(max_interrupt_work, int, 0); module_param(mtu, int, 0); @@ -425,7 +384,7 @@ minimum-length padding. It does not use the completion queue consumer index, but instead checks for non-zero status entries. -For receive this driver uses type 0/1/2/3 receive descriptors. The driver +For receive this driver uses type 2/3 receive descriptors. The driver allocates full frame size skbuffs for the Rx ring buffers, so all frames should fit in a single descriptor. The driver does not use the completion queue consumer index, but instead checks for non-zero status entries. @@ -476,7 +435,7 @@ */ - + enum chip_capability_flags {CanHaveMII=1, }; @@ -670,7 +629,6 @@ u32 timestamp; }; /* XXX: this is ugly and I'm not sure it's worth the trouble -Ion */ -#ifdef HAS_FIRMWARE #ifdef VLAN_SUPPORT typedef struct full_rx_done_desc rx_done_desc; #define RxComplType RxComplType3 @@ -678,15 +636,6 @@ typedef struct csum_rx_done_desc rx_done_desc; #define RxComplType RxComplType2 #endif /* not VLAN_SUPPORT */ -#else /* not HAS_FIRMWARE */ -#ifdef VLAN_SUPPORT -typedef struct basic_rx_done_desc rx_done_desc; -#define RxComplType RxComplType1 -#else /* not VLAN_SUPPORT */ -typedef struct short_rx_done_desc rx_done_desc; -#define RxComplType RxComplType0 -#endif /* not VLAN_SUPPORT */ -#endif /* not HAS_FIRMWARE */ enum rx_done_bits { RxOK=0x20000000, RxFIFOErr=0x10000000, RxBufQ2=0x08000000, @@ -898,13 +847,10 @@ /* enable MWI -- it vastly improves Rx performance on sparc64 */ pci_set_mwi(pdev); -#ifdef MAX_SKB_FRAGS - dev->features |= NETIF_F_SG; -#endif /* MAX_SKB_FRAGS */ #ifdef ZEROCOPY /* Starfire can do TCP/UDP checksumming */ if (enable_hw_cksum) - dev->features |= NETIF_F_IP_CSUM; + dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; #endif /* ZEROCOPY */ #ifdef VLAN_SUPPORT dev->features |= NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; @@ -1008,7 +954,8 @@ /* The chip-specific entries in the device structure. */ dev->open = &netdev_open; dev->hard_start_xmit = &start_tx; - init_tx_timer(dev, tx_timeout, TX_TIMEOUT); + dev->tx_timeout = tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; init_poll(dev); dev->stop = &netdev_close; dev->get_stats = &get_stats; @@ -1039,7 +986,7 @@ if ((mdio_read(dev, phy, MII_BMCR) & BMCR_RESET) == 0) break; if (boguscnt == 0) { - printk("%s: PHY reset never completed!\n", dev->name); + printk("%s: PHY#%d reset never completed!\n", dev->name, phy); continue; } mii_status = mdio_read(dev, phy, MII_BMSR); @@ -1110,6 +1057,7 @@ size_t tx_done_q_size, rx_done_q_size, tx_ring_size, rx_ring_size; /* Do we ever need to reset the chip??? */ + retval = request_irq(dev->irq, &intr_handler, SA_SHIRQ, dev->name, dev); if (retval) return retval; @@ -1211,7 +1159,6 @@ writel(np->intr_timer_ctrl, ioaddr + IntrTimerCtrl); - netif_start_if(dev); netif_start_queue(dev); if (debug > 1) @@ -1238,13 +1185,11 @@ writel(ETH_P_8021Q, ioaddr + VlanType); #endif /* VLAN_SUPPORT */ -#ifdef HAS_FIRMWARE /* Load Rx/Tx firmware into the frame processors */ for (i = 0; i < FIRMWARE_RX_SIZE * 2; i++) writel(firmware_rx[i], ioaddr + RxGfpMem + i * 4); for (i = 0; i < FIRMWARE_TX_SIZE * 2; i++) writel(firmware_tx[i], ioaddr + TxGfpMem + i * 4); -#endif /* HAS_FIRMWARE */ if (enable_hw_cksum) /* Enable the Rx and Tx units, and the Rx/Tx frame processors. */ writel(TxEnable|TxGFPEnable|RxEnable|RxGFPEnable, ioaddr + GenCtrl); @@ -1378,8 +1323,6 @@ u32 status; int i; - kick_tx_timer(dev, tx_timeout, TX_TIMEOUT); - /* * be cautious here, wrapping the queue has weird semantics * and we may not have enough slots even when it seems we do. @@ -1404,7 +1347,7 @@ } if (has_bad_length) - skb_checksum_help(skb); + skb_checksum_help(skb, 0); } #endif /* ZEROCOPY && HAS_BROKEN_FIRMWARE */ @@ -1433,12 +1376,10 @@ np->tx_info[entry].mapping = pci_map_single(np->pci_dev, skb->data, skb_first_frag_len(skb), PCI_DMA_TODEVICE); } else { -#ifdef MAX_SKB_FRAGS skb_frag_t *this_frag = &skb_shinfo(skb)->frags[i - 1]; status |= this_frag->size; np->tx_info[entry].mapping = pci_map_single(np->pci_dev, page_address(this_frag->page) + this_frag->page_offset, this_frag->size, PCI_DMA_TODEVICE); -#endif /* MAX_SKB_FRAGS */ } np->tx_ring[entry].addr = cpu_to_dma(np->tx_info[entry].mapping); @@ -1531,7 +1472,6 @@ np->tx_info[entry].mapping = 0; np->dirty_tx += np->tx_info[entry].used_slots; entry = (entry + np->tx_info[entry].used_slots) % TX_RING_SIZE; -#ifdef MAX_SKB_FRAGS { int i; for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { @@ -1543,7 +1483,7 @@ entry++; } } -#endif /* MAX_SKB_FRAGS */ + dev_kfree_skb_irq(skb); } np->tx_done_q[np->tx_done].status = 0; @@ -1603,7 +1543,7 @@ if (debug > 4) printk(KERN_DEBUG " netdev_rx() status of %d was %#8.8x.\n", np->rx_done, desc_status); if (!(desc_status & RxOK)) { - /* There was a error. */ + /* There was an error. */ if (debug > 2) printk(KERN_DEBUG " netdev_rx() Rx error was %#8.8x.\n", desc_status); np->stats.rx_errors++; @@ -1656,11 +1596,10 @@ #endif skb->protocol = eth_type_trans(skb, dev); -#if defined(HAS_FIRMWARE) || defined(VLAN_SUPPORT) +#ifdef VLAN_SUPPORT if (debug > 4) printk(KERN_DEBUG " netdev_rx() status2 of %d was %#4.4x.\n", np->rx_done, le16_to_cpu(desc->status2)); #endif -#ifdef HAS_FIRMWARE if (le16_to_cpu(desc->status2) & 0x0100) { skb->ip_summed = CHECKSUM_UNNECESSARY; np->stats.rx_compressed++; @@ -1679,7 +1618,6 @@ skb->csum = le16_to_cpu(desc->csum); printk(KERN_DEBUG "%s: checksum_hw, status2 = %#x\n", dev->name, le16_to_cpu(desc->status2)); } -#endif /* HAS_FIRMWARE */ #ifdef VLAN_SUPPORT if (np->vlgrp && le16_to_cpu(desc->status2) & 0x0200) { if (debug > 4) @@ -1900,9 +1838,6 @@ } -/* Chips may use the upper or lower CRC bits, and may reverse and/or invert - them. Select the endian-ness that results in minimal calculations. -*/ static void set_rx_mode(struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); @@ -1969,6 +1904,8 @@ memset(mc_filter, 0, sizeof(mc_filter)); for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count; i++, mclist = mclist->next) { + /* The chip uses the upper 9 CRC bits + as index into the hash table */ int bit_nr = ether_crc_le(ETH_ALEN, mclist->dmi_addr) >> 23; __u32 *fptr = (__u32 *) &mc_filter[(bit_nr >> 4) & ~1]; @@ -2001,7 +1938,7 @@ struct netdev_private *np = netdev_priv(dev); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); - strcpy(info->bus_info, PCI_SLOT_NAME(np->pci_dev)); + strcpy(info->bus_info, pci_name(np->pci_dev)); } static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) @@ -2083,7 +2020,6 @@ int i; netif_stop_queue(dev); - netif_stop_if(dev); if (debug > 1) { printk(KERN_DEBUG "%s: Shutting down ethercard, Intr status %#8.8x.\n", @@ -2184,7 +2120,13 @@ /* when a module, this is printed whether or not devices are found in probe */ #ifdef MODULE printk(version); +#ifdef HAVE_NETDEV_POLL + printk(KERN_INFO DRV_NAME ": polling (NAPI) enabled\n"); +#else + printk(KERN_INFO DRV_NAME ": polling (NAPI) disabled\n"); #endif +#endif + #ifndef ADDR_64BITS /* we can do this test only at run-time... sigh */ if (sizeof(dma_addr_t) == sizeof(u64)) { @@ -2192,10 +2134,6 @@ return -ENODEV; } #endif /* not ADDR_64BITS */ -#ifndef HAS_FIRMWARE - /* unconditionally disable hw cksums if firmware is not present */ - enable_hw_cksum = 0; -#endif /* not HAS_FIRMWARE */ return pci_module_init (&starfire_driver); } diff -Nru a/drivers/net/starfire_firmware.h b/drivers/net/starfire_firmware.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/starfire_firmware.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,346 @@ +/* + * Copyright 2003 Adaptec, Inc. + * + * Please read the following license before using the Adaptec Software + * ("Program"). If you do not agree to the license terms, do not use the + * Program: + * + * You agree to be bound by version 2 of the General Public License ("GPL") + * dated June 1991, which can be found at http://www.fsf.org/licenses/gpl.html. + * If the link is broken, write to Free Software Foundation, 59 Temple Place, + * Boston, Massachusetts 02111-1307. + * + * BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE IT IS LICENSED "AS IS" AND + * THERE IS NO WARRANTY FOR THE PROGRAM, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR A PARTICULAR PURPOSE + * (TO THE EXTENT PERMITTED BY APPLICABLE LAW). USE OF THE PROGRAM IS AT YOUR + * OWN RISK. IN NO EVENT WILL ADAPTEC OR ITS LICENSORS BE LIABLE TO YOU FOR + * DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM. + * + */ + +static const u32 firmware_rx[] = { + 0x010003dc, 0x00000000, + 0x04000421, 0x00000086, + 0x80000015, 0x0000180e, + 0x81000015, 0x00006664, + 0x1a0040ab, 0x00000b06, + 0x14200011, 0x00000000, + 0x14204022, 0x0000aaaa, + 0x14204022, 0x00000300, + 0x14204022, 0x00000000, + 0x1a0040ab, 0x00000b14, + 0x14200011, 0x00000000, + 0x83000015, 0x00000002, + 0x04000021, 0x00000000, + 0x00000010, 0x00000000, + 0x04000421, 0x00000087, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00008015, 0x00000000, + 0x0000003e, 0x00000000, + 0x00000010, 0x00000000, + 0x82000015, 0x00004000, + 0x009e8050, 0x00000000, + 0x03008015, 0x00000000, + 0x86008015, 0x00000000, + 0x82000015, 0x00008000, + 0x0100001c, 0x00000000, + 0x000050a0, 0x0000010c, + 0x4e20d011, 0x00006008, + 0x1420d012, 0x00004008, + 0x0000f090, 0x00007000, + 0x0000c8b0, 0x00003000, + 0x00004040, 0x00000000, + 0x00108015, 0x00000000, + 0x00a2c150, 0x00004000, + 0x00a400b0, 0x00000014, + 0x00000020, 0x00000000, + 0x2500400d, 0x00002525, + 0x00047220, 0x00003100, + 0x00934070, 0x00000000, + 0x00000020, 0x00000000, + 0x00924460, 0x00000184, + 0x2b20c011, 0x00000000, + 0x0000c420, 0x00000540, + 0x36014018, 0x0000422d, + 0x14200011, 0x00000000, + 0x00924460, 0x00000183, + 0x3200001f, 0x00000034, + 0x02ac0015, 0x00000002, + 0x00a60110, 0x00000008, + 0x42200011, 0x00000000, + 0x00924060, 0x00000103, + 0x0000001e, 0x00000000, + 0x00000020, 0x00000100, + 0x0000001e, 0x00000000, + 0x00924460, 0x00000086, + 0x00004080, 0x00000000, + 0x0092c070, 0x00000000, + 0x00924060, 0x00000100, + 0x0000c890, 0x00005000, + 0x00a6c110, 0x00000000, + 0x00b0c090, 0x00000012, + 0x021c0015, 0x00000000, + 0x3200001f, 0x00000034, + 0x00924460, 0x00000510, + 0x44210011, 0x00000000, + 0x42000011, 0x00000000, + 0x83000015, 0x00000040, + 0x00924460, 0x00000508, + 0x45014018, 0x00004545, + 0x00808050, 0x00000000, + 0x62208012, 0x00000000, + 0x82000015, 0x00000800, + 0x15200011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x80000015, 0x0000eea4, + 0x81000015, 0x0000005f, + 0x00000060, 0x00000000, + 0x00004120, 0x00000000, + 0x00004a00, 0x00004000, + 0x00924460, 0x00000190, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00934050, 0x00000018, + 0x00930050, 0x00000018, + 0x3601403a, 0x0000002d, + 0x000643a9, 0x00000000, + 0x0000c420, 0x00000140, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x000642a9, 0x00000000, + 0x00024420, 0x00000183, + 0x5601401a, 0x00005956, + 0x82000015, 0x00002000, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, +}; /* 104 Rx instructions */ +#define FIRMWARE_RX_SIZE 104 + +static const u32 firmware_tx[] = { + 0x010003dc, 0x00000000, + 0x04000421, 0x00000086, + 0x80000015, 0x0000180e, + 0x81000015, 0x00006664, + 0x1a0040ab, 0x00000b06, + 0x14200011, 0x00000000, + 0x14204022, 0x0000aaaa, + 0x14204022, 0x00000300, + 0x14204022, 0x00000000, + 0x1a0040ab, 0x00000b14, + 0x14200011, 0x00000000, + 0x83000015, 0x00000002, + 0x04000021, 0x00000000, + 0x00000010, 0x00000000, + 0x04000421, 0x00000087, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00008015, 0x00000000, + 0x0000003e, 0x00000000, + 0x00000010, 0x00000000, + 0x82000015, 0x00004000, + 0x009e8050, 0x00000000, + 0x03008015, 0x00000000, + 0x86008015, 0x00000000, + 0x82000015, 0x00008000, + 0x0100001c, 0x00000000, + 0x000050a0, 0x0000010c, + 0x4e20d011, 0x00006008, + 0x1420d012, 0x00004008, + 0x0000f090, 0x00007000, + 0x0000c8b0, 0x00003000, + 0x00004040, 0x00000000, + 0x00108015, 0x00000000, + 0x00a2c150, 0x00004000, + 0x00a400b0, 0x00000014, + 0x00000020, 0x00000000, + 0x2500400d, 0x00002525, + 0x00047220, 0x00003100, + 0x00934070, 0x00000000, + 0x00000020, 0x00000000, + 0x00924460, 0x00000184, + 0x2b20c011, 0x00000000, + 0x0000c420, 0x00000540, + 0x36014018, 0x0000422d, + 0x14200011, 0x00000000, + 0x00924460, 0x00000183, + 0x3200001f, 0x00000034, + 0x02ac0015, 0x00000002, + 0x00a60110, 0x00000008, + 0x42200011, 0x00000000, + 0x00924060, 0x00000103, + 0x0000001e, 0x00000000, + 0x00000020, 0x00000100, + 0x0000001e, 0x00000000, + 0x00924460, 0x00000086, + 0x00004080, 0x00000000, + 0x0092c070, 0x00000000, + 0x00924060, 0x00000100, + 0x0000c890, 0x00005000, + 0x00a6c110, 0x00000000, + 0x00b0c090, 0x00000012, + 0x021c0015, 0x00000000, + 0x3200001f, 0x00000034, + 0x00924460, 0x00000510, + 0x44210011, 0x00000000, + 0x42000011, 0x00000000, + 0x83000015, 0x00000040, + 0x00924460, 0x00000508, + 0x45014018, 0x00004545, + 0x00808050, 0x00000000, + 0x62208012, 0x00000000, + 0x82000015, 0x00000800, + 0x15200011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x80000015, 0x0000eea4, + 0x81000015, 0x0000005f, + 0x00000060, 0x00000000, + 0x00004120, 0x00000000, + 0x00004a00, 0x00004000, + 0x00924460, 0x00000190, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00934050, 0x00000018, + 0x00930050, 0x00000018, + 0x3601403a, 0x0000002d, + 0x000643a9, 0x00000000, + 0x0000c420, 0x00000140, + 0x5601401a, 0x00005956, + 0x14000011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x000642a9, 0x00000000, + 0x00024420, 0x00000183, + 0x5601401a, 0x00005956, + 0x82000015, 0x00002000, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, + 0x82000015, 0x00000010, + 0x15200011, 0x00000000, +}; /* 104 Tx instructions */ +#define FIRMWARE_TX_SIZE 104 +#if 0 +static const u32 firmware_wol[] = { + 0x010003dc, 0x00000000, + 0x19000421, 0x00000087, + 0x80000015, 0x00001a1a, + 0x81000015, 0x00001a1a, + 0x1a0040ab, 0x00000b06, + 0x15200011, 0x00000000, + 0x15204022, 0x0000aaaa, + 0x15204022, 0x00000300, + 0x15204022, 0x00000000, + 0x1a0040ab, 0x00000b15, + 0x15200011, 0x00000000, + 0x83000015, 0x00000002, + 0x04000021, 0x00000000, + 0x00000010, 0x00000000, + 0x04000421, 0x00000087, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00008015, 0x00000000, + 0x0000003e, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x82000015, 0x00004000, + 0x82000015, 0x00008000, + 0x0000000c, 0x00000000, + 0x00000010, 0x00000000, + 0x00004080, 0x00000100, + 0x1f20c011, 0x00001122, + 0x2720f011, 0x00003011, + 0x19200071, 0x00000000, + 0x1a200051, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x1d2040a4, 0x00003344, + 0x1d2040a2, 0x00005566, + 0x000040a0, 0x00000100, + 0x00108050, 0x00000001, + 0x1a208012, 0x00000006, + 0x82000015, 0x00008080, + 0x010003dc, 0x00000000, + 0x1d2040a4, 0x00002233, + 0x1d2040a4, 0x00004455, + 0x2d208011, 0x00000005, + 0x1d2040a4, 0x00006611, + 0x00108050, 0x00000001, + 0x27200011, 0x00000000, + 0x1d2050a4, 0x00006600, + 0x82000015, 0x00008080, + 0x010003dc, 0x00000000, + 0x00000050, 0x00000000, + 0x1b200031, 0x00000000, + 0x0000001e, 0x00000000, + 0x0000001e, 0x00000000, + 0x0000001e, 0x00000000, + 0x0000001e, 0x00000000, + 0x00924460, 0x00000086, + 0x00004080, 0x00000000, + 0x0092c070, 0x00000000, + 0x00924060, 0x00000100, + 0x0000c890, 0x00005000, + 0x00a6c110, 0x00000000, + 0x00b0c090, 0x00000012, + 0x021c0015, 0x00000000, + 0x3200001f, 0x00000034, + 0x00924460, 0x00000510, + 0x44210011, 0x00000000, + 0x42000011, 0x00000000, + 0x83000015, 0x00000040, + 0x00924460, 0x00000508, + 0x476a0012, 0x00000100, + 0x83000015, 0x00000008, + 0x16200011, 0x00000000, + 0x001e8050, 0x00000000, + 0x001e8050, 0x00000000, + 0x00808050, 0x00000000, + 0x03008015, 0x00000000, + 0x62208012, 0x00000000, + 0x82000015, 0x00000800, + 0x16200011, 0x00000000, + 0x80000015, 0x0000eea4, + 0x81000015, 0x0000005f, + 0x00000020, 0x00000000, + 0x00004120, 0x00000000, + 0x00004a00, 0x00004000, + 0x00924460, 0x00000190, + 0x5c01401a, 0x0000595c, + 0x15000011, 0x00000000, + 0x00934050, 0x00000018, + 0x00930050, 0x00000018, + 0x3601403a, 0x0000002d, + 0x00064029, 0x00000000, + 0x0000c420, 0x00000140, + 0x5c01401a, 0x0000595c, + 0x15000011, 0x00000000, + 0x00000010, 0x00000000, + 0x00000010, 0x00000000, + 0x00064029, 0x00000000, + 0x00024420, 0x00000183, + 0x5c01401a, 0x0000595c, + 0x82000015, 0x00002000, + 0x16200011, 0x00000000, + 0x82000015, 0x00000010, + 0x16200011, 0x00000000, + 0x82000015, 0x00000010, + 0x16200011, 0x00000000, +}; /* 104 WoL instructions */ +#define FIRMWARE_WOL_SIZE 104 +#endif diff -Nru a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig --- a/drivers/net/wireless/Kconfig 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/wireless/Kconfig 2005-03-15 23:46:53 -08:00 @@ -355,6 +355,8 @@ say M here and read . The module will be called prism54.ko. +source "drivers/net/wireless/hostap/Kconfig" + # yes, this works even when no drivers are selected config NET_WIRELESS bool diff -Nru a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile --- a/drivers/net/wireless/Makefile 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/wireless/Makefile 2005-03-15 23:46:53 -08:00 @@ -28,6 +28,8 @@ obj-$(CONFIG_PRISM54) += prism54/ +obj-$(CONFIG_HOSTAP) += hostap/ + # 16-bit wireless PCMCIA client drivers obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o diff -Nru a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c --- a/drivers/net/wireless/atmel.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/wireless/atmel.c 2005-03-15 23:46:53 -08:00 @@ -68,7 +68,7 @@ #include #include #include -#include "ieee802_11.h" +#include #include "atmel.h" #define DRIVER_MAJOR 0 @@ -618,12 +618,12 @@ static void atmel_wmem32(struct atmel_private *priv, u16 pos, u32 data); static void atmel_command_irq(struct atmel_private *priv); static int atmel_validate_channel(struct atmel_private *priv, int channel); -static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header, u16 frame_len, u8 rssi); static void atmel_management_timer(u_long a); static void atmel_send_command(struct atmel_private *priv, int command, void *cmd, int cmd_size); static int atmel_send_command_wait(struct atmel_private *priv, int command, void *cmd, int cmd_size); -static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void atmel_transmit_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header, u8 *body, int body_len); static u8 atmel_get_mib8(struct atmel_private *priv, u8 type, u8 index); @@ -827,7 +827,7 @@ static int start_tx (struct sk_buff *skb, struct net_device *dev) { struct atmel_private *priv = netdev_priv(dev); - struct ieee802_11_hdr header; + struct ieee80211_hdr header; unsigned long flags; u16 buff, frame_ctl, len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; u8 SNAP_RFC1024[6] = {0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00}; @@ -863,17 +863,17 @@ return 1; } - frame_ctl = IEEE802_11_FTYPE_DATA; + frame_ctl = IEEE80211_FTYPE_DATA; header.duration_id = 0; header.seq_ctl = 0; if (priv->wep_is_on) - frame_ctl |= IEEE802_11_FCTL_WEP; + frame_ctl |= IEEE80211_FCTL_WEP; if (priv->operating_mode == IW_MODE_ADHOC) { memcpy(&header.addr1, skb->data, 6); memcpy(&header.addr2, dev->dev_addr, 6); memcpy(&header.addr3, priv->BSSID, 6); } else { - frame_ctl |= IEEE802_11_FCTL_TODS; + frame_ctl |= IEEE80211_FCTL_TODS; memcpy(&header.addr1, priv->CurrentBSSID, 6); memcpy(&header.addr2, dev->dev_addr, 6); memcpy(&header.addr3, skb->data, 6); @@ -902,7 +902,7 @@ } static void atmel_transmit_management_frame(struct atmel_private *priv, - struct ieee802_11_hdr *header, + struct ieee80211_hdr *header, u8 *body, int body_len) { u16 buff; @@ -917,7 +917,7 @@ tx_update_descriptor(priv, header->addr1[0] & 0x01, len, buff, TX_PACKET_TYPE_MGMT); } -static void fast_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void fast_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header, u16 msdu_size, u16 rx_packet_loc, u32 crc) { /* fast path: unfragmented packet copy directly into skbuf */ @@ -955,7 +955,7 @@ } memcpy(skbp, header->addr1, 6); /* destination address */ - if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS) + if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS) memcpy(&skbp[6], header->addr3, 6); else memcpy(&skbp[6], header->addr2, 6); /* source address */ @@ -990,14 +990,14 @@ return (crc ^ 0xffffffff) == netcrc; } -static void frag_rx_path(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void frag_rx_path(struct atmel_private *priv, struct ieee80211_hdr *header, u16 msdu_size, u16 rx_packet_loc, u32 crc, u16 seq_no, u8 frag_no, int more_frags) { u8 mac4[6]; u8 source[6]; struct sk_buff *skb; - if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS) + if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS) memcpy(source, header->addr3, 6); else memcpy(source, header->addr2, 6); @@ -1082,7 +1082,7 @@ static void rx_done_irq(struct atmel_private *priv) { int i; - struct ieee802_11_hdr header; + struct ieee80211_hdr header; for (i = 0; atmel_rmem8(priv, atmel_rx(priv, RX_DESC_FLAGS_OFFSET, priv->rx_desc_head)) == RX_DESC_FLAG_VALID && @@ -1117,7 +1117,7 @@ /* probe for CRC use here if needed once five packets have arrived with the same crc status, we assume we know what's happening and stop probing */ if (priv->probe_crc) { - if (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP)) { + if (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_WEP)) { priv->do_rx_crc = probe_crc(priv, rx_packet_loc, msdu_size); } else { priv->do_rx_crc = probe_crc(priv, rx_packet_loc + 24, msdu_size - 24); @@ -1132,16 +1132,16 @@ } /* don't CRC header when WEP in use */ - if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE802_11_FCTL_WEP))) { + if (priv->do_rx_crc && (!priv->wep_is_on || !(frame_ctl & IEEE80211_FCTL_WEP))) { crc = crc32_le(0xffffffff, (unsigned char *)&header, 24); } msdu_size -= 24; /* header */ - if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_DATA) { + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) { - int more_fragments = frame_ctl & IEEE802_11_FCTL_MOREFRAGS; - u8 packet_fragment_no = seq_control & IEEE802_11_SCTL_FRAG; - u16 packet_sequence_no = (seq_control & IEEE802_11_SCTL_SEQ) >> 4; + int more_fragments = frame_ctl & IEEE80211_FCTL_MOREFRAGS; + u8 packet_fragment_no = seq_control & IEEE80211_SCTL_FRAG; + u16 packet_sequence_no = (seq_control & IEEE80211_SCTL_SEQ) >> 4; if (!more_fragments && packet_fragment_no == 0 ) { fast_rx_path(priv, &header, msdu_size, rx_packet_loc, crc); @@ -1151,7 +1151,7 @@ } } - if ((frame_ctl & IEEE802_11_FCTL_FTYPE) == IEEE802_11_FTYPE_MGMT) { + if ((frame_ctl & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT) { /* copy rest of packet into buffer */ atmel_copy_to_host(priv->dev, (unsigned char *)&priv->rx_buf, rx_packet_loc + 24, msdu_size); @@ -2663,10 +2663,10 @@ static void send_authentication_request(struct atmel_private *priv, u8 *challenge, int challenge_len) { - struct ieee802_11_hdr header; + struct ieee80211_hdr header; struct auth_body auth; - header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT | IEEE802_11_STYPE_AUTH); + header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_AUTH); header.duration_id = cpu_to_le16(0x8000); header.seq_ctl = 0; memcpy(header.addr1, priv->CurrentBSSID, 6); @@ -2677,7 +2677,7 @@ auth.alg = cpu_to_le16(C80211_MGMT_AAN_SHAREDKEY); /* no WEP for authentication frames with TrSeqNo 1 */ if (priv->CurrentAuthentTransactionSeqNum != 1) - header.frame_ctl |= cpu_to_le16(IEEE802_11_FCTL_WEP); + header.frame_ctl |= cpu_to_le16(IEEE80211_FCTL_WEP); } else { auth.alg = cpu_to_le16(C80211_MGMT_AAN_OPENSYSTEM); } @@ -2701,7 +2701,7 @@ { u8 *ssid_el_p; int bodysize; - struct ieee802_11_hdr header; + struct ieee80211_hdr header; struct ass_req_format { u16 capability; u16 listen_interval; @@ -2714,8 +2714,8 @@ u8 rates[4]; } body; - header.frame_ctl = cpu_to_le16(IEEE802_11_FTYPE_MGMT | - (is_reassoc ? IEEE802_11_STYPE_REASSOC_REQ : IEEE802_11_STYPE_ASSOC_REQ)); + header.frame_ctl = cpu_to_le16(IEEE80211_FTYPE_MGMT | + (is_reassoc ? IEEE80211_STYPE_REASSOC_REQ : IEEE80211_STYPE_ASSOC_REQ)); header.duration_id = cpu_to_le16(0x8000); header.seq_ctl = 0; @@ -2751,9 +2751,9 @@ atmel_transmit_management_frame(priv, &header, (void *)&body, bodysize); } -static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee802_11_hdr *header) +static int is_frame_from_current_bss(struct atmel_private *priv, struct ieee80211_hdr *header) { - if (le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_FROMDS) + if (le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_FROMDS) return memcmp(header->addr3, priv->CurrentBSSID, 6) == 0; else return memcmp(header->addr2, priv->CurrentBSSID, 6) == 0; @@ -2801,7 +2801,7 @@ } -static void store_bss_info(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void store_bss_info(struct atmel_private *priv, struct ieee80211_hdr *header, u16 capability, u16 beacon_period, u8 channel, u8 rssi, u8 ssid_len, u8 *ssid, int is_beacon) { @@ -3085,12 +3085,12 @@ } /* deals with incoming managment frames. */ -static void atmel_management_frame(struct atmel_private *priv, struct ieee802_11_hdr *header, +static void atmel_management_frame(struct atmel_private *priv, struct ieee80211_hdr *header, u16 frame_len, u8 rssi) { u16 subtype; - switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE802_11_FCTL_STYPE) { + switch (subtype = le16_to_cpu(header->frame_ctl) & IEEE80211_FCTL_STYPE) { case C80211_SUBTYPE_MGMT_BEACON : case C80211_SUBTYPE_MGMT_ProbeResponse: diff -Nru a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/Kconfig 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,104 @@ +config HOSTAP + tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" + depends on NET_RADIO + ---help--- + Shared driver code for IEEE 802.11b wireless cards based on + Intersil Prism2/2.5/3 chipset. This driver supports so called + Host AP mode that allows the card to act as an IEEE 802.11 + access point. + + In addition, this includes generic IEEE 802.11 code, e.g., for + WEP/TKIP/CCMP encryption that can be shared with other drivers. + + See for more information about the + Host AP driver configuration and tools. This site includes + information and tools (hostapd and wpa_supplicant) for WPA/WPA2 + support. + + This option includes the base Host AP driver code that is shared by + different hardware models. You will also need to enable support for + PLX/PCI/CS version of the driver to actually use the driver. + + The driver can be compiled as a module and it will be called + "hostap.ko". + +config HOSTAP_WEP + tristate "IEEE 802.11 WEP encryption" + depends on HOSTAP + select CRYPTO + ---help--- + Software implementation of IEEE 802.11 WEP encryption. + + This can be compiled as a modules and it will be called + "hostap_crypt_wep.ko". + +config HOSTAP_TKIP + tristate "IEEE 802.11 TKIP encryption" + depends on HOSTAP + select CRYPTO + ---help--- + Software implementation of IEEE 802.11 TKIP encryption. + + This can be compiled as a modules and it will be called + "hostap_crypt_tkip.ko". + +config HOSTAP_CCMP + tristate "IEEE 802.11 CCMP encryption" + depends on HOSTAP + select CRYPTO + ---help--- + Software implementation of IEEE 802.11 CCMP encryption. + + This can be compiled as a modules and it will be called + "hostap_crypt_ccmp.ko". + +config HOSTAP_FIRMWARE + bool "Support downloading firmware images with Host AP driver" + depends on HOSTAP + ---help--- + Configure Host AP driver to include support for firmware image + download. Current version supports only downloading to volatile, i.e., + RAM memory. Flash upgrade is not yet supported. + + Firmware image downloading needs user space tool, prism2_srec. It is + available from http://hostap.epitest.fi/. + +config HOSTAP_PLX + tristate "Host AP driver for Prism2/2.5/3 in PLX9052 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards in PLX9052 based + PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_plx.ko". + +config HOSTAP_PCI + tristate "Host AP driver for Prism2.5 PCI adaptors" + depends on PCI && HOSTAP + ---help--- + Host AP driver's version for Prism2.5 PCI adaptors. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_pci.ko". + +config HOSTAP_CS + tristate "Host AP driver for Prism2/2.5/3 PC Cards" + depends on PCMCIA!=n && HOSTAP + ---help--- + Host AP driver's version for Prism2/2.5/3 PC Cards. + + "Host AP support for Prism2/2.5/3 IEEE 802.11b" is required for this + driver and its help text includes more information about the Host AP + driver. + + The driver can be compiled as a module and will be named + "hostap_cs.ko". diff -Nru a/drivers/net/wireless/hostap/Makefile b/drivers/net/wireless/hostap/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/Makefile 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,8 @@ +obj-$(CONFIG_HOSTAP) += hostap.o +obj-$(CONFIG_HOSTAP_WEP) += hostap_crypt_wep.o +obj-$(CONFIG_HOSTAP_TKIP) += hostap_crypt_tkip.o +obj-$(CONFIG_HOSTAP_CCMP) += hostap_crypt_ccmp.o + +obj-$(CONFIG_HOSTAP_CS) += hostap_cs.o +obj-$(CONFIG_HOSTAP_PLX) += hostap_plx.o +obj-$(CONFIG_HOSTAP_PCI) += hostap_pci.o diff -Nru a/drivers/net/wireless/hostap/hostap.c b/drivers/net/wireless/hostap/hostap.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,1205 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#ifndef EXPORT_SYMTAB +#define EXPORT_SYMTAB +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_wlan.h" +#include "hostap_80211.h" +#include "hostap_ap.h" +#include "hostap.h" +#include "hostap_crypt.h" + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP common routines"); +MODULE_LICENSE("GPL"); + +/* Old hostap_crypt module is now part of hostap module. */ +#include "hostap_crypt.c" + +#define TX_TIMEOUT (2 * HZ) + +#define PRISM2_MAX_FRAME_SIZE 2304 +#define PRISM2_MIN_MTU 256 +/* FIX: */ +#define PRISM2_MAX_MTU (PRISM2_MAX_FRAME_SIZE - (6 /* LLC */ + 8 /* WEP */)) + + +/* hostap.c */ +static int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked); +static int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove); + +/* hostap_ap.c */ +static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist); +static int prism2_ap_translate_scan(struct net_device *dev, char *buffer); +static int prism2_hostapd(struct ap_data *ap, + struct prism2_hostapd_param *param); +static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct prism2_crypt_data ***crypt); +static void ap_control_kickall(struct ap_data *ap); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_control_add_mac(struct mac_restrictions *mac_restrictions, + u8 *mac); +static int ap_control_del_mac(struct mac_restrictions *mac_restrictions, + u8 *mac); +static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions); +static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, + u8 *mac); +#endif /* !PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; +#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0])) + + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + + +/* FIX: these could be compiled separately and linked together to hostap.o */ +#include "hostap_ap.c" +#include "hostap_info.c" +#include "hostap_ioctl.c" +#include "hostap_proc.c" +#include "hostap_80211_rx.c" +#include "hostap_80211_tx.c" + + +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, + const char *name) +{ + struct net_device *dev, *mdev; + struct hostap_interface *iface; + int ret; + + dev = alloc_etherdev(sizeof(struct hostap_interface)); + if (dev == NULL) + return NULL; + + iface = netdev_priv(dev); + iface->dev = dev; + iface->local = local; + iface->type = type; + list_add(&iface->list, &local->hostap_interfaces); + + mdev = local->dev; + memcpy(dev->dev_addr, mdev->dev_addr, ETH_ALEN); + dev->base_addr = mdev->base_addr; + dev->irq = mdev->irq; + dev->mem_start = mdev->mem_start; + dev->mem_end = mdev->mem_end; + + hostap_setup_dev(dev, local, 0); + dev->destructor = free_netdev; + + sprintf(dev->name, "%s%s", prefix, name); + if (!rtnl_locked) + rtnl_lock(); + + ret = 0; + if (strchr(dev->name, '%')) + ret = dev_alloc_name(dev, dev->name); + + if (ret >= 0) + ret = register_netdevice(dev); + + if (!rtnl_locked) + rtnl_unlock(); + + if (ret < 0) { + printk(KERN_WARNING "%s: failed to add new netdevice!\n", + dev->name); + free_netdev(dev); + return NULL; + } + + printk(KERN_DEBUG "%s: registered netdevice %s\n", + mdev->name, dev->name); + + return dev; +} + + +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list) +{ + struct hostap_interface *iface; + + if (!dev) + return; + + iface = netdev_priv(dev); + + if (remove_from_list) { + list_del(&iface->list); + } + + if (dev == iface->local->ddev) + iface->local->ddev = NULL; + else if (dev == iface->local->apdev) + iface->local->apdev = NULL; + else if (dev == iface->local->stadev) + iface->local->stadev = NULL; + + if (rtnl_locked) + unregister_netdevice(dev); + else + unregister_netdev(dev); + + /* dev->destructor = free_netdev() will free the device data, including + * private data, when removing the device */ +} + + +static inline int prism2_wds_special_addr(u8 *addr) +{ + if (addr[0] || addr[1] || addr[2] || addr[3] || addr[4] || addr[5]) + return 0; + + return 1; +} + + +static int prism2_wds_add(local_info_t *local, u8 *remote_addr, + int rtnl_locked) +{ + struct net_device *dev; + struct list_head *ptr; + struct hostap_interface *iface, *empty, *match; + + empty = match = NULL; + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (prism2_wds_special_addr(iface->u.wds.remote_addr)) + empty = iface; + else if (memcmp(iface->u.wds.remote_addr, remote_addr, + ETH_ALEN) == 0) { + match = iface; + break; + } + } + if (!match && empty && !prism2_wds_special_addr(remote_addr)) { + /* take pre-allocated entry into use */ + memcpy(empty->u.wds.remote_addr, remote_addr, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + printk(KERN_DEBUG "%s: using pre-allocated WDS netdevice %s\n", + local->dev->name, empty->dev->name); + return 0; + } + read_unlock_bh(&local->iface_lock); + + if (!prism2_wds_special_addr(remote_addr)) { + if (match) + return -EEXIST; + hostap_add_sta(local->ap, remote_addr); + } + + if (local->wds_connections >= local->wds_max_connections) + return -ENOBUFS; + + /* verify that there is room for wds# postfix in the interface name */ + if (strlen(local->dev->name) > IFNAMSIZ - 5) { + printk(KERN_DEBUG "'%s' too long base device name\n", + local->dev->name); + return -EINVAL; + } + + dev = hostap_add_interface(local, HOSTAP_INTERFACE_WDS, rtnl_locked, + local->ddev->name, "wds%d"); + if (dev == NULL) + return -ENOMEM; + + iface = netdev_priv(dev); + memcpy(iface->u.wds.remote_addr, remote_addr, ETH_ALEN); + + local->wds_connections++; + + return 0; +} + + +static int prism2_wds_del(local_info_t *local, u8 *remote_addr, + int rtnl_locked, int do_not_remove) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_interface *iface, *selected = NULL; + + write_lock_irqsave(&local->iface_lock, flags); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + + if (memcmp(iface->u.wds.remote_addr, remote_addr, + ETH_ALEN) == 0) { + selected = iface; + break; + } + } + if (selected && !do_not_remove) + list_del(&selected->list); + write_unlock_irqrestore(&local->iface_lock, flags); + + if (selected) { + if (do_not_remove) + memset(selected->u.wds.remote_addr, 0, ETH_ALEN); + else { + hostap_remove_interface(selected->dev, rtnl_locked, 0); + local->wds_connections--; + } + } + + return selected ? 0 : -ENODEV; +} + + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data) +{ + unsigned long flags; + struct hostap_tx_callback_info *entry; + + entry = (struct hostap_tx_callback_info *) kmalloc(sizeof(*entry), + GFP_ATOMIC); + if (entry == NULL) + return 0; + + entry->func = func; + entry->data = data; + + spin_lock_irqsave(&local->lock, flags); + entry->idx = local->tx_callback ? local->tx_callback->idx + 1 : 1; + entry->next = local->tx_callback; + local->tx_callback = entry; + spin_unlock_irqrestore(&local->lock, flags); + + return entry->idx; +} + + +int hostap_tx_callback_unregister(local_info_t *local, u16 idx) +{ + unsigned long flags; + struct hostap_tx_callback_info *cb, *prev = NULL; + + spin_lock_irqsave(&local->lock, flags); + cb = local->tx_callback; + while (cb != NULL && cb->idx != idx) { + prev = cb; + cb = cb->next; + } + if (cb) { + if (prev == NULL) + local->tx_callback = cb->next; + else + prev->next = cb->next; + kfree(cb); + } + spin_unlock_irqrestore(&local->lock, flags); + + return cb ? 0 : -1; +} + + +/* val is in host byte order */ +int hostap_set_word(struct net_device *dev, int rid, u16 val) +{ + struct hostap_interface *iface; + u16 tmp = cpu_to_le16(val); + iface = netdev_priv(dev); + return iface->local->func->set_rid(dev, rid, &tmp, 2); +} + + +int hostap_set_string(struct net_device *dev, int rid, const char *val) +{ + struct hostap_interface *iface; + char buf[MAX_SSID_LEN + 2]; + int len; + + iface = netdev_priv(dev); + len = strlen(val); + if (len > MAX_SSID_LEN) + return -1; + memset(buf, 0, sizeof(buf)); + buf[0] = len; /* little endian 16 bit word */ + memcpy(buf + 2, val, len); + + return iface->local->func->set_rid(dev, rid, &buf, MAX_SSID_LEN + 2); +} + + +u16 hostap_get_porttype(local_info_t *local) +{ + if (local->iw_mode == IW_MODE_ADHOC && local->pseudo_adhoc) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + if (local->iw_mode == IW_MODE_ADHOC) + return HFA384X_PORTTYPE_IBSS; + if (local->iw_mode == IW_MODE_INFRA) + return HFA384X_PORTTYPE_BSS; + if (local->iw_mode == IW_MODE_REPEAT) + return HFA384X_PORTTYPE_WDS; + if (local->iw_mode == IW_MODE_MONITOR) + return HFA384X_PORTTYPE_PSEUDO_IBSS; + return HFA384X_PORTTYPE_HOSTAP; +} + + +int hostap_set_encryption(local_info_t *local) +{ + u16 val, old_val; + int i, keylen, len, idx; + char keybuf[WEP_KEY_LEN + 1]; + enum { NONE, WEP, OTHER } encrypt_type; + + idx = local->tx_keyidx; + if (local->crypt[idx] == NULL || local->crypt[idx]->ops == NULL) + encrypt_type = NONE; + else if (strcmp(local->crypt[idx]->ops->name, "WEP") == 0) + encrypt_type = WEP; + else + encrypt_type = OTHER; + + if (local->func->get_rid(local->dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, + 1) < 0) { + printk(KERN_DEBUG "Could not read current WEP flags.\n"); + goto fail; + } + le16_to_cpus(&val); + old_val = val; + + if (encrypt_type != NONE || local->privacy_invoked) + val |= HFA384X_WEPFLAGS_PRIVACYINVOKED; + else + val &= ~HFA384X_WEPFLAGS_PRIVACYINVOKED; + + if (local->open_wep || encrypt_type == NONE || + ((local->ieee_802_1x || local->wpa) && local->host_decrypt)) + val &= ~HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + else + val |= HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED; + + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_encrypt)) + val |= HFA384X_WEPFLAGS_HOSTENCRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTENCRYPT; + if ((encrypt_type != NONE || local->privacy_invoked) && + (encrypt_type == OTHER || local->host_decrypt)) + val |= HFA384X_WEPFLAGS_HOSTDECRYPT; + else + val &= ~HFA384X_WEPFLAGS_HOSTDECRYPT; + + if (val != old_val && + hostap_set_word(local->dev, HFA384X_RID_CNFWEPFLAGS, val)) { + printk(KERN_DEBUG "Could not write new WEP flags (0x%x)\n", + val); + goto fail; + } + + if (encrypt_type != WEP) + return 0; + + /* 104-bit support seems to require that all the keys are set to the + * same keylen */ + keylen = 6; /* first 5 octets */ + len = local->crypt[idx]->ops->get_key(keybuf, sizeof(keybuf), + NULL, local->crypt[idx]->priv); + if (idx >= 0 && idx < WEP_KEYS && len > 5) + keylen = WEP_KEY_LEN + 1; /* first 13 octets */ + + for (i = 0; i < WEP_KEYS; i++) { + memset(keybuf, 0, sizeof(keybuf)); + if (local->crypt[i]) { + (void) local->crypt[i]->ops->get_key( + keybuf, sizeof(keybuf), + NULL, local->crypt[i]->priv); + } + if (local->func->set_rid(local->dev, + HFA384X_RID_CNFDEFAULTKEY0 + i, + keybuf, keylen)) { + printk(KERN_DEBUG "Could not set key %d (len=%d)\n", + i, keylen); + goto fail; + } + } + if (hostap_set_word(local->dev, HFA384X_RID_CNFWEPDEFAULTKEYID, idx)) { + printk(KERN_DEBUG "Could not set default keyid %d\n", idx); + goto fail; + } + + return 0; + + fail: + printk(KERN_DEBUG "%s: encryption setup failed\n", local->dev->name); + return -1; +} + + +int hostap_set_antsel(local_info_t *local) +{ + u16 val; + int ret = 0; + + if (local->antsel_tx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_TX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(2) | BIT(1)); + switch (local->antsel_tx) { + case HOSTAP_ANTSEL_DIVERSITY: + val |= BIT(1); + break; + case HOSTAP_ANTSEL_LOW: + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(2); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_TX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting TX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + if (local->antsel_rx != HOSTAP_ANTSEL_DO_NOT_TOUCH && + local->func->cmd(local->dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_RX_CONFIGURE, + NULL, &val) == 0) { + val &= ~(BIT(1) | BIT(0)); + switch (local->antsel_rx) { + case HOSTAP_ANTSEL_DIVERSITY: + break; + case HOSTAP_ANTSEL_LOW: + val |= BIT(0); + break; + case HOSTAP_ANTSEL_HIGH: + val |= BIT(0) | BIT(1); + break; + } + + if (local->func->cmd(local->dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_RX_CONFIGURE, &val, NULL)) { + printk(KERN_INFO "%s: setting RX AntSel failed\n", + local->dev->name); + ret = -1; + } + } + + return ret; +} + + +int hostap_set_roaming(local_info_t *local) +{ + u16 val; + + switch (local->host_roaming) { + case 1: + val = HFA384X_ROAMING_HOST; + break; + case 2: + val = HFA384X_ROAMING_DISABLED; + break; + case 0: + default: + val = HFA384X_ROAMING_FIRMWARE; + break; + } + + return hostap_set_word(local->dev, HFA384X_RID_CNFROAMINGMODE, val); +} + + +int hostap_set_auth_algs(local_info_t *local) +{ + int val = local->auth_algs; + /* At least STA f/w v0.6.2 seems to have issues with cnfAuthentication + * set to include both Open and Shared Key flags. It tries to use + * Shared Key authentication in that case even if WEP keys are not + * configured.. STA f/w v0.7.6 is able to handle such configuration, + * but it is unknown when this was fixed between 0.6.2 .. 0.7.6. */ + if (local->sta_fw_ver < PRISM2_FW_VER(0,7,0) && + val != PRISM2_AUTH_OPEN && val != PRISM2_AUTH_SHARED_KEY) + val = PRISM2_AUTH_OPEN; + + if (hostap_set_word(local->dev, HFA384X_RID_CNFAUTHENTICATION, val)) { + printk(KERN_INFO "%s: cnfAuthentication setting to 0x%x " + "failed\n", local->dev->name, local->auth_algs); + return -EINVAL; + } + + return 0; +} + + +void hostap_dump_rx_header(const char *name, const struct hfa384x_rx_frame *rx) +{ + u16 status, fc; + + status = __le16_to_cpu(rx->status); + + printk(KERN_DEBUG "%s: RX status=0x%04x (port=%d, type=%d, " + "fcserr=%d) silence=%d signal=%d rate=%d rxflow=%d; " + "jiffies=%ld\n", + name, status, (status >> 8) & 0x07, status >> 13, status & 1, + rx->silence, rx->signal, rx->rate, rx->rxflow, jiffies); + + fc = __le16_to_cpu(rx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + __le16_to_cpu(rx->duration_id), __le16_to_cpu(rx->seq_ctrl), + __le16_to_cpu(rx->data_len), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4=" + MACSTR "\n", + MAC2STR(rx->addr1), MAC2STR(rx->addr2), MAC2STR(rx->addr3), + MAC2STR(rx->addr4)); + + printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n", + MAC2STR(rx->dst_addr), MAC2STR(rx->src_addr), + __be16_to_cpu(rx->len)); +} + + +void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) +{ + u16 fc; + + printk(KERN_DEBUG "%s: TX status=0x%04x retry_count=%d tx_rate=%d " + "tx_control=0x%04x; jiffies=%ld\n", + name, __le16_to_cpu(tx->status), tx->retry_count, tx->tx_rate, + __le16_to_cpu(tx->tx_control), jiffies); + + fc = __le16_to_cpu(tx->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d) dur=0x%04x seq=0x%04x " + "data_len=%d%s%s\n", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + __le16_to_cpu(tx->duration_id), __le16_to_cpu(tx->seq_ctrl), + __le16_to_cpu(tx->data_len), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR " A4=" + MACSTR "\n", + MAC2STR(tx->addr1), MAC2STR(tx->addr2), MAC2STR(tx->addr3), + MAC2STR(tx->addr4)); + + printk(KERN_DEBUG " dst=" MACSTR " src=" MACSTR " len=%d\n", + MAC2STR(tx->dst_addr), MAC2STR(tx->src_addr), + __be16_to_cpu(tx->len)); +} + + +int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */ + return ETH_ALEN; +} + + +int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr) +{ + if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) { + memcpy(haddr, skb->mac.raw + + sizeof(struct linux_wlan_ng_prism_hdr) + 10, + ETH_ALEN); /* addr2 */ + } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */ + memcpy(haddr, skb->mac.raw + + sizeof(struct linux_wlan_ng_cap_hdr) + 10, + ETH_ALEN); /* addr2 */ + } + return ETH_ALEN; +} + + +int hostap_80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (WLAN_FC_GET_TYPE(fc)) { + case WLAN_FC_TYPE_DATA: + if ((fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS)) + hdrlen = 30; /* Addr4 */ + break; + case WLAN_FC_TYPE_CTRL: + switch (WLAN_FC_GET_STYPE(fc)) { + case WLAN_FC_STYPE_CTS: + case WLAN_FC_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + +struct net_device_stats *hostap_get_stats(struct net_device *dev) +{ + struct hostap_interface *iface; + iface = netdev_priv(dev); + return &iface->stats; +} + + +static int prism2_close(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_close\n", dev->name); + + iface = netdev_priv(dev); + local = iface->local; + + if (dev == local->ddev) { + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + } +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && dev == local->dev && + (!local->func->card_present || local->func->card_present(local)) && + local->hw_ready && local->ap && local->iw_mode == IW_MODE_MASTER) + hostap_deauth_all_stas(dev, local->ap, 1); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (local->func->dev_close && local->func->dev_close(local)) + return 0; + + if (dev == local->dev) { + local->func->hw_shutdown(dev, HOSTAP_HW_ENABLE_CMDCOMPL); + } + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + + flush_scheduled_work(); + + module_put(local->hw_module); + + local->num_dev_open--; + + if (dev != local->dev && local->dev->flags & IFF_UP && + local->master_dev_auto_open && local->num_dev_open == 1) { + /* Close master radio interface automatically if it was also + * opened automatically and we are now closing the last + * remaining non-master device. */ + dev_close(local->dev); + } + + return 0; +} + + +static int prism2_open(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + + PDEBUG(DEBUG_FLOW, "%s: prism2_open\n", dev->name); + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: could not set interface UP - no PRI " + "f/w\n", dev->name); + return 1; + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + if (local->func->dev_open && local->func->dev_open(local)) + return 1; + + if (!try_module_get(local->hw_module)) + return -ENODEV; + local->num_dev_open++; + + if (!local->dev_enabled && local->func->hw_enable(dev, 1)) { + printk(KERN_WARNING "%s: could not enable MAC port\n", + dev->name); + prism2_close(dev); + return 1; + } + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + + if (dev != local->dev && !(local->dev->flags & IFF_UP)) { + /* Master radio interface is needed for all operation, so open + * it automatically when any virtual net_device is opened. */ + local->master_dev_auto_open = 1; + dev_open(local->dev); + } + + netif_device_attach(dev); + netif_start_queue(dev); + + return 0; +} + + +static int prism2_set_mac_address(struct net_device *dev, void *p) +{ + struct hostap_interface *iface; + local_info_t *local; + struct list_head *ptr; + struct sockaddr *addr = p; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->set_rid(dev, HFA384X_RID_CNFOWNMACADDR, addr->sa_data, + ETH_ALEN) < 0 || local->func->reset_port(dev)) + return -EINVAL; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + memcpy(iface->dev->dev_addr, addr->sa_data, ETH_ALEN); + } + memcpy(local->dev->dev_addr, addr->sa_data, ETH_ALEN); + read_unlock_bh(&local->iface_lock); + + return 0; +} + + +/* TODO: to be further implemented as soon as Prism2 fully supports + * GroupAddresses and correct documentation is available */ +void hostap_set_multicast_list_queue(void *data) +{ + struct net_device *dev = (struct net_device *) data; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc)) { + printk(KERN_INFO "%s: %sabling promiscuous mode failed\n", + dev->name, local->is_promisc ? "en" : "dis"); + } +} + + +static void hostap_set_multicast_list(struct net_device *dev) +{ +#if 0 + /* FIX: promiscuous mode seems to be causing a lot of problems with + * some station firmware versions (FCSErr frames, invalid MACPort, etc. + * corrupted incoming frames). This code is now commented out while the + * problems are investigated. */ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + if ((dev->flags & IFF_ALLMULTI) || (dev->flags & IFF_PROMISC)) { + local->is_promisc = 1; + } else { + local->is_promisc = 0; + } + + schedule_work(&local->set_multicast_list_queue); +#endif +} + + +static int prism2_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < PRISM2_MIN_MTU || new_mtu > PRISM2_MAX_MTU) + return -EINVAL; + + dev->mtu = new_mtu; + return 0; +} + + +static void prism2_tx_timeout(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_regs regs; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_WARNING "%s Tx timed out! Resetting card\n", dev->name); + netif_stop_queue(local->dev); + + local->func->read_regs(dev, ®s); + printk(KERN_DEBUG "%s: CMD=%04x EVSTAT=%04x " + "OFFSET0=%04x OFFSET1=%04x SWSUPPORT0=%04x\n", + dev->name, regs.cmd, regs.evstat, regs.offset0, regs.offset1, + regs.swsupport0); + + local->func->schedule_reset(local); +} + + +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int main_dev) +{ + struct hostap_interface *iface; + + iface = netdev_priv(dev); + ether_setup(dev); + + /* kernel callbacks */ + dev->get_stats = hostap_get_stats; + if (iface) { + /* Currently, we point to the proper spy_data only on + * the main_dev. This could be fixed. Jean II */ + iface->wireless_data.spy_data = &iface->spy_data; + dev->wireless_data = &iface->wireless_data; + } + dev->wireless_handlers = + (struct iw_handler_def *) &hostap_iw_handler_def; + dev->do_ioctl = hostap_ioctl; + dev->open = prism2_open; + dev->stop = prism2_close; + dev->hard_start_xmit = hostap_data_start_xmit; + dev->set_mac_address = prism2_set_mac_address; + dev->set_multicast_list = hostap_set_multicast_list; + dev->change_mtu = prism2_change_mtu; + dev->tx_timeout = prism2_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + dev->mtu = local->mtu; + if (!main_dev) { + /* use main radio device queue */ + dev->tx_queue_len = 0; + } + + netif_stop_queue(dev); +} + + +static int hostap_enable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->apdev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd mode\n", dev->name); + + local->apdev = hostap_add_interface(local, HOSTAP_INTERFACE_AP, + rtnl_locked, local->ddev->name, + "ap"); + if (local->apdev == NULL) + return -ENOMEM; + + local->apdev->hard_start_xmit = hostap_mgmt_start_xmit; + local->apdev->type = ARPHRD_IEEE80211; + local->apdev->hard_header_parse = hostap_80211_header_parse; + + return 0; +} + + +static int hostap_disable_hostapd(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->apdev, rtnl_locked, 1); + local->apdev = NULL; + + return 0; +} + + +static int hostap_enable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + if (local->stadev) + return -EEXIST; + + printk(KERN_DEBUG "%s: enabling hostapd STA mode\n", dev->name); + + local->stadev = hostap_add_interface(local, HOSTAP_INTERFACE_STA, + rtnl_locked, local->ddev->name, + "sta"); + if (local->stadev == NULL) + return -ENOMEM; + + return 0; +} + + +static int hostap_disable_hostapd_sta(local_info_t *local, int rtnl_locked) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "%s: disabling hostapd mode\n", dev->name); + + hostap_remove_interface(local->stadev, rtnl_locked, 1); + local->stadev = NULL; + + return 0; +} + + +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd(local, rtnl_locked); + if (ret == 0) + local->hostapd = 1; + } else { + local->hostapd = 0; + ret = hostap_disable_hostapd(local, rtnl_locked); + if (ret != 0) + local->hostapd = 1; + } + + return ret; +} + + +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked) +{ + int ret; + + if (val < 0 || val > 1) + return -EINVAL; + + if (local->hostapd_sta == val) + return 0; + + if (val) { + ret = hostap_enable_hostapd_sta(local, rtnl_locked); + if (ret == 0) + local->hostapd_sta = 1; + } else { + local->hostapd_sta = 0; + ret = hostap_disable_hostapd_sta(local, rtnl_locked); + if (ret != 0) + local->hostapd_sta = 1; + } + + + return ret; +} + + +int prism2_update_comms_qual(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + struct hfa384x_comms_quality sq; + + iface = netdev_priv(dev); + local = iface->local; + if (!local->sta_fw_ver) + ret = -1; + else if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + if (local->func->get_rid(local->dev, + HFA384X_RID_DBMCOMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = (s16) le16_to_cpu(sq.comm_qual); + local->avg_signal = (s16) le16_to_cpu(sq.signal_level); + local->avg_noise = (s16) le16_to_cpu(sq.noise_level); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } else { + if (local->func->get_rid(local->dev, HFA384X_RID_COMMSQUALITY, + &sq, sizeof(sq), 1) >= 0) { + local->comms_qual = le16_to_cpu(sq.comm_qual); + local->avg_signal = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.signal_level)); + local->avg_noise = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(sq.noise_level)); + local->last_comms_qual_update = jiffies; + } else + ret = -1; + } + + return ret; +} + + +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u8 stype, + u8 *body, size_t bodylen) +{ + struct sk_buff *skb; + struct hostap_ieee80211_mgmt *mgmt; + struct hostap_skb_tx_data *meta; + struct net_device *dev = local->dev; + + skb = dev_alloc_skb(IEEE80211_MGMT_HDR_LEN + bodylen); + if (skb == NULL) + return -ENOMEM; + + mgmt = (struct hostap_ieee80211_mgmt *) + skb_put(skb, IEEE80211_MGMT_HDR_LEN); + memset(mgmt, 0, IEEE80211_MGMT_HDR_LEN); + mgmt->frame_control = + cpu_to_le16((WLAN_FC_TYPE_MGMT << 2) | (stype << 4)); + memcpy(mgmt->da, dst, ETH_ALEN); + memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN); + memcpy(mgmt->bssid, dst, ETH_ALEN); + if (body) + memcpy(skb_put(skb, bodylen), body, bodylen); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = netdev_priv(dev); + + skb->dev = dev; + skb->mac.raw = skb->nh.raw = skb->data; + dev_queue_xmit(skb); + + return 0; +} + + +int prism2_sta_deauth(local_info_t *local, u16 reason) +{ + union iwreq_data wrqu; + int ret; + + if (local->iw_mode != IW_MODE_INFRA || + memcmp(local->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 || + memcmp(local->bssid, "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == 0) + return 0; + + reason = cpu_to_le16(reason); + ret = prism2_sta_send_mgmt(local, local->bssid, WLAN_FC_STYPE_DEAUTH, + (u8 *) &reason, 2); + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); + return ret; +} + + +struct proc_dir_entry *hostap_proc; + +static int __init hostap_init(void) +{ + hostap_crypto_init(); + + if (proc_net != NULL) { + hostap_proc = proc_mkdir("hostap", proc_net); + if (!hostap_proc) + printk(KERN_WARNING "Failed to mkdir " + "/proc/net/hostap\n"); + } else + hostap_proc = NULL; + + return 0; +} + + +static void __exit hostap_exit(void) +{ + if (hostap_proc != NULL) { + hostap_proc = NULL; + remove_proc_entry("hostap", proc_net); + } + + hostap_crypto_deinit(); +} + + +EXPORT_SYMBOL(hostap_set_word); +EXPORT_SYMBOL(hostap_set_string); +EXPORT_SYMBOL(hostap_get_porttype); +EXPORT_SYMBOL(hostap_set_encryption); +EXPORT_SYMBOL(hostap_set_antsel); +EXPORT_SYMBOL(hostap_set_roaming); +EXPORT_SYMBOL(hostap_set_auth_algs); +EXPORT_SYMBOL(hostap_dump_rx_header); +EXPORT_SYMBOL(hostap_dump_tx_header); +EXPORT_SYMBOL(hostap_80211_header_parse); +EXPORT_SYMBOL(hostap_80211_prism_header_parse); +EXPORT_SYMBOL(hostap_80211_get_hdrlen); +EXPORT_SYMBOL(hostap_get_stats); +EXPORT_SYMBOL(hostap_setup_dev); +EXPORT_SYMBOL(hostap_proc); +EXPORT_SYMBOL(hostap_set_multicast_list_queue); +EXPORT_SYMBOL(hostap_set_hostapd); +EXPORT_SYMBOL(hostap_set_hostapd_sta); +EXPORT_SYMBOL(hostap_add_interface); +EXPORT_SYMBOL(hostap_remove_interface); +EXPORT_SYMBOL(prism2_update_comms_qual); + +module_init(hostap_init); +module_exit(hostap_exit); diff -Nru a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,57 @@ +#ifndef HOSTAP_H +#define HOSTAP_H + +/* hostap.c */ + +extern struct proc_dir_entry *hostap_proc; + +u16 hostap_tx_callback_register(local_info_t *local, + void (*func)(struct sk_buff *, int ok, void *), + void *data); +int hostap_tx_callback_unregister(local_info_t *local, u16 idx); +int hostap_set_word(struct net_device *dev, int rid, u16 val); +int hostap_set_string(struct net_device *dev, int rid, const char *val); +u16 hostap_get_porttype(local_info_t *local); +int hostap_set_encryption(local_info_t *local); +int hostap_set_antsel(local_info_t *local); +int hostap_set_roaming(local_info_t *local); +int hostap_set_auth_algs(local_info_t *local); +void hostap_dump_rx_header(const char *name, + const struct hfa384x_rx_frame *rx); +void hostap_dump_tx_header(const char *name, + const struct hfa384x_tx_frame *tx); +int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr); +int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr); +int hostap_80211_get_hdrlen(u16 fc); +struct net_device_stats *hostap_get_stats(struct net_device *dev); +void hostap_setup_dev(struct net_device *dev, local_info_t *local, + int main_dev); +void hostap_set_multicast_list_queue(void *data); +int hostap_set_hostapd(local_info_t *local, int val, int rtnl_locked); +int hostap_set_hostapd_sta(local_info_t *local, int val, int rtnl_locked); +void hostap_cleanup(local_info_t *local); +void hostap_cleanup_handler(void *data); +struct net_device * hostap_add_interface(struct local_info *local, + int type, int rtnl_locked, + const char *prefix, const char *name); +void hostap_remove_interface(struct net_device *dev, int rtnl_locked, + int remove_from_list); +int prism2_update_comms_qual(struct net_device *dev); +int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u8 stype, + u8 *body, size_t bodylen); +int prism2_sta_deauth(local_info_t *local, u16 reason); + + +/* hostap_proc.c */ + +void hostap_init_proc(local_info_t *local); +void hostap_remove_proc(local_info_t *local); + + +/* hostap_info.c */ + +void hostap_info_init(local_info_t *local); +void hostap_info_process(local_info_t *local, struct sk_buff *skb); + + +#endif /* HOSTAP_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_80211.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,107 @@ +#ifndef HOSTAP_80211_H +#define HOSTAP_80211_H + +struct hostap_ieee80211_hdr { + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; +} __attribute__ ((packed)); + + +struct hostap_ieee80211_mgmt { + u16 frame_control; + u16 duration; + u8 da[6]; + u8 sa[6]; + u8 bssid[6]; + u16 seq_ctrl; + union { + struct { + u16 auth_alg; + u16 auth_transaction; + u16 status_code; + /* possibly followed by Challenge text */ + u8 variable[0]; + } __attribute__ ((packed)) auth; + struct { + u16 reason_code; + } __attribute__ ((packed)) deauth; + struct { + u16 capab_info; + u16 listen_interval; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_req; + struct { + u16 capab_info; + u16 status_code; + u16 aid; + /* followed by Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) assoc_resp, reassoc_resp; + struct { + u16 capab_info; + u16 listen_interval; + u8 current_ap[6]; + /* followed by SSID and Supported rates */ + u8 variable[0]; + } __attribute__ ((packed)) reassoc_req; + struct { + u16 reason_code; + } __attribute__ ((packed)) disassoc; + struct { + } __attribute__ ((packed)) probe_req; + struct { + u8 timestamp[8]; + u16 beacon_int; + u16 capab_info; + /* followed by some of SSID, Supported rates, + * FH Params, DS Params, CF Params, IBSS Params, TIM */ + u8 variable[0]; + } __attribute__ ((packed)) beacon, probe_resp; + } u; +} __attribute__ ((packed)); + + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +struct hostap_80211_rx_status { + u32 mac_time; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ +}; + + +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); + + +/* prism2_rx_80211 'type' argument */ +enum { + PRISM2_RX_MONITOR, PRISM2_RX_MGMT, PRISM2_RX_NON_ASSOC, + PRISM2_RX_NULLFUNC_ACK +}; + +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type); +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); + +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb); +int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev); +int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev); +struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct prism2_crypt_data *crypt); +int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev); + +#endif /* HOSTAP_80211_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,1080 @@ +#include + +#include "hostap_80211.h" +#include "hostap.h" + +void hostap_dump_rx_80211(const char *name, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: RX signal=%d noise=%d rate=%d len=%d " + "jiffies=%ld\n", + name, rx_stats->signal, rx_stats->noise, rx_stats->rate, + skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR, + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3)); + if (skb->len >= 30) + printk(" A4=" MACSTR, MAC2STR(hdr->addr4)); + printk("\n"); +} + + +/* Send RX frame to netif with 802.11 (and possible prism) header. + * Called from hardware or software IRQ context. */ +int prism2_rx_80211(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int type) +{ + struct hostap_interface *iface; + local_info_t *local; + int hdrlen, phdrlen, head_need, tail_need; + u16 fc; + int prism_header, ret; + struct hostap_ieee80211_hdr *hdr; + + iface = netdev_priv(dev); + local = iface->local; + dev->last_rx = jiffies; + + if (dev->type == ARPHRD_IEEE80211_PRISM) { + if (local->monitor_type == PRISM2_MONITOR_PRISM) { + prism_header = 1; + phdrlen = sizeof(struct linux_wlan_ng_prism_hdr); + } else { /* local->monitor_type == PRISM2_MONITOR_CAPHDR */ + prism_header = 2; + phdrlen = sizeof(struct linux_wlan_ng_cap_hdr); + } + } else { + prism_header = 0; + phdrlen = 0; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (type == PRISM2_RX_MGMT && (fc & WLAN_FC_PVER)) { + printk(KERN_DEBUG "%s: dropped management frame with header " + "version %d\n", dev->name, fc & WLAN_FC_PVER); + dev_kfree_skb_any(skb); + return 0; + } + + hdrlen = hostap_80211_get_hdrlen(fc); + + /* check if there is enough room for extra data; if not, expand skb + * buffer to be large enough for the changes */ + head_need = phdrlen; + tail_need = 0; +#ifdef PRISM2_ADD_BOGUS_CRC + tail_need += 4; +#endif /* PRISM2_ADD_BOGUS_CRC */ + + head_need -= skb_headroom(skb); + tail_need -= skb_tailroom(skb); + + if (head_need > 0 || tail_need > 0) { + if (pskb_expand_head(skb, head_need > 0 ? head_need : 0, + tail_need > 0 ? tail_need : 0, + GFP_ATOMIC)) { + printk(KERN_DEBUG "%s: prism2_rx_80211 failed to " + "reallocate skb buffer\n", dev->name); + dev_kfree_skb_any(skb); + return 0; + } + } + + /* We now have an skb with enough head and tail room, so just insert + * the extra data */ + +#ifdef PRISM2_ADD_BOGUS_CRC + memset(skb_put(skb, 4), 0xff, 4); /* Prism2 strips CRC */ +#endif /* PRISM2_ADD_BOGUS_CRC */ + + if (prism_header == 1) { + struct linux_wlan_ng_prism_hdr *hdr; + hdr = (struct linux_wlan_ng_prism_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->msgcode = LWNG_CAP_DID_BASE; + hdr->msglen = sizeof(*hdr); + memcpy(hdr->devname, dev->name, sizeof(hdr->devname)); +#define LWNG_SETVAL(f,i,s,l,d) \ +hdr->f.did = LWNG_CAP_DID_BASE | (i << 12); \ +hdr->f.status = s; hdr->f.len = l; hdr->f.data = d + LWNG_SETVAL(hosttime, 1, 0, 4, jiffies); + LWNG_SETVAL(mactime, 2, 0, 0, rx_stats->mac_time); + LWNG_SETVAL(channel, 3, 1 /* no value */, 4, 0); + LWNG_SETVAL(rssi, 4, 1 /* no value */, 4, 0); + LWNG_SETVAL(sq, 5, 1 /* no value */, 4, 0); + LWNG_SETVAL(signal, 6, 0, 4, rx_stats->signal); + LWNG_SETVAL(noise, 7, 0, 4, rx_stats->noise); + LWNG_SETVAL(rate, 8, 0, 4, rx_stats->rate / 5); + LWNG_SETVAL(istx, 9, 0, 4, 0); + LWNG_SETVAL(frmlen, 10, 0, 4, skb->len - phdrlen); +#undef LWNG_SETVAL + } else if (prism_header == 2) { + struct linux_wlan_ng_cap_hdr *hdr; + hdr = (struct linux_wlan_ng_cap_hdr *) + skb_push(skb, phdrlen); + memset(hdr, 0, phdrlen); + hdr->version = htonl(LWNG_CAPHDR_VERSION); + hdr->length = htonl(phdrlen); + hdr->mactime = __cpu_to_be64(rx_stats->mac_time); + hdr->hosttime = __cpu_to_be64(jiffies); + hdr->phytype = htonl(4); /* dss_dot11_b */ + hdr->channel = htonl(local->channel); + hdr->datarate = htonl(rx_stats->rate); + hdr->antenna = htonl(0); /* unknown */ + hdr->priority = htonl(0); /* unknown */ + hdr->ssi_type = htonl(3); /* raw */ + hdr->ssi_signal = htonl(rx_stats->signal); + hdr->ssi_noise = htonl(rx_stats->noise); + hdr->preamble = htonl(0); /* unknown */ + hdr->encoding = htonl(1); /* cck */ + } + + ret = skb->len - phdrlen; + skb->dev = dev; + skb->mac.raw = skb->data; + skb_pull(skb, hdrlen); + if (prism_header) + skb_pull(skb, phdrlen); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void monitor_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device_stats *stats; + int len; + + len = prism2_rx_80211(dev, skb, rx_stats, PRISM2_RX_MONITOR); + stats = hostap_get_stats(dev); + stats->rx_packets++; + stats->rx_bytes += len; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct prism2_frag_entry * +prism2_frag_cache_find(local_info_t *local, unsigned int seq, + unsigned int frag, u8 *src, u8 *dst) +{ + struct prism2_frag_entry *entry; + int i; + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + entry = &local->frag_cache[i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + printk(KERN_DEBUG "%s: expiring fragment cache entry " + "seq=%u last_frag=%u\n", + local->dev->name, entry->seq, entry->last_frag); + dev_kfree_skb(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +prism2_frag_cache_get(local_info_t *local, struct hostap_ieee80211_hdr *hdr) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + seq = WLAN_GET_SEQ_SEQ(sc); + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(local->dev->mtu + + sizeof(struct hostap_ieee80211_hdr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + ETH_ALEN /* WDS */); + if (skb == NULL) + return NULL; + + entry = &local->frag_cache[local->frag_next_idx]; + local->frag_next_idx++; + if (local->frag_next_idx >= PRISM2_FRAG_CACHE_LEN) + local->frag_next_idx = 0; + + if (entry->skb != NULL) + dev_kfree_skb(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = prism2_frag_cache_find(local, seq, frag, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + + +/* Called only as a tasklet (software IRQ) */ +static int prism2_frag_cache_invalidate(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + u16 sc; + unsigned int seq; + struct prism2_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctrl); + seq = WLAN_GET_SEQ_SEQ(sc); + + entry = prism2_frag_cache_find(local, seq, -1, hdr->addr2, hdr->addr1); + + if (entry == NULL) { + printk(KERN_DEBUG "%s: could not invalidate fragment cache " + "entry (seq=%u)\n", + local->dev->name, seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + + +static struct hostap_bss_info *__hostap_get_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct list_head *ptr; + struct hostap_bss_info *bss; + + list_for_each(ptr, &local->bss_list) { + bss = list_entry(ptr, struct hostap_bss_info, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0 && + (ssid == NULL || + (ssid_len == bss->ssid_len && + memcmp(ssid, bss->ssid, ssid_len) == 0))) { + list_move(&bss->list, &local->bss_list); + return bss; + } + } + + return NULL; +} + + +static struct hostap_bss_info *__hostap_add_bss(local_info_t *local, u8 *bssid, + u8 *ssid, size_t ssid_len) +{ + struct hostap_bss_info *bss; + + if (local->num_bss_info >= HOSTAP_MAX_BSS_COUNT) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + list_del(&bss->list); + local->num_bss_info--; + } else { + bss = (struct hostap_bss_info *) + kmalloc(sizeof(*bss), GFP_ATOMIC); + if (bss == NULL) + return NULL; + } + + memset(bss, 0, sizeof(*bss)); + memcpy(bss->bssid, bssid, ETH_ALEN); + memcpy(bss->ssid, ssid, ssid_len); + bss->ssid_len = ssid_len; + local->num_bss_info++; + list_add(&bss->list, &local->bss_list); + return bss; +} + + +static void __hostap_expire_bss(local_info_t *local) +{ + struct hostap_bss_info *bss; + + while (local->num_bss_info > 0) { + bss = list_entry(local->bss_list.prev, + struct hostap_bss_info, list); + if (!time_after(jiffies, bss->last_update + 60 * HZ)) + break; + + list_del(&bss->list); + local->num_bss_info--; + kfree(bss); + } +} + + +/* Both IEEE 802.11 Beacon and Probe Response frames have similar structure, so + * the same routine can be used to parse both of them. */ +static void hostap_rx_sta_beacon(local_info_t *local, struct sk_buff *skb, + int stype) +{ + struct hostap_ieee80211_mgmt *mgmt; + int left; + u8 *pos; + u8 *ssid = NULL, *wpa = NULL, *rsn = NULL; + size_t ssid_len = 0, wpa_len = 0, rsn_len = 0; + struct hostap_bss_info *bss; + + if (!local->wpa || + skb->len < IEEE80211_MGMT_HDR_LEN + sizeof(mgmt->u.beacon)) + return; + + mgmt = (struct hostap_ieee80211_mgmt *) skb->data; + pos = mgmt->u.beacon.variable; + left = skb->len - (pos - skb->data); + + while (left >= 2) { + if (2 + pos[1] > left) + return; /* parse failed */ + switch (*pos) { + case WLAN_EID_SSID: + ssid = pos + 2; + ssid_len = pos[1]; + break; + case WLAN_EID_GENERIC: + if (pos[1] >= 4 && + pos[2] == 0x00 && pos[3] == 0x50 && + pos[4] == 0xf2 && pos[5] == 1) { + wpa = pos; + wpa_len = pos[1] + 2; + } + break; + case WLAN_EID_RSN: + rsn = pos; + rsn_len = pos[1] + 2; + break; + } + left -= 2 + pos[1]; + pos += 2 + pos[1]; + } + + if (wpa_len > MAX_WPA_IE_LEN) + wpa_len = MAX_WPA_IE_LEN; + if (rsn_len > MAX_WPA_IE_LEN) + rsn_len = MAX_WPA_IE_LEN; + if (ssid_len > sizeof(bss->ssid)) + ssid_len = sizeof(bss->ssid); + + spin_lock(&local->lock); + bss = __hostap_get_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss == NULL) + bss = __hostap_add_bss(local, mgmt->bssid, ssid, ssid_len); + if (bss) { + bss->last_update = jiffies; + bss->count++; + bss->capab_info = le16_to_cpu(mgmt->u.beacon.capab_info); + if (wpa) { + memcpy(bss->wpa_ie, wpa, wpa_len); + bss->wpa_ie_len = wpa_len; + } else + bss->wpa_ie_len = 0; + if (rsn) { + memcpy(bss->rsn_ie, rsn, rsn_len); + bss->rsn_ie_len = rsn_len; + } else + bss->rsn_ie_len = 0; + } + __hostap_expire_bss(local); + spin_unlock(&local->lock); +} + + +static inline int +hostap_rx_frame_mgmt(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, u16 type, + u16 stype) +{ + if (local->iw_mode == IW_MODE_MASTER) { + hostap_update_sta_ps(local, (struct hostap_ieee80211_hdr *) + skb->data); + } + + if (local->hostapd && type == WLAN_FC_TYPE_MGMT) { + if (stype == WLAN_FC_STYPE_BEACON && + local->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + if (local->apdev == NULL) + return -1; + prism2_rx_80211(local->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (local->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } else if (type == WLAN_FC_TYPE_MGMT && + (stype == WLAN_FC_STYPE_BEACON || + stype == WLAN_FC_STYPE_PROBE_RESP)) { + hostap_rx_sta_beacon(local, skb, stype); + return -1; + } else if (type == WLAN_FC_TYPE_MGMT && + (stype == WLAN_FC_STYPE_ASSOC_RESP || + stype == WLAN_FC_STYPE_REASSOC_RESP)) { + /* Ignore (Re)AssocResp silently since these are not currently + * needed but are still received when WPA/RSN mode is enabled. + */ + return -1; + } else { + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: dropped unhandled" + " management frame in non-Host AP mode (type=%d:%d)\n", + skb->dev->name, type, stype); + return -1; + } +} + + +/* Called only as a tasklet (software IRQ) */ +static inline struct net_device *prism2_rx_get_wds(local_info_t *local, + u8 *addr) +{ + struct hostap_interface *iface = NULL; + struct list_head *ptr; + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_WDS && + memcmp(iface->u.wds.remote_addr, addr, ETH_ALEN) == 0) + break; + iface = NULL; + } + read_unlock_bh(&local->iface_lock); + + return iface ? iface->dev : NULL; +} + + +static inline int +hostap_rx_frame_wds(local_info_t *local, struct hostap_ieee80211_hdr *hdr, + u16 fc, struct net_device **wds) +{ + /* FIX: is this really supposed to accept WDS frames only in Master + * mode? What about Repeater or Managed with WDS frames? */ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) != + (WLAN_FC_TODS | WLAN_FC_FROMDS) && + (local->iw_mode != IW_MODE_MASTER || !(fc & WLAN_FC_TODS))) + return 0; /* not a WDS frame */ + + /* Possible WDS frame: either IEEE 802.11 compliant (if FromDS) + * or own non-standard frame with 4th address after payload */ + if (memcmp(hdr->addr1, local->dev->dev_addr, ETH_ALEN) != 0 && + (hdr->addr1[0] != 0xff || hdr->addr1[1] != 0xff || + hdr->addr1[2] != 0xff || hdr->addr1[3] != 0xff || + hdr->addr1[4] != 0xff || hdr->addr1[5] != 0xff)) { + /* RA (or BSSID) is not ours - drop */ + PDEBUG(DEBUG_EXTRA, "%s: received WDS frame with " + "not own or broadcast %s=" MACSTR "\n", + local->dev->name, fc & WLAN_FC_FROMDS ? "RA" : "BSSID", + MAC2STR(hdr->addr1)); + return -1; + } + + /* check if the frame came from a registered WDS connection */ + *wds = prism2_rx_get_wds(local, hdr->addr2); + if (*wds == NULL && fc & WLAN_FC_FROMDS && + (local->iw_mode != IW_MODE_INFRA || + !(local->wds_type & HOSTAP_WDS_AP_CLIENT) || + memcmp(hdr->addr2, local->bssid, ETH_ALEN) != 0)) { + /* require that WDS link has been registered with TA or the + * frame is from current AP when using 'AP client mode' */ + PDEBUG(DEBUG_EXTRA, "%s: received WDS[4 addr] frame " + "from unknown TA=" MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr2)); + if (local->ap && local->ap->autom_ap_wds) + hostap_wds_link_oper(local, hdr->addr2, WDS_ADD); + return -1; + } + + if (*wds && !(fc & WLAN_FC_FROMDS) && local->ap && + hostap_is_sta_assoc(local->ap, hdr->addr2)) { + /* STA is actually associated with us even though it has a + * registered WDS link. Assume it is in 'AP client' mode. + * Since this is a 3-addr frame, assume it is not (bogus) WDS + * frame and process it like any normal ToDS frame from + * associated STA. */ + *wds = NULL; + } + + return 0; +} + + +static int hostap_is_eapol_frame(local_info_t *local, struct sk_buff *skb) +{ + struct net_device *dev = local->dev; + u16 fc, ethertype; + struct hostap_ieee80211_hdr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* check that the frame is unicast frame to us */ + if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + 24; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static inline int +hostap_rx_frame_decrypt(local_info_t *local, struct sk_buff *skb, + struct prism2_crypt_data *crypt) +{ + struct hostap_ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + if (local->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from " MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr2)); + } + return -1; + } + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: decryption failed (SA=" MACSTR + ") res=%d\n", + local->dev->name, MAC2STR(hdr->addr2), res); + local->comm_tallies.rx_discards_wep_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ) */ +static inline int +hostap_rx_frame_decrypt_msdu(local_info_t *local, struct sk_buff *skb, + int keyidx, struct prism2_crypt_data *crypt) +{ + struct hostap_ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(hdr->frame_control)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=" MACSTR " keyidx=%d)\n", + local->dev->name, MAC2STR(hdr->addr2), keyidx); + return -1; + } + + return 0; +} + + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_ieee80211_hdr *hdr; + size_t hdrlen; + u16 fc, type, stype, sc; + struct net_device *wds = NULL; + struct net_device_stats *stats; + unsigned int frag; + u8 *payload; + struct sk_buff *skb2 = NULL; + u16 ethertype; + int frame_authorized = 0; + int from_assoc_ap = 0; + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct prism2_crypt_data *crypt = NULL; + void *sta = NULL; + int keyidx = 0; + + iface = netdev_priv(dev); + local = iface->local; + iface->stats.rx_packets++; + iface->stats.rx_bytes += skb->len; + + /* dev is the master radio device; change this to be the default + * virtual interface (this may be changed to WDS device below) */ + dev = local->ddev; + iface = netdev_priv(dev); + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + stats = hostap_get_stats(dev); + + if (skb->len < 10) + goto rx_dropped; + + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctrl); + frag = WLAN_GET_SEQ_FRAG(sc); + hdrlen = hostap_80211_get_hdrlen(fc); + + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); + + if (local->iw_mode == IW_MODE_MONITOR) { + monitor_rx(dev, skb, rx_stats); + return; + } + + if (local->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt[idx]; + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & WLAN_FC_ISWEP)) { +#if 0 + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + printk(KERN_DEBUG "%s: WEP decryption failed (not set)" + " (SA=" MACSTR ")\n", + local->dev->name, MAC2STR(hdr->addr2)); +#endif + local->comm_tallies.rx_discards_wep_undecryptable++; + goto rx_dropped; + } + } + + if (type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & WLAN_FC_ISWEP && local->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from " MACSTR "\n", dev->name, + MAC2STR(hdr->addr2)); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + + if (hostap_rx_frame_mgmt(local, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } + + /* Data frame - extract src/dst addresses */ + if (skb->len < IEEE80211_DATA_HDR3_LEN) + goto rx_dropped; + + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + break; + case WLAN_FC_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + case WLAN_FC_FROMDS | WLAN_FC_TODS: + if (skb->len < IEEE80211_DATA_HDR4_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + } + + if (hostap_rx_frame_wds(local, hdr, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = wds; + stats = hostap_get_stats(dev); + } + + if (local->iw_mode == IW_MODE_MASTER && !wds && + (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_FROMDS && + local->stadev && + memcmp(hdr->addr2, local->assoc_ap_addr, ETH_ALEN) == 0) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = local->stadev; + stats = hostap_get_stats(dev); + from_assoc_ap = 1; + } + + dev->last_rx = jiffies; + + if ((local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT) && + !from_assoc_ap) { + switch (hostap_handle_sta_rx(local, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != WLAN_FC_STYPE_DATA && + stype != WLAN_FC_STYPE_DATA_CFACK && + stype != WLAN_FC_STYPE_DATA_CFPOLL && + stype != WLAN_FC_STYPE_DATA_CFACKPOLL) { + if (stype != WLAN_FC_STYPE_NULLFUNC) + printk(KERN_DEBUG "%s: RX: dropped data frame " + "with no data (type=0x%02x, subtype=0x%02x)\n", + dev->name, type, stype); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + (keyidx = hostap_rx_frame_decrypt(local, skb, crypt)) < 0) + goto rx_dropped; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + (frag != 0 || (fc & WLAN_FC_MOREFRAG))) { + int flen; + struct sk_buff *frag_skb = + prism2_frag_cache_get(local, hdr); + if (!frag_skb) { + printk(KERN_DEBUG "%s: Rx cannot get skb from " + "fragment cache (morefrag=%d seq=%u frag=%u)\n", + dev->name, (fc & WLAN_FC_MOREFRAG) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + prism2_frag_cache_invalidate(local, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, + flen); + } + dev_kfree_skb(skb); + skb = NULL; + + if (fc & WLAN_FC_MOREFRAG) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + prism2_frag_cache_invalidate(local, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + + if (local->host_decrypt && (fc & WLAN_FC_ISWEP) && + hostap_rx_frame_decrypt_msdu(local, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (crypt && !(fc & WLAN_FC_ISWEP) && !local->open_wep) { + if (local->ieee_802_1x && + hostap_is_eapol_frame(local, skb)) { + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", local->dev->name); + } else { + printk(KERN_DEBUG "%s: encryption configured, but RX " + "frame not encrypted (SA=" MACSTR ")\n", + local->dev->name, MAC2STR(hdr->addr2)); + goto rx_dropped; + } + } + + if (local->drop_unencrypted && !(fc & WLAN_FC_ISWEP) && + !hostap_is_eapol_frame(local, skb)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted RX data " + "frame from " MACSTR " (drop_unencrypted=1)\n", + dev->name, MAC2STR(hdr->addr2)); + } + goto rx_dropped; + } + + /* skb: hdr + (possible reassembled) full plaintext payload */ + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (local->ieee_802_1x && local->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + PDEBUG(DEBUG_EXTRA2, "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (local->hostapd && local->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + local->apdevstats.rx_packets++; + local->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + goto rx_dropped; + } + } + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, 6) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, 6) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + 6); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + + if (wds && ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == WLAN_FC_TODS) && + skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + memcpy(skb->data + ETH_ALEN, + skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } + + stats->rx_packets++; + stats->rx_bytes += skb->len; + + if (local->iw_mode == IW_MODE_MASTER && !wds && + local->ap->bridge_packets) { + if (dst[0] & 0x01) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + local->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_authorized(local->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + local->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb2->mac.raw = skb2->nh.raw = skb2->data; + /* skb2->nh.raw = skb2->data + ETH_HLEN; */ + skb2->dev = dev; + dev_queue_xmit(skb2); + } + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + netif_rx(skb); + } + + rx_exit: + if (sta) + hostap_handle_sta_release(sta); + return; + + rx_dropped: + dev_kfree_skb(skb); + + stats->rx_dropped++; + goto rx_exit; +} + + +EXPORT_SYMBOL(hostap_80211_rx); diff -Nru a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,522 @@ +void hostap_dump_tx_80211(const char *name, struct sk_buff *skb) +{ + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + printk(KERN_DEBUG "%s: TX len=%d jiffies=%ld\n", + name, skb->len, jiffies); + + if (skb->len < 2) + return; + + fc = le16_to_cpu(hdr->frame_control); + printk(KERN_DEBUG " FC=0x%04x (type=%d:%d)%s%s", + fc, WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " [ToDS]" : "", + fc & WLAN_FC_FROMDS ? " [FromDS]" : ""); + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + printk("\n"); + return; + } + + printk(" dur=0x%04x seq=0x%04x\n", le16_to_cpu(hdr->duration_id), + le16_to_cpu(hdr->seq_ctrl)); + + printk(KERN_DEBUG " A1=" MACSTR " A2=" MACSTR " A3=" MACSTR, + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), MAC2STR(hdr->addr3)); + if (skb->len >= 30) + printk(" A4=" MACSTR, MAC2STR(hdr->addr4)); + printk("\n"); +} + + +/* hard_start_xmit function for data interfaces (wlan#, wlan#wds#, wlan#sta) + * Convert Ethernet header into a suitable IEEE 802.11 header depending on + * device configuration. */ +int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int need_headroom, need_tailroom = 0; + struct hostap_ieee80211_hdr hdr; + u16 fc, ethertype = 0; + enum { + WDS_NO = 0, WDS_OWN_FRAME, WDS_COMPLIANT_FRAME + } use_wds = WDS_NO; + u8 *encaps_data; + int hdr_len, encaps_len, skip_header_bytes; + int to_assoc_ap = 0; + struct hostap_skb_tx_data *meta; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < ETH_HLEN) { + printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return 0; + } + + if (local->ddev != dev) { + use_wds = (local->iw_mode == IW_MODE_MASTER && + !(local->wds_type & HOSTAP_WDS_STANDARD_FRAME)) ? + WDS_OWN_FRAME : WDS_COMPLIANT_FRAME; + if (dev == local->stadev) { + to_assoc_ap = 1; + use_wds = WDS_NO; + } else if (dev == local->apdev) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "AP device with Ethernet net dev\n", dev->name); + kfree_skb(skb); + return 0; + } + } else { + if (local->iw_mode == IW_MODE_REPEAT) { + printk(KERN_DEBUG "%s: prism2_tx: trying to use " + "non-WDS link in Repeater mode\n", dev->name); + kfree_skb(skb); + return 0; + } else if (local->iw_mode == IW_MODE_INFRA && + (local->wds_type & HOSTAP_WDS_AP_CLIENT) && + memcmp(skb->data + ETH_ALEN, dev->dev_addr, + ETH_ALEN) != 0) { + /* AP client mode: send frames with foreign src addr + * using 4-addr WDS frames */ + use_wds = WDS_COMPLIANT_FRAME; + } + } + + /* Incoming skb->data: dst_addr[6], src_addr[6], proto[2], payload + * ==> + * Prism2 TX frame with 802.11 header: + * txdesc (address order depending on used mode; includes dst_addr and + * src_addr), possible encapsulation (RFC1042/Bridge-Tunnel; + * proto[2], payload {, possible addr4[6]} */ + + ethertype = (skb->data[12] << 8) | skb->data[13]; + + memset(&hdr, 0, sizeof(hdr)); + + /* Length of data after IEEE 802.11 header */ + encaps_data = NULL; + encaps_len = 0; + skip_header_bytes = ETH_HLEN; + if (ethertype == ETH_P_AARP || ethertype == ETH_P_IPX) { + encaps_data = bridge_tunnel_header; + encaps_len = sizeof(bridge_tunnel_header); + skip_header_bytes -= 2; + } else if (ethertype >= 0x600) { + encaps_data = rfc1042_header; + encaps_len = sizeof(rfc1042_header); + skip_header_bytes -= 2; + } + + fc = (WLAN_FC_TYPE_DATA << 2) | (WLAN_FC_STYPE_DATA << 4); + hdr_len = IEEE80211_DATA_HDR3_LEN; + + if (use_wds != WDS_NO) { + /* Note! Prism2 station firmware has problems with sending real + * 802.11 frames with four addresses; until these problems can + * be fixed or worked around, 4-addr frames needed for WDS are + * using incompatible format: FromDS flag is not set and the + * fourth address is added after the frame payload; it is + * assumed, that the receiving station knows how to handle this + * frame format */ + + if (use_wds == WDS_COMPLIANT_FRAME) { + fc |= WLAN_FC_FROMDS | WLAN_FC_TODS; + /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, + * Addr4 = SA */ + memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + hdr_len += ETH_ALEN; + } else { + /* bogus 4-addr format to workaround Prism2 station + * f/w bug */ + fc |= WLAN_FC_TODS; + /* From DS: Addr1 = DA (used as RA), + * Addr2 = BSSID (used as TA), Addr3 = SA (used as DA), + */ + + /* SA from skb->data + ETH_ALEN will be added after + * frame payload; use hdr.addr4 as a temporary buffer + */ + memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + need_tailroom += ETH_ALEN; + } + + /* send broadcast and multicast frames to broadcast RA, if + * configured; otherwise, use unicast RA of the WDS link */ + if ((local->wds_type & HOSTAP_WDS_BROADCAST_RA) && + skb->data[0] & 0x01) + memset(&hdr.addr1, 0xff, ETH_ALEN); + else if (iface->type == HOSTAP_INTERFACE_WDS) + memcpy(&hdr.addr1, iface->u.wds.remote_addr, + ETH_ALEN); + else + memcpy(&hdr.addr1, local->bssid, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&hdr.addr3, skb->data, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { + fc |= WLAN_FC_FROMDS; + /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ + memcpy(&hdr.addr1, skb->data, ETH_ALEN); + memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); + memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { + fc |= WLAN_FC_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ + memcpy(&hdr.addr1, to_assoc_ap ? + local->assoc_ap_addr : local->bssid, ETH_ALEN); + memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&hdr.addr3, skb->data, ETH_ALEN); + } else if (local->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ + memcpy(&hdr.addr1, skb->data, ETH_ALEN); + memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + memcpy(&hdr.addr3, local->bssid, ETH_ALEN); + } + + hdr.frame_control = cpu_to_le16(fc); + + skb_pull(skb, skip_header_bytes); + need_headroom = local->func->need_tx_headroom + hdr_len + encaps_len; + if (skb_tailroom(skb) < need_tailroom) { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + if (pskb_expand_head(skb, need_headroom, need_tailroom, + GFP_ATOMIC)) { + kfree_skb(skb); + iface->stats.tx_dropped++; + return 0; + } + } else if (skb_headroom(skb) < need_headroom) { + struct sk_buff *tmp = skb; + skb = skb_realloc_headroom(skb, need_headroom); + kfree_skb(tmp); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + } else { + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) { + iface->stats.tx_dropped++; + return 0; + } + } + + if (encaps_data) + memcpy(skb_push(skb, encaps_len), encaps_data, encaps_len); + memcpy(skb_push(skb, hdr_len), &hdr, hdr_len); + if (use_wds == WDS_OWN_FRAME) { + memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN); + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + skb->mac.raw = skb->data; + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->wds = use_wds; + meta->ethertype = ethertype; + meta->iface = iface; + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return 0; +} + + +/* hard_start_xmit function for hostapd wlan#ap interfaces */ +int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_skb_tx_data *meta; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < 10) { + printk(KERN_DEBUG "%s: hostap_mgmt_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + kfree_skb(skb); + return 0; + } + + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + + if (skb->len >= IEEE80211_DATA_HDR3_LEN + sizeof(rfc1042_header) + 2) { + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_DATA) { + u8 *pos = &skb->data[IEEE80211_DATA_HDR3_LEN + + sizeof(rfc1042_header)]; + meta->ethertype = (pos[0] << 8) | pos[1]; + } + } + + /* Send IEEE 802.11 encapsulated frame using the master radio device */ + skb->dev = local->dev; + dev_queue_xmit(skb); + return 0; +} + + +/* Called only from software IRQ */ +struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb, + struct prism2_crypt_data *crypt) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + int hdr_len, res; + + iface = netdev_priv(skb->dev); + local = iface->local; + + if (skb->len < IEEE80211_DATA_HDR3_LEN) { + kfree_skb(skb); + return NULL; + } + + if (local->tkip_countermeasures && + crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to " MACSTR "\n", + local->dev->name, MAC2STR(hdr->addr1)); + } + kfree_skb(skb); + return NULL; + } + + skb = skb_unshare(skb, GFP_ATOMIC); + if (skb == NULL) + return NULL; + + if ((skb_headroom(skb) < crypt->ops->extra_prefix_len || + skb_tailroom(skb) < crypt->ops->extra_postfix_len) && + pskb_expand_head(skb, crypt->ops->extra_prefix_len, + crypt->ops->extra_postfix_len, GFP_ATOMIC)) { + kfree_skb(skb); + return NULL; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + hdr_len = hostap_80211_get_hdrlen(fc); + + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(skb, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(skb, hdr_len, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + kfree_skb(skb); + return NULL; + } + + return skb; +} + + +/* hard_start_xmit function for master radio interface wifi#. + * AP processing (TX rate control, power save buffering, etc.). + * Use hardware TX function to send the frame. */ +int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret = 1; + u16 fc; + struct hostap_tx_data tx; + ap_tx_ret tx_ret; + struct hostap_skb_tx_data *meta; + int no_encrypt = 0; + struct hostap_ieee80211_hdr *hdr; + + iface = netdev_priv(dev); + local = iface->local; + + tx.skb = skb; + tx.sta_ptr = NULL; + + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x)\n", + dev->name, meta->magic, HOSTAP_SKB_TX_DATA_MAGIC); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + + if (local->host_encrypt) { + /* Set crypt to default algorithm and key; will be replaced in + * AP code if STA has own alg/key */ + tx.crypt = local->crypt[local->tx_keyidx]; + tx.host_encrypt = 1; + } else { + tx.crypt = NULL; + tx.host_encrypt = 0; + } + + if (skb->len < 24) { + printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + + /* FIX (?): + * Wi-Fi 802.11b test plan suggests that AP should ignore power save + * bit in authentication and (re)association frames and assume tha + * STA remains awake for the response. */ + tx_ret = hostap_handle_sta_tx(local, &tx); + skb = tx.skb; + meta = (struct hostap_skb_tx_data *) skb->cb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + switch (tx_ret) { + case AP_TX_CONTINUE: + break; + case AP_TX_CONTINUE_NOT_AUTHORIZED: + if (local->ieee_802_1x && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + meta->ethertype != ETH_P_PAE && !meta->wds) { + printk(KERN_DEBUG "%s: dropped frame to unauthorized " + "port (IEEE 802.1X): ethertype=0x%04x\n", + dev->name, meta->ethertype); + hostap_dump_tx_80211(dev->name, skb); + + ret = 0; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + } + break; + case AP_TX_DROP: + ret = 0; /* drop packet */ + iface->stats.tx_dropped++; + goto fail; + case AP_TX_RETRY: + goto fail; + case AP_TX_BUFFERED: + /* do not free skb here, it will be freed when the + * buffered frame is sent/timed out */ + ret = 0; + goto tx_exit; + } + + /* Request TX callback if protocol version is 2 in 802.11 header; + * this version 2 is a special case used between hostapd and kernel + * driver */ + if (((fc & WLAN_FC_PVER) == BIT(1)) && + local->ap && local->ap->tx_callback_idx && meta->tx_cb_idx == 0) { + meta->tx_cb_idx = local->ap->tx_callback_idx; + + /* remove special version from the frame header */ + fc &= ~WLAN_FC_PVER; + hdr->frame_control = cpu_to_le16(fc); + } + + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_DATA) { + no_encrypt = 1; + tx.crypt = NULL; + } + + if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt && + !(fc & WLAN_FC_ISWEP)) { + no_encrypt = 1; + PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing " + "unencrypted EAPOL frame\n", dev->name); + tx.crypt = NULL; /* no encryption for IEEE 802.1X frames */ + } + + if (tx.crypt && (!tx.crypt->ops || !tx.crypt->ops->encrypt_mpdu)) + tx.crypt = NULL; + else if ((tx.crypt || local->crypt[local->tx_keyidx]) && !no_encrypt) { + /* Add ISWEP flag both for firmware and host based encryption + */ + fc |= WLAN_FC_ISWEP; + hdr->frame_control = cpu_to_le16(fc); + } else if (local->drop_unencrypted && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + meta->ethertype != ETH_P_PAE) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: dropped unencrypted TX data " + "frame (drop_unencrypted=1)\n", dev->name); + } + iface->stats.tx_dropped++; + ret = 0; + goto fail; + } + + if (tx.crypt) { + skb = hostap_tx_encrypt(skb, tx.crypt); + if (skb == NULL) { + printk(KERN_DEBUG "%s: TX - encryption failed\n", + dev->name); + ret = 0; + goto fail; + } + meta = (struct hostap_skb_tx_data *) skb->cb; + if (meta->magic != HOSTAP_SKB_TX_DATA_MAGIC) { + printk(KERN_DEBUG "%s: invalid skb->cb magic (0x%08x, " + "expected 0x%08x) after hostap_tx_encrypt\n", + dev->name, meta->magic, + HOSTAP_SKB_TX_DATA_MAGIC); + ret = 0; + iface->stats.tx_dropped++; + goto fail; + } + } + + if (local->func->tx == NULL || local->func->tx(skb, dev)) { + ret = 0; + iface->stats.tx_dropped++; + } else { + ret = 0; + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + } + + fail: + if (!ret && skb) + dev_kfree_skb(skb); + tx_exit: + if (tx.sta_ptr) + hostap_handle_sta_release(tx.sta_ptr); + return ret; +} + + +EXPORT_SYMBOL(hostap_dump_tx_80211); +EXPORT_SYMBOL(hostap_tx_encrypt); +EXPORT_SYMBOL(hostap_master_start_xmit); diff -Nru a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_ap.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,3259 @@ +/* + * Intersil Prism2 driver with Host AP (software access point) support + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * This file is to be included into hostap.c when S/W AP functionality is + * compiled. + * + * AP: FIX: + * - if unicast Class 2 (assoc,reassoc,disassoc) frame received from + * unauthenticated STA, send deauth. frame (8802.11: 5.5) + * - if unicast Class 3 (data with to/from DS,deauth,pspoll) frame received + * from authenticated, but unassoc STA, send disassoc frame (8802.11: 5.5) + * - if unicast Class 3 received from unauthenticated STA, send deauth. frame + * (8802.11: 5.5) + */ + +static int other_ap_policy[MAX_PARM_DEVICES] = { AP_OTHER_AP_SKIP_ALL, + DEF_INTS }; +module_param_array(other_ap_policy, int, NULL, 0444); +MODULE_PARM_DESC(other_ap_policy, "Other AP beacon monitoring policy (0-3)"); + +static int ap_max_inactivity[MAX_PARM_DEVICES] = { AP_MAX_INACTIVITY_SEC, + DEF_INTS }; +module_param_array(ap_max_inactivity, int, NULL, 0444); +MODULE_PARM_DESC(ap_max_inactivity, "AP timeout (in seconds) for station " + "inactivity"); + +static int ap_bridge_packets[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +module_param_array(ap_bridge_packets, int, NULL, 0444); +MODULE_PARM_DESC(ap_bridge_packets, "Bridge packets directly between " + "stations"); + +static int autom_ap_wds[MAX_PARM_DEVICES] = { 0, DEF_INTS }; +module_param_array(autom_ap_wds, int, NULL, 0444); +MODULE_PARM_DESC(autom_ap_wds, "Add WDS connections to other APs " + "automatically"); + + +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta); +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta); +static void handle_add_proc_queue(void *data); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static void handle_wds_oper_queue(void *data); +static void prism2_send_mgmt(struct net_device *dev, + int type, int subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int ap_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "BridgedUnicastFrames=%u\n", ap->bridged_unicast); + p += sprintf(p, "BridgedMulticastFrames=%u\n", ap->bridged_multicast); + p += sprintf(p, "max_inactivity=%u\n", ap->max_inactivity / HZ); + p += sprintf(p, "bridge_packets=%u\n", ap->bridge_packets); + p += sprintf(p, "nullfunc_ack=%u\n", ap->nullfunc_ack); + p += sprintf(p, "autom_ap_wds=%u\n", ap->autom_ap_wds); + p += sprintf(p, "auth_algs=%u\n", ap->local->auth_algs); + p += sprintf(p, "tx_drop_nonassoc=%u\n", ap->tx_drop_nonassoc); + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static void ap_sta_hash_add(struct ap_data *ap, struct sta_info *sta) +{ + sta->hnext = ap->sta_hash[STA_HASH(sta->addr)]; + ap->sta_hash[STA_HASH(sta->addr)] = sta; +} + +static void ap_sta_hash_del(struct ap_data *ap, struct sta_info *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (memcmp(s->addr, sta->addr, ETH_ALEN) == 0) { + ap->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && memcmp(s->hnext->addr, sta->addr, ETH_ALEN) + != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printk("AP: could not remove STA " MACSTR " from hash table\n", + MAC2STR(sta->addr)); +} + +static void ap_free_sta(struct ap_data *ap, struct sta_info *sta) +{ + if (sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + + if (ap->proc != NULL) { + char name[20]; + sprintf(name, MACSTR, MAC2STR(sta->addr)); + remove_proc_entry(name, ap->proc); + } + + if (sta->crypt) { + sta->crypt->ops->deinit(sta->crypt->priv); + kfree(sta->crypt); + sta->crypt = NULL; + } + + skb_queue_purge(&sta->tx_buf); + + ap->num_sta--; +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->aid > 0) + ap->sta_aid[sta->aid - 1] = NULL; + + if (!sta->ap && sta->u.sta.challenge) + kfree(sta->u.sta.challenge); + del_timer(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + kfree(sta); +} + + +static void hostap_set_tim(local_info_t *local, int aid, int set) +{ + if (local->func->set_tim) + local->func->set_tim(local->dev, aid, set); +} + + +static void hostap_event_new_sta(struct net_device *dev, struct sta_info *sta) +{ + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVREGISTERED, &wrqu, NULL); +} + + +static void hostap_event_expired_sta(struct net_device *dev, + struct sta_info *sta) +{ + union iwreq_data wrqu; + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.addr.sa_data, sta->addr, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVEXPIRED, &wrqu, NULL); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_handle_timer(unsigned long data) +{ + struct sta_info *sta = (struct sta_info *) data; + local_info_t *local; + struct ap_data *ap; + unsigned long next_time = 0; + int was_assoc; + + if (sta == NULL || sta->local == NULL || sta->local->ap == NULL) { + PDEBUG(DEBUG_AP, "ap_handle_timer() called with NULL data\n"); + return; + } + + local = sta->local; + ap = local->ap; + was_assoc = sta->flags & WLAN_STA_ASSOC; + + if (atomic_read(&sta->users) != 0) + next_time = jiffies + HZ; + else if ((sta->flags & WLAN_STA_PERM) && !(sta->flags & WLAN_STA_AUTH)) + next_time = jiffies + ap->max_inactivity; + + if (time_before(jiffies, sta->last_rx + ap->max_inactivity)) { + /* station activity detected; reset timeout state */ + sta->timeout_next = STA_NULLFUNC; + next_time = sta->last_rx + ap->max_inactivity; + } else if (sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + /* STA ACKed data nullfunc frame poll */ + sta->timeout_next = STA_NULLFUNC; + next_time = jiffies + ap->max_inactivity; + } + + if (next_time) { + sta->timer.expires = next_time; + add_timer(&sta->timer); + return; + } + + if (sta->ap) + sta->timeout_next = STA_DEAUTH; + + if (sta->timeout_next == STA_DEAUTH && !(sta->flags & WLAN_STA_PERM)) { + spin_lock(&ap->sta_table_lock); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + spin_unlock(&ap->sta_table_lock); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } else if (sta->timeout_next == STA_DISASSOC) + sta->flags &= ~WLAN_STA_ASSOC; + + if (was_assoc && !(sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + + if (sta->timeout_next == STA_DEAUTH && sta->aid > 0 && + !skb_queue_empty(&sta->tx_buf)) { + hostap_set_tim(local, sta->aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + if (sta->ap) { + if (ap->autom_ap_wds) { + PDEBUG(DEBUG_AP, "%s: removing automatic WDS " + "connection to AP " MACSTR "\n", + local->dev->name, MAC2STR(sta->addr)); + hostap_wds_link_oper(local, sta->addr, WDS_DEL); + } + } else if (sta->timeout_next == STA_NULLFUNC) { + /* send data frame to poll STA and check whether this frame + * is ACKed */ + /* FIX: WLAN_FC_STYPE_NULLFUNC would be more appropriate, but + * it is apparently not retried so TX Exc events are not + * received for it */ + sta->flags |= WLAN_STA_PENDING_POLL; + prism2_send_mgmt(local->dev, WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA, NULL, 0, + sta->addr, ap->tx_callback_poll); + } else { + int deauth = sta->timeout_next == STA_DEAUTH; + u16 resp; + PDEBUG(DEBUG_AP, "%s: sending %s info to STA " MACSTR + "(last=%lu, jiffies=%lu)\n", + local->dev->name, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr), sta->last_rx, jiffies); + + resp = cpu_to_le16(deauth ? WLAN_REASON_PREV_AUTH_NOT_VALID : + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + prism2_send_mgmt(local->dev, WLAN_FC_TYPE_MGMT, + (deauth ? WLAN_FC_STYPE_DEAUTH : + WLAN_FC_STYPE_DISASSOC), + (char *) &resp, 2, sta->addr, 0); + } + + if (sta->timeout_next == STA_DEAUTH) { + if (sta->flags & WLAN_STA_PERM) { + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " would have been " + "removed, but it has 'perm' flag\n", + local->dev->name, MAC2STR(sta->addr)); + } else + ap_free_sta(ap, sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC) { + sta->timeout_next = STA_DISASSOC; + sta->timer.expires = jiffies + AP_DISASSOC_DELAY; + } else { + sta->timeout_next = STA_DEAUTH; + sta->timer.expires = jiffies + AP_DEAUTH_DELAY; + } + + add_timer(&sta->timer); +} + + +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend) +{ + u8 addr[ETH_ALEN]; + u16 resp; + int i; + + PDEBUG(DEBUG_AP, "%s: Deauthenticate all stations\n", dev->name); + memset(addr, 0xff, ETH_ALEN); + + resp = __constant_cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + + /* deauth message sent; try to resend it few times; the message is + * broadcast, so it may be delayed until next DTIM; there is not much + * else we can do at this point since the driver is going to be shut + * down */ + for (i = 0; i < 5; i++) { + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, addr, 0); + + if (!resend || ap->num_sta <= 0) + return; + + mdelay(50); + } +} + + +static int ap_control_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + char *policy_txt; + struct list_head *ptr; + struct mac_entry *entry; + + if (off != 0) { + *eof = 1; + return 0; + } + + switch (ap->mac_restrictions.policy) { + case MAC_POLICY_OPEN: + policy_txt = "open"; + break; + case MAC_POLICY_ALLOW: + policy_txt = "allow"; + break; + case MAC_POLICY_DENY: + policy_txt = "deny"; + break; + default: + policy_txt = "unknown"; + break; + }; + p += sprintf(p, "MAC policy: %s\n", policy_txt); + p += sprintf(p, "MAC entries: %u\n", ap->mac_restrictions.entries); + p += sprintf(p, "MAC list:\n"); + spin_lock_bh(&ap->mac_restrictions.lock); + for (ptr = ap->mac_restrictions.mac_list.next; + ptr != &ap->mac_restrictions.mac_list; ptr = ptr->next) { + if (p - page > PAGE_SIZE - 80) { + p += sprintf(p, "All entries did not fit one page.\n"); + break; + } + + entry = list_entry(ptr, struct mac_entry, list); + p += sprintf(p, MACSTR "\n", MAC2STR(entry->addr)); + } + spin_unlock_bh(&ap->mac_restrictions.lock); + + return (p - page); +} + + +static int ap_control_add_mac(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct mac_entry *entry; + + entry = kmalloc(sizeof(struct mac_entry), GFP_KERNEL); + if (entry == NULL) + return -1; + + memcpy(entry->addr, mac, ETH_ALEN); + + spin_lock_bh(&mac_restrictions->lock); + list_add_tail(&entry->list, &mac_restrictions->mac_list); + mac_restrictions->entries++; + spin_unlock_bh(&mac_restrictions->lock); + + return 0; +} + + +static int ap_control_del_mac(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct list_head *ptr; + struct mac_entry *entry; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next; + ptr != &mac_restrictions->mac_list; ptr = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + + if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + list_del(ptr); + kfree(entry); + mac_restrictions->entries--; + spin_unlock_bh(&mac_restrictions->lock); + return 0; + } + } + spin_unlock_bh(&mac_restrictions->lock); + return -1; +} + + +static int ap_control_mac_deny(struct mac_restrictions *mac_restrictions, + u8 *mac) +{ + struct list_head *ptr; + struct mac_entry *entry; + int found = 0; + + if (mac_restrictions->policy == MAC_POLICY_OPEN) + return 0; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next; + ptr != &mac_restrictions->mac_list; ptr = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + + if (memcmp(entry->addr, mac, ETH_ALEN) == 0) { + found = 1; + break; + } + } + spin_unlock_bh(&mac_restrictions->lock); + + if (mac_restrictions->policy == MAC_POLICY_ALLOW) + return !found; + else + return found; +} + + +static void ap_control_flush_macs(struct mac_restrictions *mac_restrictions) +{ + struct list_head *ptr, *n; + struct mac_entry *entry; + + if (mac_restrictions->entries == 0) + return; + + spin_lock_bh(&mac_restrictions->lock); + for (ptr = mac_restrictions->mac_list.next, n = ptr->next; + ptr != &mac_restrictions->mac_list; + ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct mac_entry, list); + list_del(ptr); + kfree(entry); + } + mac_restrictions->entries = 0; + spin_unlock_bh(&mac_restrictions->lock); +} + + +static int ap_control_kick_mac(struct ap_data *ap, struct net_device *dev, + u8 *mac) +{ + struct sta_info *sta; + u16 resp; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, mac); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -EINVAL; + + resp = cpu_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID); + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH, + (char *) &resp, 2, sta->addr, 0); + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(dev, sta); + + ap_free_sta(ap, sta); + + return 0; +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static void ap_control_kickall(struct ap_data *ap) +{ + struct list_head *ptr, *n; + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next, n = ptr->next; ptr != &ap->sta_list; + ptr = n, n = ptr->next) { + sta = list_entry(ptr, struct sta_info, list); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +#define PROC_LIMIT (PAGE_SIZE - 80) + +static int prism2_ap_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct ap_data *ap = (struct ap_data *) data; + struct list_head *ptr; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "# BSSID CHAN SIGNAL NOISE RATE SSID FLAGS\n"); + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + if (!sta->ap) + continue; + + p += sprintf(p, MACSTR " %d %d %d %d '", MAC2STR(sta->addr), + sta->u.ap.channel, sta->last_rx_signal, + sta->last_rx_silence, sta->last_rx_rate); + for (i = 0; i < sta->u.ap.ssid_len; i++) + p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 && + sta->u.ap.ssid[i] < 127) ? + "%c" : "<%02x>"), + sta->u.ap.ssid[i]); + p += sprintf(p, "'"); + if (sta->capability & WLAN_CAPABILITY_ESS) + p += sprintf(p, " [ESS]"); + if (sta->capability & WLAN_CAPABILITY_IBSS) + p += sprintf(p, " [IBSS]"); + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + p += sprintf(p, " [WEP]"); + p += sprintf(p, "\n"); + + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "hostap: ap proc did not fit\n"); + break; + } + } + spin_unlock_bh(&ap->sta_table_lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver) +{ + if (!ap) + return; + + if (sta_fw_ver == PRISM2_FW_VER(0,8,0)) { + PDEBUG(DEBUG_AP, "Using data::nullfunc ACK workaround - " + "firmware upgrade recommended\n"); + ap->nullfunc_ack = 1; + } else + ap->nullfunc_ack = 0; + + if (sta_fw_ver == PRISM2_FW_VER(1,4,2)) { + printk(KERN_WARNING "%s: Warning: secondary station firmware " + "version 1.4.2 does not seem to work in Host AP mode\n", + ap->local->dev->name); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + u16 fc; + struct hostap_ieee80211_hdr *hdr; + + if (!ap->local->hostapd || !ap->local->apdev) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + /* Pass the TX callback frame to the hostapd; use 802.11 header version + * 1 to indicate failure (no ACK) and 2 success (frame ACKed) */ + + fc &= ~WLAN_FC_PVER; + fc |= ok ? BIT(1) : BIT(0); + hdr->frame_control = cpu_to_le16(fc); + + skb->dev = ap->local->apdev; + skb_pull(skb, hostap_80211_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_802_2); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_auth(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct hostap_ieee80211_hdr *hdr; + u16 fc, *pos, auth_alg, auth_transaction, status; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_AUTH || + skb->len < IEEE80211_MGMT_HDR_LEN + 6) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_auth received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = le16_to_cpu(*pos++); + auth_transaction = le16_to_cpu(*pos++); + status = le16_to_cpu(*pos++); + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + if (status == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { + txt = "STA authenticated"; + sta->flags |= WLAN_STA_AUTH; + sta->last_auth = jiffies; + } else if (status != WLAN_STATUS_SUCCESS) + txt = "authentication failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " auth_cb - alg=%d trans#=%d " + "status=%d - %s\n", + dev->name, MAC2STR(hdr->addr1), auth_alg, + auth_transaction, status, txt); + } + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_ap_tx_cb_assoc(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct net_device *dev = ap->local->dev; + struct hostap_ieee80211_hdr *hdr; + u16 fc, *pos, status; + struct sta_info *sta = NULL; + char *txt = NULL; + + if (ap->local->hostapd) { + dev_kfree_skb(skb); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ASSOC_RESP && + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_REASSOC_RESP) || + skb->len < IEEE80211_MGMT_HDR_LEN + 4) { + printk(KERN_DEBUG "%s: hostap_ap_tx_cb_assoc received invalid " + "frame\n", dev->name); + dev_kfree_skb(skb); + return; + } + + if (!ok) { + txt = "frame was not ACKed"; + goto done; + } + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&ap->sta_table_lock); + + if (!sta) { + txt = "STA not found"; + goto done; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + pos++; + status = le16_to_cpu(*pos++); + if (status == WLAN_STATUS_SUCCESS) { + if (!(sta->flags & WLAN_STA_ASSOC)) + hostap_event_new_sta(dev, sta); + txt = "STA associated"; + sta->flags |= WLAN_STA_ASSOC; + sta->last_assoc = jiffies; + } else + txt = "association failed"; + + done: + if (sta) + atomic_dec(&sta->users); + if (txt) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " assoc_cb - %s\n", + dev->name, MAC2STR(hdr->addr1), txt); + } + dev_kfree_skb(skb); +} + +/* Called only as a tasklet (software IRQ); TX callback for poll frames used + * in verifying whether the STA is still present. */ +static void hostap_ap_tx_cb_poll(struct sk_buff *skb, int ok, void *data) +{ + struct ap_data *ap = data; + struct hostap_ieee80211_hdr *hdr; + struct sta_info *sta; + + if (skb->len < 24) + goto fail; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (ok) { + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr1); + if (sta) + sta->flags &= ~WLAN_STA_PENDING_POLL; + spin_unlock(&ap->sta_table_lock); + } else { + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " did not ACK activity " + "poll frame\n", ap->local->dev->name, + MAC2STR(hdr->addr1)); + } + + fail: + dev_kfree_skb(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +void hostap_init_data(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + if (ap == NULL) { + printk(KERN_WARNING "hostap_init_data: ap == NULL\n"); + return; + } + memset(ap, 0, sizeof(struct ap_data)); + ap->local = local; + + ap->ap_policy = GET_INT_PARM(other_ap_policy, local->card_idx); + ap->bridge_packets = GET_INT_PARM(ap_bridge_packets, local->card_idx); + ap->max_inactivity = + GET_INT_PARM(ap_max_inactivity, local->card_idx) * HZ; + ap->autom_ap_wds = GET_INT_PARM(autom_ap_wds, local->card_idx); + + spin_lock_init(&ap->sta_table_lock); + INIT_LIST_HEAD(&ap->sta_list); + + /* Initialize task queue structure for AP management */ + INIT_WORK(&local->ap->add_sta_proc_queue, handle_add_proc_queue, ap); + + ap->tx_callback_idx = + hostap_tx_callback_register(local, hostap_ap_tx_cb, ap); + if (ap->tx_callback_idx == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + INIT_WORK(&local->ap->wds_oper_queue, handle_wds_oper_queue, local); + + ap->tx_callback_auth = + hostap_tx_callback_register(local, hostap_ap_tx_cb_auth, ap); + ap->tx_callback_assoc = + hostap_tx_callback_register(local, hostap_ap_tx_cb_assoc, ap); + ap->tx_callback_poll = + hostap_tx_callback_register(local, hostap_ap_tx_cb_poll, ap); + if (ap->tx_callback_auth == 0 || ap->tx_callback_assoc == 0 || + ap->tx_callback_poll == 0) + printk(KERN_WARNING "%s: failed to register TX callback for " + "AP\n", local->dev->name); + + spin_lock_init(&ap->mac_restrictions.lock); + INIT_LIST_HEAD(&ap->mac_restrictions.mac_list); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 1; +} + + +void hostap_init_ap_proc(local_info_t *local) +{ + struct ap_data *ap = local->ap; + + ap->proc = local->proc; + if (ap->proc == NULL) + return; + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("ap_debug", 0, ap->proc, + ap_debug_proc_read, ap); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + create_proc_read_entry("ap_control", 0, ap->proc, + ap_control_proc_read, ap); + create_proc_read_entry("ap", 0, ap->proc, + prism2_ap_proc_read, ap); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +} + + +void hostap_free_data(struct ap_data *ap) +{ + struct list_head *n, *ptr; + + if (ap == NULL || !ap->initialized) { + printk(KERN_DEBUG "hostap_free_data: ap has not yet been " + "initialized - skip resource freeing\n"); + return; + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (ap->crypt) + ap->crypt->deinit(ap->crypt_priv); + ap->crypt = ap->crypt_priv = NULL; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + list_for_each_safe(ptr, n, &ap->sta_list) { + struct sta_info *sta = list_entry(ptr, struct sta_info, list); + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (ap->proc != NULL) { + remove_proc_entry("ap_debug", ap->proc); + } +#endif /* PRISM2_NO_PROCFS_DEBUG */ + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (ap->proc != NULL) { + remove_proc_entry("ap", ap->proc); + remove_proc_entry("ap_control", ap->proc); + } + ap_control_flush_macs(&ap->mac_restrictions); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + ap->initialized = 0; +} + + +/* caller should have mutex for AP STA list handling */ +static struct sta_info* ap_get_sta(struct ap_data *ap, u8 *sta) +{ + struct sta_info *s; + + s = ap->sta_hash[STA_HASH(sta)]; + while (s != NULL && memcmp(s->addr, sta, ETH_ALEN) != 0) + s = s->hnext; + return s; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +/* Called from timer handler and from scheduled AP queue handlers */ +static void prism2_send_mgmt(struct net_device *dev, + int type, int subtype, char *body, + int body_len, u8 *addr, u16 tx_cb_idx) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_ieee80211_hdr *hdr; + u16 fc; + struct sk_buff *skb; + struct hostap_skb_tx_data *meta; + int hdrlen; + + iface = netdev_priv(dev); + local = iface->local; + dev = local->dev; /* always use master radio device */ + iface = netdev_priv(dev); + + if (!(dev->flags & IFF_UP)) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt - device is not UP - " + "cannot send frame\n", dev->name); + return; + } + + skb = dev_alloc_skb(sizeof(*hdr) + body_len); + if (skb == NULL) { + PDEBUG(DEBUG_AP, "%s: prism2_send_mgmt failed to allocate " + "skb\n", dev->name); + return; + } + + fc = (type << 2) | (subtype << 4); + hdrlen = hostap_80211_get_hdrlen(fc); + hdr = (struct hostap_ieee80211_hdr *) skb_put(skb, hdrlen); + if (body) + memcpy(skb_put(skb, body_len), body, body_len); + + memset(hdr, 0, hdrlen); + + /* FIX: ctrl::ack sending used special HFA384X_TX_CTRL_802_11 + * tx_control instead of using local->tx_control */ + + + memcpy(hdr->addr1, addr, ETH_ALEN); /* DA / RA */ + if (type == WLAN_FC_TYPE_DATA) { + fc |= WLAN_FC_FROMDS; + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* BSSID */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* SA */ + } else if (type == WLAN_FC_TYPE_CTRL) { + /* control:ACK does not have addr2 or addr3 */ + memset(hdr->addr2, 0, ETH_ALEN); + memset(hdr->addr3, 0, ETH_ALEN); + } else { + memcpy(hdr->addr2, dev->dev_addr, ETH_ALEN); /* SA */ + memcpy(hdr->addr3, dev->dev_addr, ETH_ALEN); /* BSSID */ + } + + hdr->frame_control = cpu_to_le16(fc); + + meta = (struct hostap_skb_tx_data *) skb->cb; + memset(meta, 0, sizeof(*meta)); + meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; + meta->iface = iface; + meta->tx_cb_idx = tx_cb_idx; + + skb->dev = dev; + skb->mac.raw = skb->nh.raw = skb->data; + dev_queue_xmit(skb); +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +static int prism2_sta_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + struct sta_info *sta = (struct sta_info *) data; + int i; + + /* FIX: possible race condition.. the STA data could have just expired, + * but proc entry was still here so that the read could have started; + * some locking should be done here.. */ + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "%s=" MACSTR "\nusers=%d\naid=%d\n" + "flags=0x%04x%s%s%s%s%s%s%s\n" + "capability=0x%02x\nlisten_interval=%d\nsupported_rates=", + sta->ap ? "AP" : "STA", + MAC2STR(sta->addr), atomic_read(&sta->users), sta->aid, + sta->flags, + sta->flags & WLAN_STA_AUTH ? " AUTH" : "", + sta->flags & WLAN_STA_ASSOC ? " ASSOC" : "", + sta->flags & WLAN_STA_PS ? " PS" : "", + sta->flags & WLAN_STA_TIM ? " TIM" : "", + sta->flags & WLAN_STA_PERM ? " PERM" : "", + sta->flags & WLAN_STA_AUTHORIZED ? " AUTHORIZED" : "", + sta->flags & WLAN_STA_PENDING_POLL ? " POLL" : "", + sta->capability, sta->listen_interval); + /* supported_rates: 500 kbit/s units with msb ignored */ + for (i = 0; i < sizeof(sta->supported_rates); i++) + if (sta->supported_rates[i] != 0) + p += sprintf(p, "%d%sMbps ", + (sta->supported_rates[i] & 0x7f) / 2, + sta->supported_rates[i] & 1 ? ".5" : ""); + p += sprintf(p, "\njiffies=%lu\nlast_auth=%lu\nlast_assoc=%lu\n" + "last_rx=%lu\nlast_tx=%lu\nrx_packets=%lu\n" + "tx_packets=%lu\n" + "rx_bytes=%lu\ntx_bytes=%lu\nbuffer_count=%d\n" + "last_rx: silence=%d dBm signal=%d dBm rate=%d%s Mbps\n" + "tx_rate=%d\ntx[1M]=%d\ntx[2M]=%d\ntx[5.5M]=%d\n" + "tx[11M]=%d\n" + "rx[1M]=%d\nrx[2M]=%d\nrx[5.5M]=%d\nrx[11M]=%d\n", + jiffies, sta->last_auth, sta->last_assoc, sta->last_rx, + sta->last_tx, + sta->rx_packets, sta->tx_packets, sta->rx_bytes, + sta->tx_bytes, skb_queue_len(&sta->tx_buf), + sta->last_rx_silence, + sta->last_rx_signal, sta->last_rx_rate / 10, + sta->last_rx_rate % 10 ? ".5" : "", + sta->tx_rate, sta->tx_count[0], sta->tx_count[1], + sta->tx_count[2], sta->tx_count[3], sta->rx_count[0], + sta->rx_count[1], sta->rx_count[2], sta->rx_count[3]); + if (sta->crypt && sta->crypt->ops && sta->crypt->ops->print_stats) + p = sta->crypt->ops->print_stats(p, sta->crypt->priv); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + if (sta->u.ap.channel >= 0) + p += sprintf(p, "channel=%d\n", sta->u.ap.channel); + p += sprintf(p, "ssid="); + for (i = 0; i < sta->u.ap.ssid_len; i++) + p += sprintf(p, ((sta->u.ap.ssid[i] >= 32 && + sta->u.ap.ssid[i] < 127) ? + "%c" : "<%02x>"), + sta->u.ap.ssid[i]); + p += sprintf(p, "\n"); + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return (p - page); +} + + +static void handle_add_proc_queue(void *data) +{ + struct ap_data *ap = (struct ap_data *) data; + struct sta_info *sta; + char name[20]; + struct add_sta_proc_data *entry, *prev; + + entry = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = NULL; + + while (entry) { + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, entry->addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta) { + sprintf(name, MACSTR, MAC2STR(sta->addr)); + sta->proc = create_proc_read_entry( + name, 0, ap->proc, + prism2_sta_proc_read, sta); + + atomic_dec(&sta->users); + } + + prev = entry; + entry = entry->next; + kfree(prev); + } +} + + +static struct sta_info * ap_add_sta(struct ap_data *ap, u8 *addr) +{ + struct sta_info *sta; + + sta = (struct sta_info *) + kmalloc(sizeof(struct sta_info), GFP_ATOMIC); + if (sta == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed\n"); + return NULL; + } + + /* initialize STA info data */ + memset(sta, 0, sizeof(struct sta_info)); + sta->local = ap->local; + skb_queue_head_init(&sta->tx_buf); + memcpy(sta->addr, addr, ETH_ALEN); + + atomic_inc(&sta->users); + spin_lock_bh(&ap->sta_table_lock); + list_add(&sta->list, &ap->sta_list); + ap->num_sta++; + ap_sta_hash_add(ap, sta); + spin_unlock_bh(&ap->sta_table_lock); + + if (ap->proc) { + struct add_sta_proc_data *entry; + /* schedule a non-interrupt context process to add a procfs + * entry for the STA since procfs code use GFP_KERNEL */ + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry) { + memcpy(entry->addr, sta->addr, ETH_ALEN); + entry->next = ap->add_sta_proc_entries; + ap->add_sta_proc_entries = entry; + schedule_work(&ap->add_sta_proc_queue); + } else + printk(KERN_DEBUG "Failed to add STA proc data\n"); + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + init_timer(&sta->timer); + sta->timer.expires = jiffies + ap->max_inactivity; + sta->timer.data = (unsigned long) sta; + sta->timer.function = ap_handle_timer; + if (!ap->local->hostapd) + add_timer(&sta->timer); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + return sta; +} + + +static int ap_tx_rate_ok(int rateidx, struct sta_info *sta, + local_info_t *local) +{ + if (rateidx > sta->tx_max_rate || + !(sta->tx_supp_rates & (1 << rateidx))) + return 0; + + if (local->tx_rate_control != 0 && + !(local->tx_rate_control & (1 << rateidx))) + return 0; + + return 1; +} + + +static void prism2_check_tx_rates(struct sta_info *sta) +{ + int i; + + sta->tx_supp_rates = 0; + for (i = 0; i < sizeof(sta->supported_rates); i++) { + if ((sta->supported_rates[i] & 0x7f) == 2) + sta->tx_supp_rates |= WLAN_RATE_1M; + if ((sta->supported_rates[i] & 0x7f) == 4) + sta->tx_supp_rates |= WLAN_RATE_2M; + if ((sta->supported_rates[i] & 0x7f) == 11) + sta->tx_supp_rates |= WLAN_RATE_5M5; + if ((sta->supported_rates[i] & 0x7f) == 22) + sta->tx_supp_rates |= WLAN_RATE_11M; + } + sta->tx_max_rate = sta->tx_rate = sta->tx_rate_idx = 0; + if (sta->tx_supp_rates & WLAN_RATE_1M) { + sta->tx_max_rate = 0; + if (ap_tx_rate_ok(0, sta, sta->local)) { + sta->tx_rate = 10; + sta->tx_rate_idx = 0; + } + } + if (sta->tx_supp_rates & WLAN_RATE_2M) { + sta->tx_max_rate = 1; + if (ap_tx_rate_ok(1, sta, sta->local)) { + sta->tx_rate = 20; + sta->tx_rate_idx = 1; + } + } + if (sta->tx_supp_rates & WLAN_RATE_5M5) { + sta->tx_max_rate = 2; + if (ap_tx_rate_ok(2, sta, sta->local)) { + sta->tx_rate = 55; + sta->tx_rate_idx = 2; + } + } + if (sta->tx_supp_rates & WLAN_RATE_11M) { + sta->tx_max_rate = 3; + if (ap_tx_rate_ok(3, sta, sta->local)) { + sta->tx_rate = 110; + sta->tx_rate_idx = 3; + } + } +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void ap_crypt_init(struct ap_data *ap) +{ + ap->crypt = hostap_get_crypto_ops("WEP"); + + if (ap->crypt) { + if (ap->crypt->init) { + ap->crypt_priv = ap->crypt->init(0); + if (ap->crypt_priv == NULL) + ap->crypt = NULL; + else { + u8 key[WEP_KEY_LEN]; + get_random_bytes(key, WEP_KEY_LEN); + ap->crypt->set_key(key, WEP_KEY_LEN, NULL, + ap->crypt_priv); + } + } + } + + if (ap->crypt == NULL) { + printk(KERN_WARNING "AP could not initialize WEP: load module " + "hostap_crypt_wep.o\n"); + } +} + + +/* Generate challenge data for shared key authentication. IEEE 802.11 specifies + * that WEP algorithm is used for generating challange. This should be unique, + * but otherwise there is not really need for randomness etc. Initialize WEP + * with pseudo random key and then use increasing IV to get unique challenge + * streams. + * + * Called only as a scheduled task for pending AP frames. + */ +static char * ap_auth_make_challenge(struct ap_data *ap) +{ + char *tmpbuf; + struct sk_buff *skb; + + if (ap->crypt == NULL) { + ap_crypt_init(ap); + if (ap->crypt == NULL) + return NULL; + } + + tmpbuf = (char *) kmalloc(WLAN_AUTH_CHALLENGE_LEN, GFP_ATOMIC); + if (tmpbuf == NULL) { + PDEBUG(DEBUG_AP, "AP: kmalloc failed for challenge\n"); + return NULL; + } + + skb = dev_alloc_skb(WLAN_AUTH_CHALLENGE_LEN + + ap->crypt->extra_prefix_len + + ap->crypt->extra_postfix_len); + if (skb == NULL) { + kfree(tmpbuf); + return NULL; + } + + skb_reserve(skb, ap->crypt->extra_prefix_len); + memset(skb_put(skb, WLAN_AUTH_CHALLENGE_LEN), 0, + WLAN_AUTH_CHALLENGE_LEN); + if (ap->crypt->encrypt_mpdu(skb, 0, ap->crypt_priv)) { + dev_kfree_skb(skb); + kfree(tmpbuf); + return NULL; + } + + memcpy(tmpbuf, skb->data + ap->crypt->extra_prefix_len, + WLAN_AUTH_CHALLENGE_LEN); + dev_kfree_skb(skb); + + return tmpbuf; +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_authen(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + size_t hdrlen; + struct ap_data *ap = local->ap; + char body[8 + WLAN_AUTH_CHALLENGE_LEN], *challenge = NULL; + int len, olen; + u16 auth_alg, auth_transaction, status_code, *pos; + u16 resp = WLAN_STATUS_SUCCESS, fc; + struct sta_info *sta = NULL; + struct prism2_crypt_data *crypt; + char *txt = ""; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + fc = le16_to_cpu(hdr->frame_control); + hdrlen = hostap_80211_get_hdrlen(fc); + + if (len < 6) { + PDEBUG(DEBUG_AP, "%s: handle_authen - too short payload " + "(len=%d) from " MACSTR "\n", dev->name, len, + MAC2STR(hdr->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta && sta->crypt) + crypt = sta->crypt; + else { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = local->crypt[idx]; + } + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + auth_alg = __le16_to_cpu(*pos); + pos++; + auth_transaction = __le16_to_cpu(*pos); + pos++; + status_code = __le16_to_cpu(*pos); + pos++; + + if (memcmp(dev->dev_addr, hdr->addr2, ETH_ALEN) == 0 || + ap_control_mac_deny(&ap->mac_restrictions, hdr->addr2)) { + txt = "authentication denied"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (((local->auth_algs & PRISM2_AUTH_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || + ((local->auth_algs & PRISM2_AUTH_SHARED_KEY) && + crypt && auth_alg == WLAN_AUTH_SHARED_KEY)) { + } else { + txt = "unsupported algorithm"; + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (len >= 8) { + u8 *u = (u8 *) pos; + if (*u == WLAN_EID_CHALLENGE) { + if (*(u + 1) != WLAN_AUTH_CHALLENGE_LEN) { + txt = "invalid challenge len"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + if (len - 8 < WLAN_AUTH_CHALLENGE_LEN) { + txt = "challenge underflow"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + challenge = (char *) (u + 2); + } + } + + if (sta && sta->ap) { + if (time_after(jiffies, sta->u.ap.last_beacon + + (10 * sta->listen_interval * HZ) / 1024)) { + PDEBUG(DEBUG_AP, "%s: no beacons received for a while," + " assuming AP " MACSTR " is now STA\n", + dev->name, MAC2STR(sta->addr)); + sta->ap = 0; + sta->flags = 0; + sta->u.sta.challenge = NULL; + } else { + txt = "AP trying to authenticate?"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + if ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 1) || + (auth_alg == WLAN_AUTH_SHARED_KEY && + (auth_transaction == 1 || + (auth_transaction == 3 && sta != NULL && + sta->u.sta.challenge != NULL)))) { + } else { + txt = "unknown authentication transaction number"; + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (sta == NULL) { + txt = "new STA"; + + if (local->ap->num_sta >= MAX_STA_COUNT) { + /* FIX: might try to remove some old STAs first? */ + txt = "no more room for new STAs"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + txt = "ap_add_sta failed"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + txt = "authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + break; + + case WLAN_AUTH_SHARED_KEY: + if (auth_transaction == 1) { + if (sta->u.sta.challenge == NULL) { + sta->u.sta.challenge = + ap_auth_make_challenge(local->ap); + if (sta->u.sta.challenge == NULL) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + } + } else { + if (sta->u.sta.challenge == NULL || + challenge == NULL || + memcmp(sta->u.sta.challenge, challenge, + WLAN_AUTH_CHALLENGE_LEN) != 0 || + !(fc & WLAN_FC_ISWEP)) { + txt = "challenge response incorrect"; + resp = WLAN_STATUS_CHALLENGE_FAIL; + goto fail; + } + + txt = "challenge OK - authOK"; + /* IEEE 802.11 standard is not completely clear about + * whether STA is considered authenticated after + * authentication OK frame has been send or after it + * has been ACKed. In order to reduce interoperability + * issues, mark the STA authenticated before ACK. */ + sta->flags |= WLAN_STA_AUTH; + kfree(sta->u.sta.challenge); + sta->u.sta.challenge = NULL; + } + break; + } + + fail: + pos = (u16 *) body; + *pos = cpu_to_le16(auth_alg); + pos++; + *pos = cpu_to_le16(auth_transaction + 1); + pos++; + *pos = cpu_to_le16(resp); /* status_code */ + pos++; + olen = 6; + + if (resp == WLAN_STATUS_SUCCESS && sta != NULL && + sta->u.sta.challenge != NULL && + auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 1) { + u8 *tmp = (u8 *) pos; + *tmp++ = WLAN_EID_CHALLENGE; + *tmp++ = WLAN_AUTH_CHALLENGE_LEN; + pos++; + memcpy(pos, sta->u.sta.challenge, WLAN_AUTH_CHALLENGE_LEN); + olen += 2 + WLAN_AUTH_CHALLENGE_LEN; + } + + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_AUTH, + body, olen, hdr->addr2, ap->tx_callback_auth); + + if (sta) { + sta->last_rx = jiffies; + atomic_dec(&sta->users); + } + + if (resp) { + PDEBUG(DEBUG_AP, "%s: " MACSTR " auth (alg=%d trans#=%d " + "stat=%d len=%d fc=%04x) ==> %d (%s)\n", + dev->name, MAC2STR(hdr->addr2), auth_alg, + auth_transaction, status_code, len, fc, resp, txt); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_assoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, int reassoc) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char body[12], *p, *lpos; + int len, left; + u16 *pos; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + int send_deauth = 0; + char *txt = ""; + u8 prev_ap[ETH_ALEN]; + + left = len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < (reassoc ? 10 : 4)) { + PDEBUG(DEBUG_AP, "%s: handle_assoc - too short payload " + "(len=%d, reassoc=%d) from " MACSTR "\n", + dev->name, len, reassoc, MAC2STR(hdr->addr2)); + return; + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "trying to associate before authentication"; + send_deauth = 1; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + sta = NULL; /* do not decrement sta->users */ + goto fail; + } + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + pos = (u16 *) (skb->data + IEEE80211_MGMT_HDR_LEN); + sta->capability = __le16_to_cpu(*pos); + pos++; left -= 2; + sta->listen_interval = __le16_to_cpu(*pos); + pos++; left -= 2; + + if (reassoc) { + memcpy(prev_ap, pos, ETH_ALEN); + pos++; pos++; pos++; left -= 6; + } else + memset(prev_ap, 0, ETH_ALEN); + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + txt = "SSID overflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0) { + txt = "not our SSID"; + resp = WLAN_STATUS_ASSOC_DENIED_UNSPEC; + goto fail; + } + + u += ileft; + left -= ileft; + } + + if (left >= 2 && *u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || + ileft > WLAN_SUPP_RATES_MAX) { + txt = "SUPP_RATES len error"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + memset(sta->supported_rates, 0, + sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, u, ileft); + prism2_check_tx_rates(sta); + + u += ileft; + left -= ileft; + } + + if (left > 0) { + PDEBUG(DEBUG_AP, "%s: assoc from " MACSTR " with extra" + " data (%d bytes) [", + dev->name, MAC2STR(hdr->addr2), left); + while (left > 0) { + PDEBUG2(DEBUG_AP, "<%02x>", *u); + u++; left--; + } + PDEBUG2(DEBUG_AP, "]\n"); + } + } else { + txt = "frame underflow"; + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + /* get a unique AID */ + if (sta->aid > 0) + txt = "OK, old AID"; + else { + spin_lock_bh(&local->ap->sta_table_lock); + for (sta->aid = 1; sta->aid <= MAX_AID_TABLE_SIZE; sta->aid++) + if (local->ap->sta_aid[sta->aid - 1] == NULL) + break; + if (sta->aid > MAX_AID_TABLE_SIZE) { + sta->aid = 0; + spin_unlock_bh(&local->ap->sta_table_lock); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + txt = "no room for more AIDs"; + } else { + local->ap->sta_aid[sta->aid - 1] = sta; + spin_unlock_bh(&local->ap->sta_table_lock); + txt = "OK, new AID"; + } + } + + fail: + pos = (u16 *) body; + + if (send_deauth) { + *pos = __constant_cpu_to_le16( + WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH); + pos++; + } else { + /* FIX: CF-Pollable and CF-PollReq should be set to match the + * values in beacons/probe responses */ + /* FIX: how about privacy and WEP? */ + /* capability */ + *pos = __constant_cpu_to_le16(WLAN_CAPABILITY_ESS); + pos++; + + /* status_code */ + *pos = __cpu_to_le16(resp); + pos++; + + *pos = __cpu_to_le16((sta && sta->aid > 0 ? sta->aid : 0) | + BIT(14) | BIT(15)); /* AID */ + pos++; + + /* Supported rates (Information element) */ + p = (char *) pos; + *p++ = WLAN_EID_SUPP_RATES; + lpos = p; + *p++ = 0; /* len */ + if (local->tx_rate_control & WLAN_RATE_1M) { + *p++ = local->basic_rates & WLAN_RATE_1M ? 0x82 : 0x02; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_2M) { + *p++ = local->basic_rates & WLAN_RATE_2M ? 0x84 : 0x04; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_5M5) { + *p++ = local->basic_rates & WLAN_RATE_5M5 ? + 0x8b : 0x0b; + (*lpos)++; + } + if (local->tx_rate_control & WLAN_RATE_11M) { + *p++ = local->basic_rates & WLAN_RATE_11M ? + 0x96 : 0x16; + (*lpos)++; + } + pos = (u16 *) p; + } + + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, + (send_deauth ? WLAN_FC_STYPE_DEAUTH : + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP)), + body, (u8 *) pos - (u8 *) body, + hdr->addr2, + send_deauth ? 0 : local->ap->tx_callback_assoc); + + if (sta) { + if (resp == WLAN_STATUS_SUCCESS) { + sta->last_rx = jiffies; + /* STA will be marked associated from TX callback, if + * AssocResp is ACKed */ + } + atomic_dec(&sta->users); + } + +#if 0 + PDEBUG(DEBUG_AP, "%s: " MACSTR " %sassoc (len=%d prev_ap=" MACSTR + ") => %d(%d) (%s)\n", + dev->name, MAC2STR(hdr->addr2), reassoc ? "re" : "", len, + MAC2STR(prev_ap), resp, send_deauth, txt); +#endif +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_deauth(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = (char *) (skb->data + IEEE80211_MGMT_HDR_LEN); + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_deauth - too short payload (len=%d)\n", len); + return; + } + + pos = (u16 *) body; + reason_code = __le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: deauthentication: " MACSTR " len=%d, " + "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: deauthentication from " MACSTR ", " + "reason_code=%d, but STA not authenticated\n", dev->name, + MAC2STR(hdr->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_disassoc(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len; + u16 reason_code, *pos; + struct sta_info *sta = NULL; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 2) { + printk("handle_disassoc - too short payload (len=%d)\n", len); + return; + } + + pos = (u16 *) body; + reason_code = __le16_to_cpu(*pos); + + PDEBUG(DEBUG_AP, "%s: disassociation: " MACSTR " len=%d, " + "reason_code=%d\n", dev->name, MAC2STR(hdr->addr2), len, + reason_code); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) { + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap) + hostap_event_expired_sta(local->dev, sta); + sta->flags &= ~WLAN_STA_ASSOC; + } + spin_unlock_bh(&local->ap->sta_table_lock); + if (sta == NULL) { + printk("%s: disassociation from " MACSTR ", " + "reason_code=%d, but STA not authenticated\n", + dev->name, MAC2STR(hdr->addr2), reason_code); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_data_nullfunc(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + + /* some STA f/w's seem to require control::ACK frame for + * data::nullfunc, but at least Prism2 station f/w version 0.8.0 does + * not send this.. + * send control::ACK for the data::nullfunc */ + + printk(KERN_DEBUG "Sending control::ACK for data::nullfunc\n"); + prism2_send_mgmt(dev, WLAN_FC_TYPE_CTRL, WLAN_FC_STYPE_ACK, + NULL, 0, hdr->addr2, 0); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void ap_handle_dropped_data(local_info_t *local, + struct hostap_ieee80211_hdr *hdr) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + u16 reason; + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC)) { + PDEBUG(DEBUG_AP, "ap_handle_dropped_data: STA is now okay?\n"); + atomic_dec(&sta->users); + return; + } + + reason = __constant_cpu_to_le16( + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + prism2_send_mgmt(dev, WLAN_FC_TYPE_MGMT, + ((sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) ? + WLAN_FC_STYPE_DEAUTH : WLAN_FC_STYPE_DISASSOC), + (char *) &reason, sizeof(reason), hdr->addr2, 0); + + if (sta) + atomic_dec(&sta->users); +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a scheduled task for pending AP frames. */ +static void pspoll_send_buffered(local_info_t *local, struct sta_info *sta, + struct sk_buff *skb) +{ + if (!(sta->flags & WLAN_STA_PS)) { + /* Station has moved to non-PS mode, so send all buffered + * frames using normal device queue. */ + dev_queue_xmit(skb); + return; + } + + /* add a flag for hostap_handle_sta_tx() to know that this skb should + * be passed through even though STA is using PS */ + memcpy(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN); + skb->cb[AP_SKB_CB_MAGIC_LEN] = AP_SKB_CB_BUFFERED_FRAME; + if (!skb_queue_empty(&sta->tx_buf)) { + /* indicate to STA that more frames follow */ + skb->cb[AP_SKB_CB_MAGIC_LEN] |= AP_SKB_CB_ADD_MOREDATA; + } + dev_queue_xmit(skb); +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_pspoll(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct net_device *dev = local->dev; + struct sta_info *sta; + u16 aid; + struct sk_buff *skb; + + PDEBUG(DEBUG_PS2, "handle_pspoll: BSSID=" MACSTR ", TA=" MACSTR + " PWRMGT=%d\n", + MAC2STR(hdr->addr1), MAC2STR(hdr->addr2), + !!(le16_to_cpu(hdr->frame_control) & WLAN_FC_PWRMGT)); + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_pspoll - addr1(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr1)); + return; + } + + aid = __le16_to_cpu(hdr->duration_id); + if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14))) { + PDEBUG(DEBUG_PS, " PSPOLL and AID[15:14] not set\n"); + return; + } + aid &= ~BIT(15) & ~BIT(14); + if (aid == 0 || aid > MAX_AID_TABLE_SIZE) { + PDEBUG(DEBUG_PS, " invalid aid=%d\n", aid); + return; + } + PDEBUG(DEBUG_PS2, " aid=%d\n", aid); + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + PDEBUG(DEBUG_PS, " STA not found\n"); + return; + } + if (sta->aid != aid) { + PDEBUG(DEBUG_PS, " received aid=%i does not match with " + "assoc.aid=%d\n", aid, sta->aid); + return; + } + + /* FIX: todo: + * - add timeout for buffering (clear aid in TIM vector if buffer timed + * out (expiry time must be longer than ListenInterval for + * the corresponding STA; "8802-11: 11.2.1.9 AP aging function" + * - what to do, if buffered, pspolled, and sent frame is not ACKed by + * sta; store buffer for later use and leave TIM aid bit set? use + * TX event to check whether frame was ACKed? + */ + + while ((skb = skb_dequeue(&sta->tx_buf)) != NULL) { + /* send buffered frame .. */ + PDEBUG(DEBUG_PS2, "Sending buffered frame to STA after PS POLL" + " (buffer_count=%d)\n", skb_queue_len(&sta->tx_buf)); + + pspoll_send_buffered(local, sta, skb); + + if (sta->flags & WLAN_STA_PS) { + /* send only one buffered packet per PS Poll */ + /* FIX: should ignore further PS Polls until the + * buffered packet that was just sent is acknowledged + * (Tx or TxExc event) */ + break; + } + } + + if (skb_queue_empty(&sta->tx_buf)) { + /* try to clear aid from TIM */ + if (!(sta->flags & WLAN_STA_TIM)) + PDEBUG(DEBUG_PS2, "Re-unsetting TIM for aid %d\n", + aid); + hostap_set_tim(local, aid, 0); + sta->flags &= ~WLAN_STA_TIM; + } + + atomic_dec(&sta->users); +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + +static void handle_wds_oper_queue(void *data) +{ + local_info_t *local = data; + struct wds_oper_data *entry, *prev; + + spin_lock_bh(&local->lock); + entry = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = NULL; + spin_unlock_bh(&local->lock); + + while (entry) { + PDEBUG(DEBUG_AP, "%s: %s automatic WDS connection " + "to AP " MACSTR "\n", + local->dev->name, + entry->type == WDS_ADD ? "adding" : "removing", + MAC2STR(entry->addr)); + if (entry->type == WDS_ADD) + prism2_wds_add(local, entry->addr, 0); + else if (entry->type == WDS_DEL) + prism2_wds_del(local, entry->addr, 0, 1); + + prev = entry; + entry = entry->next; + kfree(prev); + } +} + + +/* Called only as a scheduled task for pending AP frames. */ +static void handle_beacon(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_ieee80211_hdr *hdr = + (struct hostap_ieee80211_hdr *) skb->data; + char *body = skb->data + IEEE80211_MGMT_HDR_LEN; + int len, left; + u16 *pos, beacon_int, capability; + char *ssid = NULL; + unsigned char *supp_rates = NULL; + int ssid_len = 0, supp_rates_len = 0; + struct sta_info *sta = NULL; + int new_sta = 0, channel = -1; + + len = skb->len - IEEE80211_MGMT_HDR_LEN; + + if (len < 8 + 2 + 2) { + printk(KERN_DEBUG "handle_beacon - too short payload " + "(len=%d)\n", len); + return; + } + + pos = (u16 *) body; + left = len; + + /* Timestamp (8 octets) */ + pos += 4; left -= 8; + /* Beacon interval (2 octets) */ + beacon_int = __le16_to_cpu(*pos); + pos++; left -= 2; + /* Capability information (2 octets) */ + capability = __le16_to_cpu(*pos); + pos++; left -= 2; + + if (local->ap->ap_policy != AP_OTHER_AP_EVEN_IBSS && + capability & WLAN_CAPABILITY_IBSS) + return; + + if (left >= 2) { + unsigned int ileft; + unsigned char *u = (unsigned char *) pos; + + if (*u == WLAN_EID_SSID) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft > MAX_SSID_LEN) { + PDEBUG(DEBUG_AP, "SSID: overflow\n"); + return; + } + + if (local->ap->ap_policy == AP_OTHER_AP_SAME_SSID && + (ileft != strlen(local->essid) || + memcmp(local->essid, u, ileft) != 0)) { + /* not our SSID */ + return; + } + + ssid = u; + ssid_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_SUPP_RATES) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft == 0 || ileft > 8) { + PDEBUG(DEBUG_AP, " - SUPP_RATES len error\n"); + return; + } + + supp_rates = u; + supp_rates_len = ileft; + + u += ileft; + left -= ileft; + } + + if (*u == WLAN_EID_DS_PARAMS) { + u++; left--; + ileft = *u; + u++; left--; + + if (ileft > left || ileft != 1) { + PDEBUG(DEBUG_AP, " - DS_PARAMS len error\n"); + return; + } + + channel = *u; + + u += ileft; + left -= ileft; + } + } + + spin_lock_bh(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta != NULL) + atomic_inc(&sta->users); + spin_unlock_bh(&local->ap->sta_table_lock); + + if (sta == NULL) { + /* add new AP */ + new_sta = 1; + sta = ap_add_sta(local->ap, hdr->addr2); + if (sta == NULL) { + printk(KERN_INFO "prism2: kmalloc failed for AP " + "data structure\n"); + return; + } + hostap_event_new_sta(local->dev, sta); + + /* mark APs authentication and associated for pseudo ad-hoc + * style communication */ + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + + if (local->ap->autom_ap_wds) { + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + } + + sta->ap = 1; + if (ssid) { + sta->u.ap.ssid_len = ssid_len; + memcpy(sta->u.ap.ssid, ssid, ssid_len); + sta->u.ap.ssid[ssid_len] = '\0'; + } else { + sta->u.ap.ssid_len = 0; + sta->u.ap.ssid[0] = '\0'; + } + sta->u.ap.channel = channel; + sta->rx_packets++; + sta->rx_bytes += len; + sta->u.ap.last_beacon = sta->last_rx = jiffies; + sta->capability = capability; + sta->listen_interval = beacon_int; + + atomic_dec(&sta->users); + + if (new_sta) { + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, supp_rates, supp_rates_len); + prism2_check_tx_rates(sta); + } +} + +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +/* Called only as a tasklet. */ +static void handle_ap_item(local_info_t *local, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + struct net_device *dev = local->dev; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + u16 fc, type, stype; + struct hostap_ieee80211_hdr *hdr; + + /* FIX: should give skb->len to handler functions and check that the + * buffer is long enough */ + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (!local->hostapd && type == WLAN_FC_TYPE_DATA) { + PDEBUG(DEBUG_AP, "handle_ap_item - data frame\n"); + + if (!(fc & WLAN_FC_TODS) || (fc & WLAN_FC_FROMDS)) { + if (stype == WLAN_FC_STYPE_NULLFUNC) { + /* no ToDS nullfunc seems to be used to check + * AP association; so send reject message to + * speed up re-association */ + ap_handle_dropped_data(local, hdr); + goto done; + } + PDEBUG(DEBUG_AP, " not ToDS frame (fc=0x%04x)\n", + fc); + goto done; + } + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(BSSID)=" + MACSTR " not own MAC\n", + MAC2STR(hdr->addr1)); + goto done; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC) + ap_handle_data_nullfunc(local, hdr); + else + ap_handle_dropped_data(local, hdr); + goto done; + } + + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(local, skb, rx_stats); + goto done; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL) { + handle_pspoll(local, hdr, rx_stats); + goto done; + } + + if (local->hostapd) { + PDEBUG(DEBUG_AP, "Unknown frame in AP queue: type=0x%02x " + "subtype=0x%02x\n", type, stype); + goto done; + } + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (type != WLAN_FC_TYPE_MGMT) { + PDEBUG(DEBUG_AP, "handle_ap_item - not a management frame?\n"); + goto done; + } + + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr1(DA)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr1)); + goto done; + } + + if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN)) { + PDEBUG(DEBUG_AP, "handle_ap_item - addr3(BSSID)=" MACSTR + " not own MAC\n", MAC2STR(hdr->addr3)); + goto done; + } + + switch (stype) { + case WLAN_FC_STYPE_ASSOC_REQ: + handle_assoc(local, skb, rx_stats, 0); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + PDEBUG(DEBUG_AP, "==> ASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + handle_assoc(local, skb, rx_stats, 1); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + PDEBUG(DEBUG_AP, "==> REASSOC RESP (ignored)\n"); + break; + case WLAN_FC_STYPE_ATIM: + PDEBUG(DEBUG_AP, "==> ATIM (ignored)\n"); + break; + case WLAN_FC_STYPE_DISASSOC: + handle_disassoc(local, skb, rx_stats); + break; + case WLAN_FC_STYPE_AUTH: + handle_authen(local, skb, rx_stats); + break; + case WLAN_FC_STYPE_DEAUTH: + handle_deauth(local, skb, rx_stats); + break; + default: + PDEBUG(DEBUG_AP, "Unknown mgmt frame subtype 0x%02x\n", stype); + break; + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + done: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 fc; + struct hostap_ieee80211_hdr *hdr; + + iface = netdev_priv(dev); + local = iface->local; + + if (skb->len < 16) + goto drop; + + local->stats.rx_packets++; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_control); + + if (local->ap->ap_policy == AP_OTHER_AP_SKIP_ALL && + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + goto drop; + + skb->protocol = __constant_htons(ETH_P_HOSTAP); + handle_ap_item(local, skb, rx_stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void schedule_packet_send(local_info_t *local, struct sta_info *sta) +{ + struct sk_buff *skb; + struct hostap_ieee80211_hdr *hdr; + struct hostap_80211_rx_status rx_stats; + + if (skb_queue_empty(&sta->tx_buf)) + return; + + skb = dev_alloc_skb(16); + if (skb == NULL) { + printk(KERN_DEBUG "%s: schedule_packet_send: skb alloc " + "failed\n", local->dev->name); + return; + } + + hdr = (struct hostap_ieee80211_hdr *) skb_put(skb, 16); + + /* Generate a fake pspoll frame to start packet delivery */ + hdr->frame_control = __constant_cpu_to_le16( + (WLAN_FC_TYPE_CTRL << 2) | (WLAN_FC_STYPE_PSPOLL << 4)); + memcpy(hdr->addr1, local->dev->dev_addr, ETH_ALEN); + memcpy(hdr->addr2, sta->addr, ETH_ALEN); + hdr->duration_id = cpu_to_le16(sta->aid | BIT(15) | BIT(14)); + + PDEBUG(DEBUG_PS2, "%s: Scheduling buffered packet delivery for " + "STA " MACSTR "\n", local->dev->name, MAC2STR(sta->addr)); + + skb->dev = local->dev; + + memset(&rx_stats, 0, sizeof(rx_stats)); + hostap_rx(local->dev, skb, &rx_stats); +} + + +static int prism2_ap_get_sta_qual(local_info_t *local, struct sockaddr addr[], + struct iw_quality qual[], int buf_size, + int aplist) +{ + struct ap_data *ap = local->ap; + struct list_head *ptr; + int count = 0; + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + if (aplist && !sta->ap) + continue; + addr[count].sa_family = ARPHRD_ETHER; + memcpy(addr[count].sa_data, sta->addr, ETH_ALEN); + if (sta->last_rx_silence == 0) + qual[count].qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + qual[count].qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + qual[count].level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + qual[count].noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + qual[count].updated = sta->last_rx_updated; + + sta->last_rx_updated = 0; + + count++; + if (count >= buf_size) + break; + } + spin_unlock_bh(&ap->sta_table_lock); + + return count; +} + + +/* Translate our list of Access Points & Stations to a card independant + * format that the Wireless Tools will understand - Jean II */ +static int prism2_ap_translate_scan(struct net_device *dev, char *buffer) +{ + struct hostap_interface *iface; + local_info_t *local; + struct ap_data *ap; + struct list_head *ptr; + struct iw_event iwe; + char *current_ev = buffer; + char *end_buf = buffer + IW_SCAN_MAX_DATA; +#if !defined(PRISM2_NO_KERNEL_IEEE80211_MGMT) + char buf[64]; +#endif + + iface = netdev_priv(dev); + local = iface->local; + ap = local->ap; + + spin_lock_bh(&ap->sta_table_lock); + + for (ptr = ap->sta_list.next; ptr != NULL && ptr != &ap->sta_list; + ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, sta->addr, ETH_ALEN); + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Use the mode to indicate if it's a station or + * an Access Point */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + if (sta->ap) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_INFRA; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + + /* Some quality */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (sta->last_rx_silence == 0) + iwe.u.qual.qual = sta->last_rx_signal < 27 ? + 0 : (sta->last_rx_signal - 27) * 92 / 127; + else + iwe.u.qual.qual = sta->last_rx_signal - + sta->last_rx_silence - 35; + iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(sta->last_rx_signal); + iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm(sta->last_rx_silence); + iwe.u.qual.updated = sta->last_rx_updated; + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + if (sta->ap) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = sta->u.ap.ssid_len; + iwe.u.data.flags = 1; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, + sta->u.ap.ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (sta->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = + IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, + sta->u.ap.ssid + /* 0 byte memcpy */); + + if (sta->u.ap.channel > 0 && + sta->u.ap.channel <= FREQ_COUNT) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = freq_list[sta->u.ap.channel - 1] + * 100000; + iwe.u.freq.e = 1; + current_ev = iwe_stream_add_event( + current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "beacon_interval=%d", + sta->listen_interval); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + sta->last_rx_updated = 0; + + /* To be continued, we should make good use of IWEVCUSTOM */ + } + + spin_unlock_bh(&ap->sta_table_lock); + + return current_ev - buffer; +} + + +static int prism2_hostapd_add_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (sta == NULL) { + sta = ap_add_sta(ap, param->sta_addr); + if (sta == NULL) + return -1; + } + + if (!(sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_new_sta(sta->local->dev, sta); + + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->last_rx = jiffies; + sta->aid = param->u.add_sta.aid; + sta->capability = param->u.add_sta.capability; + sta->tx_supp_rates = param->u.add_sta.tx_supp_rates; + if (sta->tx_supp_rates & WLAN_RATE_1M) + sta->supported_rates[0] = 2; + if (sta->tx_supp_rates & WLAN_RATE_2M) + sta->supported_rates[1] = 4; + if (sta->tx_supp_rates & WLAN_RATE_5M5) + sta->supported_rates[2] = 11; + if (sta->tx_supp_rates & WLAN_RATE_11M) + sta->supported_rates[3] = 22; + prism2_check_tx_rates(sta); + atomic_dec(&sta->users); + return 0; +} + + +static int prism2_hostapd_remove_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + ap_sta_hash_del(ap, sta); + list_del(&sta->list); + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + if ((sta->flags & WLAN_STA_ASSOC) && !sta->ap && sta->local) + hostap_event_expired_sta(sta->local->dev, sta); + ap_free_sta(ap, sta); + + return 0; +} + + +static int prism2_hostapd_get_info_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + param->u.get_info_sta.inactive_sec = (jiffies - sta->last_rx) / HZ; + + atomic_dec(&sta->users); + + return 1; +} + + +static int prism2_hostapd_set_flags_sta(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, param->sta_addr); + if (sta) { + sta->flags |= param->u.set_flags_sta.flags_or; + sta->flags &= param->u.set_flags_sta.flags_and; + } + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta) + return -ENOENT; + + return 0; +} + + +static int prism2_hostapd(struct ap_data *ap, + struct prism2_hostapd_param *param) +{ + switch (param->cmd) { + case PRISM2_HOSTAPD_FLUSH: + ap_control_kickall(ap); + return 0; + case PRISM2_HOSTAPD_ADD_STA: + return prism2_hostapd_add_sta(ap, param); + case PRISM2_HOSTAPD_REMOVE_STA: + return prism2_hostapd_remove_sta(ap, param); + case PRISM2_HOSTAPD_GET_INFO_STA: + return prism2_hostapd_get_info_sta(ap, param); + case PRISM2_HOSTAPD_SET_FLAGS_STA: + return prism2_hostapd_set_flags_sta(ap, param); + default: + printk(KERN_WARNING "prism2_hostapd: unknown cmd=%d\n", + param->cmd); + return -EOPNOTSUPP; + } +} + + +/* Update station info for host-based TX rate control and return current + * TX rate */ +static int ap_update_sta_tx_rate(struct sta_info *sta, struct net_device *dev) +{ + int ret = sta->tx_rate; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + sta->tx_count[sta->tx_rate_idx]++; + sta->tx_since_last_failure++; + sta->tx_consecutive_exc = 0; + if (sta->tx_since_last_failure >= WLAN_RATE_UPDATE_COUNT && + sta->tx_rate_idx < sta->tx_max_rate) { + /* use next higher rate */ + int old_rate, new_rate; + old_rate = new_rate = sta->tx_rate_idx; + while (new_rate < sta->tx_max_rate) { + new_rate++; + if (ap_tx_rate_ok(new_rate, sta, local)) { + sta->tx_rate_idx = new_rate; + break; + } + } + if (old_rate != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate raised to" + " %d\n", dev->name, MAC2STR(sta->addr), + sta->tx_rate); + } + sta->tx_since_last_failure = 0; + } + + return ret; +} + + +/* Called only from software IRQ. Called for each TX frame prior possible + * encryption and transmit. */ +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx) +{ + struct sta_info *sta = NULL; + struct sk_buff *skb = tx->skb; + int set_tim, ret; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + meta = (struct hostap_skb_tx_data *) skb->cb; + ret = AP_TX_CONTINUE; + if (local->ap == NULL || skb->len < 10 || + meta->iface->type == HOSTAP_INTERFACE_STA) + goto out; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + if (hdr->addr1[0] & 0x01) { + /* broadcast/multicast frame - no AP related processing */ + goto out; + } + + /* unicast packet - check whether destination STA is associated */ + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (local->iw_mode == IW_MODE_MASTER && sta == NULL && !meta->wds && + meta->iface->type != HOSTAP_INTERFACE_MASTER && + meta->iface->type != HOSTAP_INTERFACE_AP) { +#if 0 + /* This can happen, e.g., when wlan0 is added to a bridge and + * bridging code does not know which port is the correct target + * for a unicast frame. In this case, the packet is send to all + * ports of the bridge. Since this is a valid scenario, do not + * print out any errors here. */ + if (net_ratelimit()) { + printk(KERN_DEBUG "AP: drop packet to non-associated " + "STA " MACSTR "\n", MAC2STR(hdr->addr1)); + } +#endif + local->ap->tx_drop_nonassoc++; + ret = AP_TX_DROP; + goto out; + } + + if (sta == NULL) + goto out; + + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_TX_CONTINUE_NOT_AUTHORIZED; + + /* Set tx_rate if using host-based TX rate control */ + if (!local->fw_tx_rate_control) + local->ap->last_tx_rate = meta->rate = + ap_update_sta_tx_rate(sta, local->dev); + + if (local->iw_mode != IW_MODE_MASTER) + goto out; + + if (!(sta->flags & WLAN_STA_PS)) + goto out; + + if (memcmp(skb->cb, AP_SKB_CB_MAGIC, AP_SKB_CB_MAGIC_LEN) == 0) { + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_ADD_MOREDATA) { + /* indicate to STA that more frames follow */ + hdr->frame_control |= + __constant_cpu_to_le16(WLAN_FC_MOREDATA); + } + + if (skb->cb[AP_SKB_CB_MAGIC_LEN] & AP_SKB_CB_BUFFERED_FRAME) { + /* packet was already buffered and now send due to + * PS poll, so do not rebuffer it */ + goto out; + } + } + + if (skb_queue_len(&sta->tx_buf) >= STA_MAX_TX_BUFFER) { + PDEBUG(DEBUG_PS, "%s: No more space in STA (" MACSTR ")'s PS " + "mode buffer\n", local->dev->name, MAC2STR(sta->addr)); + /* Make sure that TIM is set for the station (it might not be + * after AP wlan hw reset). */ + /* FIX: should fix hw reset to restore bits based on STA + * buffer state.. */ + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + ret = AP_TX_DROP; + goto out; + } + + /* STA in PS mode, buffer frame for later delivery */ + set_tim = skb_queue_empty(&sta->tx_buf); + skb_queue_tail(&sta->tx_buf, skb); + /* FIX: could save RX time to skb and expire buffered frames after + * some time if STA does not poll for them */ + + if (set_tim) { + if (sta->flags & WLAN_STA_TIM) + PDEBUG(DEBUG_PS2, "Re-setting TIM for aid %d\n", + sta->aid); + hostap_set_tim(local, sta->aid, 1); + sta->flags |= WLAN_STA_TIM; + } + + ret = AP_TX_BUFFERED; + + out: + if (sta != NULL) { + if (ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) { + sta->tx_packets++; + sta->tx_bytes += skb->len; + sta->last_tx = jiffies; + } + + if ((ret == AP_TX_CONTINUE || + ret == AP_TX_CONTINUE_NOT_AUTHORIZED) && + sta->crypt && tx->host_encrypt) { + tx->crypt = sta->crypt; + tx->sta_ptr = sta; /* hostap_handle_sta_release() will + * be called to release sta info + * later */ + } else + atomic_dec(&sta->users); + } + + return ret; +} + + +void hostap_handle_sta_release(void *ptr) +{ + struct sta_info *sta = ptr; + atomic_dec(&sta->users); +} + + +/* Called only as a tasklet (software IRQ) */ +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb) +{ + struct sta_info *sta; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + meta = (struct hostap_skb_tx_data *) skb->cb; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr1); + if (!sta) { + spin_unlock(&local->ap->sta_table_lock); + PDEBUG(DEBUG_AP, "%s: Could not find STA " MACSTR " for this " + "TX error (@%lu)\n", + local->dev->name, MAC2STR(hdr->addr1), jiffies); + return; + } + + sta->tx_since_last_failure = 0; + sta->tx_consecutive_exc++; + + if (sta->tx_consecutive_exc >= WLAN_RATE_DECREASE_THRESHOLD && + sta->tx_rate_idx > 0 && meta->rate <= sta->tx_rate) { + /* use next lower rate */ + int old, rate; + old = rate = sta->tx_rate_idx; + while (rate > 0) { + rate--; + if (ap_tx_rate_ok(rate, sta, local)) { + sta->tx_rate_idx = rate; + break; + } + } + if (old != sta->tx_rate_idx) { + switch (sta->tx_rate_idx) { + case 0: sta->tx_rate = 10; break; + case 1: sta->tx_rate = 20; break; + case 2: sta->tx_rate = 55; break; + case 3: sta->tx_rate = 110; break; + default: sta->tx_rate = 0; break; + } + PDEBUG(DEBUG_AP, "%s: STA " MACSTR " TX rate lowered " + "to %d\n", local->dev->name, MAC2STR(sta->addr), + sta->tx_rate); + } + sta->tx_consecutive_exc = 0; + } + spin_unlock(&local->ap->sta_table_lock); +} + + +static void hostap_update_sta_ps2(local_info_t *local, struct sta_info *sta, + int pwrmgt, int type, int stype) +{ + if (pwrmgt && !(sta->flags & WLAN_STA_PS)) { + sta->flags |= WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to use PS " + "mode (type=0x%02X, stype=0x%02X)\n", + MAC2STR(sta->addr), type, stype); + } else if (!pwrmgt && (sta->flags & WLAN_STA_PS)) { + sta->flags &= ~WLAN_STA_PS; + PDEBUG(DEBUG_PS2, "STA " MACSTR " changed to not use " + "PS mode (type=0x%02X, stype=0x%02X)\n", + MAC2STR(sta->addr), type, stype); + if (type != WLAN_FC_TYPE_CTRL || stype != WLAN_FC_STYPE_PSPOLL) + schedule_packet_send(local, sta); + } +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame to update + * STA power saving state. pwrmgt is a flag from 802.11 frame_control field. */ +int hostap_update_sta_ps(local_info_t *local, struct hostap_ieee80211_hdr *hdr) +{ + struct sta_info *sta; + u16 fc; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + fc = le16_to_cpu(hdr->frame_control); + hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT, + WLAN_FC_GET_TYPE(fc), WLAN_FC_GET_STYPE(fc)); + + atomic_dec(&sta->users); + return 0; +} + + +/* Called only as a tasklet (software IRQ). Called for each RX frame after + * getting RX header and payload from hardware. */ +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds) +{ + int ret; + struct sta_info *sta; + u16 fc, type, stype; + struct hostap_ieee80211_hdr *hdr; + + if (local->ap == NULL) + return AP_RX_CONTINUE; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + fc = le16_to_cpu(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (sta && !(sta->flags & WLAN_STA_AUTHORIZED)) + ret = AP_RX_CONTINUE_NOT_AUTHORIZED; + else + ret = AP_RX_CONTINUE; + + + if (fc & WLAN_FC_TODS) { + if (!wds && (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + printk(KERN_DEBUG "%s: dropped received packet" + " from non-associated STA " MACSTR + " (type=0x%02x, subtype=0x%02x)\n", + dev->name, MAC2STR(hdr->addr2), type, + stype); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + } else if (fc & WLAN_FC_FROMDS) { + if (!wds) { + /* FromDS frame - not for us; probably + * broadcast/multicast in another BSS - drop */ + if (memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + printk(KERN_DEBUG "Odd.. FromDS packet " + "received with own BSSID\n"); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + } else if (stype == WLAN_FC_STYPE_NULLFUNC && sta == NULL && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NON_ASSOC); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* At least Lucent f/w seems to send data::nullfunc + * frames with no ToDS flag when the current AP returns + * after being unavailable for some time. Speed up + * re-association by informing the station about it not + * being associated. */ + printk(KERN_DEBUG "%s: rejected received nullfunc " + "frame without ToDS from not associated STA " + MACSTR "\n", + dev->name, MAC2STR(hdr->addr2)); + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } else if (stype == WLAN_FC_STYPE_NULLFUNC) { + /* At least Lucent cards seem to send periodic nullfunc + * frames with ToDS. Let these through to update SQ + * stats and PS state. Nullfunc frames do not contain + * any data and they will be dropped below. */ + } else { + /* If BSSID (Addr3) is foreign, this frame is a normal + * broadcast frame from an IBSS network. Drop it silently. + * If BSSID is own, report the dropping of this frame. */ + if (memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + printk(KERN_DEBUG "%s: dropped received packet from " + MACSTR " with no ToDS flag (type=0x%02x, " + "subtype=0x%02x)\n", dev->name, + MAC2STR(hdr->addr2), type, stype); + hostap_dump_rx_80211(dev->name, skb, rx_stats); + } + ret = AP_RX_DROP; + goto out; + } + + if (sta) { + hostap_update_sta_ps2(local, sta, fc & WLAN_FC_PWRMGT, + type, stype); + + sta->rx_packets++; + sta->rx_bytes += skb->len; + sta->last_rx = jiffies; + } + + if (local->ap->nullfunc_ack && stype == WLAN_FC_STYPE_NULLFUNC && + fc & WLAN_FC_TODS) { + if (local->hostapd) { + prism2_rx_80211(local->apdev, skb, rx_stats, + PRISM2_RX_NULLFUNC_ACK); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + } else { + /* some STA f/w's seem to require control::ACK frame + * for data::nullfunc, but Prism2 f/w 0.8.0 (at least + * from Compaq) does not send this.. Try to generate + * ACK for these frames from the host driver to make + * power saving work with, e.g., Lucent WaveLAN f/w */ + hostap_rx(dev, skb, rx_stats); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + } + ret = AP_RX_EXIT; + goto out; + } + + out: + if (sta) + atomic_dec(&sta->users); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_handle_sta_crypto(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct prism2_crypt_data **crypt, void **sta_ptr) +{ + struct sta_info *sta; + + spin_lock(&local->ap->sta_table_lock); + sta = ap_get_sta(local->ap, hdr->addr2); + if (sta) + atomic_inc(&sta->users); + spin_unlock(&local->ap->sta_table_lock); + + if (!sta) + return -1; + + if (sta->crypt) { + *crypt = sta->crypt; + *sta_ptr = sta; + /* hostap_handle_sta_release() will be called to release STA + * info */ + } else + atomic_dec(&sta->users); + + return 0; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 0; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta != NULL && (sta->flags & WLAN_STA_ASSOC) && !sta->ap && + ((sta->flags & WLAN_STA_AUTHORIZED) || + ap->local->ieee_802_1x == 0)) + ret = 1; + spin_unlock(&ap->sta_table_lock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr) +{ + struct sta_info *sta; + int ret = 1; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, sta_addr); + if (sta) + ret = 0; + spin_unlock(&ap->sta_table_lock); + + if (ret == 1) { + sta = ap_add_sta(ap, sta_addr); + if (!sta) + ret = -1; + sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; + sta->ap = 1; + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + /* No way of knowing which rates are supported since we did not + * get supported rates element from beacon/assoc req. Assume + * that remote end supports all 802.11b rates. */ + sta->supported_rates[0] = 0x82; + sta->supported_rates[1] = 0x84; + sta->supported_rates[2] = 0x0b; + sta->supported_rates[3] = 0x16; + sta->tx_supp_rates = WLAN_RATE_1M | WLAN_RATE_2M | + WLAN_RATE_5M5 | WLAN_RATE_11M; + sta->tx_rate = 110; + sta->tx_max_rate = sta->tx_rate_idx = 3; + } + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +int hostap_update_rx_stats(struct ap_data *ap, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats) +{ + struct sta_info *sta; + + if (!ap) + return -1; + + spin_lock(&ap->sta_table_lock); + sta = ap_get_sta(ap, hdr->addr2); + if (sta) { + sta->last_rx_silence = rx_stats->noise; + sta->last_rx_signal = rx_stats->signal; + sta->last_rx_rate = rx_stats->rate; + sta->last_rx_updated = 7; + if (rx_stats->rate == 10) + sta->rx_count[0]++; + else if (rx_stats->rate == 20) + sta->rx_count[1]++; + else if (rx_stats->rate == 55) + sta->rx_count[2]++; + else if (rx_stats->rate == 110) + sta->rx_count[3]++; + } + spin_unlock(&ap->sta_table_lock); + + return sta ? 0 : -1; +} + + +void hostap_update_rates(local_info_t *local) +{ + struct list_head *ptr; + struct ap_data *ap = local->ap; + + if (!ap) + return; + + spin_lock_bh(&ap->sta_table_lock); + for (ptr = ap->sta_list.next; ptr != &ap->sta_list; ptr = ptr->next) { + struct sta_info *sta = (struct sta_info *) ptr; + prism2_check_tx_rates(sta); + } + spin_unlock_bh(&ap->sta_table_lock); +} + + +static void * ap_crypt_get_ptrs(struct ap_data *ap, u8 *addr, int permanent, + struct prism2_crypt_data ***crypt) +{ + struct sta_info *sta; + + spin_lock_bh(&ap->sta_table_lock); + sta = ap_get_sta(ap, addr); + if (sta) + atomic_inc(&sta->users); + spin_unlock_bh(&ap->sta_table_lock); + + if (!sta && permanent) + sta = ap_add_sta(ap, addr); + + if (!sta) + return NULL; + + if (permanent) + sta->flags |= WLAN_STA_PERM; + + *crypt = &sta->crypt; + + return sta; +} + + +void hostap_add_wds_links(local_info_t *local) +{ + struct ap_data *ap = local->ap; + struct list_head *ptr; + + spin_lock_bh(&ap->sta_table_lock); + list_for_each(ptr, &ap->sta_list) { + struct sta_info *sta = list_entry(ptr, struct sta_info, list); + if (sta->ap) + hostap_wds_link_oper(local, sta->addr, WDS_ADD); + } + spin_unlock_bh(&ap->sta_table_lock); + + schedule_work(&local->ap->wds_oper_queue); +} + + +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type) +{ + struct wds_oper_data *entry; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) + return; + memcpy(entry->addr, addr, ETH_ALEN); + entry->type = type; + spin_lock_bh(&local->lock); + entry->next = local->ap->wds_oper_entries; + local->ap->wds_oper_entries = entry; + spin_unlock_bh(&local->lock); + + schedule_work(&local->ap->wds_oper_queue); +} + + +EXPORT_SYMBOL(hostap_init_data); +EXPORT_SYMBOL(hostap_init_ap_proc); +EXPORT_SYMBOL(hostap_free_data); +EXPORT_SYMBOL(hostap_check_sta_fw_version); +EXPORT_SYMBOL(hostap_handle_sta_tx); +EXPORT_SYMBOL(hostap_handle_sta_release); +EXPORT_SYMBOL(hostap_handle_sta_tx_exc); +EXPORT_SYMBOL(hostap_update_sta_ps); +EXPORT_SYMBOL(hostap_handle_sta_rx); +EXPORT_SYMBOL(hostap_is_sta_assoc); +EXPORT_SYMBOL(hostap_is_sta_authorized); +EXPORT_SYMBOL(hostap_add_sta); +EXPORT_SYMBOL(hostap_update_rates); +EXPORT_SYMBOL(hostap_add_wds_links); +EXPORT_SYMBOL(hostap_wds_link_oper); +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +EXPORT_SYMBOL(hostap_deauth_all_stas); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ diff -Nru a/drivers/net/wireless/hostap/hostap_ap.h b/drivers/net/wireless/hostap/hostap_ap.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_ap.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,272 @@ +#ifndef HOSTAP_AP_H +#define HOSTAP_AP_H + +/* AP data structures for STAs */ + +/* maximum number of frames to buffer per STA */ +#define STA_MAX_TX_BUFFER 32 + +/* Flags used in skb->cb[6] to control how the packet is handled in TX path. + * skb->cb[0..5] must contain magic value 'hostap' to indicate that cb[6] is + * used. */ +#define AP_SKB_CB_MAGIC "hostap" +#define AP_SKB_CB_MAGIC_LEN 6 +#define AP_SKB_CB_BUFFERED_FRAME BIT(0) +#define AP_SKB_CB_ADD_MOREDATA BIT(1) + + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) /* TIM bit is on for PS stations */ +#define WLAN_STA_PERM BIT(4) /* permanent; do not remove entry on expiration */ +#define WLAN_STA_AUTHORIZED BIT(5) /* If 802.1X is used, this flag is + * controlling whether STA is authorized to + * send and receive non-IEEE 802.1X frames + */ +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ + +#define WLAN_RATE_1M BIT(0) +#define WLAN_RATE_2M BIT(1) +#define WLAN_RATE_5M5 BIT(2) +#define WLAN_RATE_11M BIT(3) +#define WLAN_RATE_COUNT 4 + +/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, + * but some pre-standard IEEE 802.11g products use longer elements. */ +#define WLAN_SUPP_RATES_MAX 32 + +/* Try to increase TX rate after # successfully sent consecutive packets */ +#define WLAN_RATE_UPDATE_COUNT 50 + +/* Decrease TX rate after # consecutive dropped packets */ +#define WLAN_RATE_DECREASE_THRESHOLD 2 + +struct sta_info { + struct list_head list; + struct sta_info *hnext; /* next entry in hash table list */ + atomic_t users; /* number of users (do not remove if > 0) */ + struct proc_dir_entry *proc; + + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + + unsigned long last_auth; + unsigned long last_assoc; + unsigned long last_rx; + unsigned long last_tx; + unsigned long rx_packets, tx_packets; + unsigned long rx_bytes, tx_bytes; + struct sk_buff_head tx_buf; + /* FIX: timeout buffers with an expiry time somehow derived from + * listen_interval */ + + s8 last_rx_silence; /* Noise in dBm */ + s8 last_rx_signal; /* Signal strength in dBm */ + u8 last_rx_rate; /* TX rate in 0.1 Mbps */ + u8 last_rx_updated; /* IWSPY's struct iw_quality::updated */ + + u8 tx_supp_rates; /* bit field of supported TX rates */ + u8 tx_rate; /* current TX rate (in 0.1 Mbps) */ + u8 tx_rate_idx; /* current TX rate (WLAN_RATE_*) */ + u8 tx_max_rate; /* max TX rate (WLAN_RATE_*) */ + u32 tx_count[WLAN_RATE_COUNT]; /* number of frames sent (per rate) */ + u32 rx_count[WLAN_RATE_COUNT]; /* number of frames received (per rate) + */ + u32 tx_since_last_failure; + u32 tx_consecutive_exc; + + struct prism2_crypt_data *crypt; + + int ap; /* whether this station is an AP */ + + local_info_t *local; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + union { + struct { + char *challenge; /* shared key authentication + * challenge */ + } sta; + struct { + int ssid_len; + unsigned char ssid[MAX_SSID_LEN + 1]; /* AP's ssid */ + int channel; + unsigned long last_beacon; /* last RX beacon time */ + } ap; + } u; + + struct timer_list timer; + enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH } timeout_next; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +#define MAX_STA_COUNT 1024 + +/* Maximum number of AIDs to use for STAs; must be 2007 or lower + * (8802.11 limitation) */ +#define MAX_AID_TABLE_SIZE 128 + +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY_SEC + * has passed since last received frame from the station, a nullfunc data + * frame is sent to the station. If this frame is not acknowledged and no other + * frames have been received, the station will be disassociated after + * AP_DISASSOC_DELAY. Similarily, a the station will be deauthenticated after + * AP_DEAUTH_DELAY. AP_TIMEOUT_RESOLUTION is the resolution that is used with + * max inactivity timer. */ +#define AP_MAX_INACTIVITY_SEC (5 * 60) +#define AP_DISASSOC_DELAY (HZ) +#define AP_DEAUTH_DELAY (HZ) + +/* ap_policy: whether to accept frames to/from other APs/IBSS */ +typedef enum { + AP_OTHER_AP_SKIP_ALL = 0, + AP_OTHER_AP_SAME_SSID = 1, + AP_OTHER_AP_ALL = 2, + AP_OTHER_AP_EVEN_IBSS = 3 +} ap_policy_enum; + +#define PRISM2_AUTH_OPEN BIT(0) +#define PRISM2_AUTH_SHARED_KEY BIT(1) + + +/* MAC address-based restrictions */ +struct mac_entry { + struct list_head list; + u8 addr[6]; +}; + +struct mac_restrictions { + enum { MAC_POLICY_OPEN = 0, MAC_POLICY_ALLOW, MAC_POLICY_DENY } policy; + unsigned int entries; + struct list_head mac_list; + spinlock_t lock; +}; + + +struct add_sta_proc_data { + u8 addr[ETH_ALEN]; + struct add_sta_proc_data *next; +}; + + +typedef enum { WDS_ADD, WDS_DEL } wds_oper_type; +struct wds_oper_data { + wds_oper_type type; + u8 addr[ETH_ALEN]; + struct wds_oper_data *next; +}; + + +struct ap_data { + int initialized; /* whether ap_data has been initialized */ + local_info_t *local; + int bridge_packets; /* send packet to associated STAs directly to the + * wireless media instead of higher layers in the + * kernel */ + unsigned int bridged_unicast; /* number of unicast frames bridged on + * wireless media */ + unsigned int bridged_multicast; /* number of non-unicast frames + * bridged on wireless media */ + unsigned int tx_drop_nonassoc; /* number of unicast TX packets dropped + * because they were to an address that + * was not associated */ + int nullfunc_ack; /* use workaround for nullfunc frame ACKs */ + + spinlock_t sta_table_lock; + int num_sta; /* number of entries in sta_list */ + struct list_head sta_list; /* STA info list head */ + struct sta_info *sta_hash[STA_HASH_SIZE]; + + struct proc_dir_entry *proc; + + ap_policy_enum ap_policy; + unsigned int max_inactivity; + int autom_ap_wds; + + struct mac_restrictions mac_restrictions; /* MAC-based auth */ + int last_tx_rate; + + struct work_struct add_sta_proc_queue; + struct add_sta_proc_data *add_sta_proc_entries; + + struct work_struct wds_oper_queue; + struct wds_oper_data *wds_oper_entries; + + u16 tx_callback_idx; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + /* pointers to STA info; based on allocated AID or NULL if AID free + * AID is in the range 1-2007, so sta_aid[0] corresponders to AID 1 + * and so on + */ + struct sta_info *sta_aid[MAX_AID_TABLE_SIZE]; + + u16 tx_callback_auth, tx_callback_assoc, tx_callback_poll; + + /* WEP operations for generating challenges to be used with shared key + * authentication */ + struct hostap_crypto_ops *crypt; + void *crypt_priv; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ +}; + + +void hostap_rx(struct net_device *dev, struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats); +void hostap_init_data(local_info_t *local); +void hostap_init_ap_proc(local_info_t *local); +void hostap_free_data(struct ap_data *ap); +void hostap_check_sta_fw_version(struct ap_data *ap, int sta_fw_ver); + +typedef enum { + AP_TX_CONTINUE, AP_TX_DROP, AP_TX_RETRY, AP_TX_BUFFERED, + AP_TX_CONTINUE_NOT_AUTHORIZED +} ap_tx_ret; +struct hostap_tx_data { + struct sk_buff *skb; + int host_encrypt; + struct prism2_crypt_data *crypt; + void *sta_ptr; +}; +ap_tx_ret hostap_handle_sta_tx(local_info_t *local, struct hostap_tx_data *tx); +void hostap_handle_sta_release(void *ptr); +void hostap_handle_sta_tx_exc(local_info_t *local, struct sk_buff *skb); +int hostap_update_sta_ps(local_info_t *local, + struct hostap_ieee80211_hdr *hdr); +typedef enum { + AP_RX_CONTINUE, AP_RX_DROP, AP_RX_EXIT, AP_RX_CONTINUE_NOT_AUTHORIZED +} ap_rx_ret; +ap_rx_ret hostap_handle_sta_rx(local_info_t *local, struct net_device *dev, + struct sk_buff *skb, + struct hostap_80211_rx_status *rx_stats, + int wds); +int hostap_handle_sta_crypto(local_info_t *local, + struct hostap_ieee80211_hdr *hdr, + struct prism2_crypt_data **crypt, void **sta_ptr); +int hostap_is_sta_assoc(struct ap_data *ap, u8 *sta_addr); +int hostap_is_sta_authorized(struct ap_data *ap, u8 *sta_addr); +int hostap_add_sta(struct ap_data *ap, u8 *sta_addr); +int hostap_update_rx_stats(struct ap_data *ap, + struct hostap_ieee80211_hdr *hdr, + struct hostap_80211_rx_status *rx_stats); +void hostap_update_rates(local_info_t *local); +void hostap_add_wds_links(local_info_t *local); +void hostap_wds_link_oper(local_info_t *local, u8 *addr, wds_oper_type type); + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +void hostap_deauth_all_stas(struct net_device *dev, struct ap_data *ap, + int resend); +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +#endif /* HOSTAP_AP_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_common.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,556 @@ +#ifndef HOSTAP_COMMON_H +#define HOSTAP_COMMON_H + +#define BIT(x) (1 << (x)) + +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" + + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + + + +/* IEEE 802.11 defines */ + +#define WLAN_FC_PVER (BIT(1) | BIT(0)) +#define WLAN_FC_TODS BIT(8) +#define WLAN_FC_FROMDS BIT(9) +#define WLAN_FC_MOREFRAG BIT(10) +#define WLAN_FC_RETRY BIT(11) +#define WLAN_FC_PWRMGT BIT(12) +#define WLAN_FC_MOREDATA BIT(13) +#define WLAN_FC_ISWEP BIT(14) +#define WLAN_FC_ORDER BIT(15) + +#define WLAN_FC_GET_TYPE(fc) (((fc) & (BIT(3) | BIT(2))) >> 2) +#define WLAN_FC_GET_STYPE(fc) \ + (((fc) & (BIT(7) | BIT(6) | BIT(5) | BIT(4))) >> 4) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0))) +#define WLAN_GET_SEQ_SEQ(seq) \ + (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4) + +#define WLAN_FC_TYPE_MGMT 0 +#define WLAN_FC_TYPE_CTRL 1 +#define WLAN_FC_TYPE_DATA 2 + +/* management */ +#define WLAN_FC_STYPE_ASSOC_REQ 0 +#define WLAN_FC_STYPE_ASSOC_RESP 1 +#define WLAN_FC_STYPE_REASSOC_REQ 2 +#define WLAN_FC_STYPE_REASSOC_RESP 3 +#define WLAN_FC_STYPE_PROBE_REQ 4 +#define WLAN_FC_STYPE_PROBE_RESP 5 +#define WLAN_FC_STYPE_BEACON 8 +#define WLAN_FC_STYPE_ATIM 9 +#define WLAN_FC_STYPE_DISASSOC 10 +#define WLAN_FC_STYPE_AUTH 11 +#define WLAN_FC_STYPE_DEAUTH 12 + +/* control */ +#define WLAN_FC_STYPE_PSPOLL 10 +#define WLAN_FC_STYPE_RTS 11 +#define WLAN_FC_STYPE_CTS 12 +#define WLAN_FC_STYPE_ACK 13 +#define WLAN_FC_STYPE_CFEND 14 +#define WLAN_FC_STYPE_CFENDACK 15 + +/* data */ +#define WLAN_FC_STYPE_DATA 0 +#define WLAN_FC_STYPE_DATA_CFACK 1 +#define WLAN_FC_STYPE_DATA_CFPOLL 2 +#define WLAN_FC_STYPE_DATA_CFACKPOLL 3 +#define WLAN_FC_STYPE_NULLFUNC 4 +#define WLAN_FC_STYPE_CFACK 5 +#define WLAN_FC_STYPE_CFPOLL 6 +#define WLAN_FC_STYPE_CFACKPOLL 7 + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_ESS BIT(0) +#define WLAN_CAPABILITY_IBSS BIT(1) +#define WLAN_CAPABILITY_CF_POLLABLE BIT(2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3) +#define WLAN_CAPABILITY_PRIVACY BIT(4) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 +/* IEEE 802.11i */ +#define WLAN_STATUS_INVALID_IE 40 +#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 +#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42 +#define WLAN_STATUS_AKMP_NOT_VALID 43 +#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44 +#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45 +#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 +/* IEEE 802.11i */ +#define WLAN_REASON_INVALID_IE 13 +#define WLAN_REASON_MICHAEL_MIC_FAILURE 14 +#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15 +#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16 +#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17 +#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18 +#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19 +#define WLAN_REASON_AKMP_NOT_VALID 20 +#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21 +#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22 +#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23 +#define WLAN_REASON_CIPHER_SUITE_REJECTED 24 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + + +/* HFA384X Configuration RIDs */ +#define HFA384X_RID_CNFPORTTYPE 0xFC00 +#define HFA384X_RID_CNFOWNMACADDR 0xFC01 +#define HFA384X_RID_CNFDESIREDSSID 0xFC02 +#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 +#define HFA384X_RID_CNFOWNSSID 0xFC04 +#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 +#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 +#define HFA384X_RID_CNFMAXDATALEN 0xFC07 +#define HFA384X_RID_CNFWDSADDRESS 0xFC08 +#define HFA384X_RID_CNFPMENABLED 0xFC09 +#define HFA384X_RID_CNFPMEPS 0xFC0A +#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HFA384X_RID_CNFOWNNAME 0xFC0E +#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ +#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ +#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ +#define HFA384X_RID_UNKNOWN1 0xFC20 +#define HFA384X_RID_UNKNOWN2 0xFC21 +#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 +#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 +#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 +#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 +#define HFA384X_RID_CNFWEPFLAGS 0xFC28 +#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A +#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ +#define HFA384X_RID_CNFTXCONTROL 0xFC2C +#define HFA384X_RID_CNFROAMINGMODE 0xFC2D +#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ +#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 +#define HFA384X_RID_CNFMMLIFE 0xFC31 +#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 +#define HFA384X_RID_CNFBEACONINT 0xFC33 +#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ +#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 +#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HFA384X_RID_CNFTIMCTRL 0xFC40 +#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ +#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ +#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ +#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; + * write only */ +#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_GROUPADDRESSES 0xFC80 +#define HFA384X_RID_CREATEIBSS 0xFC81 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 +#define HFA384X_RID_RTSTHRESHOLD 0xFC83 +#define HFA384X_RID_TXRATECONTROL 0xFC84 +#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ +#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ +#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ +#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ +#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HFA384X_RID_CNFBASICRATES 0xFCB3 +#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ +#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ +#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ +#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ +#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ +#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ +#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ +#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_TICKTIME 0xFCE0 +#define HFA384X_RID_SCANREQUEST 0xFCE1 +#define HFA384X_RID_JOINREQUEST 0xFCE2 +#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ +#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ +#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ + +/* HFA384X Information RIDs */ +#define HFA384X_RID_MAXLOADTIME 0xFD00 +#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 +#define HFA384X_RID_PRIID 0xFD02 +#define HFA384X_RID_PRISUPRANGE 0xFD03 +#define HFA384X_RID_CFIACTRANGES 0xFD04 +#define HFA384X_RID_NICSERNUM 0xFD0A +#define HFA384X_RID_NICID 0xFD0B +#define HFA384X_RID_MFISUPRANGE 0xFD0C +#define HFA384X_RID_CFISUPRANGE 0xFD0D +#define HFA384X_RID_CHANNELLIST 0xFD10 +#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 +#define HFA384X_RID_TEMPTYPE 0xFD12 +#define HFA384X_RID_CIS 0xFD13 +#define HFA384X_RID_STAID 0xFD20 +#define HFA384X_RID_STASUPRANGE 0xFD21 +#define HFA384X_RID_MFIACTRANGES 0xFD22 +#define HFA384X_RID_CFIACTRANGES2 0xFD23 +#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; + * only Prism2.5(?) */ +#define HFA384X_RID_PORTSTATUS 0xFD40 +#define HFA384X_RID_CURRENTSSID 0xFD41 +#define HFA384X_RID_CURRENTBSSID 0xFD42 +#define HFA384X_RID_COMMSQUALITY 0xFD43 +#define HFA384X_RID_CURRENTTXRATE 0xFD44 +#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 +#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 +#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 +#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B +#define HFA384X_RID_CFPOLLABLE 0xFD4C +#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ +#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ +#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ +#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ +#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ +#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ +#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ +#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ +#define HFA384X_RID_PHYTYPE 0xFDC0 +#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 +#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 +#define HFA384X_RID_CCAMODE 0xFDC3 +#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 +#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ +#define HFA384X_RID_BUILDSEQ 0xFFFE +#define HFA384X_RID_FWID 0xFFFF + + +struct hfa384x_comp_ident +{ + u16 id; + u16 variant; + u16 major; + u16 minor; +} __attribute__ ((packed)); + +#define HFA384X_COMP_ID_PRI 0x15 +#define HFA384X_COMP_ID_STA 0x1f +#define HFA384X_COMP_ID_FW_AP 0x14b + +struct hfa384x_sup_range +{ + u16 role; + u16 id; + u16 variant; + u16 bottom; + u16 top; +} __attribute__ ((packed)); + + +struct hfa384x_build_id +{ + u16 pri_seq; + u16 sec_seq; +} __attribute__ ((packed)); + +/* FD01 - Download Buffer */ +struct hfa384x_rid_download_buffer +{ + u16 page; + u16 offset; + u16 length; +} __attribute__ ((packed)); + +/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ +struct hfa384x_comms_quality { + u16 comm_qual; /* 0 .. 92 */ + u16 signal_level; /* 27 .. 154 */ + u16 noise_level; /* 27 .. 154 */ +} __attribute__ ((packed)); + + +/* netdevice private ioctls (used, e.g., with iwpriv from user space) */ + +/* New wireless extensions API - SET/GET convention (even ioctl numbers are + * root only) + */ +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_GET_PRISM2_PARAM (SIOCIWFIRSTPRIV + 1) +#define PRISM2_IOCTL_WRITEMIF (SIOCIWFIRSTPRIV + 2) +#define PRISM2_IOCTL_READMIF (SIOCIWFIRSTPRIV + 3) +#define PRISM2_IOCTL_MONITOR (SIOCIWFIRSTPRIV + 4) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_INQUIRE (SIOCIWFIRSTPRIV + 8) +#define PRISM2_IOCTL_WDS_ADD (SIOCIWFIRSTPRIV + 10) +#define PRISM2_IOCTL_WDS_DEL (SIOCIWFIRSTPRIV + 12) +#define PRISM2_IOCTL_SET_RID_WORD (SIOCIWFIRSTPRIV + 14) +#define PRISM2_IOCTL_MACCMD (SIOCIWFIRSTPRIV + 16) +#define PRISM2_IOCTL_ADDMAC (SIOCIWFIRSTPRIV + 18) +#define PRISM2_IOCTL_DELMAC (SIOCIWFIRSTPRIV + 20) +#define PRISM2_IOCTL_KICKMAC (SIOCIWFIRSTPRIV + 22) + +/* following are not in SIOCGIWPRIV list; check permission in the driver code + */ +#define PRISM2_IOCTL_DOWNLOAD (SIOCDEVPRIVATE + 13) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, +}; + +enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, + HOSTAP_ANTSEL_LOW = 2, HOSTAP_ANTSEL_HIGH = 3 }; + + +/* PRISM2_IOCTL_MACCMD ioctl() subcommands: */ +enum { AP_MAC_CMD_POLICY_OPEN = 0, AP_MAC_CMD_POLICY_ALLOW = 1, + AP_MAC_CMD_POLICY_DENY = 2, AP_MAC_CMD_FLUSH = 3, + AP_MAC_CMD_KICKALL = 4 }; + + +/* PRISM2_IOCTL_DOWNLOAD ioctl() dl_cmd: */ +enum { + PRISM2_DOWNLOAD_VOLATILE = 1 /* RAM */, + /* Note! Old versions of prism2_srec have a fatal error in CRC-16 + * calculation, which will corrupt all non-volatile downloads. + * PRISM2_DOWNLOAD_NON_VOLATILE used to be 2, but it is now 3 to + * prevent use of old versions of prism2_srec for non-volatile + * download. */ + PRISM2_DOWNLOAD_NON_VOLATILE = 3 /* FLASH */, + PRISM2_DOWNLOAD_VOLATILE_GENESIS = 4 /* RAM in Genesis mode */, + /* Persistent versions of volatile download commands (keep firmware + * data in memory and automatically re-download after hw_reset */ + PRISM2_DOWNLOAD_VOLATILE_PERSISTENT = 5, + PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT = 6, +}; + +struct prism2_download_param { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_area { + u32 addr; /* wlan card address */ + u32 len; + void __user *ptr; /* pointer to data in user space */ + } data[0]; +}; + +#define PRISM2_MAX_DOWNLOAD_AREA_LEN 131072 +#define PRISM2_MAX_DOWNLOAD_LEN 262144 + + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY BIT(0) +#define HOSTAP_CRYPT_FLAG_PERMANENT BIT(1) + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + + +#endif /* HOSTAP_COMMON_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_config.h b/drivers/net/wireless/hostap/hostap_config.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_config.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,86 @@ +#ifndef HOSTAP_CONFIG_H +#define HOSTAP_CONFIG_H + +#define PRISM2_VERSION "CVS" + +/* In the previous versions of Host AP driver, support for user space version + * of IEEE 802.11 management (hostapd) used to be disabled in the default + * configuration. From now on, support for hostapd is always included and it is + * possible to disable kernel driver version of IEEE 802.11 management with a + * separate define, PRISM2_NO_KERNEL_IEEE80211_MGMT. */ +/* #define PRISM2_NO_KERNEL_IEEE80211_MGMT */ + +/* Maximum number of events handler per one interrupt */ +#define PRISM2_MAX_INTERRUPT_EVENTS 20 + +/* Use PCI bus master to copy data to/from BAP (only available for + * hostap_pci.o). + * + * Note! This is extremely experimental. PCI bus master is not supported by + * Intersil and it seems to have some problems at least on TX path (see below). + * The driver code for implementing bus master support is based on guessing + * and experimenting suitable control bits and these might not be correct. + * This code is included because using bus master makes a huge difference in + * host CPU load (something like 40% host CPU usage to 5-10% when sending or + * receiving at maximum throughput). + * + * Note2! Station firmware version 1.3.5 and primary firmware version 1.0.7 + * have some fixes for PCI corruption and these (or newer) versions are + * recommended especially when using bus mastering. + * + * NOTE: PCI bus mastering code has not been updated for long time and it is + * not likely to compile and it will _not_ work as is. Only enable this if you + * are prepared to first fix the implementation.. + */ +/* #define PRISM2_BUS_MASTER */ + +#ifdef PRISM2_BUS_MASTER + +/* PCI bus master implementation seems to be broken in current + * hardware/firmware versions. Enable this to use enable command to fix + * something before starting bus master operation on TX path. This will add + * some latency and an extra interrupt to each TX packet. */ +#define PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + +#endif /* PRISM2_BUS_MASTER */ + +/* Include code for downloading firmware images into volatile RAM. */ +#define PRISM2_DOWNLOAD_SUPPORT + +/* Allow kernel configuration to enable download support. */ +#if !defined(PRISM2_DOWNLOAD_SUPPORT) && defined(CONFIG_HOSTAP_FIRMWARE) +#define PRISM2_DOWNLOAD_SUPPORT +#endif + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* Allow writing firmware images into flash, i.e., to non-volatile storage. + * Before you enable this option, you should make absolutely sure that you are + * using prism2_srec utility that comes with THIS version of the driver! + * In addition, please note that it is possible to kill your card with + * non-volatile download if you are using incorrect image. This feature has not + * been fully tested, so please be careful with it. */ +/* #define PRISM2_NON_VOLATILE_DOWNLOAD */ +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +/* Save low-level I/O for debugging. This should not be enabled in normal use. + */ +/* #define PRISM2_IO_DEBUG */ + +/* Following defines can be used to remove unneeded parts of the driver, e.g., + * to limit the size of the kernel module. Definitions can be added here in + * hostap_config.h or they can be added to make command with EXTRA_CFLAGS, + * e.g., + * 'make pccard EXTRA_CFLAGS="-DPRISM2_NO_DEBUG -DPRISM2_NO_PROCFS_DEBUG"' + */ + +/* Do not include debug messages into the driver */ +/* #define PRISM2_NO_DEBUG */ + +/* Do not include /proc/net/prism2/wlan#/{registers,debug} */ +/* #define PRISM2_NO_PROCFS_DEBUG */ + +/* Do not include station functionality (i.e., allow only Master (Host AP) mode + */ +/* #define PRISM2_NO_STATION_MODES */ + +#endif /* HOSTAP_CONFIG_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_crypt.c b/drivers/net/wireless/hostap/hostap_crypt.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_crypt.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,167 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +struct hostap_crypto_alg { + struct list_head list; + struct hostap_crypto_ops *ops; +}; + + +struct hostap_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct hostap_crypto *hcrypt; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct hostap_crypto_alg *alg; + + if (hcrypt == NULL) + return -1; + + alg = (struct hostap_crypto_alg *) kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->lock, flags); + + printk(KERN_DEBUG "hostap_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} + + +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct hostap_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +static void * hostap_crypt_null_init(int keyidx) { return (void *) 1; } +static void hostap_crypt_null_deinit(void *priv) {} + +static struct hostap_crypto_ops hostap_crypt_null = { + .name = "NULL", + .init = hostap_crypt_null_init, + .deinit = hostap_crypt_null_deinit, + .encrypt_mpdu = NULL, + .decrypt_mpdu = NULL, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = NULL, + .get_key = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0 +}; + + +static int __init hostap_crypto_init(void) +{ + hcrypt = (struct hostap_crypto *) kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (hcrypt == NULL) + return -ENOMEM; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + (void) hostap_register_crypto_ops(&hostap_crypt_null); + + return 0; +} + + +static void __exit hostap_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct hostap_crypto_alg *alg = + (struct hostap_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "hostap_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + + +EXPORT_SYMBOL(hostap_register_crypto_ops); +EXPORT_SYMBOL(hostap_unregister_crypto_ops); +EXPORT_SYMBOL(hostap_get_crypto_ops); diff -Nru a/drivers/net/wireless/hostap/hostap_crypt.h b/drivers/net/wireless/hostap/hostap_crypt.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_crypt.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,50 @@ +#ifndef PRISM2_CRYPT_H +#define PRISM2_CRYPT_H + +struct hostap_crypto_ops { + char *name; + + /* init new crypto context (e.g., allocate private data space, + * select IV, etc.); returns NULL on failure or pointer to allocated + * private data on success */ + void * (*init)(int keyidx); + + /* deinitialize crypto context and free allocated private data */ + void (*deinit)(void *priv); + + /* encrypt/decrypt return < 0 on error or >= 0 on success. The return + * value from decrypt_mpdu is passed as the keyidx value for + * decrypt_msdu. skb must have enough head and tail room for the + * encryption; if not, error will be returned; these functions are + * called for all MPDUs (i.e., fragments). + */ + int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + + /* These functions are called for full MSDUs, i.e. full frames. + * These can be NULL if full MSDU operations are not needed. */ + int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len, + void *priv); + + int (*set_key)(void *key, int len, u8 *seq, void *priv); + int (*get_key)(void *key, int len, u8 *seq, void *priv); + + /* procfs handler for printing out key information and possible + * statistics */ + char * (*print_stats)(char *p, void *priv); + + /* maximum number of bytes added by encryption; encrypt buf is + * allocated with extra_prefix_len bytes, copy of in_buf, and + * extra_postfix_len; encrypt need not use all this space, but + * the result must start at the beginning of the buffer and correct + * length must be returned */ + int extra_prefix_len, extra_postfix_len; +}; + + +int hostap_register_crypto_ops(struct hostap_crypto_ops *ops); +int hostap_unregister_crypto_ops(struct hostap_crypto_ops *ops); +struct hostap_crypto_ops * hostap_get_crypto_ops(const char *name); + +#endif /* PRISM2_CRYPT_H */ diff -Nru a/drivers/net/wireless/hostap/hostap_crypt_ccmp.c b/drivers/net/wireless/hostap/hostap_crypt_ccmp.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_crypt_ccmp.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,486 @@ +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" +#include "hostap_wlan.h" +#include "hostap_80211.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: CCMP"); +MODULE_LICENSE("GPL"); + + +#define AES_BLOCK_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + + +struct hostap_ccmp_data { + u8 key[CCMP_TK_LEN]; + int key_set; + + u8 tx_pn[CCMP_PN_LEN]; + u8 rx_pn[CCMP_PN_LEN]; + + u32 dot11RSNAStatsCCMPFormatErrors; + u32 dot11RSNAStatsCCMPReplays; + u32 dot11RSNAStatsCCMPDecryptErrors; + + int key_idx; + + struct crypto_tfm *tfm; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], + tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; + u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; +}; + + +void hostap_ccmp_aes_encrypt(struct crypto_tfm *tfm, + const u8 pt[16], u8 ct[16]) +{ + struct scatterlist src, dst; + + src.page = virt_to_page(pt); + src.offset = offset_in_page(pt); + src.length = AES_BLOCK_LEN; + + dst.page = virt_to_page(ct); + dst.offset = offset_in_page(ct); + dst.length = AES_BLOCK_LEN; + + crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN); +} + + +static void * hostap_ccmp_init(int key_idx) +{ + struct hostap_ccmp_data *priv; + + if (!try_module_get(THIS_MODULE)) + return NULL; + + priv = (struct hostap_ccmp_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) { + goto fail; + } + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm = crypto_alloc_tfm("aes", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "hostap_crypt_ccmp: could not allocate " + "crypto API aes\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + module_put(THIS_MODULE); + return NULL; +} + + +static void hostap_ccmp_deinit(void *priv) +{ + struct hostap_ccmp_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + kfree(priv); + module_put(THIS_MODULE); +} + + +static inline void xor_block(u8 *b, u8 *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + + +static void ccmp_init_blocks(struct crypto_tfm *tfm, + struct hostap_ieee80211_hdr *hdr, + u8 *pn, size_t dlen, u8 *b0, u8 *auth, + u8 *s0) +{ + u8 *pos, qc = 0; + size_t aad_len; + u16 fc; + int a4_included, qc_included; + u8 aad[2 * AES_BLOCK_LEN]; + + fc = le16_to_cpu(hdr->frame_control); + a4_included = ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS)); + qc_included = ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x08)); + aad_len = 22; + if (a4_included) + aad_len += 6; + if (qc_included) { + pos = (u8 *) &hdr->addr4; + if (a4_included) + pos += 6; + qc = *pos & 0x0f; + aad_len += 2; + } + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + b0[1] = qc; + memcpy(b0 + 2, hdr->addr2, ETH_ALEN); + memcpy(b0 + 8, pn, CCMP_PN_LEN); + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + pos = (u8 *) hdr; + aad[0] = 0; /* aad_len >> 8 */ + aad[1] = aad_len & 0xff; + aad[2] = pos[0] & 0x8f; + aad[3] = pos[1] & 0xc7; + memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); + pos = (u8 *) &hdr->seq_ctrl; + aad[22] = pos[0] & 0x0f; + aad[23] = 0; /* all bits masked */ + memset(aad + 24, 0, 8); + if (a4_included) + memcpy(aad + 24, hdr->addr4, ETH_ALEN); + if (qc_included) { + aad[a4_included ? 30 : 24] = qc; + /* rest of QC masked */ + } + + /* Start with the first block and AAD */ + hostap_ccmp_aes_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + hostap_ccmp_aes_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + hostap_ccmp_aes_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + hostap_ccmp_aes_encrypt(tfm, b0, s0); +} + + +static int hostap_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_ccmp_data *key = priv; + int data_len, i, blocks, last, len; + u8 *pos, *mic; + struct hostap_ieee80211_hdr *hdr; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; + + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + mic = skb_put(skb, CCMP_MIC_LEN); + + i = CCMP_PN_LEN - 1; + while (i >= 0) { + key->tx_pn[i]++; + if (key->tx_pn[i] != 0) + break; + i--; + } + + *pos++ = key->tx_pn[5]; + *pos++ = key->tx_pn[4]; + *pos++ = 0; + *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = key->tx_pn[3]; + *pos++ = key->tx_pn[2]; + *pos++ = key->tx_pn[1]; + *pos++ = key->tx_pn[0]; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Authentication */ + xor_block(b, pos, len); + hostap_ccmp_aes_encrypt(key->tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + hostap_ccmp_aes_encrypt(key->tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s0[i]; + + return 0; +} + + +static int hostap_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_ccmp_data *key = priv; + u8 keyidx, *pos; + struct hostap_ieee80211_hdr *hdr; + u8 *b0 = key->rx_b0; + u8 *b = key->rx_b; + u8 *a = key->rx_a; + u8 pn[6]; + int i, blocks, last, len; + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; + u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; + + if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { + key->dot11RSNAStatsCCMPFormatErrors++; + return -1; + } + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet without ExtIV" + " flag from " MACSTR "\n", MAC2STR(hdr->addr2)); + } + key->dot11RSNAStatsCCMPFormatErrors++; + return -2; + } + keyidx >>= 6; + if (key->key_idx != keyidx) { + printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); + return -6; + } + if (!key->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet from " MACSTR + " with keyid=%d that does not have a configured" + " key\n", MAC2STR(hdr->addr2), keyidx); + } + return -3; + } + + pn[0] = pos[7]; + pn[1] = pos[6]; + pn[2] = pos[5]; + pn[3] = pos[4]; + pn[4] = pos[1]; + pn[5] = pos[0]; + pos += 8; + + if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=" MACSTR + " previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + MAC2STR(hdr->addr2), MAC2STR(key->rx_pn), + MAC2STR(pn)); + } + key->dot11RSNAStatsCCMPReplays++; + return -4; + } + + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Decrypt, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + hostap_ccmp_aes_encrypt(key->tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + hostap_ccmp_aes_encrypt(key->tfm, a, a); + pos += len; + } + + if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: decrypt failed: STA=" + MACSTR "\n", MAC2STR(hdr->addr2)); + } + key->dot11RSNAStatsCCMPDecryptErrors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + + /* Remove hdr and MIC */ + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, CCMP_HDR_LEN); + skb_trim(skb, skb->len - CCMP_MIC_LEN); + + return keyidx; +} + + +static int hostap_ccmp_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_ccmp_data *data = priv; + int keyidx; + struct crypto_tfm *tfm = data->tfm; + + keyidx = data->key_idx; + memset(data, 0, sizeof(*data)); + data->key_idx = keyidx; + data->tfm = tfm; + if (len == CCMP_TK_LEN) { + memcpy(data->key, key, CCMP_TK_LEN); + data->key_set = 1; + if (seq) { + data->rx_pn[0] = seq[5]; + data->rx_pn[1] = seq[4]; + data->rx_pn[2] = seq[3]; + data->rx_pn[3] = seq[2]; + data->rx_pn[4] = seq[1]; + data->rx_pn[5] = seq[0]; + } + crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); + } else if (len == 0) { + data->key_set = 0; + } else + return -1; + + return 0; +} + + +static int hostap_ccmp_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_ccmp_data *data = priv; + + if (len < CCMP_TK_LEN) + return -1; + + if (!data->key_set) + return 0; + memcpy(key, data->key, CCMP_TK_LEN); + + if (seq) { + seq[0] = data->tx_pn[5]; + seq[1] = data->tx_pn[4]; + seq[2] = data->tx_pn[3]; + seq[3] = data->tx_pn[2]; + seq[4] = data->tx_pn[1]; + seq[5] = data->tx_pn[0]; + } + + return CCMP_TK_LEN; +} + + +static char * hostap_ccmp_print_stats(char *p, void *priv) +{ + struct hostap_ccmp_data *ccmp = priv; + p += sprintf(p, "key[%d] alg=CCMP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "format_errors=%d replays=%d decrypt_errors=%d\n", + ccmp->key_idx, ccmp->key_set, + MAC2STR(ccmp->tx_pn), MAC2STR(ccmp->rx_pn), + ccmp->dot11RSNAStatsCCMPFormatErrors, + ccmp->dot11RSNAStatsCCMPReplays, + ccmp->dot11RSNAStatsCCMPDecryptErrors); + + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_ccmp = { + .name = "CCMP", + .init = hostap_ccmp_init, + .deinit = hostap_ccmp_deinit, + .encrypt_mpdu = hostap_ccmp_encrypt, + .decrypt_mpdu = hostap_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = hostap_ccmp_set_key, + .get_key = hostap_ccmp_get_key, + .print_stats = hostap_ccmp_print_stats, + .extra_prefix_len = CCMP_HDR_LEN, + .extra_postfix_len = CCMP_MIC_LEN +}; + + +static int __init hostap_crypto_ccmp_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_ccmp) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_ccmp_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_ccmp); +} + + +module_init(hostap_crypto_ccmp_init); +module_exit(hostap_crypto_ccmp_exit); diff -Nru a/drivers/net/wireless/hostap/hostap_crypt_tkip.c b/drivers/net/wireless/hostap/hostap_crypt_tkip.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_crypt_tkip.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,696 @@ +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" +#include "hostap_wlan.h" +#include "hostap_80211.h" +#include "hostap_config.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif +#include +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: TKIP"); +MODULE_LICENSE("GPL"); + + +struct hostap_tkip_data { +#define TKIP_KEY_LEN 32 + u8 key[TKIP_KEY_LEN]; + int key_set; + + u32 tx_iv32; + u16 tx_iv16; + u16 tx_ttak[5]; + int tx_phase1_done; + + u32 rx_iv32; + u16 rx_iv16; + u16 rx_ttak[5]; + int rx_phase1_done; + u32 rx_iv32_new; + u16 rx_iv16_new; + + u32 dot11RSNAStatsTKIPReplays; + u32 dot11RSNAStatsTKIPICVErrors; + u32 dot11RSNAStatsTKIPLocalMICFailures; + + int key_idx; + + struct crypto_tfm *tfm_arc4; + struct crypto_tfm *tfm_michael; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; +}; + + +static void * hostap_tkip_init(int key_idx) +{ + struct hostap_tkip_data *priv; + + if (!try_module_get(THIS_MODULE)) + return NULL; + + priv = (struct hostap_tkip_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tfm_arc4 == NULL) { + printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) { + printk(KERN_DEBUG "hostap_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm_michael) + crypto_free_tfm(priv->tfm_michael); + if (priv->tfm_arc4) + crypto_free_tfm(priv->tfm_arc4); + kfree(priv); + } + module_put(THIS_MODULE); + return NULL; +} + + +static void hostap_tkip_deinit(void *priv) +{ + struct hostap_tkip_data *_priv = priv; + if (_priv && _priv->tfm_michael) + crypto_free_tfm(_priv->tfm_michael); + if (_priv && _priv->tfm_arc4) + crypto_free_tfm(_priv->tfm_arc4); + kfree(priv); + module_put(THIS_MODULE); +} + + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + + +static inline u16 Mk16_le(u16 *v) +{ + return le16_to_cpu(*v); +} + + +static const u16 Sbox[256] = +{ + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + + +static int hostap_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + int len; + u8 rc4key[16], *pos, *icv; + struct hostap_ieee80211_hdr *hdr; + u32 crc; + struct scatterlist sg; + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; + icv = skb_put(skb, 4); + + *pos++ = rc4key[0]; + *pos++ = rc4key[1]; + *pos++ = rc4key[2]; + *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = tkey->tx_iv32 & 0xff; + *pos++ = (tkey->tx_iv32 >> 8) & 0xff; + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + + crc = ~crc32_le(~0, pos, len); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + return 0; +} + + +static int hostap_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 rc4key[16]; + u8 keyidx, *pos, icv[4]; + u32 iv32; + u16 iv16; + struct hostap_ieee80211_hdr *hdr; + u32 crc; + struct scatterlist sg; + int plen; + + if (skb->len < hdr_len + 8 + 4) + return -1; + + hdr = (struct hostap_ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet without ExtIV" + " flag from " MACSTR "\n", MAC2STR(hdr->addr2)); + } + return -2; + } + keyidx >>= 6; + if (tkey->key_idx != keyidx) { + printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); + return -6; + } + if (!tkey->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet from " MACSTR + " with keyid=%d that does not have a configured" + " key\n", MAC2STR(hdr->addr2), keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; + + if (iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=" MACSTR + " previous TSC %08x%04x received TSC " + "%08x%04x\n", MAC2STR(hdr->addr2), + tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); + } + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } + + if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { + tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); + tkey->rx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); + + plen = skb->len - hdr_len - 12; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + if (iv32 != tkey->rx_iv32) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + tkey->rx_phase1_done = 0; + } + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + MACSTR "\n", MAC2STR(hdr->addr2)); + } + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + + /* Update real counters only after Michael MIC verification has + * completed */ + tkey->rx_iv32_new = iv32; + tkey->rx_iv16_new = iv16; + + /* Remove IV and ICV */ + memmove(skb->data + 8, skb->data, hdr_len); + skb_pull(skb, 8); + skb_trim(skb, skb->len - 4); + + return keyidx; +} + + +static int michael_mic(struct hostap_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + struct scatterlist sg[2]; + + if (tkey->tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + crypto_digest_init(tkey->tfm_michael); + crypto_digest_setkey(tkey->tfm_michael, key, 8); + crypto_digest_update(tkey->tfm_michael, sg, 2); + crypto_digest_final(tkey->tfm_michael, mic); + + return 0; +} + + +static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr) +{ + struct hostap_ieee80211_hdr *hdr11; + + hdr11 = (struct hostap_ieee80211_hdr *) skb->data; + switch (le16_to_cpu(hdr11->frame_control) & + (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + case WLAN_FC_FROMDS: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ + break; + case WLAN_FC_FROMDS | WLAN_FC_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ + break; + case 0: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + } + + hdr[12] = 0; /* priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + + +static int hostap_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 *pos; + + if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { + printk(KERN_DEBUG "Invalid packet for Michael MIC add " + "(tailroom=%d hdr_len=%d skb->len=%d)\n", + skb_tailroom(skb), hdr_len, skb->len); + return -1; + } + + michael_mic_hdr(skb, tkey->tx_hdr); + pos = skb_put(skb, 8); + if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + + +static void hostap_michael_mic_failure(struct net_device *dev, + struct hostap_ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + char buf[128]; + + /* TODO: needed parameters: count, keyid, key type, src address, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + MACSTR ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC2STR(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); +} + + +static int hostap_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + u8 mic[8]; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + return -1; + if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { + struct hostap_ieee80211_hdr *hdr; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + printk(KERN_DEBUG "%s: Michael MIC verification failed for " + "MSDU from " MACSTR " keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", MAC2STR(hdr->addr2), + keyidx); + if (skb->dev) + hostap_michael_mic_failure(skb->dev, hdr, keyidx); + tkey->dot11RSNAStatsTKIPLocalMICFailures++; + return -1; + } + + /* Update TSC counters for RX now that the packet verification has + * completed. */ + tkey->rx_iv32 = tkey->rx_iv32_new; + tkey->rx_iv16 = tkey->rx_iv16_new; + + skb_trim(skb, skb->len - 8); + + return 0; +} + + +static int hostap_tkip_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + int keyidx; + struct crypto_tfm *tfm = tkey->tfm_michael; + struct crypto_tfm *tfm2 = tkey->tfm_arc4; + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + tkey->tfm_michael = tfm; + tkey->tfm_arc4 = tfm2; + if (len == TKIP_KEY_LEN) { + memcpy(tkey->key, key, TKIP_KEY_LEN); + tkey->key_set = 1; + tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ + if (seq) { + tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | + (seq[3] << 8) | seq[2]; + tkey->rx_iv16 = (seq[1] << 8) | seq[0]; + } + } else if (len == 0) { + tkey->key_set = 0; + } else + return -1; + + return 0; +} + + +static int hostap_tkip_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct hostap_tkip_data *tkey = priv; + + if (len < TKIP_KEY_LEN) + return -1; + + if (!tkey->key_set) + return 0; + memcpy(key, tkey->key, TKIP_KEY_LEN); + + if (seq) { + /* Return the sequence number of the last transmitted frame. */ + u16 iv16 = tkey->tx_iv16; + u32 iv32 = tkey->tx_iv32; + if (iv16 == 0) + iv32--; + iv16--; + seq[0] = tkey->tx_iv16; + seq[1] = tkey->tx_iv16 >> 8; + seq[2] = tkey->tx_iv32; + seq[3] = tkey->tx_iv32 >> 8; + seq[4] = tkey->tx_iv32 >> 16; + seq[5] = tkey->tx_iv32 >> 24; + } + + return TKIP_KEY_LEN; +} + + +static char * hostap_tkip_print_stats(char *p, void *priv) +{ + struct hostap_tkip_data *tkip = priv; + p += sprintf(p, "key[%d] alg=TKIP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "replays=%d icv_errors=%d local_mic_failures=%d\n", + tkip->key_idx, tkip->key_set, + (tkip->tx_iv32 >> 24) & 0xff, + (tkip->tx_iv32 >> 16) & 0xff, + (tkip->tx_iv32 >> 8) & 0xff, + tkip->tx_iv32 & 0xff, + (tkip->tx_iv16 >> 8) & 0xff, + tkip->tx_iv16 & 0xff, + (tkip->rx_iv32 >> 24) & 0xff, + (tkip->rx_iv32 >> 16) & 0xff, + (tkip->rx_iv32 >> 8) & 0xff, + tkip->rx_iv32 & 0xff, + (tkip->rx_iv16 >> 8) & 0xff, + tkip->rx_iv16 & 0xff, + tkip->dot11RSNAStatsTKIPReplays, + tkip->dot11RSNAStatsTKIPICVErrors, + tkip->dot11RSNAStatsTKIPLocalMICFailures); + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_tkip = { + .name = "TKIP", + .init = hostap_tkip_init, + .deinit = hostap_tkip_deinit, + .encrypt_mpdu = hostap_tkip_encrypt, + .decrypt_mpdu = hostap_tkip_decrypt, + .encrypt_msdu = hostap_michael_mic_add, + .decrypt_msdu = hostap_michael_mic_verify, + .set_key = hostap_tkip_set_key, + .get_key = hostap_tkip_get_key, + .print_stats = hostap_tkip_print_stats, + .extra_prefix_len = 4 + 4 /* IV + ExtIV */, + .extra_postfix_len = 8 + 4 /* MIC + ICV */ +}; + + +static int __init hostap_crypto_tkip_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_tkip) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_tkip_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_tkip); +} + + +module_init(hostap_crypto_tkip_init); +module_exit(hostap_crypto_tkip_exit); diff -Nru a/drivers/net/wireless/hostap/hostap_crypt_wep.c b/drivers/net/wireless/hostap/hostap_crypt_wep.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_crypt_wep.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,281 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hostap_crypt.h" + +#ifndef CONFIG_CRYPTO +#error CONFIG_CRYPTO is required to build this module. +#endif +#include +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: WEP"); +MODULE_LICENSE("GPL"); + + +struct prism2_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; + struct crypto_tfm *tfm; +}; + + +static void * prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + + if (!try_module_get(THIS_MODULE)) + return NULL; + + priv = (struct prism2_wep_data *) kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = keyidx; + + priv->tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "hostap_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + module_put(THIS_MODULE); + return NULL; +} + + +static void prism2_wep_deinit(void *priv) +{ + struct prism2_wep_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + kfree(priv); + module_put(THIS_MODULE); +} + + +/* Perform WEP encryption on given skb that has at least 4 bytes of headroom + * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, + * so the payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, len; + u8 key[WEP_KEY_LEN + 3]; + u8 *pos, *icv; + struct scatterlist sg; + + if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + len = skb->len - hdr_len; + pos = skb_push(skb, 4); + memmove(pos, pos + 4, hdr_len); + pos += hdr_len; + + klen = 3 + wep->key_len; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + *pos++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + + return 0; +} + + +/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed. + */ +static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos, icv[4]; + struct scatterlist sg; + + if (skb->len < hdr_len + 8) + return -1; + + pos = skb->data + hdr_len; + key[0] = *pos++; + key[1] = *pos++; + key[2] = *pos++; + keyidx = *pos++ >> 6; + if (keyidx != wep->key_idx) + return -1; + + klen = 3 + wep->key_len; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + /* ICV mismatch - drop frame */ + return -2; + } + + /* Remove IV and ICV */ + memmove(skb->data + 4, skb->data, hdr_len); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + + return 0; +} + + +static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->key, key, len); + wep->key_len = len; + + return 0; +} + + +static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + + +static char * prism2_wep_print_stats(char *p, void *priv) +{ + struct prism2_wep_data *wep = priv; + p += sprintf(p, "key[%d] alg=WEP len=%d\n", + wep->key_idx, wep->key_len); + return p; +} + + +static struct hostap_crypto_ops hostap_crypt_wep = { + .name = "WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt_mpdu = prism2_wep_encrypt, + .decrypt_mpdu = prism2_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .print_stats = prism2_wep_print_stats, + .extra_prefix_len = 4 /* IV */, + .extra_postfix_len = 4 /* ICV */ +}; + + +static int __init hostap_crypto_wep_init(void) +{ + if (hostap_register_crypto_ops(&hostap_crypt_wep) < 0) + return -1; + + return 0; +} + + +static void __exit hostap_crypto_wep_exit(void) +{ + hostap_unregister_crypto_ops(&hostap_crypt_wep); +} + + +module_init(hostap_crypto_wep_init); +module_exit(hostap_crypto_wep_exit); diff -Nru a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_cs.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,785 @@ +#define PRISM2_PCCARD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static dev_info_t dev_info = "hostap_cs"; +static dev_link_t *dev_list = NULL; + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PC Card)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PC Card)"); +MODULE_LICENSE("GPL"); + + +static int irq_mask = 0xdeb8; +module_param(irq_mask, int, 0444); + +static int irq_list[4] = { -1 }; +module_param_array(irq_list, int, NULL, 0444); + +static int ignore_cis_vcc; +module_param(ignore_cis_vcc, int, 0444); +MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry"); + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + + +static void prism2_detach(dev_link_t *link); +static void prism2_release(u_long arg); +static int prism2_event(event_t event, int priority, + event_callback_args_t *args); + + +static int prism2_pccard_card_present(local_info_t *local) +{ + if (local->link != NULL && + ((local->link->state & (DEV_PRESENT | DEV_CONFIG)) == + (DEV_PRESENT | DEV_CONFIG))) + return 1; + return 0; +} + +static void prism2_pccard_cor_sreset(local_info_t *local) +{ + int res; + conf_reg_t reg; + + if (!prism2_pccard_card_present(local)) + return; + + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 1 (%d)\n", + res); + return; + } + printk(KERN_DEBUG "prism2_pccard_cor_sreset: original COR %02x\n", + reg.Value); + + reg.Action = CS_WRITE; + reg.Value |= COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 2 (%d)\n", + res); + return; + } + + mdelay(2); + + reg.Value &= ~COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_cor_sreset failed 3 (%d)\n", + res); + return; + } + + mdelay(2); +} + + +static void prism2_pccard_genesis_reset(local_info_t *local, int hcr) +{ + int res; + conf_reg_t reg; + int old_cor; + + if (!prism2_pccard_card_present(local)) + return; + + reg.Function = 0; + reg.Action = CS_READ; + reg.Offset = CISREG_COR; + reg.Value = 0; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 1 " + "(%d)\n", res); + return; + } + printk(KERN_DEBUG "prism2_pccard_genesis_sreset: original COR %02x\n", + reg.Value); + old_cor = reg.Value; + + reg.Action = CS_WRITE; + reg.Value |= COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 2 " + "(%d)\n", res); + return; + } + + mdelay(10); + + /* Setup Genesis mode */ + reg.Action = CS_WRITE; + reg.Value = hcr; + reg.Offset = CISREG_CCSR; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 3 " + "(%d)\n", res); + return; + } + mdelay(10); + + reg.Action = CS_WRITE; + reg.Offset = CISREG_COR; + reg.Value = old_cor & ~COR_SOFT_RESET; + res = pcmcia_access_configuration_register(local->link->handle, ®); + if (res != CS_SUCCESS) { + printk(KERN_DEBUG "prism2_pccard_genesis_sreset failed 4 " + "(%d)\n", res); + return; + } + + mdelay(10); +} + + +static int prism2_pccard_dev_open(local_info_t *local) +{ + local->link->open++; + return 0; +} + + +static int prism2_pccard_dev_close(local_info_t *local) +{ + if (local == NULL || local->link == NULL) + return 1; + + if (!local->link->open) { + printk(KERN_WARNING "%s: prism2_pccard_dev_close(): " + "link not open?!\n", local->dev->name); + return 1; + } + + local->link->open--; + + return 0; +} + + +static struct prism2_helper_functions prism2_pccard_funcs = +{ + .card_present = prism2_pccard_card_present, + .cor_sreset = prism2_pccard_cor_sreset, + .dev_open = prism2_pccard_dev_open, + .dev_close = prism2_pccard_dev_close, + .genesis_reset = prism2_pccard_genesis_reset, + .hw_type = HOSTAP_HW_PCCARD, +}; + + +/* allocate local data and register with CardServices + * initialize dev_link structure, but do not configure the card yet */ +static dev_link_t *prism2_attach(void) +{ + dev_link_t *link; + client_reg_t client_reg; + int ret; + + link = kmalloc(sizeof(dev_link_t), GFP_KERNEL); + if (link == NULL) + return NULL; + + memset(link, 0, sizeof(dev_link_t)); + + PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info); + link->conf.Vcc = 33; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* register with CardServices */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT; + client_reg.EventMask = CS_EVENT_CARD_INSERTION | + CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &prism2_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = pcmcia_register_client(&link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + prism2_detach(link); + return NULL; + } + return link; +} + + +static void prism2_detach(dev_link_t *link) +{ + dev_link_t **linkp; + + PDEBUG(DEBUG_FLOW, "prism2_detach\n"); + + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) + break; + if (*linkp == NULL) { + printk(KERN_WARNING "%s: Attempt to detach non-existing " + "PCMCIA client\n", dev_info); + return; + } + + if (link->state & DEV_CONFIG) { + prism2_release((u_long)link); + } + + if (link->handle) { + int res = pcmcia_deregister_client(link->handle); + if (res) { + printk("CardService(DeregisterClient) => %d\n", res); + cs_error(link->handle, DeregisterClient, res); + } + } + + *linkp = link->next; + /* release net devices */ + if (link->priv) { + prism2_free_local_data((struct net_device *) link->priv); + + } + kfree(link); +} + + +#define CS_CHECK(fn, ret) \ +do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) + +#define CFG_CHECK2(fn, retf) \ +do { int ret = (retf); \ +if (ret != 0) { \ + PDEBUG(DEBUG_EXTRA, "CardServices(" #fn ") returned %d\n", ret); \ + cs_error(link->handle, fn, ret); \ + goto next_entry; \ +} \ +} while (0) + + +/* run after a CARD_INSERTION event is received to configure the PCMCIA + * socket and make the device available to the system */ +static int prism2_config(dev_link_t *link) +{ + struct net_device *dev; + struct hostap_interface *iface; + local_info_t *local; + int ret; + tuple_t tuple; + cisparse_t parse; + int last_fn, last_ret; + u_char buf[64]; + config_info_t conf; + cistpl_cftable_entry_t dflt = { 0 }; + + PDEBUG(DEBUG_FLOW, "prism2_config()\n"); + + tuple.DesiredTuple = CISTPL_CONFIG; + tuple.Attributes = 0; + tuple.TupleData = buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); + CS_CHECK(GetTupleData, pcmcia_get_tuple_data(link->handle, &tuple)); + CS_CHECK(ParseTuple, pcmcia_parse_tuple(link->handle, &tuple, &parse)); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + CS_CHECK(GetConfigurationInfo, + pcmcia_get_configuration_info(link->handle, &conf)); + PDEBUG(DEBUG_HW, "%s: %s Vcc=%d (from config)\n", dev_info, + ignore_cis_vcc ? "ignoring" : "setting", conf.Vcc); + link->conf.Vcc = conf.Vcc; + + /* Look for an appropriate configuration table entry in the CIS */ + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link->handle, &tuple)); + for (;;) { + cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); + CFG_CHECK2(GetTupleData, + pcmcia_get_tuple_data(link->handle, &tuple)); + CFG_CHECK2(ParseTuple, + pcmcia_parse_tuple(link->handle, &tuple, &parse)); + + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) + dflt = *cfg; + if (cfg->index == 0) + goto next_entry; + link->conf.ConfigIndex = cfg->index; + PDEBUG(DEBUG_EXTRA, "Checking CFTABLE_ENTRY 0x%02X " + "(default 0x%02X)\n", cfg->index, dflt.index); + + /* Does this card need audio output? */ + if (cfg->flags & CISTPL_CFTABLE_AUDIO) { + link->conf.Attributes |= CONF_ENABLE_SPKR; + link->conf.Status = CCSR_AUDIO_ENA; + } + + /* Use power settings for Vcc and Vpp if present */ + /* Note that the CIS values need to be rescaled */ + if (cfg->vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != cfg->vcc.param[CISTPL_POWER_VNOM] / + 10000 && !ignore_cis_vcc) { + PDEBUG(DEBUG_EXTRA, " Vcc mismatch - skipping" + " this entry\n"); + goto next_entry; + } + } else if (dflt.vcc.present & (1 << CISTPL_POWER_VNOM)) { + if (conf.Vcc != dflt.vcc.param[CISTPL_POWER_VNOM] / + 10000 && !ignore_cis_vcc) { + PDEBUG(DEBUG_EXTRA, " Vcc (default) mismatch " + "- skipping this entry\n"); + goto next_entry; + } + } + + if (cfg->vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM] / 10000; + else if (dflt.vpp1.present & (1 << CISTPL_POWER_VNOM)) + link->conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM] / 10000; + + /* Do we need to allocate an interrupt? */ + if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) + link->conf.Attributes |= CONF_ENABLE_IRQ; + else if (!(link->conf.Attributes & CONF_ENABLE_IRQ)) { + /* At least Compaq WL200 does not have IRQInfo1 set, + * but it does not work without interrupts.. */ + printk("Config has no IRQ info, but trying to enable " + "IRQ anyway..\n"); + link->conf.Attributes |= CONF_ENABLE_IRQ; + } + + /* IO window settings */ + PDEBUG(DEBUG_EXTRA, "IO window settings: cfg->io.nwin=%d " + "dflt.io.nwin=%d\n", + cfg->io.nwin, dflt.io.nwin); + link->io.NumPorts1 = link->io.NumPorts2 = 0; + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + PDEBUG(DEBUG_EXTRA, "io->flags = 0x%04X, " + "io.base=0x%04x, len=%d\n", io->flags, + io->win[0].base, io->win[0].len); + if (!(io->flags & CISTPL_IO_8BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_16; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = io->flags & + CISTPL_IO_LINES_MASK; + link->io.BasePort1 = io->win[0].base; + link->io.NumPorts1 = io->win[0].len; + if (io->nwin > 1) { + link->io.Attributes2 = link->io.Attributes1; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = io->win[1].len; + } + } + + /* This reserves IO space but doesn't actually enable it */ + CFG_CHECK2(RequestIO, + pcmcia_request_io(link->handle, &link->io)); + + /* This configuration table entry is OK */ + break; + + next_entry: + CS_CHECK(GetNextTuple, + pcmcia_get_next_tuple(link->handle, &tuple)); + } + + /* Need to allocate net_device before requesting IRQ handler */ + dev = prism2_init_local_data(&prism2_pccard_funcs, 0); + if (dev == NULL) + goto failed; + link->priv = dev; + + /* + * Allocate an interrupt line. Note that this does not assign a + * handler to the interrupt, unless the 'Handler' member of the + * irq structure is initialized. + */ + if (link->conf.Attributes & CONF_ENABLE_IRQ) { + int i; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; + link->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->irq.Handler = prism2_interrupt; + link->irq.Instance = dev; + CS_CHECK(RequestIRQ, + pcmcia_request_irq(link->handle, &link->irq)); + } + + /* + * This actually configures the PCMCIA socket -- setting up + * the I/O windows and the interrupt mapping, and putting the + * card and host interface into "Memory and IO" mode. + */ + CS_CHECK(RequestConfiguration, + pcmcia_request_configuration(link->handle, &link->conf)); + + dev->irq = link->irq.AssignedIRQ; + dev->base_addr = link->io.BasePort1; + + /* Finally, report what we've done */ + printk(KERN_INFO "%s: index 0x%02x: Vcc %d.%d", + dev_info, link->conf.ConfigIndex, + link->conf.Vcc / 10, link->conf.Vcc % 10); + if (link->conf.Vpp1) + printk(", Vpp %d.%d", link->conf.Vpp1 / 10, + link->conf.Vpp1 % 10); + if (link->conf.Attributes & CONF_ENABLE_IRQ) + printk(", irq %d", link->irq.AssignedIRQ); + if (link->io.NumPorts1) + printk(", io 0x%04x-0x%04x", link->io.BasePort1, + link->io.BasePort1+link->io.NumPorts1-1); + if (link->io.NumPorts2) + printk(" & 0x%04x-0x%04x", link->io.BasePort2, + link->io.BasePort2+link->io.NumPorts2-1); + printk("\n"); + + link->state |= DEV_CONFIG; + link->state &= ~DEV_CONFIG_PENDING; + + iface = netdev_priv(dev); + local = iface->local; + local->link = link; + strcpy(local->node.dev_name, dev->name); + link->dev = &local->node; + + local->shutdown = 0; + + ret = prism2_hw_config(dev, 1); + if (!ret) { + ret = hostap_hw_ready(dev); + if (ret == 0 && local->ddev) + strcpy(local->node.dev_name, local->ddev->name); + } + return ret; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + + failed: + prism2_release((u_long)link); + return 1; +} + + +static void prism2_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + + PDEBUG(DEBUG_FLOW, "prism2_release\n"); + + if (link->priv) { + struct net_device *dev = link->priv; + struct hostap_interface *iface; + + iface = netdev_priv(dev); + if (link->state & DEV_CONFIG) + prism2_hw_shutdown(dev, 0); + iface->local->shutdown = 1; + } + + if (link->win) + pcmcia_release_window(link->win); + pcmcia_release_configuration(link->handle); + if (link->io.NumPorts1) + pcmcia_release_io(link->handle, &link->io); + if (link->irq.AssignedIRQ) + pcmcia_release_irq(link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + + PDEBUG(DEBUG_FLOW, "release - done\n"); +} + + +static int prism2_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + struct net_device *dev = (struct net_device *) link->priv; + + switch (event) { + case CS_EVENT_CARD_INSERTION: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_INSERTION\n", dev_info); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + if (prism2_config(link)) { + PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n"); + } + break; + + case CS_EVENT_CARD_REMOVAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_REMOVAL\n", dev_info); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) { + netif_stop_queue(dev); + netif_device_detach(dev); + prism2_release((u_long) link); + } + break; + + case CS_EVENT_PM_SUSPEND: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_SUSPEND\n", dev_info); + link->state |= DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_RESET_PHYSICAL: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_RESET_PHYSICAL\n", dev_info); + if (link->state & DEV_CONFIG) { + if (link->open) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + pcmcia_release_configuration(link->handle); + } + break; + + case CS_EVENT_PM_RESUME: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_PM_RESUME\n", dev_info); + link->state &= ~DEV_SUSPEND; + /* fall through */ + + case CS_EVENT_CARD_RESET: + PDEBUG(DEBUG_EXTRA, "%s: CS_EVENT_CARD_RESET\n", dev_info); + if (link->state & DEV_CONFIG) { + pcmcia_request_configuration(link->handle, + &link->conf); + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, link->open ? 0 : 1); + if (link->open) { + netif_device_attach(dev); + netif_start_queue(dev); + } + } + break; + + default: + PDEBUG(DEBUG_EXTRA, "%s: prism2_event() - unknown event %d\n", + dev_info, event); + break; + } + return 0; +} + + +static struct pcmcia_driver hostap_driver = { + .drv = { + .name = "hostap_cs", + }, + .attach = prism2_attach, + .detach = prism2_detach, + .owner = THIS_MODULE, +}; + +static int __init init_prism2_pccard(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + return pcmcia_register_driver(&hostap_driver); +} + +static void __exit exit_prism2_pccard(void) +{ + pcmcia_unregister_driver(&hostap_driver); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_pccard); +module_exit(exit_prism2_pccard); diff -Nru a/drivers/net/wireless/hostap/hostap_download.c b/drivers/net/wireless/hostap/hostap_download.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_download.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,761 @@ +static int prism2_enable_aux_port(struct net_device *dev, int enable) +{ + u16 val, reg; + int i, tries; + unsigned long flags; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + if (enable) { + PDEBUG(DEBUG_EXTRA2, "%s: no PRI f/w - assuming Aux " + "port is already enabled\n", dev->name); + } + return 0; + } + + spin_lock_irqsave(&local->cmdlock, flags); + + /* wait until busy bit is clear */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + spin_unlock_irqrestore(&local->cmdlock, flags); + printk("%s: prism2_enable_aux_port - timeout - reg=0x%04x\n", + dev->name, reg); + return -ETIMEDOUT; + } + + val = HFA384X_INW(HFA384X_CONTROL_OFF); + + if (enable) { + HFA384X_OUTW(HFA384X_AUX_MAGIC0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(HFA384X_AUX_MAGIC2, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_DISABLED) + printk("prism2_enable_aux_port: was not disabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_ENABLE; + } else { + HFA384X_OUTW(0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + + if ((val & HFA384X_AUX_PORT_MASK) != HFA384X_AUX_PORT_ENABLED) + printk("prism2_enable_aux_port: was not enabled!?\n"); + val &= ~HFA384X_AUX_PORT_MASK; + val |= HFA384X_AUX_PORT_DISABLE; + } + HFA384X_OUTW(val, HFA384X_CONTROL_OFF); + + udelay(5); + + i = 10000; + while (i > 0) { + val = HFA384X_INW(HFA384X_CONTROL_OFF); + val &= HFA384X_AUX_PORT_MASK; + + if ((enable && val == HFA384X_AUX_PORT_ENABLED) || + (!enable && val == HFA384X_AUX_PORT_DISABLED)) + break; + + udelay(10); + i--; + } + + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (i == 0) { + printk("prism2_enable_aux_port(%d) timed out\n", + enable); + return -ETIMEDOUT; + } + + return 0; +} + + +static int hfa384x_from_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + *pos++ = HFA384X_INW_DATA(HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_INSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int hfa384x_to_aux(struct net_device *dev, unsigned int addr, int len, + void *buf) +{ + u16 page, offset; + if (addr & 1 || len & 1) + return -1; + + page = addr >> 7; + offset = addr & 0x7f; + + HFA384X_OUTW(page, HFA384X_AUXPAGE_OFF); + HFA384X_OUTW(offset, HFA384X_AUXOFFSET_OFF); + + udelay(5); + +#ifdef PRISM2_PCI + { + u16 *pos = (u16 *) buf; + while (len > 0) { + HFA384X_OUTW_DATA(*pos++, HFA384X_AUXDATA_OFF); + len -= 2; + } + } +#else /* PRISM2_PCI */ + HFA384X_OUTSW(HFA384X_AUXDATA_OFF, buf, len / 2); +#endif /* PRISM2_PCI */ + + return 0; +} + + +static int prism2_pda_ok(u8 *buf) +{ + u16 *pda = (u16 *) buf; + int pos; + u16 len, pdr; + + if (buf[0] == 0xff && buf[1] == 0x00 && buf[2] == 0xff && + buf[3] == 0x00) + return 0; + + pos = 0; + while (pos + 1 < PRISM2_PDA_SIZE / 2) { + len = le16_to_cpu(pda[pos]); + pdr = le16_to_cpu(pda[pos + 1]); + if (len == 0 || pos + len > PRISM2_PDA_SIZE / 2) + return 0; + + if (pdr == 0x0000 && len == 2) { + /* PDA end found */ + return 1; + } + + pos += len + 1; + } + + return 0; +} + + +static int prism2_download_aux_dump(struct net_device *dev, + unsigned int addr, int len, u8 *buf) +{ + int res; + + prism2_enable_aux_port(dev, 1); + res = hfa384x_from_aux(dev, addr, len, buf); + prism2_enable_aux_port(dev, 0); + if (res) + return -1; + + return 0; +} + + +static u8 * prism2_read_pda(struct net_device *dev) +{ + u8 *buf; + int res, i, found = 0; +#define NUM_PDA_ADDRS 4 + unsigned int pda_addr[NUM_PDA_ADDRS] = { + 0x7f0000 /* others than HFA3841 */, + 0x3f0000 /* HFA3841 */, + 0x390000 /* apparently used in older cards */, + 0x7f0002 /* Intel PRO/Wireless 2011B (PCI) */, + }; + + buf = (u8 *) kmalloc(PRISM2_PDA_SIZE, GFP_KERNEL); + if (buf == NULL) + return NULL; + + /* Note: wlan card should be in initial state (just after init cmd) + * and no other operations should be performed concurrently. */ + + prism2_enable_aux_port(dev, 1); + + for (i = 0; i < NUM_PDA_ADDRS; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: trying to read PDA from 0x%08x", + dev->name, pda_addr[i]); + res = hfa384x_from_aux(dev, pda_addr[i], PRISM2_PDA_SIZE, buf); + if (res) + continue; + if (res == 0 && prism2_pda_ok(buf)) { + PDEBUG2(DEBUG_EXTRA2, ": OK\n"); + found = 1; + break; + } else { + PDEBUG2(DEBUG_EXTRA2, ": failed\n"); + } + } + + prism2_enable_aux_port(dev, 0); + + if (!found) { + printk(KERN_DEBUG "%s: valid PDA not found\n", dev->name); + kfree(buf); + buf = NULL; + } + + return buf; +} + + +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + u16 param0, param1; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + local->hw_downloading = 1; + if (local->pri_only) { + hfa384x_disable_interrupts(dev); + } else { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + param0 = param->start_addr & 0xffff; + param1 = param->start_addr >> 16; + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -1; + goto out; + } + } + + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), param0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + /* ProgMode disable causes the hardware to restart itself from the + * given starting address. Give hw some time and ACK command just in + * case restart did not happen. */ + mdelay(5); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after RAM " + "download failed\n", dev->name); + ret = -1; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +static int prism2_enable_genesis(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + u8 initseq[4] = { 0x00, 0xe1, 0xa1, 0xff }; + u8 readbuf[4]; + + printk(KERN_DEBUG "%s: test Genesis mode with HCR 0x%02x\n", + dev->name, hcr); + local->func->cor_sreset(local); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + local->func->genesis_reset(local, hcr); + + /* Readback test */ + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + hfa384x_to_aux(dev, 0x7e0038, sizeof(initseq), initseq); + hfa384x_from_aux(dev, 0x7e0038, sizeof(readbuf), readbuf); + + if (memcmp(initseq, readbuf, sizeof(initseq)) == 0) { + printk(KERN_DEBUG "Readback test succeeded, HCR 0x%02x\n", + hcr); + return 0; + } else { + printk(KERN_DEBUG "Readback test failed, HCR 0x%02x " + "write %02x %02x %02x %02x read %02x %02x %02x %02x\n", + hcr, initseq[0], initseq[1], initseq[2], initseq[3], + readbuf[0], readbuf[1], readbuf[2], readbuf[3]); + return 1; + } +} + + +static int prism2_get_ram_size(local_info_t *local) +{ + int ret; + + /* Try to enable genesis mode; 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) + ret = 8; + else if (prism2_enable_genesis(local, 0x0f) == 0) + ret = 16; + else + ret = -1; + + /* Disable genesis mode */ + local->func->genesis_reset(local, ret == 16 ? 0x07 : 0x17); + + return ret; +} + + +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param) +{ + struct net_device *dev = local->dev; + int ram16 = 0, i; + int ret = 0; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -EBUSY; + } + + if (!local->func->genesis_reset || !local->func->cor_sreset) { + printk(KERN_INFO "%s: Genesis mode downloading not supported " + "with this hwmodel\n", dev->name); + return -EOPNOTSUPP; + } + + local->hw_downloading = 1; + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_DEBUG "%s: failed to enable AUX port\n", + dev->name); + ret = -EIO; + goto out; + } + + if (local->sram_type == -1) { + /* 0x1F for x8 SRAM or 0x0F for x16 SRAM */ + if (prism2_enable_genesis(local, 0x1f) == 0) { + ram16 = 0; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x8 " + "SRAM\n", dev->name); + } else if (prism2_enable_genesis(local, 0x0f) == 0) { + ram16 = 1; + PDEBUG(DEBUG_EXTRA2, "%s: Genesis mode OK using x16 " + "SRAM\n", dev->name); + } else { + printk(KERN_DEBUG "%s: Could not initiate genesis " + "mode\n", dev->name); + ret = -EIO; + goto out; + } + } else { + if (prism2_enable_genesis(local, local->sram_type == 8 ? + 0x1f : 0x0f)) { + printk(KERN_DEBUG "%s: Failed to set Genesis " + "mode (sram_type=%d)\n", dev->name, + local->sram_type); + ret = -EIO; + goto out; + } + ram16 = local->sram_type != 8; + } + + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, "%s: Writing %d bytes at 0x%08x\n", + dev->name, param->data[i].len, param->data[i].addr); + if (hfa384x_to_aux(dev, param->data[i].addr, + param->data[i].len, param->data[i].data)) { + printk(KERN_WARNING "%s: RAM download at 0x%08x " + "(len=%d) failed\n", dev->name, + param->data[i].addr, param->data[i].len); + ret = -EIO; + goto out; + } + } + + PDEBUG(DEBUG_EXTRA2, "Disable genesis mode\n"); + local->func->genesis_reset(local, ram16 ? 0x07 : 0x17); + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Failed to disable AUX port\n", + dev->name); + } + + mdelay(5); + local->hw_downloading = 0; + + PDEBUG(DEBUG_EXTRA2, "Trying to initialize card\n"); + if (prism2_hw_init(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + PDEBUG(DEBUG_EXTRA2, "Card initialized - running PRI only\n"); + if (prism2_hw_init2(dev, 1)) { + printk(KERN_DEBUG "%s: Initialization(2) after genesis mode " + "download failed\n", dev->name); + ret = -EIO; + goto out; + } + + out: + local->hw_downloading = 0; + return ret; +} + + +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD +/* Note! Non-volatile downloading functionality has not yet been tested + * thoroughly and it may corrupt flash image and effectively kill the card that + * is being updated. You have been warned. */ + +static inline int prism2_download_block(struct net_device *dev, + u32 addr, u8 *data, + u32 bufaddr, int rest_len) +{ + u16 param0, param1; + int block_len; + + block_len = rest_len < 4096 ? rest_len : 4096; + + param0 = addr & 0xffff; + param1 = addr >> 16; + + HFA384X_OUTW(block_len, HFA384X_PARAM2_OFF); + HFA384X_OUTW(param1, HFA384X_PARAM1_OFF); + + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_ENABLE_NON_VOLATILE << 8), + param0)) { + printk(KERN_WARNING "%s: Flash download command execution " + "failed\n", dev->name); + return -1; + } + + if (hfa384x_to_aux(dev, bufaddr, block_len, data)) { + printk(KERN_WARNING "%s: flash download at 0x%08x " + "(len=%d) failed\n", dev->name, addr, block_len); + return -1; + } + + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_PROGRAM_NON_VOLATILE << 8), + 0)) { + printk(KERN_WARNING "%s: Flash write command execution " + "failed\n", dev->name); + return -1; + } + + return block_len; +} + + +static int prism2_download_nonvolatile(local_info_t *local, + struct prism2_download_data *dl) +{ + struct net_device *dev = local->dev; + int ret = 0, i; + struct { + u16 page; + u16 offset; + u16 len; + } dlbuffer; + u32 bufaddr; + + if (local->hw_downloading) { + printk(KERN_WARNING "%s: Already downloading - aborting new " + "request\n", dev->name); + return -1; + } + + ret = local->func->get_rid(dev, HFA384X_RID_DOWNLOADBUFFER, + &dlbuffer, 6, 0); + + if (ret < 0) { + printk(KERN_WARNING "%s: Could not read download buffer " + "parameters\n", dev->name); + goto out; + } + + dlbuffer.page = le16_to_cpu(dlbuffer.page); + dlbuffer.offset = le16_to_cpu(dlbuffer.offset); + dlbuffer.len = le16_to_cpu(dlbuffer.len); + + printk(KERN_DEBUG "Download buffer: %d bytes at 0x%04x:0x%04x\n", + dlbuffer.len, dlbuffer.page, dlbuffer.offset); + + bufaddr = (dlbuffer.page << 7) + dlbuffer.offset; + + local->hw_downloading = 1; + + if (!local->pri_only) { + prism2_hw_shutdown(dev, 0); + + if (prism2_hw_init(dev, 0)) { + printk(KERN_WARNING "%s: Could not initialize card for" + " download\n", dev->name); + ret = -1; + goto out; + } + } + + hfa384x_disable_interrupts(dev); + + if (prism2_enable_aux_port(dev, 1)) { + printk(KERN_WARNING "%s: Could not enable AUX port\n", + dev->name); + ret = -1; + goto out; + } + + printk(KERN_DEBUG "%s: starting flash download\n", dev->name); + for (i = 0; i < dl->num_areas; i++) { + int rest_len = dl->data[i].len; + int data_off = 0; + + while (rest_len > 0) { + int block_len; + + block_len = prism2_download_block( + dev, dl->data[i].addr + data_off, + dl->data[i].data + data_off, bufaddr, + rest_len); + + if (block_len < 0) { + ret = -1; + goto out; + } + + rest_len -= block_len; + data_off += block_len; + } + } + + HFA384X_OUTW(0, HFA384X_PARAM1_OFF); + HFA384X_OUTW(0, HFA384X_PARAM2_OFF); + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_DOWNLOAD | + (HFA384X_PROGMODE_DISABLE << 8), 0)) { + printk(KERN_WARNING "%s: Download command execution failed\n", + dev->name); + ret = -1; + goto out; + } + + if (prism2_enable_aux_port(dev, 0)) { + printk(KERN_DEBUG "%s: Disabling AUX port failed\n", + dev->name); + /* continue anyway.. restart should have taken care of this */ + } + + mdelay(5); + + local->func->hw_reset(dev); + local->hw_downloading = 0; + if (prism2_hw_config(dev, 2)) { + printk(KERN_WARNING "%s: Card configuration after flash " + "download failed\n", dev->name); + ret = -1; + } else { + printk(KERN_INFO "%s: Card initialized successfully after " + "flash download\n", dev->name); + } + + out: + local->hw_downloading = 0; + return ret; +} +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + + +static void prism2_download_free_data(struct prism2_download_data *dl) +{ + int i; + + if (dl == NULL) + return; + + for (i = 0; i < dl->num_areas; i++) + kfree(dl->data[i].data); + kfree(dl); +} + + +static int prism2_download(local_info_t *local, + struct prism2_download_param *param) +{ + int ret = 0; + int i; + u32 total_len = 0; + struct prism2_download_data *dl = NULL; + + printk(KERN_DEBUG "prism2_download: dl_cmd=%d start_addr=0x%08x " + "num_areas=%d\n", + param->dl_cmd, param->start_addr, param->num_areas); + + if (param->num_areas > 100) { + ret = -EINVAL; + goto out; + } + + dl = kmalloc(sizeof(*dl) + param->num_areas * + sizeof(struct prism2_download_data_area), GFP_KERNEL); + if (dl == NULL) { + ret = -ENOMEM; + goto out; + } + memset(dl, 0, sizeof(*dl) + param->num_areas * + sizeof(struct prism2_download_data_area)); + dl->dl_cmd = param->dl_cmd; + dl->start_addr = param->start_addr; + dl->num_areas = param->num_areas; + for (i = 0; i < param->num_areas; i++) { + PDEBUG(DEBUG_EXTRA2, + " area %d: addr=0x%08x len=%d ptr=0x%p\n", + i, param->data[i].addr, param->data[i].len, + param->data[i].ptr); + + dl->data[i].addr = param->data[i].addr; + dl->data[i].len = param->data[i].len; + + total_len += param->data[i].len; + if (param->data[i].len > PRISM2_MAX_DOWNLOAD_AREA_LEN || + total_len > PRISM2_MAX_DOWNLOAD_LEN) { + ret = -E2BIG; + goto out; + } + + dl->data[i].data = kmalloc(dl->data[i].len, GFP_KERNEL); + if (dl->data[i].data == NULL) { + ret = -ENOMEM; + goto out; + } + + if (copy_from_user(dl->data[i].data, param->data[i].ptr, + param->data[i].len)) { + ret = -EFAULT; + goto out; + } + } + + switch (param->dl_cmd) { + case PRISM2_DOWNLOAD_VOLATILE: + case PRISM2_DOWNLOAD_VOLATILE_PERSISTENT: + ret = prism2_download_volatile(local, dl); + break; + case PRISM2_DOWNLOAD_VOLATILE_GENESIS: + case PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT: + ret = prism2_download_genesis(local, dl); + break; + case PRISM2_DOWNLOAD_NON_VOLATILE: +#ifdef PRISM2_NON_VOLATILE_DOWNLOAD + ret = prism2_download_nonvolatile(local, dl); +#else /* PRISM2_NON_VOLATILE_DOWNLOAD */ + printk(KERN_INFO "%s: non-volatile downloading not enabled\n", + local->dev->name); + ret = -EOPNOTSUPP; +#endif /* PRISM2_NON_VOLATILE_DOWNLOAD */ + break; + default: + printk(KERN_DEBUG "%s: unsupported download command %d\n", + local->dev->name, param->dl_cmd); + ret = -EINVAL; + break; + }; + + out: + if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_GENESIS_PERSISTENT) { + prism2_download_free_data(local->dl_pri); + local->dl_pri = dl; + } else if (ret == 0 && dl && + param->dl_cmd == PRISM2_DOWNLOAD_VOLATILE_PERSISTENT) { + prism2_download_free_data(local->dl_sec); + local->dl_sec = dl; + } else + prism2_download_free_data(dl); + + return ret; +} diff -Nru a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_hw.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,3607 @@ +/* + * Host AP (software wireless LAN access point) driver for + * Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + * + * FIX: + * - there is currently no way of associating TX packets to correct wds device + * when TX Exc/OK event occurs, so all tx_packets and some + * tx_errors/tx_dropped are added to the main netdevice; using sw_support + * field in txdesc might be used to fix this (using Alloc event to increment + * tx_packets would need some further info in txfid table) + * + * Buffer Access Path (BAP) usage: + * Prism2 cards have two separate BAPs for accessing the card memory. These + * should allow concurrent access to two different frames and the driver + * previously used BAP0 for sending data and BAP1 for receiving data. + * However, there seems to be number of issues with concurrent access and at + * least one know hardware bug in using BAP0 and BAP1 concurrently with PCI + * Prism2.5. Therefore, the driver now only uses BAP0 for moving data between + * host and card memories. BAP0 accesses are protected with local->baplock + * (spin_lock_bh) to prevent concurrent use. + */ + + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "hostap_80211.h" +#include "hostap.h" +#include "hostap_ap.h" + + +/* #define final_version */ + +static int mtu = 1500; +module_param(mtu, int, 0444); +MODULE_PARM_DESC(mtu, "Maximum transfer unit"); + +static int channel[MAX_PARM_DEVICES] = { 3, DEF_INTS }; +module_param_array(channel, int, NULL, 0444); +MODULE_PARM_DESC(channel, "Initial channel"); + +static char essid[33] = "test"; +module_param_string(essid, essid, sizeof(essid), 0444); +MODULE_PARM_DESC(essid, "Host AP's ESSID"); + +static int iw_mode[MAX_PARM_DEVICES] = { IW_MODE_MASTER, DEF_INTS }; +module_param_array(iw_mode, int, NULL, 0444); +MODULE_PARM_DESC(iw_mode, "Initial operation mode"); + +static int beacon_int[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +module_param_array(beacon_int, int, NULL, 0444); +MODULE_PARM_DESC(beacon_int, "Beacon interval (1 = 1024 usec)"); + +static int dtim_period[MAX_PARM_DEVICES] = { 1, DEF_INTS }; +module_param_array(dtim_period, int, NULL, 0444); +MODULE_PARM_DESC(dtim_period, "DTIM period"); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +static int bus_master_threshold_rx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +module_param_array(bus_master_threshold_rx, int, NULL, 0444); +MODULE_PARM_DESC(bus_master_threshold_rx, "Packet length threshold for using " + "PCI bus master on RX"); + +static int bus_master_threshold_tx[MAX_PARM_DEVICES] = { 100, DEF_INTS }; +module_param_array(bus_master_threshold_tx, int, NULL, 0444); +MODULE_PARM_DESC(bus_master_threshold_tx, "Packet length threshold for using " + "PCI bus master on TX"); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + +static char dev_template[16] = "wlan%d"; +module_param_string(dev_template, dev_template, sizeof(dev_template), 0444); +MODULE_PARM_DESC(dev_template, "Prefix for network device name (default: " + "wlan%d)"); + +#ifdef final_version +#define EXTRA_EVENTS_WTERR 0 +#else +/* check WTERR events (Wait Time-out) in development versions */ +#define EXTRA_EVENTS_WTERR HFA384X_EV_WTERR +#endif + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +#define EXTRA_EVENTS_BUS_MASTER (HFA384X_EV_PCI_M0 | HFA384X_EV_PCI_M1) +#else +#define EXTRA_EVENTS_BUS_MASTER 0 +#endif + +/* Events that will be using BAP0 */ +#define HFA384X_BAP0_EVENTS \ + (HFA384X_EV_TXEXC | HFA384X_EV_RX | HFA384X_EV_INFO | HFA384X_EV_TX) + +/* event mask, i.e., events that will result in an interrupt */ +#define HFA384X_EVENT_MASK \ + (HFA384X_BAP0_EVENTS | HFA384X_EV_ALLOC | HFA384X_EV_INFDROP | \ + HFA384X_EV_CMD | HFA384X_EV_TICK | \ + EXTRA_EVENTS_WTERR | EXTRA_EVENTS_BUS_MASTER) + +/* Default TX control flags: use 802.11 headers and request interrupt for + * failed transmits. Frames that request ACK callback, will add + * _TX_OK flag and _ALT_RTRY flag may be used to select different retry policy. + */ +#define HFA384X_TX_CTRL_FLAGS \ + (HFA384X_TX_CTRL_802_11 | HFA384X_TX_CTRL_TX_EX) + + +/* ca. 1 usec */ +#define HFA384X_CMD_BUSY_TIMEOUT 5000 +#define HFA384X_BAP_BUSY_TIMEOUT 50000 + +/* ca. 10 usec */ +#define HFA384X_CMD_COMPL_TIMEOUT 20000 +#define HFA384X_DL_COMPL_TIMEOUT 1000000 + +/* Wait times for initialization; yield to other processes to avoid busy + * waiting for long time. */ +#define HFA384X_INIT_TIMEOUT (HZ / 2) /* 500 ms */ +#define HFA384X_ALLOC_COMPL_TIMEOUT (HZ / 20) /* 50 ms */ + + +static void prism2_hw_reset(struct net_device *dev); +static void prism2_check_sta_fw_version(local_info_t *local); + +#ifdef PRISM2_DOWNLOAD_SUPPORT +/* hostap_download.c */ +static int prism2_download_aux_dump(struct net_device *dev, + unsigned int addr, int len, u8 *buf); +static u8 * prism2_read_pda(struct net_device *dev); +static int prism2_download(local_info_t *local, + struct prism2_download_param *param); +static void prism2_download_free_data(struct prism2_download_data *dl); +static int prism2_download_volatile(local_info_t *local, + struct prism2_download_data *param); +static int prism2_download_genesis(local_info_t *local, + struct prism2_download_data *param); +static int prism2_get_ram_size(local_info_t *local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + + + +#ifndef final_version +/* magic value written to SWSUPPORT0 reg. for detecting whether card is still + * present */ +#define HFA384X_MAGIC 0x8A32 +#endif + + +static u16 hfa384x_read_reg(struct net_device *dev, u16 reg) +{ + return HFA384X_INW(reg); +} + + +static void hfa384x_read_regs(struct net_device *dev, + struct hfa384x_regs *regs) +{ + regs->cmd = HFA384X_INW(HFA384X_CMD_OFF); + regs->evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + regs->offset0 = HFA384X_INW(HFA384X_OFFSET0_OFF); + regs->offset1 = HFA384X_INW(HFA384X_OFFSET1_OFF); + regs->swsupport0 = HFA384X_INW(HFA384X_SWSUPPORT0_OFF); +} + + +/** + * __hostap_cmd_queue_free - Free Prism2 command queue entry (private) + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Internal helper function for freeing Prism2 command queue entries. + * Caller must have acquired local->cmdlock before calling this function. + */ +static inline void __hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + if (del_req) { + entry->del_req = 1; + if (!list_empty(&entry->list)) { + list_del_init(&entry->list); + local->cmd_queue_len--; + } + } + + if (atomic_dec_and_test(&entry->usecnt) && entry->del_req) + kfree(entry); +} + + +/** + * hostap_cmd_queue_free - Free Prism2 command queue entry + * @local: pointer to private Host AP driver data + * @entry: Prism2 command queue entry to be freed + * @del_req: request the entry to be removed + * + * Free a Prism2 command queue entry. + */ +static inline void hostap_cmd_queue_free(local_info_t *local, + struct hostap_cmd_queue *entry, + int del_req) +{ + unsigned long flags; + + spin_lock_irqsave(&local->cmdlock, flags); + __hostap_cmd_queue_free(local, entry, del_req); + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * prism2_clear_cmd_queue - Free all pending Prism2 command queue entries + * @local: pointer to private Host AP driver data + */ +static void prism2_clear_cmd_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + unsigned long flags; + struct hostap_cmd_queue *entry; + + spin_lock_irqsave(&local->cmdlock, flags); + list_for_each_safe(ptr, n, &local->cmd_queue) { + entry = list_entry(ptr, struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + printk(KERN_DEBUG "%s: removed pending cmd_queue entry " + "(type=%d, cmd=0x%04x, param0=0x%04x)\n", + local->dev->name, entry->type, entry->cmd, + entry->param0); + __hostap_cmd_queue_free(local, entry, 1); + } + if (local->cmd_queue_len) { + /* This should not happen; print debug message and clear + * queue length. */ + printk(KERN_DEBUG "%s: cmd_queue_len (%d) not zero after " + "flush\n", local->dev->name, local->cmd_queue_len); + local->cmd_queue_len = 0; + } + spin_unlock_irqrestore(&local->cmdlock, flags); +} + + +/** + * hfa384x_cmd_issue - Issue a Prism2 command to the hardware + * @dev: pointer to net_device + * @entry: Prism2 command queue entry to be issued + */ +static inline int hfa384x_cmd_issue(struct net_device *dev, + struct hostap_cmd_queue *entry) +{ + struct hostap_interface *iface; + local_info_t *local; + int tries; + u16 reg; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->card_present && !local->func->card_present(local)) + return -ENODEV; + + if (entry->issued) { + printk(KERN_DEBUG "%s: driver bug - re-issuing command @%p\n", + dev->name, entry); + } + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } +#ifndef final_version + if (tries != HFA384X_CMD_BUSY_TIMEOUT) { + prism2_io_debug_error(dev, 1); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue: cmd reg was busy " + "for %d usec\n", dev->name, + HFA384X_CMD_BUSY_TIMEOUT - tries); + } +#endif + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, 2); + printk(KERN_DEBUG "%s: hfa384x_cmd_issue - timeout - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + /* write command */ + spin_lock_irqsave(&local->cmdlock, flags); + HFA384X_OUTW(entry->param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(entry->param1, HFA384X_PARAM1_OFF); + HFA384X_OUTW(entry->cmd, HFA384X_CMD_OFF); + entry->issued = 1; + spin_unlock_irqrestore(&local->cmdlock, flags); + + return 0; +} + + +/** + * hfa384x_cmd - Issue a Prism2 command and wait (sleep) for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @param1: value for Param1 register (pointer; %NULL if not used) + * @resp0: pointer for Resp0 data or %NULL if Resp0 is not needed + * + * Issue given command (possibly after waiting in command queue) and sleep + * until the command is completed (or timed out or interrupted). This can be + * called only from user process context. + */ +static int hfa384x_cmd(struct net_device *dev, u16 cmd, u16 param0, + u16 *param1, u16 *resp0) +{ + struct hostap_interface *iface; + local_info_t *local; + int err, res, issue, issued = 0; + unsigned long flags; + struct hostap_cmd_queue *entry; + DECLARE_WAITQUEUE(wait, current); + + iface = netdev_priv(dev); + local = iface->local; + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: hfa384x_cmd called from interrupt " + "context\n", dev->name); + return -1; + } + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + if (signal_pending(current)) + return -EINTR; + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd - kmalloc failed\n", + dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_SLEEP; + entry->cmd = cmd; + entry->param0 = param0; + if (param1) + entry->param1 = *param1; + init_waitqueue_head(&entry->compl); + + /* prepare to wait for command completion event, but do not sleep yet + */ + add_wait_queue(&entry->compl, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + err = 0; + if (!issue) + goto wait_completion; + + if (signal_pending(current)) + err = -EINTR; + + if (!err) { + if (hfa384x_cmd_issue(dev, entry)) + err = -ETIMEDOUT; + else + issued = 1; + } + + wait_completion: + if (!err && entry->type != CMD_COMPLETED) { + /* sleep until command is completed or timed out */ + res = schedule_timeout(2 * HZ); + } else + res = -1; + + if (!err && signal_pending(current)) + err = -EINTR; + + if (err && issued) { + /* the command was issued, so a CmdCompl event should occur + * soon; however, there's a pending signal and + * schedule_timeout() would be interrupted; wait a short period + * of time to avoid removing entry from the list before + * CmdCompl event */ + udelay(300); + } + + set_current_state(TASK_RUNNING); + remove_wait_queue(&entry->compl, &wait); + + /* If entry->list is still in the list, it must be removed + * first and in this case prism2_cmd_ev() does not yet have + * local reference to it, and the data can be kfree()'d + * here. If the command completion event is still generated, + * it will be assigned to next (possibly) pending command, but + * the driver will reset the card anyway due to timeout + * + * If the entry is not in the list prism2_cmd_ev() has a local + * reference to it, but keeps cmdlock as long as the data is + * needed, so the data can be kfree()'d here. */ + + /* FIX: if the entry->list is in the list, it has not been completed + * yet, so removing it here is somewhat wrong.. this could cause + * references to freed memory and next list_del() causing NULL pointer + * dereference.. it would probably be better to leave the entry in the + * list and the list should be emptied during hw reset */ + + spin_lock_irqsave(&local->cmdlock, flags); + if (!list_empty(&entry->list)) { + printk(KERN_DEBUG "%s: hfa384x_cmd: entry still in list? " + "(entry=%p, type=%d, res=%d)\n", dev->name, entry, + entry->type, res); + list_del_init(&entry->list); + local->cmd_queue_len--; + } + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (err) { + printk(KERN_DEBUG "%s: hfa384x_cmd: interrupted; err=%d\n", + dev->name, err); + res = err; + goto done; + } + + if (entry->type != CMD_COMPLETED) { + u16 reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + printk(KERN_DEBUG "%s: hfa384x_cmd: command was not " + "completed (res=%d, entry=%p, type=%d, cmd=0x%04x, " + "param0=0x%04x, EVSTAT=%04x INTEN=%04x)\n", dev->name, + res, entry, entry->type, entry->cmd, entry->param0, reg, + HFA384X_INW(HFA384X_INTEN_OFF)); + if (reg & HFA384X_EV_CMD) { + /* Command completion event is pending, but the + * interrupt was not delivered - probably an issue + * with pcmcia-cs configuration. */ + printk(KERN_WARNING "%s: interrupt delivery does not " + "seem to work\n", dev->name); + } + prism2_io_debug_error(dev, 3); + res = -ETIMEDOUT; + goto done; + } + + if (resp0 != NULL) + *resp0 = entry->resp0; +#ifndef final_version + if (entry->res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x, " + "resp0=0x%04x\n", + dev->name, cmd, entry->res, entry->resp0); + } +#endif /* final_version */ + + res = entry->res; + done: + hostap_cmd_queue_free(local, entry, 1); + return res; +} + + +/** + * hfa384x_cmd_callback - Issue a Prism2 command; callback when completed + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @callback: command completion callback function (%NULL = no callback) + * @context: data pointer to be given to callback function + * + * Issue given command (possibly after waiting in command queue) and use + * callback function to indicate command completion. This can be called both + * from user and interrupt context. The callback function will be called in + * hardware IRQ context. It can be %NULL, when no function is called when + * command is completed. + */ +static int hfa384x_cmd_callback(struct net_device *dev, u16 cmd, u16 param0, + void (*callback)(struct net_device *dev, + void *context, u16 resp0, + u16 status), + void *context) +{ + struct hostap_interface *iface; + local_info_t *local; + int issue, ret; + unsigned long flags; + struct hostap_cmd_queue *entry; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->cmd_queue_len >= HOSTAP_CMD_QUEUE_MAX_LEN + 2) { + printk(KERN_DEBUG "%s: hfa384x_cmd: cmd_queue full\n", + dev->name); + return -1; + } + + entry = (struct hostap_cmd_queue *) + kmalloc(sizeof(*entry), GFP_ATOMIC); + if (entry == NULL) { + printk(KERN_DEBUG "%s: hfa384x_cmd_callback - kmalloc " + "failed\n", dev->name); + return -ENOMEM; + } + memset(entry, 0, sizeof(*entry)); + atomic_set(&entry->usecnt, 1); + entry->type = CMD_CALLBACK; + entry->cmd = cmd; + entry->param0 = param0; + entry->callback = callback; + entry->context = context; + + spin_lock_irqsave(&local->cmdlock, flags); + issue = list_empty(&local->cmd_queue); + if (issue) + entry->issuing = 1; + list_add_tail(&entry->list, &local->cmd_queue); + local->cmd_queue_len++; + spin_unlock_irqrestore(&local->cmdlock, flags); + + if (issue && hfa384x_cmd_issue(dev, entry)) + ret = -ETIMEDOUT; + else + ret = 0; + + hostap_cmd_queue_free(local, entry, ret); + + return ret; +} + + +/** + * __hfa384x_cmd_no_wait - Issue a Prism2 command (private) + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + * @io_debug_num: I/O debug error number + * + * Shared helper function for hfa384x_cmd_wait() and hfa384x_cmd_no_wait(). + */ +static int __hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, u16 param0, + int io_debug_num) +{ + int tries; + u16 reg; + + /* wait until busy bit is clear; this should always be clear since the + * commands are serialized */ + tries = HFA384X_CMD_BUSY_TIMEOUT; + while (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY && tries > 0) { + tries--; + udelay(1); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_CMD_OFF); + prism2_io_debug_error(dev, io_debug_num); + printk(KERN_DEBUG "%s: __hfa384x_cmd_no_wait(%d) - timeout - " + "reg=0x%04x\n", dev->name, io_debug_num, reg); + return -ETIMEDOUT; + } + + /* write command */ + HFA384X_OUTW(param0, HFA384X_PARAM0_OFF); + HFA384X_OUTW(cmd, HFA384X_CMD_OFF); + + return 0; +} + + +/** + * hfa384x_cmd_wait - Issue a Prism2 command and busy wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static int hfa384x_cmd_wait(struct net_device *dev, u16 cmd, u16 param0) +{ + int res, tries; + u16 reg; + + res = __hfa384x_cmd_no_wait(dev, cmd, param0, 4); + if (res) + return res; + + /* wait for command completion */ + if ((cmd & HFA384X_CMDCODE_MASK) == HFA384X_CMDCODE_DOWNLOAD) + tries = HFA384X_DL_COMPL_TIMEOUT; + else + tries = HFA384X_CMD_COMPL_TIMEOUT; + + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + tries > 0) { + tries--; + udelay(10); + } + if (tries == 0) { + reg = HFA384X_INW(HFA384X_EVSTAT_OFF); + prism2_io_debug_error(dev, 5); + printk(KERN_DEBUG "%s: hfa384x_cmd_wait - timeout2 - " + "reg=0x%04x\n", dev->name, reg); + return -ETIMEDOUT; + } + + res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | BIT(9) | + BIT(8))) >> 8; +#ifndef final_version + if (res) { + printk(KERN_DEBUG "%s: CMD=0x%04x => res=0x%02x\n", + dev->name, cmd, res); + } +#endif + + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + return res; +} + + +/** + * hfa384x_cmd_no_wait - Issue a Prism2 command; do not wait for completion + * @dev: pointer to net_device + * @cmd: Prism2 command code (HFA384X_CMD_CODE_*) + * @param0: value for Param0 register + */ +static inline int hfa384x_cmd_no_wait(struct net_device *dev, u16 cmd, + u16 param0) +{ + return __hfa384x_cmd_no_wait(dev, cmd, param0, 6); +} + + +/** + * prism2_cmd_ev - Prism2 command completion event handler + * @dev: pointer to net_device + * + * Interrupt handler for command completion events. Called by the main + * interrupt handler in hardware IRQ context. Read Resp0 and status registers + * from the hardware and ACK the event. Depending on the issued command type + * either wake up the sleeping process that is waiting for command completion + * or call the callback function. Issue the next command, if one is pending. + */ +static void prism2_cmd_ev(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hostap_cmd_queue *entry = NULL; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + atomic_inc(&entry->usecnt); + list_del_init(&entry->list); + local->cmd_queue_len--; + + if (!entry->issued) { + printk(KERN_DEBUG "%s: Command completion event, but " + "cmd not issued\n", dev->name); + __hostap_cmd_queue_free(local, entry, 1); + entry = NULL; + } + } + spin_unlock(&local->cmdlock); + + if (!entry) { + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: Command completion event, but no " + "pending commands\n", dev->name); + return; + } + + entry->resp0 = HFA384X_INW(HFA384X_RESP0_OFF); + entry->res = (HFA384X_INW(HFA384X_STATUS_OFF) & + (BIT(14) | BIT(13) | BIT(12) | BIT(11) | BIT(10) | + BIT(9) | BIT(8))) >> 8; + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + + /* TODO: rest of the CmdEv handling could be moved to tasklet */ + if (entry->type == CMD_SLEEP) { + entry->type = CMD_COMPLETED; + wake_up_interruptible(&entry->compl); + } else if (entry->type == CMD_CALLBACK) { + if (entry->callback) + entry->callback(dev, entry->context, entry->resp0, + entry->res); + } else { + printk(KERN_DEBUG "%s: Invalid command completion type %d\n", + dev->name, entry->type); + } + hostap_cmd_queue_free(local, entry, 1); + + /* issue next command, if pending */ + entry = NULL; + spin_lock(&local->cmdlock); + if (!list_empty(&local->cmd_queue)) { + entry = list_entry(local->cmd_queue.next, + struct hostap_cmd_queue, list); + if (entry->issuing) { + /* hfa384x_cmd() has already started issuing this + * command, so do not start here */ + entry = NULL; + } + if (entry) + atomic_inc(&entry->usecnt); + } + spin_unlock(&local->cmdlock); + + if (entry) { + /* issue next command; if command issuing fails, remove the + * entry from cmd_queue */ + int res = hfa384x_cmd_issue(dev, entry); + spin_lock(&local->cmdlock); + __hostap_cmd_queue_free(local, entry, res); + spin_unlock(&local->cmdlock); + } +} + + +static inline int hfa384x_wait_offset(struct net_device *dev, u16 o_off) +{ + int tries = HFA384X_BAP_BUSY_TIMEOUT; + int res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + + while (res && tries > 0) { + tries--; + udelay(1); + res = HFA384X_INW(o_off) & HFA384X_OFFSET_BUSY; + } + return res; +} + + +/* Offset must be even */ +static int hfa384x_setup_bap(struct net_device *dev, u16 bap, u16 id, + int offset) +{ + u16 o_off, s_off; + int ret = 0; + + if (offset % 2 || bap > 1) + return -EINVAL; + + if (bap == BAP1) { + o_off = HFA384X_OFFSET1_OFF; + s_off = HFA384X_SELECT1_OFF; + } else { + o_off = HFA384X_OFFSET0_OFF; + s_off = HFA384X_SELECT0_OFF; + } + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 7); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout before\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } + + HFA384X_OUTW(id, s_off); + HFA384X_OUTW(offset, o_off); + + if (hfa384x_wait_offset(dev, o_off)) { + prism2_io_debug_error(dev, 8); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - timeout after\n", + dev->name); + ret = -ETIMEDOUT; + goto out; + } +#ifndef final_version + if (HFA384X_INW(o_off) & HFA384X_OFFSET_ERR) { + prism2_io_debug_error(dev, 9); + printk(KERN_DEBUG "%s: hfa384x_setup_bap - offset error " + "(%d,0x04%x,%d); reg=0x%04x\n", + dev->name, bap, id, offset, HFA384X_INW(o_off)); + ret = -EINVAL; + } +#endif + + out: + return ret; +} + + +static int hfa384x_get_rid(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len) +{ + struct hostap_interface *iface; + local_info_t *local; + int res, rlen = 0; + struct hfa384x_rid_hdr rec; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot get RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS, rid, NULL, NULL); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_get_rid: CMDCODE_ACCESS failed " + "(res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + up(&local->rid_bap_sem); + return res; + } + + spin_lock_bh(&local->baplock); + + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rec, sizeof(rec)); + + if (le16_to_cpu(rec.len) == 0) { + /* RID not available */ + res = -ENODATA; + } + + rlen = (le16_to_cpu(rec.len) - 1) * 2; + if (!res && exact_len && rlen != len) { + printk(KERN_DEBUG "%s: hfa384x_get_rid - RID len mismatch: " + "rid=0x%04x, len=%d (expected %d)\n", + dev->name, rid, rlen, len); + res = -ENODATA; + } + + if (!res) + res = hfa384x_from_bap(dev, BAP0, buf, len); + + spin_unlock_bh(&local->baplock); + up(&local->rid_bap_sem); + + if (res) { + if (res != -ENODATA) + printk(KERN_DEBUG "%s: hfa384x_get_rid (rid=%04x, " + "len=%d) - failed - res=%d\n", dev->name, rid, + len, res); + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + return res; + } + + return rlen; +} + + +static int hfa384x_set_rid(struct net_device *dev, u16 rid, void *buf, int len) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_rid_hdr rec; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + printk(KERN_DEBUG "%s: cannot set RID %04x (len=%d) - no PRI " + "f/w\n", dev->name, rid, len); + return -ENOTTY; /* Well.. not really correct, but return + * something unique enough.. */ + } + + if ((local->func->card_present && !local->func->card_present(local)) || + local->hw_downloading) + return -ENODEV; + + rec.rid = cpu_to_le16(rid); + /* RID len in words and +1 for rec.rid */ + rec.len = cpu_to_le16(len / 2 + len % 2 + 1); + + res = down_interruptible(&local->rid_bap_sem); + if (res) + return res; + + spin_lock_bh(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rid, 0); + if (!res) + res = hfa384x_to_bap(dev, BAP0, &rec, sizeof(rec)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, buf, len); + spin_unlock_bh(&local->baplock); + + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid (rid=%04x, len=%d) - " + "failed - res=%d\n", dev->name, rid, len, res); + up(&local->rid_bap_sem); + return res; + } + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ACCESS_WRITE, rid, NULL, NULL); + up(&local->rid_bap_sem); + if (res) { + printk(KERN_DEBUG "%s: hfa384x_set_rid: CMDCODE_ACCESS_WRITE " + "failed (res=%d, rid=%04x, len=%d)\n", + dev->name, res, rid, len); + return res; + } + + if (res == -ETIMEDOUT) + prism2_hw_reset(dev); + + return res; +} + + +static void hfa384x_disable_interrupts(struct net_device *dev) +{ + /* disable interrupts and clear event status */ + HFA384X_OUTW(0, HFA384X_INTEN_OFF); + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); +} + + +static void hfa384x_enable_interrupts(struct net_device *dev) +{ + /* ack pending events and enable interrupts from selected events */ + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_no_bap0(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK & ~HFA384X_BAP0_EVENTS, + HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_all(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EVENT_MASK, HFA384X_INTEN_OFF); +} + + +static void hfa384x_events_only_cmd(struct net_device *dev) +{ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_INTEN_OFF); +} + + +static u16 hfa384x_allocate_fid(struct net_device *dev, int len) +{ + u16 fid; + unsigned long delay; + + /* FIX: this could be replace with hfa384x_cmd() if the Alloc event + * below would be handled like CmdCompl event (sleep here, wake up from + * interrupt handler */ + if (hfa384x_cmd_wait(dev, HFA384X_CMDCODE_ALLOC, len)) { + printk(KERN_DEBUG "%s: cannot allocate fid, len=%d\n", + dev->name, len); + return 0xffff; + } + + delay = jiffies + HFA384X_ALLOC_COMPL_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_ALLOC)) { + printk("%s: fid allocate, len=%d - timeout\n", dev->name, len); + return 0xffff; + } + + fid = HFA384X_INW(HFA384X_ALLOCFID_OFF); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + + return fid; +} + + +static int prism2_reset_port(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (!local->dev_enabled) + return 0; + + res = hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to disable port\n", + dev->name); + else { + res = hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, + NULL, NULL); + if (res) + printk(KERN_DEBUG "%s: reset port failed to enable " + "port\n", dev->name); + } + + /* It looks like at least some STA firmware versions reset + * fragmentation threshold back to 2346 after enable command. Restore + * the configured value, if it differs from this default. */ + if (local->fragm_threshold != 2346 && + hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_DEBUG "%s: failed to restore fragmentation " + "threshold (%d) after Port0 enable\n", + dev->name, local->fragm_threshold); + } + + return res; +} + + +static int prism2_get_version_info(struct net_device *dev, u16 rid, + const char *txt) +{ + struct hfa384x_comp_ident comp; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->no_pri) { + /* PRI f/w not yet available - cannot read RIDs */ + return -1; + } + if (hfa384x_get_rid(dev, rid, &comp, sizeof(comp), 1) < 0) { + printk(KERN_DEBUG "Could not get RID for component %s\n", txt); + return -1; + } + + printk(KERN_INFO "%s: %s: id=0x%02x v%d.%d.%d\n", dev->name, txt, + __le16_to_cpu(comp.id), __le16_to_cpu(comp.major), + __le16_to_cpu(comp.minor), __le16_to_cpu(comp.variant)); + return 0; +} + + +static int prism2_setup_rids(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 tmp; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + hostap_set_word(dev, HFA384X_RID_TICKTIME, 2000); + + if (!local->fw_ap) { + tmp = hostap_get_porttype(local); + ret = hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, tmp); + if (ret) { + printk("%s: Port type setting to %d failed\n", + dev->name, tmp); + goto fail; + } + } + + /* Setting SSID to empty string seems to kill the card in Host AP mode + */ + if (local->iw_mode != IW_MODE_MASTER || local->essid[0] != '\0') { + ret = hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, + local->essid); + if (ret) { + printk("%s: AP own SSID setting failed\n", dev->name); + goto fail; + } + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXDATALEN, + PRISM2_DATA_MAXLEN); + if (ret) { + printk("%s: MAC data length setting to %d failed\n", + dev->name, PRISM2_DATA_MAXLEN); + goto fail; + } + + if (hfa384x_get_rid(dev, HFA384X_RID_CHANNELLIST, &tmp, 2, 1) < 0) { + printk("%s: Channel list read failed\n", dev->name); + ret = -EINVAL; + goto fail; + } + local->channel_mask = __le16_to_cpu(tmp); + + if (local->channel < 1 || local->channel > 14 || + !(local->channel_mask & (1 << (local->channel - 1)))) { + printk(KERN_WARNING "%s: Channel setting out of range " + "(%d)!\n", dev->name, local->channel); + ret = -EBUSY; + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel); + if (ret) { + printk("%s: Channel setting to %d failed\n", + dev->name, local->channel); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, + local->beacon_int); + if (ret) { + printk("%s: Beacon interval setting to %d failed\n", + dev->name, local->beacon_int); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, + local->dtim_period); + if (ret) { + printk("%s: DTIM period setting to %d failed\n", + dev->name, local->dtim_period); + /* this may fail with Symbol/Lucent firmware */ + if (ret == -ETIMEDOUT) + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, + local->is_promisc); + if (ret) + printk(KERN_INFO "%s: Setting promiscuous mode (%d) failed\n", + dev->name, local->is_promisc); + + if (!local->fw_ap) { + ret = hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, + local->essid); + if (ret) { + printk("%s: Desired SSID setting failed\n", dev->name); + goto fail; + } + } + + /* Setup TXRateControl, defaults to allow use of 1, 2, 5.5, and + * 11 Mbps in automatic TX rate fallback and 1 and 2 Mbps as basic + * rates */ + if (local->tx_rate_control == 0) { + local->tx_rate_control = + HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | + HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + } + if (local->basic_rates == 0) + local->basic_rates = HFA384X_RATES_1MBPS | HFA384X_RATES_2MBPS; + + if (!local->fw_ap) { + ret = hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control); + if (ret) { + printk("%s: TXRateControl setting to %d failed\n", + dev->name, local->tx_rate_control); + goto fail; + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control); + if (ret) { + printk("%s: cnfSupportedRates setting to %d failed\n", + dev->name, local->tx_rate_control); + } + + ret = hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates); + if (ret) { + printk("%s: cnfBasicRates setting to %d failed\n", + dev->name, local->basic_rates); + } + + ret = hostap_set_word(dev, HFA384X_RID_CREATEIBSS, 1); + if (ret) { + printk("%s: Create IBSS setting to 1 failed\n", + dev->name); + } + } + + if (local->name_set) + (void) hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, + local->name); + + if (hostap_set_encryption(local)) { + printk(KERN_INFO "%s: could not configure encryption\n", + dev->name); + } + + (void) hostap_set_antsel(local); + + if (hostap_set_roaming(local)) { + printk(KERN_INFO "%s: could not set host roaming\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,6,3) && + hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, local->enh_sec)) + printk(KERN_INFO "%s: cnfEnhSecurity setting to 0x%x failed\n", + dev->name, local->enh_sec); + + /* 32-bit tallies were added in STA f/w 0.8.0, but they were apparently + * not working correctly (last seven counters report bogus values). + * This has been fixed in 0.8.2, so enable 32-bit tallies only + * beginning with that firmware version. Another bug fix for 32-bit + * tallies in 1.4.0; should 16-bit tallies be used for some other + * versions, too? */ + if (local->sta_fw_ver >= PRISM2_FW_VER(0,8,2)) { + if (hostap_set_word(dev, HFA384X_RID_CNFTHIRTY2TALLY, 1)) { + printk(KERN_INFO "%s: cnfThirty2Tally setting " + "failed\n", dev->name); + local->tallies32 = 0; + } else + local->tallies32 = 1; + } else + local->tallies32 = 0; + + hostap_set_auth_algs(local); + + if (hostap_set_word(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + local->fragm_threshold)) { + printk(KERN_INFO "%s: setting FragmentationThreshold to %d " + "failed\n", dev->name, local->fragm_threshold); + } + + if (hostap_set_word(dev, HFA384X_RID_RTSTHRESHOLD, + local->rts_threshold)) { + printk(KERN_INFO "%s: setting RTSThreshold to %d failed\n", + dev->name, local->rts_threshold); + } + + if (local->manual_retry_count >= 0 && + hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + local->manual_retry_count)) { + printk(KERN_INFO "%s: setting cnfAltRetryCount to %d failed\n", + dev->name, local->manual_retry_count); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1) && + hfa384x_get_rid(dev, HFA384X_RID_CNFDBMADJUST, &tmp, 2, 1) == 2) { + local->rssi_to_dBm = le16_to_cpu(tmp); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->wpa && + hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, 1)) { + printk(KERN_INFO "%s: setting ssnHandlingMode to 1 failed\n", + dev->name); + } + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,7,0) && local->generic_elem && + hfa384x_set_rid(dev, HFA384X_RID_GENERICELEMENT, + local->generic_elem, local->generic_elem_len)) { + printk(KERN_INFO "%s: setting genericElement failed\n", + dev->name); + } + + fail: + return ret; +} + + +static int prism2_hw_init(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret, first = 1; + unsigned long start, delay; + + PDEBUG(DEBUG_FLOW, "prism2_hw_init()\n"); + + iface = netdev_priv(dev); + local = iface->local; + + clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits); + + init: + /* initialize HFA 384x */ + ret = hfa384x_cmd_no_wait(dev, HFA384X_CMDCODE_INIT, 0); + if (ret) { + printk(KERN_INFO "%s: first command failed - assuming card " + "does not have primary firmware\n", dev_info); + } + + if (first && (HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + /* EvStat has Cmd bit set in some cases, so retry once if no + * wait was needed */ + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: init command completed too quickly - " + "retrying\n", dev->name); + first = 0; + goto init; + } + + start = jiffies; + delay = jiffies + HFA384X_INIT_TIMEOUT; + while (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD) && + time_before(jiffies, delay)) + yield(); + if (!(HFA384X_INW(HFA384X_EVSTAT_OFF) & HFA384X_EV_CMD)) { + printk(KERN_DEBUG "%s: assuming no Primary image in " + "flash - card initialization not completed\n", + dev_info); + local->no_pri = 1; +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->sram_type == -1) + local->sram_type = prism2_get_ram_size(local); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + return 1; + } + local->no_pri = 0; + printk(KERN_DEBUG "prism2_hw_init: initialized in %lu ms\n", + (jiffies - start) * 1000 / HZ); + HFA384X_OUTW(HFA384X_EV_CMD, HFA384X_EVACK_OFF); + return 0; +} + + +static int prism2_hw_init2(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int i; + + iface = netdev_priv(dev); + local = iface->local; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + kfree(local->pda); + if (local->no_pri) + local->pda = NULL; + else + local->pda = prism2_read_pda(dev); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + hfa384x_disable_interrupts(dev); + +#ifndef final_version + HFA384X_OUTW(HFA384X_MAGIC, HFA384X_SWSUPPORT0_OFF); + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + printk("SWSUPPORT0 write/read failed: %04X != %04X\n", + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), HFA384X_MAGIC); + goto failed; + } +#endif + + if (initial || local->pri_only) { + hfa384x_events_only_cmd(dev); + /* get card version information */ + if (prism2_get_version_info(dev, HFA384X_RID_NICID, "NIC") || + prism2_get_version_info(dev, HFA384X_RID_PRIID, "PRI")) { + hfa384x_disable_interrupts(dev); + goto failed; + } + + if (prism2_get_version_info(dev, HFA384X_RID_STAID, "STA")) { + printk(KERN_DEBUG "%s: Failed to read STA f/w version " + "- only Primary f/w present\n", dev->name); + local->pri_only = 1; + return 0; + } + local->pri_only = 0; + hfa384x_disable_interrupts(dev); + } + + /* FIX: could convert allocate_fid to use sleeping CmdCompl wait and + * enable interrupts before this. This would also require some sort of + * sleeping AllocEv waiting */ + + /* allocate TX FIDs */ + local->txfid_len = PRISM2_TXFID_LEN; + for (i = 0; i < PRISM2_TXFID_COUNT; i++) { + local->txfid[i] = hfa384x_allocate_fid(dev, local->txfid_len); + if (local->txfid[i] == 0xffff && local->txfid_len > 1600) { + local->txfid[i] = hfa384x_allocate_fid(dev, 1600); + if (local->txfid[i] != 0xffff) { + printk(KERN_DEBUG "%s: Using shorter TX FID " + "(1600 bytes)\n", dev->name); + local->txfid_len = 1600; + } + } + if (local->txfid[i] == 0xffff) + goto failed; + local->intransmitfid[i] = PRISM2_TXFID_EMPTY; + } + + hfa384x_events_only_cmd(dev); + + if (initial) { + struct list_head *ptr; + prism2_check_sta_fw_version(local); + + if (hfa384x_get_rid(dev, HFA384X_RID_CNFOWNMACADDR, + &dev->dev_addr, 6, 1) < 0) { + printk("%s: could not get own MAC address\n", + dev->name); + } + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + memcpy(iface->dev->dev_addr, dev->dev_addr, ETH_ALEN); + } + } else if (local->fw_ap) + prism2_check_sta_fw_version(local); + + prism2_setup_rids(dev); + + /* MAC is now configured, but port 0 is not yet enabled */ + return 0; + + failed: + if (!local->no_pri) + printk(KERN_WARNING "%s: Initialization failed\n", dev_info); + return 1; +} + + +static int prism2_hw_enable(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + int was_resetting; + + iface = netdev_priv(dev); + local = iface->local; + was_resetting = local->hw_resetting; + + if (hfa384x_cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, NULL)) { + printk("%s: MAC port 0 enabling failed\n", dev->name); + return 1; + } + + local->hw_ready = 1; + local->hw_reset_tries = 0; + local->hw_resetting = 0; + hfa384x_enable_interrupts(dev); + + /* at least D-Link DWL-650 seems to require additional port reset + * before it starts acting as an AP, so reset port automatically + * here just in case */ + if (initial && prism2_reset_port(dev)) { + printk("%s: MAC port 0 reseting failed\n", dev->name); + return 1; + } + + if (was_resetting && netif_queue_stopped(dev)) { + /* If hw_reset() was called during pending transmit, netif + * queue was stopped. Wake it up now since the wlan card has + * been resetted. */ + netif_wake_queue(dev); + } + + return 0; +} + + +static int prism2_hw_config(struct net_device *dev, int initial) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->hw_downloading) + return 1; + + if (prism2_hw_init(dev, initial)) { + return local->no_pri ? 0 : 1; + } + + if (prism2_hw_init2(dev, initial)) + return 1; + + /* Enable firmware if secondary image is loaded and at least one of the + * netdevices is up. */ + if (!local->pri_only && + (initial == 0 || (initial == 2 && local->num_dev_open > 0))) { + if (!local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_ENABLE); + local->dev_enabled = 1; + return prism2_hw_enable(dev, initial); + } + + return 0; +} + + +static void prism2_hw_shutdown(struct net_device *dev, int no_disable) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* Allow only command completion events during disable */ + hfa384x_events_only_cmd(dev); + + local->hw_ready = 0; + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + local->dev_enabled = 0; + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: card already removed or not configured " + "during shutdown\n", dev->name); + return; + } + + if ((no_disable & HOSTAP_HW_NO_DISABLE) == 0 && + hfa384x_cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, NULL)) + printk(KERN_WARNING "%s: Shutdown failed\n", dev_info); + + hfa384x_disable_interrupts(dev); + + if (no_disable & HOSTAP_HW_ENABLE_CMDCOMPL) + hfa384x_events_only_cmd(dev); + else + prism2_clear_cmd_queue(local); +} + + +static void prism2_hw_reset(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + +#if 0 + static long last_reset = 0; + + /* do not reset card more than once per second to avoid ending up in a + * busy loop reseting the card */ + if (time_before_eq(jiffies, last_reset + HZ)) + return; + last_reset = jiffies; +#endif + + iface = netdev_priv(dev); + local = iface->local; + + if (in_interrupt()) { + printk(KERN_DEBUG "%s: driver bug - prism2_hw_reset() called " + "in interrupt context\n", dev->name); + return; + } + + if (local->hw_downloading) + return; + + if (local->hw_resetting) { + printk(KERN_WARNING "%s: %s: already resetting card - " + "ignoring reset request\n", dev_info, dev->name); + return; + } + + local->hw_reset_tries++; + if (local->hw_reset_tries > 10) { + printk(KERN_WARNING "%s: too many reset tries, skipping\n", + dev->name); + return; + } + + printk(KERN_WARNING "%s: %s: resetting card\n", dev_info, dev->name); + hfa384x_disable_interrupts(dev); + local->hw_resetting = 1; + if (local->func->cor_sreset) { + /* Host system seems to hang in some cases with high traffic + * load or shared interrupts during COR sreset. Disable shared + * interrupts during reset to avoid these crashes. COS sreset + * takes quite a long time, so it is unfortunate that this + * seems to be needed. Anyway, I do not know of any better way + * of avoiding the crash. */ + disable_irq(dev->irq); + local->func->cor_sreset(local); + enable_irq(dev->irq); + } + prism2_hw_shutdown(dev, 1); + prism2_hw_config(dev, 0); + local->hw_resetting = 0; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + if (local->dl_pri) { + printk(KERN_DEBUG "%s: persistent download of primary " + "firmware\n", dev->name); + if (prism2_download_genesis(local, local->dl_pri) < 0) + printk(KERN_WARNING "%s: download (PRI) failed\n", + dev->name); + } + + if (local->dl_sec) { + printk(KERN_DEBUG "%s: persistent download of secondary " + "firmware\n", dev->name); + if (prism2_download_volatile(local, local->dl_sec) < 0) + printk(KERN_WARNING "%s: download (SEC) failed\n", + dev->name); + } +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + /* TODO: restore beacon TIM bits for STAs that have buffered frames */ +} + + +static void prism2_schedule_reset(local_info_t *local) +{ + schedule_work(&local->reset_queue); +} + + +/* Called only as scheduled task after noticing card timeout in interrupt + * context */ +static void handle_reset_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + printk(KERN_DEBUG "%s: scheduled card reset\n", local->dev->name); + prism2_hw_reset(local->dev); + + if (netif_queue_stopped(local->dev)) { + int i; + + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + if (local->intransmitfid[i] == PRISM2_TXFID_EMPTY) { + PDEBUG(DEBUG_EXTRA, "prism2_tx_timeout: " + "wake up queue\n"); + netif_wake_queue(local->dev); + break; + } + } +} + + +static int prism2_get_txfid_idx(local_info_t *local) +{ + int idx, end; + unsigned long flags; + + spin_lock_irqsave(&local->txfidlock, flags); + end = idx = local->next_txfid; + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + local->intransmitfid[idx] = PRISM2_TXFID_RESERVED; + spin_unlock_irqrestore(&local->txfidlock, flags); + return idx; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != end); + spin_unlock_irqrestore(&local->txfidlock, flags); + + PDEBUG(DEBUG_EXTRA2, "prism2_get_txfid_idx: no room in txfid buf: " + "packet dropped\n"); + local->stats.tx_dropped++; + + return -1; +} + + +/* Called only from hardware IRQ */ +static void prism2_transmit_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + struct hostap_interface *iface; + local_info_t *local; + int idx = (int) context; + + iface = netdev_priv(dev); + local = iface->local; + + if (res) { + printk(KERN_DEBUG "%s: prism2_transmit_cb - res=0x%02x\n", + dev->name, res); + return; + } + + if (idx < 0 || idx >= PRISM2_TXFID_COUNT) { + printk(KERN_DEBUG "%s: prism2_transmit_cb called with invalid " + "idx=%d\n", dev->name, idx); + return; + } + + if (!test_and_clear_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug: prism2_transmit_cb called " + "with no pending transmit\n", dev->name); + } + + if (netif_queue_stopped(dev)) { + /* ready for next TX, so wake up queue that was stopped in + * prism2_transmit() */ + netif_wake_queue(dev); + } + + spin_lock(&local->txfidlock); + + /* With reclaim, Resp0 contains new txfid for transmit; the old txfid + * will be automatically allocated for the next TX frame */ + local->intransmitfid[idx] = resp0; + + PDEBUG(DEBUG_FID, "%s: prism2_transmit_cb: txfid[%d]=0x%04x, " + "resp0=0x%04x, transmit_txfid=0x%04x\n", + dev->name, idx, local->txfid[idx], + resp0, local->intransmitfid[local->next_txfid]); + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + local->next_txfid = idx; + + /* check if all TX buffers are occupied */ + do { + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) { + spin_unlock(&local->txfidlock); + return; + } + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_txfid); + spin_unlock(&local->txfidlock); + + /* no empty TX buffers, stop queue */ + netif_stop_queue(dev); +} + + +/* Called only from software IRQ if PCI bus master is not used (with bus master + * this can be called both from software and hardware IRQ) */ +static int prism2_transmit(struct net_device *dev, int idx) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + /* The driver tries to stop netif queue so that there would not be + * more than one attempt to transmit frames going on; check that this + * is really the case */ + + if (test_and_set_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: driver bug - prism2_transmit() called " + "when previous TX was pending\n", dev->name); + return -1; + } + + /* stop the queue for the time that transmit is pending */ + netif_stop_queue(dev); + + /* transmit packet */ + res = hfa384x_cmd_callback( + dev, + HFA384X_CMDCODE_TRANSMIT | HFA384X_CMD_TX_RECLAIM, + local->txfid[idx], + prism2_transmit_cb, (void *) idx); + + if (res) { + struct net_device_stats *stats; + printk(KERN_DEBUG "%s: prism2_transmit: CMDCODE_TRANSMIT " + "failed (res=%d)\n", dev->name, res); + stats = hostap_get_stats(dev); + stats->tx_dropped++; + netif_wake_queue(dev); + return -1; + } + dev->trans_start = jiffies; + + /* Since we did not wait for command completion, the card continues + * to process on the background and we will finish handling when + * command completion event is handled (prism2_cmd_ev() function) */ + + return 0; +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +/* Called only from hardware IRQ */ +static void prism2_tx_cb(struct net_device *dev, void *context, + u16 resp0, u16 res) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long addr; + int buf_len = (int) context; + + iface = netdev_priv(dev); + local = iface->local; + + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_cb - res=0x%02x\n", + dev->name, res); + return; + } + + addr = virt_to_phys(local->bus_m0_buf); + HFA384X_OUTW((addr & 0xffff0000) >> 16, HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + HFA384X_OUTW(buf_len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_TO_BAP, HFA384X_PCI_M0_CTL_OFF); +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* Send IEEE 802.11 frame (convert the header into Prism2 TX descriptor and + * send the payload with this descriptor) */ +/* Called only from software IRQ */ +static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_tx_frame txdesc; + struct hostap_ieee80211_hdr *hdr; + struct hostap_skb_tx_data *meta; + int hdr_len, data_len, idx, res, ret = -1; + u16 tx_control, fc; + + iface = netdev_priv(dev); + local = iface->local; + + meta = (struct hostap_skb_tx_data *) skb->cb; + hdr = (struct hostap_ieee80211_hdr *) skb->data; + + prism2_callback(local, PRISM2_CALLBACK_TX_START); + + if ((local->func->card_present && !local->func->card_present(local)) || + !local->hw_ready || local->hw_downloading || local->pri_only) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_tx_80211: hw not ready -" + " skipping\n", dev->name); + } + goto fail; + } + + memset(&txdesc, 0, sizeof(txdesc)); + + /* skb->data starts with txdesc->frame_control */ + hdr_len = 24; + memcpy(&txdesc.frame_control, skb->data, hdr_len); + fc = le16_to_cpu(txdesc.frame_control); + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA && + (fc & WLAN_FC_FROMDS) && (fc & WLAN_FC_TODS) && skb->len >= 30) { + /* Addr4 */ + memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN); + hdr_len += ETH_ALEN; + } + + tx_control = local->tx_control; + if (meta->tx_cb_idx) { + tx_control |= HFA384X_TX_CTRL_TX_OK; + txdesc.sw_support = cpu_to_le16(meta->tx_cb_idx); + } + txdesc.tx_control = cpu_to_le16(tx_control); + txdesc.tx_rate = meta->rate; + + data_len = skb->len - hdr_len; + txdesc.data_len = cpu_to_le16(data_len); + txdesc.len = cpu_to_be16(data_len); + + idx = prism2_get_txfid_idx(local); + if (idx < 0) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) + hostap_dump_tx_header(dev->name, &txdesc); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, local->txfid[idx], 0); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (!res && skb->len >= local->bus_master_threshold_tx) { + u8 *pos; + int buf_len; + + local->bus_m0_tx_idx = idx; + + /* FIX: BAP0 should be locked during bus master transfer, but + * baplock with BH's disabled is not OK for this; netif queue + * stopping is not enough since BAP0 is used also for RID + * read/write */ + + /* stop the queue for the time that bus mastering on BAP0 is + * in use */ + netif_stop_queue(dev); + + spin_unlock(&local->baplock); + + /* Copy frame data to bus_m0_buf */ + pos = local->bus_m0_buf; + memcpy(pos, &txdesc, sizeof(txdesc)); + pos += sizeof(txdesc); + memcpy(pos, skb->data + hdr_len, skb->len - hdr_len); + pos += skb->len - hdr_len; + buf_len = pos - local->bus_m0_buf; + if (buf_len & 1) + buf_len++; + +#ifdef PRISM2_ENABLE_BEFORE_TX_BUS_MASTER + /* Any RX packet seems to break something with TX bus + * mastering; enable command is enough to fix this.. */ + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_ENABLE, 0, + prism2_tx_cb, (void *) buf_len)) { + printk(KERN_DEBUG "%s: TX: enable port0 failed\n", + dev->name); + } +#else /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + prism2_tx_cb(dev, (void *) buf_len, 0, 0); +#endif /* PRISM2_ENABLE_BEFORE_TX_BUS_MASTER */ + + /* Bus master transfer will be started from command completion + * event handler and TX handling will be finished by calling + * prism2_transmit() from bus master event handler */ + goto tx_stats; + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (!res) + res = hfa384x_to_bap(dev, BAP0, &txdesc, sizeof(txdesc)); + if (!res) + res = hfa384x_to_bap(dev, BAP0, skb->data + hdr_len, + skb->len - hdr_len); + spin_unlock(&local->baplock); + + if (!res) + res = prism2_transmit(dev, idx); + if (res) { + printk(KERN_DEBUG "%s: prism2_tx_80211 - to BAP0 failed\n", + dev->name); + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + schedule_work(&local->reset_queue); + goto fail; + } + + ret = 0; + +fail: + prism2_callback(local, PRISM2_CALLBACK_TX_END); + return ret; +} + + +/* Some SMP systems have reported number of odd errors with hostap_pci. fid + * register has changed values between consecutive reads for an unknown reason. + * This should really not happen, so more debugging is needed. This test + * version is a big slower, but it will detect most of such register changes + * and will try to get the correct fid eventually. */ +#define EXTRA_FID_READ_TESTS + +static inline u16 prism2_read_fid_reg(struct net_device *dev, u16 reg) +{ +#ifdef EXTRA_FID_READ_TESTS + u16 val, val2, val3; + int i; + + for (i = 0; i < 10; i++) { + val = HFA384X_INW(reg); + val2 = HFA384X_INW(reg); + val3 = HFA384X_INW(reg); + + if (val == val2 && val == val3) + return val; + + printk(KERN_DEBUG "%s: detected fid change (try=%d, reg=%04x):" + " %04x %04x %04x\n", + dev->name, i, reg, val, val2, val3); + if ((val == val2 || val == val3) && val != 0) + return val; + if (val2 == val3 && val2 != 0) + return val2; + } + printk(KERN_WARNING "%s: Uhhuh.. could not read good fid from reg " + "%04x (%04x %04x %04x)\n", dev->name, reg, val, val2, val3); + return val; +#else /* EXTRA_FID_READ_TESTS */ + return HFA384X_INW(reg); +#endif /* EXTRA_FID_READ_TESTS */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_rx(local_info_t *local) +{ + struct net_device *dev = local->dev; + int res, rx_pending = 0; + u16 len, hdr_len, rxfid, status, macport; + struct net_device_stats *stats; + struct hfa384x_rx_frame rxdesc; + struct sk_buff *skb = NULL; + + prism2_callback(local, PRISM2_CALLBACK_RX_START); + stats = hostap_get_stats(dev); + + rxfid = prism2_read_fid_reg(dev, HFA384X_RXFID_OFF); +#ifndef final_version + if (rxfid == 0) { + rxfid = HFA384X_INW(HFA384X_RXFID_OFF); + printk(KERN_DEBUG "prism2_rx: rxfid=0 (next 0x%04x)\n", + rxfid); + if (rxfid == 0) { + schedule_work(&local->reset_queue); + goto rx_dropped; + } + /* try to continue with the new rxfid value */ + } +#endif + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, rxfid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &rxdesc, sizeof(rxdesc)); + + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: copy from BAP0 failed %d\n", dev->name, + res); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + goto rx_dropped; + } + + len = le16_to_cpu(rxdesc.data_len); + hdr_len = sizeof(rxdesc); + status = le16_to_cpu(rxdesc.status); + macport = (status >> 8) & 0x07; + + /* Drop frames with too large reported payload length. Monitor mode + * seems to sometimes pass frames (e.g., ctrl::ack) with signed and + * negative value, so allow also values 65522 .. 65534 (-14 .. -2) for + * macport 7 */ + if (len > PRISM2_DATA_MAXLEN + 8 /* WEP */) { + if (macport == 7 && local->iw_mode == IW_MODE_MONITOR) { + if (len >= (u16) -14) { + hdr_len -= 65535 - len; + hdr_len--; + } + len = 0; + } else { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received frame with invalid " + "length 0x%04x\n", dev->name, len); + hostap_dump_rx_header(dev->name, &rxdesc); + goto rx_dropped; + } + } + + skb = dev_alloc_skb(len + hdr_len); + if (!skb) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: RX failed to allocate skb\n", + dev->name); + goto rx_dropped; + } + skb->dev = dev; + memcpy(skb_put(skb, hdr_len), &rxdesc, hdr_len); + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (len >= local->bus_master_threshold_rx) { + unsigned long addr; + + hfa384x_events_no_bap1(dev); + + local->rx_skb = skb; + /* Internal BAP0 offset points to the byte following rxdesc; + * copy rest of the data using bus master */ + addr = virt_to_phys(skb_put(skb, len)); + HFA384X_OUTW((addr & 0xffff0000) >> 16, + HFA384X_PCI_M0_ADDRH_OFF); + HFA384X_OUTW(addr & 0x0000ffff, HFA384X_PCI_M0_ADDRL_OFF); + if (len & 1) + len++; + HFA384X_OUTW(len / 2, HFA384X_PCI_M0_LEN_OFF); + HFA384X_OUTW(HFA384X_PCI_CTL_FROM_BAP, HFA384X_PCI_M0_CTL_OFF); + + /* pci_bus_m1 event will be generated when data transfer is + * complete and the frame will then be added to rx_list and + * rx_tasklet is scheduled */ + rx_pending = 1; + + /* Have to release baplock before returning, although BAP0 + * should really not be used before DMA transfer has been + * completed. */ + spin_unlock(&local->baplock); + } else +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + { + if (len > 0) + res = hfa384x_from_bap(dev, BAP0, skb_put(skb, len), + len); + spin_unlock(&local->baplock); + if (res) { + printk(KERN_DEBUG "%s: RX failed to read " + "frame data\n", dev->name); + goto rx_dropped; + } + + skb_queue_tail(&local->rx_list, skb); + tasklet_schedule(&local->rx_tasklet); + } + + rx_exit: + prism2_callback(local, PRISM2_CALLBACK_RX_END); + if (!rx_pending) { + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } + + return; + + rx_dropped: + stats->rx_dropped++; + if (skb) + dev_kfree_skb(skb); + goto rx_exit; +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_skb(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_rx_frame *rxdesc; + struct net_device *dev = skb->dev; + struct hostap_80211_rx_status stats; + int hdrlen, rx_hdrlen; + + rx_hdrlen = sizeof(*rxdesc); + if (skb->len < sizeof(*rxdesc)) { + /* Allow monitor mode to receive shorter frames */ + if (local->iw_mode == IW_MODE_MONITOR && + skb->len >= sizeof(*rxdesc) - 30) { + rx_hdrlen = skb->len; + } else { + dev_kfree_skb(skb); + return; + } + } + + rxdesc = (struct hfa384x_rx_frame *) skb->data; + + if (local->frame_dump & PRISM2_DUMP_RX_HDR && + skb->len >= sizeof(*rxdesc)) + hostap_dump_rx_header(dev->name, rxdesc); + + if (le16_to_cpu(rxdesc->status) & HFA384X_RX_STATUS_FCSERR && + (!local->monitor_allow_fcserr || + local->iw_mode != IW_MODE_MONITOR)) + goto drop; + + if (skb->len > PRISM2_DATA_MAXLEN) { + printk(KERN_DEBUG "%s: RX: len(%d) > MAX(%d)\n", + dev->name, skb->len, PRISM2_DATA_MAXLEN); + goto drop; + } + + stats.mac_time = le32_to_cpu(rxdesc->time); + stats.signal = rxdesc->signal - local->rssi_to_dBm; + stats.noise = rxdesc->silence - local->rssi_to_dBm; + stats.rate = rxdesc->rate; + + /* Convert Prism2 RX structure into IEEE 802.11 header */ + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(rxdesc->frame_control)); + if (hdrlen > rx_hdrlen) + hdrlen = rx_hdrlen; + + memmove(skb_pull(skb, rx_hdrlen - hdrlen), + &rxdesc->frame_control, hdrlen); + + hostap_80211_rx(dev, skb, &stats); + return; + + drop: + dev_kfree_skb(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_rx_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->rx_list)) != NULL) + hostap_rx_skb(local, skb); +} + + +/* Called only from hardware IRQ */ +static void prism2_alloc_ev(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int idx; + u16 fid; + + iface = netdev_priv(dev); + local = iface->local; + + fid = prism2_read_fid_reg(dev, HFA384X_ALLOCFID_OFF); + + PDEBUG(DEBUG_FID, "FID: interrupt: ALLOC - fid=0x%04x\n", fid); + + spin_lock(&local->txfidlock); + idx = local->next_alloc; + + do { + if (local->txfid[idx] == fid) { + PDEBUG(DEBUG_FID, "FID: found matching txfid[%d]\n", + idx); + +#ifndef final_version + if (local->intransmitfid[idx] == PRISM2_TXFID_EMPTY) + printk("Already released txfid found at idx " + "%d\n", idx); + if (local->intransmitfid[idx] == PRISM2_TXFID_RESERVED) + printk("Already reserved txfid found at idx " + "%d\n", idx); +#endif + local->intransmitfid[idx] = PRISM2_TXFID_EMPTY; + idx++; + local->next_alloc = idx >= PRISM2_TXFID_COUNT ? 0 : + idx; + + if (!test_bit(HOSTAP_BITS_TRANSMIT, &local->bits) && + netif_queue_stopped(dev)) + netif_wake_queue(dev); + + spin_unlock(&local->txfidlock); + return; + } + + idx++; + if (idx >= PRISM2_TXFID_COUNT) + idx = 0; + } while (idx != local->next_alloc); + + printk(KERN_WARNING "%s: could not find matching txfid (0x%04x, new " + "read 0x%04x) for alloc event\n", dev->name, fid, + HFA384X_INW(HFA384X_ALLOCFID_OFF)); + printk(KERN_DEBUG "TXFIDs:"); + for (idx = 0; idx < PRISM2_TXFID_COUNT; idx++) + printk(" %04x[%04x]", local->txfid[idx], + local->intransmitfid[idx]); + printk("\n"); + spin_unlock(&local->txfidlock); + + /* FIX: should probably schedule reset; reference to one txfid was lost + * completely.. Bad things will happen if we run out of txfids + * Actually, this will cause netdev watchdog to notice TX timeout and + * then card reset after all txfids have been leaked. */ +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_tx_callback(local_info_t *local, + struct hfa384x_tx_frame *txdesc, int ok, + char *payload) +{ + u16 sw_support, hdrlen, len; + struct sk_buff *skb; + struct hostap_tx_callback_info *cb; + + /* Make sure that frame was from us. */ + if (memcmp(txdesc->addr2, local->dev->dev_addr, ETH_ALEN)) { + printk(KERN_DEBUG "%s: TX callback - foreign frame\n", + local->dev->name); + return; + } + + sw_support = le16_to_cpu(txdesc->sw_support); + + spin_lock(&local->lock); + cb = local->tx_callback; + while (cb != NULL && cb->idx != sw_support) + cb = cb->next; + spin_unlock(&local->lock); + + if (cb == NULL) { + printk(KERN_DEBUG "%s: could not find TX callback (idx %d)\n", + local->dev->name, sw_support); + return; + } + + hdrlen = hostap_80211_get_hdrlen(le16_to_cpu(txdesc->frame_control)); + len = le16_to_cpu(txdesc->data_len); + skb = dev_alloc_skb(hdrlen + len); + if (skb == NULL) { + printk(KERN_DEBUG "%s: hostap_tx_callback failed to allocate " + "skb\n", local->dev->name); + return; + } + + memcpy(skb_put(skb, hdrlen), (void *) &txdesc->frame_control, hdrlen); + if (payload) + memcpy(skb_put(skb, len), payload, len); + + skb->dev = local->dev; + skb->mac.raw = skb->data; + + cb->func(skb, ok, cb->data); +} + + +/* Called only as a tasklet (software IRQ) */ +static int hostap_tx_compl_read(local_info_t *local, int error, + struct hfa384x_tx_frame *txdesc, + char **payload) +{ + u16 fid, len; + int res, ret = 0; + struct net_device *dev = local->dev; + + fid = prism2_read_fid_reg(dev, HFA384X_TXCOMPLFID_OFF); + + PDEBUG(DEBUG_FID, "interrupt: TX (err=%d) - fid=0x%04x\n", fid, error); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, txdesc, sizeof(*txdesc)); + if (res) { + PDEBUG(DEBUG_EXTRA, "%s: TX (err=%d) - fid=0x%04x - could not " + "read txdesc\n", dev->name, error, fid); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + ret = -1; + goto fail; + } + if (txdesc->sw_support) { + len = le16_to_cpu(txdesc->data_len); + if (len < PRISM2_DATA_MAXLEN) { + *payload = (char *) kmalloc(len, GFP_ATOMIC); + if (*payload == NULL || + hfa384x_from_bap(dev, BAP0, *payload, len)) { + PDEBUG(DEBUG_EXTRA, "%s: could not read TX " + "frame payload\n", dev->name); + kfree(*payload); + *payload = NULL; + ret = -1; + goto fail; + } + } + } + + fail: + spin_unlock(&local->baplock); + + return ret; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_tx_ev(local_info_t *local) +{ + struct net_device *dev = local->dev; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + if (hostap_tx_compl_read(local, 0, &txdesc, &payload)) + goto fail; + + if (local->frame_dump & PRISM2_DUMP_TX_HDR) { + PDEBUG(DEBUG_EXTRA, "%s: TX - status=0x%04x " + "retry_count=%d tx_rate=%d seq_ctrl=%d " + "duration_id=%d\n", + dev->name, le16_to_cpu(txdesc.status), + txdesc.retry_count, txdesc.tx_rate, + le16_to_cpu(txdesc.seq_ctrl), + le16_to_cpu(txdesc.duration_id)); + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 1, payload); + kfree(payload); + + fail: + HFA384X_OUTW(HFA384X_EV_TX, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_sta_tx_exc_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->sta_tx_exc_list)) != NULL) { + struct hfa384x_tx_frame *txdesc = + (struct hfa384x_tx_frame *) skb->data; + + if (skb->len >= sizeof(*txdesc)) { + /* Convert Prism2 RX structure into IEEE 802.11 header + */ + u16 fc = le16_to_cpu(txdesc->frame_control); + int hdrlen = hostap_80211_get_hdrlen(fc); + memmove(skb_pull(skb, sizeof(*txdesc) - hdrlen), + &txdesc->frame_control, hdrlen); + + hostap_handle_sta_tx_exc(local, skb); + } + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_txexc(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 status, fc; + int show_dump, res; + char *payload = NULL; + struct hfa384x_tx_frame txdesc; + + show_dump = local->frame_dump & PRISM2_DUMP_TXEXC_HDR; + local->stats.tx_errors++; + + res = hostap_tx_compl_read(local, 1, &txdesc, &payload); + HFA384X_OUTW(HFA384X_EV_TXEXC, HFA384X_EVACK_OFF); + if (res) + return; + + status = le16_to_cpu(txdesc.status); + + /* We produce a TXDROP event only for retry or lifetime + * exceeded, because that's the only status that really mean + * that this particular node went away. + * Other errors means that *we* screwed up. - Jean II */ + if (status & (HFA384X_TX_STATUS_RETRYERR | HFA384X_TX_STATUS_AGEDERR)) + { + union iwreq_data wrqu; + + /* Copy 802.11 dest address. */ + memcpy(wrqu.addr.sa_data, txdesc.addr1, ETH_ALEN); + wrqu.addr.sa_family = ARPHRD_ETHER; + wireless_send_event(dev, IWEVTXDROP, &wrqu, NULL); + } else + show_dump = 1; + + if (local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->wds_type & HOSTAP_WDS_AP_CLIENT) { + struct sk_buff *skb; + skb = dev_alloc_skb(sizeof(txdesc)); + if (skb) { + memcpy(skb_put(skb, sizeof(txdesc)), &txdesc, + sizeof(txdesc)); + skb_queue_tail(&local->sta_tx_exc_list, skb); + tasklet_schedule(&local->sta_tx_exc_tasklet); + } + } + + if (txdesc.sw_support) + hostap_tx_callback(local, &txdesc, 0, payload); + kfree(payload); + + if (!show_dump) + return; + + PDEBUG(DEBUG_EXTRA, "%s: TXEXC - status=0x%04x (%s%s%s%s)" + " tx_control=%04x\n", + dev->name, status, + status & HFA384X_TX_STATUS_RETRYERR ? "[RetryErr]" : "", + status & HFA384X_TX_STATUS_AGEDERR ? "[AgedErr]" : "", + status & HFA384X_TX_STATUS_DISCON ? "[Discon]" : "", + status & HFA384X_TX_STATUS_FORMERR ? "[FormErr]" : "", + le16_to_cpu(txdesc.tx_control)); + + fc = le16_to_cpu(txdesc.frame_control); + PDEBUG(DEBUG_EXTRA, " retry_count=%d tx_rate=%d fc=0x%04x " + "(%s%s%s::%d%s%s)\n", + txdesc.retry_count, txdesc.tx_rate, fc, + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT ? "Mgmt" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_CTRL ? "Ctrl" : "", + WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ? "Data" : "", + WLAN_FC_GET_STYPE(fc), + fc & WLAN_FC_TODS ? " ToDS" : "", + fc & WLAN_FC_FROMDS ? " FromDS" : ""); + PDEBUG(DEBUG_EXTRA, " A1=" MACSTR " A2=" MACSTR " A3=" + MACSTR " A4=" MACSTR "\n", + MAC2STR(txdesc.addr1), MAC2STR(txdesc.addr2), + MAC2STR(txdesc.addr3), MAC2STR(txdesc.addr4)); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_info_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct sk_buff *skb; + + while ((skb = skb_dequeue(&local->info_list)) != NULL) { + hostap_info_process(local, skb); + dev_kfree_skb(skb); + } +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 fid; + int res, left; + struct hfa384x_info_frame info; + struct sk_buff *skb; + + fid = HFA384X_INW(HFA384X_INFOFID_OFF); + + spin_lock(&local->baplock); + res = hfa384x_setup_bap(dev, BAP0, fid, 0); + if (!res) + res = hfa384x_from_bap(dev, BAP0, &info, sizeof(info)); + if (res) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "Could not get info frame (fid=0x%04x)\n", + fid); + if (res == -ETIMEDOUT) { + schedule_work(&local->reset_queue); + } + goto out; + } + + le16_to_cpus(&info.len); + le16_to_cpus(&info.type); + left = (info.len - 1) * 2; + + if (info.len & 0x8000 || info.len == 0 || left > 2060) { + /* data register seems to give 0x8000 in some error cases even + * though busy bit is not set in offset register; + * in addition, length must be at least 1 due to type field */ + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Received info frame with invalid " + "length 0x%04x (type 0x%04x)\n", dev->name, info.len, + info.type); + goto out; + } + + skb = dev_alloc_skb(sizeof(info) + left); + if (skb == NULL) { + spin_unlock(&local->baplock); + printk(KERN_DEBUG "%s: Could not allocate skb for info " + "frame\n", dev->name); + goto out; + } + + memcpy(skb_put(skb, sizeof(info)), &info, sizeof(info)); + if (left > 0 && hfa384x_from_bap(dev, BAP0, skb_put(skb, left), left)) + { + spin_unlock(&local->baplock); + printk(KERN_WARNING "%s: Info frame read failed (fid=0x%04x, " + "len=0x%04x, type=0x%04x\n", + dev->name, fid, info.len, info.type); + dev_kfree_skb(skb); + goto out; + } + spin_unlock(&local->baplock); + + skb_queue_tail(&local->info_list, skb); + tasklet_schedule(&local->info_tasklet); + + out: + HFA384X_OUTW(HFA384X_EV_INFO, HFA384X_EVACK_OFF); +} + + +/* Called only as a tasklet (software IRQ) */ +static void hostap_bap_tasklet(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 ev; + int frames = 30; + + if (local->func->card_present && !local->func->card_present(local)) + return; + + set_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Process all pending BAP events without generating new interrupts + * for them */ + while (frames-- > 0) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff || !(ev & HFA384X_BAP0_EVENTS)) + break; + if (ev & HFA384X_EV_RX) + prism2_rx(local); + if (ev & HFA384X_EV_INFO) + prism2_info(local); + if (ev & HFA384X_EV_TX) + prism2_tx_ev(local); + if (ev & HFA384X_EV_TXEXC) + prism2_txexc(local); + } + + set_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); + clear_bit(HOSTAP_BITS_BAP_TASKLET, &local->bits); + + /* Enable interrupts for new BAP events */ + hfa384x_events_all(dev); + clear_bit(HOSTAP_BITS_BAP_TASKLET2, &local->bits); +} + + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) +/* Called only from hardware IRQ */ +static void prism2_bus_master_ev(struct net_device *dev, int bap) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (bap == BAP1) { + /* FIX: frame payload was DMA'd to skb->data; might need to + * invalidate data cache for that memory area */ + skb_queue_tail(&local->rx_list, local->rx_skb); + tasklet_schedule(&local->rx_tasklet); + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + } else { + if (prism2_transmit(dev, local->bus_m0_tx_idx)) { + printk(KERN_DEBUG "%s: prism2_transmit() failed " + "when called from bus master event\n", + dev->name); + local->intransmitfid[local->bus_m0_tx_idx] = + PRISM2_TXFID_EMPTY; + schedule_work(&local->reset_queue); + } + } +} +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + +/* Called only from hardware IRQ */ +static void prism2_infdrop(struct net_device *dev) +{ + static unsigned long last_inquire = 0; + + PDEBUG(DEBUG_EXTRA, "%s: INFDROP event\n", dev->name); + + /* some firmware versions seem to get stuck with + * full CommTallies in high traffic load cases; every + * packet will then cause INFDROP event and CommTallies + * info frame will not be sent automatically. Try to + * get out of this state by inquiring CommTallies. */ + if (!last_inquire || time_after(jiffies, last_inquire + HZ)) { + hfa384x_cmd_callback(dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, NULL); + last_inquire = jiffies; + } +} + + +/* Called only from hardware IRQ */ +static void prism2_ev_tick(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 evstat, inten; + static int prev_stuck = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (time_after(jiffies, local->last_tick_timer + 5 * HZ) && + local->last_tick_timer) { + evstat = HFA384X_INW(HFA384X_EVSTAT_OFF); + inten = HFA384X_INW(HFA384X_INTEN_OFF); + if (!prev_stuck) { + printk(KERN_INFO "%s: SW TICK stuck? " + "bits=0x%lx EvStat=%04x IntEn=%04x\n", + dev->name, local->bits, evstat, inten); + } + local->sw_tick_stuck++; + if ((evstat & HFA384X_BAP0_EVENTS) && + (inten & HFA384X_BAP0_EVENTS)) { + printk(KERN_INFO "%s: trying to recover from IRQ " + "hang\n", dev->name); + hfa384x_events_no_bap0(dev); + } + prev_stuck = 1; + } else + prev_stuck = 0; +} + + +/* Called only from hardware IRQ */ +static inline void prism2_check_magic(local_info_t *local) +{ + /* at least PCI Prism2.5 with bus mastering seems to sometimes + * return 0x0000 in SWSUPPORT0 for unknown reason, but re-reading the + * register once or twice seems to get the correct value.. PCI cards + * cannot anyway be removed during normal operation, so there is not + * really any need for this verification with them. */ + +#ifndef PRISM2_PCI +#ifndef final_version + static unsigned long last_magic_err = 0; + struct net_device *dev = local->dev; + + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != HFA384X_MAGIC) { + if (!local->hw_ready) + return; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + if (time_after(jiffies, last_magic_err + 10 * HZ)) { + printk("%s: Interrupt, but SWSUPPORT0 does not match: " + "%04X != %04X - card removed?\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + last_magic_err = jiffies; + } else if (net_ratelimit()) { + printk(KERN_DEBUG "%s: interrupt - SWSUPPORT0=%04x " + "MAGIC=%04x\n", dev->name, + HFA384X_INW(HFA384X_SWSUPPORT0_OFF), + HFA384X_MAGIC); + } + if (HFA384X_INW(HFA384X_SWSUPPORT0_OFF) != 0xffff) + schedule_work(&local->reset_queue); + return; + } +#endif /* final_version */ +#endif /* !PRISM2_PCI */ +} + + +/* Called only from hardware IRQ */ +static irqreturn_t prism2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *) dev_id; + struct hostap_interface *iface; + local_info_t *local; + int events = 0; + u16 ev; + + iface = netdev_priv(dev); + local = iface->local; + + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { + printk(KERN_DEBUG "%s: Interrupt, but dev not OK\n", + dev->name); + return IRQ_HANDLED; + } + + prism2_check_magic(local); + + for (;;) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev == 0xffff) { + if (local->shutdown) + return IRQ_HANDLED; + HFA384X_OUTW(0xffff, HFA384X_EVACK_OFF); + printk(KERN_DEBUG "%s: prism2_interrupt: ev=0xffff\n", + dev->name); + return IRQ_HANDLED; + } + + ev &= HFA384X_INW(HFA384X_INTEN_OFF); + if (ev == 0) + break; + + if (ev & HFA384X_EV_CMD) { + prism2_cmd_ev(dev); + } + + /* Above events are needed even before hw is ready, but other + * events should be skipped during initialization. This may + * change for AllocEv if allocate_fid is implemented without + * busy waiting. */ + if (!local->hw_ready || local->hw_resetting || + !local->dev_enabled) { + ev = HFA384X_INW(HFA384X_EVSTAT_OFF); + if (ev & HFA384X_EV_CMD) + goto next_event; + if ((ev & HFA384X_EVENT_MASK) == 0) + return IRQ_HANDLED; + if (local->dev_enabled && (ev & ~HFA384X_EV_TICK) && + net_ratelimit()) { + printk(KERN_DEBUG "%s: prism2_interrupt: hw " + "not ready; skipping events 0x%04x " + "(IntEn=0x%04x)%s%s%s\n", + dev->name, ev, + HFA384X_INW(HFA384X_INTEN_OFF), + !local->hw_ready ? " (!hw_ready)" : "", + local->hw_resetting ? + " (hw_resetting)" : "", + !local->dev_enabled ? + " (!dev_enabled)" : ""); + } + HFA384X_OUTW(ev, HFA384X_EVACK_OFF); + return IRQ_HANDLED; + } + + if (ev & HFA384X_EV_TICK) { + prism2_ev_tick(dev); + HFA384X_OUTW(HFA384X_EV_TICK, HFA384X_EVACK_OFF); + } + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + if (ev & HFA384X_EV_PCI_M0) { + prism2_bus_master_ev(dev, BAP0); + HFA384X_OUTW(HFA384X_EV_PCI_M0, HFA384X_EVACK_OFF); + } + + if (ev & HFA384X_EV_PCI_M1) { + /* previous RX has been copied can be ACKed now */ + HFA384X_OUTW(HFA384X_EV_RX, HFA384X_EVACK_OFF); + + prism2_bus_master_ev(dev, BAP1); + HFA384X_OUTW(HFA384X_EV_PCI_M1, HFA384X_EVACK_OFF); + } +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + if (ev & HFA384X_EV_ALLOC) { + prism2_alloc_ev(dev); + HFA384X_OUTW(HFA384X_EV_ALLOC, HFA384X_EVACK_OFF); + } + + /* Reading data from the card is quite time consuming, so do it + * in tasklets. TX, TXEXC, RX, and INFO events will be ACKed + * and unmasked after needed data has been read completely. */ + if (ev & HFA384X_BAP0_EVENTS) { + hfa384x_events_no_bap0(dev); + tasklet_schedule(&local->bap_tasklet); + } + +#ifndef final_version + if (ev & HFA384X_EV_WTERR) { + PDEBUG(DEBUG_EXTRA, "%s: WTERR event\n", dev->name); + HFA384X_OUTW(HFA384X_EV_WTERR, HFA384X_EVACK_OFF); + } +#endif /* final_version */ + + if (ev & HFA384X_EV_INFDROP) { + prism2_infdrop(dev); + HFA384X_OUTW(HFA384X_EV_INFDROP, HFA384X_EVACK_OFF); + } + + next_event: + events++; + if (events >= PRISM2_MAX_INTERRUPT_EVENTS) { + PDEBUG(DEBUG_EXTRA, "prism2_interrupt: >%d events " + "(EvStat=0x%04x)\n", + PRISM2_MAX_INTERRUPT_EVENTS, + HFA384X_INW(HFA384X_EVSTAT_OFF)); + break; + } + } + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 1); + return IRQ_RETVAL(events); +} + + +static void prism2_check_sta_fw_version(local_info_t *local) +{ + struct hfa384x_comp_ident comp; + int id, variant, major, minor; + + if (hfa384x_get_rid(local->dev, HFA384X_RID_STAID, + &comp, sizeof(comp), 1) < 0) + return; + + local->fw_ap = 0; + id = le16_to_cpu(comp.id); + if (id != HFA384X_COMP_ID_STA) { + if (id == HFA384X_COMP_ID_FW_AP) + local->fw_ap = 1; + return; + } + + major = __le16_to_cpu(comp.major); + minor = __le16_to_cpu(comp.minor); + variant = __le16_to_cpu(comp.variant); + local->sta_fw_ver = PRISM2_FW_VER(major, minor, variant); + + /* Station firmware versions before 1.4.x seem to have a bug in + * firmware-based WEP encryption when using Host AP mode, so use + * host_encrypt as a default for them. Firmware version 1.4.9 is the + * first one that has been seen to produce correct encryption, but the + * bug might be fixed before that (although, at least 1.4.2 is broken). + */ + local->fw_encrypt_ok = local->sta_fw_ver >= PRISM2_FW_VER(1,4,9); + + if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + local->dev->name); + local->host_encrypt = 1; + } + + /* IEEE 802.11 standard compliant WDS frames (4 addresses) were broken + * in station firmware versions before 1.5.x. With these versions, the + * driver uses a workaround with bogus frame format (4th address after + * the payload). This is not compatible with other AP devices. Since + * the firmware bug is fixed in the latest station firmware versions, + * automatically enable standard compliant mode for cards using station + * firmware version 1.5.0 or newer. */ + if (local->sta_fw_ver >= PRISM2_FW_VER(1,5,0)) + local->wds_type |= HOSTAP_WDS_STANDARD_FRAME; + else { + printk(KERN_DEBUG "%s: defaulting to bogus WDS frame as a " + "workaround for firmware bug in Host AP mode WDS\n", + local->dev->name); + } + + hostap_check_sta_fw_version(local->ap, local->sta_fw_ver); +} + + +static void prism2_crypt_deinit_entries(local_info_t *local, int force) +{ + struct list_head *ptr, *n; + struct prism2_crypt_data *entry; + + for (ptr = local->crypt_deinit_list.next, n = ptr->next; + ptr != &local->crypt_deinit_list; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct prism2_crypt_data, list); + + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(ptr); + + if (entry->ops) + entry->ops->deinit(entry->priv); + kfree(entry); + } +} + + +static void prism2_crypt_deinit_handler(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + unsigned long flags; + + spin_lock_irqsave(&local->lock, flags); + prism2_crypt_deinit_entries(local, 0); + if (!list_empty(&local->crypt_deinit_list)) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", local->dev->name); + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); + +} + + +static void hostap_passive_scan(unsigned long data) +{ + local_info_t *local = (local_info_t *) data; + struct net_device *dev = local->dev; + u16 channel; + + if (local->passive_scan_interval <= 0) + return; + + if (local->passive_scan_state == PASSIVE_SCAN_LISTEN) { + int max_tries = 16; + + /* Even though host system does not really know when the WLAN + * MAC is sending frames, try to avoid changing channels for + * passive scanning when a host-generated frame is being + * transmitted */ + if (test_bit(HOSTAP_BITS_TRANSMIT, &local->bits)) { + printk(KERN_DEBUG "%s: passive scan detected pending " + "TX - delaying\n", dev->name); + local->passive_scan_timer.expires = jiffies + HZ / 10; + add_timer(&local->passive_scan_timer); + return; + } + + do { + local->passive_scan_channel++; + if (local->passive_scan_channel > 14) + local->passive_scan_channel = 1; + max_tries--; + } while (!(local->channel_mask & + (1 << (local->passive_scan_channel - 1))) && + max_tries > 0); + + if (max_tries == 0) { + printk(KERN_INFO "%s: no allowed passive scan channels" + " found\n", dev->name); + return; + } + + printk(KERN_DEBUG "%s: passive scan channel %d\n", + dev->name, local->passive_scan_channel); + channel = local->passive_scan_channel; + local->passive_scan_state = PASSIVE_SCAN_WAIT; + local->passive_scan_timer.expires = jiffies + HZ / 10; + } else { + channel = local->channel; + local->passive_scan_state = PASSIVE_SCAN_LISTEN; + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + } + + if (hfa384x_cmd_callback(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CHANGE_CHANNEL << 8), + channel, NULL, NULL)) + printk(KERN_ERR "%s: passive scan channel set %d " + "failed\n", dev->name, channel); + + add_timer(&local->passive_scan_timer); +} + + +/* Called only as a scheduled task when communications quality values should + * be updated. */ +static void handle_comms_qual_update(void *data) +{ + local_info_t *local = data; + prism2_update_comms_qual(local->dev); +} + + +/* Software watchdog - called as a timer. Hardware interrupt (Tick event) is + * used to monitor that local->last_tick_timer is being updated. If not, + * interrupt busy-loop is assumed and driver tries to recover by masking out + * some events. */ +static void hostap_tick_timer(unsigned long data) +{ + static unsigned long last_inquire = 0; + local_info_t *local = (local_info_t *) data; + local->last_tick_timer = jiffies; + + /* Inquire CommTallies every 10 seconds to keep the statistics updated + * more often during low load and when using 32-bit tallies. */ + if ((!last_inquire || time_after(jiffies, last_inquire + 10 * HZ)) && + !local->hw_downloading && local->hw_ready && + !local->hw_resetting && local->dev_enabled) { + hfa384x_cmd_callback(local->dev, HFA384X_CMDCODE_INQUIRE, + HFA384X_INFO_COMMTALLIES, NULL, NULL); + last_inquire = jiffies; + } + + if ((local->last_comms_qual_update == 0 || + time_after(jiffies, local->last_comms_qual_update + 10 * HZ)) && + (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC)) { + schedule_work(&local->comms_qual_update); + } + + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); +} + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_registers_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + + if (off != 0) { + *eof = 1; + return 0; + } + +#define SHOW_REG(n) \ +p += sprintf(p, #n "=%04x\n", hfa384x_read_reg(local->dev, HFA384X_##n##_OFF)) + + SHOW_REG(CMD); + SHOW_REG(PARAM0); + SHOW_REG(PARAM1); + SHOW_REG(PARAM2); + SHOW_REG(STATUS); + SHOW_REG(RESP0); + SHOW_REG(RESP1); + SHOW_REG(RESP2); + SHOW_REG(INFOFID); + SHOW_REG(CONTROL); + SHOW_REG(SELECT0); + SHOW_REG(SELECT1); + SHOW_REG(OFFSET0); + SHOW_REG(OFFSET1); + SHOW_REG(RXFID); + SHOW_REG(ALLOCFID); + SHOW_REG(TXCOMPLFID); + SHOW_REG(SWSUPPORT0); + SHOW_REG(SWSUPPORT1); + SHOW_REG(SWSUPPORT2); + SHOW_REG(EVSTAT); + SHOW_REG(INTEN); + SHOW_REG(EVACK); + /* Do not read data registers, because they change the state of the + * MAC (offset += 2) */ + /* SHOW_REG(DATA0); */ + /* SHOW_REG(DATA1); */ + SHOW_REG(AUXPAGE); + SHOW_REG(AUXOFFSET); + /* SHOW_REG(AUXDATA); */ +#ifdef PRISM2_PCI + SHOW_REG(PCICOR); + SHOW_REG(PCIHCR); + SHOW_REG(PCI_M0_ADDRH); + SHOW_REG(PCI_M0_ADDRL); + SHOW_REG(PCI_M0_LEN); + SHOW_REG(PCI_M0_CTL); + SHOW_REG(PCI_STATUS); + SHOW_REG(PCI_M1_ADDRH); + SHOW_REG(PCI_M1_ADDRL); + SHOW_REG(PCI_M1_LEN); + SHOW_REG(PCI_M1_CTL); +#endif /* PRISM2_PCI */ + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +struct set_tim_data { + struct list_head list; + int aid; + int set; +}; + +static int prism2_set_tim(struct net_device *dev, int aid, int set) +{ + struct list_head *ptr; + struct set_tim_data *new_entry; + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + new_entry = (struct set_tim_data *) + kmalloc(sizeof(*new_entry), GFP_ATOMIC); + if (new_entry == NULL) { + printk(KERN_DEBUG "%s: prism2_set_tim: kmalloc failed\n", + local->dev->name); + return -ENOMEM; + } + memset(new_entry, 0, sizeof(*new_entry)); + new_entry->aid = aid; + new_entry->set = set; + + spin_lock_bh(&local->set_tim_lock); + list_for_each(ptr, &local->set_tim_list) { + struct set_tim_data *entry = + list_entry(ptr, struct set_tim_data, list); + if (entry->aid == aid) { + PDEBUG(DEBUG_PS2, "%s: prism2_set_tim: aid=%d " + "set=%d ==> %d\n", + local->dev->name, aid, entry->set, set); + entry->set = set; + kfree(new_entry); + new_entry = NULL; + break; + } + } + if (new_entry) + list_add_tail(&new_entry->list, &local->set_tim_list); + spin_unlock_bh(&local->set_tim_lock); + + schedule_work(&local->set_tim_queue); + + return 0; +} + + +static void handle_set_tim_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + struct set_tim_data *entry; + u16 val; + + for (;;) { + entry = NULL; + spin_lock_bh(&local->set_tim_lock); + if (!list_empty(&local->set_tim_list)) { + entry = list_entry(local->set_tim_list.next, + struct set_tim_data, list); + list_del(&entry->list); + } + spin_unlock_bh(&local->set_tim_lock); + if (!entry) + break; + + PDEBUG(DEBUG_PS2, "%s: handle_set_tim_queue: aid=%d set=%d\n", + local->dev->name, entry->aid, entry->set); + + val = entry->aid; + if (entry->set) + val |= 0x8000; + if (hostap_set_word(local->dev, HFA384X_RID_CNFTIMCTRL, val)) { + printk(KERN_DEBUG "%s: set_tim failed (aid=%d " + "set=%d)\n", + local->dev->name, entry->aid, entry->set); + } + + kfree(entry); + } +} + + +static void prism2_clear_set_tim_queue(local_info_t *local) +{ + struct list_head *ptr, *n; + + list_for_each_safe(ptr, n, &local->set_tim_list) { + struct set_tim_data *entry; + entry = list_entry(ptr, struct set_tim_data, list); + list_del(&entry->list); + kfree(entry); + } +} + + +static struct net_device * +prism2_init_local_data(struct prism2_helper_functions *funcs, int card_idx) +{ + struct net_device *dev; + struct hostap_interface *iface; + struct local_info *local; + int len, i, ret; + + if (funcs == NULL) + return NULL; + + len = strlen(dev_template); + if (len >= IFNAMSIZ || strstr(dev_template, "%d") == NULL) { + printk(KERN_WARNING "hostap: Invalid dev_template='%s'\n", + dev_template); + return NULL; + } + + len = sizeof(struct hostap_interface) + + 3 + sizeof(struct local_info) + + 3 + sizeof(struct ap_data); + + dev = alloc_etherdev(len); + if (dev == NULL) + return NULL; + + iface = netdev_priv(dev); + local = (struct local_info *) ((((long) (iface + 1)) + 3) & ~3); + local->ap = (struct ap_data *) ((((long) (local + 1)) + 3) & ~3); + local->dev = iface->dev = dev; + iface->local = local; + iface->type = HOSTAP_INTERFACE_MASTER; + INIT_LIST_HEAD(&local->hostap_interfaces); + + local->hw_module = THIS_MODULE; + +#ifdef PRISM2_IO_DEBUG + local->io_debug_enabled = 1; +#endif /* PRISM2_IO_DEBUG */ + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_m0_buf = (u8 *) kmalloc(sizeof(struct hfa384x_tx_frame) + + PRISM2_DATA_MAXLEN, GFP_DMA); + if (local->bus_m0_buf == NULL) + goto fail; +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + local->func = funcs; + local->func->cmd = hfa384x_cmd; + local->func->read_regs = hfa384x_read_regs; + local->func->get_rid = hfa384x_get_rid; + local->func->set_rid = hfa384x_set_rid; + local->func->hw_enable = prism2_hw_enable; + local->func->hw_config = prism2_hw_config; + local->func->hw_reset = prism2_hw_reset; + local->func->hw_shutdown = prism2_hw_shutdown; + local->func->reset_port = prism2_reset_port; + local->func->schedule_reset = prism2_schedule_reset; +#ifdef PRISM2_DOWNLOAD_SUPPORT + local->func->read_aux = prism2_download_aux_dump; + local->func->download = prism2_download; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + local->func->tx = prism2_tx_80211; + local->func->set_tim = prism2_set_tim; + local->func->need_tx_headroom = 0; /* no need to add txdesc in + * skb->data (FIX: maybe for DMA bus + * mastering? */ + + local->mtu = mtu; + + rwlock_init(&local->iface_lock); + spin_lock_init(&local->txfidlock); + spin_lock_init(&local->cmdlock); + spin_lock_init(&local->baplock); + spin_lock_init(&local->lock); + init_MUTEX(&local->rid_bap_sem); + + if (card_idx < 0 || card_idx >= MAX_PARM_DEVICES) + card_idx = 0; + local->card_idx = card_idx; + + len = strlen(essid); + memcpy(local->essid, essid, + len > MAX_SSID_LEN ? MAX_SSID_LEN : len); + local->essid[MAX_SSID_LEN] = '\0'; + i = GET_INT_PARM(iw_mode, card_idx); + if ((i >= IW_MODE_ADHOC && i <= IW_MODE_REPEAT) || + i == IW_MODE_MONITOR) { + local->iw_mode = i; + } else { + printk(KERN_WARNING "prism2: Unknown iw_mode %d; using " + "IW_MODE_MASTER\n", i); + local->iw_mode = IW_MODE_MASTER; + } + local->channel = GET_INT_PARM(channel, card_idx); + local->beacon_int = GET_INT_PARM(beacon_int, card_idx); + local->dtim_period = GET_INT_PARM(dtim_period, card_idx); + local->wds_max_connections = 16; + local->tx_control = HFA384X_TX_CTRL_FLAGS; + local->manual_retry_count = -1; + local->rts_threshold = 2347; + local->fragm_threshold = 2346; + local->rssi_to_dBm = 100; /* default; to be overriden by + * cnfDbmAdjust, if available */ + local->auth_algs = PRISM2_AUTH_OPEN | PRISM2_AUTH_SHARED_KEY; + local->sram_type = -1; +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + local->bus_master_threshold_rx = GET_INT_PARM(bus_master_threshold_rx, + card_idx); + local->bus_master_threshold_tx = GET_INT_PARM(bus_master_threshold_tx, + card_idx); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + + /* Initialize task queue structures */ + INIT_WORK(&local->reset_queue, handle_reset_queue, local); + INIT_WORK(&local->set_multicast_list_queue, + hostap_set_multicast_list_queue, local->dev); + + INIT_WORK(&local->set_tim_queue, handle_set_tim_queue, local); + INIT_LIST_HEAD(&local->set_tim_list); + spin_lock_init(&local->set_tim_lock); + + INIT_WORK(&local->comms_qual_update, handle_comms_qual_update, local); + + /* Initialize tasklets for handling hardware IRQ related operations + * outside hw IRQ handler */ +#define HOSTAP_TASKLET_INIT(q, f, d) \ +do { memset((q), 0, sizeof(*(q))); (q)->func = (f); (q)->data = (d); } \ +while (0) + HOSTAP_TASKLET_INIT(&local->bap_tasklet, hostap_bap_tasklet, + (unsigned long) local); + + HOSTAP_TASKLET_INIT(&local->info_tasklet, hostap_info_tasklet, + (unsigned long) local); + hostap_info_init(local); + + HOSTAP_TASKLET_INIT(&local->rx_tasklet, + hostap_rx_tasklet, (unsigned long) local); + skb_queue_head_init(&local->rx_list); + + HOSTAP_TASKLET_INIT(&local->sta_tx_exc_tasklet, + hostap_sta_tx_exc_tasklet, (unsigned long) local); + skb_queue_head_init(&local->sta_tx_exc_list); + + INIT_LIST_HEAD(&local->cmd_queue); + init_waitqueue_head(&local->hostscan_wq); + INIT_LIST_HEAD(&local->crypt_deinit_list); + init_timer(&local->crypt_deinit_timer); + local->crypt_deinit_timer.data = (unsigned long) local; + local->crypt_deinit_timer.function = prism2_crypt_deinit_handler; + + init_timer(&local->passive_scan_timer); + local->passive_scan_timer.data = (unsigned long) local; + local->passive_scan_timer.function = hostap_passive_scan; + + init_timer(&local->tick_timer); + local->tick_timer.data = (unsigned long) local; + local->tick_timer.function = hostap_tick_timer; + local->tick_timer.expires = jiffies + 2 * HZ; + add_timer(&local->tick_timer); + + INIT_LIST_HEAD(&local->bss_list); + + hostap_setup_dev(dev, local, 1); + local->saved_eth_header_parse = dev->hard_header_parse; + + dev->hard_start_xmit = hostap_master_start_xmit; + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + + rtnl_lock(); + ret = dev_alloc_name(dev, "wifi%d"); + if (ret >= 0) + ret = register_netdevice(dev); + rtnl_unlock(); + if (ret < 0) { + printk(KERN_WARNING "%s: register netdevice failed!\n", + dev_info); + goto fail; + } + printk(KERN_INFO "%s: Registered netdevice %s\n", dev_info, dev->name); + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("registers", 0, local->proc, + prism2_registers_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + hostap_init_data(local); + return dev; + + fail: +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + free_netdev(dev); + return NULL; +} + + +static int hostap_hw_ready(struct net_device *dev) +{ + struct hostap_interface *iface; + struct local_info *local; + + iface = netdev_priv(dev); + local = iface->local; + local->ddev = hostap_add_interface(local, HOSTAP_INTERFACE_MAIN, 0, + "", dev_template); + + if (local->ddev) { + if (local->iw_mode == IW_MODE_INFRA || + local->iw_mode == IW_MODE_ADHOC) { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + } + hostap_init_proc(local); + hostap_init_ap_proc(local); + return 0; + } + + return -1; +} + + +static void prism2_free_local_data(struct net_device *dev) +{ + struct hostap_tx_callback_info *tx_cb, *tx_cb_prev; + int i; + struct hostap_interface *iface; + struct local_info *local; + struct list_head *ptr, *n; + + if (dev == NULL) + return; + + iface = netdev_priv(dev); + local = iface->local; + + flush_scheduled_work(); + + if (timer_pending(&local->crypt_deinit_timer)) + del_timer(&local->crypt_deinit_timer); + prism2_crypt_deinit_entries(local, 1); + + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + + if (timer_pending(&local->tick_timer)) + del_timer(&local->tick_timer); + + prism2_clear_cmd_queue(local); + + skb_queue_purge(&local->info_list); + skb_queue_purge(&local->rx_list); + skb_queue_purge(&local->sta_tx_exc_list); + + if (local->dev_enabled) + prism2_callback(local, PRISM2_CALLBACK_DISABLE); + + for (i = 0; i < WEP_KEYS; i++) { + struct prism2_crypt_data *crypt = local->crypt[i]; + if (crypt) { + if (crypt->ops) + crypt->ops->deinit(crypt->priv); + kfree(crypt); + local->crypt[i] = NULL; + } + } + + if (local->ap != NULL) + hostap_free_data(local->ap); + +#ifndef PRISM2_NO_PROCFS_DEBUG + if (local->proc != NULL) + remove_proc_entry("registers", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + hostap_remove_proc(local); + + tx_cb = local->tx_callback; + while (tx_cb != NULL) { + tx_cb_prev = tx_cb; + tx_cb = tx_cb->next; + kfree(tx_cb_prev); + } + + hostap_set_hostapd(local, 0, 0); + hostap_set_hostapd_sta(local, 0, 0); + + for (i = 0; i < PRISM2_FRAG_CACHE_LEN; i++) { + if (local->frag_cache[i].skb != NULL) + dev_kfree_skb(local->frag_cache[i].skb); + } + +#ifdef PRISM2_DOWNLOAD_SUPPORT + prism2_download_free_data(local->dl_pri); + prism2_download_free_data(local->dl_sec); +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + list_for_each_safe(ptr, n, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type == HOSTAP_INTERFACE_MASTER) { + /* special handling for this interface below */ + continue; + } + hostap_remove_interface(iface->dev, 0, 1); + } + + prism2_clear_set_tim_queue(local); + + list_for_each_safe(ptr, n, &local->bss_list) { + struct hostap_bss_info *bss = + list_entry(ptr, struct hostap_bss_info, list); + kfree(bss); + } + +#if defined(PRISM2_PCI) && defined(PRISM2_BUS_MASTER) + kfree(local->bus_m0_buf); +#endif /* PRISM2_PCI and PRISM2_BUS_MASTER */ + kfree(local->pda); + kfree(local->last_scan_results); + kfree(local->generic_elem); + + unregister_netdev(local->dev); + free_netdev(local->dev); +} + + +/* These might at some point be compiled separately and used as separate + * kernel modules or linked into one */ +#ifdef PRISM2_DOWNLOAD_SUPPORT +#include "hostap_download.c" +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_CALLBACK +/* External hostap_callback.c file can be used to, e.g., blink activity led. + * This can use platform specific code and must define prism2_callback() + * function (if PRISM2_CALLBACK is not defined, these function calls are not + * used. */ +#include "hostap_callback.c" +#endif /* PRISM2_CALLBACK */ diff -Nru a/drivers/net/wireless/hostap/hostap_info.c b/drivers/net/wireless/hostap/hostap_info.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_info.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,469 @@ +/* Host AP driver Info Frame processing (part of hostap.o module) */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies16(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le16_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies32(local_info_t *local, unsigned char *buf, + int left) +{ + struct hfa384x_comm_tallies32 *tallies; + + if (left < sizeof(struct hfa384x_comm_tallies32)) { + printk(KERN_DEBUG "%s: too short (len=%d) commtallies32 " + "info frame\n", local->dev->name, left); + return; + } + + tallies = (struct hfa384x_comm_tallies32 *) buf; +#define ADD_COMM_TALLIES(name) \ +local->comm_tallies.name += le32_to_cpu(tallies->name) + ADD_COMM_TALLIES(tx_unicast_frames); + ADD_COMM_TALLIES(tx_multicast_frames); + ADD_COMM_TALLIES(tx_fragments); + ADD_COMM_TALLIES(tx_unicast_octets); + ADD_COMM_TALLIES(tx_multicast_octets); + ADD_COMM_TALLIES(tx_deferred_transmissions); + ADD_COMM_TALLIES(tx_single_retry_frames); + ADD_COMM_TALLIES(tx_multiple_retry_frames); + ADD_COMM_TALLIES(tx_retry_limit_exceeded); + ADD_COMM_TALLIES(tx_discards); + ADD_COMM_TALLIES(rx_unicast_frames); + ADD_COMM_TALLIES(rx_multicast_frames); + ADD_COMM_TALLIES(rx_fragments); + ADD_COMM_TALLIES(rx_unicast_octets); + ADD_COMM_TALLIES(rx_multicast_octets); + ADD_COMM_TALLIES(rx_fcs_errors); + ADD_COMM_TALLIES(rx_discards_no_buffer); + ADD_COMM_TALLIES(tx_discards_wrong_sa); + ADD_COMM_TALLIES(rx_discards_wep_undecryptable); + ADD_COMM_TALLIES(rx_message_in_msg_fragments); + ADD_COMM_TALLIES(rx_message_in_bad_msg_fragments); +#undef ADD_COMM_TALLIES +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_commtallies(local_info_t *local, unsigned char *buf, + int left) +{ + if (local->tallies32) + prism2_info_commtallies32(local, buf, left); + else + prism2_info_commtallies16(local, buf, left); +} + + +#ifndef PRISM2_NO_STATION_MODES +#ifndef PRISM2_NO_DEBUG +static const char* hfa384x_linkstatus_str(u16 linkstatus) +{ + switch (linkstatus) { + case HFA384X_LINKSTATUS_CONNECTED: + return "Connected"; + case HFA384X_LINKSTATUS_DISCONNECTED: + return "Disconnected"; + case HFA384X_LINKSTATUS_AP_CHANGE: + return "Access point change"; + case HFA384X_LINKSTATUS_AP_OUT_OF_RANGE: + return "Access point out of range"; + case HFA384X_LINKSTATUS_AP_IN_RANGE: + return "Access point in range"; + case HFA384X_LINKSTATUS_ASSOC_FAILED: + return "Association failed"; + default: + return "Unknown"; + } +} +#endif /* PRISM2_NO_DEBUG */ + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_linkstatus(local_info_t *local, unsigned char *buf, + int left) +{ + u16 val; + int non_sta_mode; + + /* Alloc new JoinRequests to occur since LinkStatus for the previous + * has been received */ + local->last_join_time = 0; + + if (left != 2) { + printk(KERN_DEBUG "%s: invalid linkstatus info frame " + "length %d\n", local->dev->name, left); + return; + } + + non_sta_mode = local->iw_mode == IW_MODE_MASTER || + local->iw_mode == IW_MODE_REPEAT || + local->iw_mode == IW_MODE_MONITOR; + + val = buf[0] | (buf[1] << 8); + if (!non_sta_mode || val != HFA384X_LINKSTATUS_DISCONNECTED) { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus=%d (%s)\n", + local->dev->name, val, hfa384x_linkstatus_str(val)); + } + + if (non_sta_mode) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + return; + } + + /* Get current BSSID later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_LINKSTATUS, &local->pending_info); + local->prev_link_status = val; + schedule_work(&local->info_queue); +} + + +static void prism2_host_roaming(local_info_t *local) +{ + struct hfa384x_join_request req; + struct net_device *dev = local->dev; + struct hfa384x_scan_result *selected, *entry; + int i; + unsigned long flags; + + if (local->last_join_time && + time_before(jiffies, local->last_join_time + 10 * HZ)) { + PDEBUG(DEBUG_EXTRA, "%s: last join request has not yet been " + "completed - waiting for it before issuing new one\n", + dev->name); + return; + } + + /* ScanResults are sorted: first ESS results in decreasing signal + * quality then IBSS results in similar order. + * Trivial roaming policy: just select the first entry. + * This could probably be improved by adding hysteresis to limit + * number of handoffs, etc. + * + * Could do periodic RID_SCANREQUEST or Inquire F101 to get new + * ScanResults */ + spin_lock_irqsave(&local->lock, flags); + if (local->last_scan_results == NULL || + local->last_scan_results_count == 0) { + spin_unlock_irqrestore(&local->lock, flags); + PDEBUG(DEBUG_EXTRA, "%s: no scan results for host roaming\n", + dev->name); + return; + } + + selected = &local->last_scan_results[0]; + + if (local->preferred_ap[0] || local->preferred_ap[1] || + local->preferred_ap[2] || local->preferred_ap[3] || + local->preferred_ap[4] || local->preferred_ap[5]) { + /* Try to find preferred AP */ + PDEBUG(DEBUG_EXTRA, "%s: Preferred AP BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + for (i = 0; i < local->last_scan_results_count; i++) { + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, 6) == 0) + { + PDEBUG(DEBUG_EXTRA, "%s: using preferred AP " + "selection\n", dev->name); + selected = entry; + break; + } + } + } + + memcpy(req.bssid, selected->bssid, 6); + req.channel = selected->chid; + spin_unlock_irqrestore(&local->lock, flags); + + PDEBUG(DEBUG_EXTRA, "%s: JoinRequest: BSSID=" MACSTR " channel=%d\n", + dev->name, MAC2STR(req.bssid), le16_to_cpu(req.channel)); + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest failed\n", dev->name); + } + local->last_join_time = jiffies; +} + + +static void hostap_report_scan_complete(local_info_t *local) +{ + union iwreq_data wrqu; + + /* Inform user space about new scan results (just empty event, + * SIOCGIWSCAN can be used to fetch data */ + wrqu.data.length = 0; + wrqu.data.flags = 0; + wireless_send_event(local->dev, SIOCGIWSCAN, &wrqu, NULL); + + /* Allow SIOCGIWSCAN handling to occur since we have received + * scanning result */ + local->scan_timestamp = 0; +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_scanresults(local_info_t *local, unsigned char *buf, + int left) +{ + u16 *pos; + int new_count; + unsigned long flags; + struct hfa384x_scan_result *results, *prev; + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid scanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + pos++; + pos++; + left -= 4; + + new_count = left / sizeof(struct hfa384x_scan_result); + results = kmalloc(new_count * sizeof(struct hfa384x_scan_result), + GFP_ATOMIC); + if (results == NULL) + return; + memcpy(results, pos, new_count * sizeof(struct hfa384x_scan_result)); + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_SCAN; + prev = local->last_scan_results; + local->last_scan_results = results; + local->last_scan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); + + /* Perform rest of ScanResults handling later in scheduled task */ + set_bit(PRISM2_INFO_PENDING_SCANRESULTS, &local->pending_info); + schedule_work(&local->info_queue); +} + + +/* Called only as a tasklet (software IRQ) */ +static void prism2_info_hostscanresults(local_info_t *local, + unsigned char *buf, int left) +{ + int i, result_size, copy_len, new_count; + struct hfa384x_hostscan_result *results, *prev; + unsigned long flags; + u16 *pos; + u8 *ptr; + + wake_up_interruptible(&local->hostscan_wq); + + if (left < 4) { + printk(KERN_DEBUG "%s: invalid hostscanresult info frame " + "length %d\n", local->dev->name, left); + return; + } + + pos = (u16 *) buf; + copy_len = result_size = le16_to_cpu(*pos); + if (result_size == 0) { + printk(KERN_DEBUG "%s: invalid result_size (0) in " + "hostscanresults\n", local->dev->name); + return; + } + if (copy_len > sizeof(struct hfa384x_hostscan_result)) + copy_len = sizeof(struct hfa384x_hostscan_result); + + pos++; + pos++; + left -= 4; + ptr = (u8 *) pos; + + new_count = left / result_size; + results = kmalloc(new_count * sizeof(struct hfa384x_hostscan_result), + GFP_ATOMIC); + if (results == NULL) + return; + memset(results, 0, new_count * sizeof(struct hfa384x_hostscan_result)); + + for (i = 0; i < new_count; i++) { + memcpy(&results[i], ptr, copy_len); + ptr += result_size; + left -= result_size; + } + + if (left) { + printk(KERN_DEBUG "%s: short HostScan result entry (%d/%d)\n", + local->dev->name, left, result_size); + } + + spin_lock_irqsave(&local->lock, flags); + local->last_scan_type = PRISM2_HOSTSCAN; + prev = local->last_hostscan_results; + local->last_hostscan_results = results; + local->last_hostscan_results_count = new_count; + spin_unlock_irqrestore(&local->lock, flags); + kfree(prev); + + hostap_report_scan_complete(local); +} +#endif /* PRISM2_NO_STATION_MODES */ + + +/* Called only as a tasklet (software IRQ) */ +void hostap_info_process(local_info_t *local, struct sk_buff *skb) +{ + struct hfa384x_info_frame *info; + unsigned char *buf; + int left; +#ifndef PRISM2_NO_DEBUG + int i; +#endif /* PRISM2_NO_DEBUG */ + + info = (struct hfa384x_info_frame *) skb->data; + buf = skb->data + sizeof(*info); + left = skb->len - sizeof(*info); + + switch (info->type) { + case HFA384X_INFO_COMMTALLIES: + prism2_info_commtallies(local, buf, left); + break; + +#ifndef PRISM2_NO_STATION_MODES + case HFA384X_INFO_LINKSTATUS: + prism2_info_linkstatus(local, buf, left); + break; + + case HFA384X_INFO_SCANRESULTS: + prism2_info_scanresults(local, buf, left); + break; + + case HFA384X_INFO_HOSTSCANRESULTS: + prism2_info_hostscanresults(local, buf, left); + break; +#endif /* PRISM2_NO_STATION_MODES */ + +#ifndef PRISM2_NO_DEBUG + default: + PDEBUG(DEBUG_EXTRA, "%s: INFO - len=%d type=0x%04x\n", + local->dev->name, info->len, info->type); + PDEBUG(DEBUG_EXTRA, "Unknown info frame:"); + for (i = 0; i < (left < 100 ? left : 100); i++) + PDEBUG2(DEBUG_EXTRA, " %02x", buf[i]); + PDEBUG2(DEBUG_EXTRA, "\n"); + break; +#endif /* PRISM2_NO_DEBUG */ + } +} + + +#ifndef PRISM2_NO_STATION_MODES +static void handle_info_queue_linkstatus(local_info_t *local) +{ + int val = local->prev_link_status; + int connected; + union iwreq_data wrqu; + + connected = + val == HFA384X_LINKSTATUS_CONNECTED || + val == HFA384X_LINKSTATUS_AP_CHANGE || + val == HFA384X_LINKSTATUS_AP_IN_RANGE; + + if (local->func->get_rid(local->dev, HFA384X_RID_CURRENTBSSID, + local->bssid, ETH_ALEN, 1) < 0) { + printk(KERN_DEBUG "%s: could not read CURRENTBSSID after " + "LinkStatus event\n", local->dev->name); + } else { + PDEBUG(DEBUG_EXTRA, "%s: LinkStatus: BSSID=" MACSTR "\n", + local->dev->name, + MAC2STR((unsigned char *) local->bssid)); + if (local->wds_type & HOSTAP_WDS_AP_CLIENT) + hostap_add_sta(local->ap, local->bssid); + } + + /* Get BSSID if we have a valid AP address */ + if (connected) { + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + memcpy(wrqu.ap_addr.sa_data, local->bssid, ETH_ALEN); + } else { + netif_carrier_off(local->dev); + netif_carrier_off(local->ddev); + memset(wrqu.ap_addr.sa_data, 0, ETH_ALEN); + } + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(local->dev, SIOCGIWAP, &wrqu, NULL); +} + + +static void handle_info_queue_scanresults(local_info_t *local) +{ + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) + prism2_host_roaming(local); +} + + +/* Called only as scheduled task after receiving info frames (used to avoid + * pending too much time in HW IRQ handler). */ +static void handle_info_queue(void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (test_and_clear_bit(PRISM2_INFO_PENDING_LINKSTATUS, + &local->pending_info)) + handle_info_queue_linkstatus(local); + + if (test_and_clear_bit(PRISM2_INFO_PENDING_SCANRESULTS, + &local->pending_info)) + handle_info_queue_scanresults(local); +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_info_init(local_info_t *local) +{ + skb_queue_head_init(&local->info_list); +#ifndef PRISM2_NO_STATION_MODES + INIT_WORK(&local->info_queue, handle_info_queue, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +EXPORT_SYMBOL(hostap_info_init); +EXPORT_SYMBOL(hostap_info_process); diff -Nru a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_ioctl.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,3624 @@ +/* ioctl() (mostly Linux Wireless Extensions) routines for Host AP driver */ + +#ifdef in_atomic +/* Get kernel_locked() for in_atomic() */ +#include +#endif +#include + + +static struct iw_statistics *hostap_get_wireless_stats(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct iw_statistics *wstats; + + iface = netdev_priv(dev); + local = iface->local; + + /* Why are we doing that ? Jean II */ + if (iface->type != HOSTAP_INTERFACE_MAIN) + return NULL; + + wstats = &local->wstats; + + wstats->status = 0; + wstats->discard.code = + local->comm_tallies.rx_discards_wep_undecryptable; + wstats->discard.misc = + local->comm_tallies.rx_fcs_errors + + local->comm_tallies.rx_discards_no_buffer + + local->comm_tallies.tx_discards_wrong_sa; + + wstats->discard.retries = + local->comm_tallies.tx_retry_limit_exceeded; + wstats->discard.fragment = + local->comm_tallies.rx_message_in_bad_msg_fragments; + + if (local->iw_mode != IW_MODE_MASTER && + local->iw_mode != IW_MODE_REPEAT) { + int update = 1; +#ifdef in_atomic + /* RID reading might sleep and it must not be called in + * interrupt context or while atomic. However, this + * function seems to be called while atomic (at least in Linux + * 2.5.59). Update signal quality values only if in suitable + * context. Otherwise, previous values read from tick timer + * will be used. */ + if (in_atomic()) + update = 0; +#endif /* in_atomic */ + + if (update && prism2_update_comms_qual(dev) == 0) + wstats->qual.updated = 7; + + wstats->qual.qual = local->comms_qual; + wstats->qual.level = local->avg_signal; + wstats->qual.noise = local->avg_noise; + } else { + wstats->qual.qual = 0; + wstats->qual.level = 0; + wstats->qual.noise = 0; + wstats->qual.updated = 0; + } + + return wstats; +} + + +static int prism2_get_datarates(struct net_device *dev, u8 *rates) +{ + struct hostap_interface *iface; + local_info_t *local; + u8 buf[12]; + int len; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + len = local->func->get_rid(dev, HFA384X_RID_SUPPORTEDDATARATES, buf, + sizeof(buf), 0); + if (len < 2) + return 0; + + val = le16_to_cpu(*(u16 *) buf); /* string length */ + + if (len - 2 < val || val > 10) + return 0; + + memcpy(rates, buf + 2, val); + return val; +} + + +static int prism2_get_name(struct net_device *dev, + struct iw_request_info *info, + char *name, char *extra) +{ + u8 rates[10]; + int len, i, over2 = 0; + + len = prism2_get_datarates(dev, rates); + + for (i = 0; i < len; i++) { + if (rates[i] == 0x0b || rates[i] == 0x16) { + over2 = 1; + break; + } + } + + strcpy(name, over2 ? "IEEE 802.11b" : "IEEE 802.11-DS"); + + return 0; +} + + +static void prism2_crypt_delayed_deinit(local_info_t *local, + struct prism2_crypt_data **crypt) +{ + struct prism2_crypt_data *tmp; + unsigned long flags; + + tmp = *crypt; + *crypt = NULL; + + if (tmp == NULL) + return; + + /* must not run ops->deinit() while there may be pending encrypt or + * decrypt operations. Use a list of delayed deinits to avoid needing + * locking. */ + + spin_lock_irqsave(&local->lock, flags); + list_add(&tmp->list, &local->crypt_deinit_list); + if (!timer_pending(&local->crypt_deinit_timer)) { + local->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&local->crypt_deinit_timer); + } + spin_unlock_irqrestore(&local->lock, flags); +} + + +static int prism2_ioctl_siwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *keybuf) +{ + struct hostap_interface *iface; + local_info_t *local; + int i; + struct prism2_crypt_data **crypt; + + iface = netdev_priv(dev); + local = iface->local; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = &local->crypt[i]; + + if (erq->flags & IW_ENCODE_DISABLED) { + if (*crypt) + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm */ + prism2_crypt_delayed_deinit(local, crypt); + } + + if (*crypt == NULL) { + struct prism2_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("hostap_crypt_wep"); + new_crypt->ops = hostap_get_crypto_ops("WEP"); + } + if (new_crypt->ops) + new_crypt->priv = new_crypt->ops->init(i); + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module hostap_crypt_wep.o\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + if (erq->length > 0) { + int len = erq->length <= 5 ? 5 : 13; + int first = 1, j; + if (len > erq->length) + memset(keybuf + erq->length, 0, len - erq->length); + (*crypt)->ops->set_key(keybuf, len, NULL, (*crypt)->priv); + for (j = 0; j < WEP_KEYS; j++) { + if (j != i && local->crypt[j]) { + first = 0; + break; + } + } + if (first) + local->tx_keyidx = i; + } else { + /* No key data - just set the default TX key index */ + local->tx_keyidx = i; + } + + done: + local->open_wep = erq->flags & IW_ENCODE_OPEN; + + if (hostap_set_encryption(local)) { + printk(KERN_DEBUG "%s: set_encryption failed\n", dev->name); + return -EINVAL; + } + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (local->iw_mode != IW_MODE_INFRA && local->func->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + + return 0; +} + + +static int prism2_ioctl_giwencode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *erq, char *key) +{ + struct hostap_interface *iface; + local_info_t *local; + int i, len; + u16 val; + struct prism2_crypt_data *crypt; + + iface = netdev_priv(dev); + local = iface->local; + + i = erq->flags & IW_ENCODE_INDEX; + if (i < 1 || i > 4) + i = local->tx_keyidx; + else + i--; + if (i < 0 || i >= WEP_KEYS) + return -EINVAL; + + crypt = local->crypt[i]; + erq->flags = i + 1; + + if (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + /* Reads from HFA384X_RID_CNFDEFAULTKEY* return bogus values, so show + * the keys from driver buffer */ + len = crypt->ops->get_key(key, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + if (local->func->get_rid(dev, HFA384X_RID_CNFWEPFLAGS, &val, 2, 1) < 0) + { + printk("CNFWEPFLAGS reading failed\n"); + return -EOPNOTSUPP; + } + le16_to_cpus(&val); + if (val & HFA384X_WEPFLAGS_PRIVACYINVOKED) + erq->flags |= IW_ENCODE_ENABLED; + else + erq->flags |= IW_ENCODE_DISABLED; + if (val & HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED) + erq->flags |= IW_ENCODE_RESTRICTED; + else + erq->flags |= IW_ENCODE_OPEN; + + return 0; +} + + +static int hostap_set_rate(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret, basic_rates; + + iface = netdev_priv(dev); + local = iface->local; + + basic_rates = local->basic_rates & local->tx_rate_control; + if (!basic_rates || basic_rates != local->basic_rates) { + printk(KERN_INFO "%s: updating basic rate set automatically " + "to match with the new supported rate set\n", + dev->name); + if (!basic_rates) + basic_rates = local->tx_rate_control; + + local->basic_rates = basic_rates; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + basic_rates)) + printk(KERN_WARNING "%s: failed to set " + "cnfBasicRates\n", dev->name); + } + + ret = (hostap_set_word(dev, HFA384X_RID_TXRATECONTROL, + local->tx_rate_control) || + hostap_set_word(dev, HFA384X_RID_CNFSUPPORTEDRATES, + local->tx_rate_control) || + local->func->reset_port(dev)); + + if (ret) { + printk(KERN_WARNING "%s: TXRateControl/cnfSupportedRates " + "setting to 0x%x failed\n", + dev->name, local->tx_rate_control); + } + + /* Update TX rate configuration for all STAs based on new operational + * rate set. */ + hostap_update_rates(local); + + return ret; +} + + +static int prism2_ioctl_siwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->fixed) { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } else { + switch (rrq->value) { + case 11000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + case 5500000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS; + break; + case 2000000: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS; + break; + case 1000000: + local->tx_rate_control = HFA384X_RATES_1MBPS; + break; + default: + local->tx_rate_control = HFA384X_RATES_1MBPS | + HFA384X_RATES_2MBPS | HFA384X_RATES_5MBPS | + HFA384X_RATES_11MBPS; + break; + } + } + + return hostap_set_rate(dev); +} + + +static int prism2_ioctl_giwrate(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + u16 val; + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_TXRATECONTROL, &val, 2, 1) < + 0) + return -EINVAL; + + if ((val & 0x1) && (val > 1)) + rrq->fixed = 0; + else + rrq->fixed = 1; + + if (local->iw_mode == IW_MODE_MASTER && local->ap != NULL && + !local->fw_tx_rate_control) { + /* HFA384X_RID_CURRENTTXRATE seems to always be 2 Mbps in + * Host AP mode, so use the recorded TX rate of the last sent + * frame */ + rrq->value = local->ap->last_tx_rate > 0 ? + local->ap->last_tx_rate * 100000 : 11000000; + return 0; + } + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTTXRATE, &val, 2, 1) < + 0) + return -EINVAL; + + switch (val) { + case HFA384X_RATES_1MBPS: + rrq->value = 1000000; + break; + case HFA384X_RATES_2MBPS: + rrq->value = 2000000; + break; + case HFA384X_RATES_5MBPS: + rrq->value = 5500000; + break; + case HFA384X_RATES_11MBPS: + rrq->value = 11000000; + break; + default: + /* should not happen */ + rrq->value = 11000000; + ret = -EINVAL; + break; + } + + return ret; +} + + +static int prism2_ioctl_siwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* Set the desired AP density */ + if (sens->value < 1 || sens->value > 3) + return -EINVAL; + + if (hostap_set_word(dev, HFA384X_RID_CNFSYSTEMSCALE, sens->value) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwsens(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *sens, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + /* Get the current AP density */ + if (local->func->get_rid(dev, HFA384X_RID_CNFSYSTEMSCALE, &val, 2, 1) < + 0) + return -EINVAL; + + sens->value = __le16_to_cpu(val); + sens->fixed = 1; + + return 0; +} + + +/* Deprecated in new wireless extension API */ +static int prism2_ioctl_giwaplist(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct sockaddr addr[IW_MAX_AP]; + struct iw_quality qual[IW_MAX_AP]; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode != IW_MODE_MASTER) { + printk(KERN_DEBUG "SIOCGIWAPLIST is currently only supported " + "in Host AP mode\n"); + data->length = 0; + return -EOPNOTSUPP; + } + + data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1); + + memcpy(extra, &addr, sizeof(addr[0]) * data->length); + data->flags = 1; /* has quality information */ + memcpy(extra + sizeof(addr[0]) * data->length, &qual, + sizeof(qual[0]) * data->length); + + return 0; +} + + +static int prism2_ioctl_siwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = __constant_cpu_to_le16(2347); + else if (rts->value < 0 || rts->value > 2347) + return -EINVAL; + else + val = __cpu_to_le16(rts->value); + + if (local->func->set_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2) || + local->func->reset_port(dev)) + return -EINVAL; + + local->rts_threshold = rts->value; + + return 0; +} + +static int prism2_ioctl_giwrts(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_RTSTHRESHOLD, &val, 2, 1) < + 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2347); + rts->fixed = 1; + + return 0; +} + + +static int prism2_ioctl_siwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (rts->disabled) + val = __constant_cpu_to_le16(2346); + else if (rts->value < 256 || rts->value > 2346) + return -EINVAL; + else + val = __cpu_to_le16(rts->value & ~0x1); /* even numbers only */ + + local->fragm_threshold = rts->value & ~0x1; + if (local->func->set_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, &val, + 2) + || local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfrag(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rts, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_FRAGMENTATIONTHRESHOLD, + &val, 2, 1) < 0) + return -EINVAL; + + rts->value = __le16_to_cpu(val); + rts->disabled = (rts->value == 2346); + rts->fixed = 1; + + return 0; +} + + +#ifndef PRISM2_NO_STATION_MODES +static int hostap_join_ap(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_join_request req; + unsigned long flags; + int i; + struct hfa384x_scan_result *entry; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(req.bssid, local->preferred_ap, ETH_ALEN); + req.channel = 0; + + spin_lock_irqsave(&local->lock, flags); + for (i = 0; i < local->last_scan_results_count; i++) { + if (!local->last_scan_results) + break; + entry = &local->last_scan_results[i]; + if (memcmp(local->preferred_ap, entry->bssid, ETH_ALEN) == 0) { + req.channel = entry->chid; + break; + } + } + spin_unlock_irqrestore(&local->lock, flags); + + if (local->func->set_rid(dev, HFA384X_RID_JOINREQUEST, &req, + sizeof(req))) { + printk(KERN_DEBUG "%s: JoinRequest " MACSTR + " failed\n", + dev->name, MAC2STR(local->preferred_ap)); + return -1; + } + + printk(KERN_DEBUG "%s: Trying to join BSSID " MACSTR "\n", + dev->name, MAC2STR(local->preferred_ap)); + + return 0; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memcpy(local->preferred_ap, &ap_addr->sa_data, ETH_ALEN); + + if (local->host_roaming == 1 && local->iw_mode == IW_MODE_INFRA) { + struct hfa384x_scan_request scan_req; + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, + &scan_req, sizeof(scan_req))) { + printk(KERN_DEBUG "%s: ScanResults request failed - " + "preferred AP delayed to next unsolicited " + "scan\n", dev->name); + } + } else if (local->host_roaming == 2 && + local->iw_mode == IW_MODE_INFRA) { + if (hostap_join_ap(dev)) + return -EINVAL; + } else { + printk(KERN_DEBUG "%s: Preferred AP (SIOCSIWAP) is used only " + "in Managed mode when host_roaming is enabled\n", + dev->name); + } + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + +static int prism2_ioctl_giwap(struct net_device *dev, + struct iw_request_info *info, + struct sockaddr *ap_addr, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + ap_addr->sa_family = ARPHRD_ETHER; + switch (iface->type) { + case HOSTAP_INTERFACE_AP: + memcpy(&ap_addr->sa_data, dev->dev_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_STA: + memcpy(&ap_addr->sa_data, local->assoc_ap_addr, ETH_ALEN); + break; + case HOSTAP_INTERFACE_WDS: + memcpy(&ap_addr->sa_data, iface->u.wds.remote_addr, ETH_ALEN); + break; + default: + if (local->func->get_rid(dev, HFA384X_RID_CURRENTBSSID, + &ap_addr->sa_data, ETH_ALEN, 1) < 0) + return -EOPNOTSUPP; + + /* local->bssid is also updated in LinkStatus handler when in + * station mode */ + memcpy(local->bssid, &ap_addr->sa_data, ETH_ALEN); + break; + } + + return 0; +} + + +static int prism2_ioctl_siwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + memset(local->name, 0, sizeof(local->name)); + memcpy(local->name, nickname, data->length); + local->name_set = 1; + + if (hostap_set_string(dev, HFA384X_RID_CNFOWNNAME, local->name) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwnickn(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *nickname) +{ + struct hostap_interface *iface; + local_info_t *local; + int len; + char name[MAX_NAME_LEN + 3]; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + len = local->func->get_rid(dev, HFA384X_RID_CNFOWNNAME, + &name, MAX_NAME_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) name); + if (len > MAX_NAME_LEN + 2 || len < 0 || val > MAX_NAME_LEN) + return -EOPNOTSUPP; + + name[val + 2] = '\0'; + data->length = val + 1; + memcpy(nickname, name + 2, val + 1); + + return 0; +} + + +static int prism2_ioctl_siwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + /* freq => chan. */ + if (freq->e == 1 && + freq->m / 100000 >= freq_list[0] && + freq->m / 100000 <= freq_list[FREQ_COUNT - 1]) { + int ch; + int fr = freq->m / 100000; + for (ch = 0; ch < FREQ_COUNT; ch++) { + if (fr == freq_list[ch]) { + freq->e = 0; + freq->m = ch + 1; + break; + } + } + } + + if (freq->e != 0 || freq->m < 1 || freq->m > FREQ_COUNT || + !(local->channel_mask & (1 << (freq->m - 1)))) + return -EINVAL; + + local->channel = freq->m; /* channel is used in prism2_setup_rids() */ + if (hostap_set_word(dev, HFA384X_RID_CNFOWNCHANNEL, local->channel) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwfreq(struct net_device *dev, + struct iw_request_info *info, + struct iw_freq *freq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_CURRENTCHANNEL, &val, 2, 1) < + 0) + return -EINVAL; + + le16_to_cpus(&val); + if (val < 1 || val > FREQ_COUNT) + return -EINVAL; + + freq->m = freq_list[val - 1] * 100000; + freq->e = 1; + + return 0; +} + + +static void hostap_monitor_set_type(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return; + + if (local->monitor_type == PRISM2_MONITOR_PRISM || + local->monitor_type == PRISM2_MONITOR_CAPHDR) { + dev->type = ARPHRD_IEEE80211_PRISM; + dev->hard_header_parse = + hostap_80211_prism_header_parse; + } else { + dev->type = ARPHRD_IEEE80211; + dev->hard_header_parse = hostap_80211_header_parse; + } +} + + +static int prism2_ioctl_siwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *ssid) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + if (data->flags == 0) + ssid[0] = '\0'; /* ANY */ + + if (local->iw_mode == IW_MODE_MASTER && ssid[0] == '\0') { + /* Setting SSID to empty string seems to kill the card in + * Host AP mode */ + printk(KERN_DEBUG "%s: Host AP mode does not support " + "'Any' essid\n", dev->name); + return -EINVAL; + } + + memcpy(local->essid, ssid, data->length); + local->essid[data->length] = '\0'; + + if ((!local->fw_ap && + hostap_set_string(dev, HFA384X_RID_CNFDESIREDSSID, local->essid)) + || hostap_set_string(dev, HFA384X_RID_CNFOWNSSID, local->essid) || + local->func->reset_port(dev)) + return -EINVAL; + + return 0; +} + +static int prism2_ioctl_giwessid(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *essid) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + if (iface->type == HOSTAP_INTERFACE_WDS) + return -EOPNOTSUPP; + + data->flags = 1; /* active */ + if (local->iw_mode == IW_MODE_MASTER) { + data->length = strlen(local->essid); + memcpy(essid, local->essid, IW_ESSID_MAX_SIZE); + } else { + int len; + char ssid[MAX_SSID_LEN + 2]; + memset(ssid, 0, sizeof(ssid)); + len = local->func->get_rid(dev, HFA384X_RID_CURRENTSSID, + &ssid, MAX_SSID_LEN + 2, 0); + val = __le16_to_cpu(*(u16 *) ssid); + if (len > MAX_SSID_LEN + 2 || len < 0 || val > MAX_SSID_LEN) { + return -EOPNOTSUPP; + } + data->length = val; + memcpy(essid, ssid + 2, IW_ESSID_MAX_SIZE); + } + + return 0; +} + + +static int prism2_ioctl_giwrange(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + struct iw_range *range = (struct iw_range *) extra; + u8 rates[10]; + u16 val; + int i, len, over2; + + iface = netdev_priv(dev); + local = iface->local; + + data->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + /* TODO: could fill num_txpower and txpower array with + * something; however, there are 128 different values.. */ + + range->txpower_capa = IW_TXPOW_DBM; + + if (local->iw_mode == IW_MODE_INFRA || local->iw_mode == IW_MODE_ADHOC) + { + range->min_pmp = 1 * 1024; + range->max_pmp = 65535 * 1024; + range->min_pmt = 1 * 1024; + range->max_pmt = 1000 * 1024; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | + IW_POWER_UNICAST_R | IW_POWER_ALL_R; + } + + range->we_version_compiled = WIRELESS_EXT; + range->we_version_source = 16; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT; + range->min_retry = 0; + range->max_retry = 255; + + range->num_channels = FREQ_COUNT; + + val = 0; + for (i = 0; i < FREQ_COUNT; i++) { + if (local->channel_mask & (1 << i)) { + range->freq[val].i = i + 1; + range->freq[val].m = freq_list[i] * 100000; + range->freq[val].e = 1; + val++; + } + if (val == IW_MAX_FREQUENCIES) + break; + } + range->num_frequency = val; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) { + range->max_qual.qual = 70; /* what is correct max? This was not + * documented exactly. At least + * 69 has been observed. */ + range->max_qual.level = 0; /* dB */ + range->max_qual.noise = 0; /* dB */ + + /* What would be suitable values for "average/typical" qual? */ + range->avg_qual.qual = 20; + range->avg_qual.level = -60; + range->avg_qual.noise = -95; + } else { + range->max_qual.qual = 92; /* 0 .. 92 */ + range->max_qual.level = 154; /* 27 .. 154 */ + range->max_qual.noise = 154; /* 27 .. 154 */ + } + range->sensitivity = 3; + + range->max_encoding_tokens = WEP_KEYS; + range->num_encoding_sizes = 2; + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + + over2 = 0; + len = prism2_get_datarates(dev, rates); + range->num_bitrates = 0; + for (i = 0; i < len; i++) { + if (range->num_bitrates < IW_MAX_BITRATES) { + range->bitrate[range->num_bitrates] = + rates[i] * 500000; + range->num_bitrates++; + } + if (rates[i] == 0x0b || rates[i] == 0x16) + over2 = 1; + } + /* estimated maximum TCP throughput values (bps) */ + range->throughput = over2 ? 5500000 : 1500000; + + range->min_rts = 0; + range->max_rts = 2347; + range->min_frag = 256; + range->max_frag = 2346; + + /* Event capability (kernel + driver) */ + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | + IW_EVENT_CAPA_MASK(IWEVCUSTOM) | + IW_EVENT_CAPA_MASK(IWEVREGISTERED) | + IW_EVENT_CAPA_MASK(IWEVEXPIRED)); + + return 0; +} + + +static int hostap_monitor_mode_enable(local_info_t *local) +{ + struct net_device *dev = local->dev; + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_PSEUDO_IBSS)) { + printk(KERN_DEBUG "Port type setting for monitor mode " + "failed\n"); + return -EOPNOTSUPP; + } + + /* Host decrypt is needed to get the IV and ICV fields; + * however, monitor mode seems to remove WEP flag from frame + * control field */ + if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, + HFA384X_WEPFLAGS_HOSTENCRYPT | + HFA384X_WEPFLAGS_HOSTDECRYPT)) { + printk(KERN_DEBUG "WEP flags setting failed\n"); + return -EOPNOTSUPP; + } + + if (local->func->reset_port(dev) || + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_MONITOR << 8), + 0, NULL, NULL)) { + printk(KERN_DEBUG "Setting monitor mode failed\n"); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int hostap_monitor_mode_disable(local_info_t *local) +{ + struct net_device *dev = local->ddev; + + if (dev == NULL) + return -1; + + printk(KERN_DEBUG "%s: Disabling monitor mode\n", dev->name); + dev->type = ARPHRD_ETHER; + dev->hard_header_parse = local->saved_eth_header_parse; + if (local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_STOP << 8), + 0, NULL, NULL)) + return -1; + return hostap_set_encryption(local); +} + + +static int prism2_ioctl_siwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int double_reset = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (*mode != IW_MODE_ADHOC && *mode != IW_MODE_INFRA && + *mode != IW_MODE_MASTER && *mode != IW_MODE_REPEAT && + *mode != IW_MODE_MONITOR) + return -EOPNOTSUPP; + +#ifdef PRISM2_NO_STATION_MODES + if (*mode == IW_MODE_ADHOC || *mode == IW_MODE_INFRA) + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ + + if (*mode == local->iw_mode) + return 0; + + if (*mode == IW_MODE_MASTER && local->essid[0] == '\0') { + printk(KERN_WARNING "%s: empty SSID not allowed in Master " + "mode\n", dev->name); + return -EINVAL; + } + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_disable(local); + + if (local->iw_mode == IW_MODE_ADHOC && *mode == IW_MODE_MASTER) { + /* There seems to be a firmware bug in at least STA f/w v1.5.6 + * that leaves beacon frames to use IBSS type when moving from + * IBSS to Host AP mode. Doing double Port0 reset seems to be + * enough to workaround this. */ + double_reset = 1; + } + + printk(KERN_DEBUG "prism2: %s: operating mode changed " + "%d -> %d\n", dev->name, local->iw_mode, *mode); + local->iw_mode = *mode; + + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_mode_enable(local); + else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " + "a workaround for firmware bug in Host AP mode WEP\n", + dev->name); + local->host_encrypt = 1; + } + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) + return -EOPNOTSUPP; + + if (local->func->reset_port(dev)) + return -EINVAL; + if (double_reset && local->func->reset_port(dev)) + return -EINVAL; + + if (local->iw_mode != IW_MODE_INFRA && local->iw_mode != IW_MODE_ADHOC) + { + /* netif_carrier is used only in client modes for now, so make + * sure carrier is on when moving to non-client modes. */ + netif_carrier_on(local->dev); + netif_carrier_on(local->ddev); + } + return 0; +} + + +static int prism2_ioctl_giwmode(struct net_device *dev, + struct iw_request_info *info, + __u32 *mode, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + switch (iface->type) { + case HOSTAP_INTERFACE_STA: + *mode = IW_MODE_INFRA; + break; + case HOSTAP_INTERFACE_WDS: + *mode = IW_MODE_REPEAT; + break; + default: + *mode = local->iw_mode; + break; + } + return 0; +} + + +static int prism2_ioctl_siwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *wrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + int ret = 0; + + if (wrq->disabled) + return hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 0); + + switch (wrq->flags & IW_POWER_MODE) { + case IW_POWER_UNICAST_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 0); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ALL_R: + ret = hostap_set_word(dev, HFA384X_RID_CNFMULTICASTRECEIVE, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + break; + case IW_POWER_ON: + break; + default: + return -EINVAL; + } + + if (wrq->flags & IW_POWER_TIMEOUT) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFPMHOLDOVERDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + if (wrq->flags & IW_POWER_PERIOD) { + ret = hostap_set_word(dev, HFA384X_RID_CNFPMENABLED, 1); + if (ret) + return ret; + ret = hostap_set_word(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + wrq->value / 1024); + if (ret) + return ret; + } + + return ret; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwpower(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + u16 enable, mcast; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_CNFPMENABLED, &enable, 2, 1) + < 0) + return -EINVAL; + + if (!__le16_to_cpu(enable)) { + rrq->disabled = 1; + return 0; + } + + rrq->disabled = 0; + + if ((rrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + u16 timeout; + if (local->func->get_rid(dev, + HFA384X_RID_CNFPMHOLDOVERDURATION, + &timeout, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_TIMEOUT; + rrq->value = __le16_to_cpu(timeout) * 1024; + } else { + u16 period; + if (local->func->get_rid(dev, HFA384X_RID_CNFMAXSLEEPDURATION, + &period, 2, 1) < 0) + return -EINVAL; + + rrq->flags = IW_POWER_PERIOD; + rrq->value = __le16_to_cpu(period) * 1024; + } + + if (local->func->get_rid(dev, HFA384X_RID_CNFMULTICASTRECEIVE, &mcast, + 2, 1) < 0) + return -EINVAL; + + if (__le16_to_cpu(mcast)) + rrq->flags |= IW_POWER_ALL_R; + else + rrq->flags |= IW_POWER_UNICAST_R; + + return 0; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_siwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->disabled) + return -EINVAL; + + /* setting retry limits is not supported with the current station + * firmware code; simulate this with alternative retry count for now */ + if (rrq->flags == IW_RETRY_LIMIT) { + if (rrq->value < 0) { + /* disable manual retry count setting and use firmware + * defaults */ + local->manual_retry_count = -1; + local->tx_control &= ~HFA384X_TX_CTRL_ALT_RTRY; + } else { + if (hostap_set_word(dev, HFA384X_RID_CNFALTRETRYCOUNT, + rrq->value)) { + printk(KERN_DEBUG "%s: Alternate retry count " + "setting to %d failed\n", + dev->name, rrq->value); + return -EOPNOTSUPP; + } + + local->manual_retry_count = rrq->value; + local->tx_control |= HFA384X_TX_CTRL_ALT_RTRY; + } + return 0; + } + + return -EOPNOTSUPP; + +#if 0 + /* what could be done, if firmware would support this.. */ + + if (rrq->flags & IW_RETRY_LIMIT) { + if (rrq->flags & IW_RETRY_MAX) + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + else if (rrq->flags & IW_RETRY_MIN) + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + else { + HFA384X_RID_LONGRETRYLIMIT = rrq->value; + HFA384X_RID_SHORTRETRYLIMIT = rrq->value; + } + + } + + if (rrq->flags & IW_RETRY_LIFETIME) { + HFA384X_RID_MAXTRANSMITLIFETIME = rrq->value / 1024; + } + + return 0; +#endif /* 0 */ +} + +static int prism2_ioctl_giwretry(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 shortretry, longretry, lifetime, altretry; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->get_rid(dev, HFA384X_RID_SHORTRETRYLIMIT, &shortretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_LONGRETRYLIMIT, &longretry, + 2, 1) < 0 || + local->func->get_rid(dev, HFA384X_RID_MAXTRANSMITLIFETIME, + &lifetime, 2, 1) < 0) + return -EINVAL; + + le16_to_cpus(&shortretry); + le16_to_cpus(&longretry); + le16_to_cpus(&lifetime); + + rrq->disabled = 0; + + if ((rrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) { + rrq->flags = IW_RETRY_LIFETIME; + rrq->value = lifetime * 1024; + } else { + if (local->manual_retry_count >= 0) { + rrq->flags = IW_RETRY_LIMIT; + if (local->func->get_rid(dev, + HFA384X_RID_CNFALTRETRYCOUNT, + &altretry, 2, 1) >= 0) + rrq->value = le16_to_cpu(altretry); + else + rrq->value = local->manual_retry_count; + } else if ((rrq->flags & IW_RETRY_MAX)) { + rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + rrq->value = longretry; + } else { + rrq->flags = IW_RETRY_LIMIT; + rrq->value = shortretry; + if (shortretry != longretry) + rrq->flags |= IW_RETRY_MIN; + } + } + return 0; +} + + +/* Note! This TX power controlling is experimental and should not be used in + * production use. It just sets raw power register and does not use any kind of + * feedback information from the measured TX power (CR58). This is now + * commented out to make sure that it is not used by accident. TX power + * configuration will be enabled again after proper algorithm using feedback + * has been implemented. */ + +#ifdef RAW_TXPOWER_SETTING +/* Map HFA386x's CR31 to and from dBm with some sort of ad hoc mapping.. + * This version assumes following mapping: + * CR31 is 7-bit value with -64 to +63 range. + * -64 is mapped into +20dBm and +63 into -43dBm. + * This is certainly not an exact mapping for every card, but at least + * increasing dBm value should correspond to increasing TX power. + */ + +static int prism2_txpower_hfa386x_to_dBm(u16 val) +{ + signed char tmp; + + if (val > 255) + val = 255; + + tmp = val; + tmp >>= 2; + + return -12 - tmp; +} + +static u16 prism2_txpower_dBm_to_hfa386x(int val) +{ + signed char tmp; + + if (val > 20) + return 128; + else if (val < -43) + return 127; + + tmp = val; + tmp = -12 - tmp; + tmp <<= 2; + + return (unsigned char) tmp; +} +#endif /* RAW_TXPOWER_SETTING */ + + +static int prism2_ioctl_siwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; +#ifdef RAW_TXPOWER_SETTING + char *tmp; +#endif + u16 val; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + if (rrq->disabled) { + if (local->txpower_type != PRISM2_TXPOWER_OFF) { + val = 0xff; /* use all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, + &val, NULL); + printk(KERN_DEBUG "%s: Turning radio off: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_OFF; + } + return (ret ? -EOPNOTSUPP : 0); + } + + if (local->txpower_type == PRISM2_TXPOWER_OFF) { + val = 0; /* disable all standby and sleep modes */ + ret = local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_A_D_TEST_MODES2, &val, NULL); + printk(KERN_DEBUG "%s: Turning radio on: %s\n", + dev->name, ret ? "failed" : "OK"); + local->txpower_type = PRISM2_TXPOWER_UNKNOWN; + } + +#ifdef RAW_TXPOWER_SETTING + if (!rrq->fixed && local->txpower_type != PRISM2_TXPOWER_AUTO) { + printk(KERN_DEBUG "Setting ALC on\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 1, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_AUTO; + return 0; + } + + if (local->txpower_type != PRISM2_TXPOWER_FIXED) { + printk(KERN_DEBUG "Setting ALC off\n"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), 0, &val, NULL); + local->txpower_type = PRISM2_TXPOWER_FIXED; + } + + if (rrq->flags == IW_TXPOW_DBM) + tmp = "dBm"; + else if (rrq->flags == IW_TXPOW_MWATT) + tmp = "mW"; + else + tmp = "UNKNOWN"; + printk(KERN_DEBUG "Setting TX power to %d %s\n", rrq->value, tmp); + + if (rrq->flags != IW_TXPOW_DBM) { + printk("SIOCSIWTXPOW with mW is not supported; use dBm\n"); + return -EOPNOTSUPP; + } + + local->txpower = rrq->value; + val = prism2_txpower_dBm_to_hfa386x(local->txpower); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, + HFA386X_CR_MANUAL_TX_POWER, &val, NULL)) + ret = -EOPNOTSUPP; +#else /* RAW_TXPOWER_SETTING */ + if (rrq->fixed) + ret = -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ + + return ret; +} + +static int prism2_ioctl_giwtxpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *rrq, char *extra) +{ +#ifdef RAW_TXPOWER_SETTING + struct hostap_interface *iface; + local_info_t *local; + u16 resp0; + + iface = netdev_priv(dev); + local = iface->local; + + rrq->flags = IW_TXPOW_DBM; + rrq->disabled = 0; + rrq->fixed = 0; + + if (local->txpower_type == PRISM2_TXPOWER_AUTO) { + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, + HFA386X_CR_MANUAL_TX_POWER, + NULL, &resp0) == 0) { + rrq->value = prism2_txpower_hfa386x_to_dBm(resp0); + } else { + /* Could not get real txpower; guess 15 dBm */ + rrq->value = 15; + } + } else if (local->txpower_type == PRISM2_TXPOWER_OFF) { + rrq->value = 0; + rrq->disabled = 1; + } else if (local->txpower_type == PRISM2_TXPOWER_FIXED) { + rrq->value = local->txpower; + rrq->fixed = 1; + } else { + printk("SIOCGIWTXPOW - unknown txpower_type=%d\n", + local->txpower_type); + } + return 0; +#else /* RAW_TXPOWER_SETTING */ + return -EOPNOTSUPP; +#endif /* RAW_TXPOWER_SETTING */ +} + + +#ifndef PRISM2_NO_STATION_MODES + +/* HostScan request works with and without host_roaming mode. In addition, it + * does not break current association. However, it requires newer station + * firmware version (>= 1.3.1) than scan request. */ +static int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_hostscan_request scan_req; + + iface = netdev_priv(dev); + local = iface->local; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(local->channel_mask); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + if (ssid) { + if (ssid_len > 32) + return -EINVAL; + scan_req.target_ssid_len = cpu_to_le16(ssid_len); + memcpy(scan_req.target_ssid, ssid, ssid_len); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "%s: HOSTSCAN failed\n", dev->name); + return -EINVAL; + } + return 0; +} + + +static int prism2_request_scan(struct net_device *dev) +{ + struct hostap_interface *iface; + local_info_t *local; + struct hfa384x_scan_request scan_req; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(local->channel_mask); + scan_req.txrate = __constant_cpu_to_le16(HFA384X_RATES_1MBPS); + + /* FIX: + * It seems to be enough to set roaming mode for a short moment to + * host-based and then setup scanrequest data and return the mode to + * firmware-based. + * + * Master mode would need to drop to Managed mode for a short while + * to make scanning work.. Or sweep through the different channels and + * use passive scan based on beacons. */ + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_HOST); + + if (local->func->set_rid(dev, HFA384X_RID_SCANREQUEST, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "SCANREQUEST failed\n"); + ret = -EINVAL; + } + + if (!local->host_roaming) + hostap_set_word(dev, HFA384X_RID_CNFROAMINGMODE, + HFA384X_ROAMING_FIRMWARE); + + return 0; +} + +#else /* !PRISM2_NO_STATION_MODES */ + +static inline int prism2_request_hostscan(struct net_device *dev, + u8 *ssid, u8 ssid_len) +{ + return -EOPNOTSUPP; +} + + +static inline int prism2_request_scan(struct net_device *dev) +{ + return -EOPNOTSUPP; +} + +#endif /* !PRISM2_NO_STATION_MODES */ + + +static int prism2_ioctl_siwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In master mode, we just return the results of our local + * tables, so we don't need to start anything... + * Jean II */ + data->length = 0; + return 0; + } + + if (!local->dev_enabled) + return -ENETDOWN; + + if (local->sta_fw_ver >= PRISM2_FW_VER(1,3,1)) + ret = prism2_request_hostscan(dev, NULL, 0); + else + ret = prism2_request_scan(dev); + + if (ret == 0) + local->scan_timestamp = jiffies; + + /* Could inquire F101, F103 or wait for SIOCGIWSCAN and read RID */ + + return ret; +} + + +#ifndef PRISM2_NO_STATION_MODES +static char * __prism2_translate_scan(local_info_t *local, + struct hfa384x_scan_result *scan, + struct hfa384x_hostscan_result *hscan, + int hostscan, + struct hostap_bss_info *bss, u8 *bssid, + char *current_ev, char *end_buf) +{ + int i; + struct iw_event iwe; + char *current_val; + u16 capabilities; + u8 *pos; + u8 *ssid; + size_t ssid_len; + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + if (bss) { + ssid = bss->ssid; + ssid_len = bss->ssid_len; + } else { + ssid = hostscan ? hscan->ssid : scan->ssid; + ssid_len = le16_to_cpu(hostscan ? hscan->ssid_len : + scan->ssid_len); + } + if (ssid_len > 32) + ssid_len = 32; + + /* First entry *MUST* be the AP MAC address */ + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, bssid, ETH_ALEN); + /* FIX: + * I do not know how this is possible, but iwe_stream_add_event + * seems to re-order memcpy execution so that len is set only + * after copying.. Pre-setting len here "fixes" this, but real + * problems should be solved (after which these iwe.len + * settings could be removed from this function). */ + iwe.len = IW_EV_ADDR_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_ADDR_LEN); + + /* Other entries will be displayed in the order we give them */ + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWESSID; + iwe.u.data.length = ssid_len; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ssid); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWMODE; + capabilities = le16_to_cpu(hostscan ? hscan->capability : + scan->capability); + if (capabilities & (WLAN_CAPABILITY_ESS | + WLAN_CAPABILITY_IBSS)) { + if (capabilities & WLAN_CAPABILITY_ESS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + iwe.len = IW_EV_UINT_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_UINT_LEN); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = freq_list[le16_to_cpu(hostscan ? hscan->chid : + scan->chid) - 1] * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_FREQ_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVQUAL; + if (hostscan) { + iwe.u.qual.level = le16_to_cpu(hscan->sl); + iwe.u.qual.noise = le16_to_cpu(hscan->anl); + } else { + iwe.u.qual.level = HFA384X_LEVEL_TO_dBm(le16_to_cpu(scan->sl)); + iwe.u.qual.noise = HFA384X_LEVEL_TO_dBm( + le16_to_cpu(scan->anl)); + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, + IW_EV_QUAL_LEN); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWENCODE; + if (capabilities & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, ""); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = SIOCGIWRATE; + current_val = current_ev + IW_EV_LCP_LEN; + pos = hostscan ? hscan->sup_rates : scan->sup_rates; + for (i = 0; i < sizeof(scan->sup_rates); i++) { + if (pos[i] == 0) + break; + /* Bit rate given in 500 kb/s units (+ 0x80) */ + iwe.u.bitrate.value = ((pos[i] & 0x7f) * 500000); + current_val = iwe_stream_add_value( + current_ev, current_val, end_buf, &iwe, + IW_EV_PARAM_LEN); + } + /* Check if we added any event */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "bcn_int=%d", + le16_to_cpu(hostscan ? hscan->beacon_interval : + scan->beacon_interval)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "resp_rate=%d", le16_to_cpu(hostscan ? hscan->rate : + scan->rate)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, buf); + + if (hostscan && (capabilities & WLAN_CAPABILITY_IBSS)) { + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + sprintf(buf, "atim=%d", le16_to_cpu(hscan->atim)); + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + } + + if (bss && bss->wpa_ie_len > 0 && bss->wpa_ie_len <= MAX_WPA_IE_LEN ) { + u8 *p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < bss->wpa_ie_len; i++) { + p += sprintf(p, "%02x", bss->wpa_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, buf); + } + + if (bss && bss->rsn_ie_len > 0 && bss->rsn_ie_len <= MAX_WPA_IE_LEN ) { + u8 *p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < bss->rsn_ie_len; i++) { + p += sprintf(p, "%02x", bss->rsn_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + current_ev = iwe_stream_add_point( + current_ev, end_buf, &iwe, buf); + } + + return current_ev; +} + + +/* Translate scan data returned from the card to a card independant + * format that the Wireless Tools will understand - Jean II */ +static inline int prism2_translate_scan(local_info_t *local, + char *buffer, int buflen) +{ + struct hfa384x_scan_result *scan; + struct hfa384x_hostscan_result *hscan; + int entries, entry, hostscan; + char *current_ev = buffer; + char *end_buf = buffer + buflen; + u8 *bssid; + struct list_head *ptr; + + spin_lock_bh(&local->lock); + + hostscan = local->last_scan_type == PRISM2_HOSTSCAN; + entries = hostscan ? local->last_hostscan_results_count : + local->last_scan_results_count; + for (entry = 0; entry < entries; entry++) { + int found = 0; + scan = &local->last_scan_results[entry]; + hscan = &local->last_hostscan_results[entry]; + + bssid = hostscan ? hscan->bssid : scan->bssid; + + /* Report every SSID if the AP is using multiple SSIDs. If no + * BSS record is found (e.g., when WPA mode is disabled), + * report the AP once. */ + list_for_each(ptr, &local->bss_list) { + struct hostap_bss_info *bss; + bss = list_entry(ptr, struct hostap_bss_info, list); + if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) { + current_ev = __prism2_translate_scan( + local, scan, hscan, hostscan, bss, + bssid, current_ev, end_buf); + found++; + } + } + if (!found) { + current_ev = __prism2_translate_scan( + local, scan, hscan, hostscan, NULL, bssid, + current_ev, end_buf); + } + /* Check if there is space for one more entry */ + if ((end_buf - current_ev) <= IW_EV_ADDR_LEN) { + /* Ask user space to try again with a bigger buffer */ + spin_unlock_bh(&local->lock); + return -E2BIG; + } + } + + spin_unlock_bh(&local->lock); + + return current_ev - buffer; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +static inline int prism2_ioctl_giwscan_sta(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ +#ifdef PRISM2_NO_STATION_MODES + return -EOPNOTSUPP; +#else /* PRISM2_NO_STATION_MODES */ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + /* Wait until the scan is finished. We can probably do better + * than that - Jean II */ + if (local->scan_timestamp && + time_before(jiffies, local->scan_timestamp + 3 * HZ)) { + /* Important note : we don't want to block the caller + * until results are ready for various reasons. + * First, managing wait queues is complex and racy + * (there may be multiple simultaneous callers). + * Second, we grab some rtnetlink lock before comming + * here (in dev_ioctl()). + * Third, the caller can wait on the Wireless Event + * - Jean II */ + return -EAGAIN; + } + local->scan_timestamp = 0; + + res = prism2_translate_scan(local, extra, data->length); + + if (res >= 0) { + data->length = res; + return 0; + } else { + data->length = 0; + return res; + } +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_giwscan(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *data, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int res; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->iw_mode == IW_MODE_MASTER) { + /* In MASTER mode, it doesn't make sense to go around + * scanning the frequencies and make the stations we serve + * wait when what the user is really interested about is the + * list of stations and access points we are talking to. + * So, just extract results from our cache... + * Jean II */ + + /* Translate to WE format */ + res = prism2_ap_translate_scan(dev, extra); + if (res >= 0) { + printk(KERN_DEBUG "Scan result translation succeeded " + "(length=%d)\n", res); + data->length = res; + return 0; + } else { + printk(KERN_DEBUG + "Scan result translation failed (res=%d)\n", + res); + data->length = 0; + return res; + } + } else { + /* Station mode */ + return prism2_ioctl_giwscan_sta(dev, info, data, extra); + } +} + + +static const struct iw_priv_args prism2_priv[] = { + { PRISM2_IOCTL_MONITOR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor" }, + { PRISM2_IOCTL_READMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, "readmif" }, + { PRISM2_IOCTL_WRITEMIF, + IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, 0, "writemif" }, + { PRISM2_IOCTL_RESET, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "reset" }, + { PRISM2_IOCTL_INQUIRE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "inquire" }, + { PRISM2_IOCTL_SET_RID_WORD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "set_rid_word" }, + { PRISM2_IOCTL_MACCMD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "maccmd" }, + { PRISM2_IOCTL_WDS_ADD, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_add" }, + { PRISM2_IOCTL_WDS_DEL, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "wds_del" }, + { PRISM2_IOCTL_ADDMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "addmac" }, + { PRISM2_IOCTL_DELMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "delmac" }, + { PRISM2_IOCTL_KICKMAC, + IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "kickmac" }, + /* --- raw access to sub-ioctls --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, 0, "prism2_param" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprism2_param" }, + /* --- sub-ioctls handlers --- */ + { PRISM2_IOCTL_PRISM2_PARAM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "" }, + { PRISM2_IOCTL_GET_PRISM2_PARAM, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "" }, + /* --- sub-ioctls definitions --- */ + { PRISM2_PARAM_TXRATECTRL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "txratectrl" }, + { PRISM2_PARAM_TXRATECTRL, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettxratectrl" }, + { PRISM2_PARAM_BEACON_INT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "beacon_int" }, + { PRISM2_PARAM_BEACON_INT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbeacon_int" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_PSEUDO_IBSS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "pseudo_ibss" }, + { PRISM2_PARAM_PSEUDO_IBSS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getpseudo_ibss" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_ALC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "alc" }, + { PRISM2_PARAM_ALC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getalc" }, + { PRISM2_PARAM_DUMP, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dump" }, + { PRISM2_PARAM_DUMP, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdump" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "other_ap_policy" }, + { PRISM2_PARAM_OTHER_AP_POLICY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getother_ap_pol" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_inactivity" }, + { PRISM2_PARAM_AP_MAX_INACTIVITY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_inactivi" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bridge_packets" }, + { PRISM2_PARAM_AP_BRIDGE_PACKETS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbridge_packe" }, + { PRISM2_PARAM_DTIM_PERIOD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "dtim_period" }, + { PRISM2_PARAM_DTIM_PERIOD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdtim_period" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "nullfunc_ack" }, + { PRISM2_PARAM_AP_NULLFUNC_ACK, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getnullfunc_ack" }, + { PRISM2_PARAM_MAX_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "max_wds" }, + { PRISM2_PARAM_MAX_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmax_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "autom_ap_wds" }, + { PRISM2_PARAM_AP_AUTOM_AP_WDS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getautom_ap_wds" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_auth_algs" }, + { PRISM2_PARAM_AP_AUTH_ALGS, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_auth_algs" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "allow_fcserr" }, + { PRISM2_PARAM_MONITOR_ALLOW_FCSERR, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getallow_fcserr" }, + { PRISM2_PARAM_HOST_ENCRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_encrypt" }, + { PRISM2_PARAM_HOST_ENCRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_encrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_decrypt" }, + { PRISM2_PARAM_HOST_DECRYPT, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_decrypt" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_rx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "busmaster_tx" }, + { PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbusmaster_tx" }, +#ifndef PRISM2_NO_STATION_MODES + { PRISM2_PARAM_HOST_ROAMING, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "host_roaming" }, + { PRISM2_PARAM_HOST_ROAMING, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethost_roaming" }, +#endif /* PRISM2_NO_STATION_MODES */ + { PRISM2_PARAM_BCRX_STA_KEY, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "bcrx_sta_key" }, + { PRISM2_PARAM_BCRX_STA_KEY, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbcrx_sta_key" }, + { PRISM2_PARAM_IEEE_802_1X, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ieee_802_1x" }, + { PRISM2_PARAM_IEEE_802_1X, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getieee_802_1x" }, + { PRISM2_PARAM_ANTSEL_TX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_tx" }, + { PRISM2_PARAM_ANTSEL_TX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_tx" }, + { PRISM2_PARAM_ANTSEL_RX, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "antsel_rx" }, + { PRISM2_PARAM_ANTSEL_RX, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getantsel_rx" }, + { PRISM2_PARAM_MONITOR_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "monitor_type" }, + { PRISM2_PARAM_MONITOR_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getmonitor_type" }, + { PRISM2_PARAM_WDS_TYPE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wds_type" }, + { PRISM2_PARAM_WDS_TYPE, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwds_type" }, + { PRISM2_PARAM_HOSTSCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostscan" }, + { PRISM2_PARAM_HOSTSCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostscan" }, + { PRISM2_PARAM_AP_SCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "ap_scan" }, + { PRISM2_PARAM_AP_SCAN, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getap_scan" }, + { PRISM2_PARAM_ENH_SEC, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "enh_sec" }, + { PRISM2_PARAM_ENH_SEC, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getenh_sec" }, +#ifdef PRISM2_IO_DEBUG + { PRISM2_PARAM_IO_DEBUG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "io_debug" }, + { PRISM2_PARAM_IO_DEBUG, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getio_debug" }, +#endif /* PRISM2_IO_DEBUG */ + { PRISM2_PARAM_BASIC_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "basic_rates" }, + { PRISM2_PARAM_BASIC_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getbasic_rates" }, + { PRISM2_PARAM_OPER_RATES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oper_rates" }, + { PRISM2_PARAM_OPER_RATES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getoper_rates" }, + { PRISM2_PARAM_HOSTAPD, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd" }, + { PRISM2_PARAM_HOSTAPD, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd" }, + { PRISM2_PARAM_HOSTAPD_STA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "hostapd_sta" }, + { PRISM2_PARAM_HOSTAPD_STA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gethostapd_sta" }, + { PRISM2_PARAM_WPA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "wpa" }, + { PRISM2_PARAM_WPA, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getwpa" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "privacy_invoked" }, + { PRISM2_PARAM_PRIVACY_INVOKED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getprivacy_invo" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "tkip_countermea" }, + { PRISM2_PARAM_TKIP_COUNTERMEASURES, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "gettkip_counter" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "drop_unencrypte" }, + { PRISM2_PARAM_DROP_UNENCRYPTED, + 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, "getdrop_unencry" }, +}; + + +static int prism2_ioctl_priv_inquire(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->cmd(dev, HFA384X_CMDCODE_INQUIRE, *i, NULL, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int *i = (int *) extra; + int param = *i; + int value = *(i + 1); + int ret = 0; + u16 val; + + iface = netdev_priv(dev); + local = iface->local; + + switch (param) { + case PRISM2_PARAM_TXRATECTRL: + local->fw_tx_rate_control = value; + break; + + case PRISM2_PARAM_BEACON_INT: + if (hostap_set_word(dev, HFA384X_RID_CNFBEACONINT, value) || + local->func->reset_port(dev)) + ret = -EINVAL; + else + local->beacon_int = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_PSEUDO_IBSS: + if (value == local->pseudo_adhoc) + break; + + if (value != 0 && value != 1) { + ret = -EINVAL; + break; + } + + printk(KERN_DEBUG "prism2: %s: pseudo IBSS change %d -> %d\n", + dev->name, local->pseudo_adhoc, value); + local->pseudo_adhoc = value; + if (local->iw_mode != IW_MODE_ADHOC) + break; + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + hostap_get_porttype(local))) { + ret = -EOPNOTSUPP; + break; + } + + if (local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_ALC: + printk(KERN_DEBUG "%s: %s ALC\n", dev->name, + value == 0 ? "Disabling" : "Enabling"); + val = HFA384X_TEST_CFG_BIT_ALC; + local->func->cmd(dev, HFA384X_CMDCODE_TEST | + (HFA384X_TEST_CFG_BITS << 8), + value == 0 ? 0 : 1, &val, NULL); + break; + + case PRISM2_PARAM_DUMP: + local->frame_dump = value; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->ap_policy = value; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (value < 0 || value > 7 * 24 * 60 * 60) { + ret = -EINVAL; + break; + } + if (local->ap != NULL) + local->ap->max_inactivity = value * HZ; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + local->ap->bridge_packets = value; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + if (value < 0 || value > 65535) { + ret = -EINVAL; + break; + } + if (hostap_set_word(dev, HFA384X_RID_CNFOWNDTIMPERIOD, value) + || local->func->reset_port(dev)) + ret = -EINVAL; + else + local->dtim_period = value; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + local->ap->nullfunc_ack = value; + break; + + case PRISM2_PARAM_MAX_WDS: + local->wds_max_connections = value; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) { + if (!local->ap->autom_ap_wds && value) { + /* add WDS link to all APs in STA table */ + hostap_add_wds_links(local); + } + local->ap->autom_ap_wds = value; + } + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + local->auth_algs = value; + if (hostap_set_auth_algs(local)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + local->monitor_allow_fcserr = value; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + local->host_encrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + local->host_decrypt = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + local->bus_master_threshold_rx = value; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + local->bus_master_threshold_tx = value; + break; + +#ifndef PRISM2_NO_STATION_MODES + case PRISM2_PARAM_HOST_ROAMING: + if (value < 0 || value > 2) { + ret = -EINVAL; + break; + } + local->host_roaming = value; + if (hostap_set_roaming(local) || local->func->reset_port(dev)) + ret = -EINVAL; + break; +#endif /* PRISM2_NO_STATION_MODES */ + + case PRISM2_PARAM_BCRX_STA_KEY: + local->bcrx_sta_key = value; + break; + + case PRISM2_PARAM_IEEE_802_1X: + local->ieee_802_1x = value; + break; + + case PRISM2_PARAM_ANTSEL_TX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_tx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_ANTSEL_RX: + if (value < 0 || value > HOSTAP_ANTSEL_HIGH) { + ret = -EINVAL; + break; + } + local->antsel_rx = value; + hostap_set_antsel(local); + break; + + case PRISM2_PARAM_MONITOR_TYPE: + if (value != PRISM2_MONITOR_80211 && + value != PRISM2_MONITOR_CAPHDR && + value != PRISM2_MONITOR_PRISM) { + ret = -EINVAL; + break; + } + local->monitor_type = value; + if (local->iw_mode == IW_MODE_MONITOR) + hostap_monitor_set_type(local); + break; + + case PRISM2_PARAM_WDS_TYPE: + local->wds_type = value; + break; + + case PRISM2_PARAM_HOSTSCAN: + { + struct hfa384x_hostscan_request scan_req; + u16 rate; + + memset(&scan_req, 0, sizeof(scan_req)); + scan_req.channel_list = __constant_cpu_to_le16(0x3fff); + switch (value) { + case 1: rate = HFA384X_RATES_1MBPS; break; + case 2: rate = HFA384X_RATES_2MBPS; break; + case 3: rate = HFA384X_RATES_5MBPS; break; + case 4: rate = HFA384X_RATES_11MBPS; break; + default: rate = HFA384X_RATES_1MBPS; break; + } + scan_req.txrate = cpu_to_le16(rate); + /* leave SSID empty to accept all SSIDs */ + + if (local->iw_mode == IW_MODE_MASTER) { + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_BSS) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Leaving Host AP mode " + "for HostScan failed\n"); + } + + if (local->func->set_rid(dev, HFA384X_RID_HOSTSCAN, &scan_req, + sizeof(scan_req))) { + printk(KERN_DEBUG "HOSTSCAN failed\n"); + ret = -EINVAL; + } + if (local->iw_mode == IW_MODE_MASTER) { + wait_queue_t __wait; + init_waitqueue_entry(&__wait, current); + add_wait_queue(&local->hostscan_wq, &__wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + if (signal_pending(current)) + ret = -EINTR; + set_current_state(TASK_RUNNING); + remove_wait_queue(&local->hostscan_wq, &__wait); + + if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, + HFA384X_PORTTYPE_HOSTAP) || + local->func->reset_port(dev)) + printk(KERN_DEBUG "Returning to Host AP mode " + "after HostScan failed\n"); + } + break; + } + + case PRISM2_PARAM_AP_SCAN: + local->passive_scan_interval = value; + if (timer_pending(&local->passive_scan_timer)) + del_timer(&local->passive_scan_timer); + if (value > 0) { + local->passive_scan_timer.expires = jiffies + + local->passive_scan_interval * HZ; + add_timer(&local->passive_scan_timer); + } + break; + + case PRISM2_PARAM_ENH_SEC: + if (value < 0 || value > 3) { + ret = -EINVAL; + break; + } + local->enh_sec = value; + if (hostap_set_word(dev, HFA384X_RID_CNFENHSECURITY, + local->enh_sec) || + local->func->reset_port(dev)) { + printk(KERN_INFO "%s: cnfEnhSecurity requires STA f/w " + "1.6.3 or newer\n", dev->name); + ret = -EOPNOTSUPP; + } + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + local->io_debug_enabled = value; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + if ((value & local->tx_rate_control) != value || value == 0) { + printk(KERN_INFO "%s: invalid basic rate set - basic " + "rates must be in supported rate set\n", + dev->name); + ret = -EINVAL; + break; + } + local->basic_rates = value; + if (hostap_set_word(dev, HFA384X_RID_CNFBASICRATES, + local->basic_rates) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_OPER_RATES: + local->tx_rate_control = value; + if (hostap_set_rate(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_HOSTAPD: + ret = hostap_set_hostapd(local, value, 1); + break; + + case PRISM2_PARAM_HOSTAPD_STA: + ret = hostap_set_hostapd_sta(local, value, 1); + break; + + case PRISM2_PARAM_WPA: + local->wpa = value; + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + else if (hostap_set_word(dev, HFA384X_RID_SSNHANDLINGMODE, + value ? 1 : 0)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + local->privacy_invoked = value; + if (hostap_set_encryption(local) || + local->func->reset_port(dev)) + ret = -EINVAL; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + local->tkip_countermeasures = value; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + local->drop_unencrypted = value; + break; + + default: + printk(KERN_DEBUG "%s: prism2_param: unknown param %d\n", + dev->name, param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_get_prism2_param(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + int *param = (int *) extra; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + switch (*param) { + case PRISM2_PARAM_TXRATECTRL: + *param = local->fw_tx_rate_control; + break; + + case PRISM2_PARAM_BEACON_INT: + *param = local->beacon_int; + break; + + case PRISM2_PARAM_PSEUDO_IBSS: + *param = local->pseudo_adhoc; + break; + + case PRISM2_PARAM_ALC: + ret = -EOPNOTSUPP; /* FIX */ + break; + + case PRISM2_PARAM_DUMP: + *param = local->frame_dump; + break; + + case PRISM2_PARAM_OTHER_AP_POLICY: + if (local->ap != NULL) + *param = local->ap->ap_policy; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_MAX_INACTIVITY: + if (local->ap != NULL) + *param = local->ap->max_inactivity / HZ; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_BRIDGE_PACKETS: + if (local->ap != NULL) + *param = local->ap->bridge_packets; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_DTIM_PERIOD: + *param = local->dtim_period; + break; + + case PRISM2_PARAM_AP_NULLFUNC_ACK: + if (local->ap != NULL) + *param = local->ap->nullfunc_ack; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_MAX_WDS: + *param = local->wds_max_connections; + break; + + case PRISM2_PARAM_AP_AUTOM_AP_WDS: + if (local->ap != NULL) + *param = local->ap->autom_ap_wds; + else + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_AUTH_ALGS: + *param = local->auth_algs; + break; + + case PRISM2_PARAM_MONITOR_ALLOW_FCSERR: + *param = local->monitor_allow_fcserr; + break; + + case PRISM2_PARAM_HOST_ENCRYPT: + *param = local->host_encrypt; + break; + + case PRISM2_PARAM_HOST_DECRYPT: + *param = local->host_decrypt; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX: + *param = local->bus_master_threshold_rx; + break; + + case PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX: + *param = local->bus_master_threshold_tx; + break; + + case PRISM2_PARAM_HOST_ROAMING: + *param = local->host_roaming; + break; + + case PRISM2_PARAM_BCRX_STA_KEY: + *param = local->bcrx_sta_key; + break; + + case PRISM2_PARAM_IEEE_802_1X: + *param = local->ieee_802_1x; + break; + + case PRISM2_PARAM_ANTSEL_TX: + *param = local->antsel_tx; + break; + + case PRISM2_PARAM_ANTSEL_RX: + *param = local->antsel_rx; + break; + + case PRISM2_PARAM_MONITOR_TYPE: + *param = local->monitor_type; + break; + + case PRISM2_PARAM_WDS_TYPE: + *param = local->wds_type; + break; + + case PRISM2_PARAM_HOSTSCAN: + ret = -EOPNOTSUPP; + break; + + case PRISM2_PARAM_AP_SCAN: + *param = local->passive_scan_interval; + break; + + case PRISM2_PARAM_ENH_SEC: + *param = local->enh_sec; + break; + +#ifdef PRISM2_IO_DEBUG + case PRISM2_PARAM_IO_DEBUG: + *param = local->io_debug_enabled; + break; +#endif /* PRISM2_IO_DEBUG */ + + case PRISM2_PARAM_BASIC_RATES: + *param = local->basic_rates; + break; + + case PRISM2_PARAM_OPER_RATES: + *param = local->tx_rate_control; + break; + + case PRISM2_PARAM_HOSTAPD: + *param = local->hostapd; + break; + + case PRISM2_PARAM_HOSTAPD_STA: + *param = local->hostapd_sta; + break; + + case PRISM2_PARAM_WPA: + if (local->sta_fw_ver < PRISM2_FW_VER(1,7,0)) + ret = -EOPNOTSUPP; + *param = local->wpa; + break; + + case PRISM2_PARAM_PRIVACY_INVOKED: + *param = local->privacy_invoked; + break; + + case PRISM2_PARAM_TKIP_COUNTERMEASURES: + *param = local->tkip_countermeasures; + break; + + case PRISM2_PARAM_DROP_UNENCRYPTED: + *param = local->drop_unencrypted; + break; + + default: + printk(KERN_DEBUG "%s: get_prism2_param: unknown param %d\n", + dev->name, *param); + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + + +static int prism2_ioctl_priv_readmif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 resp0; + + iface = netdev_priv(dev); + local = iface->local; + + if (local->func->cmd(dev, HFA384X_CMDCODE_READMIF, *extra, NULL, + &resp0)) + return -EOPNOTSUPP; + else + *extra = resp0; + + return 0; +} + + +static int prism2_ioctl_priv_writemif(struct net_device *dev, + struct iw_request_info *info, + void *wrqu, char *extra) +{ + struct hostap_interface *iface; + local_info_t *local; + u16 cr, val; + + iface = netdev_priv(dev); + local = iface->local; + + cr = *extra; + val = *(extra + 1); + if (local->func->cmd(dev, HFA384X_CMDCODE_WRITEMIF, cr, &val, NULL)) + return -EOPNOTSUPP; + + return 0; +} + + +static int prism2_ioctl_priv_monitor(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + u32 mode; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_DEBUG "%s: process %d (%s) used deprecated iwpriv monitor " + "- update software to use iwconfig mode monitor\n", + dev->name, current->pid, current->comm); + + /* Backward compatibility code - this can be removed at some point */ + + if (*i == 0) { + /* Disable monitor mode - old mode was not saved, so go to + * Master mode */ + mode = IW_MODE_MASTER; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + } else if (*i == 1) { + /* netlink socket mode is not supported anymore since it did + * not separate different devices from each other and was not + * best method for delivering large amount of packets to + * user space */ + ret = -EOPNOTSUPP; + } else if (*i == 2 || *i == 3) { + switch (*i) { + case 2: + local->monitor_type = PRISM2_MONITOR_80211; + break; + case 3: + local->monitor_type = PRISM2_MONITOR_PRISM; + break; + } + mode = IW_MODE_MONITOR; + ret = prism2_ioctl_siwmode(dev, NULL, &mode, NULL); + hostap_monitor_mode_enable(local); + } else + ret = -EINVAL; + + return ret; +} + + +static int prism2_ioctl_priv_reset(struct net_device *dev, int *i) +{ + struct hostap_interface *iface; + local_info_t *local; + + iface = netdev_priv(dev); + local = iface->local; + + printk(KERN_DEBUG "%s: manual reset request(%d)\n", dev->name, *i); + switch (*i) { + case 0: + /* Disable and enable card */ + local->func->hw_shutdown(dev, 1); + local->func->hw_config(dev, 0); + break; + + case 1: + /* COR sreset */ + local->func->hw_reset(dev); + break; + + case 2: + /* Disable and enable port 0 */ + local->func->reset_port(dev); + break; + + case 3: + prism2_sta_deauth(local, WLAN_REASON_DEAUTH_LEAVING); + if (local->func->cmd(dev, HFA384X_CMDCODE_DISABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + case 4: + if (local->func->cmd(dev, HFA384X_CMDCODE_ENABLE, 0, NULL, + NULL)) + return -EINVAL; + break; + + default: + printk(KERN_DEBUG "Unknown reset request %d\n", *i); + return -EOPNOTSUPP; + } + + return 0; +} + + +static int prism2_ioctl_priv_set_rid_word(struct net_device *dev, int *i) +{ + int rid = *i; + int value = *(i + 1); + + printk(KERN_DEBUG "%s: Set RID[0x%X] = %d\n", dev->name, rid, value); + + if (hostap_set_word(dev, rid, value)) + return -EINVAL; + + return 0; +} + + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT +static int ap_mac_cmd_ioctl(local_info_t *local, int *cmd) +{ + int ret = 0; + + switch (*cmd) { + case AP_MAC_CMD_POLICY_OPEN: + local->ap->mac_restrictions.policy = MAC_POLICY_OPEN; + break; + case AP_MAC_CMD_POLICY_ALLOW: + local->ap->mac_restrictions.policy = MAC_POLICY_ALLOW; + break; + case AP_MAC_CMD_POLICY_DENY: + local->ap->mac_restrictions.policy = MAC_POLICY_DENY; + break; + case AP_MAC_CMD_FLUSH: + ap_control_flush_macs(&local->ap->mac_restrictions); + break; + case AP_MAC_CMD_KICKALL: + ap_control_kickall(local->ap); + hostap_deauth_all_stas(local->dev, local->ap, 0); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + +#ifdef PRISM2_DOWNLOAD_SUPPORT +static int prism2_ioctl_priv_download(local_info_t *local, struct iw_point *p) +{ + struct prism2_download_param *param; + int ret = 0; + + if (p->length < sizeof(struct prism2_download_param) || + p->length > 1024 || !p->pointer) + return -EINVAL; + + param = (struct prism2_download_param *) + kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + if (p->length < sizeof(struct prism2_download_param) + + param->num_areas * sizeof(struct prism2_download_area)) { + ret = -EINVAL; + goto out; + } + + ret = local->func->download(local, param); + + out: + if (param != NULL) + kfree(param); + + return ret; +} +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + +static int prism2_ioctl_set_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int ret = 0; + struct hostap_crypto_ops *ops; + struct prism2_crypt_data **crypt; + void *sta_ptr; + + param->u.crypt.err = 0; + param->u.crypt.alg[HOSTAP_CRYPT_ALG_NAME_LEN - 1] = '\0'; + + if (param_len != + (int) ((char *) param->u.crypt.key - (char *) param) + + param->u.crypt.key_len) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + if (param->u.crypt.idx >= WEP_KEYS) + return -EINVAL; + sta_ptr = NULL; + crypt = &local->crypt[param->u.crypt.idx]; + } else { + if (param->u.crypt.idx) + return -EINVAL; + sta_ptr = ap_crypt_get_ptrs( + local->ap, param->sta_addr, + (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_PERMANENT), + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (strcmp(param->u.crypt.alg, "none") == 0) { + if (crypt) + prism2_crypt_delayed_deinit(local, crypt); + goto done; + } + + ops = hostap_get_crypto_ops(param->u.crypt.alg); + if (ops == NULL && strcmp(param->u.crypt.alg, "WEP") == 0) { + request_module("hostap_crypt_wep"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "TKIP") == 0) { + request_module("hostap_crypt_tkip"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } else if (ops == NULL && strcmp(param->u.crypt.alg, "CCMP") == 0) { + request_module("hostap_crypt_ccmp"); + ops = hostap_get_crypto_ops(param->u.crypt.alg); + } + if (ops == NULL) { + printk(KERN_DEBUG "%s: unknown crypto alg '%s'\n", + local->dev->name, param->u.crypt.alg); + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ALG; + ret = -EINVAL; + goto done; + } + + /* station based encryption and other than WEP algorithms require + * host-based encryption, so force them on automatically */ + local->host_decrypt = local->host_encrypt = 1; + + if (*crypt == NULL || (*crypt)->ops != ops) { + struct prism2_crypt_data *new_crypt; + + prism2_crypt_delayed_deinit(local, crypt); + + new_crypt = (struct prism2_crypt_data *) + kmalloc(sizeof(struct prism2_crypt_data), GFP_KERNEL); + if (new_crypt == NULL) { + ret = -ENOMEM; + goto done; + } + memset(new_crypt, 0, sizeof(struct prism2_crypt_data)); + new_crypt->ops = ops; + new_crypt->priv = new_crypt->ops->init(param->u.crypt.idx); + if (new_crypt->priv == NULL) { + kfree(new_crypt); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED; + ret = -EINVAL; + goto done; + } + + *crypt = new_crypt; + } + + if ((!(param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) || + param->u.crypt.key_len > 0) && (*crypt)->ops->set_key && + (*crypt)->ops->set_key(param->u.crypt.key, + param->u.crypt.key_len, param->u.crypt.seq, + (*crypt)->priv) < 0) { + printk(KERN_DEBUG "%s: key setting failed\n", + local->dev->name); + param->u.crypt.err = HOSTAP_CRYPT_ERR_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + + if (param->u.crypt.flags & HOSTAP_CRYPT_FLAG_SET_TX_KEY) { + if (!sta_ptr) + local->tx_keyidx = param->u.crypt.idx; + else if (param->u.crypt.idx) { + printk(KERN_DEBUG "%s: TX key idx setting failed\n", + local->dev->name); + param->u.crypt.err = + HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED; + ret = -EINVAL; + goto done; + } + } + + done: + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + /* Do not reset port0 if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. Prism2 documentation seem to require port reset + * after WEP configuration. However, keys are apparently changed at + * least in Managed mode. */ + if (ret == 0 && + (hostap_set_encryption(local) || + (local->iw_mode != IW_MODE_INFRA && + local->func->reset_port(local->dev)))) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_CARD_CONF_FAILED; + return -EINVAL; + } + + return ret; +} + + +static int prism2_ioctl_get_encryption(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + struct prism2_crypt_data **crypt; + void *sta_ptr; + int max_key_len; + + param->u.crypt.err = 0; + + max_key_len = param_len - + (int) ((char *) param->u.crypt.key - (char *) param); + if (max_key_len < 0) + return -EINVAL; + + if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && + param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { + sta_ptr = NULL; + if (param->u.crypt.idx >= WEP_KEYS) + param->u.crypt.idx = local->tx_keyidx; + crypt = &local->crypt[param->u.crypt.idx]; + } else { + param->u.crypt.idx = 0; + sta_ptr = ap_crypt_get_ptrs(local->ap, param->sta_addr, 0, + &crypt); + + if (sta_ptr == NULL) { + param->u.crypt.err = HOSTAP_CRYPT_ERR_UNKNOWN_ADDR; + return -EINVAL; + } + } + + if (*crypt == NULL || (*crypt)->ops == NULL) { + memcpy(param->u.crypt.alg, "none", 5); + param->u.crypt.key_len = 0; + param->u.crypt.idx = 0xff; + } else { + strncpy(param->u.crypt.alg, (*crypt)->ops->name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.key_len = 0; + + memset(param->u.crypt.seq, 0, 8); + if ((*crypt)->ops->get_key) { + param->u.crypt.key_len = + (*crypt)->ops->get_key(param->u.crypt.key, + max_key_len, + param->u.crypt.seq, + (*crypt)->priv); + } + } + + if (sta_ptr) + hostap_handle_sta_release(sta_ptr); + + return 0; +} + + +static int prism2_ioctl_get_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, res; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0) + return -EINVAL; + + res = local->func->get_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len, 0); + if (res >= 0) { + param->u.rid.len = res; + return 0; + } + + return res; +} + + +static int prism2_ioctl_set_rid(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len; + + max_len = param_len - PRISM2_HOSTAPD_RID_HDR_LEN; + if (max_len < 0 || max_len < param->u.rid.len) + return -EINVAL; + + return local->func->set_rid(local->dev, param->u.rid.rid, + param->u.rid.data, param->u.rid.len); +} + + +static int prism2_ioctl_set_assoc_ap_addr(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + printk(KERN_DEBUG "%ssta: associated as client with AP " MACSTR "\n", + local->dev->name, MAC2STR(param->sta_addr)); + memcpy(local->assoc_ap_addr, param->sta_addr, ETH_ALEN); + return 0; +} + + +static int prism2_ioctl_set_generic_element(local_info_t *local, + struct prism2_hostapd_param *param, + int param_len) +{ + int max_len, len; + u8 *buf; + + len = param->u.generic_elem.len; + max_len = param_len - PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (max_len < 0 || max_len < len) + return -EINVAL; + + /* Add 16-bit length in the beginning of the buffer because Prism2 RID + * includes it. */ + buf = kmalloc(len + 2, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + *((u16 *) buf) = cpu_to_le16(len); + memcpy(buf + 2, param->u.generic_elem.data, len); + + kfree(local->generic_elem); + local->generic_elem = buf; + local->generic_elem_len = len + 2; + + return local->func->set_rid(local->dev, HFA384X_RID_GENERICELEMENT, + buf, len + 2); +} + + +static int prism2_ioctl_mlme(local_info_t *local, + struct prism2_hostapd_param *param) +{ + u16 reason; + + reason = cpu_to_le16(param->u.mlme.reason_code); + switch (param->u.mlme.cmd) { + case MLME_STA_DEAUTH: + return prism2_sta_send_mgmt(local, param->sta_addr, + WLAN_FC_STYPE_DEAUTH, + (u8 *) &reason, 2); + case MLME_STA_DISASSOC: + return prism2_sta_send_mgmt(local, param->sta_addr, + WLAN_FC_STYPE_DISASSOC, + (u8 *) &reason, 2); + default: + return -EOPNOTSUPP; + } +} + + +static int prism2_ioctl_scan_req(local_info_t *local, + struct prism2_hostapd_param *param) +{ +#ifndef PRISM2_NO_STATION_MODES + if ((local->iw_mode != IW_MODE_INFRA && + local->iw_mode != IW_MODE_ADHOC) || + (local->sta_fw_ver < PRISM2_FW_VER(1,3,1))) + return -EOPNOTSUPP; + + if (!local->dev_enabled) + return -ENETDOWN; + + return prism2_request_hostscan(local->dev, param->u.scan_req.ssid, + param->u.scan_req.ssid_len); +#else /* PRISM2_NO_STATION_MODES */ + return -EOPNOTSUPP; +#endif /* PRISM2_NO_STATION_MODES */ +} + + +static int prism2_ioctl_priv_hostapd(local_info_t *local, struct iw_point *p) +{ + struct prism2_hostapd_param *param; + int ret = 0; + int ap_ioctl = 0; + + if (p->length < sizeof(struct prism2_hostapd_param) || + p->length > PRISM2_HOSTAPD_MAX_BUF_SIZE || !p->pointer) + return -EINVAL; + + param = (struct prism2_hostapd_param *) kmalloc(p->length, GFP_KERNEL); + if (param == NULL) + return -ENOMEM; + + if (copy_from_user(param, p->pointer, p->length)) { + ret = -EFAULT; + goto out; + } + + switch (param->cmd) { + case PRISM2_SET_ENCRYPTION: + ret = prism2_ioctl_set_encryption(local, param, p->length); + break; + case PRISM2_GET_ENCRYPTION: + ret = prism2_ioctl_get_encryption(local, param, p->length); + break; + case PRISM2_HOSTAPD_GET_RID: + ret = prism2_ioctl_get_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_RID: + ret = prism2_ioctl_set_rid(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR: + ret = prism2_ioctl_set_assoc_ap_addr(local, param, p->length); + break; + case PRISM2_HOSTAPD_SET_GENERIC_ELEMENT: + ret = prism2_ioctl_set_generic_element(local, param, + p->length); + break; + case PRISM2_HOSTAPD_MLME: + ret = prism2_ioctl_mlme(local, param); + break; + case PRISM2_HOSTAPD_SCAN_REQ: + ret = prism2_ioctl_scan_req(local, param); + break; + default: + ret = prism2_hostapd(local->ap, param); + ap_ioctl = 1; + break; + } + + if (ret == 1 || !ap_ioctl) { + if (copy_to_user(p->pointer, param, p->length)) { + ret = -EFAULT; + goto out; + } else if (ap_ioctl) + ret = 0; + } + + out: + if (param != NULL) + kfree(param); + + return ret; +} + + +static int prism2_ioctl_ethtool(local_info_t *local, void __user *useraddr) +{ + u32 ethcmd; + struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO }; + + if (copy_from_user(ðcmd, useraddr, sizeof(ethcmd))) + return -EFAULT; + + switch (ethcmd) { + case ETHTOOL_GDRVINFO: + strncpy(info.driver, "hostap", sizeof(info.driver) - 1); + strncpy(info.version, PRISM2_VERSION, + sizeof(info.version) - 1); + snprintf(info.fw_version, sizeof(info.fw_version) - 1, + "%d.%d.%d", (local->sta_fw_ver >> 16) & 0xff, + (local->sta_fw_ver >> 8) & 0xff, + local->sta_fw_ver & 0xff); + if (copy_to_user(useraddr, &info, sizeof(info))) + return -EFAULT; + return 0; + } + + return -EOPNOTSUPP; +} + + +/* Structures to export the Wireless Handlers */ + +static const iw_handler prism2_handler[] = +{ + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) prism2_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) prism2_ioctl_siwfreq, /* SIOCSIWFREQ */ + (iw_handler) prism2_ioctl_giwfreq, /* SIOCGIWFREQ */ + (iw_handler) prism2_ioctl_siwmode, /* SIOCSIWMODE */ + (iw_handler) prism2_ioctl_giwmode, /* SIOCGIWMODE */ + (iw_handler) prism2_ioctl_siwsens, /* SIOCSIWSENS */ + (iw_handler) prism2_ioctl_giwsens, /* SIOCGIWSENS */ + (iw_handler) NULL /* not used */, /* SIOCSIWRANGE */ + (iw_handler) prism2_ioctl_giwrange, /* SIOCGIWRANGE */ + (iw_handler) NULL /* not used */, /* SIOCSIWPRIV */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWPRIV */ + (iw_handler) NULL /* not used */, /* SIOCSIWSTATS */ + (iw_handler) NULL /* kernel code */, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) prism2_ioctl_siwap, /* SIOCSIWAP */ + (iw_handler) prism2_ioctl_giwap, /* SIOCGIWAP */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_giwaplist, /* SIOCGIWAPLIST */ + (iw_handler) prism2_ioctl_siwscan, /* SIOCSIWSCAN */ + (iw_handler) prism2_ioctl_giwscan, /* SIOCGIWSCAN */ + (iw_handler) prism2_ioctl_siwessid, /* SIOCSIWESSID */ + (iw_handler) prism2_ioctl_giwessid, /* SIOCGIWESSID */ + (iw_handler) prism2_ioctl_siwnickn, /* SIOCSIWNICKN */ + (iw_handler) prism2_ioctl_giwnickn, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) prism2_ioctl_siwrate, /* SIOCSIWRATE */ + (iw_handler) prism2_ioctl_giwrate, /* SIOCGIWRATE */ + (iw_handler) prism2_ioctl_siwrts, /* SIOCSIWRTS */ + (iw_handler) prism2_ioctl_giwrts, /* SIOCGIWRTS */ + (iw_handler) prism2_ioctl_siwfrag, /* SIOCSIWFRAG */ + (iw_handler) prism2_ioctl_giwfrag, /* SIOCGIWFRAG */ + (iw_handler) prism2_ioctl_siwtxpow, /* SIOCSIWTXPOW */ + (iw_handler) prism2_ioctl_giwtxpow, /* SIOCGIWTXPOW */ + (iw_handler) prism2_ioctl_siwretry, /* SIOCSIWRETRY */ + (iw_handler) prism2_ioctl_giwretry, /* SIOCGIWRETRY */ + (iw_handler) prism2_ioctl_siwencode, /* SIOCSIWENCODE */ + (iw_handler) prism2_ioctl_giwencode, /* SIOCGIWENCODE */ + (iw_handler) prism2_ioctl_siwpower, /* SIOCSIWPOWER */ + (iw_handler) prism2_ioctl_giwpower, /* SIOCGIWPOWER */ +}; + +static const iw_handler prism2_private_handler[] = +{ /* SIOCIWFIRSTPRIV + */ + (iw_handler) prism2_ioctl_priv_prism2_param, /* 0 */ + (iw_handler) prism2_ioctl_priv_get_prism2_param, /* 1 */ + (iw_handler) prism2_ioctl_priv_writemif, /* 2 */ + (iw_handler) prism2_ioctl_priv_readmif, /* 3 */ +}; + +static const struct iw_handler_def hostap_iw_handler_def = +{ + .num_standard = sizeof(prism2_handler) / sizeof(iw_handler), + .num_private = sizeof(prism2_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(prism2_priv) / sizeof(struct iw_priv_args), + .standard = (iw_handler *) prism2_handler, + .private = (iw_handler *) prism2_private_handler, + .private_args = (struct iw_priv_args *) prism2_priv, + .get_wireless_stats = hostap_get_wireless_stats, +}; + + +int hostap_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) +{ + struct iwreq *wrq = (struct iwreq *) ifr; + struct hostap_interface *iface; + local_info_t *local; + int ret = 0; + + iface = netdev_priv(dev); + local = iface->local; + + switch (cmd) { + /* Private ioctls (iwpriv) that have not yet been converted + * into new wireless extensions API */ + + case PRISM2_IOCTL_INQUIRE: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_inquire(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_MONITOR: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_monitor(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_RESET: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_reset(dev, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_WDS_ADD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_add(local, wrq->u.ap_addr.sa_data, 1); + break; + + case PRISM2_IOCTL_WDS_DEL: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_wds_del(local, wrq->u.ap_addr.sa_data, 1, 0); + break; + + case PRISM2_IOCTL_SET_RID_WORD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_set_rid_word(dev, + (int *) wrq->u.name); + break; + +#ifndef PRISM2_NO_KERNEL_IEEE80211_MGMT + case PRISM2_IOCTL_MACCMD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_mac_cmd_ioctl(local, (int *) wrq->u.name); + break; + + case PRISM2_IOCTL_ADDMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_add_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_DELMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_del_mac(&local->ap->mac_restrictions, + wrq->u.ap_addr.sa_data); + break; + case PRISM2_IOCTL_KICKMAC: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = ap_control_kick_mac(local->ap, local->dev, + wrq->u.ap_addr.sa_data); + break; +#endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ + + + /* Private ioctls that are not used with iwpriv; + * in SIOCDEVPRIVATE range */ + +#ifdef PRISM2_DOWNLOAD_SUPPORT + case PRISM2_IOCTL_DOWNLOAD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_download(local, &wrq->u.data); + break; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + + case PRISM2_IOCTL_HOSTAPD: + if (!capable(CAP_NET_ADMIN)) ret = -EPERM; + else ret = prism2_ioctl_priv_hostapd(local, &wrq->u.data); + break; + + case SIOCETHTOOL: + ret = prism2_ioctl_ethtool(local, ifr->ifr_data); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} diff -Nru a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_pci.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,452 @@ +#define PRISM2_PCI + +/* Host AP driver's support for Intersil Prism2.5 PCI cards is based on + * driver patches from Reyk Floeter and + * Andy Warner */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_pci"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2.5-based 802.11 wireless LAN " + "PCI cards."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2.5-based WLAN PCI cards"); +MODULE_LICENSE("GPL"); + + +/* FIX: do we need mb/wmb/rmb with memory operations? */ + + +static struct pci_device_id prism2_pci_id_table[] __devinitdata = { + /* Intersil Prism3 ISL3872 11Mb/s WLAN Controller */ + { 0x1260, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, + /* Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller */ + { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, + /* Samsung MagicLAN SWL-2210P */ + { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + writeb(v, local->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = readb(local->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + writew(v, local->mem_start + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = readw(local->mem_start + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTW_DATA(v,a) hfa384x_outw_debug(dev, (a), cpu_to_le16((v))) +#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw_debug(dev, (a))) + +#else /* PRISM2_IO_DEBUG */ + +static inline void hfa384x_outb(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + iface = netdev_priv(dev); + local = iface->local; + writeb(v, local->mem_start + a); +} + +static inline u8 hfa384x_inb(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + iface = netdev_priv(dev); + local = iface->local; + return readb(local->mem_start + a); +} + +static inline void hfa384x_outw(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + iface = netdev_priv(dev); + local = iface->local; + writew(v, local->mem_start + a); +} + +static inline u16 hfa384x_inw(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + iface = netdev_priv(dev); + local = iface->local; + return readw(local->mem_start + a); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw(dev, (a)) +#define HFA384X_OUTW_DATA(v,a) hfa384x_outw(dev, (a), cpu_to_le16((v))) +#define HFA384X_INW_DATA(a) (u16) le16_to_cpu(hfa384x_inw(dev, (a))) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + for ( ; len > 1; len -= 2) + *pos++ = HFA384X_INW_DATA(d_off); + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + for ( ; len > 1; len -= 2) + HFA384X_OUTW_DATA(*pos++, d_off); + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + +static void prism2_pci_cor_sreset(local_info_t *local) +{ + struct net_device *dev = local->dev; + u16 reg; + + reg = HFA384X_INB(HFA384X_PCICOR_OFF); + printk(KERN_DEBUG "%s: Original COR value: 0x%0x\n", dev->name, reg); + + /* linux-wlan-ng uses extremely long hold and settle times for + * COR sreset. A comment in the driver code mentions that the long + * delays appear to be necessary. However, at least IBM 22P6901 seems + * to work fine with shorter delays. + * + * Longer delays can be configured by uncommenting following line: */ +/* #define PRISM2_PCI_USE_LONG_DELAYS */ + +#ifdef PRISM2_PCI_USE_LONG_DELAYS + int i; + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(250); + + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(500); + + /* Wait for f/w to complete initialization (CMD:BUSY == 0) */ + i = 2000000 / 10; + while ((HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) && --i) + udelay(10); + +#else /* PRISM2_PCI_USE_LONG_DELAYS */ + + HFA384X_OUTW(reg | 0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + HFA384X_OUTW(reg & ~0x0080, HFA384X_PCICOR_OFF); + mdelay(2); + +#endif /* PRISM2_PCI_USE_LONG_DELAYS */ + + if (HFA384X_INW(HFA384X_CMD_OFF) & HFA384X_CMD_BUSY) { + printk(KERN_DEBUG "%s: COR sreset timeout\n", dev->name); + } +} + + +static void prism2_pci_genesis_reset(local_info_t *local, int hcr) +{ + struct net_device *dev = local->dev; + + HFA384X_OUTW(0x00C5, HFA384X_PCICOR_OFF); + mdelay(10); + HFA384X_OUTW(hcr, HFA384X_PCIHCR_OFF); + mdelay(10); + HFA384X_OUTW(0x0045, HFA384X_PCICOR_OFF); + mdelay(10); +} + + +static struct prism2_helper_functions prism2_pci_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_pci_cor_sreset, + .dev_open = NULL, + .dev_close = NULL, + .genesis_reset = prism2_pci_genesis_reset, + .hw_type = HOSTAP_HW_PCI, +}; + + +static int prism2_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned long phymem; + void __iomem *mem = NULL; + local_info_t *local = NULL; + struct net_device *dev = NULL; + static int cards_found /* = 0 */; + int irq_registered = 0; + struct hostap_interface *iface; + + if (pci_enable_device(pdev)) + return -EIO; + + phymem = pci_resource_start(pdev, 0); + + if (!request_mem_region(phymem, pci_resource_len(pdev, 0), "Prism2")) { + printk(KERN_ERR "prism2: Cannot reserve PCI memory region\n"); + goto err_out_disable; + } + + mem = ioremap(phymem, pci_resource_len(pdev, 0)); + if (mem == NULL) { + printk(KERN_ERR "prism2: Cannot remap PCI memory region\n") ; + goto fail; + } + +#ifdef PRISM2_BUS_MASTER + pci_set_master(pdev); +#endif /* PRISM2_BUS_MASTER */ + + dev = prism2_init_local_data(&prism2_pci_funcs, cards_found); + if (dev == NULL) + goto fail; + iface = netdev_priv(dev); + local = iface->local; + cards_found++; + + dev->irq = pdev->irq; + local->mem_start = mem; + + prism2_pci_cor_sreset(local); + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (!local->pri_only && prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "%s: Intersil Prism2.5 PCI: " + "mem=0x%lx, irq=%d\n", dev->name, phymem, dev->irq); + + return hostap_hw_ready(dev); + + fail: + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (mem) + iounmap(mem); + + release_mem_region(phymem, pci_resource_len(pdev, 0)); + + err_out_disable: + pci_disable_device(pdev); + prism2_free_local_data(dev); + + return -ENODEV; +} + + +static void prism2_pci_remove(struct pci_dev *pdev) +{ + struct net_device *dev; + struct hostap_interface *iface; + void __iomem *mem_start; + + dev = pci_get_drvdata(pdev); + iface = netdev_priv(dev); + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_pci_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (dev->irq) + free_irq(dev->irq, dev); + + mem_start = iface->local->mem_start; + prism2_free_local_data(dev); + + iounmap(mem_start); + + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); +} + + +#ifdef CONFIG_PM +static int prism2_pci_suspend(struct pci_dev *pdev, u32 state) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + if (netif_running(dev)) { + netif_stop_queue(dev); + netif_device_detach(dev); + } + prism2_hw_shutdown(dev, 0); + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, 3); + + return 0; +} + +static int prism2_pci_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + + pci_enable_device(pdev); + pci_restore_state(pdev); + prism2_hw_config(dev, 0); + if (netif_running(dev)) { + netif_device_attach(dev); + netif_start_queue(dev); + } + + return 0; +} +#endif /* CONFIG_PM */ + + +MODULE_DEVICE_TABLE(pci, prism2_pci_id_table); + +static struct pci_driver prism2_pci_drv_id = { + .name = "prism2_pci", + .id_table = prism2_pci_id_table, + .probe = prism2_pci_probe, + .remove = prism2_pci_remove, +#ifdef CONFIG_PM + .suspend = prism2_pci_suspend, + .resume = prism2_pci_resume, +#endif /* CONFIG_PM */ + /* Linux 2.4.6 added save_state and enable_wake that are not used here + */ +}; + + +static int __init init_prism2_pci(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + + return pci_register_driver(&prism2_pci_drv_id); +} + + +static void __exit exit_prism2_pci(void) +{ + pci_unregister_driver(&prism2_pci_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_pci); +module_exit(exit_prism2_pci); diff -Nru a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_plx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,611 @@ +#define PRISM2_PLX + +/* Host AP driver's support for PC Cards on PCI adapters using PLX9052 is + * based on: + * - Host AP driver patch from james@madingley.org + * - linux-wlan-ng driver, Copyright (C) AbsoluteValue Systems, Inc. + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "hostap_wlan.h" + + +static char *version = PRISM2_VERSION " (Jouni Malinen )"; +static char *dev_info = "hostap_plx"; + + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Support for Intersil Prism2-based 802.11 wireless LAN " + "cards (PLX)."); +MODULE_SUPPORTED_DEVICE("Intersil Prism2-based WLAN cards (PLX)"); +MODULE_LICENSE("GPL"); + + +static int ignore_cis; +module_param(ignore_cis, int, 0444); +MODULE_PARM_DESC(ignore_cis, "Do not verify manfid information in CIS"); + + +#define PLX_MIN_ATTR_LEN 512 /* at least 2 x 256 is needed for CIS */ +#define COR_SRESET 0x80 +#define COR_LEVLREQ 0x40 +#define COR_ENABLE_FUNC 0x01 +/* PCI Configuration Registers */ +#define PLX_PCIIPR 0x3d /* PCI Interrupt Pin */ +/* Local Configuration Registers */ +#define PLX_INTCSR 0x4c /* Interrupt Control/Status Register */ +#define PLX_INTCSR_PCI_INTEN BIT(6) /* PCI Interrupt Enable */ +#define PLX_CNTRL 0x50 +#define PLX_CNTRL_SERIAL_EEPROM_PRESENT BIT(28) + + +#define PLXDEV(vendor,dev,str) { vendor, dev, PCI_ANY_ID, PCI_ANY_ID } + +static struct pci_device_id prism2_plx_id_table[] __devinitdata = { + PLXDEV(0x10b7, 0x7770, "3Com AirConnect PCI 777A"), + PLXDEV(0x111a, 0x1023, "Siemens SpeedStream SS1023"), + PLXDEV(0x126c, 0x8030, "Nortel emobility"), + PLXDEV(0x1385, 0x4100, "Netgear MA301"), + PLXDEV(0x15e8, 0x0130, "National Datacomm NCP130 (PLX9052)"), + PLXDEV(0x15e8, 0x0131, "National Datacomm NCP130 (TMD7160)"), + PLXDEV(0x1638, 0x1100, "Eumitcom WL11000"), + PLXDEV(0x16ab, 0x1101, "Global Sun Tech GL24110P (?)"), + PLXDEV(0x16ab, 0x1102, "Linksys WPC11 with WDT11"), + PLXDEV(0x16ab, 0x1103, "Longshine 8031"), + PLXDEV(0x16ec, 0x3685, "US Robotics USR2415"), + PLXDEV(0xec80, 0xec00, "Belkin F5D6000"), + { 0 } +}; + + +/* Array of known Prism2/2.5 PC Card manufactured ids. If your card's manfid + * is not listed here, you will need to add it here to get the driver + * initialized. */ +static struct prism2_plx_manfid { + u16 manfid1, manfid2; +} prism2_plx_known_manfids[] = { + { 0x000b, 0x7110 } /* D-Link DWL-650 Rev. P1 */, + { 0x000b, 0x7300 } /* Philips 802.11b WLAN PCMCIA */, + { 0x0101, 0x0777 } /* 3Com AirConnect PCI 777A */, + { 0x0126, 0x8000 } /* Proxim RangeLAN */, + { 0x0138, 0x0002 } /* Compaq WL100 */, + { 0x0156, 0x0002 } /* Intersil Prism II Ref. Design (and others) */, + { 0x026f, 0x030b } /* Buffalo WLI-CF-S11G */, + { 0x0274, 0x1612 } /* Linksys WPC11 Ver 2.5 */, + { 0x0274, 0x1613 } /* Linksys WPC11 Ver 3 */, + { 0x028a, 0x0002 } /* D-Link DRC-650 */, + { 0x0250, 0x0002 } /* Samsung SWL2000-N */, + { 0xc250, 0x0002 } /* EMTAC A2424i */, + { 0xd601, 0x0002 } /* Z-Com XI300 */, + { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, + { 0, 0} +}; + + +#ifdef PRISM2_IO_DEBUG + +static inline void hfa384x_outb_debug(struct net_device *dev, int a, u8 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTB, a, v); + outb(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u8 hfa384x_inb_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u8 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = inb(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INB, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outw_debug(struct net_device *dev, int a, u16 v) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTW, a, v); + outw(v, dev->base_addr + a); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline u16 hfa384x_inw_debug(struct net_device *dev, int a) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + u16 v; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + v = inw(dev->base_addr + a); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INW, a, v); + spin_unlock_irqrestore(&local->lock, flags); + return v; +} + +static inline void hfa384x_outsw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_OUTSW, a, wc); + outsw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +static inline void hfa384x_insw_debug(struct net_device *dev, int a, + u8 *buf, int wc) +{ + struct hostap_interface *iface; + local_info_t *local; + unsigned long flags; + + iface = netdev_priv(dev); + local = iface->local; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INSW, a, wc); + insw(dev->base_addr + a, buf, wc); + spin_unlock_irqrestore(&local->lock, flags); +} + +#define HFA384X_OUTB(v,a) hfa384x_outb_debug(dev, (a), (v)) +#define HFA384X_INB(a) hfa384x_inb_debug(dev, (a)) +#define HFA384X_OUTW(v,a) hfa384x_outw_debug(dev, (a), (v)) +#define HFA384X_INW(a) hfa384x_inw_debug(dev, (a)) +#define HFA384X_OUTSW(a, buf, wc) hfa384x_outsw_debug(dev, (a), (buf), (wc)) +#define HFA384X_INSW(a, buf, wc) hfa384x_insw_debug(dev, (a), (buf), (wc)) + +#else /* PRISM2_IO_DEBUG */ + +#define HFA384X_OUTB(v,a) outb((v), dev->base_addr + (a)) +#define HFA384X_INB(a) inb(dev->base_addr + (a)) +#define HFA384X_OUTW(v,a) outw((v), dev->base_addr + (a)) +#define HFA384X_INW(a) inw(dev->base_addr + (a)) +#define HFA384X_INSW(a, buf, wc) insw(dev->base_addr + (a), buf, wc) +#define HFA384X_OUTSW(a, buf, wc) outsw(dev->base_addr + (a), buf, wc) + +#endif /* PRISM2_IO_DEBUG */ + + +static int hfa384x_from_bap(struct net_device *dev, u16 bap, void *buf, + int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_INSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + *((char *) pos) = HFA384X_INB(d_off); + + return 0; +} + + +static int hfa384x_to_bap(struct net_device *dev, u16 bap, void *buf, int len) +{ + u16 d_off; + u16 *pos; + + d_off = (bap == 1) ? HFA384X_DATA1_OFF : HFA384X_DATA0_OFF; + pos = (u16 *) buf; + + if (len / 2) + HFA384X_OUTSW(d_off, buf, len / 2); + pos += len / 2; + + if (len & 1) + HFA384X_OUTB(*((char *) pos), d_off); + + return 0; +} + + +/* FIX: This might change at some point.. */ +#include "hostap_hw.c" + + +static void prism2_plx_cor_sreset(local_info_t *local) +{ + unsigned char corsave; + + printk(KERN_DEBUG "%s: Doing reset via direct COR access.\n", + dev_info); + + /* Set sreset bit of COR and clear it after hold time */ + + if (local->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(local->cor_offset); + outb(corsave | COR_SRESET, local->cor_offset); + mdelay(2); + outb(corsave & ~COR_SRESET, local->cor_offset); + mdelay(2); + } else { + /* PLX9052 */ + corsave = readb(local->attr_mem + local->cor_offset); + writeb(corsave | COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(2); + writeb(corsave & ~COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(2); + } +} + + +static void prism2_plx_genesis_reset(local_info_t *local, int hcr) +{ + unsigned char corsave; + + if (local->attr_mem == NULL) { + /* TMD7160 - COR at card's first I/O addr */ + corsave = inb(local->cor_offset); + outb(corsave | COR_SRESET, local->cor_offset); + mdelay(10); + outb(hcr, local->cor_offset + 2); + mdelay(10); + outb(corsave & ~COR_SRESET, local->cor_offset); + mdelay(10); + } else { + /* PLX9052 */ + corsave = readb(local->attr_mem + local->cor_offset); + writeb(corsave | COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(10); + writeb(hcr, local->attr_mem + local->cor_offset + 2); + mdelay(10); + writeb(corsave & ~COR_SRESET, + local->attr_mem + local->cor_offset); + mdelay(10); + } +} + + +static struct prism2_helper_functions prism2_plx_funcs = +{ + .card_present = NULL, + .cor_sreset = prism2_plx_cor_sreset, + .dev_open = NULL, + .dev_close = NULL, + .genesis_reset = prism2_plx_genesis_reset, + .hw_type = HOSTAP_HW_PLX, +}; + + +static int prism2_plx_check_cis(void __iomem *attr_mem, int attr_len, + unsigned int *cor_offset, + unsigned int *cor_index) +{ +#define CISTPL_CONFIG 0x1A +#define CISTPL_MANFID 0x20 +#define CISTPL_END 0xFF +#define CIS_MAX_LEN 256 + u8 cis[CIS_MAX_LEN]; + int i, pos; + unsigned int rmsz, rasz, manfid1, manfid2; + struct prism2_plx_manfid *manfid; + + /* read CIS; it is in even offsets in the beginning of attr_mem */ + for (i = 0; i < CIS_MAX_LEN; i++) + cis[i] = readb(attr_mem + 2 * i); + printk(KERN_DEBUG "%s: CIS: %02x %02x %02x %02x %02x %02x ...\n", + dev_info, cis[0], cis[1], cis[2], cis[3], cis[4], cis[5]); + + /* set reasonable defaults for Prism2 cards just in case CIS parsing + * fails */ + *cor_offset = 0x3e0; + *cor_index = 0x01; + manfid1 = manfid2 = 0; + + pos = 0; + while (pos < CIS_MAX_LEN - 1 && cis[pos] != CISTPL_END) { + if (pos + cis[pos + 1] >= CIS_MAX_LEN) + goto cis_error; + + switch (cis[pos]) { + case CISTPL_CONFIG: + if (cis[pos + 1] < 1) + goto cis_error; + rmsz = (cis[pos + 2] & 0x3c) >> 2; + rasz = cis[pos + 2] & 0x03; + if (4 + rasz + rmsz > cis[pos + 1]) + goto cis_error; + *cor_index = cis[pos + 3] & 0x3F; + *cor_offset = 0; + for (i = 0; i <= rasz; i++) + *cor_offset += cis[pos + 4 + i] << (8 * i); + printk(KERN_DEBUG "%s: cor_index=0x%x " + "cor_offset=0x%x\n", dev_info, + *cor_index, *cor_offset); + if (*cor_offset > attr_len) { + printk(KERN_ERR "%s: COR offset not within " + "attr_mem\n", dev_info); + return -1; + } + break; + + case CISTPL_MANFID: + if (cis[pos + 1] < 4) + goto cis_error; + manfid1 = cis[pos + 2] + (cis[pos + 3] << 8); + manfid2 = cis[pos + 4] + (cis[pos + 5] << 8); + printk(KERN_DEBUG "%s: manfid=0x%04x, 0x%04x\n", + dev_info, manfid1, manfid2); + break; + } + + pos += cis[pos + 1] + 2; + } + + if (pos >= CIS_MAX_LEN || cis[pos] != CISTPL_END) + goto cis_error; + + for (manfid = prism2_plx_known_manfids; manfid->manfid1 != 0; manfid++) + if (manfid1 == manfid->manfid1 && manfid2 == manfid->manfid2) + return 0; + + printk(KERN_INFO "%s: unknown manfid 0x%04x, 0x%04x - assuming this is" + " not supported card\n", dev_info, manfid1, manfid2); + goto fail; + + cis_error: + printk(KERN_WARNING "%s: invalid CIS data\n", dev_info); + + fail: + if (ignore_cis) { + printk(KERN_INFO "%s: ignore_cis parameter set - ignoring " + "errors during CIS verification\n", dev_info); + return 0; + } + return -1; +} + + +static int prism2_plx_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + unsigned int pccard_ioaddr, plx_ioaddr; + unsigned long pccard_attr_mem; + unsigned int pccard_attr_len; + void __iomem *attr_mem = NULL; + unsigned int cor_offset, cor_index; + u32 reg; + local_info_t *local = NULL; + struct net_device *dev = NULL; + struct hostap_interface *iface; + static int cards_found /* = 0 */; + int irq_registered = 0; + int tmd7160; + + if (pci_enable_device(pdev)) + return -EIO; + + /* National Datacomm NCP130 based on TMD7160, not PLX9052. */ + tmd7160 = (pdev->vendor == 0x15e8) && (pdev->device == 0x0131); + + plx_ioaddr = pci_resource_start(pdev, 1); + pccard_ioaddr = pci_resource_start(pdev, tmd7160 ? 2 : 3); + + if (tmd7160) { + /* TMD7160 */ + attr_mem = NULL; /* no access to PC Card attribute memory */ + + printk(KERN_INFO "TMD7160 PCI/PCMCIA adapter: io=0x%x, " + "irq=%d, pccard_io=0x%x\n", + plx_ioaddr, pdev->irq, pccard_ioaddr); + + cor_offset = plx_ioaddr; + cor_index = 0x04; + + outb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, plx_ioaddr); + mdelay(1); + reg = inb(plx_ioaddr); + if (reg != (cor_index | COR_LEVLREQ | COR_ENABLE_FUNC)) { + printk(KERN_ERR "%s: Error setting COR (expected=" + "0x%02x, was=0x%02x)\n", dev_info, + cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, reg); + goto fail; + } + } else { + /* PLX9052 */ + pccard_attr_mem = pci_resource_start(pdev, 2); + pccard_attr_len = pci_resource_len(pdev, 2); + if (pccard_attr_len < PLX_MIN_ATTR_LEN) + goto fail; + + + attr_mem = ioremap(pccard_attr_mem, pccard_attr_len); + if (attr_mem == NULL) { + printk(KERN_ERR "%s: cannot remap attr_mem\n", + dev_info); + goto fail; + } + + printk(KERN_INFO "PLX9052 PCI/PCMCIA adapter: " + "mem=0x%lx, plx_io=0x%x, irq=%d, pccard_io=0x%x\n", + pccard_attr_mem, plx_ioaddr, pdev->irq, pccard_ioaddr); + + if (prism2_plx_check_cis(attr_mem, pccard_attr_len, + &cor_offset, &cor_index)) { + printk(KERN_INFO "Unknown PC Card CIS - not a " + "Prism2/2.5 card?\n"); + goto fail; + } + + printk(KERN_DEBUG "Prism2/2.5 PC Card detected in PLX9052 " + "adapter\n"); + + /* Write COR to enable PC Card */ + writeb(cor_index | COR_LEVLREQ | COR_ENABLE_FUNC, + attr_mem + cor_offset); + + /* Enable PCI interrupts if they are not already enabled */ + reg = inl(plx_ioaddr + PLX_INTCSR); + printk(KERN_DEBUG "PLX_INTCSR=0x%x\n", reg); + if (!(reg & PLX_INTCSR_PCI_INTEN)) { + outl(reg | PLX_INTCSR_PCI_INTEN, + plx_ioaddr + PLX_INTCSR); + if (!(inl(plx_ioaddr + PLX_INTCSR) & + PLX_INTCSR_PCI_INTEN)) { + printk(KERN_WARNING "%s: Could not enable " + "Local Interrupts\n", dev_info); + goto fail; + } + } + + reg = inl(plx_ioaddr + PLX_CNTRL); + printk(KERN_DEBUG "PLX_CNTRL=0x%x (Serial EEPROM " + "present=%d)\n", + reg, (reg & PLX_CNTRL_SERIAL_EEPROM_PRESENT) != 0); + /* should set PLX_PCIIPR to 0x01 (INTA#) if Serial EEPROM is + * not present; but are there really such cards in use(?) */ + } + + dev = prism2_init_local_data(&prism2_plx_funcs, cards_found); + if (dev == NULL) + goto fail; + iface = netdev_priv(dev); + local = iface->local; + cards_found++; + + dev->irq = pdev->irq; + dev->base_addr = pccard_ioaddr; + local->attr_mem = attr_mem; + local->cor_offset = cor_offset; + + pci_set_drvdata(pdev, dev); + + if (request_irq(dev->irq, prism2_interrupt, SA_SHIRQ, dev->name, + dev)) { + printk(KERN_WARNING "%s: request_irq failed\n", dev->name); + goto fail; + } else + irq_registered = 1; + + if (prism2_hw_config(dev, 1)) { + printk(KERN_DEBUG "%s: hardware initialization failed\n", + dev_info); + goto fail; + } + + return hostap_hw_ready(dev); + + fail: + prism2_free_local_data(dev); + + if (irq_registered && dev) + free_irq(dev->irq, dev); + + if (attr_mem) + iounmap(attr_mem); + + pci_disable_device(pdev); + + return -ENODEV; +} + + +static void prism2_plx_remove(struct pci_dev *pdev) +{ + struct net_device *dev; + struct hostap_interface *iface; + + dev = pci_get_drvdata(pdev); + iface = netdev_priv(dev); + + /* Reset the hardware, and ensure interrupts are disabled. */ + prism2_plx_cor_sreset(iface->local); + hfa384x_disable_interrupts(dev); + + if (iface->local->attr_mem) + iounmap(iface->local->attr_mem); + if (dev->irq) + free_irq(dev->irq, dev); + + prism2_free_local_data(dev); + pci_disable_device(pdev); +} + + +MODULE_DEVICE_TABLE(pci, prism2_plx_id_table); + +static struct pci_driver prism2_plx_drv_id = { + .name = "prism2_plx", + .id_table = prism2_plx_id_table, + .probe = prism2_plx_probe, + .remove = prism2_plx_remove, + .suspend = NULL, + .resume = NULL, + .enable_wake = NULL +}; + + +static int __init init_prism2_plx(void) +{ + printk(KERN_INFO "%s: %s\n", dev_info, version); + + return pci_register_driver(&prism2_plx_drv_id); +} + + +static void __exit exit_prism2_plx(void) +{ + pci_unregister_driver(&prism2_plx_drv_id); + printk(KERN_INFO "%s: Driver unloaded\n", dev_info); +} + + +module_init(init_prism2_plx); +module_exit(exit_prism2_plx); diff -Nru a/drivers/net/wireless/hostap/hostap_proc.c b/drivers/net/wireless/hostap/hostap_proc.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_proc.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,466 @@ +/* /proc routines for Host AP driver */ + +#define PROC_LIMIT (PAGE_SIZE - 80) + + +#ifndef PRISM2_NO_PROCFS_DEBUG +static int prism2_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int i; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "next_txfid=%d next_alloc=%d\n", + local->next_txfid, local->next_alloc); + for (i = 0; i < PRISM2_TXFID_COUNT; i++) + p += sprintf(p, "FID: tx=%04X intransmit=%04X\n", + local->txfid[i], local->intransmitfid[i]); + p += sprintf(p, "FW TX rate control: %d\n", local->fw_tx_rate_control); + p += sprintf(p, "beacon_int=%d\n", local->beacon_int); + p += sprintf(p, "dtim_period=%d\n", local->dtim_period); + p += sprintf(p, "wds_max_connections=%d\n", + local->wds_max_connections); + p += sprintf(p, "dev_enabled=%d\n", local->dev_enabled); + p += sprintf(p, "sw_tick_stuck=%d\n", local->sw_tick_stuck); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt[i] && local->crypt[i]->ops) { + p += sprintf(p, "crypt[%d]=%s\n", + i, local->crypt[i]->ops->name); + } + } + p += sprintf(p, "pri_only=%d\n", local->pri_only); + p += sprintf(p, "pci=%d\n", local->func->hw_type == HOSTAP_HW_PCI); + p += sprintf(p, "sram_type=%d\n", local->sram_type); + p += sprintf(p, "no_pri=%d\n", local->no_pri); + + return (p - page); +} +#endif /* PRISM2_NO_PROCFS_DEBUG */ + + +static int prism2_stats_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct comm_tallies_sums *sums = (struct comm_tallies_sums *) + &local->comm_tallies; + + if (off != 0) { + *eof = 1; + return 0; + } + + p += sprintf(p, "TxUnicastFrames=%u\n", sums->tx_unicast_frames); + p += sprintf(p, "TxMulticastframes=%u\n", sums->tx_multicast_frames); + p += sprintf(p, "TxFragments=%u\n", sums->tx_fragments); + p += sprintf(p, "TxUnicastOctets=%u\n", sums->tx_unicast_octets); + p += sprintf(p, "TxMulticastOctets=%u\n", sums->tx_multicast_octets); + p += sprintf(p, "TxDeferredTransmissions=%u\n", + sums->tx_deferred_transmissions); + p += sprintf(p, "TxSingleRetryFrames=%u\n", + sums->tx_single_retry_frames); + p += sprintf(p, "TxMultipleRetryFrames=%u\n", + sums->tx_multiple_retry_frames); + p += sprintf(p, "TxRetryLimitExceeded=%u\n", + sums->tx_retry_limit_exceeded); + p += sprintf(p, "TxDiscards=%u\n", sums->tx_discards); + p += sprintf(p, "RxUnicastFrames=%u\n", sums->rx_unicast_frames); + p += sprintf(p, "RxMulticastFrames=%u\n", sums->rx_multicast_frames); + p += sprintf(p, "RxFragments=%u\n", sums->rx_fragments); + p += sprintf(p, "RxUnicastOctets=%u\n", sums->rx_unicast_octets); + p += sprintf(p, "RxMulticastOctets=%u\n", sums->rx_multicast_octets); + p += sprintf(p, "RxFCSErrors=%u\n", sums->rx_fcs_errors); + p += sprintf(p, "RxDiscardsNoBuffer=%u\n", + sums->rx_discards_no_buffer); + p += sprintf(p, "TxDiscardsWrongSA=%u\n", sums->tx_discards_wrong_sa); + p += sprintf(p, "RxDiscardsWEPUndecryptable=%u\n", + sums->rx_discards_wep_undecryptable); + p += sprintf(p, "RxMessageInMsgFragments=%u\n", + sums->rx_message_in_msg_fragments); + p += sprintf(p, "RxMessageInBadMsgFragments=%u\n", + sums->rx_message_in_bad_msg_fragments); + /* FIX: this may grow too long for one page(?) */ + + return (p - page); +} + + +static int prism2_wds_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct list_head *ptr; + struct hostap_interface *iface; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + read_lock_bh(&local->iface_lock); + list_for_each(ptr, &local->hostap_interfaces) { + iface = list_entry(ptr, struct hostap_interface, list); + if (iface->type != HOSTAP_INTERFACE_WDS) + continue; + p += sprintf(p, "%s\t" MACSTR "\n", + iface->dev->name, + MAC2STR(iface->u.wds.remote_addr)); + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "%s: wds proc did not fit\n", + local->dev->name); + break; + } + } + read_unlock_bh(&local->iface_lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_bss_list_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + struct list_head *ptr; + struct hostap_bss_info *bss; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "#BSSID\tlast_update\tcount\tcapab_info\tSSID(txt)\t" + "SSID(hex)\tWPA IE\n"); + spin_lock_bh(&local->lock); + list_for_each(ptr, &local->bss_list) { + bss = list_entry(ptr, struct hostap_bss_info, list); + p += sprintf(p, MACSTR "\t%lu\t%u\t0x%x\t", + MAC2STR(bss->bssid), bss->last_update, + bss->count, bss->capab_info); + for (i = 0; i < bss->ssid_len; i++) { + p += sprintf(p, "%c", + bss->ssid[i] >= 32 && bss->ssid[i] < 127 ? + bss->ssid[i] : '_'); + } + p += sprintf(p, "\t"); + for (i = 0; i < bss->ssid_len; i++) { + p += sprintf(p, "%02x", bss->ssid[i]); + } + p += sprintf(p, "\t"); + for (i = 0; i < bss->wpa_ie_len; i++) { + p += sprintf(p, "%02x", bss->wpa_ie[i]); + } + p += sprintf(p, "\n"); + if ((p - page) > PROC_LIMIT) { + printk(KERN_DEBUG "%s: BSS proc did not fit\n", + local->dev->name); + break; + } + } + spin_unlock_bh(&local->lock); + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_crypt_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int i; + + if (off > PROC_LIMIT) { + *eof = 1; + return 0; + } + + p += sprintf(p, "tx_keyidx=%d\n", local->tx_keyidx); + for (i = 0; i < WEP_KEYS; i++) { + if (local->crypt[i] && local->crypt[i]->ops && + local->crypt[i]->ops->print_stats) { + p = local->crypt[i]->ops->print_stats( + p, local->crypt[i]->priv); + } + } + + if ((p - page) <= off) { + *eof = 1; + return 0; + } + + *start = page + off; + + return (p - page - off); +} + + +static int prism2_pda_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (local->pda == NULL || off >= PRISM2_PDA_SIZE) { + *eof = 1; + return 0; + } + + if (off + count > PRISM2_PDA_SIZE) + count = PRISM2_PDA_SIZE - off; + + memcpy(page, local->pda + off, count); + return count; +} + + +static int prism2_aux_dump_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + + if (local->func->read_aux == NULL) { + *eof = 1; + return 0; + } + + if (local->func->read_aux(local->dev, off, count, page)) { + *eof = 1; + return 0; + } + *start = page; + + return count; +} + + +#ifdef PRISM2_IO_DEBUG +static int prism2_io_debug_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + local_info_t *local = (local_info_t *) data; + int head = local->io_debug_head; + int start_bytes, left, copy, copied; + + if (off + count > PRISM2_IO_DEBUG_SIZE * 4) { + *eof = 1; + if (off >= PRISM2_IO_DEBUG_SIZE * 4) + return 0; + count = PRISM2_IO_DEBUG_SIZE * 4 - off; + } + + copied = 0; + start_bytes = (PRISM2_IO_DEBUG_SIZE - head) * 4; + left = count; + + if (off < start_bytes) { + copy = start_bytes - off; + if (copy > count) + copy = count; + memcpy(page, ((u8 *) &local->io_debug[head]) + off, copy); + left -= copy; + if (left > 0) + memcpy(&page[copy], local->io_debug, left); + } else { + memcpy(page, ((u8 *) local->io_debug) + (off - start_bytes), + left); + } + + *start = page; + + return count; +} +#endif /* PRISM2_IO_DEBUG */ + + +#ifndef PRISM2_NO_STATION_MODES +static int prism2_scan_results_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *p = page; + local_info_t *local = (local_info_t *) data; + int entries, entry, i, len, total = 0, hostscan; + struct hfa384x_scan_result *scanres; + struct hfa384x_hostscan_result *hscanres; + u8 *pos; + + p += sprintf(p, "CHID ANL SL BcnInt Capab Rate BSSID ATIM SupRates " + "SSID\n"); + + spin_lock_bh(&local->lock); + hostscan = local->last_scan_type == PRISM2_HOSTSCAN; + entries = hostscan ? local->last_hostscan_results_count : + local->last_scan_results_count; + for (entry = 0; entry < entries; entry++) { + hscanres = &local->last_hostscan_results[entry]; + scanres = &local->last_scan_results[entry]; + + if (total + (p - page) <= off) { + total += p - page; + p = page; + } + if (total + (p - page) > off + count) + break; + if ((p - page) > (PAGE_SIZE - 200)) + break; + + if (hostscan) { + p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR " %d ", + le16_to_cpu(hscanres->chid), + (s16) le16_to_cpu(hscanres->anl), + (s16) le16_to_cpu(hscanres->sl), + le16_to_cpu(hscanres->beacon_interval), + le16_to_cpu(hscanres->capability), + le16_to_cpu(hscanres->rate), + MAC2STR(hscanres->bssid), + le16_to_cpu(hscanres->atim)); + } else { + p += sprintf(p, "%d %d %d %d 0x%02x %d " MACSTR + " N/A ", + le16_to_cpu(scanres->chid), + (s16) le16_to_cpu(scanres->anl), + (s16) le16_to_cpu(scanres->sl), + le16_to_cpu(scanres->beacon_interval), + le16_to_cpu(scanres->capability), + le16_to_cpu(scanres->rate), + MAC2STR(scanres->bssid)); + } + + pos = hostscan ? hscanres->sup_rates : scanres->sup_rates; + for (i = 0; i < sizeof(hscanres->sup_rates); i++) { + if (pos[i] == 0) + break; + p += sprintf(p, "<%02x>", pos[i]); + } + p += sprintf(p, " "); + + pos = hostscan ? hscanres->ssid : scanres->ssid; + len = le16_to_cpu(hostscan ? hscanres->ssid_len : + scanres->ssid_len); + if (len > 32) + len = 32; + for (i = 0; i < len; i++) { + unsigned char c = pos[i]; + if (c >= 32 && c < 127) + p += sprintf(p, "%c", c); + else + p += sprintf(p, "<%02x>", c); + } + p += sprintf(p, "\n"); + } + spin_unlock_bh(&local->lock); + + total += (p - page); + if (total >= off + count) + *eof = 1; + + if (total < off) { + *eof = 1; + return 0; + } + + len = total - off; + if (len > (p - page)) + len = p - page; + *start = p - len; + if (len > count) + len = count; + + return len; +} +#endif /* PRISM2_NO_STATION_MODES */ + + +void hostap_init_proc(local_info_t *local) +{ + local->proc = NULL; + + if (hostap_proc == NULL) { + printk(KERN_WARNING "%s: hostap proc directory not created\n", + local->dev->name); + return; + } + + local->proc = proc_mkdir(local->ddev->name, hostap_proc); + if (local->proc == NULL) { + printk(KERN_INFO "/proc/net/hostap/%s creation failed\n", + local->ddev->name); + return; + } + +#ifndef PRISM2_NO_PROCFS_DEBUG + create_proc_read_entry("debug", 0, local->proc, + prism2_debug_proc_read, local); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + create_proc_read_entry("stats", 0, local->proc, + prism2_stats_proc_read, local); + create_proc_read_entry("wds", 0, local->proc, + prism2_wds_proc_read, local); + create_proc_read_entry("pda", 0, local->proc, + prism2_pda_proc_read, local); + create_proc_read_entry("aux_dump", 0, local->proc, + prism2_aux_dump_proc_read, local); + create_proc_read_entry("bss_list", 0, local->proc, + prism2_bss_list_proc_read, local); + create_proc_read_entry("crypt", 0, local->proc, + prism2_crypt_proc_read, local); +#ifdef PRISM2_IO_DEBUG + create_proc_read_entry("io_debug", 0, local->proc, + prism2_io_debug_proc_read, local); +#endif /* PRISM2_IO_DEBUG */ +#ifndef PRISM2_NO_STATION_MODES + create_proc_read_entry("scan_results", 0, local->proc, + prism2_scan_results_proc_read, local); +#endif /* PRISM2_NO_STATION_MODES */ +} + + +void hostap_remove_proc(local_info_t *local) +{ + if (local->proc != NULL) { +#ifndef PRISM2_NO_STATION_MODES + remove_proc_entry("scan_results", local->proc); +#endif /* PRISM2_NO_STATION_MODES */ +#ifdef PRISM2_IO_DEBUG + remove_proc_entry("io_debug", local->proc); +#endif /* PRISM2_IO_DEBUG */ + remove_proc_entry("pda", local->proc); + remove_proc_entry("aux_dump", local->proc); + remove_proc_entry("wds", local->proc); + remove_proc_entry("stats", local->proc); + remove_proc_entry("bss_list", local->proc); + remove_proc_entry("crypt", local->proc); +#ifndef PRISM2_NO_PROCFS_DEBUG + remove_proc_entry("debug", local->proc); +#endif /* PRISM2_NO_PROCFS_DEBUG */ + if (local->ddev != NULL && hostap_proc != NULL) + remove_proc_entry(local->ddev->name, hostap_proc); + } +} + + +EXPORT_SYMBOL(hostap_init_proc); +EXPORT_SYMBOL(hostap_remove_proc); diff -Nru a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/drivers/net/wireless/hostap/hostap_wlan.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,1071 @@ +#ifndef HOSTAP_WLAN_H +#define HOSTAP_WLAN_H + +#include "hostap_config.h" +#include "hostap_crypt.h" +#include "hostap_common.h" + +#define MAX_PARM_DEVICES 8 +#define PARM_MIN_MAX "1-" __MODULE_STRING(MAX_PARM_DEVICES) +#define DEF_INTS -1, -1, -1, -1, -1, -1, -1 +#define GET_INT_PARM(var,idx) var[var[idx] < 0 ? 0 : idx] + + +/* Specific skb->protocol value that indicates that the packet already contains + * txdesc header. + * FIX: This might need own value that would be allocated especially for Prism2 + * txdesc; ETH_P_CONTROL is commented as "Card specific control frames". + * However, these skb's should have only minimal path in the kernel side since + * prism2_send_mgmt() sends these with dev_queue_xmit() to prism2_tx(). */ +#define ETH_P_HOSTAP ETH_P_CONTROL + +#ifndef ARPHRD_IEEE80211 +#define ARPHRD_IEEE80211 801 +#endif +#ifndef ARPHRD_IEEE80211_PRISM +#define ARPHRD_IEEE80211_PRISM 802 +#endif + +/* ARPHRD_IEEE80211_PRISM uses a bloated version of Prism2 RX frame header + * (from linux-wlan-ng) */ +struct linux_wlan_ng_val { + u32 did; + u16 status, len; + u32 data; +} __attribute__ ((packed)); + +struct linux_wlan_ng_prism_hdr { + u32 msgcode, msglen; + char devname[16]; + struct linux_wlan_ng_val hosttime, mactime, channel, rssi, sq, signal, + noise, rate, istx, frmlen; +} __attribute__ ((packed)); + +struct linux_wlan_ng_cap_hdr { + u32 version; + u32 length; + u64 mactime; + u64 hosttime; + u32 phytype; + u32 channel; + u32 datarate; + u32 antenna; + u32 priority; + u32 ssi_type; + s32 ssi_signal; + s32 ssi_noise; + u32 preamble; + u32 encoding; +} __attribute__ ((packed)); + +#define LWNG_CAP_DID_BASE (4 | (1 << 6)) /* section 4, group 1 */ +#define LWNG_CAPHDR_VERSION 0x80211001 + +struct hfa384x_rx_frame { + /* HFA384X RX frame descriptor */ + u16 status; /* HFA384X_RX_STATUS_ flags */ + u32 time; /* timestamp, 1 microsecond resolution */ + u8 silence; /* 27 .. 154; seems to be 0 */ + u8 signal; /* 27 .. 154 */ + u8 rate; /* 10, 20, 55, or 110 */ + u8 rxflow; + u32 reserved; + + /* 802.11 */ + u16 frame_control; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_tx_frame { + /* HFA384X TX frame descriptor */ + u16 status; /* HFA384X_TX_STATUS_ flags */ + u16 reserved1; + u16 reserved2; + u32 sw_support; + u8 retry_count; /* not yet implemented */ + u8 tx_rate; /* Host AP only; 0 = firmware, or 10, 20, 55, 110 */ + u16 tx_control; /* HFA384X_TX_CTRL_ flags */ + + /* 802.11 */ + u16 frame_control; /* parts not used */ + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; /* filled by firmware */ + u8 addr3[6]; + u16 seq_ctrl; /* filled by firmware */ + u8 addr4[6]; + u16 data_len; + + /* 802.3 */ + u8 dst_addr[6]; + u8 src_addr[6]; + u16 len; + + /* followed by frame data; max 2304 bytes */ +} __attribute__ ((packed)); + + +struct hfa384x_rid_hdr +{ + u16 len; + u16 rid; +} __attribute__ ((packed)); + + +/* Macro for converting signal levels (range 27 .. 154) to wireless ext + * dBm value with some accuracy */ +#define HFA384X_LEVEL_TO_dBm(v) 0x100 + (v) * 100 / 255 - 100 + +#define HFA384X_LEVEL_TO_dBm_sign(v) (v) * 100 / 255 - 100 + +struct hfa384x_scan_request { + u16 channel_list; + u16 txrate; /* HFA384X_RATES_* */ +} __attribute__ ((packed)); + +struct hfa384x_hostscan_request { + u16 channel_list; + u16 txrate; + u16 target_ssid_len; + u8 target_ssid[32]; +} __attribute__ ((packed)); + +struct hfa384x_join_request { + u8 bssid[6]; + u16 channel; +} __attribute__ ((packed)); + +struct hfa384x_info_frame { + u16 len; + u16 type; +} __attribute__ ((packed)); + +struct hfa384x_comm_tallies { + u16 tx_unicast_frames; + u16 tx_multicast_frames; + u16 tx_fragments; + u16 tx_unicast_octets; + u16 tx_multicast_octets; + u16 tx_deferred_transmissions; + u16 tx_single_retry_frames; + u16 tx_multiple_retry_frames; + u16 tx_retry_limit_exceeded; + u16 tx_discards; + u16 rx_unicast_frames; + u16 rx_multicast_frames; + u16 rx_fragments; + u16 rx_unicast_octets; + u16 rx_multicast_octets; + u16 rx_fcs_errors; + u16 rx_discards_no_buffer; + u16 tx_discards_wrong_sa; + u16 rx_discards_wep_undecryptable; + u16 rx_message_in_msg_fragments; + u16 rx_message_in_bad_msg_fragments; +} __attribute__ ((packed)); + +struct hfa384x_comm_tallies32 { + u32 tx_unicast_frames; + u32 tx_multicast_frames; + u32 tx_fragments; + u32 tx_unicast_octets; + u32 tx_multicast_octets; + u32 tx_deferred_transmissions; + u32 tx_single_retry_frames; + u32 tx_multiple_retry_frames; + u32 tx_retry_limit_exceeded; + u32 tx_discards; + u32 rx_unicast_frames; + u32 rx_multicast_frames; + u32 rx_fragments; + u32 rx_unicast_octets; + u32 rx_multicast_octets; + u32 rx_fcs_errors; + u32 rx_discards_no_buffer; + u32 tx_discards_wrong_sa; + u32 rx_discards_wep_undecryptable; + u32 rx_message_in_msg_fragments; + u32 rx_message_in_bad_msg_fragments; +} __attribute__ ((packed)); + +struct hfa384x_scan_result_hdr { + u16 reserved; + u16 scan_reason; +#define HFA384X_SCAN_IN_PROGRESS 0 /* no results available yet */ +#define HFA384X_SCAN_HOST_INITIATED 1 +#define HFA384X_SCAN_FIRMWARE_INITIATED 2 +#define HFA384X_SCAN_INQUIRY_FROM_HOST 3 +} __attribute__ ((packed)); + +#define HFA384X_SCAN_MAX_RESULTS 32 + +struct hfa384x_scan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; +} __attribute__ ((packed)); + +struct hfa384x_hostscan_result { + u16 chid; + u16 anl; + u16 sl; + u8 bssid[6]; + u16 beacon_interval; + u16 capability; + u16 ssid_len; + u8 ssid[32]; + u8 sup_rates[10]; + u16 rate; + u16 atim; +} __attribute__ ((packed)); + +struct comm_tallies_sums { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_wep_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + + +struct hfa384x_regs { + u16 cmd; + u16 evstat; + u16 offset0; + u16 offset1; + u16 swsupport0; +}; + + +#if defined(PRISM2_PCCARD) || defined(PRISM2_PLX) +/* I/O ports for HFA384X Controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x02 +#define HFA384X_PARAM1_OFF 0x04 +#define HFA384X_PARAM2_OFF 0x06 +#define HFA384X_STATUS_OFF 0x08 +#define HFA384X_RESP0_OFF 0x0A +#define HFA384X_RESP1_OFF 0x0C +#define HFA384X_RESP2_OFF 0x0E +#define HFA384X_INFOFID_OFF 0x10 +#define HFA384X_CONTROL_OFF 0x14 +#define HFA384X_SELECT0_OFF 0x18 +#define HFA384X_SELECT1_OFF 0x1A +#define HFA384X_OFFSET0_OFF 0x1C +#define HFA384X_OFFSET1_OFF 0x1E +#define HFA384X_RXFID_OFF 0x20 +#define HFA384X_ALLOCFID_OFF 0x22 +#define HFA384X_TXCOMPLFID_OFF 0x24 +#define HFA384X_SWSUPPORT0_OFF 0x28 +#define HFA384X_SWSUPPORT1_OFF 0x2A +#define HFA384X_SWSUPPORT2_OFF 0x2C +#define HFA384X_EVSTAT_OFF 0x30 +#define HFA384X_INTEN_OFF 0x32 +#define HFA384X_EVACK_OFF 0x34 +#define HFA384X_DATA0_OFF 0x36 +#define HFA384X_DATA1_OFF 0x38 +#define HFA384X_AUXPAGE_OFF 0x3A +#define HFA384X_AUXOFFSET_OFF 0x3C +#define HFA384X_AUXDATA_OFF 0x3E +#endif /* PRISM2_PCCARD || PRISM2_PLX */ + +#ifdef PRISM2_PCI +/* Memory addresses for ISL3874 controller access */ +#define HFA384X_CMD_OFF 0x00 +#define HFA384X_PARAM0_OFF 0x04 +#define HFA384X_PARAM1_OFF 0x08 +#define HFA384X_PARAM2_OFF 0x0C +#define HFA384X_STATUS_OFF 0x10 +#define HFA384X_RESP0_OFF 0x14 +#define HFA384X_RESP1_OFF 0x18 +#define HFA384X_RESP2_OFF 0x1C +#define HFA384X_INFOFID_OFF 0x20 +#define HFA384X_CONTROL_OFF 0x28 +#define HFA384X_SELECT0_OFF 0x30 +#define HFA384X_SELECT1_OFF 0x34 +#define HFA384X_OFFSET0_OFF 0x38 +#define HFA384X_OFFSET1_OFF 0x3C +#define HFA384X_RXFID_OFF 0x40 +#define HFA384X_ALLOCFID_OFF 0x44 +#define HFA384X_TXCOMPLFID_OFF 0x48 +#define HFA384X_PCICOR_OFF 0x4C +#define HFA384X_SWSUPPORT0_OFF 0x50 +#define HFA384X_SWSUPPORT1_OFF 0x54 +#define HFA384X_SWSUPPORT2_OFF 0x58 +#define HFA384X_PCIHCR_OFF 0x5C +#define HFA384X_EVSTAT_OFF 0x60 +#define HFA384X_INTEN_OFF 0x64 +#define HFA384X_EVACK_OFF 0x68 +#define HFA384X_DATA0_OFF 0x6C +#define HFA384X_DATA1_OFF 0x70 +#define HFA384X_AUXPAGE_OFF 0x74 +#define HFA384X_AUXOFFSET_OFF 0x78 +#define HFA384X_AUXDATA_OFF 0x7C +#define HFA384X_PCI_M0_ADDRH_OFF 0x80 +#define HFA384X_PCI_M0_ADDRL_OFF 0x84 +#define HFA384X_PCI_M0_LEN_OFF 0x88 +#define HFA384X_PCI_M0_CTL_OFF 0x8C +#define HFA384X_PCI_STATUS_OFF 0x98 +#define HFA384X_PCI_M1_ADDRH_OFF 0xA0 +#define HFA384X_PCI_M1_ADDRL_OFF 0xA4 +#define HFA384X_PCI_M1_LEN_OFF 0xA8 +#define HFA384X_PCI_M1_CTL_OFF 0xAC + +/* PCI bus master control bits (these are undocumented; based on guessing and + * experimenting..) */ +#define HFA384X_PCI_CTL_FROM_BAP (BIT(5) | BIT(1) | BIT(0)) +#define HFA384X_PCI_CTL_TO_BAP (BIT(5) | BIT(0)) + +#endif /* PRISM2_PCI */ + + +/* Command codes for CMD reg. */ +#define HFA384X_CMDCODE_INIT 0x00 +#define HFA384X_CMDCODE_ENABLE 0x01 +#define HFA384X_CMDCODE_DISABLE 0x02 +#define HFA384X_CMDCODE_ALLOC 0x0A +#define HFA384X_CMDCODE_TRANSMIT 0x0B +#define HFA384X_CMDCODE_INQUIRE 0x11 +#define HFA384X_CMDCODE_ACCESS 0x21 +#define HFA384X_CMDCODE_ACCESS_WRITE (0x21 | BIT(8)) +#define HFA384X_CMDCODE_DOWNLOAD 0x22 +#define HFA384X_CMDCODE_READMIF 0x30 +#define HFA384X_CMDCODE_WRITEMIF 0x31 +#define HFA384X_CMDCODE_TEST 0x38 + +#define HFA384X_CMDCODE_MASK 0x3F + +/* Test mode operations */ +#define HFA384X_TEST_CHANGE_CHANNEL 0x08 +#define HFA384X_TEST_MONITOR 0x0B +#define HFA384X_TEST_STOP 0x0F +#define HFA384X_TEST_CFG_BITS 0x15 +#define HFA384X_TEST_CFG_BIT_ALC BIT(3) + +#define HFA384X_CMD_BUSY BIT(15) + +#define HFA384X_CMD_TX_RECLAIM BIT(8) + +#define HFA384X_OFFSET_ERR BIT(14) +#define HFA384X_OFFSET_BUSY BIT(15) + + +/* ProgMode for download command */ +#define HFA384X_PROGMODE_DISABLE 0 +#define HFA384X_PROGMODE_ENABLE_VOLATILE 1 +#define HFA384X_PROGMODE_ENABLE_NON_VOLATILE 2 +#define HFA384X_PROGMODE_PROGRAM_NON_VOLATILE 3 + +#define HFA384X_AUX_MAGIC0 0xfe01 +#define HFA384X_AUX_MAGIC1 0xdc23 +#define HFA384X_AUX_MAGIC2 0xba45 + +#define HFA384X_AUX_PORT_DISABLED 0 +#define HFA384X_AUX_PORT_DISABLE BIT(14) +#define HFA384X_AUX_PORT_ENABLE BIT(15) +#define HFA384X_AUX_PORT_ENABLED (BIT(14) | BIT(15)) +#define HFA384X_AUX_PORT_MASK (BIT(14) | BIT(15)) + +#define PRISM2_PDA_SIZE 1024 + + +/* Events; EvStat, Interrupt mask (IntEn), and acknowledge bits (EvAck) */ +#define HFA384X_EV_TICK BIT(15) +#define HFA384X_EV_WTERR BIT(14) +#define HFA384X_EV_INFDROP BIT(13) +#ifdef PRISM2_PCI +#define HFA384X_EV_PCI_M1 BIT(9) +#define HFA384X_EV_PCI_M0 BIT(8) +#endif /* PRISM2_PCI */ +#define HFA384X_EV_INFO BIT(7) +#define HFA384X_EV_DTIM BIT(5) +#define HFA384X_EV_CMD BIT(4) +#define HFA384X_EV_ALLOC BIT(3) +#define HFA384X_EV_TXEXC BIT(2) +#define HFA384X_EV_TX BIT(1) +#define HFA384X_EV_RX BIT(0) + + +/* HFA384X Information frames */ +#define HFA384X_INFO_HANDOVERADDR 0xF000 /* AP f/w ? */ +#define HFA384X_INFO_HANDOVERDEAUTHADDR 0xF001 /* AP f/w 1.3.7 */ +#define HFA384X_INFO_COMMTALLIES 0xF100 +#define HFA384X_INFO_SCANRESULTS 0xF101 +#define HFA384X_INFO_CHANNELINFORESULTS 0xF102 /* AP f/w only */ +#define HFA384X_INFO_HOSTSCANRESULTS 0xF103 +#define HFA384X_INFO_LINKSTATUS 0xF200 +#define HFA384X_INFO_ASSOCSTATUS 0xF201 /* ? */ +#define HFA384X_INFO_AUTHREQ 0xF202 /* ? */ +#define HFA384X_INFO_PSUSERCNT 0xF203 /* ? */ +#define HFA384X_INFO_KEYIDCHANGED 0xF204 /* ? */ + +enum { HFA384X_LINKSTATUS_CONNECTED = 1, + HFA384X_LINKSTATUS_DISCONNECTED = 2, + HFA384X_LINKSTATUS_AP_CHANGE = 3, + HFA384X_LINKSTATUS_AP_OUT_OF_RANGE = 4, + HFA384X_LINKSTATUS_AP_IN_RANGE = 5, + HFA384X_LINKSTATUS_ASSOC_FAILED = 6 }; + +enum { HFA384X_PORTTYPE_BSS = 1, HFA384X_PORTTYPE_WDS = 2, + HFA384X_PORTTYPE_PSEUDO_IBSS = 3, HFA384X_PORTTYPE_IBSS = 0, + HFA384X_PORTTYPE_HOSTAP = 6 }; + +#define HFA384X_RATES_1MBPS BIT(0) +#define HFA384X_RATES_2MBPS BIT(1) +#define HFA384X_RATES_5MBPS BIT(2) +#define HFA384X_RATES_11MBPS BIT(3) + +#define HFA384X_ROAMING_FIRMWARE 1 +#define HFA384X_ROAMING_HOST 2 +#define HFA384X_ROAMING_DISABLED 3 + +#define HFA384X_WEPFLAGS_PRIVACYINVOKED BIT(0) +#define HFA384X_WEPFLAGS_EXCLUDEUNENCRYPTED BIT(1) +#define HFA384X_WEPFLAGS_HOSTENCRYPT BIT(4) +#define HFA384X_WEPFLAGS_HOSTDECRYPT BIT(7) + +#define HFA384X_RX_STATUS_MSGTYPE (BIT(15) | BIT(14) | BIT(13)) +#define HFA384X_RX_STATUS_PCF BIT(12) +#define HFA384X_RX_STATUS_MACPORT (BIT(10) | BIT(9) | BIT(8)) +#define HFA384X_RX_STATUS_UNDECR BIT(1) +#define HFA384X_RX_STATUS_FCSERR BIT(0) + +#define HFA384X_RX_STATUS_GET_MSGTYPE(s) \ +(((s) & HFA384X_RX_STATUS_MSGTYPE) >> 13) +#define HFA384X_RX_STATUS_GET_MACPORT(s) \ +(((s) & HFA384X_RX_STATUS_MACPORT) >> 8) + +enum { HFA384X_RX_MSGTYPE_NORMAL = 0, HFA384X_RX_MSGTYPE_RFC1042 = 1, + HFA384X_RX_MSGTYPE_BRIDGETUNNEL = 2, HFA384X_RX_MSGTYPE_MGMT = 4 }; + + +#define HFA384X_TX_CTRL_ALT_RTRY BIT(5) +#define HFA384X_TX_CTRL_802_11 BIT(3) +#define HFA384X_TX_CTRL_802_3 0 +#define HFA384X_TX_CTRL_TX_EX BIT(2) +#define HFA384X_TX_CTRL_TX_OK BIT(1) + +#define HFA384X_TX_STATUS_RETRYERR BIT(0) +#define HFA384X_TX_STATUS_AGEDERR BIT(1) +#define HFA384X_TX_STATUS_DISCON BIT(2) +#define HFA384X_TX_STATUS_FORMERR BIT(3) + +/* HFA3861/3863 (BBP) Control Registers */ +#define HFA386X_CR_TX_CONFIGURE 0x12 /* CR9 */ +#define HFA386X_CR_RX_CONFIGURE 0x14 /* CR10 */ +#define HFA386X_CR_A_D_TEST_MODES2 0x1A /* CR13 */ +#define HFA386X_CR_MANUAL_TX_POWER 0x3E /* CR31 */ +#define HFA386X_CR_MEASURED_TX_POWER 0x74 /* CR58 */ + + +#ifdef __KERNEL__ + +#define PRISM2_TXFID_COUNT 8 +#define PRISM2_DATA_MAXLEN 2304 +#define PRISM2_TXFID_LEN (PRISM2_DATA_MAXLEN + sizeof(struct hfa384x_tx_frame)) +#define PRISM2_TXFID_EMPTY 0xffff +#define PRISM2_TXFID_RESERVED 0xfffe +#define PRISM2_DUMMY_FID 0xffff +#define MAX_SSID_LEN 32 +#define MAX_NAME_LEN 32 /* this is assumed to be equal to MAX_SSID_LEN */ + +#define PRISM2_DUMP_RX_HDR BIT(0) +#define PRISM2_DUMP_TX_HDR BIT(1) +#define PRISM2_DUMP_TXEXC_HDR BIT(2) + +struct hostap_tx_callback_info { + u16 idx; + void (*func)(struct sk_buff *, int ok, void *); + void *data; + struct hostap_tx_callback_info *next; +}; + + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define PRISM2_FRAG_CACHE_LEN 4 + +struct prism2_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + + +struct prism2_crypt_data { + struct list_head list; /* delayed deletion list */ + struct hostap_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +struct hostap_cmd_queue { + struct list_head list; + wait_queue_head_t compl; + volatile enum { CMD_SLEEP, CMD_CALLBACK, CMD_COMPLETED } type; + void (*callback)(struct net_device *dev, void *context, u16 resp0, + u16 res); + void *context; + u16 cmd, param0, param1; + u16 resp0, res; + volatile int issued, issuing; + + atomic_t usecnt; + int del_req; +}; + +/* options for hw_shutdown */ +#define HOSTAP_HW_NO_DISABLE BIT(0) +#define HOSTAP_HW_ENABLE_CMDCOMPL BIT(1) + +typedef struct local_info local_info_t; + +struct prism2_helper_functions { + /* these functions are defined in hardware model specific files + * (hostap_{cs,plx,pci}.c */ + int (*card_present)(local_info_t *local); + void (*cor_sreset)(local_info_t *local); + int (*dev_open)(local_info_t *local); + int (*dev_close)(local_info_t *local); + void (*genesis_reset)(local_info_t *local, int hcr); + + /* the following functions are from hostap_hw.c, but they may have some + * hardware model specific code */ + + /* FIX: low-level commands like cmd might disappear at some point to + * make it easier to change them if needed (e.g., cmd would be replaced + * with write_mif/read_mif/testcmd/inquire); at least get_rid and + * set_rid might move to hostap_{cs,plx,pci}.c */ + int (*cmd)(struct net_device *dev, u16 cmd, u16 param0, u16 *param1, + u16 *resp0); + void (*read_regs)(struct net_device *dev, struct hfa384x_regs *regs); + int (*get_rid)(struct net_device *dev, u16 rid, void *buf, int len, + int exact_len); + int (*set_rid)(struct net_device *dev, u16 rid, void *buf, int len); + int (*hw_enable)(struct net_device *dev, int initial); + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); + int (*reset_port)(struct net_device *dev); + void (*schedule_reset)(local_info_t *local); + int (*download)(local_info_t *local, + struct prism2_download_param *param); + int (*tx)(struct sk_buff *skb, struct net_device *dev); + int (*set_tim)(struct net_device *dev, int aid, int set); + int (*read_aux)(struct net_device *dev, unsigned addr, int len, + u8 *buf); + + int need_tx_headroom; /* number of bytes of headroom needed before + * IEEE 802.11 header */ + enum { HOSTAP_HW_PCCARD, HOSTAP_HW_PLX, HOSTAP_HW_PCI } hw_type; +}; + + +struct prism2_download_data { + u32 dl_cmd; + u32 start_addr; + u32 num_areas; + struct prism2_download_data_area { + u32 addr; /* wlan card address */ + u32 len; + u8 *data; /* allocated data */ + } data[0]; +}; + + +#define HOSTAP_MAX_BSS_COUNT 64 +#define MAX_WPA_IE_LEN 64 + +struct hostap_bss_info { + struct list_head list; + unsigned long last_update; + unsigned int count; + u8 bssid[ETH_ALEN]; + u16 capab_info; + u8 ssid[32]; + size_t ssid_len; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; +}; + + +/* Per radio private Host AP data - shared by all net devices interfaces used + * by each radio (wlan#, wlan#ap, wlan#sta, WDS). + * ((struct hostap_interface *) netdev_priv(dev))->local points to this + * structure. */ +struct local_info { + struct module *hw_module; + int card_idx; + int dev_enabled; + int master_dev_auto_open; /* was master device opened automatically */ + int num_dev_open; /* number of open devices */ + struct net_device *dev; /* master radio device */ + struct net_device *ddev; /* main data device */ + struct list_head hostap_interfaces; /* Host AP interface list (contains + * struct hostap_interface entries) + */ + rwlock_t iface_lock; /* hostap_interfaces read lock; use write lock + * when removing entries from the list. + * TX and RX paths can use read lock. */ + spinlock_t cmdlock, baplock, lock; + struct semaphore rid_bap_sem; + u16 infofid; /* MAC buffer id for info frame */ + /* txfid, intransmitfid, next_txtid, and next_alloc are protected by + * txfidlock */ + spinlock_t txfidlock; + int txfid_len; /* length of allocated TX buffers */ + u16 txfid[PRISM2_TXFID_COUNT]; /* buffer IDs for TX frames */ + /* buffer IDs for intransmit frames or PRISM2_TXFID_EMPTY if + * corresponding txfid is free for next TX frame */ + u16 intransmitfid[PRISM2_TXFID_COUNT]; + int next_txfid; /* index to the next txfid to be checked for + * availability */ + int next_alloc; /* index to the next intransmitfid to be checked for + * allocation events */ + + /* bitfield for atomic bitops */ +#define HOSTAP_BITS_TRANSMIT 0 +#define HOSTAP_BITS_BAP_TASKLET 1 +#define HOSTAP_BITS_BAP_TASKLET2 2 + long bits; + + struct ap_data *ap; + + char essid[MAX_SSID_LEN + 1]; + char name[MAX_NAME_LEN + 1]; + int name_set; + u16 channel_mask; + struct comm_tallies_sums comm_tallies; + struct net_device_stats stats; + struct proc_dir_entry *proc; + int iw_mode; /* operating mode (IW_MODE_*) */ + int pseudo_adhoc; /* 0: IW_MODE_ADHOC is real 802.11 compliant IBSS + * 1: IW_MODE_ADHOC is "pseudo IBSS" */ + char bssid[ETH_ALEN]; + int channel; + int beacon_int; + int dtim_period; + int mtu; + int frame_dump; /* dump RX/TX frame headers, PRISM2_DUMP_ flags */ + int fw_tx_rate_control; + u16 tx_rate_control; + u16 basic_rates; + int hw_resetting; + int hw_ready; + int hw_reset_tries; /* how many times reset has been tried */ + int hw_downloading; + int shutdown; + int pri_only; + int no_pri; /* no PRI f/w present */ + int sram_type; /* 8 = x8 SRAM, 16 = x16 SRAM, -1 = unknown */ + + enum { + PRISM2_TXPOWER_AUTO = 0, PRISM2_TXPOWER_OFF, + PRISM2_TXPOWER_FIXED, PRISM2_TXPOWER_UNKNOWN + } txpower_type; + int txpower; /* if txpower_type == PRISM2_TXPOWER_FIXED */ + + /* command queue for hfa384x_cmd(); protected with cmdlock */ + struct list_head cmd_queue; + /* max_len for cmd_queue; in addition, cmd_callback can use two + * additional entries to prevent sleeping commands from stopping + * transmits */ +#define HOSTAP_CMD_QUEUE_MAX_LEN 16 + int cmd_queue_len; /* number of entries in cmd_queue */ + + /* if card timeout is detected in interrupt context, reset_queue is + * used to schedule card reseting to be done in user context */ + struct work_struct reset_queue; + + /* For scheduling a change of the promiscuous mode RID */ + int is_promisc; + struct work_struct set_multicast_list_queue; + + struct work_struct set_tim_queue; + struct list_head set_tim_list; + spinlock_t set_tim_lock; + + int wds_max_connections; + int wds_connections; +#define HOSTAP_WDS_BROADCAST_RA BIT(0) +#define HOSTAP_WDS_AP_CLIENT BIT(1) +#define HOSTAP_WDS_STANDARD_FRAME BIT(2) + u32 wds_type; + u16 tx_control; /* flags to be used in TX description */ + int manual_retry_count; /* -1 = use f/w default; otherwise retry count + * to be used with all frames */ + + struct iw_statistics wstats; + unsigned long scan_timestamp; /* Time started to scan */ + enum { + PRISM2_MONITOR_80211 = 0, PRISM2_MONITOR_PRISM = 1, + PRISM2_MONITOR_CAPHDR = 2 + } monitor_type; + int (*saved_eth_header_parse)(struct sk_buff *skb, + unsigned char *haddr); + int monitor_allow_fcserr; + + int hostapd; /* whether user space daemon, hostapd, is used for AP + * management */ + int hostapd_sta; /* whether hostapd is used with an extra STA interface + */ + struct net_device *apdev; + struct net_device_stats apdevstats; + + char assoc_ap_addr[ETH_ALEN]; + struct net_device *stadev; + struct net_device_stats stadevstats; + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + struct prism2_crypt_data *crypt[WEP_KEYS]; + int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ + struct timer_list crypt_deinit_timer; + struct list_head crypt_deinit_list; + + int open_wep; /* allow unencrypted frames */ + int host_encrypt; + int host_decrypt; + int privacy_invoked; /* force privacy invoked flag even if no keys are + * configured */ + int fw_encrypt_ok; /* whether firmware-based WEP encrypt is working + * in Host AP mode (STA f/w 1.4.9 or newer) */ + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + struct prism2_frag_entry frag_cache[PRISM2_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + + int ieee_802_1x; /* is IEEE 802.1X used */ + + int antsel_tx, antsel_rx; + int rts_threshold; /* dot11RTSThreshold */ + int fragm_threshold; /* dot11FragmentationThreshold */ + int auth_algs; /* PRISM2_AUTH_ flags */ + + int enh_sec; /* cnfEnhSecurity options (broadcast SSID hide/ignore) */ + int tallies32; /* 32-bit tallies in use */ + + struct prism2_helper_functions *func; + + int bus_master_threshold_tx; + int bus_master_threshold_rx; + u8 *bus_m1_buf; + + u8 *pda; + int fw_ap; +#define PRISM2_FW_VER(major, minor, variant) \ +(((major) << 16) | ((minor) << 8) | variant) + u32 sta_fw_ver; + + /* Tasklets for handling hardware IRQ related operations outside hw IRQ + * handler */ + struct tasklet_struct bap_tasklet; + + struct tasklet_struct info_tasklet; + struct sk_buff_head info_list; /* info frames as skb's for + * info_tasklet */ + + struct hostap_tx_callback_info *tx_callback; /* registered TX callbacks + */ + + struct tasklet_struct rx_tasklet; + struct sk_buff_head rx_list; + + struct tasklet_struct sta_tx_exc_tasklet; + struct sk_buff_head sta_tx_exc_list; + + int host_roaming; + unsigned long last_join_time; /* time of last JoinRequest */ + struct hfa384x_scan_result *last_scan_results; + int last_scan_results_count; + struct hfa384x_hostscan_result *last_hostscan_results; + int last_hostscan_results_count; + enum { PRISM2_SCAN, PRISM2_HOSTSCAN } last_scan_type; + struct work_struct info_queue; + long pending_info; /* bit field of pending info_queue items */ +#define PRISM2_INFO_PENDING_LINKSTATUS 0 +#define PRISM2_INFO_PENDING_SCANRESULTS 1 + int prev_link_status; /* previous received LinkStatus info */ + u8 preferred_ap[6]; /* use this AP if possible */ + +#ifdef PRISM2_CALLBACK + void *callback_data; /* Can be used in callbacks; e.g., allocate + * on enable event and free on disable event. + * Host AP driver code does not touch this. */ +#endif /* PRISM2_CALLBACK */ + + wait_queue_head_t hostscan_wq; + + /* Passive scan in Host AP mode */ + struct timer_list passive_scan_timer; + int passive_scan_interval; /* in seconds, 0 = disabled */ + int passive_scan_channel; + enum { PASSIVE_SCAN_WAIT, PASSIVE_SCAN_LISTEN } passive_scan_state; + + struct timer_list tick_timer; + unsigned long last_tick_timer; + unsigned int sw_tick_stuck; + + /* commsQuality / dBmCommsQuality data from periodic polling; only + * valid for Managed and Ad-hoc modes */ + unsigned long last_comms_qual_update; + int comms_qual; /* in some odd unit.. */ + int avg_signal; /* in dB (note: negative) */ + int avg_noise; /* in dB (note: negative) */ + struct work_struct comms_qual_update; + + /* RSSI to dBm adjustment (for RX descriptor fields) */ + int rssi_to_dBm; /* substract from RSSI to get approximate dBm value */ + + /* BSS list / protected by local->lock */ + struct list_head bss_list; + int num_bss_info; + int wpa; /* WPA support enabled */ + int tkip_countermeasures; + int drop_unencrypted; + /* Generic IEEE 802.11 info element to be added to + * ProbeResp/Beacon/(Re)AssocReq */ + u8 *generic_elem; + size_t generic_elem_len; + +#ifdef PRISM2_DOWNLOAD_SUPPORT + /* Persistent volatile download data */ + struct prism2_download_data *dl_pri; + struct prism2_download_data *dl_sec; +#endif /* PRISM2_DOWNLOAD_SUPPORT */ + +#ifdef PRISM2_IO_DEBUG +#define PRISM2_IO_DEBUG_SIZE 10000 + u32 io_debug[PRISM2_IO_DEBUG_SIZE]; + int io_debug_head; + int io_debug_enabled; +#endif /* PRISM2_IO_DEBUG */ + + /* struct local_info is used also in hostap.o that does not define + * any PRISM2_{PCCARD,PLX,PCI}. Make sure that the hardware version + * specific fields are in the end of the struct (these could also be + * moved to void *priv or something like that). */ +#ifdef PRISM2_PCCARD + dev_node_t node; + dev_link_t *link; +#endif /* PRISM2_PCCARD */ + +#ifdef PRISM2_PLX + void __iomem *attr_mem; + unsigned int cor_offset; +#endif /* PRISM2_PLX */ + +#ifdef PRISM2_PCI + void __iomem *mem_start; +#ifdef PRISM2_BUS_MASTER + /* bus master for BAP0 (TX) */ + int bus_m0_tx_idx; + u8 *bus_m0_buf; + + /* bus master for BAP1 (RX) */ + struct sk_buff *rx_skb; +#endif /* PRISM2_BUS_MASTER */ +#endif /* PRISM2_PCI */ + + /* NOTE! Do not add common entries here after hardware version + * specific blocks. */ +}; + + +/* Per interface private Host AP data + * Allocated for each net device that Host AP uses (wlan#, wlan#ap, wlan#sta, + * WDS) and netdev_priv(dev) points to this structure. */ +struct hostap_interface { + struct list_head list; /* list entry in Host AP interface list */ + struct net_device *dev; /* pointer to this device */ + struct local_info *local; /* pointer to shared private data */ + struct net_device_stats stats; + struct iw_spy_data spy_data; /* iwspy support */ + struct iw_public_data wireless_data; + + enum { + HOSTAP_INTERFACE_MASTER, + HOSTAP_INTERFACE_MAIN, + HOSTAP_INTERFACE_AP, + HOSTAP_INTERFACE_STA, + HOSTAP_INTERFACE_WDS, + } type; + + union { + struct hostap_interface_wds { + u8 remote_addr[ETH_ALEN]; + } wds; + } u; +}; + + +#define HOSTAP_SKB_TX_DATA_MAGIC 0xf08a36a2 + +/* TX meta data - stored in skb->cb buffer, so this must be not increase over + * 48-byte limit */ +struct hostap_skb_tx_data { + unsigned int magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ + int rate; /* transmit rate */ + struct hostap_interface *iface; + unsigned long jiffies; /* queueing timestamp */ + int wds; + unsigned short ethertype; + int tx_cb_idx; +}; + + +#ifndef PRISM2_NO_DEBUG + +#define DEBUG_FID BIT(0) +#define DEBUG_PS BIT(1) +#define DEBUG_FLOW BIT(2) +#define DEBUG_AP BIT(3) +#define DEBUG_HW BIT(4) +#define DEBUG_EXTRA BIT(5) +#define DEBUG_EXTRA2 BIT(6) +#define DEBUG_PS2 BIT(7) +#define DEBUG_MASK (DEBUG_PS | DEBUG_AP | DEBUG_HW | DEBUG_EXTRA) +#define PDEBUG(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(KERN_DEBUG args); } while (0) +#define PDEBUG2(n, args...) \ +do { if ((n) & DEBUG_MASK) printk(args); } while (0) + +#else /* PRISM2_NO_DEBUG */ + +#define PDEBUG(n, args...) +#define PDEBUG2(n, args...) + +#endif /* PRISM2_NO_DEBUG */ + +enum { BAP0 = 0, BAP1 = 1 }; + +#define PRISM2_IO_DEBUG_CMD_INB 0 +#define PRISM2_IO_DEBUG_CMD_INW 1 +#define PRISM2_IO_DEBUG_CMD_INSW 2 +#define PRISM2_IO_DEBUG_CMD_OUTB 3 +#define PRISM2_IO_DEBUG_CMD_OUTW 4 +#define PRISM2_IO_DEBUG_CMD_OUTSW 5 +#define PRISM2_IO_DEBUG_CMD_ERROR 6 +#define PRISM2_IO_DEBUG_CMD_INTERRUPT 7 + +#ifdef PRISM2_IO_DEBUG + +#define PRISM2_IO_DEBUG_ENTRY(cmd, reg, value) \ +(((cmd) << 24) | ((reg) << 16) | value) + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + + if (!local->io_debug_enabled) + return; + + local->io_debug[local->io_debug_head] = jiffies & 0xffffffff; + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; + local->io_debug[local->io_debug_head] = + PRISM2_IO_DEBUG_ENTRY(cmd, reg, value); + if (++local->io_debug_head >= PRISM2_IO_DEBUG_SIZE) + local->io_debug_head = 0; +} + + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ + struct hostap_interface *iface = netdev_priv(dev); + local_info_t *local = iface->local; + unsigned long flags; + + if (!local->io_debug_enabled) + return; + + spin_lock_irqsave(&local->lock, flags); + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_ERROR, 0, err); + if (local->io_debug_enabled == 1) { + local->io_debug_enabled = 0; + printk(KERN_DEBUG "%s: I/O debug stopped\n", dev->name); + } + spin_unlock_irqrestore(&local->lock, flags); +} + +#else /* PRISM2_IO_DEBUG */ + +static inline void prism2_io_debug_add(struct net_device *dev, int cmd, + int reg, int value) +{ +} + +static inline void prism2_io_debug_error(struct net_device *dev, int err) +{ +} + +#endif /* PRISM2_IO_DEBUG */ + + +#ifdef PRISM2_CALLBACK +enum { + /* Called when card is enabled */ + PRISM2_CALLBACK_ENABLE, + + /* Called when card is disabled */ + PRISM2_CALLBACK_DISABLE, + + /* Called when RX/TX starts/ends */ + PRISM2_CALLBACK_RX_START, PRISM2_CALLBACK_RX_END, + PRISM2_CALLBACK_TX_START, PRISM2_CALLBACK_TX_END +}; +void prism2_callback(local_info_t *local, int event); +#else /* PRISM2_CALLBACK */ +#define prism2_callback(d, e) do { } while (0) +#endif /* PRISM2_CALLBACK */ + +#endif /* __KERNEL__ */ + +#endif /* HOSTAP_WLAN_H */ diff -Nru a/drivers/net/wireless/ieee802_11.h b/drivers/net/wireless/ieee802_11.h --- a/drivers/net/wireless/ieee802_11.h 2005-03-15 23:46:53 -08:00 +++ /dev/null Wed Dec 31 16:00:00 196900 @@ -1,78 +0,0 @@ -#ifndef _IEEE802_11_H -#define _IEEE802_11_H - -#define IEEE802_11_DATA_LEN 2304 -/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section - 6.2.1.1.2. - - The figure in section 7.1.2 suggests a body size of up to 2312 - bytes is allowed, which is a bit confusing, I suspect this - represents the 2304 bytes of real data, plus a possible 8 bytes of - WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ - - -#define IEEE802_11_HLEN 30 -#define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN) - -struct ieee802_11_hdr { - u16 frame_ctl; - u16 duration_id; - u8 addr1[ETH_ALEN]; - u8 addr2[ETH_ALEN]; - u8 addr3[ETH_ALEN]; - u16 seq_ctl; - u8 addr4[ETH_ALEN]; -} __attribute__ ((packed)); - -/* Frame control field constants */ -#define IEEE802_11_FCTL_VERS 0x0002 -#define IEEE802_11_FCTL_FTYPE 0x000c -#define IEEE802_11_FCTL_STYPE 0x00f0 -#define IEEE802_11_FCTL_TODS 0x0100 -#define IEEE802_11_FCTL_FROMDS 0x0200 -#define IEEE802_11_FCTL_MOREFRAGS 0x0400 -#define IEEE802_11_FCTL_RETRY 0x0800 -#define IEEE802_11_FCTL_PM 0x1000 -#define IEEE802_11_FCTL_MOREDATA 0x2000 -#define IEEE802_11_FCTL_WEP 0x4000 -#define IEEE802_11_FCTL_ORDER 0x8000 - -#define IEEE802_11_FTYPE_MGMT 0x0000 -#define IEEE802_11_FTYPE_CTL 0x0004 -#define IEEE802_11_FTYPE_DATA 0x0008 - -/* management */ -#define IEEE802_11_STYPE_ASSOC_REQ 0x0000 -#define IEEE802_11_STYPE_ASSOC_RESP 0x0010 -#define IEEE802_11_STYPE_REASSOC_REQ 0x0020 -#define IEEE802_11_STYPE_REASSOC_RESP 0x0030 -#define IEEE802_11_STYPE_PROBE_REQ 0x0040 -#define IEEE802_11_STYPE_PROBE_RESP 0x0050 -#define IEEE802_11_STYPE_BEACON 0x0080 -#define IEEE802_11_STYPE_ATIM 0x0090 -#define IEEE802_11_STYPE_DISASSOC 0x00A0 -#define IEEE802_11_STYPE_AUTH 0x00B0 -#define IEEE802_11_STYPE_DEAUTH 0x00C0 - -/* control */ -#define IEEE802_11_STYPE_PSPOLL 0x00A0 -#define IEEE802_11_STYPE_RTS 0x00B0 -#define IEEE802_11_STYPE_CTS 0x00C0 -#define IEEE802_11_STYPE_ACK 0x00D0 -#define IEEE802_11_STYPE_CFEND 0x00E0 -#define IEEE802_11_STYPE_CFENDACK 0x00F0 - -/* data */ -#define IEEE802_11_STYPE_DATA 0x0000 -#define IEEE802_11_STYPE_DATA_CFACK 0x0010 -#define IEEE802_11_STYPE_DATA_CFPOLL 0x0020 -#define IEEE802_11_STYPE_DATA_CFACKPOLL 0x0030 -#define IEEE802_11_STYPE_NULLFUNC 0x0040 -#define IEEE802_11_STYPE_CFACK 0x0050 -#define IEEE802_11_STYPE_CFPOLL 0x0060 -#define IEEE802_11_STYPE_CFACKPOLL 0x0070 - -#define IEEE802_11_SCTL_FRAG 0x000F -#define IEEE802_11_SCTL_SEQ 0xFFF0 - -#endif /* _IEEE802_11_H */ diff -Nru a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c --- a/drivers/net/wireless/orinoco.c 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/wireless/orinoco.c 2005-03-15 23:46:53 -08:00 @@ -464,6 +464,8 @@ #include #include +#include + #include #include #include @@ -471,7 +473,6 @@ #include "hermes.h" #include "hermes_rid.h" #include "orinoco.h" -#include "ieee802_11.h" /********************************************************************/ /* Module information */ @@ -509,7 +510,7 @@ /********************************************************************/ #define ORINOCO_MIN_MTU 256 -#define ORINOCO_MAX_MTU (IEEE802_11_DATA_LEN - ENCAPS_OVERHEAD) +#define ORINOCO_MAX_MTU (IEEE80211_DATA_LEN - ENCAPS_OVERHEAD) #define SYMBOL_MAX_VER_LEN (14) #define USER_BAP 0 @@ -760,7 +761,7 @@ if ( (new_mtu < ORINOCO_MIN_MTU) || (new_mtu > ORINOCO_MAX_MTU) ) return -EINVAL; - if ( (new_mtu + ENCAPS_OVERHEAD + IEEE802_11_HLEN) > + if ( (new_mtu + ENCAPS_OVERHEAD + IEEE80211_HLEN) > (priv->nicbuf_size - ETH_HLEN) ) return -EINVAL; @@ -1104,7 +1105,7 @@ stats->rx_dropped++; goto drop; } - if (length > IEEE802_11_DATA_LEN) { + if (length > IEEE80211_DATA_LEN) { printk(KERN_WARNING "%s: Oversized frame received (%d bytes)\n", dev->name, length); stats->rx_length_errors++; @@ -2264,7 +2265,7 @@ /* No need to lock, the hw_unavailable flag is already set in * alloc_orinocodev() */ - priv->nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; + priv->nicbuf_size = IEEE80211_FRAME_LEN + ETH_HLEN; /* Initialize the firmware */ err = hermes_init(hw); diff -Nru a/drivers/net/wireless/wl3501.h b/drivers/net/wireless/wl3501.h --- a/drivers/net/wireless/wl3501.h 2005-03-15 23:46:53 -08:00 +++ b/drivers/net/wireless/wl3501.h 2005-03-15 23:46:53 -08:00 @@ -2,7 +2,7 @@ #define __WL3501_H__ #include -#include "ieee802_11.h" +#include /* define for WLA 2.0 */ #define WL3501_BLKSZ 256 @@ -548,7 +548,7 @@ struct wl3501_80211_tx_hdr { struct wl3501_80211_tx_plcp_hdr pclp_hdr; - struct ieee802_11_hdr mac_hdr; + struct ieee80211_hdr mac_hdr; } __attribute__ ((packed)); /* diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h --- a/include/linux/pci_ids.h 2005-03-15 23:46:53 -08:00 +++ b/include/linux/pci_ids.h 2005-03-15 23:46:53 -08:00 @@ -119,6 +119,9 @@ /* Vendors and devices. Sort key: vendor first, device next. */ +#define PCI_VENDOR_ID_TTTECH 0x0357 +#define PCI_DEVICE_ID_TTTECH_MC312 0x000A + #define PCI_VENDOR_ID_DYNALINK 0x0675 #define PCI_DEVICE_ID_DYNALINK_IS64PH 0x1702 diff -Nru a/include/net/ieee80211.h b/include/net/ieee80211.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/net/ieee80211.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,887 @@ +/* + * Merged with mainline ieee80211.h in Aug 2004. Original ieee802_11 + * remains copyright by the original authors + * + * Portions of the merged code are based on Host AP (software wireless + * LAN access point) driver for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * Copyright (c) 2004, Intel Corporation + * + * 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. See README and COPYING for + * more details. + */ +#ifndef IEEE80211_H +#define IEEE80211_H +#include /* ETH_ALEN */ +#include /* ARRAY_SIZE */ + +#if WIRELESS_EXT < 17 +#define IW_QUAL_QUAL_INVALID 0x10 +#define IW_QUAL_LEVEL_INVALID 0x20 +#define IW_QUAL_NOISE_INVALID 0x40 +#define IW_QUAL_QUAL_UPDATED 0x1 +#define IW_QUAL_LEVEL_UPDATED 0x2 +#define IW_QUAL_NOISE_UPDATED 0x4 +#endif + +#define IEEE80211_DATA_LEN 2304 +/* Maximum size for the MA-UNITDATA primitive, 802.11 standard section + 6.2.1.1.2. + + The figure in section 7.1.2 suggests a body size of up to 2312 + bytes is allowed, which is a bit confusing, I suspect this + represents the 2304 bytes of real data, plus a possible 8 bytes of + WEP IV and ICV. (this interpretation suggested by Ramiro Barreiro) */ + + +#define IEEE80211_HLEN 30 +#define IEEE80211_FRAME_LEN (IEEE80211_DATA_LEN + IEEE80211_HLEN) + +struct ieee80211_hdr { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; + u8 addr4[ETH_ALEN]; +} __attribute__ ((packed)); + +struct ieee80211_hdr_3addr { + u16 frame_ctl; + u16 duration_id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; + u8 addr3[ETH_ALEN]; + u16 seq_ctl; +} __attribute__ ((packed)); + +enum eap_type { + EAP_PACKET = 0, + EAPOL_START, + EAPOL_LOGOFF, + EAPOL_KEY, + EAPOL_ENCAP_ASF_ALERT +}; + +static const char *eap_types[] = { + [EAP_PACKET] = "EAP-Packet", + [EAPOL_START] = "EAPOL-Start", + [EAPOL_LOGOFF] = "EAPOL-Logoff", + [EAPOL_KEY] = "EAPOL-Key", + [EAPOL_ENCAP_ASF_ALERT] = "EAPOL-Encap-ASF-Alert" +}; + +static inline const char *eap_get_type(int type) +{ + return (type >= ARRAY_SIZE(eap_types)) ? "Unknown" : eap_types[type]; +} + +struct eapol { + u8 snap[6]; + u16 ethertype; + u8 version; + u8 type; + u16 length; +} __attribute__ ((packed)); + +#define IEEE80211_3ADDR_LEN 24 +#define IEEE80211_4ADDR_LEN 30 +#define IEEE80211_FCS_LEN 4 + +#define MIN_FRAG_THRESHOLD 256U +#define MAX_FRAG_THRESHOLD 2346U + +/* Frame control field constants */ +#define IEEE80211_FCTL_VERS 0x0002 +#define IEEE80211_FCTL_FTYPE 0x000c +#define IEEE80211_FCTL_STYPE 0x00f0 +#define IEEE80211_FCTL_TODS 0x0100 +#define IEEE80211_FCTL_FROMDS 0x0200 +#define IEEE80211_FCTL_MOREFRAGS 0x0400 +#define IEEE80211_FCTL_RETRY 0x0800 +#define IEEE80211_FCTL_PM 0x1000 +#define IEEE80211_FCTL_MOREDATA 0x2000 +#define IEEE80211_FCTL_WEP 0x4000 +#define IEEE80211_FCTL_ORDER 0x8000 + +#define IEEE80211_FTYPE_MGMT 0x0000 +#define IEEE80211_FTYPE_CTL 0x0004 +#define IEEE80211_FTYPE_DATA 0x0008 + +/* management */ +#define IEEE80211_STYPE_ASSOC_REQ 0x0000 +#define IEEE80211_STYPE_ASSOC_RESP 0x0010 +#define IEEE80211_STYPE_REASSOC_REQ 0x0020 +#define IEEE80211_STYPE_REASSOC_RESP 0x0030 +#define IEEE80211_STYPE_PROBE_REQ 0x0040 +#define IEEE80211_STYPE_PROBE_RESP 0x0050 +#define IEEE80211_STYPE_BEACON 0x0080 +#define IEEE80211_STYPE_ATIM 0x0090 +#define IEEE80211_STYPE_DISASSOC 0x00A0 +#define IEEE80211_STYPE_AUTH 0x00B0 +#define IEEE80211_STYPE_DEAUTH 0x00C0 + +/* control */ +#define IEEE80211_STYPE_PSPOLL 0x00A0 +#define IEEE80211_STYPE_RTS 0x00B0 +#define IEEE80211_STYPE_CTS 0x00C0 +#define IEEE80211_STYPE_ACK 0x00D0 +#define IEEE80211_STYPE_CFEND 0x00E0 +#define IEEE80211_STYPE_CFENDACK 0x00F0 + +/* data */ +#define IEEE80211_STYPE_DATA 0x0000 +#define IEEE80211_STYPE_DATA_CFACK 0x0010 +#define IEEE80211_STYPE_DATA_CFPOLL 0x0020 +#define IEEE80211_STYPE_DATA_CFACKPOLL 0x0030 +#define IEEE80211_STYPE_NULLFUNC 0x0040 +#define IEEE80211_STYPE_CFACK 0x0050 +#define IEEE80211_STYPE_CFPOLL 0x0060 +#define IEEE80211_STYPE_CFACKPOLL 0x0070 + +#define IEEE80211_SCTL_FRAG 0x000F +#define IEEE80211_SCTL_SEQ 0xFFF0 + + +/* debug macros */ + +#ifdef CONFIG_IEEE80211_DEBUG +extern u32 ieee80211_debug_level; +#define IEEE80211_DEBUG(level, fmt, args...) \ +do { if (ieee80211_debug_level & (level)) \ + printk(KERN_DEBUG "ieee80211: %c %s " fmt, \ + in_interrupt() ? 'I' : 'U', __FUNCTION__ , ## args); } while (0) +#else +#define IEEE80211_DEBUG(level, fmt, args...) do {} while (0) +#endif /* CONFIG_IEEE80211_DEBUG */ + +/* + * To use the debug system; + * + * If you are defining a new debug classification, simply add it to the #define + * list here in the form of: + * + * #define IEEE80211_DL_xxxx VALUE + * + * shifting value to the left one bit from the previous entry. xxxx should be + * the name of the classification (for example, WEP) + * + * You then need to either add a IEEE80211_xxxx_DEBUG() macro definition for your + * classification, or use IEEE80211_DEBUG(IEEE80211_DL_xxxx, ...) whenever you want + * to send output to that classification. + * + * To add your debug level to the list of levels seen when you perform + * + * % cat /proc/net/ipw/debug_level + * + * you simply need to add your entry to the ipw_debug_levels array. + * + * If you do not see debug_level in /proc/net/ipw then you do not have + * CONFIG_IEEE80211_DEBUG defined in your kernel configuration + * + */ + +#define IEEE80211_DL_INFO (1<<0) +#define IEEE80211_DL_WX (1<<1) +#define IEEE80211_DL_SCAN (1<<2) +#define IEEE80211_DL_STATE (1<<3) +#define IEEE80211_DL_MGMT (1<<4) +#define IEEE80211_DL_FRAG (1<<5) +#define IEEE80211_DL_EAP (1<<6) +#define IEEE80211_DL_DROP (1<<7) + +#define IEEE80211_DL_TX (1<<8) +#define IEEE80211_DL_RX (1<<9) + +#define IEEE80211_ERROR(f, a...) printk(KERN_ERR "ieee80211: " f, ## a) +#define IEEE80211_WARNING(f, a...) printk(KERN_WARNING "ieee80211: " f, ## a) +#define IEEE80211_DEBUG_INFO(f, a...) IEEE80211_DEBUG(IEEE80211_DL_INFO, f, ## a) + +#define IEEE80211_DEBUG_WX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_WX, f, ## a) +#define IEEE80211_DEBUG_SCAN(f, a...) IEEE80211_DEBUG(IEEE80211_DL_SCAN, f, ## a) +#define IEEE80211_DEBUG_STATE(f, a...) IEEE80211_DEBUG(IEEE80211_DL_STATE, f, ## a) +#define IEEE80211_DEBUG_MGMT(f, a...) IEEE80211_DEBUG(IEEE80211_DL_MGMT, f, ## a) +#define IEEE80211_DEBUG_FRAG(f, a...) IEEE80211_DEBUG(IEEE80211_DL_FRAG, f, ## a) +#define IEEE80211_DEBUG_EAP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_EAP, f, ## a) +#define IEEE80211_DEBUG_DROP(f, a...) IEEE80211_DEBUG(IEEE80211_DL_DROP, f, ## a) +#define IEEE80211_DEBUG_TX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_TX, f, ## a) +#define IEEE80211_DEBUG_RX(f, a...) IEEE80211_DEBUG(IEEE80211_DL_RX, f, ## a) +#include +#include +#include /* ARPHRD_ETHER */ + +#ifndef WIRELESS_SPY +#define WIRELESS_SPY // enable iwspy support +#endif +#include // new driver API + +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW (ETH_P_ECONET + 1) +#endif + +/* IEEE 802.11 defines */ + +#define P80211_OUI_LEN 3 + +struct ieee80211_snap_hdr { + + u8 dsap; /* always 0xAA */ + u8 ssap; /* always 0xAA */ + u8 ctrl; /* always 0x03 */ + u8 oui[P80211_OUI_LEN]; /* organizational universal id */ + +} __attribute__ ((packed)); + +#define SNAP_SIZE sizeof(struct ieee80211_snap_hdr) + +#define WLAN_FC_GET_TYPE(fc) ((fc) & IEEE80211_FCTL_FTYPE) +#define WLAN_FC_GET_STYPE(fc) ((fc) & IEEE80211_FCTL_STYPE) + +#define WLAN_GET_SEQ_FRAG(seq) ((seq) & IEEE80211_SCTL_FRAG) +#define WLAN_GET_SEQ_SEQ(seq) ((seq) & IEEE80211_SCTL_SEQ) + +/* Authentication algorithms */ +#define WLAN_AUTH_OPEN 0 +#define WLAN_AUTH_SHARED_KEY 1 + +#define WLAN_AUTH_CHALLENGE_LEN 128 + +#define WLAN_CAPABILITY_BSS (1<<0) +#define WLAN_CAPABILITY_IBSS (1<<1) +#define WLAN_CAPABILITY_CF_POLLABLE (1<<2) +#define WLAN_CAPABILITY_CF_POLL_REQUEST (1<<3) +#define WLAN_CAPABILITY_PRIVACY (1<<4) +#define WLAN_CAPABILITY_SHORT_PREAMBLE (1<<5) +#define WLAN_CAPABILITY_PBCC (1<<6) +#define WLAN_CAPABILITY_CHANNEL_AGILITY (1<<7) + +/* Status codes */ +#define WLAN_STATUS_SUCCESS 0 +#define WLAN_STATUS_UNSPECIFIED_FAILURE 1 +#define WLAN_STATUS_CAPS_UNSUPPORTED 10 +#define WLAN_STATUS_REASSOC_NO_ASSOC 11 +#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12 +#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13 +#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14 +#define WLAN_STATUS_CHALLENGE_FAIL 15 +#define WLAN_STATUS_AUTH_TIMEOUT 16 +#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17 +#define WLAN_STATUS_ASSOC_DENIED_RATES 18 +/* 802.11b */ +#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19 +#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20 +#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21 + +/* Reason codes */ +#define WLAN_REASON_UNSPECIFIED 1 +#define WLAN_REASON_PREV_AUTH_NOT_VALID 2 +#define WLAN_REASON_DEAUTH_LEAVING 3 +#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4 +#define WLAN_REASON_DISASSOC_AP_BUSY 5 +#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6 +#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7 +#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8 +#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9 + + +/* Information Element IDs */ +#define WLAN_EID_SSID 0 +#define WLAN_EID_SUPP_RATES 1 +#define WLAN_EID_FH_PARAMS 2 +#define WLAN_EID_DS_PARAMS 3 +#define WLAN_EID_CF_PARAMS 4 +#define WLAN_EID_TIM 5 +#define WLAN_EID_IBSS_PARAMS 6 +#define WLAN_EID_CHALLENGE 16 +#define WLAN_EID_RSN 48 +#define WLAN_EID_GENERIC 221 + +#define IEEE80211_MGMT_HDR_LEN 24 +#define IEEE80211_DATA_HDR3_LEN 24 +#define IEEE80211_DATA_HDR4_LEN 30 + + +#define IEEE80211_STATMASK_SIGNAL (1<<0) +#define IEEE80211_STATMASK_RSSI (1<<1) +#define IEEE80211_STATMASK_NOISE (1<<2) +#define IEEE80211_STATMASK_RATE (1<<3) +#define IEEE80211_STATMASK_WEMASK 0x7 + + +#define IEEE80211_CCK_MODULATION (1<<0) +#define IEEE80211_OFDM_MODULATION (1<<1) + +#define IEEE80211_24GHZ_BAND (1<<0) +#define IEEE80211_52GHZ_BAND (1<<1) + +#define IEEE80211_CCK_RATE_1MB 0x02 +#define IEEE80211_CCK_RATE_2MB 0x04 +#define IEEE80211_CCK_RATE_5MB 0x0B +#define IEEE80211_CCK_RATE_11MB 0x16 +#define IEEE80211_OFDM_RATE_6MB 0x0C +#define IEEE80211_OFDM_RATE_9MB 0x12 +#define IEEE80211_OFDM_RATE_12MB 0x18 +#define IEEE80211_OFDM_RATE_18MB 0x24 +#define IEEE80211_OFDM_RATE_24MB 0x30 +#define IEEE80211_OFDM_RATE_36MB 0x48 +#define IEEE80211_OFDM_RATE_48MB 0x60 +#define IEEE80211_OFDM_RATE_54MB 0x6C +#define IEEE80211_BASIC_RATE_MASK 0x80 + +#define IEEE80211_CCK_RATE_1MB_MASK (1<<0) +#define IEEE80211_CCK_RATE_2MB_MASK (1<<1) +#define IEEE80211_CCK_RATE_5MB_MASK (1<<2) +#define IEEE80211_CCK_RATE_11MB_MASK (1<<3) +#define IEEE80211_OFDM_RATE_6MB_MASK (1<<4) +#define IEEE80211_OFDM_RATE_9MB_MASK (1<<5) +#define IEEE80211_OFDM_RATE_12MB_MASK (1<<6) +#define IEEE80211_OFDM_RATE_18MB_MASK (1<<7) +#define IEEE80211_OFDM_RATE_24MB_MASK (1<<8) +#define IEEE80211_OFDM_RATE_36MB_MASK (1<<9) +#define IEEE80211_OFDM_RATE_48MB_MASK (1<<10) +#define IEEE80211_OFDM_RATE_54MB_MASK (1<<11) + +#define IEEE80211_CCK_RATES_MASK 0x0000000F +#define IEEE80211_CCK_BASIC_RATES_MASK (IEEE80211_CCK_RATE_1MB_MASK | \ + IEEE80211_CCK_RATE_2MB_MASK) +#define IEEE80211_CCK_DEFAULT_RATES_MASK (IEEE80211_CCK_BASIC_RATES_MASK | \ + IEEE80211_CCK_RATE_5MB_MASK | \ + IEEE80211_CCK_RATE_11MB_MASK) + +#define IEEE80211_OFDM_RATES_MASK 0x00000FF0 +#define IEEE80211_OFDM_BASIC_RATES_MASK (IEEE80211_OFDM_RATE_6MB_MASK | \ + IEEE80211_OFDM_RATE_12MB_MASK | \ + IEEE80211_OFDM_RATE_24MB_MASK) +#define IEEE80211_OFDM_DEFAULT_RATES_MASK (IEEE80211_OFDM_BASIC_RATES_MASK | \ + IEEE80211_OFDM_RATE_9MB_MASK | \ + IEEE80211_OFDM_RATE_18MB_MASK | \ + IEEE80211_OFDM_RATE_36MB_MASK | \ + IEEE80211_OFDM_RATE_48MB_MASK | \ + IEEE80211_OFDM_RATE_54MB_MASK) +#define IEEE80211_DEFAULT_RATES_MASK (IEEE80211_OFDM_DEFAULT_RATES_MASK | \ + IEEE80211_CCK_DEFAULT_RATES_MASK) + +#define IEEE80211_NUM_OFDM_RATES 8 +#define IEEE80211_NUM_CCK_RATES 4 +#define IEEE80211_OFDM_SHIFT_MASK_A 4 + + + + +/* NOTE: This data is for statistical purposes; not all hardware provides this + * information for frames received. Not setting these will not cause + * any adverse affects. */ +struct ieee80211_rx_stats { + u32 mac_time; + s8 rssi; + u8 signal; + u8 noise; + u16 rate; /* in 100 kbps */ + u8 received_channel; + u8 control; + u8 mask; + u8 freq; + u16 len; +}; + +/* IEEE 802.11 requires that STA supports concurrent reception of at least + * three fragmented frames. This define can be increased to support more + * concurrent frames, but it should be noted that each entry can consume about + * 2 kB of RAM and increasing cache size will slow down frame reassembly. */ +#define IEEE80211_FRAG_CACHE_LEN 4 + +struct ieee80211_frag_entry { + unsigned long first_frag_time; + unsigned int seq; + unsigned int last_frag; + struct sk_buff *skb; + u8 src_addr[ETH_ALEN]; + u8 dst_addr[ETH_ALEN]; +}; + +struct ieee80211_stats { + unsigned int tx_unicast_frames; + unsigned int tx_multicast_frames; + unsigned int tx_fragments; + unsigned int tx_unicast_octets; + unsigned int tx_multicast_octets; + unsigned int tx_deferred_transmissions; + unsigned int tx_single_retry_frames; + unsigned int tx_multiple_retry_frames; + unsigned int tx_retry_limit_exceeded; + unsigned int tx_discards; + unsigned int rx_unicast_frames; + unsigned int rx_multicast_frames; + unsigned int rx_fragments; + unsigned int rx_unicast_octets; + unsigned int rx_multicast_octets; + unsigned int rx_fcs_errors; + unsigned int rx_discards_no_buffer; + unsigned int tx_discards_wrong_sa; + unsigned int rx_discards_undecryptable; + unsigned int rx_message_in_msg_fragments; + unsigned int rx_message_in_bad_msg_fragments; +}; + +struct ieee80211_device; + +#include "ieee80211_crypt.h" + +#define SEC_KEY_1 (1<<0) +#define SEC_KEY_2 (1<<1) +#define SEC_KEY_3 (1<<2) +#define SEC_KEY_4 (1<<3) +#define SEC_ACTIVE_KEY (1<<4) +#define SEC_AUTH_MODE (1<<5) +#define SEC_UNICAST_GROUP (1<<6) +#define SEC_LEVEL (1<<7) +#define SEC_ENABLED (1<<8) + +#define SEC_LEVEL_0 0 /* None */ +#define SEC_LEVEL_1 1 /* WEP 40 and 104 bit */ +#define SEC_LEVEL_2 2 /* Level 1 + TKIP */ +#define SEC_LEVEL_2_CKIP 3 /* Level 1 + CKIP */ +#define SEC_LEVEL_3 4 /* Level 2 + CCMP */ + +#define WEP_KEYS 4 +#define WEP_KEY_LEN 13 + +struct ieee80211_security { + u16 active_key:2, + enabled:1, + auth_mode:2, + auth_algo:4, + unicast_uses_group:1; + u8 key_sizes[WEP_KEYS]; + u8 keys[WEP_KEYS][WEP_KEY_LEN]; + u8 level; + u16 flags; +} __attribute__ ((packed)); + + +/* + + 802.11 data frame from AP + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `-------------------------------------------------------------------' + +Total: 28-2340 bytes + +*/ + +struct ieee80211_header_data { + u16 frame_ctl; + u16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + u16 seq_ctrl; +}; + +#define BEACON_PROBE_SSID_ID_POSITION 12 + +/* Management Frame Information Element Types */ +#define MFIE_TYPE_SSID 0 +#define MFIE_TYPE_RATES 1 +#define MFIE_TYPE_FH_SET 2 +#define MFIE_TYPE_DS_SET 3 +#define MFIE_TYPE_CF_SET 4 +#define MFIE_TYPE_TIM 5 +#define MFIE_TYPE_IBSS_SET 6 +#define MFIE_TYPE_CHALLENGE 16 +#define MFIE_TYPE_RSN 48 +#define MFIE_TYPE_RATES_EX 50 +#define MFIE_TYPE_GENERIC 221 + +struct ieee80211_info_element_hdr { + u8 id; + u8 len; +} __attribute__ ((packed)); + +struct ieee80211_info_element { + u8 id; + u8 len; + u8 data[0]; +} __attribute__ ((packed)); + +/* + * These are the data types that can make up management packets + * + u16 auth_algorithm; + u16 auth_sequence; + u16 beacon_interval; + u16 capability; + u8 current_ap[ETH_ALEN]; + u16 listen_interval; + struct { + u16 association_id:14, reserved:2; + } __attribute__ ((packed)); + u32 time_stamp[2]; + u16 reason; + u16 status; +*/ + +struct ieee80211_authentication { + struct ieee80211_header_data header; + u16 algorithm; + u16 transaction; + u16 status; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + + +struct ieee80211_probe_response { + struct ieee80211_header_data header; + u32 time_stamp[2]; + u16 beacon_interval; + u16 capability; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + +struct ieee80211_assoc_request_frame { + u16 capability; + u16 listen_interval; + u8 current_ap[ETH_ALEN]; + struct ieee80211_info_element info_element; +} __attribute__ ((packed)); + +struct ieee80211_assoc_response_frame { + struct ieee80211_hdr_3addr header; + u16 capability; + u16 status; + u16 aid; + struct ieee80211_info_element info_element; /* supported rates */ +} __attribute__ ((packed)); + + +struct ieee80211_txb { + u8 nr_frags; + u8 encrypted; + u16 reserved; + u16 frag_size; + u16 payload_size; + struct sk_buff *fragments[0]; +}; + + +/* SWEEP TABLE ENTRIES NUMBER*/ +#define MAX_SWEEP_TAB_ENTRIES 42 +#define MAX_SWEEP_TAB_ENTRIES_PER_PACKET 7 +/* MAX_RATES_LENGTH needs to be 12. The spec says 8, and many APs + * only use 8, and then use extended rates for the remaining supported + * rates. Other APs, however, stick all of their supported rates on the + * main rates information element... */ +#define MAX_RATES_LENGTH ((u8)12) +#define MAX_RATES_EX_LENGTH ((u8)16) +#define MAX_NETWORK_COUNT 128 + +#define CRC_LENGTH 4U + +#define MAX_WPA_IE_LEN 64 + +#define NETWORK_EMPTY_ESSID (1<<0) +#define NETWORK_HAS_OFDM (1<<1) +#define NETWORK_HAS_CCK (1<<2) + +struct ieee80211_network { + /* These entries are used to identify a unique network */ + u8 bssid[ETH_ALEN]; + u8 channel; + /* Ensure null-terminated for any debug msgs */ + u8 ssid[IW_ESSID_MAX_SIZE + 1]; + u8 ssid_len; + + /* These are network statistics */ + struct ieee80211_rx_stats stats; + u16 capability; + u8 rates[MAX_RATES_LENGTH]; + u8 rates_len; + u8 rates_ex[MAX_RATES_EX_LENGTH]; + u8 rates_ex_len; + unsigned long last_scanned; + u8 mode; + u8 flags; + u32 last_associate; + u32 time_stamp[2]; + u16 beacon_interval; + u16 listen_interval; + u16 atim_window; + u8 wpa_ie[MAX_WPA_IE_LEN]; + size_t wpa_ie_len; + u8 rsn_ie[MAX_WPA_IE_LEN]; + size_t rsn_ie_len; + struct list_head list; +}; + +enum ieee80211_state { + IEEE80211_UNINITIALIZED = 0, + IEEE80211_INITIALIZED, + IEEE80211_ASSOCIATING, + IEEE80211_ASSOCIATED, + IEEE80211_AUTHENTICATING, + IEEE80211_AUTHENTICATED, + IEEE80211_SHUTDOWN +}; + +#define DEFAULT_MAX_SCAN_AGE (15 * HZ) +#define DEFAULT_FTS 2346 +#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x" +#define MAC_ARG(x) ((u8*)(x))[0],((u8*)(x))[1],((u8*)(x))[2],((u8*)(x))[3],((u8*)(x))[4],((u8*)(x))[5] + + +extern inline int is_multicast_ether_addr(const u8 *addr) +{ + return ((addr[0] != 0xff) && (0x01 & addr[0])); +} + +extern inline int is_broadcast_ether_addr(const u8 *addr) +{ + return ((addr[0] == 0xff) && (addr[1] == 0xff) && (addr[2] == 0xff) && \ + (addr[3] == 0xff) && (addr[4] == 0xff) && (addr[5] == 0xff)); +} + +#define CFG_IEEE80211_RESERVE_FCS (1<<0) +#define CFG_IEEE80211_COMPUTE_FCS (1<<1) + +struct ieee80211_device { + struct net_device *dev; + + /* Bookkeeping structures */ + struct net_device_stats stats; + struct ieee80211_stats ieee_stats; + + /* Probe / Beacon management */ + struct list_head network_free_list; + struct list_head network_list; + struct ieee80211_network *networks; + int scans; + int scan_age; + + int iw_mode; /* operating mode (IW_MODE_*) */ + + spinlock_t lock; + + int tx_headroom; /* Set to size of any additional room needed at front + * of allocated Tx SKBs */ + u32 config; + + /* WEP and other encryption related settings at the device level */ + int open_wep; /* Set to 1 to allow unencrypted frames */ + + int reset_on_keychange; /* Set to 1 if the HW needs to be reset on + * WEP key changes */ + + /* If the host performs {en,de}cryption, then set to 1 */ + int host_encrypt; + int host_decrypt; + int ieee802_1x; /* is IEEE 802.1X used */ + + /* WPA data */ + int wpa_enabled; + int drop_unencrypted; + int tkip_countermeasures; + int privacy_invoked; + size_t wpa_ie_len; + u8 *wpa_ie; + + struct list_head crypt_deinit_list; + struct ieee80211_crypt_data *crypt[WEP_KEYS]; + int tx_keyidx; /* default TX key index (crypt[tx_keyidx]) */ + struct timer_list crypt_deinit_timer; + + int bcrx_sta_key; /* use individual keys to override default keys even + * with RX of broad/multicast frames */ + + /* Fragmentation structures */ + struct ieee80211_frag_entry frag_cache[IEEE80211_FRAG_CACHE_LEN]; + unsigned int frag_next_idx; + u16 fts; /* Fragmentation Threshold */ + + /* Association info */ + u8 bssid[ETH_ALEN]; + + enum ieee80211_state state; + + int mode; /* A, B, G */ + int modulation; /* CCK, OFDM */ + int freq_band; /* 2.4Ghz, 5.2Ghz, Mixed */ + int abg_ture; /* ABG flag */ + + /* Callback functions */ + void (*set_security)(struct net_device *dev, + struct ieee80211_security *sec); + int (*hard_start_xmit)(struct ieee80211_txb *txb, + struct net_device *dev); + int (*reset_port)(struct net_device *dev); + + /* This must be the last item so that it points to the data + * allocated beyond this structure by alloc_ieee80211 */ + u8 priv[0]; +}; + +#define IEEE_A (1<<0) +#define IEEE_B (1<<1) +#define IEEE_G (1<<2) +#define IEEE_MODE_MASK (IEEE_A|IEEE_B|IEEE_G) + +extern inline void *ieee80211_priv(struct net_device *dev) +{ + return ((struct ieee80211_device *)netdev_priv(dev))->priv; +} + +extern inline int ieee80211_is_empty_essid(const char *essid, int essid_len) +{ + /* Single white space is for Linksys APs */ + if (essid_len == 1 && essid[0] == ' ') + return 1; + + /* Otherwise, if the entire essid is 0, we assume it is hidden */ + while (essid_len) { + essid_len--; + if (essid[essid_len] != '\0') + return 0; + } + + return 1; +} + +extern inline int ieee80211_is_valid_mode(struct ieee80211_device *ieee, int mode) +{ + /* + * It is possible for both access points and our device to support + * combinations of modes, so as long as there is one valid combination + * of ap/device supported modes, then return success + * + */ + if ((mode & IEEE_A) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_52GHZ_BAND)) + return 1; + + if ((mode & IEEE_G) && + (ieee->modulation & IEEE80211_OFDM_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + if ((mode & IEEE_B) && + (ieee->modulation & IEEE80211_CCK_MODULATION) && + (ieee->freq_band & IEEE80211_24GHZ_BAND)) + return 1; + + return 0; +} + +extern inline int ieee80211_get_hdrlen(u16 fc) +{ + int hdrlen = 24; + + switch (WLAN_FC_GET_TYPE(fc)) { + case IEEE80211_FTYPE_DATA: + if ((fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS)) + hdrlen = 30; /* Addr4 */ + break; + case IEEE80211_FTYPE_CTL: + switch (WLAN_FC_GET_STYPE(fc)) { + case IEEE80211_STYPE_CTS: + case IEEE80211_STYPE_ACK: + hdrlen = 10; + break; + default: + hdrlen = 16; + break; + } + break; + } + + return hdrlen; +} + + + +/* ieee80211.c */ +extern void free_ieee80211(struct net_device *dev); +extern struct net_device *alloc_ieee80211(int sizeof_priv); + +extern int ieee80211_set_encryption(struct ieee80211_device *ieee); + +/* ieee80211_tx.c */ + + +extern int ieee80211_xmit(struct sk_buff *skb, + struct net_device *dev); +extern void ieee80211_txb_free(struct ieee80211_txb *); + + +/* ieee80211_rx.c */ +extern int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats); +extern void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats); + +/* iee80211_wx.c */ +extern int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); +extern int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *key); + + +extern inline void ieee80211_increment_scans(struct ieee80211_device *ieee) +{ + ieee->scans++; +} + +extern inline int ieee80211_get_scans(struct ieee80211_device *ieee) +{ + return ieee->scans; +} + +static inline const char *escape_essid(const char *essid, u8 essid_len) { + static char escaped[IW_ESSID_MAX_SIZE * 2 + 1]; + const char *s = essid; + char *d = escaped; + + if (ieee80211_is_empty_essid(essid, essid_len)) { + memcpy(escaped, "", sizeof("")); + return escaped; + } + + essid_len = min(essid_len, (u8)IW_ESSID_MAX_SIZE); + while (essid_len--) { + if (*s == '\0') { + *d++ = '\\'; + *d++ = '0'; + s++; + } else { + *d++ = *s++; + } + } + *d = '\0'; + return escaped; +} +#endif /* IEEE80211_H */ diff -Nru a/include/net/ieee80211_crypt.h b/include/net/ieee80211_crypt.h --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/include/net/ieee80211_crypt.h 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,86 @@ +/* + * Original code based on Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3. + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * + * Adaption to a generic IEEE 802.11 stack by James Ketrenos + * + * + * Copyright (c) 2004, Intel Corporation + * + * 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. See README and COPYING for + * more details. + */ + +/* + * This file defines the interface to the ieee80211 crypto module. + */ +#ifndef IEEE80211_CRYPT_H +#define IEEE80211_CRYPT_H + +#include + +struct ieee80211_crypto_ops { + const char *name; + + /* init new crypto context (e.g., allocate private data space, + * select IV, etc.); returns NULL on failure or pointer to allocated + * private data on success */ + void * (*init)(int keyidx); + + /* deinitialize crypto context and free allocated private data */ + void (*deinit)(void *priv); + + /* encrypt/decrypt return < 0 on error or >= 0 on success. The return + * value from decrypt_mpdu is passed as the keyidx value for + * decrypt_msdu. skb must have enough head and tail room for the + * encryption; if not, error will be returned; these functions are + * called for all MPDUs (i.e., fragments). + */ + int (*encrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_mpdu)(struct sk_buff *skb, int hdr_len, void *priv); + + /* These functions are called for full MSDUs, i.e. full frames. + * These can be NULL if full MSDU operations are not needed. */ + int (*encrypt_msdu)(struct sk_buff *skb, int hdr_len, void *priv); + int (*decrypt_msdu)(struct sk_buff *skb, int keyidx, int hdr_len, + void *priv); + + int (*set_key)(void *key, int len, u8 *seq, void *priv); + int (*get_key)(void *key, int len, u8 *seq, void *priv); + + /* procfs handler for printing out key information and possible + * statistics */ + char * (*print_stats)(char *p, void *priv); + + /* maximum number of bytes added by encryption; encrypt buf is + * allocated with extra_prefix_len bytes, copy of in_buf, and + * extra_postfix_len; encrypt need not use all this space, but + * the result must start at the beginning of the buffer and correct + * length must be returned */ + int extra_prefix_len, extra_postfix_len; + + struct module *owner; +}; + +struct ieee80211_crypt_data { + struct list_head list; /* delayed deletion list */ + struct ieee80211_crypto_ops *ops; + void *priv; + atomic_t refcnt; +}; + +int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops); +int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops); +struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name); +void ieee80211_crypt_deinit_entries(struct ieee80211_device *, int); +void ieee80211_crypt_deinit_handler(unsigned long); +void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee, + struct ieee80211_crypt_data **crypt); + +#endif diff -Nru a/net/Kconfig b/net/Kconfig --- a/net/Kconfig 2005-03-15 23:46:53 -08:00 +++ b/net/Kconfig 2005-03-15 23:46:53 -08:00 @@ -649,6 +649,8 @@ source "net/bluetooth/Kconfig" +source "net/ieee80211/Kconfig" + source "drivers/net/Kconfig" endmenu diff -Nru a/net/Makefile b/net/Makefile --- a/net/Makefile 2005-03-15 23:46:53 -08:00 +++ b/net/Makefile 2005-03-15 23:46:53 -08:00 @@ -42,6 +42,7 @@ obj-$(CONFIG_ECONET) += econet/ obj-$(CONFIG_VLAN_8021Q) += 8021q/ obj-$(CONFIG_IP_SCTP) += sctp/ +obj-$(CONFIG_IEEE80211) += ieee80211/ ifeq ($(CONFIG_NET),y) obj-$(CONFIG_SYSCTL) += sysctl_net.o diff -Nru a/net/ieee80211/Kconfig b/net/ieee80211/Kconfig --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/Kconfig 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,67 @@ +config IEEE80211 + tristate "Generic IEEE 802.11 Networking Stack" + select NET_RADIO + ---help--- + This option enables the hardware independent IEEE 802.11 + networking stack. + +config IEEE80211_DEBUG + bool "Enable full debugging output" + depends on IEEE80211 + ---help--- + This option will enable debug tracing output for the + ieee80211 network stack. + + This will result in the kernel module being ~70k larger. You + can control which debug output is sent to the kernel log by + setting the value in + + /proc/net/ieee80211/debug_level + + For example: + + % echo 0x00000FFO > /proc/net/ieee80211/debug_level + + For a list of values you can assign to debug_level, you + can look at the bit mask values in + + If you are not trying to debug or develop the ieee80211 + subsystem, you most likely want to say N here. + +config IEEE80211_CRYPT_WEP + tristate "IEEE 802.11 WEP encryption (802.1x)" + depends on IEEE80211 + select CRYPTO + select CRYPTO_ARC4 + select CRC32 + ---help--- + Include software based cipher suites in support of IEEE + 802.11's WEP. This is needed for WEP as well as 802.1x. + + This can be compiled as a modules and it will be called + "ieee80211_crypt_wep". + +config IEEE80211_CRYPT_CCMP + tristate "IEEE 802.11i CCMP support" + depends on IEEE80211 + select CRYPTO_AES + ---help--- + Include software based cipher suites in support of IEEE 802.11i + (aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with CCMP enabled + networks. + + This can be compiled as a modules and it will be called + "ieee80211_crypt_ccmp". + +config IEEE80211_CRYPT_TKIP + tristate "IEEE 802.11i TKIP encryption" + depends on IEEE80211 + select CRYPTO_MICHAEL_MIC + ---help--- + Include software based cipher suites in support of IEEE 802.11i + (aka TGi, WPA, WPA2, WPA-PSK, etc.) for use with TKIP enabled + networks. + + This can be compiled as a modules and it will be called + "ieee80211_crypt_tkip". + diff -Nru a/net/ieee80211/Makefile b/net/ieee80211/Makefile --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/Makefile 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,11 @@ +obj-$(CONFIG_IEEE80211) += ieee80211.o +obj-$(CONFIG_IEEE80211) += ieee80211_crypt.o +obj-$(CONFIG_IEEE80211_CRYPT_WEP) += ieee80211_crypt_wep.o +obj-$(CONFIG_IEEE80211_CRYPT_CCMP) += ieee80211_crypt_ccmp.o +obj-$(CONFIG_IEEE80211_CRYPT_TKIP) += ieee80211_crypt_tkip.o +ieee80211-objs := \ + ieee80211_module.o \ + ieee80211_tx.o \ + ieee80211_rx.o \ + ieee80211_wx.o + diff -Nru a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_crypt.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,259 @@ +/* + * Host AP crypto routines + * + * Copyright (c) 2002-2003, Jouni Malinen + * Portions Copyright (C) 2004, Intel Corporation + * + * 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. See README and COPYING for + * more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("HostAP crypto"); +MODULE_LICENSE("GPL"); + +struct ieee80211_crypto_alg { + struct list_head list; + struct ieee80211_crypto_ops *ops; +}; + + +struct ieee80211_crypto { + struct list_head algs; + spinlock_t lock; +}; + +static struct ieee80211_crypto *hcrypt; + +void ieee80211_crypt_deinit_entries(struct ieee80211_device *ieee, + int force) +{ + struct list_head *ptr, *n; + struct ieee80211_crypt_data *entry; + + for (ptr = ieee->crypt_deinit_list.next, n = ptr->next; + ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) { + entry = list_entry(ptr, struct ieee80211_crypt_data, list); + + if (atomic_read(&entry->refcnt) != 0 && !force) + continue; + + list_del(ptr); + + if (entry->ops) { + entry->ops->deinit(entry->priv); + module_put(entry->ops->owner); + } + kfree(entry); + } +} + +void ieee80211_crypt_deinit_handler(unsigned long data) +{ + struct ieee80211_device *ieee = (struct ieee80211_device *)data; + unsigned long flags; + + spin_lock_irqsave(&ieee->lock, flags); + ieee80211_crypt_deinit_entries(ieee, 0); + if (!list_empty(&ieee->crypt_deinit_list)) { + printk(KERN_DEBUG "%s: entries remaining in delayed crypt " + "deletion list\n", ieee->dev->name); + ieee->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&ieee->crypt_deinit_timer); + } + spin_unlock_irqrestore(&ieee->lock, flags); + +} + +void ieee80211_crypt_delayed_deinit(struct ieee80211_device *ieee, + struct ieee80211_crypt_data **crypt) +{ + struct ieee80211_crypt_data *tmp; + unsigned long flags; + + if (*crypt == NULL) + return; + + tmp = *crypt; + *crypt = NULL; + + /* must not run ops->deinit() while there may be pending encrypt or + * decrypt operations. Use a list of delayed deinits to avoid needing + * locking. */ + + spin_lock_irqsave(&ieee->lock, flags); + list_add(&tmp->list, &ieee->crypt_deinit_list); + if (!timer_pending(&ieee->crypt_deinit_timer)) { + ieee->crypt_deinit_timer.expires = jiffies + HZ; + add_timer(&ieee->crypt_deinit_timer); + } + spin_unlock_irqrestore(&ieee->lock, flags); +} + +int ieee80211_register_crypto_ops(struct ieee80211_crypto_ops *ops) +{ + unsigned long flags; + struct ieee80211_crypto_alg *alg; + + if (hcrypt == NULL) + return -1; + + alg = kmalloc(sizeof(*alg), GFP_KERNEL); + if (alg == NULL) + return -ENOMEM; + + memset(alg, 0, sizeof(*alg)); + alg->ops = ops; + + spin_lock_irqsave(&hcrypt->lock, flags); + list_add(&alg->list, &hcrypt->algs); + spin_unlock_irqrestore(&hcrypt->lock, flags); + + printk(KERN_DEBUG "ieee80211_crypt: registered algorithm '%s'\n", + ops->name); + + return 0; +} + +int ieee80211_unregister_crypto_ops(struct ieee80211_crypto_ops *ops) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *del_alg = NULL; + + if (hcrypt == NULL) + return -1; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (alg->ops == ops) { + list_del(&alg->list); + del_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (del_alg) { + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s'\n", ops->name); + kfree(del_alg); + } + + return del_alg ? 0 : -1; +} + + +struct ieee80211_crypto_ops * ieee80211_get_crypto_ops(const char *name) +{ + unsigned long flags; + struct list_head *ptr; + struct ieee80211_crypto_alg *found_alg = NULL; + + if (hcrypt == NULL) + return NULL; + + spin_lock_irqsave(&hcrypt->lock, flags); + for (ptr = hcrypt->algs.next; ptr != &hcrypt->algs; ptr = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + if (strcmp(alg->ops->name, name) == 0) { + found_alg = alg; + break; + } + } + spin_unlock_irqrestore(&hcrypt->lock, flags); + + if (found_alg) + return found_alg->ops; + else + return NULL; +} + + +static void * ieee80211_crypt_null_init(int keyidx) { return (void *) 1; } +static void ieee80211_crypt_null_deinit(void *priv) {} + +static struct ieee80211_crypto_ops ieee80211_crypt_null = { + .name = "NULL", + .init = ieee80211_crypt_null_init, + .deinit = ieee80211_crypt_null_deinit, + .encrypt_mpdu = NULL, + .decrypt_mpdu = NULL, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = NULL, + .get_key = NULL, + .extra_prefix_len = 0, + .extra_postfix_len = 0, + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_init(void) +{ + int ret = -ENOMEM; + + hcrypt = kmalloc(sizeof(*hcrypt), GFP_KERNEL); + if (!hcrypt) + goto out; + + memset(hcrypt, 0, sizeof(*hcrypt)); + INIT_LIST_HEAD(&hcrypt->algs); + spin_lock_init(&hcrypt->lock); + + ret = ieee80211_register_crypto_ops(&ieee80211_crypt_null); + if (ret < 0) { + kfree(hcrypt); + hcrypt = NULL; + } +out: + return ret; +} + + +static void __exit ieee80211_crypto_deinit(void) +{ + struct list_head *ptr, *n; + + if (hcrypt == NULL) + return; + + for (ptr = hcrypt->algs.next, n = ptr->next; ptr != &hcrypt->algs; + ptr = n, n = ptr->next) { + struct ieee80211_crypto_alg *alg = + (struct ieee80211_crypto_alg *) ptr; + list_del(ptr); + printk(KERN_DEBUG "ieee80211_crypt: unregistered algorithm " + "'%s' (deinit)\n", alg->ops->name); + kfree(alg); + } + + kfree(hcrypt); +} + +EXPORT_SYMBOL(ieee80211_crypt_deinit_entries); +EXPORT_SYMBOL(ieee80211_crypt_deinit_handler); +EXPORT_SYMBOL(ieee80211_crypt_delayed_deinit); + +EXPORT_SYMBOL(ieee80211_register_crypto_ops); +EXPORT_SYMBOL(ieee80211_unregister_crypto_ops); +EXPORT_SYMBOL(ieee80211_get_crypto_ops); + +module_init(ieee80211_crypto_init); +module_exit(ieee80211_crypto_deinit); diff -Nru a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_crypt_ccmp.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,470 @@ +/* + * Host AP crypt: host-based CCMP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: CCMP"); +MODULE_LICENSE("GPL"); + +#define AES_BLOCK_LEN 16 +#define CCMP_HDR_LEN 8 +#define CCMP_MIC_LEN 8 +#define CCMP_TK_LEN 16 +#define CCMP_PN_LEN 6 + +struct ieee80211_ccmp_data { + u8 key[CCMP_TK_LEN]; + int key_set; + + u8 tx_pn[CCMP_PN_LEN]; + u8 rx_pn[CCMP_PN_LEN]; + + u32 dot11RSNAStatsCCMPFormatErrors; + u32 dot11RSNAStatsCCMPReplays; + u32 dot11RSNAStatsCCMPDecryptErrors; + + int key_idx; + + struct crypto_tfm *tfm; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 tx_b0[AES_BLOCK_LEN], tx_b[AES_BLOCK_LEN], + tx_e[AES_BLOCK_LEN], tx_s0[AES_BLOCK_LEN]; + u8 rx_b0[AES_BLOCK_LEN], rx_b[AES_BLOCK_LEN], rx_a[AES_BLOCK_LEN]; +}; + +void ieee80211_ccmp_aes_encrypt(struct crypto_tfm *tfm, + const u8 pt[16], u8 ct[16]) +{ + struct scatterlist src, dst; + + src.page = virt_to_page(pt); + src.offset = offset_in_page(pt); + src.length = AES_BLOCK_LEN; + + dst.page = virt_to_page(ct); + dst.offset = offset_in_page(ct); + dst.length = AES_BLOCK_LEN; + + crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN); +} + +static void * ieee80211_ccmp_init(int key_idx) +{ + struct ieee80211_ccmp_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm = crypto_alloc_tfm("aes", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_ccmp: could not allocate " + "crypto API aes\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + + return NULL; +} + + +static void ieee80211_ccmp_deinit(void *priv) +{ + struct ieee80211_ccmp_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + kfree(priv); +} + + +static inline void xor_block(u8 *b, u8 *a, size_t len) +{ + int i; + for (i = 0; i < len; i++) + b[i] ^= a[i]; +} + + +static void ccmp_init_blocks(struct crypto_tfm *tfm, + struct ieee80211_hdr *hdr, + u8 *pn, size_t dlen, u8 *b0, u8 *auth, + u8 *s0) +{ + u8 *pos, qc = 0; + size_t aad_len; + u16 fc; + int a4_included, qc_included; + u8 aad[2 * AES_BLOCK_LEN]; + + fc = le16_to_cpu(hdr->frame_ctl); + a4_included = ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)); + qc_included = ((WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA) && + (WLAN_FC_GET_STYPE(fc) & 0x08)); + aad_len = 22; + if (a4_included) + aad_len += 6; + if (qc_included) { + pos = (u8 *) &hdr->addr4; + if (a4_included) + pos += 6; + qc = *pos & 0x0f; + aad_len += 2; + } + + /* CCM Initial Block: + * Flag (Include authentication header, M=3 (8-octet MIC), + * L=1 (2-octet Dlen)) + * Nonce: 0x00 | A2 | PN + * Dlen */ + b0[0] = 0x59; + b0[1] = qc; + memcpy(b0 + 2, hdr->addr2, ETH_ALEN); + memcpy(b0 + 8, pn, CCMP_PN_LEN); + b0[14] = (dlen >> 8) & 0xff; + b0[15] = dlen & 0xff; + + /* AAD: + * FC with bits 4..6 and 11..13 masked to zero; 14 is always one + * A1 | A2 | A3 + * SC with bits 4..15 (seq#) masked to zero + * A4 (if present) + * QC (if present) + */ + pos = (u8 *) hdr; + aad[0] = 0; /* aad_len >> 8 */ + aad[1] = aad_len & 0xff; + aad[2] = pos[0] & 0x8f; + aad[3] = pos[1] & 0xc7; + memcpy(aad + 4, hdr->addr1, 3 * ETH_ALEN); + pos = (u8 *) &hdr->seq_ctl; + aad[22] = pos[0] & 0x0f; + aad[23] = 0; /* all bits masked */ + memset(aad + 24, 0, 8); + if (a4_included) + memcpy(aad + 24, hdr->addr4, ETH_ALEN); + if (qc_included) { + aad[a4_included ? 30 : 24] = qc; + /* rest of QC masked */ + } + + /* Start with the first block and AAD */ + ieee80211_ccmp_aes_encrypt(tfm, b0, auth); + xor_block(auth, aad, AES_BLOCK_LEN); + ieee80211_ccmp_aes_encrypt(tfm, auth, auth); + xor_block(auth, &aad[AES_BLOCK_LEN], AES_BLOCK_LEN); + ieee80211_ccmp_aes_encrypt(tfm, auth, auth); + b0[0] &= 0x07; + b0[14] = b0[15] = 0; + ieee80211_ccmp_aes_encrypt(tfm, b0, s0); +} + + +static int ieee80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_ccmp_data *key = priv; + int data_len, i, blocks, last, len; + u8 *pos, *mic; + struct ieee80211_hdr *hdr; + u8 *b0 = key->tx_b0; + u8 *b = key->tx_b; + u8 *e = key->tx_e; + u8 *s0 = key->tx_s0; + + if (skb_headroom(skb) < CCMP_HDR_LEN || + skb_tailroom(skb) < CCMP_MIC_LEN || + skb->len < hdr_len) + return -1; + + data_len = skb->len - hdr_len; + pos = skb_push(skb, CCMP_HDR_LEN); + memmove(pos, pos + CCMP_HDR_LEN, hdr_len); + pos += hdr_len; + mic = skb_put(skb, CCMP_MIC_LEN); + + i = CCMP_PN_LEN - 1; + while (i >= 0) { + key->tx_pn[i]++; + if (key->tx_pn[i] != 0) + break; + i--; + } + + *pos++ = key->tx_pn[5]; + *pos++ = key->tx_pn[4]; + *pos++ = 0; + *pos++ = (key->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = key->tx_pn[3]; + *pos++ = key->tx_pn[2]; + *pos++ = key->tx_pn[1]; + *pos++ = key->tx_pn[0]; + + hdr = (struct ieee80211_hdr *) skb->data; + ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Authentication */ + xor_block(b, pos, len); + ieee80211_ccmp_aes_encrypt(key->tfm, b, b); + /* Encryption, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + ieee80211_ccmp_aes_encrypt(key->tfm, b0, e); + xor_block(pos, e, len); + pos += len; + } + + for (i = 0; i < CCMP_MIC_LEN; i++) + mic[i] = b[i] ^ s0[i]; + + return 0; +} + + +static int ieee80211_ccmp_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_ccmp_data *key = priv; + u8 keyidx, *pos; + struct ieee80211_hdr *hdr; + u8 *b0 = key->rx_b0; + u8 *b = key->rx_b; + u8 *a = key->rx_a; + u8 pn[6]; + int i, blocks, last, len; + size_t data_len = skb->len - hdr_len - CCMP_HDR_LEN - CCMP_MIC_LEN; + u8 *mic = skb->data + skb->len - CCMP_MIC_LEN; + + if (skb->len < hdr_len + CCMP_HDR_LEN + CCMP_MIC_LEN) { + key->dot11RSNAStatsCCMPFormatErrors++; + return -1; + } + + hdr = (struct ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet without ExtIV" + " flag from " MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + key->dot11RSNAStatsCCMPFormatErrors++; + return -2; + } + keyidx >>= 6; + if (key->key_idx != keyidx) { + printk(KERN_DEBUG "CCMP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", key->key_idx, keyidx, priv); + return -6; + } + if (!key->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: received packet from " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(hdr->addr2), keyidx); + } + return -3; + } + + pn[0] = pos[7]; + pn[1] = pos[6]; + pn[2] = pos[5]; + pn[3] = pos[4]; + pn[4] = pos[1]; + pn[5] = pos[0]; + pos += 8; + + if (memcmp(pn, key->rx_pn, CCMP_PN_LEN) <= 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: replay detected: STA=" MAC_FMT + " previous PN %02x%02x%02x%02x%02x%02x " + "received PN %02x%02x%02x%02x%02x%02x\n", + MAC_ARG(hdr->addr2), MAC_ARG(key->rx_pn), + MAC_ARG(pn)); + } + key->dot11RSNAStatsCCMPReplays++; + return -4; + } + + ccmp_init_blocks(key->tfm, hdr, pn, data_len, b0, a, b); + xor_block(mic, b, CCMP_MIC_LEN); + + blocks = (data_len + AES_BLOCK_LEN - 1) / AES_BLOCK_LEN; + last = data_len % AES_BLOCK_LEN; + + for (i = 1; i <= blocks; i++) { + len = (i == blocks && last) ? last : AES_BLOCK_LEN; + /* Decrypt, with counter */ + b0[14] = (i >> 8) & 0xff; + b0[15] = i & 0xff; + ieee80211_ccmp_aes_encrypt(key->tfm, b0, b); + xor_block(pos, b, len); + /* Authentication */ + xor_block(a, pos, len); + ieee80211_ccmp_aes_encrypt(key->tfm, a, a); + pos += len; + } + + if (memcmp(mic, a, CCMP_MIC_LEN) != 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "CCMP: decrypt failed: STA=" + MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + key->dot11RSNAStatsCCMPDecryptErrors++; + return -5; + } + + memcpy(key->rx_pn, pn, CCMP_PN_LEN); + + /* Remove hdr and MIC */ + memmove(skb->data + CCMP_HDR_LEN, skb->data, hdr_len); + skb_pull(skb, CCMP_HDR_LEN); + skb_trim(skb, skb->len - CCMP_MIC_LEN); + + return keyidx; +} + + +static int ieee80211_ccmp_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_ccmp_data *data = priv; + int keyidx; + struct crypto_tfm *tfm = data->tfm; + + keyidx = data->key_idx; + memset(data, 0, sizeof(*data)); + data->key_idx = keyidx; + data->tfm = tfm; + if (len == CCMP_TK_LEN) { + memcpy(data->key, key, CCMP_TK_LEN); + data->key_set = 1; + if (seq) { + data->rx_pn[0] = seq[5]; + data->rx_pn[1] = seq[4]; + data->rx_pn[2] = seq[3]; + data->rx_pn[3] = seq[2]; + data->rx_pn[4] = seq[1]; + data->rx_pn[5] = seq[0]; + } + crypto_cipher_setkey(data->tfm, data->key, CCMP_TK_LEN); + } else if (len == 0) + data->key_set = 0; + else + return -1; + + return 0; +} + + +static int ieee80211_ccmp_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_ccmp_data *data = priv; + + if (len < CCMP_TK_LEN) + return -1; + + if (!data->key_set) + return 0; + memcpy(key, data->key, CCMP_TK_LEN); + + if (seq) { + seq[0] = data->tx_pn[5]; + seq[1] = data->tx_pn[4]; + seq[2] = data->tx_pn[3]; + seq[3] = data->tx_pn[2]; + seq[4] = data->tx_pn[1]; + seq[5] = data->tx_pn[0]; + } + + return CCMP_TK_LEN; +} + + +static char * ieee80211_ccmp_print_stats(char *p, void *priv) +{ + struct ieee80211_ccmp_data *ccmp = priv; + p += sprintf(p, "key[%d] alg=CCMP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "format_errors=%d replays=%d decrypt_errors=%d\n", + ccmp->key_idx, ccmp->key_set, + MAC_ARG(ccmp->tx_pn), MAC_ARG(ccmp->rx_pn), + ccmp->dot11RSNAStatsCCMPFormatErrors, + ccmp->dot11RSNAStatsCCMPReplays, + ccmp->dot11RSNAStatsCCMPDecryptErrors); + + return p; +} + + +static struct ieee80211_crypto_ops ieee80211_crypt_ccmp = { + .name = "CCMP", + .init = ieee80211_ccmp_init, + .deinit = ieee80211_ccmp_deinit, + .encrypt_mpdu = ieee80211_ccmp_encrypt, + .decrypt_mpdu = ieee80211_ccmp_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = ieee80211_ccmp_set_key, + .get_key = ieee80211_ccmp_get_key, + .print_stats = ieee80211_ccmp_print_stats, + .extra_prefix_len = CCMP_HDR_LEN, + .extra_postfix_len = CCMP_MIC_LEN, + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_ccmp_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_ccmp); +} + + +static void __exit ieee80211_crypto_ccmp_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_ccmp); +} + + +module_init(ieee80211_crypto_ccmp_init); +module_exit(ieee80211_crypto_ccmp_exit); diff -Nru a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_crypt_tkip.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,708 @@ +/* + * Host AP crypt: host-based TKIP encryption implementation for Host AP driver + * + * Copyright (c) 2003-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: TKIP"); +MODULE_LICENSE("GPL"); + +struct ieee80211_tkip_data { +#define TKIP_KEY_LEN 32 + u8 key[TKIP_KEY_LEN]; + int key_set; + + u32 tx_iv32; + u16 tx_iv16; + u16 tx_ttak[5]; + int tx_phase1_done; + + u32 rx_iv32; + u16 rx_iv16; + u16 rx_ttak[5]; + int rx_phase1_done; + u32 rx_iv32_new; + u16 rx_iv16_new; + + u32 dot11RSNAStatsTKIPReplays; + u32 dot11RSNAStatsTKIPICVErrors; + u32 dot11RSNAStatsTKIPLocalMICFailures; + + int key_idx; + + struct crypto_tfm *tfm_arc4; + struct crypto_tfm *tfm_michael; + + /* scratch buffers for virt_to_page() (crypto API) */ + u8 rx_hdr[16], tx_hdr[16]; +}; + +static void * ieee80211_tkip_init(int key_idx) +{ + struct ieee80211_tkip_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = key_idx; + + priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0); + if (priv->tfm_arc4 == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + priv->tfm_michael = crypto_alloc_tfm("michael_mic", 0); + if (priv->tfm_michael == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_tkip: could not allocate " + "crypto API michael_mic\n"); + goto fail; + } + + return priv; + +fail: + if (priv) { + if (priv->tfm_michael) + crypto_free_tfm(priv->tfm_michael); + if (priv->tfm_arc4) + crypto_free_tfm(priv->tfm_arc4); + kfree(priv); + } + + return NULL; +} + + +static void ieee80211_tkip_deinit(void *priv) +{ + struct ieee80211_tkip_data *_priv = priv; + if (_priv && _priv->tfm_michael) + crypto_free_tfm(_priv->tfm_michael); + if (_priv && _priv->tfm_arc4) + crypto_free_tfm(_priv->tfm_arc4); + kfree(priv); +} + + +static inline u16 RotR1(u16 val) +{ + return (val >> 1) | (val << 15); +} + + +static inline u8 Lo8(u16 val) +{ + return val & 0xff; +} + + +static inline u8 Hi8(u16 val) +{ + return val >> 8; +} + + +static inline u16 Lo16(u32 val) +{ + return val & 0xffff; +} + + +static inline u16 Hi16(u32 val) +{ + return val >> 16; +} + + +static inline u16 Mk16(u8 hi, u8 lo) +{ + return lo | (((u16) hi) << 8); +} + + +static inline u16 Mk16_le(u16 *v) +{ + return le16_to_cpu(*v); +} + + +static const u16 Sbox[256] = +{ + 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154, + 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A, + 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B, + 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B, + 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F, + 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F, + 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5, + 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F, + 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB, + 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397, + 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED, + 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A, + 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194, + 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3, + 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104, + 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D, + 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39, + 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695, + 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83, + 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76, + 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4, + 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B, + 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0, + 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018, + 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751, + 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85, + 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12, + 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9, + 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7, + 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A, + 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8, + 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A, +}; + + +static inline u16 _S_(u16 v) +{ + u16 t = Sbox[Hi8(v)]; + return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8)); +} + + +#define PHASE1_LOOP_COUNT 8 + +static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32) +{ + int i, j; + + /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */ + TTAK[0] = Lo16(IV32); + TTAK[1] = Hi16(IV32); + TTAK[2] = Mk16(TA[1], TA[0]); + TTAK[3] = Mk16(TA[3], TA[2]); + TTAK[4] = Mk16(TA[5], TA[4]); + + for (i = 0; i < PHASE1_LOOP_COUNT; i++) { + j = 2 * (i & 1); + TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j])); + TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j])); + TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j])); + TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j])); + TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i; + } +} + + +static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK, + u16 IV16) +{ + /* Make temporary area overlap WEP seed so that the final copy can be + * avoided on little endian hosts. */ + u16 *PPK = (u16 *) &WEPSeed[4]; + + /* Step 1 - make copy of TTAK and bring in TSC */ + PPK[0] = TTAK[0]; + PPK[1] = TTAK[1]; + PPK[2] = TTAK[2]; + PPK[3] = TTAK[3]; + PPK[4] = TTAK[4]; + PPK[5] = TTAK[4] + IV16; + + /* Step 2 - 96-bit bijective mixing using S-box */ + PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0])); + PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2])); + PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4])); + PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6])); + PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8])); + PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10])); + + PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12])); + PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14])); + PPK[2] += RotR1(PPK[1]); + PPK[3] += RotR1(PPK[2]); + PPK[4] += RotR1(PPK[3]); + PPK[5] += RotR1(PPK[4]); + + /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value + * WEPSeed[0..2] is transmitted as WEP IV */ + WEPSeed[0] = Hi8(IV16); + WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F; + WEPSeed[2] = Lo8(IV16); + WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1); + +#ifdef __BIG_ENDIAN + { + int i; + for (i = 0; i < 6; i++) + PPK[i] = (PPK[i] << 8) | (PPK[i] >> 8); + } +#endif +} + +static int ieee80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + int len; + u8 rc4key[16], *pos, *icv; + struct ieee80211_hdr *hdr; + u32 crc; + struct scatterlist sg; + + if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + hdr = (struct ieee80211_hdr *) skb->data; + if (!tkey->tx_phase1_done) { + tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2, + tkey->tx_iv32); + tkey->tx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16); + + len = skb->len - hdr_len; + pos = skb_push(skb, 8); + memmove(pos, pos + 8, hdr_len); + pos += hdr_len; + icv = skb_put(skb, 4); + + *pos++ = rc4key[0]; + *pos++ = rc4key[1]; + *pos++ = rc4key[2]; + *pos++ = (tkey->key_idx << 6) | (1 << 5) /* Ext IV included */; + *pos++ = tkey->tx_iv32 & 0xff; + *pos++ = (tkey->tx_iv32 >> 8) & 0xff; + *pos++ = (tkey->tx_iv32 >> 16) & 0xff; + *pos++ = (tkey->tx_iv32 >> 24) & 0xff; + + crc = ~crc32_le(~0, pos, len); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(tkey->tfm_arc4, &sg, &sg, len + 4); + + tkey->tx_iv16++; + if (tkey->tx_iv16 == 0) { + tkey->tx_phase1_done = 0; + tkey->tx_iv32++; + } + + return 0; +} + +static int ieee80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 rc4key[16]; + u8 keyidx, *pos; + u32 iv32; + u16 iv16; + struct ieee80211_hdr *hdr; + u8 icv[4]; + u32 crc; + struct scatterlist sg; + int plen; + + if (skb->len < hdr_len + 8 + 4) + return -1; + + hdr = (struct ieee80211_hdr *) skb->data; + pos = skb->data + hdr_len; + keyidx = pos[3]; + if (!(keyidx & (1 << 5))) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet without ExtIV" + " flag from " MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + return -2; + } + keyidx >>= 6; + if (tkey->key_idx != keyidx) { + printk(KERN_DEBUG "TKIP: RX tkey->key_idx=%d frame " + "keyidx=%d priv=%p\n", tkey->key_idx, keyidx, priv); + return -6; + } + if (!tkey->key_set) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: received packet from " MAC_FMT + " with keyid=%d that does not have a configured" + " key\n", MAC_ARG(hdr->addr2), keyidx); + } + return -3; + } + iv16 = (pos[0] << 8) | pos[2]; + iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24); + pos += 8; + + if (iv32 < tkey->rx_iv32 || + (iv32 == tkey->rx_iv32 && iv16 <= tkey->rx_iv16)) { + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: replay detected: STA=" MAC_FMT + " previous TSC %08x%04x received TSC " + "%08x%04x\n", MAC_ARG(hdr->addr2), + tkey->rx_iv32, tkey->rx_iv16, iv32, iv16); + } + tkey->dot11RSNAStatsTKIPReplays++; + return -4; + } + + if (iv32 != tkey->rx_iv32 || !tkey->rx_phase1_done) { + tkip_mixing_phase1(tkey->rx_ttak, tkey->key, hdr->addr2, iv32); + tkey->rx_phase1_done = 1; + } + tkip_mixing_phase2(rc4key, tkey->key, tkey->rx_ttak, iv16); + + plen = skb->len - hdr_len - 12; + + crypto_cipher_setkey(tkey->tfm_arc4, rc4key, 16); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(tkey->tfm_arc4, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + if (iv32 != tkey->rx_iv32) { + /* Previously cached Phase1 result was already lost, so + * it needs to be recalculated for the next packet. */ + tkey->rx_phase1_done = 0; + } + if (net_ratelimit()) { + printk(KERN_DEBUG "TKIP: ICV error detected: STA=" + MAC_FMT "\n", MAC_ARG(hdr->addr2)); + } + tkey->dot11RSNAStatsTKIPICVErrors++; + return -5; + } + + /* Update real counters only after Michael MIC verification has + * completed */ + tkey->rx_iv32_new = iv32; + tkey->rx_iv16_new = iv16; + + /* Remove IV and ICV */ + memmove(skb->data + 8, skb->data, hdr_len); + skb_pull(skb, 8); + skb_trim(skb, skb->len - 4); + + return keyidx; +} + + +static int michael_mic(struct ieee80211_tkip_data *tkey, u8 *key, u8 *hdr, + u8 *data, size_t data_len, u8 *mic) +{ + struct scatterlist sg[2]; + + if (tkey->tfm_michael == NULL) { + printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + return -1; + } + sg[0].page = virt_to_page(hdr); + sg[0].offset = offset_in_page(hdr); + sg[0].length = 16; + + sg[1].page = virt_to_page(data); + sg[1].offset = offset_in_page(data); + sg[1].length = data_len; + + crypto_digest_init(tkey->tfm_michael); + crypto_digest_setkey(tkey->tfm_michael, key, 8); + crypto_digest_update(tkey->tfm_michael, sg, 2); + crypto_digest_final(tkey->tfm_michael, mic); + + return 0; +} + +static void michael_mic_hdr(struct sk_buff *skb, u8 *hdr) +{ + struct ieee80211_hdr *hdr11; + + hdr11 = (struct ieee80211_hdr *) skb->data; + switch (le16_to_cpu(hdr11->frame_ctl) & + (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + case IEEE80211_FCTL_FROMDS: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */ + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr4, ETH_ALEN); /* SA */ + break; + case 0: + memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */ + memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */ + break; + } + + hdr[12] = 0; /* priority */ + hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */ +} + + +static int ieee80211_michael_mic_add(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 *pos; + + if (skb_tailroom(skb) < 8 || skb->len < hdr_len) { + printk(KERN_DEBUG "Invalid packet for Michael MIC add " + "(tailroom=%d hdr_len=%d skb->len=%d)\n", + skb_tailroom(skb), hdr_len, skb->len); + return -1; + } + + michael_mic_hdr(skb, tkey->tx_hdr); + pos = skb_put(skb, 8); + if (michael_mic(tkey, &tkey->key[16], tkey->tx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, pos)) + return -1; + + return 0; +} + + +#if WIRELESS_EXT >= 18 +static void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + struct iw_michaelmicfailure ev; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + memset(&ev, 0, sizeof(ev)); + ev.flags = keyidx & IW_MICFAILURE_KEY_ID; + if (hdr->addr1[0] & 0x01) + ev.flags |= IW_MICFAILURE_GROUP; + else + ev.flags |= IW_MICFAILURE_PAIRWISE; + ev.src_addr.sa_family = ARPHRD_ETHER; + memcpy(ev.src_addr.sa_data, hdr->addr2, ETH_ALEN); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = sizeof(ev); + wireless_send_event(dev, IWEVMICHAELMICFAILURE, &wrqu, (char *) &ev); +} +#elif WIRELESS_EXT >= 15 +static void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ + union iwreq_data wrqu; + char buf[128]; + + /* TODO: needed parameters: count, keyid, key type, TSC */ + sprintf(buf, "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=" + MAC_FMT ")", keyidx, hdr->addr1[0] & 0x01 ? "broad" : "uni", + MAC_ARG(hdr->addr2)); + memset(&wrqu, 0, sizeof(wrqu)); + wrqu.data.length = strlen(buf); + wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf); +} +#else /* WIRELESS_EXT >= 15 */ +static inline void ieee80211_michael_mic_failure(struct net_device *dev, + struct ieee80211_hdr *hdr, + int keyidx) +{ +} +#endif /* WIRELESS_EXT >= 15 */ + + +static int ieee80211_michael_mic_verify(struct sk_buff *skb, int keyidx, + int hdr_len, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + u8 mic[8]; + + if (!tkey->key_set) + return -1; + + michael_mic_hdr(skb, tkey->rx_hdr); + if (michael_mic(tkey, &tkey->key[24], tkey->rx_hdr, + skb->data + hdr_len, skb->len - 8 - hdr_len, mic)) + return -1; + if (memcmp(mic, skb->data + skb->len - 8, 8) != 0) { + struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *) skb->data; + printk(KERN_DEBUG "%s: Michael MIC verification failed for " + "MSDU from " MAC_FMT " keyidx=%d\n", + skb->dev ? skb->dev->name : "N/A", MAC_ARG(hdr->addr2), + keyidx); + if (skb->dev) + ieee80211_michael_mic_failure(skb->dev, hdr, keyidx); + tkey->dot11RSNAStatsTKIPLocalMICFailures++; + return -1; + } + + /* Update TSC counters for RX now that the packet verification has + * completed. */ + tkey->rx_iv32 = tkey->rx_iv32_new; + tkey->rx_iv16 = tkey->rx_iv16_new; + + skb_trim(skb, skb->len - 8); + + return 0; +} + + +static int ieee80211_tkip_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + int keyidx; + struct crypto_tfm *tfm = tkey->tfm_michael; + struct crypto_tfm *tfm2 = tkey->tfm_arc4; + + keyidx = tkey->key_idx; + memset(tkey, 0, sizeof(*tkey)); + tkey->key_idx = keyidx; + tkey->tfm_michael = tfm; + tkey->tfm_arc4 = tfm2; + if (len == TKIP_KEY_LEN) { + memcpy(tkey->key, key, TKIP_KEY_LEN); + tkey->key_set = 1; + tkey->tx_iv16 = 1; /* TSC is initialized to 1 */ + if (seq) { + tkey->rx_iv32 = (seq[5] << 24) | (seq[4] << 16) | + (seq[3] << 8) | seq[2]; + tkey->rx_iv16 = (seq[1] << 8) | seq[0]; + } + } else if (len == 0) + tkey->key_set = 0; + else + return -1; + + return 0; +} + + +static int ieee80211_tkip_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct ieee80211_tkip_data *tkey = priv; + + if (len < TKIP_KEY_LEN) + return -1; + + if (!tkey->key_set) + return 0; + memcpy(key, tkey->key, TKIP_KEY_LEN); + + if (seq) { + /* Return the sequence number of the last transmitted frame. */ + u16 iv16 = tkey->tx_iv16; + u32 iv32 = tkey->tx_iv32; + if (iv16 == 0) + iv32--; + iv16--; + seq[0] = tkey->tx_iv16; + seq[1] = tkey->tx_iv16 >> 8; + seq[2] = tkey->tx_iv32; + seq[3] = tkey->tx_iv32 >> 8; + seq[4] = tkey->tx_iv32 >> 16; + seq[5] = tkey->tx_iv32 >> 24; + } + + return TKIP_KEY_LEN; +} + + +static char * ieee80211_tkip_print_stats(char *p, void *priv) +{ + struct ieee80211_tkip_data *tkip = priv; + p += sprintf(p, "key[%d] alg=TKIP key_set=%d " + "tx_pn=%02x%02x%02x%02x%02x%02x " + "rx_pn=%02x%02x%02x%02x%02x%02x " + "replays=%d icv_errors=%d local_mic_failures=%d\n", + tkip->key_idx, tkip->key_set, + (tkip->tx_iv32 >> 24) & 0xff, + (tkip->tx_iv32 >> 16) & 0xff, + (tkip->tx_iv32 >> 8) & 0xff, + tkip->tx_iv32 & 0xff, + (tkip->tx_iv16 >> 8) & 0xff, + tkip->tx_iv16 & 0xff, + (tkip->rx_iv32 >> 24) & 0xff, + (tkip->rx_iv32 >> 16) & 0xff, + (tkip->rx_iv32 >> 8) & 0xff, + tkip->rx_iv32 & 0xff, + (tkip->rx_iv16 >> 8) & 0xff, + tkip->rx_iv16 & 0xff, + tkip->dot11RSNAStatsTKIPReplays, + tkip->dot11RSNAStatsTKIPICVErrors, + tkip->dot11RSNAStatsTKIPLocalMICFailures); + return p; +} + + +static struct ieee80211_crypto_ops ieee80211_crypt_tkip = { + .name = "TKIP", + .init = ieee80211_tkip_init, + .deinit = ieee80211_tkip_deinit, + .encrypt_mpdu = ieee80211_tkip_encrypt, + .decrypt_mpdu = ieee80211_tkip_decrypt, + .encrypt_msdu = ieee80211_michael_mic_add, + .decrypt_msdu = ieee80211_michael_mic_verify, + .set_key = ieee80211_tkip_set_key, + .get_key = ieee80211_tkip_get_key, + .print_stats = ieee80211_tkip_print_stats, + .extra_prefix_len = 4 + 4, /* IV + ExtIV */ + .extra_postfix_len = 8 + 4, /* MIC + ICV */ + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_tkip_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_tkip); +} + + +static void __exit ieee80211_crypto_tkip_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_tkip); +} + + +module_init(ieee80211_crypto_tkip_init); +module_exit(ieee80211_crypto_tkip_exit); diff -Nru a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_crypt_wep.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,272 @@ +/* + * Host AP crypt: host-based WEP encryption implementation for Host AP driver + * + * Copyright (c) 2002-2004, Jouni Malinen + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include +#include +#include + +MODULE_AUTHOR("Jouni Malinen"); +MODULE_DESCRIPTION("Host AP crypt: WEP"); +MODULE_LICENSE("GPL"); + + +struct prism2_wep_data { + u32 iv; +#define WEP_KEY_LEN 13 + u8 key[WEP_KEY_LEN + 1]; + u8 key_len; + u8 key_idx; + struct crypto_tfm *tfm; +}; + + +static void * prism2_wep_init(int keyidx) +{ + struct prism2_wep_data *priv; + + priv = kmalloc(sizeof(*priv), GFP_ATOMIC); + if (priv == NULL) + goto fail; + memset(priv, 0, sizeof(*priv)); + priv->key_idx = keyidx; + + priv->tfm = crypto_alloc_tfm("arc4", 0); + if (priv->tfm == NULL) { + printk(KERN_DEBUG "ieee80211_crypt_wep: could not allocate " + "crypto API arc4\n"); + goto fail; + } + + /* start WEP IV from a random value */ + get_random_bytes(&priv->iv, 4); + + return priv; + +fail: + if (priv) { + if (priv->tfm) + crypto_free_tfm(priv->tfm); + kfree(priv); + } + return NULL; +} + + +static void prism2_wep_deinit(void *priv) +{ + struct prism2_wep_data *_priv = priv; + if (_priv && _priv->tfm) + crypto_free_tfm(_priv->tfm); + kfree(priv); +} + + +/* Perform WEP encryption on given skb that has at least 4 bytes of headroom + * for IV and 4 bytes of tailroom for ICV. Both IV and ICV will be transmitted, + * so the payload length increases with 8 bytes. + * + * WEP frame payload: IV + TX key idx, RC4(data), ICV = RC4(CRC32(data)) + */ +static int prism2_wep_encrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, len; + u8 key[WEP_KEY_LEN + 3]; + u8 *pos, *icv; + struct scatterlist sg; + + if (skb_headroom(skb) < 4 || skb_tailroom(skb) < 4 || + skb->len < hdr_len) + return -1; + + len = skb->len - hdr_len; + pos = skb_push(skb, 4); + memmove(pos, pos + 4, hdr_len); + pos += hdr_len; + + klen = 3 + wep->key_len; + + wep->iv++; + + /* Fluhrer, Mantin, and Shamir have reported weaknesses in the key + * scheduling algorithm of RC4. At least IVs (KeyByte + 3, 0xff, N) + * can be used to speedup attacks, so avoid using them. */ + if ((wep->iv & 0xff00) == 0xff00) { + u8 B = (wep->iv >> 16) & 0xff; + if (B >= 3 && B < klen) + wep->iv += 0x0100; + } + + /* Prepend 24-bit IV to RC4 key and TX frame */ + *pos++ = key[0] = (wep->iv >> 16) & 0xff; + *pos++ = key[1] = (wep->iv >> 8) & 0xff; + *pos++ = key[2] = wep->iv & 0xff; + *pos++ = wep->key_idx << 6; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Append little-endian CRC32 and encrypt it to produce ICV */ + crc = ~crc32_le(~0, pos, len); + icv = skb_put(skb, 4); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = len + 4; + crypto_cipher_encrypt(wep->tfm, &sg, &sg, len + 4); + + return 0; +} + + +/* Perform WEP decryption on given buffer. Buffer includes whole WEP part of + * the frame: IV (4 bytes), encrypted payload (including SNAP header), + * ICV (4 bytes). len includes both IV and ICV. + * + * Returns 0 if frame was decrypted successfully and ICV was correct and -1 on + * failure. If frame is OK, IV and ICV will be removed. + */ +static int prism2_wep_decrypt(struct sk_buff *skb, int hdr_len, void *priv) +{ + struct prism2_wep_data *wep = priv; + u32 crc, klen, plen; + u8 key[WEP_KEY_LEN + 3]; + u8 keyidx, *pos, icv[4]; + struct scatterlist sg; + + if (skb->len < hdr_len + 8) + return -1; + + pos = skb->data + hdr_len; + key[0] = *pos++; + key[1] = *pos++; + key[2] = *pos++; + keyidx = *pos++ >> 6; + if (keyidx != wep->key_idx) + return -1; + + klen = 3 + wep->key_len; + + /* Copy rest of the WEP key (the secret part) */ + memcpy(key + 3, wep->key, wep->key_len); + + /* Apply RC4 to data and compute CRC32 over decrypted data */ + plen = skb->len - hdr_len - 8; + + crypto_cipher_setkey(wep->tfm, key, klen); + sg.page = virt_to_page(pos); + sg.offset = offset_in_page(pos); + sg.length = plen + 4; + crypto_cipher_decrypt(wep->tfm, &sg, &sg, plen + 4); + + crc = ~crc32_le(~0, pos, plen); + icv[0] = crc; + icv[1] = crc >> 8; + icv[2] = crc >> 16; + icv[3] = crc >> 24; + if (memcmp(icv, pos + plen, 4) != 0) { + /* ICV mismatch - drop frame */ + return -2; + } + + /* Remove IV and ICV */ + memmove(skb->data + 4, skb->data, hdr_len); + skb_pull(skb, 4); + skb_trim(skb, skb->len - 4); + + return 0; +} + + +static int prism2_wep_set_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < 0 || len > WEP_KEY_LEN) + return -1; + + memcpy(wep->key, key, len); + wep->key_len = len; + + return 0; +} + + +static int prism2_wep_get_key(void *key, int len, u8 *seq, void *priv) +{ + struct prism2_wep_data *wep = priv; + + if (len < wep->key_len) + return -1; + + memcpy(key, wep->key, wep->key_len); + + return wep->key_len; +} + + +static char * prism2_wep_print_stats(char *p, void *priv) +{ + struct prism2_wep_data *wep = priv; + p += sprintf(p, "key[%d] alg=WEP len=%d\n", + wep->key_idx, wep->key_len); + return p; +} + + +static struct ieee80211_crypto_ops ieee80211_crypt_wep = { + .name = "WEP", + .init = prism2_wep_init, + .deinit = prism2_wep_deinit, + .encrypt_mpdu = prism2_wep_encrypt, + .decrypt_mpdu = prism2_wep_decrypt, + .encrypt_msdu = NULL, + .decrypt_msdu = NULL, + .set_key = prism2_wep_set_key, + .get_key = prism2_wep_get_key, + .print_stats = prism2_wep_print_stats, + .extra_prefix_len = 4, /* IV */ + .extra_postfix_len = 4, /* ICV */ + .owner = THIS_MODULE, +}; + + +static int __init ieee80211_crypto_wep_init(void) +{ + return ieee80211_register_crypto_ops(&ieee80211_crypt_wep); +} + + +static void __exit ieee80211_crypto_wep_exit(void) +{ + ieee80211_unregister_crypto_ops(&ieee80211_crypt_wep); +} + + +module_init(ieee80211_crypto_wep_init); +module_exit(ieee80211_crypto_wep_exit); diff -Nru a/net/ieee80211/ieee80211_module.c b/net/ieee80211/ieee80211_module.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_module.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,268 @@ +/******************************************************************************* + + Copyright(c) 2004 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + 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., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +MODULE_DESCRIPTION("802.11 data/management/control stack"); +MODULE_AUTHOR("Copyright (C) 2004 Intel Corporation "); +MODULE_LICENSE("GPL"); + +#define DRV_NAME "ieee80211" + +static inline int ieee80211_networks_allocate(struct ieee80211_device *ieee) +{ + if (ieee->networks) + return 0; + + ieee->networks = kmalloc( + MAX_NETWORK_COUNT * sizeof(struct ieee80211_network), + GFP_KERNEL); + if (!ieee->networks) { + printk(KERN_WARNING "%s: Out of memory allocating beacons\n", + ieee->dev->name); + return -ENOMEM; + } + + memset(ieee->networks, 0, + MAX_NETWORK_COUNT * sizeof(struct ieee80211_network)); + + return 0; +} + +static inline void ieee80211_networks_free(struct ieee80211_device *ieee) +{ + if (!ieee->networks) + return; + kfree(ieee->networks); + ieee->networks = NULL; +} + +static inline void ieee80211_networks_initialize(struct ieee80211_device *ieee) +{ + int i; + + INIT_LIST_HEAD(&ieee->network_free_list); + INIT_LIST_HEAD(&ieee->network_list); + for (i = 0; i < MAX_NETWORK_COUNT; i++) + list_add_tail(&ieee->networks[i].list, &ieee->network_free_list); +} + + +struct net_device *alloc_ieee80211(int sizeof_priv) +{ + struct ieee80211_device *ieee; + struct net_device *dev; + int err; + + IEEE80211_DEBUG_INFO("Initializing...\n"); + + dev = alloc_etherdev(sizeof(struct ieee80211_device) + sizeof_priv); + if (!dev) { + IEEE80211_ERROR("Unable to network device.\n"); + goto failed; + } + ieee = netdev_priv(dev); + dev->hard_start_xmit = ieee80211_xmit; + + ieee->dev = dev; + + err = ieee80211_networks_allocate(ieee); + if (err) { + IEEE80211_ERROR("Unable to allocate beacon storage: %d\n", + err); + goto failed; + } + ieee80211_networks_initialize(ieee); + + /* Default fragmentation threshold is maximum payload size */ + ieee->fts = DEFAULT_FTS; + ieee->scan_age = DEFAULT_MAX_SCAN_AGE; + ieee->open_wep = 1; + + /* Default to enabling full open WEP with host based encrypt/decrypt */ + ieee->host_encrypt = 1; + ieee->host_decrypt = 1; + ieee->ieee802_1x = 1; /* Default to supporting 802.1x */ + + INIT_LIST_HEAD(&ieee->crypt_deinit_list); + init_timer(&ieee->crypt_deinit_timer); + ieee->crypt_deinit_timer.data = (unsigned long)ieee; + ieee->crypt_deinit_timer.function = ieee80211_crypt_deinit_handler; + + spin_lock_init(&ieee->lock); + + ieee->wpa_enabled = 0; + ieee->tkip_countermeasures = 0; + ieee->drop_unencrypted = 0; + ieee->privacy_invoked = 0; + ieee->ieee802_1x = 1; + + return dev; + + failed: + if (dev) + free_netdev(dev); + return NULL; +} + + +void free_ieee80211(struct net_device *dev) +{ + struct ieee80211_device *ieee = netdev_priv(dev); + + int i; + + del_timer_sync(&ieee->crypt_deinit_timer); + ieee80211_crypt_deinit_entries(ieee, 1); + + for (i = 0; i < WEP_KEYS; i++) { + struct ieee80211_crypt_data *crypt = ieee->crypt[i]; + if (crypt) { + if (crypt->ops) { + crypt->ops->deinit(crypt->priv); + module_put(crypt->ops->owner); + } + kfree(crypt); + ieee->crypt[i] = NULL; + } + } + + ieee80211_networks_free(ieee); + free_netdev(dev); +} + +#ifdef CONFIG_IEEE80211_DEBUG + +static int debug = 0; +u32 ieee80211_debug_level = 0; +struct proc_dir_entry *ieee80211_proc = NULL; + +static int show_debug_level(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + return snprintf(page, count, "0x%08X\n", ieee80211_debug_level); +} + +static int store_debug_level(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char buf[] = "0x00000000"; + unsigned long len = min(sizeof(buf) - 1, (u32)count); + char *p = (char *)buf; + unsigned long val; + + if (copy_from_user(buf, buffer, len)) + return count; + buf[len] = 0; + if (p[1] == 'x' || p[1] == 'X' || p[0] == 'x' || p[0] == 'X') { + p++; + if (p[0] == 'x' || p[0] == 'X') + p++; + val = simple_strtoul(p, &p, 16); + } else + val = simple_strtoul(p, &p, 10); + if (p == buf) + printk(KERN_INFO DRV_NAME + ": %s is not in hex or decimal form.\n", buf); + else + ieee80211_debug_level = val; + + return strnlen(buf, count); +} + +static int __init ieee80211_init(void) +{ + struct proc_dir_entry *e; + + ieee80211_debug_level = debug; + ieee80211_proc = create_proc_entry(DRV_NAME, S_IFDIR, proc_net); + if (ieee80211_proc == NULL) { + IEEE80211_ERROR("Unable to create " DRV_NAME + " proc directory\n"); + return -EIO; + } + e = create_proc_entry("debug_level", S_IFREG | S_IRUGO | S_IWUSR, + ieee80211_proc); + if (!e) { + remove_proc_entry(DRV_NAME, proc_net); + ieee80211_proc = NULL; + return -EIO; + } + e->read_proc = show_debug_level; + e->write_proc = store_debug_level; + e->data = NULL; + + return 0; +} + +static void __exit ieee80211_exit(void) +{ + if (ieee80211_proc) { + remove_proc_entry("debug_level", ieee80211_proc); + remove_proc_entry(DRV_NAME, proc_net); + ieee80211_proc = NULL; + } +} + +#include +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "debug output mask"); + + +module_exit(ieee80211_exit); +module_init(ieee80211_init); +#endif + +EXPORT_SYMBOL(alloc_ieee80211); +EXPORT_SYMBOL(free_ieee80211); diff -Nru a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_rx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,1206 @@ +/* + * Original code based Host AP (software wireless LAN access point) driver + * for Intersil Prism2/2.5/3 - hostap.o module, common routines + * + * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + * + * Copyright (c) 2002-2003, Jouni Malinen + * Copyright (c) 2004, Intel Corporation + * + * 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. See README and COPYING for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static inline void ieee80211_monitor_rx(struct ieee80211_device *ieee, + struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + u16 fc = le16_to_cpu(hdr->frame_ctl); + + skb->dev = ieee->dev; + skb->mac.raw = skb->data; + skb_pull(skb, ieee80211_get_hdrlen(fc)); + skb->pkt_type = PACKET_OTHERHOST; + skb->protocol = __constant_htons(ETH_P_80211_RAW); + memset(skb->cb, 0, sizeof(skb->cb)); + netif_rx(skb); +} + + +/* Called only as a tasklet (software IRQ) */ +static struct ieee80211_frag_entry * +ieee80211_frag_cache_find(struct ieee80211_device *ieee, unsigned int seq, + unsigned int frag, u8 *src, u8 *dst) +{ + struct ieee80211_frag_entry *entry; + int i; + + for (i = 0; i < IEEE80211_FRAG_CACHE_LEN; i++) { + entry = &ieee->frag_cache[i]; + if (entry->skb != NULL && + time_after(jiffies, entry->first_frag_time + 2 * HZ)) { + IEEE80211_DEBUG_FRAG( + "expiring fragment cache entry " + "seq=%u last_frag=%u\n", + entry->seq, entry->last_frag); + dev_kfree_skb_any(entry->skb); + entry->skb = NULL; + } + + if (entry->skb != NULL && entry->seq == seq && + (entry->last_frag + 1 == frag || frag == -1) && + memcmp(entry->src_addr, src, ETH_ALEN) == 0 && + memcmp(entry->dst_addr, dst, ETH_ALEN) == 0) + return entry; + } + + return NULL; +} + +/* Called only as a tasklet (software IRQ) */ +static struct sk_buff * +ieee80211_frag_cache_get(struct ieee80211_device *ieee, + struct ieee80211_hdr *hdr) +{ + struct sk_buff *skb = NULL; + u16 sc; + unsigned int frag, seq; + struct ieee80211_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + seq = WLAN_GET_SEQ_SEQ(sc); + + if (frag == 0) { + /* Reserve enough space to fit maximum frame length */ + skb = dev_alloc_skb(ieee->dev->mtu + + sizeof(struct ieee80211_hdr) + + 8 /* LLC */ + + 2 /* alignment */ + + 8 /* WEP */ + ETH_ALEN /* WDS */); + if (skb == NULL) + return NULL; + + entry = &ieee->frag_cache[ieee->frag_next_idx]; + ieee->frag_next_idx++; + if (ieee->frag_next_idx >= IEEE80211_FRAG_CACHE_LEN) + ieee->frag_next_idx = 0; + + if (entry->skb != NULL) + dev_kfree_skb_any(entry->skb); + + entry->first_frag_time = jiffies; + entry->seq = seq; + entry->last_frag = frag; + entry->skb = skb; + memcpy(entry->src_addr, hdr->addr2, ETH_ALEN); + memcpy(entry->dst_addr, hdr->addr1, ETH_ALEN); + } else { + /* received a fragment of a frame for which the head fragment + * should have already been received */ + entry = ieee80211_frag_cache_find(ieee, seq, frag, hdr->addr2, + hdr->addr1); + if (entry != NULL) { + entry->last_frag = frag; + skb = entry->skb; + } + } + + return skb; +} + + +/* Called only as a tasklet (software IRQ) */ +static int ieee80211_frag_cache_invalidate(struct ieee80211_device *ieee, + struct ieee80211_hdr *hdr) +{ + u16 sc; + unsigned int seq; + struct ieee80211_frag_entry *entry; + + sc = le16_to_cpu(hdr->seq_ctl); + seq = WLAN_GET_SEQ_SEQ(sc); + + entry = ieee80211_frag_cache_find(ieee, seq, -1, hdr->addr2, + hdr->addr1); + + if (entry == NULL) { + IEEE80211_DEBUG_FRAG( + "could not invalidate fragment cache " + "entry (seq=%u)\n", seq); + return -1; + } + + entry->skb = NULL; + return 0; +} + + +#ifdef NOT_YET +/* ieee80211_rx_frame_mgtmt + * + * Responsible for handling management control frames + * + * Called by ieee80211_rx */ +static inline int +ieee80211_rx_frame_mgmt(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats, u16 type, + u16 stype) +{ + if (ieee->iw_mode == IW_MODE_MASTER) { + printk(KERN_DEBUG "%s: Master mode not yet suppported.\n", + ieee->dev->name); + return 0; +/* + hostap_update_sta_ps(ieee, (struct hostap_ieee80211_hdr *) + skb->data);*/ + } + + if (ieee->hostapd && type == WLAN_FC_TYPE_MGMT) { + if (stype == WLAN_FC_STYPE_BEACON && + ieee->iw_mode == IW_MODE_MASTER) { + struct sk_buff *skb2; + /* Process beacon frames also in kernel driver to + * update STA(AP) table statistics */ + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2) + hostap_rx(skb2->dev, skb2, rx_stats); + } + + /* send management frames to the user space daemon for + * processing */ + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + prism2_rx_80211(ieee->apdev, skb, rx_stats, PRISM2_RX_MGMT); + return 0; + } + + if (ieee->iw_mode == IW_MODE_MASTER) { + if (type != WLAN_FC_TYPE_MGMT && type != WLAN_FC_TYPE_CTRL) { + printk(KERN_DEBUG "%s: unknown management frame " + "(type=0x%02x, stype=0x%02x) dropped\n", + skb->dev->name, type, stype); + return -1; + } + + hostap_rx(skb->dev, skb, rx_stats); + return 0; + } + + printk(KERN_DEBUG "%s: hostap_rx_frame_mgmt: management frame " + "received in non-Host AP mode\n", skb->dev->name); + return -1; +} +#endif + + +/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ +/* Ethernet-II snap header (RFC1042 for most EtherTypes) */ +static unsigned char rfc1042_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +/* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ +static unsigned char bridge_tunnel_header[] = +{ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; +/* No encapsulation header if EtherType < 0x600 (=length) */ + +/* Called by ieee80211_rx_frame_decrypt */ +static int ieee80211_is_eapol_frame(struct ieee80211_device *ieee, + struct sk_buff *skb) +{ + struct net_device *dev = ieee->dev; + u16 fc, ethertype; + struct ieee80211_hdr *hdr; + u8 *pos; + + if (skb->len < 24) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + fc = le16_to_cpu(hdr->frame_ctl); + + /* check that the frame is unicast frame to us */ + if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0 && + memcmp(hdr->addr3, dev->dev_addr, ETH_ALEN) == 0) { + /* ToDS frame with own addr BSSID and DA */ + } else if ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_FROMDS && + memcmp(hdr->addr1, dev->dev_addr, ETH_ALEN) == 0) { + /* FromDS frame with own addr as DA */ + } else + return 0; + + if (skb->len < 24 + 8) + return 0; + + /* check for port access entity Ethernet type */ + pos = skb->data + 24; + ethertype = (pos[6] << 8) | pos[7]; + if (ethertype == ETH_P_PAE) + return 1; + + return 0; +} + +/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +static inline int +ieee80211_rx_frame_decrypt(struct ieee80211_device* ieee, struct sk_buff *skb, + struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_mpdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + +#ifdef CONFIG_IEEE80211_CRYPT_TKIP + if (ieee->tkip_countermeasures && + strcmp(crypt->ops->name, "TKIP") == 0) { + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "received packet from " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(hdr->addr2)); + } + return -1; + } +#endif + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + IEEE80211_DEBUG_DROP( + "decryption failed (SA=" MAC_FMT + ") res=%d\n", MAC_ARG(hdr->addr2), res); + if (res == -2) + IEEE80211_DEBUG_DROP("Decryption failed ICV " + "mismatch (key %d)\n", + skb->data[hdrlen + 3] >> 6); + ieee->ieee_stats.rx_discards_undecryptable++; + return -1; + } + + return res; +} + + +/* Called only as a tasklet (software IRQ), by ieee80211_rx */ +static inline int +ieee80211_rx_frame_decrypt_msdu(struct ieee80211_device* ieee, struct sk_buff *skb, + int keyidx, struct ieee80211_crypt_data *crypt) +{ + struct ieee80211_hdr *hdr; + int res, hdrlen; + + if (crypt == NULL || crypt->ops->decrypt_msdu == NULL) + return 0; + + hdr = (struct ieee80211_hdr *) skb->data; + hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl)); + + atomic_inc(&crypt->refcnt); + res = crypt->ops->decrypt_msdu(skb, keyidx, hdrlen, crypt->priv); + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_DEBUG "%s: MSDU decryption/MIC verification failed" + " (SA=" MAC_FMT " keyidx=%d)\n", + ieee->dev->name, MAC_ARG(hdr->addr2), keyidx); + return -1; + } + + return 0; +} + + +/* All received frames are sent to this function. @skb contains the frame in + * IEEE 802.11 format, i.e., in the format it was sent over air. + * This function is called only as a tasklet (software IRQ). */ +int ieee80211_rx(struct ieee80211_device *ieee, struct sk_buff *skb, + struct ieee80211_rx_stats *rx_stats) +{ + struct net_device *dev = ieee->dev; + struct ieee80211_hdr *hdr; + size_t hdrlen; + u16 fc, type, stype, sc; + struct net_device_stats *stats; + unsigned int frag; + u8 *payload; + u16 ethertype; +#ifdef NOT_YET + struct net_device *wds = NULL; + struct sk_buff *skb2 = NULL; + struct net_device *wds = NULL; + int frame_authorized = 0; + int from_assoc_ap = 0; + void *sta = NULL; +#endif + u8 dst[ETH_ALEN]; + u8 src[ETH_ALEN]; + struct ieee80211_crypt_data *crypt = NULL; + int keyidx = 0; + + hdr = (struct ieee80211_hdr *)skb->data; + stats = &ieee->stats; + + if (skb->len < 10) { + printk(KERN_INFO "%s: SKB length < 10\n", + dev->name); + goto rx_dropped; + } + + fc = le16_to_cpu(hdr->frame_ctl); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + sc = le16_to_cpu(hdr->seq_ctl); + frag = WLAN_GET_SEQ_FRAG(sc); + hdrlen = ieee80211_get_hdrlen(fc); + +#ifdef NOT_YET +#if WIRELESS_EXT > 15 + /* Put this code here so that we avoid duplicating it in all + * Rx paths. - Jean II */ +#ifdef IW_WIRELESS_SPY /* defined in iw_handler.h */ + /* If spy monitoring on */ + if (iface->spy_data.spy_number > 0) { + struct iw_quality wstats; + wstats.level = rx_stats->signal; + wstats.noise = rx_stats->noise; + wstats.updated = 6; /* No qual value */ + /* Update spy records */ + wireless_spy_update(dev, hdr->addr2, &wstats); + } +#endif /* IW_WIRELESS_SPY */ +#endif /* WIRELESS_EXT > 15 */ + hostap_update_rx_stats(local->ap, hdr, rx_stats); +#endif + +#if WIRELESS_EXT > 15 + if (ieee->iw_mode == IW_MODE_MONITOR) { + ieee80211_monitor_rx(ieee, skb, rx_stats); + stats->rx_packets++; + stats->rx_bytes += skb->len; + return 1; + } +#endif + + if (ieee->host_decrypt) { + int idx = 0; + if (skb->len >= hdrlen + 3) + idx = skb->data[hdrlen + 3] >> 6; + crypt = ieee->crypt[idx]; +#ifdef NOT_YET + sta = NULL; + + /* Use station specific key to override default keys if the + * receiver address is a unicast address ("individual RA"). If + * bcrx_sta_key parameter is set, station specific key is used + * even with broad/multicast targets (this is against IEEE + * 802.11, but makes it easier to use different keys with + * stations that do not support WEP key mapping). */ + + if (!(hdr->addr1[0] & 0x01) || local->bcrx_sta_key) + (void) hostap_handle_sta_crypto(local, hdr, &crypt, + &sta); +#endif + + /* allow NULL decrypt to indicate an station specific override + * for default encryption */ + if (crypt && (crypt->ops == NULL || + crypt->ops->decrypt_mpdu == NULL)) + crypt = NULL; + + if (!crypt && (fc & IEEE80211_FCTL_WEP)) { + /* This seems to be triggered by some (multicast?) + * frames from other than current BSS, so just drop the + * frames silently instead of filling system log with + * these reports. */ + IEEE80211_DEBUG_DROP("Decryption failed (not set)" + " (SA=" MAC_FMT ")\n", + MAC_ARG(hdr->addr2)); + ieee->ieee_stats.rx_discards_undecryptable++; + goto rx_dropped; + } + } + +#ifdef NOT_YET + if (type != WLAN_FC_TYPE_DATA) { + if (type == WLAN_FC_TYPE_MGMT && stype == WLAN_FC_STYPE_AUTH && + fc & IEEE80211_FCTL_WEP && ieee->host_decrypt && + (keyidx = hostap_rx_frame_decrypt(ieee, skb, crypt)) < 0) + { + printk(KERN_DEBUG "%s: failed to decrypt mgmt::auth " + "from " MAC_FMT "\n", dev->name, + MAC_ARG(hdr->addr2)); + /* TODO: could inform hostapd about this so that it + * could send auth failure report */ + goto rx_dropped; + } + + if (ieee80211_rx_frame_mgmt(ieee, skb, rx_stats, type, stype)) + goto rx_dropped; + else + goto rx_exit; + } +#endif + + /* Data frame - extract src/dst addresses */ + if (skb->len < IEEE80211_DATA_HDR3_LEN) + goto rx_dropped; + + switch (fc & (IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS)) { + case IEEE80211_FCTL_FROMDS: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr3, ETH_ALEN); + break; + case IEEE80211_FCTL_TODS: + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + case IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS: + if (skb->len < IEEE80211_DATA_HDR4_LEN) + goto rx_dropped; + memcpy(dst, hdr->addr3, ETH_ALEN); + memcpy(src, hdr->addr4, ETH_ALEN); + break; + case 0: + memcpy(dst, hdr->addr1, ETH_ALEN); + memcpy(src, hdr->addr2, ETH_ALEN); + break; + } + +#ifdef NOT_YET + if (hostap_rx_frame_wds(ieee, hdr, fc, &wds)) + goto rx_dropped; + if (wds) { + skb->dev = dev = wds; + stats = hostap_get_stats(dev); + } + + if (ieee->iw_mode == IW_MODE_MASTER && !wds && + (fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS && + ieee->stadev && + memcmp(hdr->addr2, ieee->assoc_ap_addr, ETH_ALEN) == 0) { + /* Frame from BSSID of the AP for which we are a client */ + skb->dev = dev = ieee->stadev; + stats = hostap_get_stats(dev); + from_assoc_ap = 1; + } +#endif + + dev->last_rx = jiffies; + +#ifdef NOT_YET + if ((ieee->iw_mode == IW_MODE_MASTER || + ieee->iw_mode == IW_MODE_REPEAT) && + !from_assoc_ap) { + switch (hostap_handle_sta_rx(ieee, dev, skb, rx_stats, + wds != NULL)) { + case AP_RX_CONTINUE_NOT_AUTHORIZED: + frame_authorized = 0; + break; + case AP_RX_CONTINUE: + frame_authorized = 1; + break; + case AP_RX_DROP: + goto rx_dropped; + case AP_RX_EXIT: + goto rx_exit; + } + } +#endif + + /* Nullfunc frames may have PS-bit set, so they must be passed to + * hostap_handle_sta_rx() before being dropped here. */ + if (stype != IEEE80211_STYPE_DATA && + stype != IEEE80211_STYPE_DATA_CFACK && + stype != IEEE80211_STYPE_DATA_CFPOLL && + stype != IEEE80211_STYPE_DATA_CFACKPOLL) { + if (stype != IEEE80211_STYPE_NULLFUNC) + IEEE80211_DEBUG_DROP( + "RX: dropped data frame " + "with no data (type=0x%02x, " + "subtype=0x%02x, len=%d)\n", + type, stype, skb->len); + goto rx_dropped; + } + + /* skb: hdr + (possibly fragmented, possibly encrypted) payload */ + + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + (keyidx = ieee80211_rx_frame_decrypt(ieee, skb, crypt)) < 0) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + + /* skb: hdr + (possibly fragmented) plaintext payload */ + // PR: FIXME: hostap has additional conditions in the "if" below: + // ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + if ((frag != 0 || (fc & IEEE80211_FCTL_MOREFRAGS))) { + int flen; + struct sk_buff *frag_skb = ieee80211_frag_cache_get(ieee, hdr); + IEEE80211_DEBUG_FRAG("Rx Fragment received (%u)\n", frag); + + if (!frag_skb) { + IEEE80211_DEBUG(IEEE80211_DL_RX | IEEE80211_DL_FRAG, + "Rx cannot get skb from fragment " + "cache (morefrag=%d seq=%u frag=%u)\n", + (fc & IEEE80211_FCTL_MOREFRAGS) != 0, + WLAN_GET_SEQ_SEQ(sc), frag); + goto rx_dropped; + } + + flen = skb->len; + if (frag != 0) + flen -= hdrlen; + + if (frag_skb->tail + flen > frag_skb->end) { + printk(KERN_WARNING "%s: host decrypted and " + "reassembled frame did not fit skb\n", + dev->name); + ieee80211_frag_cache_invalidate(ieee, hdr); + goto rx_dropped; + } + + if (frag == 0) { + /* copy first fragment (including full headers) into + * beginning of the fragment cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data, flen); + } else { + /* append frame payload to the end of the fragment + * cache skb */ + memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, + flen); + } + dev_kfree_skb_any(skb); + skb = NULL; + + if (fc & IEEE80211_FCTL_MOREFRAGS) { + /* more fragments expected - leave the skb in fragment + * cache for now; it will be delivered to upper layers + * after all fragments have been received */ + goto rx_exit; + } + + /* this was the last fragment and the frame will be + * delivered, so remove skb from fragment cache */ + skb = frag_skb; + hdr = (struct ieee80211_hdr *) skb->data; + ieee80211_frag_cache_invalidate(ieee, hdr); + } + + /* skb: hdr + (possible reassembled) full MSDU payload; possibly still + * encrypted/authenticated */ + if (ieee->host_decrypt && (fc & IEEE80211_FCTL_WEP) && + ieee80211_rx_frame_decrypt_msdu(ieee, skb, keyidx, crypt)) + goto rx_dropped; + + hdr = (struct ieee80211_hdr *) skb->data; + if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep) { + if (/*ieee->ieee802_1x &&*/ + ieee80211_is_eapol_frame(ieee, skb)) { +#ifdef CONFIG_IEEE80211_DEBUG + /* pass unencrypted EAPOL frames even if encryption is + * configured */ + struct eapol *eap = (struct eapol *)(skb->data + + 24); + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); +#endif + } else { + IEEE80211_DEBUG_DROP( + "encryption configured, but RX " + "frame not encrypted (SA=" MAC_FMT ")\n", + MAC_ARG(hdr->addr2)); + goto rx_dropped; + } + } + +#ifdef CONFIG_IEEE80211_DEBUG + if (crypt && !(fc & IEEE80211_FCTL_WEP) && + ieee80211_is_eapol_frame(ieee, skb)) { + struct eapol *eap = (struct eapol *)(skb->data + + 24); + IEEE80211_DEBUG_EAP("RX: IEEE 802.1X EAPOL frame: %s\n", + eap_get_type(eap->type)); + } +#endif + + if (crypt && !(fc & IEEE80211_FCTL_WEP) && !ieee->open_wep && + !ieee80211_is_eapol_frame(ieee, skb)) { + IEEE80211_DEBUG_DROP( + "dropped unencrypted RX data " + "frame from " MAC_FMT + " (drop_unencrypted=1)\n", + MAC_ARG(hdr->addr2)); + goto rx_dropped; + } + + /* skb: hdr + (possible reassembled) full plaintext payload */ + + payload = skb->data + hdrlen; + ethertype = (payload[6] << 8) | payload[7]; + +#ifdef NOT_YET + /* If IEEE 802.1X is used, check whether the port is authorized to send + * the received frame. */ + if (ieee->ieee802_1x && ieee->iw_mode == IW_MODE_MASTER) { + if (ethertype == ETH_P_PAE) { + printk(KERN_DEBUG "%s: RX: IEEE 802.1X frame\n", + dev->name); + if (ieee->hostapd && ieee->apdev) { + /* Send IEEE 802.1X frames to the user + * space daemon for processing */ + prism2_rx_80211(ieee->apdev, skb, rx_stats, + PRISM2_RX_MGMT); + ieee->apdevstats.rx_packets++; + ieee->apdevstats.rx_bytes += skb->len; + goto rx_exit; + } + } else if (!frame_authorized) { + printk(KERN_DEBUG "%s: dropped frame from " + "unauthorized port (IEEE 802.1X): " + "ethertype=0x%04x\n", + dev->name, ethertype); + goto rx_dropped; + } + } +#endif + + /* convert hdr + possible LLC headers into Ethernet header */ + if (skb->len - hdrlen >= 8 && + ((memcmp(payload, rfc1042_header, SNAP_SIZE) == 0 && + ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) || + memcmp(payload, bridge_tunnel_header, SNAP_SIZE) == 0)) { + /* remove RFC1042 or Bridge-Tunnel encapsulation and + * replace EtherType */ + skb_pull(skb, hdrlen + SNAP_SIZE); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } else { + u16 len; + /* Leave Ethernet header part of hdr and full payload */ + skb_pull(skb, hdrlen); + len = htons(skb->len); + memcpy(skb_push(skb, 2), &len, 2); + memcpy(skb_push(skb, ETH_ALEN), src, ETH_ALEN); + memcpy(skb_push(skb, ETH_ALEN), dst, ETH_ALEN); + } + +#ifdef NOT_YET + if (wds && ((fc & (IEEE80211_FCTL_TODS | IEEE80211_FCTL_FROMDS)) == + IEEE80211_FCTL_TODS) && + skb->len >= ETH_HLEN + ETH_ALEN) { + /* Non-standard frame: get addr4 from its bogus location after + * the payload */ + memcpy(skb->data + ETH_ALEN, + skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_trim(skb, skb->len - ETH_ALEN); + } +#endif + + stats->rx_packets++; + stats->rx_bytes += skb->len; + +#ifdef NOT_YET + if (ieee->iw_mode == IW_MODE_MASTER && !wds && + ieee->ap->bridge_packets) { + if (dst[0] & 0x01) { + /* copy multicast frame both to the higher layers and + * to the wireless media */ + ieee->ap->bridged_multicast++; + skb2 = skb_clone(skb, GFP_ATOMIC); + if (skb2 == NULL) + printk(KERN_DEBUG "%s: skb_clone failed for " + "multicast frame\n", dev->name); + } else if (hostap_is_sta_assoc(ieee->ap, dst)) { + /* send frame directly to the associated STA using + * wireless media and not passing to higher layers */ + ieee->ap->bridged_unicast++; + skb2 = skb; + skb = NULL; + } + } + + if (skb2 != NULL) { + /* send to wireless media */ + skb2->protocol = __constant_htons(ETH_P_802_3); + skb2->mac.raw = skb2->nh.raw = skb2->data; + /* skb2->nh.raw = skb2->data + ETH_HLEN; */ + skb2->dev = dev; + dev_queue_xmit(skb2); + } + +#endif + + if (skb) { + skb->protocol = eth_type_trans(skb, dev); + memset(skb->cb, 0, sizeof(skb->cb)); + skb->dev = dev; + skb->ip_summed = CHECKSUM_NONE; /* 802.11 crc not sufficient */ + netif_rx(skb); + } + + rx_exit: +#ifdef NOT_YET + if (sta) + hostap_handle_sta_release(sta); +#endif + return 1; + + rx_dropped: + stats->rx_dropped++; + + /* Returning 0 indicates to caller that we have not handled the SKB-- + * so it is still allocated and can be used again by underlying + * hardware as a DMA target */ + return 0; +} + +#define MGMT_FRAME_FIXED_PART_LENGTH 0x24 + +static inline int ieee80211_is_ofdm_rate(u8 rate) +{ + switch (rate & ~IEEE80211_BASIC_RATE_MASK) { + case IEEE80211_OFDM_RATE_6MB: + case IEEE80211_OFDM_RATE_9MB: + case IEEE80211_OFDM_RATE_12MB: + case IEEE80211_OFDM_RATE_18MB: + case IEEE80211_OFDM_RATE_24MB: + case IEEE80211_OFDM_RATE_36MB: + case IEEE80211_OFDM_RATE_48MB: + case IEEE80211_OFDM_RATE_54MB: + return 1; + } + return 0; +} + + +static inline int ieee80211_network_init( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_network *network, + struct ieee80211_rx_stats *stats) +{ +#ifdef CONFIG_IEEE80211_DEBUG + char rates_str[64]; + char *p; +#endif + struct ieee80211_info_element *info_element; + u16 left; + u8 i; + + /* Pull out fixed field data */ + memcpy(network->bssid, beacon->header.addr3, ETH_ALEN); + network->capability = beacon->capability; + network->last_scanned = jiffies; + network->time_stamp[0] = beacon->time_stamp[0]; + network->time_stamp[1] = beacon->time_stamp[1]; + network->beacon_interval = beacon->beacon_interval; + /* Where to pull this? beacon->listen_interval;*/ + network->listen_interval = 0x0A; + network->rates_len = network->rates_ex_len = 0; + network->last_associate = 0; + network->ssid_len = 0; + network->flags = 0; + network->atim_window = 0; + + if (stats->freq == IEEE80211_52GHZ_BAND) { + /* for A band (No DS info) */ + network->channel = stats->received_channel; + } else + network->flags |= NETWORK_HAS_CCK; + + network->wpa_ie_len = 0; + network->rsn_ie_len = 0; + + info_element = &beacon->info_element; + left = stats->len - ((void *)info_element - (void *)beacon); + while (left >= sizeof(struct ieee80211_info_element_hdr)) { + if (sizeof(struct ieee80211_info_element_hdr) + info_element->len > left) { + IEEE80211_DEBUG_SCAN("SCAN: parse failed: info_element->len + 2 > left : info_element->len+2=%d left=%d.\n", + info_element->len + sizeof(struct ieee80211_info_element), + left); + return 1; + } + + switch (info_element->id) { + case MFIE_TYPE_SSID: + if (ieee80211_is_empty_essid(info_element->data, + info_element->len)) { + network->flags |= NETWORK_EMPTY_ESSID; + break; + } + + network->ssid_len = min(info_element->len, + (u8)IW_ESSID_MAX_SIZE); + memcpy(network->ssid, info_element->data, network->ssid_len); + if (network->ssid_len < IW_ESSID_MAX_SIZE) + memset(network->ssid + network->ssid_len, 0, + IW_ESSID_MAX_SIZE - network->ssid_len); + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_SSID: '%s' len=%d.\n", + network->ssid, network->ssid_len); + break; + + case MFIE_TYPE_RATES: +#ifdef CONFIG_IEEE80211_DEBUG + p = rates_str; +#endif + network->rates_len = min(info_element->len, MAX_RATES_LENGTH); + for (i = 0; i < network->rates_len; i++) { + network->rates[i] = info_element->data[i]; +#ifdef CONFIG_IEEE80211_DEBUG + p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); +#endif + if (ieee80211_is_ofdm_rate(info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + IEEE80211_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES: '%s' (%d)\n", + rates_str, network->rates_len); + break; + + case MFIE_TYPE_RATES_EX: +#ifdef CONFIG_IEEE80211_DEBUG + p = rates_str; +#endif + network->rates_ex_len = min(info_element->len, MAX_RATES_EX_LENGTH); + for (i = 0; i < network->rates_ex_len; i++) { + network->rates_ex[i] = info_element->data[i]; +#ifdef CONFIG_IEEE80211_DEBUG + p += snprintf(p, sizeof(rates_str) - (p - rates_str), "%02X ", network->rates[i]); +#endif + if (ieee80211_is_ofdm_rate(info_element->data[i])) { + network->flags |= NETWORK_HAS_OFDM; + if (info_element->data[i] & + IEEE80211_BASIC_RATE_MASK) + network->flags &= + ~NETWORK_HAS_CCK; + } + } + + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RATES_EX: '%s' (%d)\n", + rates_str, network->rates_ex_len); + break; + + case MFIE_TYPE_DS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_DS_SET: %d\n", + info_element->data[0]); + if (stats->freq == IEEE80211_24GHZ_BAND) + network->channel = info_element->data[0]; + break; + + case MFIE_TYPE_FH_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_FH_SET: ignored\n"); + break; + + case MFIE_TYPE_CF_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CF_SET: ignored\n"); + break; + + case MFIE_TYPE_TIM: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_TIM: ignored\n"); + break; + + case MFIE_TYPE_IBSS_SET: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_IBSS_SET: ignored\n"); + break; + + case MFIE_TYPE_CHALLENGE: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_CHALLENGE: ignored\n"); + break; + + case MFIE_TYPE_GENERIC: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_GENERIC: %d bytes\n", + info_element->len); + if (info_element->len >= 4 && + info_element->data[0] == 0x00 && + info_element->data[1] == 0x50 && + info_element->data[2] == 0xf2 && + info_element->data[3] == 0x01) { + network->wpa_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->wpa_ie, info_element, + network->wpa_ie_len); + } + break; + + case MFIE_TYPE_RSN: + IEEE80211_DEBUG_SCAN("MFIE_TYPE_RSN: %d bytes\n", + info_element->len); + network->rsn_ie_len = min(info_element->len + 2, + MAX_WPA_IE_LEN); + memcpy(network->rsn_ie, info_element, + network->rsn_ie_len); + break; + + default: + IEEE80211_DEBUG_SCAN("unsupported IE %d\n", + info_element->id); + break; + } + + left -= sizeof(struct ieee80211_info_element_hdr) + + info_element->len; + info_element = (struct ieee80211_info_element *) + &info_element->data[info_element->len]; + } + + network->mode = 0; + if (stats->freq == IEEE80211_52GHZ_BAND) + network->mode = IEEE_A; + else { + if (network->flags & NETWORK_HAS_OFDM) + network->mode |= IEEE_G; + if (network->flags & NETWORK_HAS_CCK) + network->mode |= IEEE_B; + } + + if (network->mode == 0) { + IEEE80211_DEBUG_SCAN("Filtered out '%s (" MAC_FMT ")' " + "network.\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid)); + return 1; + } + + if (ieee80211_is_empty_essid(network->ssid, network->ssid_len)) + network->flags |= NETWORK_EMPTY_ESSID; + + memcpy(&network->stats, stats, sizeof(network->stats)); + + return 0; +} + +static inline int is_same_network(struct ieee80211_network *src, + struct ieee80211_network *dst) +{ + /* A network is only a duplicate if the channel, BSSID, and ESSID + * all match. We treat all with the same BSSID and channel + * as one network */ + return ((src->ssid_len == dst->ssid_len) && + (src->channel == dst->channel) && + !memcmp(src->bssid, dst->bssid, ETH_ALEN) && + !memcmp(src->ssid, dst->ssid, src->ssid_len)); +} + +static inline void update_network(struct ieee80211_network *dst, + struct ieee80211_network *src) +{ + memcpy(&dst->stats, &src->stats, sizeof(struct ieee80211_rx_stats)); + dst->capability = src->capability; + memcpy(dst->rates, src->rates, src->rates_len); + dst->rates_len = src->rates_len; + memcpy(dst->rates_ex, src->rates_ex, src->rates_ex_len); + dst->rates_ex_len = src->rates_ex_len; + + dst->mode = src->mode; + dst->flags = src->flags; + dst->time_stamp[0] = src->time_stamp[0]; + dst->time_stamp[1] = src->time_stamp[1]; + + dst->beacon_interval = src->beacon_interval; + dst->listen_interval = src->listen_interval; + dst->atim_window = src->atim_window; + + memcpy(dst->wpa_ie, src->wpa_ie, src->wpa_ie_len); + dst->wpa_ie_len = src->wpa_ie_len; + memcpy(dst->rsn_ie, src->rsn_ie, src->rsn_ie_len); + dst->rsn_ie_len = src->rsn_ie_len; + + dst->last_scanned = jiffies; + /* dst->last_associate is not overwritten */ +} + +static inline void ieee80211_process_probe_response( + struct ieee80211_device *ieee, + struct ieee80211_probe_response *beacon, + struct ieee80211_rx_stats *stats) +{ + struct ieee80211_network network; + struct ieee80211_network *target; + struct ieee80211_network *oldest = NULL; +#ifdef CONFIG_IEEE80211_DEBUG + struct ieee80211_info_element *info_element = &beacon->info_element; +#endif + unsigned long flags; + + IEEE80211_DEBUG_SCAN( + "'%s' (" MAC_FMT "): %c%c%c%c %c%c%c%c-%c%c%c%c %c%c%c%c\n", + escape_essid(info_element->data, info_element->len), + MAC_ARG(beacon->header.addr3), + (beacon->capability & (1<<0xf)) ? '1' : '0', + (beacon->capability & (1<<0xe)) ? '1' : '0', + (beacon->capability & (1<<0xd)) ? '1' : '0', + (beacon->capability & (1<<0xc)) ? '1' : '0', + (beacon->capability & (1<<0xb)) ? '1' : '0', + (beacon->capability & (1<<0xa)) ? '1' : '0', + (beacon->capability & (1<<0x9)) ? '1' : '0', + (beacon->capability & (1<<0x8)) ? '1' : '0', + (beacon->capability & (1<<0x7)) ? '1' : '0', + (beacon->capability & (1<<0x6)) ? '1' : '0', + (beacon->capability & (1<<0x5)) ? '1' : '0', + (beacon->capability & (1<<0x4)) ? '1' : '0', + (beacon->capability & (1<<0x3)) ? '1' : '0', + (beacon->capability & (1<<0x2)) ? '1' : '0', + (beacon->capability & (1<<0x1)) ? '1' : '0', + (beacon->capability & (1<<0x0)) ? '1' : '0'); + + if (ieee80211_network_init(ieee, beacon, &network, stats)) { + IEEE80211_DEBUG_SCAN("Dropped '%s' (" MAC_FMT ") via %s.\n", + escape_essid(info_element->data, + info_element->len), + MAC_ARG(beacon->header.addr3), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); + return; + } + + /* The network parsed correctly -- so now we scan our known networks + * to see if we can find it in our list. + * + * NOTE: This search is definitely not optimized. Once its doing + * the "right thing" we'll optimize it for efficiency if + * necessary */ + + /* Search for this entry in the list and update it if it is + * already there. */ + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(target, &ieee->network_list, list) { + if (is_same_network(target, &network)) + break; + + if ((oldest == NULL) || + (target->last_scanned < oldest->last_scanned)) + oldest = target; + } + + /* If we didn't find a match, then get a new network slot to initialize + * with this beacon's information */ + if (&target->list == &ieee->network_list) { + if (list_empty(&ieee->network_free_list)) { + /* If there are no more slots, expire the oldest */ + list_del(&oldest->list); + target = oldest; + IEEE80211_DEBUG_SCAN("Expired '%s' (" MAC_FMT ") from " + "network list.\n", + escape_essid(target->ssid, + target->ssid_len), + MAC_ARG(target->bssid)); + } else { + /* Otherwise just pull from the free list */ + target = list_entry(ieee->network_free_list.next, + struct ieee80211_network, list); + list_del(ieee->network_free_list.next); + } + + +#ifdef CONFIG_IEEE80211_DEBUG + IEEE80211_DEBUG_SCAN("Adding '%s' (" MAC_FMT ") via %s.\n", + escape_essid(network.ssid, + network.ssid_len), + MAC_ARG(network.bssid), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); +#endif + memcpy(target, &network, sizeof(*target)); + list_add_tail(&target->list, &ieee->network_list); + } else { + IEEE80211_DEBUG_SCAN("Updating '%s' (" MAC_FMT ") via %s.\n", + escape_essid(target->ssid, + target->ssid_len), + MAC_ARG(target->bssid), + WLAN_FC_GET_STYPE(beacon->header.frame_ctl) == + IEEE80211_STYPE_PROBE_RESP ? + "PROBE RESPONSE" : "BEACON"); + update_network(target, &network); + } + + spin_unlock_irqrestore(&ieee->lock, flags); +} + +void ieee80211_rx_mgt(struct ieee80211_device *ieee, + struct ieee80211_hdr *header, + struct ieee80211_rx_stats *stats) +{ + switch (WLAN_FC_GET_STYPE(header->frame_ctl)) { + case IEEE80211_STYPE_ASSOC_RESP: + IEEE80211_DEBUG_MGMT("received ASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + + case IEEE80211_STYPE_REASSOC_RESP: + IEEE80211_DEBUG_MGMT("received REASSOCIATION RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + + case IEEE80211_STYPE_PROBE_RESP: + IEEE80211_DEBUG_MGMT("received PROBE RESPONSE (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Probe response\n"); + ieee80211_process_probe_response( + ieee, (struct ieee80211_probe_response *)header, stats); + break; + + case IEEE80211_STYPE_BEACON: + IEEE80211_DEBUG_MGMT("received BEACON (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_DEBUG_SCAN("Beacon\n"); + ieee80211_process_probe_response( + ieee, (struct ieee80211_probe_response *)header, stats); + break; + + default: + IEEE80211_DEBUG_MGMT("received UNKNOWN (%d)\n", + WLAN_FC_GET_STYPE(header->frame_ctl)); + IEEE80211_WARNING("%s: Unknown management packet: %d\n", + ieee->dev->name, + WLAN_FC_GET_STYPE(header->frame_ctl)); + break; + } +} + + +EXPORT_SYMBOL(ieee80211_rx_mgt); +EXPORT_SYMBOL(ieee80211_rx); diff -Nru a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_tx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,448 @@ +/****************************************************************************** + + Copyright(c) 2003 - 2004 Intel Corporation. All rights reserved. + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +/* + + +802.11 Data Frame + + ,-------------------------------------------------------------------. +Bytes | 2 | 2 | 6 | 6 | 6 | 2 | 0..2312 | 4 | + |------|------|---------|---------|---------|------|---------|------| +Desc. | ctrl | dura | DA/RA | TA | SA | Sequ | Frame | fcs | + | | tion | (BSSID) | | | ence | data | | + `--------------------------------------------------| |------' +Total: 28 non-data bytes `----.----' + | + .- 'Frame data' expands to <---------------------------' + | + V + ,---------------------------------------------------. +Bytes | 1 | 1 | 1 | 3 | 2 | 0-2304 | + |------|------|---------|----------|------|---------| +Desc. | SNAP | SNAP | Control |Eth Tunnel| Type | IP | + | DSAP | SSAP | | | | Packet | + | 0xAA | 0xAA |0x03 (UI)|0x00-00-F8| | | + `-----------------------------------------| | +Total: 8 non-data bytes `----.----' + | + .- 'IP Packet' expands, if WEP enabled, to <--' + | + V + ,-----------------------. +Bytes | 4 | 0-2296 | 4 | + |-----|-----------|-----| +Desc. | IV | Encrypted | ICV | + | | IP Packet | | + `-----------------------' +Total: 8 non-data bytes + + +802.3 Ethernet Data Frame + + ,-----------------------------------------. +Bytes | 6 | 6 | 2 | Variable | 4 | + |-------|-------|------|-----------|------| +Desc. | Dest. | Source| Type | IP Packet | fcs | + | MAC | MAC | | | | + `-----------------------------------------' +Total: 18 non-data bytes + +In the event that fragmentation is required, the incoming payload is split into +N parts of size ieee->fts. The first fragment contains the SNAP header and the +remaining packets are just data. + +If encryption is enabled, each fragment payload size is reduced by enough space +to add the prefix and postfix (IV and ICV totalling 8 bytes in the case of WEP) +So if you have 1500 bytes of payload with ieee->fts set to 500 without +encryption it will take 3 frames. With WEP it will take 4 frames as the +payload of each frame is reduced to 492 bytes. + +* SKB visualization +* +* ,- skb->data +* | +* | ETHERNET HEADER ,-<-- PAYLOAD +* | | 14 bytes from skb->data +* | 2 bytes for Type --> ,T. | (sizeof ethhdr) +* | | | | +* |,-Dest.--. ,--Src.---. | | | +* | 6 bytes| | 6 bytes | | | | +* v | | | | | | +* 0 | v 1 | v | v 2 +* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 +* ^ | ^ | ^ | +* | | | | | | +* | | | | `T' <---- 2 bytes for Type +* | | | | +* | | '---SNAP--' <-------- 6 bytes for SNAP +* | | +* `-IV--' <-------------------- 4 bytes for IV (WEP) +* +* SNAP HEADER +* +*/ + +static u8 P802_1H_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 }; +static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; + +static inline int ieee80211_put_snap(u8 *data, u16 h_proto) +{ + struct ieee80211_snap_hdr *snap; + u8 *oui; + + snap = (struct ieee80211_snap_hdr *)data; + snap->dsap = 0xaa; + snap->ssap = 0xaa; + snap->ctrl = 0x03; + + if (h_proto == 0x8137 || h_proto == 0x80f3) + oui = P802_1H_OUI; + else + oui = RFC1042_OUI; + snap->oui[0] = oui[0]; + snap->oui[1] = oui[1]; + snap->oui[2] = oui[2]; + + *(u16 *)(data + SNAP_SIZE) = htons(h_proto); + + return SNAP_SIZE + sizeof(u16); +} + +static inline int ieee80211_encrypt_fragment( + struct ieee80211_device *ieee, + struct sk_buff *frag, + int hdr_len) +{ + struct ieee80211_crypt_data* crypt = ieee->crypt[ieee->tx_keyidx]; + int res; + +#ifdef CONFIG_IEEE80211_CRYPT_TKIP + struct ieee80211_hdr *header; + + if (ieee->tkip_countermeasures && + crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) { + header = (struct ieee80211_hdr *) frag->data; + if (net_ratelimit()) { + printk(KERN_DEBUG "%s: TKIP countermeasures: dropped " + "TX packet to " MAC_FMT "\n", + ieee->dev->name, MAC_ARG(header->addr1)); + } + return -1; + } +#endif + /* To encrypt, frame format is: + * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */ + + // PR: FIXME: Copied from hostap. Check fragmentation/MSDU/MPDU encryption. + /* Host-based IEEE 802.11 fragmentation for TX is not yet supported, so + * call both MSDU and MPDU encryption functions from here. */ + atomic_inc(&crypt->refcnt); + res = 0; + if (crypt->ops->encrypt_msdu) + res = crypt->ops->encrypt_msdu(frag, hdr_len, crypt->priv); + if (res == 0 && crypt->ops->encrypt_mpdu) + res = crypt->ops->encrypt_mpdu(frag, hdr_len, crypt->priv); + + atomic_dec(&crypt->refcnt); + if (res < 0) { + printk(KERN_INFO "%s: Encryption failed: len=%d.\n", + ieee->dev->name, frag->len); + ieee->ieee_stats.tx_discards++; + return -1; + } + + return 0; +} + + +void ieee80211_txb_free(struct ieee80211_txb *txb) { + int i; + if (unlikely(!txb)) + return; + for (i = 0; i < txb->nr_frags; i++) + if (txb->fragments[i]) + dev_kfree_skb_any(txb->fragments[i]); + kfree(txb); +} + +struct ieee80211_txb *ieee80211_alloc_txb(int nr_frags, int txb_size, + int gfp_mask) +{ + struct ieee80211_txb *txb; + int i; + txb = kmalloc( + sizeof(struct ieee80211_txb) + (sizeof(u8*) * nr_frags), + gfp_mask); + if (!txb) + return NULL; + + memset(txb, sizeof(struct ieee80211_txb), 0); + txb->nr_frags = nr_frags; + txb->frag_size = txb_size; + + for (i = 0; i < nr_frags; i++) { + txb->fragments[i] = dev_alloc_skb(txb_size); + if (unlikely(!txb->fragments[i])) { + i--; + break; + } + } + if (unlikely(i != nr_frags)) { + while (i >= 0) + dev_kfree_skb_any(txb->fragments[i--]); + kfree(txb); + return NULL; + } + return txb; +} + +/* SKBs are added to the ieee->tx_queue. */ +int ieee80211_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ieee80211_device *ieee = netdev_priv(dev); + struct ieee80211_txb *txb = NULL; + struct ieee80211_hdr *frag_hdr; + int i, bytes_per_frag, nr_frags, bytes_last_frag, frag_size; + unsigned long flags; + struct net_device_stats *stats = &ieee->stats; + int ether_type, encrypt; + int bytes, fc, hdr_len; + struct sk_buff *skb_frag; + struct ieee80211_hdr header = { /* Ensure zero initialized */ + .duration_id = 0, + .seq_ctl = 0 + }; + u8 dest[ETH_ALEN], src[ETH_ALEN]; + + struct ieee80211_crypt_data* crypt; + + spin_lock_irqsave(&ieee->lock, flags); + + /* If there is no driver handler to take the TXB, dont' bother + * creating it... */ + if (!ieee->hard_start_xmit) { + printk(KERN_WARNING "%s: No xmit handler.\n", + ieee->dev->name); + goto success; + } + + if (unlikely(skb->len < SNAP_SIZE + sizeof(u16))) { + printk(KERN_WARNING "%s: skb too small (%d).\n", + ieee->dev->name, skb->len); + goto success; + } + + ether_type = ntohs(((struct ethhdr *)skb->data)->h_proto); + + crypt = ieee->crypt[ieee->tx_keyidx]; + + encrypt = !(ether_type == ETH_P_PAE && ieee->ieee802_1x) && + ieee->host_encrypt && crypt && crypt->ops; + + if (!encrypt && ieee->ieee802_1x && + ieee->drop_unencrypted && ether_type != ETH_P_PAE) { + stats->tx_dropped++; + goto success; + } + +#ifdef CONFIG_IEEE80211_DEBUG + if (crypt && !encrypt && ether_type == ETH_P_PAE) { + struct eapol *eap = (struct eapol *)(skb->data + + sizeof(struct ethhdr) - SNAP_SIZE - sizeof(u16)); + IEEE80211_DEBUG_EAP("TX: IEEE 802.11 EAPOL frame: %s\n", + eap_get_type(eap->type)); + } +#endif + + /* Save source and destination addresses */ + memcpy(&dest, skb->data, ETH_ALEN); + memcpy(&src, skb->data+ETH_ALEN, ETH_ALEN); + + /* Advance the SKB to the start of the payload */ + skb_pull(skb, sizeof(struct ethhdr)); + + /* Determine total amount of storage required for TXB packets */ + bytes = skb->len + SNAP_SIZE + sizeof(u16); + + if (encrypt) + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA | + IEEE80211_FCTL_WEP; + else + fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_DATA; + + if (ieee->iw_mode == IW_MODE_INFRA) { + fc |= IEEE80211_FCTL_TODS; + /* To DS: Addr1 = BSSID, Addr2 = SA, + Addr3 = DA */ + memcpy(&header.addr1, ieee->bssid, ETH_ALEN); + memcpy(&header.addr2, &src, ETH_ALEN); + memcpy(&header.addr3, &dest, ETH_ALEN); + } else if (ieee->iw_mode == IW_MODE_ADHOC) { + /* not From/To DS: Addr1 = DA, Addr2 = SA, + Addr3 = BSSID */ + memcpy(&header.addr1, dest, ETH_ALEN); + memcpy(&header.addr2, src, ETH_ALEN); + memcpy(&header.addr3, ieee->bssid, ETH_ALEN); + } + header.frame_ctl = cpu_to_le16(fc); + hdr_len = IEEE80211_3ADDR_LEN; + + /* Determine fragmentation size based on destination (multicast + * and broadcast are not fragmented) */ + if (is_multicast_ether_addr(dest) || + is_broadcast_ether_addr(dest)) + frag_size = MAX_FRAG_THRESHOLD; + else + frag_size = ieee->fts; + + /* Determine amount of payload per fragment. Regardless of if + * this stack is providing the full 802.11 header, one will + * eventually be affixed to this fragment -- so we must account for + * it when determining the amount of payload space. */ + bytes_per_frag = frag_size - IEEE80211_3ADDR_LEN; + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + bytes_per_frag -= IEEE80211_FCS_LEN; + + /* Each fragment may need to have room for encryptiong pre/postfix */ + if (encrypt) + bytes_per_frag -= crypt->ops->extra_prefix_len + + crypt->ops->extra_postfix_len; + + /* Number of fragments is the total bytes_per_frag / + * payload_per_fragment */ + nr_frags = bytes / bytes_per_frag; + bytes_last_frag = bytes % bytes_per_frag; + if (bytes_last_frag) + nr_frags++; + else + bytes_last_frag = bytes_per_frag; + + /* When we allocate the TXB we allocate enough space for the reserve + * and full fragment bytes (bytes_per_frag doesn't include prefix, + * postfix, header, FCS, etc.) */ + txb = ieee80211_alloc_txb(nr_frags, frag_size, GFP_ATOMIC); + if (unlikely(!txb)) { + printk(KERN_WARNING "%s: Could not allocate TXB\n", + ieee->dev->name); + goto failed; + } + txb->encrypted = encrypt; + txb->payload_size = bytes; + + for (i = 0; i < nr_frags; i++) { + skb_frag = txb->fragments[i]; + + if (encrypt) + skb_reserve(skb_frag, crypt->ops->extra_prefix_len); + + frag_hdr = (struct ieee80211_hdr *)skb_put(skb_frag, hdr_len); + memcpy(frag_hdr, &header, hdr_len); + + /* If this is not the last fragment, then add the MOREFRAGS + * bit to the frame control */ + if (i != nr_frags - 1) { + frag_hdr->frame_ctl = cpu_to_le16( + fc | IEEE80211_FCTL_MOREFRAGS); + bytes = bytes_per_frag; + } else { + /* The last fragment takes the remaining length */ + bytes = bytes_last_frag; + } + + /* Put a SNAP header on the first fragment */ + if (i == 0) { + ieee80211_put_snap( + skb_put(skb_frag, SNAP_SIZE + sizeof(u16)), + ether_type); + bytes -= SNAP_SIZE + sizeof(u16); + } + + memcpy(skb_put(skb_frag, bytes), skb->data, bytes); + + /* Advance the SKB... */ + skb_pull(skb, bytes); + + /* Encryption routine will move the header forward in order + * to insert the IV between the header and the payload */ + if (encrypt) + ieee80211_encrypt_fragment(ieee, skb_frag, hdr_len); + if (ieee->config & + (CFG_IEEE80211_COMPUTE_FCS | CFG_IEEE80211_RESERVE_FCS)) + skb_put(skb_frag, 4); + } + + + success: + spin_unlock_irqrestore(&ieee->lock, flags); + + dev_kfree_skb_any(skb); + + if (txb) { + if ((*ieee->hard_start_xmit)(txb, dev) == 0) { + stats->tx_packets++; + stats->tx_bytes += txb->payload_size; + return 0; + } + ieee80211_txb_free(txb); + } + + return 0; + + failed: + spin_unlock_irqrestore(&ieee->lock, flags); + netif_stop_queue(dev); + stats->tx_errors++; + return 1; + +} + +EXPORT_SYMBOL(ieee80211_txb_free); diff -Nru a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c --- /dev/null Wed Dec 31 16:00:00 196900 +++ b/net/ieee80211/ieee80211_wx.c 2005-03-15 23:46:53 -08:00 @@ -0,0 +1,471 @@ +/****************************************************************************** + + Copyright(c) 2004 Intel Corporation. All rights reserved. + + Portions of this file are based on the WEP enablement code provided by the + Host AP project hostap-drivers v0.1.3 + Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen + + Copyright (c) 2002-2003, Jouni Malinen + + 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., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + The full GNU General Public License is included in this distribution in the + file called LICENSE. + + Contact Information: + James P. Ketrenos + Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + +******************************************************************************/ +#include +#include +#include +#include + +#include +static const char *ieee80211_modes[] = { + "?", "a", "b", "ab", "g", "ag", "bg", "abg" +}; + +#define MAX_CUSTOM_LEN 64 +static inline char *ipw2100_translate_scan(struct ieee80211_device *ieee, + char *start, char *stop, + struct ieee80211_network *network) +{ + char custom[MAX_CUSTOM_LEN]; + char *p; + struct iw_event iwe; + int i, j; + u8 max_rate, rate; + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, network->bssid, ETH_ALEN); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN); + + /* Remaining entries will be displayed in the order we provide them */ + + /* Add the ESSID */ + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + if (network->flags & NETWORK_EMPTY_ESSID) { + iwe.u.data.length = sizeof(""); + start = iwe_stream_add_point(start, stop, &iwe, ""); + } else { + iwe.u.data.length = min(network->ssid_len, (u8)32); + start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + } + + /* Add the protocol name */ + iwe.cmd = SIOCGIWNAME; + snprintf(iwe.u.name, IFNAMSIZ, "IEEE 802.11%s", ieee80211_modes[network->mode]); + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_CHAR_LEN); + + /* Add mode */ + iwe.cmd = SIOCGIWMODE; + if (network->capability & + (WLAN_CAPABILITY_BSS | WLAN_CAPABILITY_IBSS)) { + if (network->capability & WLAN_CAPABILITY_BSS) + iwe.u.mode = IW_MODE_MASTER; + else + iwe.u.mode = IW_MODE_ADHOC; + + start = iwe_stream_add_event(start, stop, &iwe, + IW_EV_UINT_LEN); + } + + /* Add frequency/channel */ + iwe.cmd = SIOCGIWFREQ; +/* iwe.u.freq.m = ieee80211_frequency(network->channel, network->mode); + iwe.u.freq.e = 3; */ + iwe.u.freq.m = network->channel; + iwe.u.freq.e = 0; + iwe.u.freq.i = 0; + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (network->capability & WLAN_CAPABILITY_PRIVACY) + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + else + iwe.u.data.flags = IW_ENCODE_DISABLED; + iwe.u.data.length = 0; + start = iwe_stream_add_point(start, stop, &iwe, network->ssid); + + /* Add basic and extended rates */ + max_rate = 0; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), " Rates (Mb/s): "); + for (i = 0, j = 0; i < network->rates_len; ) { + if (j < network->rates_ex_len && + ((network->rates_ex[j] & 0x7F) < + (network->rates[i] & 0x7F))) + rate = network->rates_ex[j++] & 0x7F; + else + rate = network->rates[i++] & 0x7F; + if (rate > max_rate) + max_rate = rate; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + } + for (; j < network->rates_ex_len; j++) { + rate = network->rates_ex[j] & 0x7F; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + "%d%s ", rate >> 1, (rate & 1) ? ".5" : ""); + if (rate > max_rate) + max_rate = rate; + } + + iwe.cmd = SIOCGIWRATE; + iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = max_rate * 500000; + start = iwe_stream_add_event(start, stop, &iwe, + IW_EV_PARAM_LEN); + + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + + /* Add quality statistics */ + /* TODO: Fix these values... */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.qual = network->stats.signal; + iwe.u.qual.level = network->stats.rssi; + iwe.u.qual.noise = network->stats.noise; + iwe.u.qual.updated = network->stats.mask & IEEE80211_STATMASK_WEMASK; + if (!(network->stats.mask & IEEE80211_STATMASK_RSSI)) + iwe.u.qual.updated |= IW_QUAL_LEVEL_INVALID; + if (!(network->stats.mask & IEEE80211_STATMASK_NOISE)) + iwe.u.qual.updated |= IW_QUAL_NOISE_INVALID; + if (!(network->stats.mask & IEEE80211_STATMASK_SIGNAL)) + iwe.u.qual.updated |= IW_QUAL_QUAL_INVALID; + + start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN); + + iwe.cmd = IWEVCUSTOM; + p = custom; + + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + + if (ieee->wpa_enabled && network->wpa_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + u8 *p = buf; + p += sprintf(p, "wpa_ie="); + for (i = 0; i < network->wpa_ie_len; i++) { + p += sprintf(p, "%02x", network->wpa_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + if (ieee->wpa_enabled && network->rsn_ie_len){ + char buf[MAX_WPA_IE_LEN * 2 + 30]; + + u8 *p = buf; + p += sprintf(p, "rsn_ie="); + for (i = 0; i < network->rsn_ie_len; i++) { + p += sprintf(p, "%02x", network->rsn_ie[i]); + } + + memset(&iwe, 0, sizeof(iwe)); + iwe.cmd = IWEVCUSTOM; + iwe.u.data.length = strlen(buf); + start = iwe_stream_add_point(start, stop, &iwe, buf); + } + + /* Add EXTRA: Age to display seconds since last beacon/probe response + * for given network. */ + iwe.cmd = IWEVCUSTOM; + p = custom; + p += snprintf(p, MAX_CUSTOM_LEN - (p - custom), + " Last beacon: %lums ago", (jiffies - network->last_scanned) / (HZ / 100)); + iwe.u.data.length = p - custom; + if (iwe.u.data.length) + start = iwe_stream_add_point(start, stop, &iwe, custom); + + + return start; +} + +int ieee80211_wx_get_scan(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *extra) +{ + struct ieee80211_network *network; + unsigned long flags; + + char *ev = extra; + char *stop = ev + IW_SCAN_MAX_DATA; + int i = 0; + + IEEE80211_DEBUG_WX("Getting scan\n"); + + spin_lock_irqsave(&ieee->lock, flags); + + list_for_each_entry(network, &ieee->network_list, list) { + i++; + if (ieee->scan_age == 0 || + time_after(network->last_scanned + ieee->scan_age, jiffies)) + ev = ipw2100_translate_scan(ieee, ev, stop, network); + else + IEEE80211_DEBUG_SCAN( + "Not showing network '%s (" + MAC_FMT ")' due to age (%lums).\n", + escape_essid(network->ssid, + network->ssid_len), + MAC_ARG(network->bssid), + (jiffies - network->last_scanned) / (HZ / 100)); + } + + spin_unlock_irqrestore(&ieee->lock, flags); + + wrqu->data.length = ev - extra; + wrqu->data.flags = 0; + + IEEE80211_DEBUG_WX("exit: %d networks returned.\n", i); + + return 0; +} + +int ieee80211_wx_set_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + struct net_device *dev = ieee->dev; + struct ieee80211_security sec = { + .flags = 0 + }; + int i, key, key_provided, len; + struct ieee80211_crypt_data **crypt; + + IEEE80211_DEBUG_WX("SET_ENCODE\n"); + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + key_provided = 1; + } else { + key_provided = 0; + key = ieee->tx_keyidx; + } + + IEEE80211_DEBUG_WX("Key: %d [%s]\n", key, key_provided ? + "provided" : "default"); + + crypt = &ieee->crypt[key]; + + if (erq->flags & IW_ENCODE_DISABLED) { + if (key_provided && *crypt) { + IEEE80211_DEBUG_WX("Disabling encryption on key %d.\n", + key); + ieee80211_crypt_delayed_deinit(ieee, crypt); + } else + IEEE80211_DEBUG_WX("Disabling encryption.\n"); + + /* Check all the keys to see if any are still configured, + * and if no key index was provided, de-init them all */ + for (i = 0; i < WEP_KEYS; i++) { + if (ieee->crypt[i] != NULL) { + if (key_provided) + break; + ieee80211_crypt_delayed_deinit( + ieee, &ieee->crypt[i]); + } + } + + if (i == WEP_KEYS) { + sec.enabled = 0; + sec.level = SEC_LEVEL_0; + sec.flags |= SEC_ENABLED | SEC_LEVEL; + } + + goto done; + } + + + + sec.enabled = 1; + sec.flags |= SEC_ENABLED; + + if (*crypt != NULL && (*crypt)->ops != NULL && + strcmp((*crypt)->ops->name, "WEP") != 0) { + /* changing to use WEP; deinit previously used algorithm + * on this key */ + ieee80211_crypt_delayed_deinit(ieee, crypt); + } + + if (*crypt == NULL) { + struct ieee80211_crypt_data *new_crypt; + + /* take WEP into use */ + new_crypt = kmalloc(sizeof(struct ieee80211_crypt_data), + GFP_KERNEL); + if (new_crypt == NULL) + return -ENOMEM; + memset(new_crypt, 0, sizeof(struct ieee80211_crypt_data)); + new_crypt->ops = ieee80211_get_crypto_ops("WEP"); + if (!new_crypt->ops) { + request_module("ieee80211_crypt_wep"); + new_crypt->ops = ieee80211_get_crypto_ops("WEP"); + } + + if (new_crypt->ops && try_module_get(new_crypt->ops->owner)) + new_crypt->priv = new_crypt->ops->init(key); + + if (!new_crypt->ops || !new_crypt->priv) { + kfree(new_crypt); + new_crypt = NULL; + + printk(KERN_WARNING "%s: could not initialize WEP: " + "load module ieee80211_crypt_wep\n", + dev->name); + return -EOPNOTSUPP; + } + *crypt = new_crypt; + } + + /* If a new key was provided, set it up */ + if (erq->length > 0) { + len = erq->length <= 5 ? 5 : 13; + memcpy(sec.keys[key], keybuf, erq->length); + if (len > erq->length) + memset(sec.keys[key] + erq->length, 0, + len - erq->length); + IEEE80211_DEBUG_WX("Setting key %d to '%s' (%d:%d bytes)\n", + key, escape_essid(sec.keys[key], len), + erq->length, len); + sec.key_sizes[key] = len; + (*crypt)->ops->set_key(sec.keys[key], len, NULL, + (*crypt)->priv); + sec.flags |= (1 << key); + /* This ensures a key will be activated if no key is + * explicitely set */ + if (key == sec.active_key) + sec.flags |= SEC_ACTIVE_KEY; + } else { + len = (*crypt)->ops->get_key(sec.keys[key], WEP_KEY_LEN, + NULL, (*crypt)->priv); + if (len == 0) { + /* Set a default key of all 0 */ + IEEE80211_DEBUG_WX("Setting key %d to all zero.\n", + key); + memset(sec.keys[key], 0, 13); + (*crypt)->ops->set_key(sec.keys[key], 13, NULL, + (*crypt)->priv); + sec.key_sizes[key] = 13; + sec.flags |= (1 << key); + } + + /* No key data - just set the default TX key index */ + if (key_provided) { + IEEE80211_DEBUG_WX( + "Setting key %d to default Tx key.\n", key); + ieee->tx_keyidx = key; + sec.active_key = key; + sec.flags |= SEC_ACTIVE_KEY; + } + } + + done: + ieee->open_wep = !(erq->flags & IW_ENCODE_RESTRICTED); + sec.auth_mode = ieee->open_wep ? WLAN_AUTH_OPEN : WLAN_AUTH_SHARED_KEY; + sec.flags |= SEC_AUTH_MODE; + IEEE80211_DEBUG_WX("Auth: %s\n", sec.auth_mode == WLAN_AUTH_OPEN ? + "OPEN" : "SHARED KEY"); + + /* For now we just support WEP, so only set that security level... + * TODO: When WPA is added this is one place that needs to change */ + sec.flags |= SEC_LEVEL; + sec.level = SEC_LEVEL_1; /* 40 and 104 bit WEP */ + + if (ieee->set_security) + ieee->set_security(dev, &sec); + + /* Do not reset port if card is in Managed mode since resetting will + * generate new IEEE 802.11 authentication which may end up in looping + * with IEEE 802.1X. If your hardware requires a reset after WEP + * configuration (for example... Prism2), implement the reset_port in + * the callbacks structures used to initialize the 802.11 stack. */ + if (ieee->reset_on_keychange && + ieee->iw_mode != IW_MODE_INFRA && + ieee->reset_port && ieee->reset_port(dev)) { + printk(KERN_DEBUG "%s: reset_port failed\n", dev->name); + return -EINVAL; + } + return 0; +} + +int ieee80211_wx_get_encode(struct ieee80211_device *ieee, + struct iw_request_info *info, + union iwreq_data *wrqu, char *keybuf) +{ + struct iw_point *erq = &(wrqu->encoding); + int len, key; + struct ieee80211_crypt_data *crypt; + + IEEE80211_DEBUG_WX("GET_ENCODE\n"); + + key = erq->flags & IW_ENCODE_INDEX; + if (key) { + if (key > WEP_KEYS) + return -EINVAL; + key--; + } else + key = ieee->tx_keyidx; + + crypt = ieee->crypt[key]; + erq->flags = key + 1; + + if (crypt == NULL || crypt->ops == NULL) { + erq->length = 0; + erq->flags |= IW_ENCODE_DISABLED; + return 0; + } + + if (strcmp(crypt->ops->name, "WEP") != 0) { + /* only WEP is supported with wireless extensions, so just + * report that encryption is used */ + erq->length = 0; + erq->flags |= IW_ENCODE_ENABLED; + return 0; + } + + len = crypt->ops->get_key(keybuf, WEP_KEY_LEN, NULL, crypt->priv); + erq->length = (len >= 0 ? len : 0); + + erq->flags |= IW_ENCODE_ENABLED; + + if (ieee->open_wep) + erq->flags |= IW_ENCODE_OPEN; + else + erq->flags |= IW_ENCODE_RESTRICTED; + + return 0; +} + +EXPORT_SYMBOL(ieee80211_wx_get_scan); +EXPORT_SYMBOL(ieee80211_wx_set_encode); +EXPORT_SYMBOL(ieee80211_wx_get_encode);