/* src/prism2/driver/prism2sta.c * * Implements the station functionality for prism2 * * Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. * -------------------------------------------------------------------- * * linux-wlan * * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * Alternatively, the contents of this file may be used under the * terms of the GNU Public License version 2 (the "GPL"), in which * case the provisions of the GPL are applicable instead of the * above. If you wish to allow the use of your version of this file * only under the terms of the GPL and not to allow others to use * your version of this file under the MPL, indicate your decision * by deleting the provisions above and replace them with the notice * and other provisions required by the GPL. If you do not delete * the provisions above, a recipient may use your version of this * file under either the MPL or the GPL. * * -------------------------------------------------------------------- * * Inquiries regarding the linux-wlan Open Source project can be * made directly to: * * AbsoluteValue Systems Inc. * info@linux-wlan.com * http://www.linux-wlan.com * * -------------------------------------------------------------------- * * Portions of the development of this software were funded by * Intersil Corporation as part of PRISM(R) chipset product development. * * -------------------------------------------------------------------- * * This file implements the module and linux pcmcia routines for the * prism2 driver. * * -------------------------------------------------------------------- */ /*================================================================*/ /* System Includes */ #include #define WLAN_DBVAR prism2_debug #include #include #include #include #include #include #include #include #include #include #include #if (WLAN_HOSTIF == WLAN_PCMCIA) #include #include #include #include #include #include #include #include #include #endif #if ((WLAN_HOSTIF == WLAN_PLX) || (WLAN_HOSTIF == WLAN_PCI)) #include #endif /*================================================================*/ /* Project Includes */ #include #include #include #include #include #include #include #include #include #include #include #include /*================================================================*/ /* Local Constants */ #define PRISM2STA_MAGIC (0x4a2d) #define INFOFRM_LEN_MAX sizeof(hfa384x_ScanResults_t) #if (WLAN_HOSTIF == WLAN_PLX) #define PLX_ATTR_SIZE 0x1000 /* Attribute memory size - 4K bytes */ #define COR_OFFSET 0x3e0 /* COR attribute offset of Prism2 PC card */ #define COR_VALUE 0x41 /* Enable PC card with irq in level trigger */ /* Eumitcom PCI WL11000 PCI Adapter (PLX) board device+vendor ID */ #define PCIVENDOR_EUMITCOM 0x1638UL #define PCIDEVICE_WL11000 0x1100UL /* Global Sun Tech GL24110P PCI Adapter (PLX) board device+vendor ID */ #define PCIVENDOR_GLOBALSUN 0x16abUL /* #define PCIDEVICE_GL24110P 0x1101UL -- was 1101, now 1102 for netgear WPC11 -jms1@jms1.net */ #define PCIDEVICE_GL24110P 0x1102UL /* Netgear MA301 PCI Adapter (PLX) board device+vendor ID */ #define PCIVENDOR_NETGEAR 0x1385UL #define PCIDEVICE_MA301 0x4100UL /* PCI Class & Sub-Class code, Network-'Other controller' */ #define PCI_CLASS_NETWORK_OTHERS 0x280 #endif #if (WLAN_HOSTIF == WLAN_PCI) #define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE) #define PCI_SIZE 0x1000 /* Memory size - 4K bytes */ /* ISL3874A 11Mb/s WLAN controller */ #define PCIVENDOR_INTERSIL 0x1260UL #define PCIDEVICE_ISL3874 0x3873UL /* [MSM] yeah I know...the ID says 3873. Trust me, it's a 3874. */ /* PCI Class & Sub-Class code, Network-'Other controller' */ #define PCI_CLASS_NETWORK_OTHERS 0x280 #endif /*================================================================*/ /* Local Macros */ #define PRISM2_INFOBUF_MAX (sizeof(hfa384x_HandoverAddr_t)) #define PRISM2_TXBUF_MAX (sizeof(hfa384x_tx_frame_t) + \ WLAN_DATA_MAXLEN - \ WLAN_WEP_IV_LEN - \ WLAN_WEP_ICV_LEN + 2) /*================================================================*/ /* Local Types */ /*================================================================*/ /* Local Static Definitions */ #if (WLAN_HOSTIF == WLAN_PCMCIA) #define DRIVER_SUFFIX "_cs" #elif (WLAN_HOSTIF == WLAN_PLX) #define DRIVER_SUFFIX "_plx" typedef char* dev_info_t; #elif (WLAN_HOSTIF == WLAN_PCI) #define DRIVER_SUFFIX "_pci" typedef char* dev_info_t; /* #else , TODO: Fix .depend generation for multiple driver build */ /* #error "HOSTIF unsupported or undefined!" */ #endif static char *version = "prism2" DRIVER_SUFFIX ".o: " WLAN_RELEASE; static dev_info_t dev_info = "prism2" DRIVER_SUFFIX; #if (WLAN_HOSTIF == WLAN_PCMCIA) static dev_link_t *dev_list = NULL; /* head of instance list */ #elif ((WLAN_HOSTIF == WLAN_PLX) || (WLAN_HOSTIF == WLAN_PCI)) typedef struct pci_link { void *priv; struct pci_link *next; } pci_link_t; static pci_link_t *dev_list = NULL; /* head of instance list */ #endif /*-----------------------------------------------------------------*/ #if (WLAN_HOSTIF == WLAN_PLX || WLAN_HOSTIF == WLAN_PCI) #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) /* NOTE: The pci code in this driver is written to the * 2.4.x (or some 2.3.x and newer) pci support. The declarations * inside this #if block are to provide backward compatibility to 2.2.x. * NOTE2: If you want to modify the pci support, please make sure you do * it in a 2.4.x compliant way. */ struct pci_device_id { unsigned int vendor, device; unsigned int subvendor, subdevice; unsigned int class, class_mask; unsigned long driver_data; }; struct pci_driver { struct {int a;} dummy; char *name; const struct pci_device_id *id_table; /* NULL if wants all devices */ int (*probe) (struct pci_dev *dev, const struct pci_device_id *id); void (*remove) (struct pci_dev *dev); int (*save_state) (struct pci_dev *dev, u32 state); int (*suspend)(struct pci_dev *dev, u32 state); int (*resume) (struct pci_dev *dev); int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); }; #define PCI_ANY_ID 0xffff static int pci_register_driver(struct pci_driver *drv); static void pci_unregister_driver(struct pci_driver *drv); #define pci_resource_start(a,b) ((a)->base_address[(b)] & ~0xf) #define pci_enable_device(a) 0 #endif /* (WLAN_HOSTIF == WLAN_PLX || WLAN_HOSTIF == WLAN_PCI) */ #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) */ /*-----------------------------------------------------------------*/ #if (WLAN_HOSTIF == WLAN_PLX) static struct pci_device_id pci_id_tbl[] = { { PCIVENDOR_EUMITCOM, PCIDEVICE_WL11000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* Driver data, we just put the name here */ (unsigned long)"Eumitcom WL11000 PCI card" }, { PCIVENDOR_GLOBALSUN, PCIDEVICE_GL24110P, PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* Driver data, we just put the name here */ (unsigned long)"Global Sun Tech GL24110P PCI card" }, { PCIVENDOR_NETGEAR, PCIDEVICE_MA301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* Driver data, we just put the name here */ (unsigned long)"Global Sun Tech GL24110P PCI card" }, { 0, 0, 0, 0, 0, 0, 0 } }; /* Function declared here because of ptr reference below */ static int prism2sta_probe_plx(struct pci_dev *pdev, const struct pci_device_id *id); struct pci_driver prism2_plx_drv_id = { {}, "prism2_plx", /* Driver name */ pci_id_tbl, /* id table */ prism2sta_probe_plx, /* probe function */ NULL, /* remove function */ NULL, /* save_state function */ NULL, /* suspend function */ NULL, /* resume function */ NULL, /* enable_wake function */ }; #endif /* WLAN_PLX */ #if (WLAN_HOSTIF == WLAN_PCI) static struct pci_device_id pci_id_tbl[] = { { PCIVENDOR_INTERSIL, PCIDEVICE_ISL3874, PCI_ANY_ID, PCI_ANY_ID, 0, 0, /* Driver data, we just put the name here */ (unsigned long)"Intersil Prism2.5 ISL3874 11Mb/s WLAN Controller" }, { 0, 0, 0, 0, 0, 0, 0 } }; /* Function declared here because of ptr reference below */ static int prism2sta_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id); struct pci_driver prism2_pci_drv_id = { {}, "prism2_pci", /* Driver name */ pci_id_tbl, /* id table */ prism2sta_probe_pci, /* probe function */ NULL, /* remove function */ NULL, /* save_state function */ NULL, /* suspend function */ NULL, /* resume function */ NULL, /* enable_wake function */ }; #endif /* WLAN_PCI */ /*----------------------------------------------------------------*/ /* --Module Parameters */ int prism2_debug=0; /* Debug output level, */ static u_int prism2_irq_evread_max=20; /* Maximum number of * ev_reads (loops) * in irq handler */ MODULE_PARM( prism2_debug, "i"); MODULE_PARM( prism2_irq_evread_max, "i"); #if (WLAN_HOSTIF == WLAN_PCMCIA) static u_int irq_mask = 0xdeb8; /* Interrupt mask */ static int irq_list[4] = { -1 }; /* Interrupt list */ static u_int prism2_ignorevcc=0; /* Boolean, if set, we * ignore what the Vcc * is set to and what the CIS * says. */ MODULE_PARM( irq_mask, "i"); MODULE_PARM( irq_list, "1-4i"); MODULE_PARM( prism2_ignorevcc, "i"); #endif /*================================================================*/ /* Local Function Declarations */ int init_module(void); void cleanup_module(void); #if (WLAN_HOSTIF == WLAN_PCMCIA) dev_link_t *prism2sta_attach(void); static void prism2sta_detach(dev_link_t *link); static void prism2sta_config(dev_link_t *link); static void prism2sta_release(UINT32 arg); static int prism2sta_event (event_t event, int priority, event_callback_args_t *args); #endif static int prism2sta_open(wlandevice_t *wlandev); static int prism2sta_close(wlandevice_t *wlandev); static void prism2sta_reset(wlandevice_t *wlandev ); static int prism2sta_txframe(wlandevice_t *wlandev, wlan_pb_t *pb); static int prism2sta_mlmerequest(wlandevice_t *wlandev, p80211msg_t *msg); static void prism2sta_interrupt(int irq, void *dev_id, struct pt_regs *regs); static void prism2sta_int_dtim(wlandevice_t *wlandev); static void prism2sta_int_infdrop(wlandevice_t *wlandev); static void prism2sta_int_info(wlandevice_t *wlandev); static void prism2sta_int_txexc(wlandevice_t *wlandev); static void prism2sta_int_tx(wlandevice_t *wlandev); static void prism2sta_int_rx(wlandevice_t *wlandev); static int prism2sta_int_rx_typedrop( wlandevice_t *wlandev, UINT16 fc); static void prism2sta_int_rxmonitor( wlandevice_t *wlandev, UINT16 rxfid, hfa384x_rx_frame_t *rxdesc); static void prism2sta_int_alloc(wlandevice_t *wlandev); static void prism2sta_inf_handover( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_tallies( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_scanresults( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_chinforesults( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_linkstatus( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_assocstatus( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_authreq( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); static void prism2sta_inf_psusercnt( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf); /*================================================================*/ /* Function Definitions */ #ifdef USE_FID_STACK inline int txfid_stack_empty(prism2sta_priv_t *priv) { /* paranoid? */ int result; #if 0 UINT32 flags; spin_lock_irqsave(&(priv->txfid_lock),flags); #endif result = (priv->txfid_top == 0); #if 0 spin_unlock_irqrestore(&(priv->txfid_lock), flags); #endif return priv->txfid_top == 0; } inline int txfid_stack_full(prism2sta_priv_t *priv) { return (priv->txfid_top < PRISM2_FIDSTACKLEN_MAX) ? 0 : 1; } inline void txfid_push(prism2sta_priv_t *priv, UINT16 val) { #if 0 UINT32 flags; spin_lock_irqsave(&(priv->txfid_lock),flags); #endif if ( priv->txfid_top < PRISM2_FIDSTACKLEN_MAX ) { priv->txfid_stack[priv->txfid_top] = val; priv->txfid_top++; } #if 0 spin_unlock_irqrestore(&(priv->txfid_lock), flags); #endif return; } inline UINT16 txfid_pop(prism2sta_priv_t *priv) { UINT16 result; #if 0 UINT32 flags; spin_lock_irqsave(&(priv->txfid_lock),flags); #endif if ( priv->txfid_top > 0 ) { priv->txfid_top--; result = priv->txfid_stack[priv->txfid_top]; } else { result = 0; } #if 0 spin_unlock_irqrestore(&(priv->txfid_lock), flags); #endif return result; } #else /* use queue */ UINT16 txfid_queue_full(prism2sta_priv_t *priv) { return (priv->txfid_head == ((priv->txfid_tail + 1) % priv->txfid_N)) ? 1 : 0; } UINT16 txfid_queue_empty(prism2sta_priv_t *priv) { return (priv->txfid_head == priv->txfid_tail) ? 1 : 0; } UINT16 txfid_queue_remove(prism2sta_priv_t *priv) { UINT16 result= 0; if (txfid_queue_empty(priv)) printk("queue empty.\n"); else { result = priv->txfid_queue[priv->txfid_head]; priv->txfid_head = (priv->txfid_head + 1) % priv->txfid_N; } return (UINT16)result; } INT16 txfid_queue_add(prism2sta_priv_t *priv, UINT16 val) { INT16 result = 0; if (txfid_queue_full(priv)) { result = -1; printk("queue full.\n"); } else { priv->txfid_queue[priv->txfid_tail] = val; result = priv->txfid_tail; priv->txfid_tail = (priv->txfid_tail + 1) % priv->txfid_N; } return result; } #endif /*---------------------------------------------------------------- * dmpmem * * Debug utility function to dump memory to the kernel debug log. * * Arguments: * buf ptr data we want dumped * len length of data * * Returns: * nothing * Side effects: * * Call context: * process thread * interrupt ----------------------------------------------------------------*/ inline void dmpmem(void *buf, int n) { int c; for ( c= 0; c < n; c++) { if ( (c % 16) == 0 ) printk(KERN_DEBUG"dmp[%d]: ", c); printk("%02x ", ((UINT8*)buf)[c]); if ( (c % 16) == 15 ) printk("\n"); } if ( (c % 16) != 0 ) printk("\n"); } #if (WLAN_HOSTIF == WLAN_PCMCIA) /*---------------------------------------------------------------- * cs_error * * Utility function to print card services error messages. * * Arguments: * handle client handle identifying this CS client * func CS function number that generated the error * ret CS function return code * * Returns: * nothing * Side effects: * * Call context: * process thread * interrupt ----------------------------------------------------------------*/ static void cs_error(client_handle_t handle, int func, int ret) { #if CS_RELEASE_CODE < 0x2911 CardServices(ReportError, dev_info, (void *)func, (void *)ret); #else error_info_t err = { func, ret }; CardServices(ReportError, handle, &err); #endif } #endif /*-----------------------------------------------------------------*/ #if (WLAN_HOSTIF == WLAN_PLX || WLAN_HOSTIF == WLAN_PCI) #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) /* NOTE: See the note above about 2.4.x and pci support */ /*---------------------------------------------------------------- * pci_register_driver * pci_unregister_driver * * 2.4.x PCI support function emulation for 2.2.x kernels. * * Arguments: * drv 2.4.x type driver description block * * Returns: * 0 success * ~0 error * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ static int pci_register_driver(struct pci_driver *drv) { int nfound = 0; struct pci_dev *pdev; const struct pci_device_id *id_tbl=drv->id_table; DBFENTER; /* Scan the bus for matching devices */ if (pcibios_present()) { static int pci_index = 0; UINT8 pci_bus; UINT8 pci_device_fn; for(;pci_index < 0xff; pci_index++) { u16 vendor; u16 device; int idx; if (pcibios_find_class(PCI_CLASS_NETWORK_OTHER<<8,pci_index, &pci_bus, &pci_device_fn) != PCIBIOS_SUCCESSFUL) break; pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &vendor); pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &device); for( idx = 0; id_tbl[idx].vendor; idx++) { if ( id_tbl[idx].vendor == vendor && id_tbl[idx].device == device ) break; /* found one! */ } if (id_tbl[idx].vendor == 0) continue; nfound++; /* Probably an invalid assumption...but we'll assume the * card is alive for now. TODO: need to add power management * stuff here. */ /* Collect the pci_device structure */ pdev = pci_find_slot(pci_bus, pci_device_fn); /* Call the driver probe function */ (*(drv->probe))(pdev, &(id_tbl[idx])); } } DBFEXIT; return nfound; } static void pci_unregister_driver(struct pci_driver *drv) { return; } #endif #endif /*---------------------------------------------------------------- * prism2sta_open * * WLAN device open method. Called from p80211netdev when kernel * device open (start) method is called in response to the * SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP * from clear to set. * * Arguments: * wlandev wlan device structure * * Returns: * 0 success * >0 f/w reported error * <0 driver reported error * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ int prism2sta_open(wlandevice_t *wlandev) { int result = 0; prism2sta_priv_t *priv = (prism2sta_priv_t*)wlandev->priv; int i; DBFENTER; /* Make sure at least 1 port is enabled */ for ( i = 0; i < HFA384x_NUMPORTS_MAX; i++) { if ( priv->hw->port_enabled[i] != 0 ) break; } if ( i >= HFA384x_NUMPORTS_MAX ) { result = -ENOMEDIUM; } else { MOD_INC_USE_COUNT; } /* We don't currently have to do anything else. */ /* The setup of the MAC should have been done previously */ /* via the mlme commands. */ /* Higher layers know we're ready from dev->start==1 and */ /* dev->tbusy==0. Our rx path knows to pass up received */ /* frames because of dev->flags&IFF_UP is true. */ DBFEXIT; return result; } /*---------------------------------------------------------------- * prism2sta_close * * WLAN device close method. Called from p80211netdev when kernel * device close method is called in response to the * SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP * from set to clear. * * Arguments: * wlandev wlan device structure * * Returns: * 0 success * >0 f/w reported error * <0 driver reported error * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ int prism2sta_close(wlandevice_t *wlandev) { DBFENTER; MOD_DEC_USE_COUNT; /* We don't currently have to do anything else. */ /* Higher layers know we're not ready from dev->start==0 and */ /* dev->tbusy==1. Our rx path knows to not pass up received */ /* frames because of dev->flags&IFF_UP is false. */ /* We leave the port enabled in case someone needs to receive */ /* Info frames or send Notify frames. All rx data frames after this */ /* will be dropped. */ DBFEXIT; return 0; } /*---------------------------------------------------------------- * prism2sta_reset * * Not currently implented. * * Arguments: * wlandev wlan device structure * none * * Returns: * nothing * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ void prism2sta_reset(wlandevice_t *wlandev ) { DBFENTER; DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_txframe * * Takes a frame from p80211 and queues it for transmission. * * Arguments: * wlandev wlan device structure * pb packet buffer struct. Contains an 802.11 * data frame. * Returns: * 0 Success and more buffs available * 1 Success but no more buffs * 2 Allocation failure * 4 Buffer full or queue busy * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ int prism2sta_txframe(wlandevice_t *wlandev, wlan_pb_t *pb ) { prism2sta_priv_t *priv = (prism2sta_priv_t*)wlandev->priv; hfa384x_t *hw = priv->hw; hfa384x_tx_frame_t txdesc; UINT16 proto; UINT8 dscp; UINT16 macq = 0; UINT16 fid; int result; DBFENTER; #if defined(DETHER) printk(KERN_DEBUG"ethfrm[x] - [%d]:\n", pb->ethbuflen); dmpmem(pb->ethbuf, pb->ethbuflen); #endif /* Build Tx frame structure */ /* Set up the control field */ memset(&txdesc, 0, sizeof(txdesc)); /* Tx complete and Tx exception disable per dleach. Might be causing * buf depletion */ #if 1 txdesc.tx_control = HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | HFA384x_TX_TXEX_SET(1) | HFA384x_TX_TXOK_SET(1); #else txdesc.tx_control = HFA384x_TX_MACPORT_SET(0) | HFA384x_TX_STRUCTYPE_SET(1) | HFA384x_TX_TXEX_SET(0) | HFA384x_TX_TXOK_SET(0); #endif txdesc.tx_control = host2hfa384x_16(txdesc.tx_control); /* Set up the 802.11 header */ if ( priv->priv_invoked ) { pb->p80211_hdr->a3.fc |= host2ieee16(WLAN_SET_FC_ISWEP(1)); } memcpy(&(txdesc.frame_control), pb->p80211_hdr, WLAN_HDR_A3_LEN); /* Set the len, complicated because of pieces in pb */ txdesc.data_len = pb->p80211buflen - WLAN_HDR_A3_LEN; /* llc+snap? */ txdesc.data_len += pb->p80211_payloadlen; txdesc.data_len = host2hfa384x_16(txdesc.data_len); /* Since tbusy is set whenever the stack is empty, there should * always be something on the stack if we get to this point. * [MSM]: NOT TRUE!!!!! so I added the test of fid below. */ /* Allocate FID */ #ifdef USE_FID_STACK fid = txfid_pop(priv); #else fid = txfid_queue_remove(priv); #endif if ( fid == 0 ) { /* stack or queue was empty */ return 4; } /* Copy descriptor part to FID */ #if defined(DDESC) printk(KERN_DEBUG "Desc[0] - [%d]: \n", sizeof(txdesc)); dmpmem(&txdesc, sizeof(txdesc)); #endif result = hfa384x_copy_to_bap(hw, hw->bap, fid, 0, &txdesc, sizeof(txdesc)); if ( result ) { WLAN_LOG_DEBUG3(1, "copy_to_bap(%04x, 0, %d) failed, result=0x%x\n", fid, sizeof(txdesc), result); /* put the fid back in the queue */ txfid_queue_add(priv, fid); result = 3; goto failed; } /* Copy 802.11 data to FID */ if ( pb->p80211buflen > WLAN_HDR_A3_LEN ) { /* copy llc+snap hdr */ #if defined(DLLC) printk(KERN_DEBUG "Hdr[%d] - [%d]:\n", sizeof(txdesc), pb->p80211buflen - WLAN_HDR_A3_LEN); dmpmem(pb->p80211buf + WLAN_HDR_A3_LEN, pb->p80211buflen - WLAN_HDR_A3_LEN); #endif result = hfa384x_copy_to_bap( hw, hw->bap, fid, sizeof(txdesc), pb->p80211buf + WLAN_HDR_A3_LEN, pb->p80211buflen - WLAN_HDR_A3_LEN); if ( result ) { WLAN_LOG_DEBUG4(1, "copy_to_bap(%04x, %d, %d) failed, result=0x%x\n", fid, sizeof(txdesc), pb->p80211buflen - WLAN_HDR_A3_LEN, result); /* put the fid back in the queue */ txfid_queue_add(priv, fid); result = 3; goto failed; } } #if defined(D80211) printk(KERN_DEBUG "Data[%d] - [%d]:\n", sizeof(txdesc) + pb->p80211buflen - WLAN_HDR_A3_LEN, pb->p80211_payloadlen); dmpmem(pb->p80211_payload, pb->p80211_payloadlen); #endif result = hfa384x_copy_to_bap( hw, hw->bap, fid, sizeof(txdesc) + pb->p80211buflen - WLAN_HDR_A3_LEN, pb->p80211_payload, pb->p80211_payloadlen); if ( result ) { WLAN_LOG_DEBUG4(1, "copy_to_bap(%04x, %d, %d) failed, result=0x%x\n", fid, sizeof(txdesc) + pb->p80211buflen - WLAN_HDR_A3_LEN, pb->p80211_payloadlen, result); /* put the fid back in the queue */ txfid_queue_add(priv, fid); result = 3; goto failed; } /* Determine if we're doing MM queuing */ if ( priv->qos_enable && priv->mm_mods ) { /* Find protocol field */ if ( pb->p80211buflen == WLAN_HDR_A3_LEN ) { proto = ntohs(*((UINT16*)(pb->p80211_payload+6))); } else { proto = ntohs(*((UINT16*)(pb->p80211buf+24+6))); } /* Is it IP? */ if (proto == 0x0800 ) { /* Get the DSCP */ if ( pb->p80211buflen == WLAN_HDR_A3_LEN ) { /* LLC+SNAP is in payload buf */ dscp = pb->p80211_payload[3+5+1]; } else { /* LLC+SNAP is in header buf */ dscp = pb->p80211_payload[1]; } dscp &= ~(BIT0 | BIT1); /* RFC bit 6 and 7 */ dscp >>= 2; macq = priv->qos_dscpmap[dscp]; } else { macq = 1; /* best effort */ } } /* Issue Tx command */ result = hfa384x_cmd_transmit(hw, HFA384x_TXCMD_RECL, macq, fid); if ( result != 0 ) { WLAN_LOG_DEBUG2(1,"cmd_tx(%04x) failed, result=%d", fid, result); result = 2; goto failed; } /* indicate we haven't any buffers, int_alloc will clear */ #ifdef USE_FID_STACK result = txfid_stack_empty(priv) ? 1 : 0; #else result = txfid_queue_empty(priv) ? 1 : 0; #endif failed: DBFEXIT; return result; } /*---------------------------------------------------------------- * prism2sta_mlmerequest * * wlan command message handler. All we do here is pass the message * over to the prism2sta_mgmt_handler. * * Arguments: * wlandev wlan device structure * msg wlan command message * Returns: * 0 success * <0 successful acceptance of message, but we're * waiting for an async process to finish before * we're done with the msg. When the asynch * process is done, we'll call the p80211 * function p80211req_confirm() . * >0 An error occurred while we were handling * the message. * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ int prism2sta_mlmerequest(wlandevice_t *wlandev, p80211msg_t *msg) { int result = 0; DBFENTER; switch( msg->msgcode ) { case DIDmsg_dot11req_mibget : WLAN_LOG_DEBUG0(2,"Received mibget request\n"); result = prism2mgmt_mibset_mibget(wlandev, msg); break; case DIDmsg_dot11req_mibset : WLAN_LOG_DEBUG0(2,"Received mibset request\n"); result = prism2mgmt_mibset_mibget(wlandev, msg); break; case DIDmsg_dot11req_powermgmt : WLAN_LOG_DEBUG0(2,"Received powermgmt request\n"); result = prism2mgmt_powermgmt(wlandev, msg); break; case DIDmsg_dot11req_scan : WLAN_LOG_DEBUG0(2,"Received scan request\n"); result = prism2mgmt_scan(wlandev, msg); break; case DIDmsg_dot11req_scan_results : WLAN_LOG_DEBUG0(2,"Received scan_results request\n"); result = prism2mgmt_scan_results(wlandev, msg); break; case DIDmsg_dot11req_join : WLAN_LOG_DEBUG0(2,"Received join request\n"); result = prism2mgmt_join(wlandev, msg); break; case DIDmsg_dot11req_authenticate : WLAN_LOG_DEBUG0(2,"Received authenticate request\n"); result = prism2mgmt_authenticate(wlandev, msg); break; case DIDmsg_dot11req_deauthenticate : WLAN_LOG_DEBUG0(2,"Received mlme deauthenticate request\n"); result = prism2mgmt_deauthenticate(wlandev, msg); break; case DIDmsg_dot11req_associate : WLAN_LOG_DEBUG0(2,"Received mlme associate request\n"); result = prism2mgmt_associate(wlandev, msg); break; case DIDmsg_dot11req_reassociate : WLAN_LOG_DEBUG0(2,"Received mlme reassociate request\n"); result = prism2mgmt_reassociate(wlandev, msg); break; case DIDmsg_dot11req_disassociate : WLAN_LOG_DEBUG0(2,"Received mlme disassociate request\n"); result = prism2mgmt_disassociate(wlandev, msg); break; case DIDmsg_dot11req_reset : WLAN_LOG_DEBUG0(2,"Received mlme reset request\n"); result = prism2mgmt_reset(wlandev, msg); break; case DIDmsg_dot11req_start : WLAN_LOG_DEBUG0(2,"Received mlme start request\n"); result = prism2mgmt_start(wlandev, msg); break; case DIDmsg_lnxreq_wlansniff : WLAN_LOG_DEBUG0(2,"Received mlme wlansniff request\n"); result = prism2mgmt_wlansniff(wlandev, msg); break; case DIDmsg_p2req_readpda : WLAN_LOG_DEBUG0(2,"Received mlme readpda request\n"); result = prism2mgmt_readpda(wlandev, msg); break; case DIDmsg_p2req_readcis : WLAN_LOG_DEBUG0(2,"Received mlme readcis request\n"); result = prism2mgmt_readcis(wlandev, msg); break; case DIDmsg_p2req_auxport_state : WLAN_LOG_DEBUG0(2,"Received mlme auxport_state request\n"); result = prism2mgmt_auxport_state(wlandev, msg); break; case DIDmsg_p2req_auxport_read : WLAN_LOG_DEBUG0(2,"Received mlme auxport_read request\n"); result = prism2mgmt_auxport_read(wlandev, msg); break; case DIDmsg_p2req_auxport_write : WLAN_LOG_DEBUG0(2,"Received mlme auxport_write request\n"); result = prism2mgmt_auxport_write(wlandev, msg); break; case DIDmsg_p2req_low_level : WLAN_LOG_DEBUG0(2,"Received mlme low_level request\n"); result = prism2mgmt_low_level(wlandev, msg); break; case DIDmsg_p2req_test_command : WLAN_LOG_DEBUG0(2,"Received mlme test_command request\n"); result = prism2mgmt_test_command(wlandev, msg); break; case DIDmsg_p2req_mmi_read : WLAN_LOG_DEBUG0(2,"Received mlme mmi_read request\n"); result = prism2mgmt_mmi_read(wlandev, msg); break; case DIDmsg_p2req_mmi_write : WLAN_LOG_DEBUG0(2,"Received mlme mmi_write request\n"); result = prism2mgmt_mmi_write(wlandev, msg); break; case DIDmsg_p2req_ramdl_state : WLAN_LOG_DEBUG0(2,"Received mlme ramdl_state request\n"); result = prism2mgmt_ramdl_state(wlandev, msg); break; case DIDmsg_p2req_ramdl_write : WLAN_LOG_DEBUG0(2,"Received mlme ramdl_write request\n"); result = prism2mgmt_ramdl_write(wlandev, msg); break; case DIDmsg_p2req_flashdl_state : WLAN_LOG_DEBUG0(2,"Received mlme flashdl_state request\n"); result = prism2mgmt_flashdl_state(wlandev, msg); break; case DIDmsg_p2req_flashdl_write : WLAN_LOG_DEBUG0(2,"Received mlme flashdl_write request\n"); result = prism2mgmt_flashdl_write(wlandev, msg); break; case DIDmsg_p2req_mm_dscpmap : WLAN_LOG_DEBUG0(2,"Received mlme mm_dscpmap request\n"); result = prism2mgmt_mm_dscpmap(wlandev, msg); break; case DIDmsg_p2req_dump_state : WLAN_LOG_DEBUG0(2,"Received mlme dump_state request\n"); result = prism2mgmt_dump_state(wlandev, msg); break; case DIDmsg_p2req_channel_info : WLAN_LOG_DEBUG0(2,"Received mlme channel_info request\n"); result = prism2mgmt_channel_info(wlandev, msg); break; case DIDmsg_p2req_channel_info_results : WLAN_LOG_DEBUG0(2,"Received mlme channel_info_results request\n"); result = prism2mgmt_channel_info_results(wlandev, msg); break; case DIDmsg_lnxreq_autojoin : WLAN_LOG_DEBUG0(2,"Received mlme autojoin request\n"); result = prism2mgmt_autojoin(wlandev, msg); break; case DIDmsg_p2req_enable : WLAN_LOG_DEBUG0(2,"Received mlme enable request\n"); result = prism2mgmt_enable(wlandev, msg); break; default: WLAN_LOG_WARNING1("Unknown mgmt request message 0x%08lx", msg->msgcode); break; } DBFEXIT; return result; } /*---------------------------------------------------------------- * prism2sta_initmac * * Issue the commands to get the MAC controller into its intial * state. * * Arguments: * wlandev wlan device structure * * Returns: * 0 success * >0 f/w reported error * <0 driver reported error * * Side effects: * * Call context: * process thread ----------------------------------------------------------------*/ int prism2sta_initmac(wlandevice_t *wlandev) { int result = 0; prism2sta_priv_t *priv = (prism2sta_priv_t*)wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 reg; int i; int j; UINT8 snum[HFA384x_RID_NICSERIALNUMBER_LEN]; char pstr[(HFA384x_RID_NICSERIALNUMBER_LEN * 4) + 1]; DBFENTER; /* call initialize */ result = hfa384x_cmd_initialize(hw); if (result != 0) { WLAN_LOG_ERROR0("Initialize command failed.\n"); goto failed; } /* Initialize FID stack */ #ifdef USE_FID_STACK spin_lock_init(&(priv->txfid_lock)); priv->txfid_top = PRISM2_FIDSTACKLEN_MAX; memset(priv->txfid_stack, 0, sizeof(priv->txfid_stack)); #else priv->txfid_head = 0; priv->txfid_tail = 0; priv->txfid_N = PRISM2_FIDSTACKLEN_MAX; memset(priv->txfid_queue, 0, sizeof(priv->txfid_queue)); #endif /* make sure interrupts are disabled and any layabout events cleared */ hfa384x_setreg(hw, 0, HFA384x_INTEN); hfa384x_setreg(hw, 0xffff, HFA384x_EVACK); /* Read the PDA */ result = hfa384x_drvr_readpda(hw, priv->pda, HFA384x_PDA_LEN_MAX); if ( result != 0) { WLAN_LOG_DEBUG0(2,"drvr_readpda() failed\n"); goto failed; } /* Allocate tx and notify FIDs */ /* First, tx */ for ( i = 0; i < PRISM2_FIDSTACKLEN_MAX-1; i++) { result = hfa384x_cmd_allocate(hw, PRISM2_TXBUF_MAX); if (result != 0) { WLAN_LOG_ERROR0("Allocate(tx) command failed.\n"); goto failed; } j = 0; do { reg = hfa384x_getreg(hw, HFA384x_EVSTAT); udelay(10); j++; } while ( !HFA384x_EVSTAT_ISALLOC(reg) && j < 50); /* 50 is timeout */ if ( j >= 50 ) { WLAN_LOG_ERROR0("Timed out waiting for evalloc(tx).\n"); result = -ETIMEDOUT; goto failed; } #ifdef USE_FID_STACK priv->txfid_stack[i] = hfa384x_getreg(hw, HFA384x_ALLOCFID); #else txfid_queue_add(priv, hfa384x_getreg(hw, HFA384x_ALLOCFID)); #endif reg = HFA384x_EVACK_ALLOC_SET(1); hfa384x_setreg(hw, reg, HFA384x_EVACK); #ifdef USE_FID_STACK WLAN_LOG_DEBUG2(1,"priv->txfid_stack[%d]=0x%04x\n",i,priv->txfid_stack[i]); #endif } /* Now, the info frame fid */ result = hfa384x_cmd_allocate(hw, PRISM2_INFOBUF_MAX); if (result != 0) { goto failed; WLAN_LOG_ERROR0("Allocate(tx) command failed.\n"); } i = 0; do { reg = hfa384x_getreg(hw, HFA384x_EVSTAT); udelay(10); i++; } while ( !HFA384x_EVSTAT_ISALLOC(reg) && i < 50); /* 50 is timeout */ if ( i >= 50 ) { WLAN_LOG_ERROR0("Timed out waiting for evalloc(info).\n"); result = -ETIMEDOUT; goto failed; } priv->infofid = hfa384x_getreg(hw, HFA384x_ALLOCFID); reg = HFA384x_EVACK_ALLOC_SET(1); hfa384x_setreg(hw, reg, HFA384x_EVACK); WLAN_LOG_DEBUG1(1,"priv->infofid=0x%04x\n", priv->infofid); /* Collect version and compatibility info */ /* Some are critical, some are not */ /* NIC identity */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICIDENTITY, &priv->ident_nic, sizeof(hfa384x_compident_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve NICIDENTITY\n"); goto failed; } /* get all the nic id fields in host byte order */ priv->ident_nic.id = hfa384x2host_16(priv->ident_nic.id); priv->ident_nic.variant = hfa384x2host_16(priv->ident_nic.variant); priv->ident_nic.major = hfa384x2host_16(priv->ident_nic.major); priv->ident_nic.minor = hfa384x2host_16(priv->ident_nic.minor); WLAN_LOG_INFO4( "ident: nic h/w: id=0x%02x %d.%d.%d\n", priv->ident_nic.id, priv->ident_nic.major, priv->ident_nic.minor, priv->ident_nic.variant); /* Primary f/w identity */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRIIDENTITY, &priv->ident_pri_fw, sizeof(hfa384x_compident_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve PRIIDENTITY\n"); goto failed; } /* get all the private fw id fields in host byte order */ priv->ident_pri_fw.id = hfa384x2host_16(priv->ident_pri_fw.id); priv->ident_pri_fw.variant = hfa384x2host_16(priv->ident_pri_fw.variant); priv->ident_pri_fw.major = hfa384x2host_16(priv->ident_pri_fw.major); priv->ident_pri_fw.minor = hfa384x2host_16(priv->ident_pri_fw.minor); WLAN_LOG_INFO4( "ident: pri f/w: id=0x%02x %d.%d.%d\n", priv->ident_pri_fw.id, priv->ident_pri_fw.major, priv->ident_pri_fw.minor, priv->ident_pri_fw.variant); /* Station (Secondary?) f/w identity */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STAIDENTITY, &priv->ident_sta_fw, sizeof(hfa384x_compident_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve STAIDENTITY\n"); goto failed; } /* get all the station fw id fields in host byte order */ priv->ident_sta_fw.id = hfa384x2host_16(priv->ident_sta_fw.id); priv->ident_sta_fw.variant = hfa384x2host_16(priv->ident_sta_fw.variant); priv->ident_sta_fw.major = hfa384x2host_16(priv->ident_sta_fw.major); priv->ident_sta_fw.minor = hfa384x2host_16(priv->ident_sta_fw.minor); /* strip out the 'special' variant bits */ priv->mm_mods = priv->ident_sta_fw.variant & (BIT14 | BIT15); priv->ident_sta_fw.variant &= ~((UINT16)(BIT14 | BIT15)); if ( priv->ident_sta_fw.id == 0x1f ) { priv->ap = 0; WLAN_LOG_INFO4( "ident: sta f/w: id=0x%02x %d.%d.%d\n", priv->ident_sta_fw.id, priv->ident_sta_fw.major, priv->ident_sta_fw.minor, priv->ident_sta_fw.variant); } else { priv->ap = 1; WLAN_LOG_INFO4( "ident: ap f/w: id=0x%02x %d.%d.%d\n", priv->ident_sta_fw.id, priv->ident_sta_fw.major, priv->ident_sta_fw.minor, priv->ident_sta_fw.variant); } /* Compatibility range, Modem supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_MFISUPRANGE, &priv->cap_sup_mfi, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve MFISUPRANGE\n"); goto failed; } /* get all the Compatibility range, modem interface supplier fields in byte order */ priv->cap_sup_mfi.role = hfa384x2host_16(priv->cap_sup_mfi.role); priv->cap_sup_mfi.id = hfa384x2host_16(priv->cap_sup_mfi.id); priv->cap_sup_mfi.variant = hfa384x2host_16(priv->cap_sup_mfi.variant); priv->cap_sup_mfi.bottom = hfa384x2host_16(priv->cap_sup_mfi.bottom); priv->cap_sup_mfi.top = hfa384x2host_16(priv->cap_sup_mfi.top); WLAN_LOG_INFO5( "MFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_sup_mfi.role, priv->cap_sup_mfi.id, priv->cap_sup_mfi.variant, priv->cap_sup_mfi.bottom, priv->cap_sup_mfi.top); /* Compatibility range, Controller supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CFISUPRANGE, &priv->cap_sup_cfi, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve CFISUPRANGE\n"); goto failed; } /* get all the Compatibility range, controller interface supplier fields in byte order */ priv->cap_sup_cfi.role = hfa384x2host_16(priv->cap_sup_cfi.role); priv->cap_sup_cfi.id = hfa384x2host_16(priv->cap_sup_cfi.id); priv->cap_sup_cfi.variant = hfa384x2host_16(priv->cap_sup_cfi.variant); priv->cap_sup_cfi.bottom = hfa384x2host_16(priv->cap_sup_cfi.bottom); priv->cap_sup_cfi.top = hfa384x2host_16(priv->cap_sup_cfi.top); WLAN_LOG_INFO5( "CFI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_sup_cfi.role, priv->cap_sup_cfi.id, priv->cap_sup_cfi.variant, priv->cap_sup_cfi.bottom, priv->cap_sup_cfi.top); /* Compatibility range, Primary f/w supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRISUPRANGE, &priv->cap_sup_pri, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve PRISUPRANGE\n"); goto failed; } /* get all the Compatibility range, primary firmware supplier fields in byte order */ priv->cap_sup_pri.role = hfa384x2host_16(priv->cap_sup_pri.role); priv->cap_sup_pri.id = hfa384x2host_16(priv->cap_sup_pri.id); priv->cap_sup_pri.variant = hfa384x2host_16(priv->cap_sup_pri.variant); priv->cap_sup_pri.bottom = hfa384x2host_16(priv->cap_sup_pri.bottom); priv->cap_sup_pri.top = hfa384x2host_16(priv->cap_sup_pri.top); WLAN_LOG_INFO5( "PRI:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_sup_pri.role, priv->cap_sup_pri.id, priv->cap_sup_pri.variant, priv->cap_sup_pri.bottom, priv->cap_sup_pri.top); /* Compatibility range, Station f/w supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STASUPRANGE, &priv->cap_sup_sta, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve STASUPRANGE\n"); goto failed; } /* get all the Compatibility range, station firmware supplier fields in byte order */ priv->cap_sup_sta.role = hfa384x2host_16(priv->cap_sup_sta.role); priv->cap_sup_sta.id = hfa384x2host_16(priv->cap_sup_sta.id); priv->cap_sup_sta.variant = hfa384x2host_16(priv->cap_sup_sta.variant); priv->cap_sup_sta.bottom = hfa384x2host_16(priv->cap_sup_sta.bottom); priv->cap_sup_sta.top = hfa384x2host_16(priv->cap_sup_sta.top); if ( priv->cap_sup_sta.id == 0x04 ) { WLAN_LOG_INFO5( "STA:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_sup_sta.role, priv->cap_sup_sta.id, priv->cap_sup_sta.variant, priv->cap_sup_sta.bottom, priv->cap_sup_sta.top); } else { WLAN_LOG_INFO5( "AP:SUP:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_sup_sta.role, priv->cap_sup_sta.id, priv->cap_sup_sta.variant, priv->cap_sup_sta.bottom, priv->cap_sup_sta.top); } /* Compatibility range, primary f/w actor, CFI supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_PRI_CFIACTRANGES, &priv->cap_act_pri_cfi, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve PRI_CFIACTRANGES\n"); goto failed; } /* get all the Compatibility range, primary f/w actor, CFI supplier fields in byte order */ priv->cap_act_pri_cfi.role = hfa384x2host_16(priv->cap_act_pri_cfi.role); priv->cap_act_pri_cfi.id = hfa384x2host_16(priv->cap_act_pri_cfi.id); priv->cap_act_pri_cfi.variant = hfa384x2host_16(priv->cap_act_pri_cfi.variant); priv->cap_act_pri_cfi.bottom = hfa384x2host_16(priv->cap_act_pri_cfi.bottom); priv->cap_act_pri_cfi.top = hfa384x2host_16(priv->cap_act_pri_cfi.top); WLAN_LOG_INFO5( "PRI-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_act_pri_cfi.role, priv->cap_act_pri_cfi.id, priv->cap_act_pri_cfi.variant, priv->cap_act_pri_cfi.bottom, priv->cap_act_pri_cfi.top); /* Compatibility range, sta f/w actor, CFI supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_CFIACTRANGES, &priv->cap_act_sta_cfi, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve STA_CFIACTRANGES\n"); goto failed; } /* get all the Compatibility range, station f/w actor, CFI supplier fields in byte order */ priv->cap_act_sta_cfi.role = hfa384x2host_16(priv->cap_act_sta_cfi.role); priv->cap_act_sta_cfi.id = hfa384x2host_16(priv->cap_act_sta_cfi.id); priv->cap_act_sta_cfi.variant = hfa384x2host_16(priv->cap_act_sta_cfi.variant); priv->cap_act_sta_cfi.bottom = hfa384x2host_16(priv->cap_act_sta_cfi.bottom); priv->cap_act_sta_cfi.top = hfa384x2host_16(priv->cap_act_sta_cfi.top); WLAN_LOG_INFO5( "STA-CFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_act_sta_cfi.role, priv->cap_act_sta_cfi.id, priv->cap_act_sta_cfi.variant, priv->cap_act_sta_cfi.bottom, priv->cap_act_sta_cfi.top); /* Compatibility range, sta f/w actor, MFI supplier */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_STA_MFIACTRANGES, &priv->cap_act_sta_mfi, sizeof(hfa384x_caplevel_t)); if ( result ) { WLAN_LOG_ERROR0("Failed to retrieve STA_MFIACTRANGES\n"); goto failed; } /* get all the Compatibility range, station f/w actor, MFI supplier fields in byte order */ priv->cap_act_sta_mfi.role = hfa384x2host_16(priv->cap_act_sta_mfi.role); priv->cap_act_sta_mfi.id = hfa384x2host_16(priv->cap_act_sta_mfi.id); priv->cap_act_sta_mfi.variant = hfa384x2host_16(priv->cap_act_sta_mfi.variant); priv->cap_act_sta_mfi.bottom = hfa384x2host_16(priv->cap_act_sta_mfi.bottom); priv->cap_act_sta_mfi.top = hfa384x2host_16(priv->cap_act_sta_mfi.top); WLAN_LOG_INFO5( "STA-MFI:ACT:role=0x%02x:id=0x%02x:var=0x%02x:b/t=%d/%d\n", priv->cap_act_sta_mfi.role, priv->cap_act_sta_mfi.id, priv->cap_act_sta_mfi.variant, priv->cap_act_sta_mfi.bottom, priv->cap_act_sta_mfi.top); /* Serial Number */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_NICSERIALNUMBER, snum, HFA384x_RID_NICSERIALNUMBER_LEN); if ( !result ) { wlan_mkprintstr(snum, HFA384x_RID_NICSERIALNUMBER_LEN, pstr, sizeof(pstr)); WLAN_LOG_INFO1("Prism2 card SN: %s\n", pstr); } else { WLAN_LOG_ERROR0("Failed to retrieve Prism2 Card SN\n"); goto failed; } /* Collect the MAC address */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CNFOWNMACADDR, wlandev->netdev->dev_addr, WLAN_ADDR_LEN); if ( result != 0 ) { WLAN_LOG_ERROR0("Failed to retrieve mac address\n"); goto failed; } /* Retrieve the maximum frame size */ hfa384x_drvr_getconfig16(hw, HFA384x_RID_CNFMAXDATALEN, ®); WLAN_LOG_DEBUG1(1,"F/W default max frame data size=%d\n", reg); reg=WLAN_DATA_MAXLEN; hfa384x_drvr_setconfig16(hw, HFA384x_RID_CNFMAXDATALEN, ®); hfa384x_drvr_getconfig16(hw, HFA384x_RID_CNFMAXDATALEN, ®); WLAN_LOG_DEBUG1(1,"F/W max frame data size after set=%d\n", reg); /* TODO: Set any internally managed config items */ /* Set swsupport regs to magic # for card presence detection */ hfa384x_setreg(hw, PRISM2STA_MAGIC, HFA384x_SWSUPPORT0); goto done; failed: WLAN_LOG_ERROR1("Failed, result=%d\n", result); done: DBFEXIT; return result; } /*---------------------------------------------------------------- * prism2sta_inf_handover * * Handles the receipt of a Handover info frame. Should only be present * in APs only. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_handover(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { DBFENTER; WLAN_LOG_DEBUG0(2,"received infoframe:HANDOVER (unhandled)\n"); DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_tallies * * Handles the receipt of a CommTallies info frame. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_tallies(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; UINT16 *src16; UINT32 *dst; UINT32 *src32; int i; int cnt; DBFENTER; /* ** Determine if these are 16-bit or 32-bit tallies, based on the ** record length of the info record. */ cnt = sizeof(hfa384x_CommTallies32_t) / sizeof(UINT32); if (inf->framelen > 22) { dst = (UINT32 *) &priv->tallies; src32 = (UINT32 *) &inf->info.commtallies32; for (i = 0; i < cnt; i++, dst++, src32++) *dst += hfa384x2host_32(*src32); } else { dst = (UINT32 *) &priv->tallies; src16 = (UINT16 *) &inf->info.commtallies16; for (i = 0; i < cnt; i++, dst++, src16++) *dst += hfa384x2host_16(*src16); } DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_scanresults * * Handles the receipt of a Scan Results info frame. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_scanresults(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; int nbss; hfa384x_ScanResult_t *sr = &(inf->info.scanresult); int i; hfa384x_JoinRequest_data_t joinreq; int result; DBFENTER; /* Get the number of results, first in bytes, then in results */ nbss = (inf->framelen * sizeof(UINT16)) - sizeof(inf->infotype) - sizeof(inf->info.scanresult.scanreason); nbss /= sizeof(hfa384x_ScanResultSub_t); /* Print em */ WLAN_LOG_DEBUG2(1,"rx scanresults, reason=%d, nbss=%d:\n", inf->info.scanresult.scanreason, nbss); for ( i = 0; i < nbss; i++) { WLAN_LOG_DEBUG4(1, "chid=%d anl=%d sl=%d bcnint=%d\n", sr->result[i].chid, sr->result[i].anl, sr->result[i].sl, sr->result[i].bcnint); WLAN_LOG_DEBUG2(1, " capinfo=0x%04x proberesp_rate=%d\n", sr->result[i].capinfo, sr->result[i].proberesp_rate); } /* issue a join request */ joinreq.channel = sr->result[0].chid; memcpy( joinreq.bssid, sr->result[0].bssid, WLAN_BSSID_LEN); result = hfa384x_drvr_setconfig( hw, HFA384x_RID_JOINREQUEST, &joinreq, HFA384x_RID_JOINREQUEST_LEN); if (result) { WLAN_LOG_ERROR1("setconfig(joinreq) failed, result=%d\n", result); } DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_chinforesults * * Handles the receipt of a Channel Info Results info frame. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_chinforesults(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; unsigned int i, n; DBFENTER; priv->channel_info.results.scanchannels = hfa384x2host_16(inf->info.chinforesult.scanchannels); // memcpy(&inf->info.chinforesult, &priv->channel_info.results, sizeof(hfa384x_ChInfoResult_t)); for (i=0, n=0; ichannel_info.results.scanchannels & (1<info.chinforesult.result[n].chid)-1; hfa384x_ChInfoResultSub_t *chinforesult=&priv->channel_info.results.result[channel]; chinforesult->chid = channel; chinforesult->anl = hfa384x2host_16(inf->info.chinforesult.result[n].anl); chinforesult->pnl = hfa384x2host_16(inf->info.chinforesult.result[n].pnl); chinforesult->active = hfa384x2host_16(inf->info.chinforesult.result[n].active); WLAN_LOG_DEBUG5(2, "chinfo: channel %d, %s level (avg/peak)=%d/%d dB, pcf %d\n", channel+1, chinforesult->active & HFA384x_CHINFORESULT_BSSACTIVE ? "signal" : "noise", chinforesult->anl, chinforesult->pnl, chinforesult->active & HFA384x_CHINFORESULT_PCFACTIVE ? 1 : 0 ); n++; } } atomic_set(&priv->channel_info.done, 2); priv->channel_info.count = n; DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_linkstatus * * Handles the receipt of a Link Status info frame. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_linkstatus(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 portstatus; int result; DBFENTER; /* Convert */ inf->info.linkstatus.linkstatus = hfa384x2host_16(inf->info.linkstatus.linkstatus); /* Handle */ switch (inf->info.linkstatus.linkstatus) { case HFA384x_LINK_NOTCONNECTED: /* I'm currently assuming that this is the initial link * state. It should only be possible immediately * following an Enable command. * Response: * Block Transmits, Ignore receives of data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=NOTCONNECTED (unhandled)\n"); break; case HFA384x_LINK_CONNECTED: /* This one indicates a successful scan/join/auth/assoc. * When we have the full MLME complement, this event will * signify successful completion of both mlme_authenticate * and mlme_associate. State management will get a little * ugly here. * Response: * Indicate authentication and/or association * Enable Transmits, Receives and pass up data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=CONNECTED\n"); /* Collect the BSSID, and set state to allow tx */ result = hfa384x_drvr_getconfig(hw, HFA384x_RID_CURRENTBSSID, wlandev->bssid, WLAN_BSSID_LEN); if ( result ) { WLAN_LOG_DEBUG2(1, "getconfig(0x%02x) failed, result = %d\n", HFA384x_RID_CURRENTBSSID, result); goto failed; } /* Collect the port status */ result = hfa384x_drvr_getconfig16(hw, HFA384x_RID_PORTSTATUS, &portstatus); if ( result ) { WLAN_LOG_DEBUG2(1, "getconfig(0x%02x) failed, result = %d\n", HFA384x_RID_PORTSTATUS, result); goto failed; } portstatus = hfa384x2host_16(portstatus); wlandev->macmode = portstatus == HFA384x_PSTATUS_CONN_IBSS ? WLAN_MACMODE_IBSS_STA : WLAN_MACMODE_ESS_STA; break; case HFA384x_LINK_DISCONNECTED: /* This one indicates that our association is gone. We've * lost connection with the AP and/or been disassociated. * This indicates that the MAC has completely cleared it's * associated state. We * should send a deauth indication * (implying disassoc) up * to the MLME. * Response: * Indicate Deauthentication * Block Transmits, Ignore receives of data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=DISCONNECTED (unhandled)\n"); break; case HFA384x_LINK_AP_CHANGE: /* This one indicates that the MAC has decided to and * successfully completed a change to another AP. We * should probably implement a reassociation indication * in response to this one. I'm thinking that the the * p80211 layer needs to be notified in case of * buffering/queueing issues. User mode also needs to be * notified so that any BSS dependent elements can be * updated. * associated state. We * should send a deauth indication * (implying disassoc) up * to the MLME. * Response: * Indicate Reassociation * Enable Transmits, Receives and pass up data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=AP_CHANGE (unhandled)\n"); break; case HFA384x_LINK_AP_OUTOFRANGE: /* This one indicates that the MAC has decided that the * AP is out of range, but hasn't found a better candidate * so the MAC maintains its "associated" state in case * we get back in range. We should block transmits and * receives in this state. Do we need an indication here? * Probably not since a polling user-mode element would * get this status from from p2PortStatus(FD40). What about * p80211? * Response: * Block Transmits, Ignore receives of data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=AP_OUTOFRANGE (unhandled)\n"); break; case HFA384x_LINK_AP_INRANGE: /* This one indicates that the MAC has decided that the * AP is back in range. We continue working with our * existing association. * Response: * Enable Transmits, Receives and pass up data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=AP_INRANGE (unhandled)\n"); break; case HFA384x_LINK_ASSOCFAIL: /* This one is actually a peer to CONNECTED. We've * requested a join for a given SSID and optionally BSSID. * We can use this one to indicate authentication and * association failures. The trick is going to be * 1) identifying the failure, and 2) state management. * Response: * Disable Transmits, Ignore receives of data frames */ WLAN_LOG_DEBUG0(1,"linkstatus=ASSOCFAIL (unhandled)\n"); break; default: /* This is bad, IO port problems? */ WLAN_LOG_WARNING1( "unknown linkstatus=0x%02x\n", inf->info.linkstatus.linkstatus); break; } failed: DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_assocstatus * * Handles the receipt of an Association Status info frame. Should * be present in APs only. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_assocstatus(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_AssocStatus_t rec; char macbuf[WLAN_ADDR_LEN*3]; char macbuf2[WLAN_ADDR_LEN*3]; int i; DBFENTER; memcpy(&rec, &inf->info.assocstatus, sizeof(rec)); rec.assocstatus = hfa384x2host_16(rec.assocstatus); rec.reason = hfa384x2host_16(rec.reason); if (priv->log) { p802addr_to_str(macbuf, rec.sta_addr); switch (rec.assocstatus) { case HFA384x_ASSOCSTATUS_STAASSOC: printk(KERN_INFO "wlan-ap: %s Associated\n", macbuf); break; case HFA384x_ASSOCSTATUS_REASSOC: p802addr_to_str(macbuf2, rec.old_ap_addr); printk(KERN_INFO "wlan-ap: %s Reassociated from %s\n", macbuf, macbuf2); break; case HFA384x_ASSOCSTATUS_DISASSOC: printk(KERN_INFO "wlan-ap: %s Disssociated. Reason = %d\n", macbuf, rec.reason); break; case HFA384x_ASSOCSTATUS_ASSOCFAIL: printk(KERN_INFO "wlan-ap: %s Association failed. Reason = %d\n", macbuf, rec.reason); break; case HFA384x_ASSOCSTATUS_AUTHFAIL: printk(KERN_INFO "wlan-ap: %s Authentication failed. Reason = %d\n", macbuf, rec.reason); break; default: printk(KERN_INFO "wlan-ap: %s Unknown failure. Reason = %d\n", macbuf, rec.reason); break; } } /* ** Find the address in the list of authenticated stations. If it wasn't ** found, then this address has not been previously authenticated and ** something weird has happened if this is anything other than an ** "authentication failed" message. If the address was found, then ** set the "associated" flag for that station, based on whether the ** station is associating or losing its association. Something weird ** has also happened if we find the address in the list of authenticated ** stations but we are getting an "authentication failed" message. */ for (i = 0; i < priv->authlist.cnt; i++) if (memcmp(rec.sta_addr, priv->authlist.addr[i], WLAN_ADDR_LEN) == 0) break; if (i >= priv->authlist.cnt) { if (rec.assocstatus != HFA384x_ASSOCSTATUS_AUTHFAIL) WLAN_LOG_WARNING0("assocstatus info frame received for non-authenticated station.\n"); } else { priv->authlist.assoc[i] = (rec.assocstatus == HFA384x_ASSOCSTATUS_STAASSOC || rec.assocstatus == HFA384x_ASSOCSTATUS_REASSOC); if (rec.assocstatus == HFA384x_ASSOCSTATUS_AUTHFAIL) WLAN_LOG_WARNING0("authfail assocstatus info frame received for authenticated station.\n"); } DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_authreq * * Handles the receipt of an Authentication Request info frame. Should * be present in APs only. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt * * TODO: Make sure that the correct status values are returned. Is * "unspecified failure" correct? * ----------------------------------------------------------------*/ void prism2sta_inf_authreq( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; hfa384x_authenticateStation_data_t rec; int i, added, result, cnt; UINT8 *addr; char macbuf[WLAN_ADDR_LEN*3]; DBFENTER; /* ** Build the AuthenticateStation record. Initialize it for denying ** authentication. */ memcpy(rec.address, inf->info.authreq.sta_addr, WLAN_ADDR_LEN); rec.status = P80211ENUM_status_unspec_failure; /* ** Authenticate based on the access mode. */ switch (priv->accessmode) { case WLAN_ACCESS_NONE: /* ** Deny all new authentications. However, if a station ** is ALREADY authenticated, then accept it. */ for (i = 0; i < priv->authlist.cnt; i++) if (memcmp(rec.address, priv->authlist.addr[i], WLAN_ADDR_LEN) == 0) { rec.status = P80211ENUM_status_successful; break; } break; case WLAN_ACCESS_ALL: /* ** Allow all authentications. */ rec.status = P80211ENUM_status_successful; break; case WLAN_ACCESS_ALLOW: /* ** Only allow the authentication if the MAC address ** is in the list of allowed addresses. ** ** Since this is the interrupt handler, we may be here ** while the access list is in the middle of being ** updated. Choose the list which is currently okay. ** See "prism2mib_priv_accessallow()" for details. */ if (priv->allow.modify == 0) { cnt = priv->allow.cnt; addr = priv->allow.addr[0]; } else { cnt = priv->allow.cnt1; addr = priv->allow.addr1[0]; } for (i = 0; i < cnt; i++, addr += WLAN_ADDR_LEN) if (memcmp(rec.address, addr, WLAN_ADDR_LEN) == 0) { rec.status = P80211ENUM_status_successful; break; } break; case WLAN_ACCESS_DENY: /* ** Allow the authentication UNLESS the MAC address is ** in the list of denied addresses. ** ** Since this is the interrupt handler, we may be here ** while the access list is in the middle of being ** updated. Choose the list which is currently okay. ** See "prism2mib_priv_accessdeny()" for details. */ if (priv->deny.modify == 0) { cnt = priv->deny.cnt; addr = priv->deny.addr[0]; } else { cnt = priv->deny.cnt1; addr = priv->deny.addr1[0]; } rec.status = P80211ENUM_status_successful; for (i = 0; i < cnt; i++, addr += WLAN_ADDR_LEN) if (memcmp(rec.address, addr, WLAN_ADDR_LEN) == 0) { rec.status = P80211ENUM_status_unspec_failure; break; } break; } /* ** If the authentication is okay, then add the MAC address to the list ** of authenticated stations. Don't add the address if it is already in ** the list. (802.11b does not seem to disallow a station from issuing ** an authentication request when the station is already authenticated. ** Does this sort of thing ever happen? We might as well do the check ** just in case.) */ added = 0; if (rec.status == P80211ENUM_status_successful) { for (i = 0; i < priv->authlist.cnt; i++) if (memcmp(rec.address, priv->authlist.addr[i], WLAN_ADDR_LEN) == 0) break; if (i >= priv->authlist.cnt) { if (priv->authlist.cnt >= WLAN_AUTH_MAX) { rec.status = P80211ENUM_status_ap_full; } else { memcpy(priv->authlist.addr[priv->authlist.cnt], rec.address, WLAN_ADDR_LEN); priv->authlist.cnt++; added = 1; } } } if (priv->log) { p802addr_to_str(macbuf, rec.address); printk(KERN_INFO "wlan-ap: %s %s\n", macbuf, (rec.status == P80211ENUM_status_successful) ? "Authenticated" : (rec.status == P80211ENUM_status_ap_full) ? "Authentication denied (AP full)" : "Authentication denied (Access List)"); } /* ** Send back the results of the authentication. If this doesn't work, ** then make sure to remove the address from the authenticated list if ** it was added. */ rec.status = host2hfa384x_16(rec.status); result = hfa384x_drvr_setconfig(hw, HFA384x_RID_AUTHENTICATESTA, &rec, sizeof(rec)); if (result) { if (added) priv->authlist.cnt--; WLAN_LOG_ERROR1("setconfig(authenticatestation) failed, result=%d\n", result); } DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_inf_psusercnt * * Handles the receipt of a PowerSaveUserCount info frame. Should * be present in APs only. * * Arguments: * wlandev wlan device structure * inf ptr to info frame (contents in hfa384x order) * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_inf_psusercnt( wlandevice_t *wlandev, hfa384x_InfFrame_t *inf) { prism2sta_priv_t *priv = wlandev->priv; DBFENTER; priv->psusercount = hfa384x2host_16(inf->info.psusercnt.usercnt); DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_interrupt * * Driver interrupt handler. * * Arguments: * irq irq number * dev_id pointer to the device * regs registers * * Returns: * nothing * * Side effects: * May result in a frame being passed up the stack or an info * frame being handled. * * Call context: * Ummm, could it be interrupt? ----------------------------------------------------------------*/ void prism2sta_interrupt(int irq, void *dev_id, struct pt_regs *regs) { int reg; wlandevice_t *wlandev = (wlandevice_t*)dev_id; prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; int ev_read = 0; DBFENTER; /* Check swsupport reg magic # for card presence */ reg = hfa384x_getreg(hw, HFA384x_SWSUPPORT0); if ( reg != PRISM2STA_MAGIC) { WLAN_LOG_DEBUG1(2, "irq=%d, no magic. Card removed?.\n", irq); return; } /* Set the BAP context */ hw->bap = HFA384x_BAP_INT; /* read the EvStat register for interrupt enabled events */ reg = hfa384x_getreg(hw, HFA384x_EVSTAT); ev_read++; do { /* Handle the events */ if ( HFA384x_EVSTAT_ISINFDROP(reg) ){ prism2sta_int_infdrop(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_INFDROP_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISINFO(reg) ){ prism2sta_int_info(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_INFO_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISTXEXC(reg) ){ prism2sta_int_txexc(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_TXEXC_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISTX(reg) ){ prism2sta_int_tx(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_TX_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISRX(reg) ){ prism2sta_int_rx(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_RX_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISALLOC(reg) ){ prism2sta_int_alloc(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_ALLOC_SET(1), HFA384x_EVACK); } if ( HFA384x_EVSTAT_ISDTIM(reg) ){ prism2sta_int_dtim(wlandev); hfa384x_setreg(hw, HFA384x_EVACK_DTIM_SET(1), HFA384x_EVACK); } /* allow the evstat to be updated after the evack */ udelay(20); /* Check swsupport reg magic # for card presence */ reg = hfa384x_getreg(hw, HFA384x_SWSUPPORT0); if ( reg != PRISM2STA_MAGIC) { WLAN_LOG_DEBUG1(2, "irq=%d, no magic. Card removed?.\n", irq); return; } /* read the EvStat register for interrupt enabled events */ reg = hfa384x_getreg(hw, HFA384x_EVSTAT); ev_read++; } while (( HFA384x_EVSTAT_ISINFDROP(reg) || HFA384x_EVSTAT_ISINFO(reg) || HFA384x_EVSTAT_ISTXEXC(reg) || HFA384x_EVSTAT_ISTX(reg) || HFA384x_EVSTAT_ISRX(reg) || HFA384x_EVSTAT_ISALLOC(reg) || HFA384x_EVSTAT_ISDTIM(reg)) && ev_read < prism2_irq_evread_max); /* Clear the BAP context */ hw->bap = HFA384x_BAP_PROC; DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_dtim * * Handles the DTIM early warning event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_dtim(wlandevice_t *wlandev) { #if 0 prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; #endif DBFENTER; WLAN_LOG_DEBUG0(3, "DTIM event, currently unhandled.\n"); DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_infdrop * * Handles the InfDrop event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_infdrop(wlandevice_t *wlandev) { #if 0 prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; #endif DBFENTER; WLAN_LOG_DEBUG0(3, "Info frame dropped due to card mem low.\n"); DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_info * * Handles the Info event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_info(wlandevice_t *wlandev) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 reg; hfa384x_InfFrame_t inf; int result; DBFENTER; /* Retrieve the FID */ reg = hfa384x_getreg(hw, HFA384x_INFOFID); /* Retrieve the length */ result = hfa384x_copy_from_bap( hw, hw->bap, reg, 0, &inf.framelen, sizeof(UINT16)); if ( result ) { WLAN_LOG_DEBUG3(1, "copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", reg, sizeof(inf), result); goto failed; } inf.framelen = hfa384x2host_16(inf.framelen); /* Retrieve the rest */ result = hfa384x_copy_from_bap( hw, hw->bap, reg, sizeof(UINT16), &(inf.infotype), inf.framelen * sizeof(UINT16)); if ( result ) { WLAN_LOG_DEBUG3(1, "copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", reg, sizeof(inf), result); goto failed; } inf.infotype = hfa384x2host_16(inf.infotype); /* Dispatch */ switch ( inf.infotype ) { case HFA384x_IT_HANDOVERADDR: prism2sta_inf_handover(wlandev, &inf); break; case HFA384x_IT_COMMTALLIES: prism2sta_inf_tallies(wlandev, &inf); break; case HFA384x_IT_SCANRESULTS: prism2sta_inf_scanresults(wlandev, &inf); break; case HFA384x_IT_CHINFORESULTS: prism2sta_inf_chinforesults(wlandev, &inf); break; case HFA384x_IT_LINKSTATUS: prism2sta_inf_linkstatus(wlandev, &inf); break; case HFA384x_IT_ASSOCSTATUS: prism2sta_inf_assocstatus(wlandev, &inf); break; case HFA384x_IT_AUTHREQ: prism2sta_inf_authreq(wlandev, &inf); break; case HFA384x_IT_PSUSERCNT: prism2sta_inf_psusercnt(wlandev, &inf); break; default: WLAN_LOG_WARNING1( "Unknown info type=0x%02x\n", inf.infotype); break; } failed: DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_txexc * * Handles the TxExc event. A Transmit Exception event indicates * that the MAC's TX process was unsuccessful - so the packet did * not get transmitted. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_txexc(wlandevice_t *wlandev) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 status; UINT16 fid; int result = 0; DBFENTER; /* Collect the status and display */ fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID); result = hfa384x_copy_from_bap(hw, hw->bap, fid, 0, &status, sizeof(status)); if ( result ) { WLAN_LOG_DEBUG3(1, "copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", fid, sizeof(status), result); goto failed; } status = hfa384x2host_16(status); WLAN_LOG_DEBUG1(3, "TxExc status=0x%x.\n", status); failed: DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_tx * * Handles the Tx event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_tx(wlandevice_t *wlandev) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 fid; UINT16 status; int result = 0; DBFENTER; fid = hfa384x_getreg(hw, HFA384x_TXCOMPLFID); result =hfa384x_copy_from_bap(hw, hw->bap, fid, 0, &status, sizeof(status)); if ( result ) { WLAN_LOG_DEBUG3(1, "copy_from_bap(0x%04x, 0, %d) failed, result=0x%x\n", fid, sizeof(status), result); goto failed; } status = hfa384x2host_16(status); WLAN_LOG_DEBUG1(4, "Tx Complete, status=0x%04x\n", status); /* update linux network stats */ wlandev->linux_stats.tx_packets++; failed: DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_rx * * Handles the Rx event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_rx(wlandevice_t *wlandev) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 rxfid; hfa384x_rx_frame_t rxdesc; wlan_pb_t *pb= NULL; int result; DBFENTER; /* Get the FID */ rxfid = hfa384x_getreg(hw, HFA384x_RXFID); /* Get the descriptor (including headers) */ result = hfa384x_copy_from_bap(hw, hw->bap, rxfid, 0, &rxdesc, sizeof(rxdesc)); if ( result ) { WLAN_LOG_DEBUG4(1, "copy_from_bap(0x%04x, %d, %d) failed, result=0x%x\n", rxfid, 0, sizeof(rxdesc), result); goto failed; } /* Byte order convert once up front. */ rxdesc.status = hfa384x2host_16(rxdesc.status); rxdesc.time = hfa384x2host_32(rxdesc.time); rxdesc.data_len = hfa384x2host_16(rxdesc.data_len); #if 0 printk(KERN_DEBUG"rxf(%d): ",rxlen); for (i=0; ip80211frmlen; i++) { printk("%x ",pb->p80211buf[i]); } printk("\n"); #endif /* Now handle frame based on port# */ switch( HFA384x_RXSTATUS_MACPORT_GET(rxdesc.status) ) { case 0: /* Allocate the buffer, note CRC (aka FCS). pballoc */ /* assumes there needs to be space for one */ pb = p80211pb_alloc_p80211(NULL, rxdesc.data_len + WLAN_HDR_A3_LEN + WLAN_CRC_LEN); if ( pb == NULL ) { WLAN_LOG_DEBUG0(1, "pballoc failed.\n"); goto failed; } if ( pb->p80211hostbuf == NULL ) { WLAN_LOG_DEBUG0(1, "pballoc failed to get hostbuf.\n"); goto failed; } /* Copy the 802.11 hdr to the buffer */ result = hfa384x_copy_from_bap(hw, hw->bap, rxfid, HFA384x_RX_80211HDR_OFF, pb->p80211_hdr, WLAN_HDR_A3_LEN); if ( result ) { WLAN_LOG_DEBUG4(1, "copy_from_bap(0x%04x, %d, %d) failed, result=0x%x\n", rxfid, HFA384x_RX_80211HDR_OFF, WLAN_HDR_A3_LEN, result); goto failed; } if (prism2sta_int_rx_typedrop(wlandev, ieee2host16(pb->p80211_hdr->a3.fc))) { WLAN_LOG_WARNING0("Unhandled frame type, dropped.\n"); goto failed; } /* If exclude and we receive an unencrypted, drop it */ if ( priv->exclude_unencrypt && !WLAN_GET_FC_ISWEP(ieee2host16(pb->p80211_hdr->a3.fc))) { goto failed; } /* Copy the payload data to the buffer */ if ( rxdesc.data_len > 0 ) { result = hfa384x_copy_from_bap(hw, hw->bap, rxfid, HFA384x_RX_DATA_OFF, pb->p80211_payload, rxdesc.data_len); if ( result ) { WLAN_LOG_DEBUG4(1, "copy_from_bap(0x%04x, %d, %d) failed, result=0x%x\n", rxfid, HFA384x_RX_DATA_OFF, rxdesc.data_len, result); goto failed; } } /* Set the length */ pb->p80211frmlen = WLAN_HDR_A3_LEN + rxdesc.data_len + WLAN_CRC_LEN; /* Call p80211netdev_rx - it will NOT free pb */ p80211netdev_rx(wlandev, pb); break; case 7: if ( ! HFA384x_RXSTATUS_ISFCSERR(rxdesc.status) ) { /* Copy to wlansnif skb */ prism2sta_int_rxmonitor( wlandev, rxfid, &rxdesc); } else { WLAN_LOG_DEBUG0(3,"Received monitor frame: FCSerr set\n" ); } break; default: WLAN_LOG_WARNING1("Received frame on unsupported port=%d\n", HFA384x_RXSTATUS_MACPORT_GET(rxdesc.status) ); goto done; break; } failed: p80211pb_free(pb); done: DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_rxmonitor * * Helper function for int_rx. Handles monitor frames. * Note that this function allocates space for the FCS and sets it * to 0xffffffff. The hfa384x doesn't give us the FCS value but the * higher layers expect it. 0xffffffff is used as a flag to indicate * the FCS is bogus. * * Arguments: * wlandev wlan device structure * rxfid received FID * rxdesc rx descriptor read from card in int_rx * * Returns: * nothing * * Side effects: * Allocates an skb and passes it up via p80211ind_sniff() * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_rxmonitor( wlandevice_t *wlandev, UINT16 rxfid, hfa384x_rx_frame_t *rxdesc) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT hdrlen = 0; UINT datalen = 0; UINT skblen = 0; p80211msg_lnxind_wlansniffrm_t *msg; UINT8 *datap; UINT16 fc; struct sk_buff *skb; DBFENTER; /* Don't forget the status, time, and data_len fields are in host order */ /* Figure out how big the frame is */ fc = ieee2host16(rxdesc->frame_control); switch ( WLAN_GET_FC_FTYPE(fc) ) { case WLAN_FTYPE_DATA: if ( WLAN_GET_FC_TODS(fc) && WLAN_GET_FC_FROMDS(fc) ) { hdrlen = WLAN_HDR_A4_LEN; } else { hdrlen = WLAN_HDR_A3_LEN; } datalen = rxdesc->data_len; break; case WLAN_FTYPE_MGMT: hdrlen = WLAN_HDR_A3_LEN; datalen = rxdesc->data_len; break; case WLAN_FTYPE_CTL: switch ( WLAN_GET_FC_FSTYPE(fc) ) { case WLAN_FSTYPE_PSPOLL: case WLAN_FSTYPE_RTS: case WLAN_FSTYPE_CFEND: case WLAN_FSTYPE_CFENDCFACK: hdrlen = 16; break; case WLAN_FSTYPE_CTS: case WLAN_FSTYPE_ACK: hdrlen = 10; break; } datalen = 0; break; default: WLAN_LOG_DEBUG1(1, "unknown frm: fc=0x%04x\n", fc); return; } /* Allocate an ind message+framesize skb */ skblen = sizeof(p80211msg_lnxind_wlansniffrm_t) + hdrlen + datalen + WLAN_CRC_LEN; /* sanity check the length */ if ( skblen > (sizeof(p80211msg_lnxind_wlansniffrm_t) + WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + WLAN_CRC_LEN) ) { WLAN_LOG_DEBUG1(1, "overlen frm: len=%d\n", skblen - sizeof(p80211msg_lnxind_wlansniffrm_t)); } if ( (skb = alloc_skb(skblen, GFP_ATOMIC)) == NULL ) { WLAN_LOG_DEBUG1(2, "alloc_skb failed trying to allocate %d bytes\n", skblen); return; } skb_put(skb, skblen); datap = skb->data + sizeof(p80211msg_lnxind_wlansniffrm_t); msg = (p80211msg_lnxind_wlansniffrm_t*)skb->data; /* Initialize the message members */ msg->msgcode = DIDmsg_lnxind_wlansniffrm; msg->msglen = sizeof(p80211msg_lnxind_wlansniffrm_t); strcpy(msg->devname, wlandev->name); msg->hosttime.did = DIDmsg_lnxind_wlansniffrm_hosttime; msg->hosttime.status = 0; msg->hosttime.len = 4; msg->hosttime.data = jiffies; msg->mactime.did = DIDmsg_lnxind_wlansniffrm_mactime; msg->mactime.status = 0; msg->mactime.len = 4; msg->mactime.data = rxdesc->time; msg->channel.did = DIDmsg_lnxind_wlansniffrm_channel; msg->channel.status = P80211ENUM_msgitem_status_no_value; msg->channel.len = 4; msg->channel.data = 0; msg->rssi.did = DIDmsg_lnxind_wlansniffrm_rssi; msg->rssi.status = P80211ENUM_msgitem_status_no_value; msg->rssi.len = 4; msg->rssi.data = 0; msg->sq.did = DIDmsg_lnxind_wlansniffrm_sq; msg->sq.status = P80211ENUM_msgitem_status_no_value; msg->sq.len = 4; msg->sq.data = 0; msg->signal.did = DIDmsg_lnxind_wlansniffrm_signal; msg->signal.status = 0; msg->signal.len = 4; msg->signal.data = rxdesc->signal; msg->noise.did = DIDmsg_lnxind_wlansniffrm_noise; msg->noise.status = 0; msg->noise.len = 4; msg->noise.data = rxdesc->silence; msg->rate.did = DIDmsg_lnxind_wlansniffrm_rate; msg->rate.status = 0; msg->rate.len = 4; msg->rate.data = rxdesc->rate / 5; /* set to 802.11 units */ msg->istx.did = DIDmsg_lnxind_wlansniffrm_istx; msg->istx.status = 0; msg->istx.len = 4; msg->istx.data = P80211ENUM_truth_false; msg->frmlen.did = DIDmsg_lnxind_wlansniffrm_frmlen; msg->frmlen.status = 0; msg->frmlen.len = 4; msg->frmlen.data = hdrlen + datalen + WLAN_CRC_LEN; /* Copy the 802.11 header to the skb (ctl frames may be less than a full header) */ memcpy( datap, &(rxdesc->frame_control), hdrlen); /* If any, copy the data from the card to the skb */ if ( datalen > 0 ) { hfa384x_copy_from_bap(hw, hw->bap, rxfid, HFA384x_RX_DATA_OFF, datap + hdrlen, datalen); } /* Set the CRC */ memset( ((UINT8 *)(skb->data)) + skb->len - WLAN_CRC_LEN, 0xff, WLAN_CRC_LEN); /* Pass it up */ p80211ind_sniff(wlandev, skb); DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_int_rx_typedrop * * Classifies the frame, increments the appropriate counter, and * returns 0|1 indicating whether the driver should handle or * drop the frame * * Arguments: * wlandev wlan device structure * fc frame control field * * Returns: * zero if the frame should be handled by the driver, * non-zero otherwise. * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ int prism2sta_int_rx_typedrop( wlandevice_t *wlandev, UINT16 fc) { UINT16 ftype; UINT16 fstype; int drop = 0; /* Classify frame, increment counter */ ftype = WLAN_GET_FC_FTYPE(fc); fstype = WLAN_GET_FC_FSTYPE(fc); WLAN_LOG_DEBUG2(4, "rx_typedrop : ftype=%d fstype=%d.\n", ftype, fstype); switch ( ftype ) { case WLAN_FTYPE_MGMT: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd mgmt:"); wlandev->rx.mgmt++; switch( fstype ) { case WLAN_FSTYPE_ASSOCREQ: printk("assocreq"); wlandev->rx.assocreq++; break; case WLAN_FSTYPE_ASSOCRESP: printk("assocresp"); wlandev->rx.assocresp++; break; case WLAN_FSTYPE_REASSOCREQ: printk("reassocreq"); wlandev->rx.reassocreq++; break; case WLAN_FSTYPE_REASSOCRESP: printk("reassocresp"); wlandev->rx.reassocresp++; break; case WLAN_FSTYPE_PROBEREQ: printk("probereq"); wlandev->rx.probereq++; break; case WLAN_FSTYPE_PROBERESP: printk("proberesp"); wlandev->rx.proberesp++; break; case WLAN_FSTYPE_BEACON: printk("beacon"); wlandev->rx.beacon++; break; case WLAN_FSTYPE_ATIM: printk("atim"); wlandev->rx.atim++; break; case WLAN_FSTYPE_DISASSOC: printk("disassoc"); wlandev->rx.disassoc++; break; case WLAN_FSTYPE_AUTHEN: printk("authen"); wlandev->rx.authen++; break; case WLAN_FSTYPE_DEAUTHEN: printk("deauthen"); wlandev->rx.deauthen++; break; default: printk("unknown"); wlandev->rx.mgmt_unknown++; break; } printk("\n"); drop = 1; break; case WLAN_FTYPE_CTL: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd ctl:"); wlandev->rx.ctl++; switch( fstype ) { case WLAN_FSTYPE_PSPOLL: printk("pspoll"); wlandev->rx.pspoll++; break; case WLAN_FSTYPE_RTS: printk("rts"); wlandev->rx.rts++; break; case WLAN_FSTYPE_CTS: printk("cts"); wlandev->rx.cts++; break; case WLAN_FSTYPE_ACK: printk("ack"); wlandev->rx.ack++; break; case WLAN_FSTYPE_CFEND: printk("cfend"); wlandev->rx.cfend++; break; case WLAN_FSTYPE_CFENDCFACK: printk("cfendcfack"); wlandev->rx.cfendcfack++; break; default: printk("unknown"); wlandev->rx.ctl_unknown++; break; } printk("\n"); drop = 1; break; case WLAN_FTYPE_DATA: wlandev->rx.data++; switch( fstype ) { case WLAN_FSTYPE_DATAONLY: wlandev->rx.dataonly++; break; case WLAN_FSTYPE_DATA_CFACK: wlandev->rx.data_cfack++; break; case WLAN_FSTYPE_DATA_CFPOLL: wlandev->rx.data_cfpoll++; break; case WLAN_FSTYPE_DATA_CFACK_CFPOLL: wlandev->rx.data__cfack_cfpoll++; break; case WLAN_FSTYPE_NULL: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd data:null\n"); wlandev->rx.null++; break; case WLAN_FSTYPE_CFACK: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd data:cfack\n"); wlandev->rx.cfack++; break; case WLAN_FSTYPE_CFPOLL: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd data:cfpoll\n"); wlandev->rx.cfpoll++; break; case WLAN_FSTYPE_CFACK_CFPOLL: WLAN_LOG_WARNING0("prism2sta_int_rx(): rx'd data:cfack_cfpoll\n"); wlandev->rx.cfack_cfpoll++; break; default: printk("unknown"); wlandev->rx.data_unknown++; break; } break; } return drop; } /*---------------------------------------------------------------- * prism2sta_int_alloc * * Handles the Alloc event. * * Arguments: * wlandev wlan device structure * * Returns: * nothing * * Side effects: * * Call context: * interrupt ----------------------------------------------------------------*/ void prism2sta_int_alloc(wlandevice_t *wlandev) { prism2sta_priv_t *priv = wlandev->priv; hfa384x_t *hw = priv->hw; UINT16 fid; DBFENTER; /* Handle the reclaimed FID */ /* collect the FID and push it onto the stack */ fid = hfa384x_getreg(hw, HFA384x_ALLOCFID); #ifdef USE_FID_STACK if (! txfid_stack_full(priv)) { txfid_push(priv, fid); #if (LINUX_VERSION_CODE < WLAN_KVERSION(2,3,38) ) wlandev->netdev->tbusy = 0; mark_bh(NET_BH); #else netif_wake_queue(wlandev->netdev); #endif WLAN_LOG_DEBUG0(5, "fid pushed.\n"); } else { WLAN_LOG_DEBUG0(5, "fidstack full.\n."); } #else /* [BD] Just because there is an fid available doesn't mean there * is room in the queue. If there is no room in the queue, * then we would like to hold off the upper layer until there * is. The problem is this : if we get an fid and there is * not room for it on the queue, what happens if we don't use it ?? * [MSM] Since, after initialization, the only way we should be * getting int_alloc's is as a result of tx w/reclaim and the fid * used to tx came _from_ the queue, this shouldn't happen. My * feeling at this point is we should treat it as a very serious * error, maybe even a fatal. The only problem is that we * haven't a way to handle fatal errors at this point in time. */ WLAN_LOG_DEBUG1(5, "int_alloc(%#x) ", fid); if (! txfid_queue_full(priv)) { txfid_queue_add(priv, fid); #if (LINUX_VERSION_CODE < WLAN_KVERSION(2,3,38) ) wlandev->netdev->tbusy = 0; mark_bh(NET_BH); #else netif_wake_queue(wlandev->netdev); #endif WLAN_LOG_DEBUG0(5, "q_add.\n"); } else { WLAN_LOG_DEBUG0(5, "q_full.\n"); } #endif DBFEXIT; return; } #if (WLAN_HOSTIF == WLAN_PCMCIA) /*---------------------------------------------------------------- * prism2sta_attach * * Half of the attach/detach pair. Creates and registers a device * instance with Card Services. In this case, it also creates the * wlandev structure and device private structure. These are * linked to the device instance via its priv member. * * Arguments: * none * * Returns: * A valid ptr to dev_link_t on success, NULL otherwise * * Side effects: * * * Call context: * process thread (insmod/init_module/register_pccard_driver) ----------------------------------------------------------------*/ dev_link_t *prism2sta_attach(void) { client_reg_t client_reg; int ret; dev_link_t *link; wlandevice_t *wlandev; prism2sta_priv_t *priv; DBFENTER; /* Create the PC card device object. */ link = kmalloc(sizeof(struct dev_link_t), GFP_KERNEL); if ( link == NULL ) { return NULL; } memset(link, 0, sizeof(struct dev_link_t)); link->release.function = &prism2sta_release; link->release.data = (u_long)link; link->conf.IntType = INT_MEMORY_AND_IO; /* Create the network device object. */ wlandev = kmalloc(sizeof(wlandevice_t), GFP_KERNEL); if ( wlandev == NULL ) { kfree_s(link, sizeof(dev_link_t)); return NULL; } memset(wlandev, 0, sizeof(wlandevice_t)); /* Make up a device private data structure. */ wlandev->priv = kmalloc(sizeof(prism2sta_priv_t), GFP_KERNEL); if ( wlandev->priv == NULL ) { kfree_s(wlandev, sizeof(wlandevice_t)); kfree_s(link, sizeof(dev_link_t)); return NULL; } /* Initialize the device private data stucture. */ priv = (prism2sta_priv_t *) wlandev->priv; memset(priv, 0, sizeof(prism2sta_priv_t)); priv->dot11_desired_bss_type = 1; /* Make up a hw data structure. */ priv->hw = kmalloc(sizeof(hfa384x_t), GFP_KERNEL); if ( priv->hw == NULL ) { kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); kfree_s(link, sizeof(dev_link_t)); return NULL; } memset(priv->hw, 0, sizeof(hfa384x_t)); /* Set our entries in the wlandev */ wlandev->open = &prism2sta_open; wlandev->close = &prism2sta_close; wlandev->reset = &prism2sta_reset; wlandev->txframe = &prism2sta_txframe; wlandev->mlmerequest = &prism2sta_mlmerequest; /* Set up the remaining entries in the wlan common way */ wlandev->name = ((prism2sta_priv_t*)wlandev->priv)->node.dev_name; wlan_setup(wlandev); link->priv = wlandev; #if CS_RELEASE_CODE > 0x2911 link->irq.Instance = wlandev; #endif /* Link in to the list of devices managed by this driver */ link->next = dev_list; dev_list = link; /* Register with Card Services */ client_reg.dev_info = &dev_info; client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; client_reg.EventMask = CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | CS_EVENT_RESET_REQUEST | CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; client_reg.event_handler = &prism2sta_event; client_reg.Version = 0x0210; client_reg.event_callback_args.client_data = link; ret = CardServices(RegisterClient, &link->handle, &client_reg); if (ret != 0) { cs_error(link->handle, RegisterClient, ret); prism2sta_detach(link); return NULL; } return link; } /*---------------------------------------------------------------- * prism2sta_detach * * Remove one of the device instances managed by this driver. * Search the list for the given instance, * check our flags for a waiting timer'd release call * call release * Deregister the instance with Card Services * (netdevice) unregister the network device. * unlink the instance from the list * free the link, priv, and priv->priv memory * Note: the dev_list variable is a driver scoped static used to * maintain a list of device instances managed by this * driver. * * Arguments: * link ptr to the instance to detach * * Returns: * nothing * * Side effects: * the link structure is gone, the netdevice is gone * * Call context: * Might be interrupt, don't block. ----------------------------------------------------------------*/ void prism2sta_detach(dev_link_t *link) { dev_link_t **linkp; UINT32 flags; wlandevice_t *wlandev; prism2sta_priv_t *priv; DBFENTER; /* Locate device structure */ for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) { if (*linkp == link) break; } if (*linkp != NULL) { /* Get rid of any timer'd release call */ save_flags(flags); cli(); if (link->state & DEV_RELEASE_PENDING) { del_timer(&link->release); link->state &= ~DEV_RELEASE_PENDING; } restore_flags(flags); /* If link says we're still config'd, call release */ if (link->state & DEV_CONFIG) { prism2sta_release((u_long)link); if (link->state & DEV_STALE_CONFIG) { link->state |= DEV_STALE_LINK; return; } } /* Tell Card Services we're not around any more */ if (link->handle) { CardServices(DeregisterClient, link->handle); } /* Unlink device structure, free bits */ *linkp = link->next; if ( link->priv != NULL ) { wlandev = (wlandevice_t*)link->priv; if (link->dev != NULL) { unregister_wlandev(wlandev); } wlan_unsetup(wlandev); if (wlandev->priv) { priv = (prism2sta_priv_t*)wlandev->priv; if ( priv->hw ) kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); } kfree_s(link->priv, sizeof(wlandevice_t)); } kfree_s(link, sizeof(struct dev_link_t)); } DBFEXIT; return; } /*---------------------------------------------------------------- * prism2sta_config * * Half of the config/release pair. Usually called in response to * a card insertion event. At this point, we _know_ there's some * physical device present. That means we can start poking around * at the CIS and at any device specific config data we want. * * Note the gotos and the macros. I recoded this once without * them, and it got incredibly ugly. It's actually simpler with * them. * * Arguments: * link the dev_link_t structure created in attach that * represents this device instance. * * Returns: * nothing * * Side effects: * Resources (irq, io, mem) are allocated * The pcmcia dev_link->node->name is set * (For netcards) The device structure is finished and, * most importantly, registered. This means that there * is now a _named_ device that can be configured from * userland. * * Call context: * May be called from a timer. Don't block! ----------------------------------------------------------------*/ #define CS_CHECK(fn, args...) \ while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed; #if defined(WLAN_INCLUDE_DEBUG) #define CFG_CHECK(fn, args...) \ if ((last_ret=CardServices(last_fn=(fn), args))!=0) { \ WLAN_LOG_DEBUG0(1,"CFG_CHECK failed\n"); \ cs_error(link->handle, last_fn, last_ret); \ goto next_entry; \ } #else #define CFG_CHECK(fn, args...) if (CardServices(fn, args)!=0) goto next_entry; #endif void prism2sta_config(dev_link_t *link) { client_handle_t handle; wlandevice_t *wlandev; prism2sta_priv_t *priv; int last_fn; int last_ret; tuple_t tuple; cisparse_t parse; config_info_t socketconf; UINT8 buf[64]; int i; int minVcc = 0; int maxVcc = 0; cistpl_cftable_entry_t dflt = { 0 }; DBFENTER; handle = link->handle; wlandev = (wlandevice_t*)link->priv; /* Collect the config register info */ tuple.DesiredTuple = CISTPL_CONFIG; tuple.Attributes = 0; tuple.TupleData = buf; tuple.TupleDataMax = sizeof(buf); tuple.TupleOffset = 0; CS_CHECK(GetFirstTuple, handle, &tuple); CS_CHECK(GetTupleData, handle, &tuple); CS_CHECK(ParseTuple, handle, &tuple, &parse); link->conf.ConfigBase = parse.config.base; link->conf.Present = parse.config.rmask[0]; /* Configure card */ link->state |= DEV_CONFIG; /* Acquire the current socket config (need Vcc setting) */ CS_CHECK(GetConfigurationInfo, handle, &socketconf); /* Loop through the config table entries until we find one that works */ /* Assumes a complete and valid CIS */ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; CS_CHECK(GetFirstTuple, handle, &tuple); while (1) { cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); CFG_CHECK(GetTupleData, handle, &tuple); CFG_CHECK(ParseTuple, handle, &tuple, &parse); if (cfg->index == 0) goto next_entry; link->conf.ConfigIndex = cfg->index; /* Lets print out the Vcc that the controller+pcmcia-cs set * for us, cause that's what we're going to use. */ WLAN_LOG_DEBUG1(1,"Initial Vcc=%d/10v\n", socketconf.Vcc); if (prism2_ignorevcc) { link->conf.Vcc = socketconf.Vcc; goto skipvcc; } /* Use power settings for Vcc and Vpp if present */ /* Note that the CIS values need to be rescaled */ if (cfg->vcc.present & (1<vcc.param[CISTPL_POWER_VNOM]/10000; } else if (dflt.vcc.present & (1<vcc.present & (1<vcc.present & (1<vcc.param[CISTPL_POWER_VMIN]/10000; maxVcc = cfg->vcc.param[CISTPL_POWER_VMAX]/10000; } else if ((dflt.vcc.present & (1<= minVcc && socketconf.Vcc <= maxVcc) { link->conf.Vcc = socketconf.Vcc; } else { /* [MSM]: Note that I've given up trying to change * the Vcc if a change is indicated. It seems the * system&socketcontroller&card vendors can't seem * to get it right, so I'm tired of trying to hack * my way around it. pcmcia-cs does its best using * the voltage sense pins but sometimes the controller * lies. Then, even if we have a good read on the VS * pins, some system designs will silently ignore our * requests to set the voltage. Additionally, some * vendors have 3.3v indicated on their sense pins, * but 5v specified in the CIS or vice-versa. I've * had it. My only recommendation is "let the buyer * beware". Your system might supply 5v to a 3v card * (possibly causing damage) or a 3v capable system * might supply 5v to a 3v capable card (wasting * precious battery life). * My only recommendation (if you care) is to get * yourself an extender card (I don't know where, I * have only one myself) and a meter and test it for * yourself. */ goto next_entry; } skipvcc: WLAN_LOG_DEBUG1(1, "link->conf.Vcc=%d\n", link->conf.Vcc); /* Do we need to allocate an interrupt? */ /* HACK: due to a bad CIS....we ALWAYS need an interrupt */ /* if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) */ link->conf.Attributes |= CONF_ENABLE_IRQ; /* IO window settings */ 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; 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.BasePort1 = io->win[0].base; if ( link->io.BasePort1 != 0 ) { WLAN_LOG_WARNING1( "Brain damaged CIS: hard coded iobase=" "0x%x, try letting pcmcia_cs decide...\n", link->io.BasePort1 ); link->io.BasePort1 = 0; } 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_CHECK(RequestIO, link->handle, &link->io); /* If we got this far, we're cool! */ break; next_entry: if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; CS_CHECK(GetNextTuple, handle, &tuple); } /* 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) { 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 = prism2sta_interrupt; link->irq.Instance = wlandev; CS_CHECK(RequestIRQ, 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, link->handle, &link->conf); /* Fill the netdevice with this info */ wlandev->netdev->irq = link->irq.AssignedIRQ; wlandev->netdev->base_addr = link->io.BasePort1; /* 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_PENDING; /* Register the network device and get assigned a name */ if (register_wlandev(wlandev) != 0) { WLAN_LOG_NOTICE0("prism2sta_cs: register_wlandev() failed.\n"); goto failed; } priv = (prism2sta_priv_t*)wlandev->priv;/* collect the device priv ptr */ link->dev = &priv->node; /* now pcmcia knows the device name */ /* Any device custom config/query stuff should be done here */ /* For a netdevice, we should at least grab the mac address */ return; cs_failed: cs_error(link->handle, last_fn, last_ret); WLAN_LOG_ERROR0("NextTuple failure? It's probably a Vcc mismatch.\n"); failed: prism2sta_release((UINT32)link); return; } /*---------------------------------------------------------------- * prism2sta_release * * Half of the config/release pair. Usually called in response to * a card ejection event. Checks to make sure no higher layers * are still (or think they are) using the card via the link->open * field. * * NOTE: Don't forget to increment the link->open variable in the * device_open method, and decrement it in the device_close * method. * * Arguments: * arg a generic 32 bit variable...we assume it's a * ptr to a dev_link. * * Returns: * nothing * * Side effects: * All resources should be released after this function * executes and finds the device !open. * * Call context: * Possibly in a timer context. Don't do anything that'll * block. ----------------------------------------------------------------*/ void prism2sta_release(UINT32 arg) { dev_link_t *link = (dev_link_t *)arg; DBFENTER; if (link->open) { WLAN_LOG_DEBUG1(1, "prism2sta_cs: release postponed, '%s' still open\n", link->dev->dev_name); link->state |= DEV_STALE_CONFIG; return; } CardServices(ReleaseConfiguration, link->handle); CardServices(ReleaseIO, link->handle, &link->io); CardServices(ReleaseIRQ, link->handle, &link->irq); link->state &= ~(DEV_CONFIG | DEV_RELEASE_PENDING); DBFEXIT; } /*---------------------------------------------------------------- * prism2sta_event * * Handler for card services events. * * Arguments: * event The event code * priority hi/low - REMOVAL is the only hi * args ptr to card services struct containing info about * pcmcia status * * Returns: * Zero on success, non-zero otherwise * * Side effects: * * * Call context: * Both interrupt and process thread, depends on the event. ----------------------------------------------------------------*/ static int prism2sta_event (event_t event, int priority, event_callback_args_t *args) { int result = 0; dev_link_t *link = (dev_link_t *) args->client_data; wlandevice_t *wlandev = (wlandevice_t*)link->priv; DBFENTER; switch (event) { case CS_EVENT_CARD_INSERTION: link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; prism2sta_config(link); if ((link->state & DEV_CONFIG) == 0 ) { wlandev->netdev->irq = 0; WLAN_LOG_WARNING1("%s: Initialization failed!\n", dev_info); } else { hfa384x_create( ((prism2sta_priv_t*)wlandev->priv)->hw, wlandev->netdev->irq, wlandev->netdev->base_addr, 0); result = prism2sta_initmac(wlandev); if ( result != 0 ) { WLAN_LOG_ERROR1( "MAC Initialization failed. result=%d\n", result); } } break; case CS_EVENT_CARD_REMOVAL: link->state &= ~DEV_PRESENT; if (link->state & DEV_CONFIG) { #if (LINUX_VERSION_CODE < WLAN_KVERSION(2,3,38) ) wlandev->netdev->tbusy = 1; wlandev->netdev->start = 0; #else netif_stop_queue(wlandev->netdev); #endif link->release.expires = RUN_AT(HZ/20); add_timer(&link->release); } break; case CS_EVENT_RESET_REQUEST: WLAN_LOG_NOTICE0( "prism2 card reset not supported " "due to post-reset user mode configuration " "requirements.\n"); WLAN_LOG_NOTICE0( " From user mode, use " "'cardctl suspend;cardctl resume' " "instead.\n"); break; case CS_EVENT_RESET_PHYSICAL: case CS_EVENT_CARD_RESET: WLAN_LOG_WARNING0("Received CS_EVENT_RESET_xxx, should not " "be possible since REQUEST is denied.\n"); break; case CS_EVENT_PM_SUSPEND: link->state |= DEV_SUSPEND; if (link->state & DEV_CONFIG) { #if (LINUX_VERSION_CODE < WLAN_KVERSION(2,3,38) ) wlandev->netdev->tbusy = 1; wlandev->netdev->start = 0; #else netif_stop_queue(wlandev->netdev); #endif CardServices(ReleaseConfiguration, link->handle); } break; case CS_EVENT_PM_RESUME: link->state &= ~DEV_SUSPEND; if (link->state & DEV_CONFIG) { CardServices(RequestConfiguration, link->handle, &link->conf); hfa384x_create( ((prism2sta_priv_t*)wlandev->priv)->hw, wlandev->netdev->irq, wlandev->netdev->base_addr, 0); result=prism2sta_initmac(wlandev); if ( result != 0 ) { WLAN_LOG_ERROR1( "MAC Initialization failed, result=%d.\n", result); } else { #if (LINUX_VERSION_CODE < WLAN_KVERSION(2,3,38) ) wlandev->netdev->tbusy = 0; wlandev->netdev->start = 1; #else netif_start_queue(wlandev->netdev); #endif } } break; } DBFEXIT; return 0; /* noone else does anthing with the return value */ } #endif /* WLAN_PCMCIA */ #if (WLAN_HOSTIF == WLAN_PCI) /*---------------------------------------------------------------- * prism2sta_probe_pci * * Probe routine called when a PCI device w/ matching ID is found. * The ISL3874 implementation uses the following map: * BAR0: Prism2.x registers memory mapped, size=4k * Here's the sequence: * - Allocate the PCI resources. * - Read the PCMCIA attribute memory to make sure we have a WLAN card * - Reset the MAC * - Initialize the netdev and wlan data * - Initialize the MAC * * Arguments: * pdev ptr to pci device structure containing info about * pci configuration. * id ptr to the device id entry that matched this device. * * Returns: * zero - success * negative - failed * * Side effects: * * * Call context: * process thread * ----------------------------------------------------------------*/ static int prism2sta_probe_pci(struct pci_dev *pdev, const struct pci_device_id *id) { unsigned char *phymem; unsigned char *mem; UINT irq; wlandevice_t *wlandev; hfa384x_t *hw; prism2sta_priv_t *priv; pci_link_t *link; DBFENTER; if (pci_enable_device(pdev)) return -EIO; phymem = (unsigned char*)pci_resource_start(pdev, 0); mem = ioremap((UINT)phymem, PCI_SIZE); irq = pdev->irq; WLAN_LOG_INFO3("A Prism2.5 PCI device found, " "phymem:0x%lx, irq:%d\n, mem:0x%lx\n", (long)phymem, irq, (long)mem); /* Create the network device object. */ wlandev = kmalloc(sizeof(wlandevice_t), GFP_KERNEL); if ( wlandev == NULL ) { return -EIO; } memset(wlandev, 0, sizeof(wlandevice_t)); /* Make up a device private data structure. */ wlandev->priv = kmalloc(sizeof(prism2sta_priv_t), GFP_KERNEL); if ( wlandev->priv == NULL ) { kfree_s(wlandev, sizeof(wlandevice_t)); return -EIO; } memset(wlandev->priv, 0, sizeof(prism2sta_priv_t)); /* Make up a hw data structure. */ priv = (prism2sta_priv_t*)wlandev->priv; hw = priv->hw = kmalloc(sizeof(hfa384x_t), GFP_KERNEL); if ( priv->hw == NULL ) { kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); return -EIO; } memset(priv->hw, 0, sizeof(hfa384x_t)); /* Set our entries in the wlandev */ wlandev->open = &prism2sta_open; wlandev->close = &prism2sta_close; wlandev->reset = &prism2sta_reset; wlandev->txframe = &prism2sta_txframe; wlandev->mlmerequest = &prism2sta_mlmerequest; /* Set up the remaining entries in the wlan common way */ wlandev->name = ((prism2sta_priv_t*)wlandev->priv)->name; if ( wlan_setup(wlandev) != 0 ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("wlan_setup() failed!\n"); return -EIO; } wlandev->netdev->irq = irq; wlandev->netdev->mem_start = (unsigned long)mem; wlandev->netdev->mem_end = (unsigned long)mem + PCI_SIZE; if ( register_wlandev(wlandev) != 0 ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("register_wlandev() failed!\n"); return -EIO; } link = kmalloc(sizeof(pci_link_t), GFP_KERNEL); if ( link == NULL ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("Failed to allocate instance link struct.\n"); return -EIO; } link->priv = wlandev; link->next = dev_list; dev_list = link; #if 0 /* TODO: Move this and an irq test into an hfa384x_testif() routine. */ outw(PRISM2STA_MAGIC, HFA384x_SWSUPPORT0(wlandev->netdev->base_addr)); reg=inw( HFA384x_SWSUPPORT0(wlandev->netdev->base_addr)); if ( reg != PRISM2STA_MAGIC ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("MAC register access test failed!\n"); return -EIO; } #endif hfa384x_create(hw, wlandev->netdev->irq, 0, (UINT32)mem); if (hfa384x_corereset(hw) != 0) { WLAN_LOG_ERROR0("MAC reset failed.\n"); kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); dev_list=link->next; kfree_s(link, sizeof(pci_link_t)); wlandev = NULL; } else { if ( prism2sta_initmac(wlandev) != 0) { WLAN_LOG_ERROR0("MAC Initialization failed.\n"); return -EIO; } /* Don't actually hook up the IRQ until we * _know_ things are alright. A test routine would help. */ request_irq(wlandev->netdev->irq, prism2sta_interrupt, SA_SHIRQ, wlandev->name, wlandev); } DBFEXIT; return 0; } #endif /* WLAN_PCI */ #if (WLAN_HOSTIF == WLAN_PLX) /*---------------------------------------------------------------- * prism2sta_probe_plx * * Probe routine called when a PCI device w/ matching ID is found. * This PLX implementation uses the following map: * BAR0: Unused * BAR1: ???? * BAR2: PCMCIA attribute memory * BAR3: PCMCIA i/o space * Here's the sequence: * - Allocate the PCI resources. * - Read the PCMCIA attribute memory to make sure we have a WLAN card * - Reset the MAC using the PCMCIA COR * - Initialize the netdev and wlan data * - Initialize the MAC * * Arguments: * pdev ptr to pci device structure containing info about * pci configuration. * id ptr to the device id entry that matched this device. * * Returns: * zero - success * negative - failed * * Side effects: * * * Call context: * process thread * ----------------------------------------------------------------*/ static int prism2sta_probe_plx(struct pci_dev *pdev, const struct pci_device_id *id) { UINT pccard_ioaddr; unsigned char *pccard_attr_mem; UINT pccard_irq; unsigned char *attr_mem; UINT ioaddr; UINT irq; wlandevice_t *wlandev; prism2sta_priv_t *priv; pci_link_t *link; int reg; if (pci_enable_device(pdev)) return -EIO; pccard_attr_mem = (unsigned char*)pci_resource_start(pdev, 2); pccard_ioaddr = pci_resource_start(pdev, 3); pccard_irq = pdev->irq; /* These assignments are here in case of future mappings for * io space and irq that might be similar to ioremap */ attr_mem =(unsigned char*)ioremap((UINT)pccard_attr_mem, PLX_ATTR_SIZE); ioaddr = pccard_ioaddr; irq = pccard_irq; WLAN_LOG_INFO5("A PLX PCI/PCMCIA interface device found, " "phymem:0x%lx, phyio=0x%x, irq:%d, " "mem: 0x%lx, io:0x%x\n", (long)pccard_attr_mem, pccard_ioaddr, pccard_irq, (long)attr_mem, ioaddr); /* Verify whether PC card is present (this needs improvement) */ if ( attr_mem[0] != 0x01 || attr_mem[2] != 0x03 || attr_mem[4] != 0x00 || attr_mem[6] != 0x00 || attr_mem[8] != 0xFF || attr_mem[10] != 0x17 || attr_mem[12] != 0x04 || attr_mem[14] != 0x67) { WLAN_LOG_ERROR0("Prism2 PC card CIS is invalid.\n"); return -EIO; } WLAN_LOG_INFO0("A PCMCIA WLAN adapter was found.\n"); /* Write COR to enable PC card */ attr_mem[COR_OFFSET] = COR_VALUE; reg = attr_mem[COR_OFFSET]; /* Create the network device object. */ wlandev = kmalloc(sizeof(wlandevice_t), GFP_KERNEL); if ( wlandev == NULL ) { return -EIO; } memset(wlandev, 0, sizeof(wlandevice_t)); /* Make up a device private data structure. */ wlandev->priv = kmalloc(sizeof(prism2sta_priv_t), GFP_KERNEL); if ( wlandev->priv == NULL ) { kfree_s(wlandev, sizeof(wlandevice_t)); return -EIO; } memset(wlandev->priv, 0, sizeof(prism2sta_priv_t)); /* Make up a hw data structure. */ priv = (prism2sta_priv_t*)wlandev->priv; priv->hw = kmalloc(sizeof(hfa384x_t), GFP_KERNEL); if ( priv->hw == NULL ) { kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); return -EIO; } memset(priv->hw, 0, sizeof(hfa384x_t)); /* Set our entries in the wlandev */ wlandev->open = &prism2sta_open; wlandev->close = &prism2sta_close; wlandev->reset = &prism2sta_reset; wlandev->txframe = &prism2sta_txframe; wlandev->mlmerequest = &prism2sta_mlmerequest; /* Set up the remaining entries in the wlan common way */ wlandev->name = ((prism2sta_priv_t*)wlandev->priv)->name; wlandev->name[0] = '\0'; if ( wlan_setup(wlandev) != 0 ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("wlan_setup() failed!\n"); return -EIO; } wlandev->netdev->irq = irq; wlandev->netdev->base_addr = ioaddr; request_region(wlandev->netdev->base_addr, 0xff, dev_info); request_irq(wlandev->netdev->irq, prism2sta_interrupt, SA_SHIRQ, dev_info, wlandev); if ( register_wlandev(wlandev) != 0 ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("register_wlandev() failed!\n"); return -EIO; } link = kmalloc(sizeof(pci_link_t), GFP_KERNEL); if ( link == NULL ) { WLAN_LOG_ERROR0("Failed to allocate instance link struct.\n"); return -EIO; } link->priv = wlandev; link->next = dev_list; dev_list = link; #if 0 outw(PRISM2STA_MAGIC, HFA384x_SWSUPPORT0(wlandev->netdev->base_addr)); reg=inw( HFA384x_SWSUPPORT0(wlandev->netdev->base_addr)); if ( reg != PRISM2STA_MAGIC ) { kfree_s(priv->hw, sizeof(hfa384x_t)); kfree_s(wlandev->priv, sizeof(prism2sta_priv_t)); kfree_s(wlandev, sizeof(wlandevice_t)); WLAN_LOG_ERROR0("IO port access failed!\n"); return -EIO; } #endif hfa384x_create(((prism2sta_priv_t*)wlandev->priv)->hw, wlandev->netdev->irq, wlandev->netdev->base_addr, 0); if ( prism2sta_initmac(wlandev) != 0) { WLAN_LOG_ERROR0("MAC Initialization failed."); return -EIO; } return 0; } #endif /* WLAN_PLX */ /*---------------------------------------------------------------- * init_module * * Module initialization routine, called once at module load time. * This one simulates some of the pcmcia calls. * * Arguments: * none * * Returns: * 0 - success * ~0 - failure, module is unloaded. * * Side effects: * TODO: define * * Call context: * process thread (insmod or modprobe) ----------------------------------------------------------------*/ int init_module(void) { #if (WLAN_HOSTIF == WLAN_PCMCIA) servinfo_t serv; #endif DBFENTER; WLAN_LOG_NOTICE1("%s Loaded\n", version); WLAN_LOG_NOTICE1("dev_info is: %s\n", dev_info); #if (WLAN_HOSTIF == WLAN_PCMCIA) CardServices(GetCardServicesInfo, &serv); if ( serv.Revision != CS_RELEASE_CODE ) { printk(KERN_NOTICE"%s: CardServices release does not match!\n", dev_info); return -1; } /* This call will result in a call to prism2sta_attach */ /* and eventually prism2sta_detach */ register_pcmcia_driver( &dev_info, &prism2sta_attach, &prism2sta_detach); #elif (WLAN_HOSTIF == WLAN_PLX) /* This call will result in a call to prism2sta_probe_plx * if there is a matched PCI card present (ie., which * vendor+device id are matched) */ if (pci_register_driver(&prism2_plx_drv_id) <= 0) { WLAN_LOG_NOTICE0("prism2_plx: No devices found, driver not installed.\n"); pci_unregister_driver(&prism2_plx_drv_id); return -ENODEV; } #elif (WLAN_HOSTIF == WLAN_PCI) /* This call will result in a call to prism2sta_probe_pci * if there is a matched PCI card present (ie., which * vendor+device id are matched) */ if (pci_register_driver(&prism2_pci_drv_id) <= 0) { WLAN_LOG_NOTICE0("prism2_pci: No devices found, driver not installed.\n"); pci_unregister_driver(&prism2_pci_drv_id); return -ENODEV; } #endif DBFEXIT; return 0; } /*---------------------------------------------------------------- * cleanup_module * * Called at module unload time. This is our last chance to * clean up after ourselves. * * Arguments: * none * * Returns: * nothing * * Side effects: * TODO: define * * Call context: * process thread * ----------------------------------------------------------------*/ void cleanup_module(void) { #if (WLAN_HOSTIF == WLAN_PCMCIA) dev_link_t *link = dev_list; DBFENTER; if ( link != NULL ) { WLAN_LOG_WARNING0( "Warning:cleanup_module():dev_list not empty.\n"); /* wlandevice_t *wlandev = (wlandevice_t *)link->priv; */ } unregister_pcmcia_driver( &dev_info); #elif (WLAN_HOSTIF == WLAN_PLX) pci_link_t *link = dev_list; DBFENTER; if ( link != NULL ) { wlandevice_t *wlandev; for (link=dev_list; link != NULL; link = link->next) { wlandev = (wlandevice_t *)link->priv; prism2mgmt_reset(wlandev, NULL); /* release_region(wlandev->netdev->base_addr, 0xff); */ free_irq(wlandev->netdev->irq, wlandev); unregister_wlandev(wlandev); wlan_unsetup(wlandev); } } pci_unregister_driver(&prism2_plx_drv_id); #elif (WLAN_HOSTIF == WLAN_PCI) pci_link_t *link = dev_list; DBFENTER; if ( link != NULL ) { wlandevice_t *wlandev; for (link=dev_list; link != NULL; link = link->next) { wlandev = (wlandevice_t *)link->priv; prism2mgmt_reset(wlandev, NULL); iounmap((void*)wlandev->netdev->mem_start); free_irq(wlandev->netdev->irq, wlandev); unregister_wlandev(wlandev); wlan_unsetup(wlandev); pci_unregister_driver(&prism2_pci_drv_id); } } pci_unregister_driver(&prism2_pci_drv_id); #endif printk(KERN_NOTICE "%s Unloaded\n", version); DBFEXIT; return; }