📄 am930hw.c
字号:
/* clear the MIB counter items */ memset( &mib_stats, 0, sizeof(mib_stats)); am930hw_mibset( hw, SUMIB_STAT, 0, sizeof(mib_stats), &mib_stats); /* increase the tx buffer size */ am930hw_mibget( hw, SUMIB_LOCAL, 0, sizeof(su_mib_local_t), &mib); WLAN_LOG_DEBUG2(1, "default f/w tx.off=0x%04lx tx.len=0x%04lx\n", amd2host32(mib.tx_buffer_offset), amd2host32(mib.tx_buffer_size)); WLAN_LOG_DEBUG2(1, "default f/w rx.off=0x%04lx rx.len=0x%04lx\n", amd2host32(mib.rx_buffer_offset), amd2host32(mib.rx_buffer_size)); mib.tx_buffer_size = host2amd32(AM930_NTXDESC * AM930_TXSLOT_LEN); rx_buffer_top = amd2host32(mib.rx_buffer_offset) + amd2host32(mib.rx_buffer_size); mib.rx_buffer_offset = host2amd32( amd2host32(mib.tx_buffer_offset) + amd2host32(mib.tx_buffer_size) ); mib.rx_buffer_size = host2amd32( rx_buffer_top - amd2host32(mib.rx_buffer_offset)); WLAN_LOG_DEBUG2(1, "f/w tx.off=0x%04lx tx.len=0x%04lx\n", amd2host32(mib.tx_buffer_offset), amd2host32(mib.tx_buffer_size)); WLAN_LOG_DEBUG2(1, "f/w rx.off=0x%04lx rx.len=0x%04lx\n", amd2host32(mib.rx_buffer_offset), amd2host32(mib.rx_buffer_size)); am930hw_mibset( hw, SUMIB_LOCAL, 0, sizeof(su_mib_local_t), &mib); /* If we're an AP, set some extra items */ if (am930_isap) /* can't do this, it isn't set yet hw->mac->mode == AM930_MACMODE_ESS_AP) */ { /* set the AP mib */ am930hw_mibsetitem( hw, LOC_NETWORK_MODE, &one, MI_SZ(LOC_NETWORK_MODE)); /* set the AP mib */ am930hw_mibsetitem( hw, LOC_ACTING_AS_AP, &one, MI_SZ(LOC_ACTING_AS_AP)); /* Set the driver state */ if (hw->mac->mode == AM930_MACMODE_ESS_AP) { am930hw_stateset( hw, SUCS_DS_AP_NO_ASSOC);#if (WLAN_OS == WLAN_LWOS)// LED_Blink( WLAN_HLED_LINK, LED_OFF);#endif } } /* clear the VBM area */ memsetcard( hw, hw->vbm, 0x00, 0x100); /* Get the rx buffer loc and size for sanity checking in */ /* onint_rx */ am930hw_mibget( hw, SUMIB_LOCAL, 0, sizeof(su_mib_local_t), &mib); hw->rx_base = amd2host32(mib.rx_buffer_offset); hw->rx_len = amd2host32(mib.rx_buffer_size); /* Save our MAC address */ am930hw_mibgetitem( hw, ADDR_MAC_ADDR, hw->addr, MI_SZ(ADDR_MAC_ADDR) ); /* Set the f/w interrupt mask and status */ am930hw_getset_int( hw, SU_SET_INTMASK1 | SU_SET_INTSTATUS1, SUCS_INT_WAKEUP | SUCS_INT_CFPEND | SUCS_INT_DTIMTX | SUCS_INT_CFPSTART | SUCS_INT_MASK_CMD, 0x00 ); am930hw_getset_int( hw, SU_SET_INTMASK2 | SU_SET_INTSTATUS2, 0x00, 0x00 ); /* Enable Core Generated Interrupts and clear any pending */ reg = inb_p( GCR(hw) ); reg |= BIT1 | BIT3; outb_p( reg, GCR(hw) ); } } } DBFEXIT; return hw;} /*----------------------------------------------------------------* am930hw_destruct** returns: nothing----------------------------------------------------------------*/void am930hw_destruct(am930hw_t *hw){ DBFENTER;#if (WLAN_OS == WLAN_LINUX_KERNEL) kfree_s( hw, sizeof(am930hw_t));#elif (WLAN_OS == WLAN_LWOS)#else #error "No WLAN_OS match!"#endif DBFEXIT; return;}/*----------------------------------------------------------------* am930hw_fw_cmd** This is a helper function used to send commands to the* firmware. It copies its arguments to the command block* and then sets the command. After setting the command, we then* wait for the command to complete. The wait includes a timeout* and if the command times out, that status is returned.* Otherwise we return the firmware indicated status. If the* firmware status is OK and if needed, the command parms are* copied to the buffer space provided by the caller.** Argument notes:* the ABS of the parmsize argument provides the number of * bytes to copy to the command parm area, and if the parm* is < 0 then after the command completes, we copy that* many bytes back from the command parm area as well.** returns: an am930cmd_result_t structure, which should be * four bytes long and can be played with like an int.----------------------------------------------------------------*/am930hw_cmd_result_t am930hw_fw_cmd( am930hw_t *hw, UINT8 cmd, void* cmdparms, INT32 parmsize){ am930hw_cmd_result_t result; UINT32 timeout; DBFENTER; /* clear the result struct */ memset( &result, 0, sizeof(result)); /* wait for the cmd block to be available */#if (WLAN_OS == WLAN_LINUX_KERNEL) timeout = jiffies + wlan_ms_to_ticks(100); while( read8(hw, hw->cmd + CMD_OFF_CODE) != 0 && read8(hw, hw->cmd + CMD_OFF_STATUS) == 0 && jiffies < timeout );#elif (WLAN_OS == WLAN_LWOS) timeout = 1000; while( read8(hw, hw->cmd + CMD_OFF_CODE) != 0 && read8(hw, hw->cmd + CMD_OFF_STATUS) == 0 && timeout ) { udelay(10); timeout--; }#else #error "No WLAN_OS match!"#endif#if (WLAN_OS == WLAN_LINUX_KERNEL) if ( jiffies >= timeout )#elif (WLAN_OS == WLAN_LWOS) if ( timeout == 0 )#else #error "No WLAN_OS match!"#endif { WLAN_LOG_WARNING0("Timed out waiting for f/w cmd byte available\n"); result.drvr_status = AM930HW_FWNOTREADY; } else { /* Make sure the status and code are set properly */ write8(hw, hw->cmd + CMD_OFF_CODE, 0); write8(hw, hw->cmd + CMD_OFF_STATUS, 0); /* copy the parms to the command block */ if ( parmsize != 0 ) { writecard(hw, hw->cmd + CMD_OFF_PARMS, cmdparms, ABS(parmsize)); } /* Tell the hw object that this cmd is the most recent */ hw->last_cmd = cmd; /* set the command */ write8(hw, hw->cmd + CMD_OFF_CODE, cmd); /* Now, wait for the result */#if (WLAN_OS == WLAN_LINUX_KERNEL) timeout = jiffies + wlan_ms_to_ticks(1500); while ( read8(hw, hw->cmd + CMD_OFF_STATUS) == SUCMD_STAT_IDLE && jiffies < timeout );#elif (WLAN_OS == WLAN_LWOS) timeout = 1500; while ( read8(hw, hw->cmd + CMD_OFF_STATUS) == SUCMD_STAT_IDLE && timeout ) { udelay(10); timeout--; }#else #error "No WLAN_OS match!"#endif /* Did we time out or did the fw finish with something? */#if (WLAN_OS == WLAN_LINUX_KERNEL) if ( jiffies >= timeout )#elif (WLAN_OS == WLAN_LWOS) if ( timeout == 0 )#else #error "No WLAN_OS match!"#endif { /* If we timed out, simply set the drvr status and return */ result.fw_status = read8(hw, hw->cmd + CMD_OFF_STATUS); result.drvr_status = AM930HW_FWCMDTIMEOUT; WLAN_LOG_WARNING1("We timed out on command: 0x%02x\n", hw->last_cmd); } else { /* The fw finished with something, what? */ if ( read8(hw, hw->cmd + CMD_OFF_STATUS) != SUCMD_STAT_CMPLT ) { /* some error occurred, save the result and move on */ result.fw_status = read8(hw, hw->cmd + CMD_OFF_STATUS); result.fw_err_off = read8(hw, hw->cmd + CMD_OFF_ERR_OFF); WLAN_LOG_DEBUG1(1,"f/w cmd error, code=0x%x\n", result.fw_status); } else { /* Everything appears to be OK, now collect any results */ result.drvr_status = 0; result.fw_status = read8(hw, hw->cmd + CMD_OFF_STATUS); if ( parmsize < 0 ) { readcard( hw, hw->cmd + CMD_OFF_PARMS, cmdparms, ABS(parmsize)); } } /* clear the cmd and status */ write8(hw, hw->cmd + CMD_OFF_CODE, 0); write8(hw, hw->cmd + CMD_OFF_STATUS, 0); } } DBFEXIT; return result;} /*----------------------------------------------------------------* am930hw_getset_int** Attempts to access the sutro f/w control structure int_mask or* int_status field of the given control/status block. It uses the* lockout_fw and lockout_host fields to block the firmware from* accessing the mask and status fields while we get or update* them.** action values:* SU_GET_INTMASK1 get the interrupt mask* SU_SET_INTMASK1 set the interrupt mask* SU_GET_INTMASK2 get the interrupt mask* SU_SET_INTMASK2 set the interrupt mask* SU_GET_INTSTATUS1 get the interrupt status* SU_GET_INTSTATUS1 get the interrupt status* SU_SET_INTSTATUS2 set the interrupt status* SU_SET_INTSTATUS2 set the interrupt status** The value argument is only used by the SET actions.** returns: -1 on failure, * 0 or GET result on success----------------------------------------------------------------*/static UINT16 am930hw_getset_int( am930hw_t *hw, int action, UINT8 mask, UINT8 status ){ UINT16 result = 0; DBFENTER; if ( am930hw_lockint(hw) == 0 ) { if (action & SU_GET_INTMASK1 ) result = read8(hw, hw->cs + CS_OFF_INT_MASK); if ( action & SU_SET_INTMASK1 ) write8(hw, hw->cs + CS_OFF_INT_MASK, mask); if (action & SU_GET_INTMASK2 ) result = read8(hw, hw->cs + CS_OFF_INT_MASK2); if ( action & SU_SET_INTMASK2 ) write8(hw, hw->cs + CS_OFF_INT_MASK2, mask); if ( action & SU_GET_INTSTATUS1 ) result = read8(hw, hw->cs + CS_OFF_INT_STATUS); if ( action & SU_SET_INTSTATUS1 ) write8(hw, hw->cs + CS_OFF_INT_STATUS, status); if ( action & SU_GET_INTSTATUS2 ) result = read8(hw, hw->cs + CS_OFF_INT_STATUS2); if ( action & SU_SET_INTSTATUS2 ) write8(hw, hw->cs + CS_OFF_INT_STATUS2, status); am930hw_unlockint(hw); } else { result = -1; } DBFEXIT; return result;}/*----------------------------------------------------------------* am930hw_init_rx_tx** Performs the second part of the object initialization. We have* to do this here so that all of the links are in place in the* other objects. Particularly so that interrupts get routed to* this object.** returns: zero on success, non-zero on failure----------------------------------------------------------------*/int am930hw_init_rx_tx( am930hw_t *hw ){ int result = 0; DBFENTER; /* Configure the tx queues */ if ( am930hw_init_tx_queues(hw) != 0 ) { result = 1; } else { /* Enable tx */ if ( am930hw_tx_enable(hw) != AM930HW_CMD_SUCCESS ) { result = 2; } else { udelay(100); /* Enable rx */ if ( am930hw_rx_enable(hw) != AM930HW_CMD_SUCCESS ) { result = 3; } else { udelay(100); } } } DBFEXIT; return result;}/*----------------------------------------------------------------* am930hw_init_tx_queue** Initialize the tx queue related data structures and members of* the hw object.** NOTE: this function takes care of am930 related queues, NOT* any of the upper level protocol packet queues.** returns: nothing----------------------------------------------------------------*/int am930hw_init_tx_queues( am930hw_t *hw){ int result = 0; int i; UINT32 curr_off; am930tx_dataslot_t ds; DBFENTER; /* From the MIB, find out where the TX queue is supposed to go */ if ( am930hw_mibgetitem( hw, LOC_TX_BUFFER_OFFSET, &(hw->tx_tail), sizeof(hw->tx_tail)) != AM930HW_CMD_SUCCESS ) { WLAN_LOG_ERROR0("mibgetitem(LOC_TX_BUFFER_OFFSET) Failed!\n"); result = 1; } else { /* We've got the MIB, now init. the main queue */ hw->tx_tail = amd2host32(hw->tx_tail); hw->tx_base = hw->tx_tail; am930hw_mibgetitem( hw, LOC_TX_BUFFER_SIZE, &(hw->tx_len), sizeof(hw->tx_len)); hw->tx_len = amd2host32(hw->tx_len); curr_off = hw->tx_base; hw->ntxbuf = hw->tx_len / sizeof(am930tx_dataslot_t); hw->used_txbuf = 0; WLAN_LOG_INFO1("Allocated %d tx slots\n", hw->ntxbuf); memset( &ds.desc, 0, sizeof(am930tx_desc_t)); for ( i = 0; i < hw->ntxbuf; i++) { /* first set the address where the next desc will go */ ds.desc.next = (i < hw->ntxbuf - 1) ? host2amd32(curr_off + sizeof(ds)) : host2amd32(hw->tx_base); /* set the prev ptr */ ds.desc.prev = (i > 0) ? host2amd32(curr_off - sizeof(ds)) : host2amd32(hw->tx_base + (sizeof(ds) * (hw->ntxbuf - 1))); /* write the descriptor */ writecard( hw, curr_off, &ds.desc, sizeof(ds.desc)); /* update the offset */ curr_off += sizeof(ds); } } DBFEXIT; return result;}/*----------------------------------------------------------------* am930hw_ISR** HW object interrupt service routine. Called when the pcmcia* circuitry has generated a host interrupt in response to an* interrupt from supposedly our card.** returns: nothing----------------------------------------------------------------*/void am930hw_ISR( am930hw_t *hw ){ UINT8 int_status1; UINT8 int_status2; UINT8 int_mask1; UINT8 int_mask2; UINT8 gcr; UINT8 handled = 0; DBFENTER; /* Make sure the hw object is ready */ /* - additional checks make sure the H/W is _really_ in the slot */ /* - sometimes an int reaches here after a card is removed and before */ /* card services has been notified. (Thanks David) */ if ( (hw != NULL) && (hw->state & AM930HW_CONFIG)#if (WLAN_OS == WLAN_LINUX_KERNEL)#ifdef WLAN_PCMCIA && (((dev_link_t*)(hw->mac->di))->state & DEV_PRESENT) && read8(hw, hw->banner) == 'P' && read8(hw, hw->banner+1) == 'C'#endif )#elif (WLAN_OS == WLAN_LWOS) )#else #error "No WLAN_OS match!"#endif { /* Make sure we have not overlapped ISR calls */ if ( hw->state & AM930HW_INTOCURRED ) { WLAN_LOG_WARNING0("Overlapping Interrupt detected!\n"); } else { hw->state |= AM930HW_INTOCURRED; /* Lock the int fields */ if ( am930hw_lockint(hw) != 0 ) { WLAN_LOG_DEBUG0(2,"Unable to lockout f/w, skip interrupt\n"); /* Clear the ECWAIT bit of GCR (by setting...wierd) */ gcr = inb_p( GCR(hw)); gcr |= BIT3; outb_p( gcr, GCR(hw));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -