patch-2.3.31 linux/drivers/net/aironet4500_core.c
Next file: linux/drivers/net/aironet4500_proc.c
Previous file: linux/drivers/net/aironet4500_card.c
Back to the patch index
Back to the overall index
- Lines: 3227
- Date:
Tue Dec 7 23:58:22 1999
- Orig file:
v2.3.30/linux/drivers/net/aironet4500_core.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.3.30/linux/drivers/net/aironet4500_core.c linux/drivers/net/aironet4500_core.c
@@ -0,0 +1,3225 @@
+/*
+ * Aironet 4500 Pcmcia driver
+ *
+ * Elmer Joandi, Januar 1999
+ * Copyright Elmer Joandi, all rights restricted
+ *
+ *
+ * Revision 0.1 ,started 30.12.1998
+ *
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include "aironet4500.h"
+
+
+int bap_sleep = 10 ;
+int bap_sleep_after_setup = 1;
+int sleep_before_command = 1;
+int bap_sleep_before_write= 1;
+int sleep_in_command = 1;
+
+EXPORT_SYMBOL(bap_sleep);
+EXPORT_SYMBOL(bap_sleep_after_setup);
+EXPORT_SYMBOL(sleep_before_command);
+EXPORT_SYMBOL(bap_sleep_before_write);
+EXPORT_SYMBOL(sleep_in_command);
+
+struct awc_strings awc_status_error_codes[]=awc_reply_error_strings;
+struct awc_strings awc_command_names[]=awc_command_name_strings;
+struct awc_strings awc_link_status_names[]=awc_link_status_strings;
+struct awc_strings awc_rid_names[]=aironet4500_RID_Select_strings;
+struct awc_strings awc_link_failure_reason_names[]=IEEE_802_11_LINK_STATUS_FAILURE_REASON_STRINGS;
+
+const char * awc_print_string( struct awc_strings* strings, int code){
+
+ struct awc_strings * str = strings;
+ int i = 0;
+ while (str[i].string != NULL){
+ if (str[i].par == (code & str[i].mask )){
+ return str[i].string;
+ };
+ i++;
+ };
+ return "UNKNOWN";
+};
+
+int awc_dump_registers(struct NET_DEVICE * dev){
+
+#ifdef AWC_DEBUG
+ int i;
+#endif
+ int status= inw(dev->base_addr +4*2);
+ int r1= inw(dev->base_addr +5*2);
+ int r2= inw(dev->base_addr +6*2);
+ int r3= inw(dev->base_addr +7*2);
+
+ printk(KERN_ERR "Command %s , result: %s, at memblk %x(RID %s) , offset %x \n",
+ awc_print_string(awc_command_names,status),
+ awc_print_string(awc_status_error_codes,r1),
+ r2, awc_print_string(awc_rid_names,r2),
+ r3);
+
+#ifdef AWC_DEBUG
+ printk(KERN_ERR "%s aironet register dump ",dev->name );
+
+
+ for (i=0; i < 32; i++){
+ printk("%4x ", inw(dev->base_addr + i*2 ) );
+ if ( (i+1)%8 == 0){
+ printk("\n");
+ printk(KERN_ERR "%02x",(i+1)*2);
+ }
+ };
+ printk(KERN_ERR " \n");
+#endif
+ return 0;
+};
+
+/****************************** COMMAND ******************/
+
+
+inline
+int awc_command_busy_clear_wait(struct NET_DEVICE * dev){
+// long long jiff = jiffies;
+ u16 active_interrupts;
+ int cnt= 0;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_command_busy_clear_wait ");
+
+ while (awc_command_busy(dev->base_addr)){
+ if (cnt > 1000 ){
+ printk(KERN_ERR "awc command busy too long, clearing\n");
+ awc_dump_registers(dev);
+ awc_event_ack_ClrStckCmdBsy(dev->base_addr);
+ break;
+ };
+ if (((struct awc_private*) dev->priv)->ejected)
+ return -1;
+ cnt++;
+ udelay(10);
+ }
+
+ cnt = 0;
+ while (awc_command_busy(dev->base_addr)){
+ //if (jiffies - jiff > (HZ/3)){
+ if (cnt > 30000 ){
+ printk(KERN_CRIT "awc command busy WAY too long, clearing\n");
+ awc_dump_registers(dev);
+ awc_event_ack_ClrStckCmdBsy(dev->base_addr);
+ active_interrupts = awc_event_status(dev->base_addr);
+ awc_event_ack(dev->base_addr, active_interrupts);
+
+ AWC_ENTRY_EXIT_DEBUG("BAD exit\n ");
+ return -1 ;
+
+ };
+ if (((struct awc_private*) dev->priv)->ejected)
+ return -1;
+ cnt++;
+ udelay(10);
+ }
+
+
+ AWC_ENTRY_EXIT_DEBUG(" exit\n ");
+
+ return 0;
+
+
+};
+
+
+
+inline unsigned short
+awc_issue_command_and_block(struct awc_command * cmd){
+
+ int ticks;
+ long long jiff;
+ u16 enabled_interrupts;
+ int cnt = 0;
+// unsigned long flags;
+
+ jiff = jiffies;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_command_and_block ");
+
+ DOWN(&cmd->priv->command_semaphore);
+
+// save_flags(flags);
+// cli();
+
+ if (awc_command_busy_clear_wait(cmd->dev)) goto final;
+
+ if (cmd->priv->sleeping_bap) udelay(sleep_before_command);
+
+ awc4500wout(cmd->port,cmd->command,cmd->par0,cmd->par1,cmd->par2);
+// awc_dump_registers(cmd->dev);
+
+
+ if (cmd->priv->sleeping_bap) udelay(sleep_in_command);
+
+ enabled_interrupts = awc_ints_enabled(cmd->dev->base_addr);
+ awc_ints_enable(cmd->dev->base_addr, enabled_interrupts & ~0x10);
+ if(cmd->priv->enabled_interrupts & 0x10)
+ cmd->priv->enabled_interrupts &= ~0x10;
+
+
+ while ( awc_command_read(cmd->port) == cmd->command) {
+ udelay(1);
+ awc_command_write(cmd->port, cmd->command);
+ //if ((jiffies - jiff) > 2){
+ if (cnt > 2000 ){
+ printk(" long wait with commmand reg busy in blocking command \n");
+ awc_dump_registers(cmd->dev);
+ goto final;
+ };
+ if (cmd->priv->ejected)
+ goto final;
+ cnt++;
+ udelay(10);
+
+ };
+ AWC_ENTRY_EXIT_DEBUG(" issued " );
+
+ ticks = 0;
+ while ( awc_event_status_Cmd(cmd->port) == 0) {
+ ticks++;
+ if (ticks > 100000){
+ printk(" long wait with commmand reg busy \n");
+ awc_dump_registers(cmd->dev);
+ goto final;
+ };
+ if (ticks > 500){
+ DEBUG(1, " long wait after issue 10mks * %d ", ticks );
+ //printk(" long wait with command reg busy about ticks\n");
+ // sti();
+ }
+ if (cmd->priv->ejected)
+ goto final;
+ udelay(10);
+ }
+ if (cmd->priv->sleeping_bap) udelay(sleep_in_command);
+
+ awc_read_response(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" resp read \n");
+
+ if (awc_command_busy(cmd->port))
+ awc_event_ack_ClrStckCmdBsy(cmd->port);
+
+ awc_event_ack_Cmd(cmd->port);
+ if (cmd->priv->sleeping_bap) udelay(sleep_in_command);
+
+ if (cmd->status & 0xff00){
+ printk(KERN_ERR " bad response to command %s, parameter %x \n",awc_print_string(awc_command_names, cmd->command),cmd->par0);
+ awc_dump_registers(cmd->dev);
+ goto final;
+ }
+
+ // restore_flags(flags);
+ UP(&cmd->priv->command_semaphore);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ udelay(1);
+ return 0;
+final:
+// restore_flags(flags);
+ UP(&cmd->priv->command_semaphore);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+};
+
+
+inline
+unsigned short
+awc_issue_command(struct awc_command * cmd){
+
+
+// long long jiff = jiffies;
+// unsigned short enabled_ints;
+ int cnt = 0;
+// int i=0;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_command");
+
+ if (!cmd){
+ printk(KERN_CRIT "cmd == NULL in awc_issue_command\n");
+ return -1;
+
+ }
+ if (!cmd->dev){
+ printk(KERN_CRIT "cmd->dev == NULL in awc_issue_command\n");
+ return -1;
+
+ }
+
+ DOWN(&cmd->priv->command_semaphore);
+
+ if(awc_command_busy_clear_wait(cmd->dev)) goto final;
+
+ if(!cmd->priv->enabled_interrupts & 0x10){
+ cmd->priv->enabled_interrupts |= 0x10;
+ awc_ints_enable(cmd->port, cmd->priv->enabled_interrupts );
+ }
+
+ cmd->priv->async_command_start = jiffies;
+ cmd->priv->command_semaphore_on++;
+
+
+ awc4500wout(cmd->port,cmd->command,cmd->par0,cmd->par1,cmd->par2);
+
+ while ( awc_command_read(cmd->port) == cmd->command) {
+
+ awc_command_write(cmd->port, cmd->command);
+ //if ((jiffies - jiff) > 2){
+ if (cnt > 2000) {
+ printk(" long wait with commmand reg busy in async command \n");
+ awc_dump_registers(cmd->dev);
+ goto final;
+ };
+ if (cmd->priv->ejected)
+ goto final;
+ cnt++;
+ udelay(10);
+ };
+
+ cmd->priv->cmd = *cmd;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ UP(&cmd->priv->command_semaphore);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+
+};
+
+inline
+unsigned short
+awc_issue_command_no_ack(struct NET_DEVICE * dev,
+ u16 com, u16 par1, u16 par2, u16 par3){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ int cnt = 0;
+ long long jiff;
+ jiff = jiffies;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_command_no_ack ");
+
+
+ DOWN(&priv->command_semaphore);
+
+ if (awc_command_busy_clear_wait(dev)) {
+ printk("aironet4x00 no_ack command (reset) with stuck card \n");
+ }
+
+ awc4500wout(dev->base_addr,com, par1, par2,par3);
+
+ udelay(10);
+ while ( awc_event_status_Cmd(dev->base_addr) == 0) {
+ if (awc_command_read(dev->base_addr) == com) {
+ awc_command_write(dev->base_addr, com);
+ }
+ //if ((jiffies - jiff) > 2){
+ if (cnt > 2000) {
+ printk(" long wait with commmand reg busy in noack command %d par %d %d %d\n",com,par1,par2,par3);
+ awc_dump_registers(dev);
+ goto final;
+ };
+ if (priv->ejected)
+ goto final;
+ udelay(10);
+ cnt++;
+ }
+
+ if (awc_command_busy(dev->base_addr))
+ awc_event_ack_ClrStckCmdBsy(dev->base_addr);
+
+ UP(&priv->command_semaphore);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+final:
+ UP(&priv->command_semaphore);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+};
+
+
+/******************************** BAP *************************/
+
+inline
+int awc_bap_setup(struct awc_command * cmd) {
+
+ int status;
+ long long jiff;
+ unsigned long flags;
+ int cleared = 0;
+ int cycles = 0;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_bap_setup ");
+
+ if ( cmd->priv->sleeping_bap)
+ udelay(bap_sleep);
+
+ if (cmd->priv->ejected)
+ return -1;
+
+ if (!cmd->bap || !(cmd->lock_state & (AWC_BAP_SEMALOCKED |AWC_BAP_LOCKED)))
+ DEBUG(1,"no bap or bap not locked cmd %d !!", cmd->command);
+
+
+ status = AWC_IN(cmd->bap->offset);
+
+ if (status & ~0x2000 ){
+ WAIT61x3;
+ status = AWC_IN(cmd->bap->offset);
+ }
+
+ if (status & ~0x2000 ){
+ WAIT61x3;
+ AWC_IN(cmd->dev->base_addr + 0x26);
+ AWC_OUT(cmd->dev->base_addr + 0x26, 0);
+ WAIT61x3;
+ udelay(60);
+ #ifdef AWC_DEBUG
+ printk("b");
+ #endif
+ status = AWC_IN(cmd->bap->offset);
+ }
+
+
+ if (status & 0xC000){
+ printk(KERN_ERR "bap entered with err or busy bit set %x \n",status);
+ if (cmd->bap->lock != 1)
+ printk(KERN_ERR "bap lock bad same time %x\n",cmd->bap->lock);
+ awc_dump_registers(cmd->dev);
+ // AWC_OUT(cmd->bap->offset, 0x800);
+ }
+
+// save_flags(flags);
+// cli();
+
+ AWC_OUT(cmd->bap->select, cmd->rid);
+ WAIT61x3;
+ AWC_OUT(cmd->bap->offset, cmd->offset);
+
+// restore_flags(flags);
+
+ WAIT61x3;
+
+ jiff = jiffies;
+
+ while (1) {
+ cycles++;
+ status = AWC_IN(cmd->bap->offset);
+ if ( cmd->priv->sleeping_bap)
+ udelay(bap_sleep);
+ if (cmd->priv->ejected)
+ return -1;
+ udelay(1);
+ if (cycles > 10000) {
+ printk(KERN_CRIT "deadlock in bap\n");
+ return AWC_ERROR;
+ };
+ status = AWC_IN(cmd->bap->offset);
+ if (status & AWC_BAP_BUSY) {
+ if (cycles % 100 == 99 ) {
+ save_flags(flags);
+ cli();
+ if (!cleared){
+ AWC_IN(cmd->dev->base_addr + 0x26);
+ AWC_OUT(cmd->dev->base_addr + 0x26, 0);
+ WAIT61x3;
+ cleared = 1;
+ }
+ AWC_OUT(cmd->bap->select, cmd->rid);
+ WAIT61x3;
+ AWC_OUT(cmd->bap->offset, cmd->offset);
+ restore_flags(flags);
+ #ifdef AWC_DEBUG
+ printk("B");
+ #endif
+
+ if ( cmd->priv->sleeping_bap)
+ udelay(bap_sleep);
+ else udelay(30);
+ //restart_timeout();
+ }
+ if (jiffies - jiff > 1 ) {
+ AWC_ENTRY_EXIT_DEBUG(" BAD BUSY exit \n");
+ awc_dump_registers(cmd->dev);
+ return AWC_ERROR;
+ }
+ continue;
+ }
+ if (status & AWC_BAP_DONE) {
+ WAIT61x3; WAIT61x3; WAIT61x3;
+
+ // if ((status & 0xfff) != cmd->offset)
+ // printk(KERN_ERR "awcPBD %x ",status);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ if (cmd->priv->sleeping_bap)
+ udelay(bap_sleep_after_setup);
+
+ // success
+ return AWC_SUCCESS;
+ }
+
+ if (status & AWC_BAP_ERR) {
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ // invalid rid or offset
+ printk(KERN_ERR "bap setup error bit set for rid %x offset %x \n",cmd->rid,cmd->offset);
+ awc_dump_registers(cmd->dev);
+ return AWC_ERROR;
+ }
+ if ( cmd->priv->sleeping_bap)
+ udelay(bap_sleep);
+ else udelay(1);
+ // -- awc missed it, try again
+
+ save_flags(flags);
+ cli();
+ AWC_OUT(cmd->bap->select, cmd->rid);
+ WAIT61x3;
+ AWC_OUT(cmd->bap->offset, cmd->offset);
+ WAIT61x3;
+ restore_flags(flags);
+
+ if (jiffies - jiff > HZ)
+ if (! (status &(AWC_BAP_ERR |AWC_BAP_DONE |AWC_BAP_BUSY))){
+ printk("aironet4500: bap setup lock without any status bits set");
+ awc_dump_registers(cmd->dev);
+ return AWC_ERROR;
+
+ };
+
+ }
+
+ AWC_ENTRY_EXIT_DEBUG(" WE MUST NOT BE HERE exit \n");
+}
+
+
+ // requires call to awc_bap_setup() first
+inline
+int
+awc_bap_read(struct awc_command * cmd) {
+ register u16 len;
+ register u16 * buff = (u16 *) cmd->buff;
+ register u16 port= cmd->bap->data;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_bap_read ");
+ if (!cmd->bap && !(cmd->lock_state & (AWC_BAP_SEMALOCKED |AWC_BAP_LOCKED)))
+ DEBUG(0,"no bap or bap not locked %d !!", cmd->command);
+ cmd->len = (cmd->len + 1) & (~1); // round up to even value
+ len = cmd->len / 2;
+ if (cmd->priv->ejected)
+ return -1;
+
+
+ if (cmd->priv->sleeping_bap)
+ udelay(bap_sleep_before_write);
+
+ if (!cmd->priv->sleeping_bap)
+ while ( len-- > 0)
+ *buff++ = AWC_IN(port);
+ else
+ while ( len-- > 0){
+ *buff++ = AWC_IN(port);
+ }
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ if (cmd->priv->ejected)
+ return -1;
+
+ return AWC_SUCCESS;
+}
+
+ // requires call to awc_bap_setup() first
+inline
+int
+awc_bap_write(struct awc_command * cmd){
+ register u16 len;
+ register u16 * buff = (u16 *) cmd->buff;
+ register u16 port= cmd->bap->data;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_bap_write ");
+ if (!cmd->bap && !(cmd->lock_state & (AWC_BAP_SEMALOCKED |AWC_BAP_LOCKED)))
+ DEBUG(0,"no bap or bap not locked %d !!", cmd->command);
+
+ cmd->len = (cmd->len + 1) & (~1); // round up to even value
+ len = cmd->len / 2;
+
+ if (cmd->priv->ejected)
+ return -1;
+
+ if (cmd->priv->sleeping_bap)
+ udelay(bap_sleep_before_write);
+
+
+ if (!cmd->priv->sleeping_bap)
+ while (len-- > 0)
+ AWC_OUT(port, *buff++);
+ else
+ while ( len-- > 0){
+ AWC_OUT(port, *buff++);
+ }
+ if (cmd->priv->ejected)
+ return -1;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+
+ return AWC_SUCCESS;
+}
+
+
+
+
+/***************************** RID READ/WRITE ********************/
+
+const struct aironet4500_rid_selector aironet4500_RID_Select_General_Config =(const struct aironet4500_rid_selector){ 0xFF10, 1,0,0, "General Configuration" }; // See notes General Configuration Many configuration items.
+const struct aironet4500_rid_selector aironet4500_RID_Select_SSID_list =(const struct aironet4500_rid_selector){ 0xFF11, 1,0,0, "Valid SSID list" }; // See notes Valid SSID list List of SSIDs which the station may associate to.
+const struct aironet4500_rid_selector aironet4500_RID_Select_AP_list =(const struct aironet4500_rid_selector){ 0xFF12, 1,0,0, "Valid AP list" }; // See notes Valid AP list List of APs which the station may associate to.
+const struct aironet4500_rid_selector aironet4500_RID_Select_Driver_name =(const struct aironet4500_rid_selector){ 0xFF13, 1,0,0, "Driver name" }; // See notes Driver name The name and version of the driver (for debugging)
+const struct aironet4500_rid_selector aironet4500_RID_Select_Encapsulation =(const struct aironet4500_rid_selector){ 0xFF14, 1,0,0, "Ethernet Protocol" }; // See notes Ethernet Protocol Rules for encapsulating ethernet payloads onto 802.11.
+const struct aironet4500_rid_selector aironet4500_RID_Select_WEP_volatile =(const struct aironet4500_rid_selector){ 0xFF15, 1,0,0, "WEP key volatile" }; //
+const struct aironet4500_rid_selector aironet4500_RID_Select_WEP_nonvolatile =(const struct aironet4500_rid_selector){ 0xFF16, 1,0,0, "WEP key non-volatile" }; //
+const struct aironet4500_rid_selector aironet4500_RID_Select_Modulation =(const struct aironet4500_rid_selector){ 0xFF17, 1,0,0, "Modulation" }; //
+const struct aironet4500_rid_selector aironet4500_RID_Select_Active_Config =(const struct aironet4500_rid_selector){ 0xFF20, 0,1,1, "Actual Configuration" }; // Read only Actual Configuration This has the same format as the General Configuration.
+const struct aironet4500_rid_selector aironet4500_RID_Select_Capabilities =(const struct aironet4500_rid_selector){ 0xFF00, 0,1,0, "Capabilities" }; // Read Only Capabilities PC4500 Information
+const struct aironet4500_rid_selector aironet4500_RID_Select_AP_Info =(const struct aironet4500_rid_selector){ 0xFF01, 0,1,1, "AP Info" }; // Read Only AP Info Access Point Information
+const struct aironet4500_rid_selector aironet4500_RID_Select_Radio_Info =(const struct aironet4500_rid_selector){ 0xFF02, 0,1,1, "Radio Info" }; // Read Only Radio Info Radio Information -- note radio specific
+const struct aironet4500_rid_selector aironet4500_RID_Select_Status =(const struct aironet4500_rid_selector){ 0xFF50, 0,1,1, "Status" }; // Read Only Status PC4500 Current Status Information
+const struct aironet4500_rid_selector aironet4500_RID_Select_16_stats =(const struct aironet4500_rid_selector){ 0xFF60, 0,1,1, "Cumulative 16-bit Statistics" }; // Read Only 16-bit Statistics Cumulative 16-bit Statistics
+const struct aironet4500_rid_selector aironet4500_RID_Select_16_stats_delta =(const struct aironet4500_rid_selector){ 0xFF61, 0,1,1, "Delta 16-bit Statistics" }; // Read Only 16-bit Statistics Delta 16-bit Statistics (since last clear)
+const struct aironet4500_rid_selector aironet4500_RID_Select_16_stats_clear =(const struct aironet4500_rid_selector){ 0xFF62, 0,1,1, "Delta 16-bit Statistics and Clear" }; // Read Only / 16-bit Statistics Delta 16-bit Statistics and Clear
+const struct aironet4500_rid_selector aironet4500_RID_Select_32_stats =(const struct aironet4500_rid_selector){ 0xFF68, 0,1,1, "Cumulative 32-bit Statistics" }; // Read Only 32-bit Statistics Cumulative 32-bit Statistics
+const struct aironet4500_rid_selector aironet4500_RID_Select_32_stats_delta =(const struct aironet4500_rid_selector){ 0xFF69, 0,1,1, "Delta 32-bit Statistics" }; // Read Only 32-bit Statistics Delta 32-bit Statistics (since last clear)
+const struct aironet4500_rid_selector aironet4500_RID_Select_32_stats_clear =(const struct aironet4500_rid_selector){ 0xFF6A, 0,1,1, "Delta 32-bit Statistics and Clear" }; // Read Only / 32-bit Statistics Delta 32-bit Statistics and Clear
+
+EXPORT_SYMBOL(aironet4500_RID_Select_General_Config);
+EXPORT_SYMBOL(aironet4500_RID_Select_SSID_list);
+EXPORT_SYMBOL(aironet4500_RID_Select_AP_list);
+EXPORT_SYMBOL(aironet4500_RID_Select_Driver_name);
+EXPORT_SYMBOL(aironet4500_RID_Select_Encapsulation);
+EXPORT_SYMBOL(aironet4500_RID_Select_WEP_volatile);
+EXPORT_SYMBOL(aironet4500_RID_Select_WEP_nonvolatile);
+EXPORT_SYMBOL(aironet4500_RID_Select_Modulation);
+EXPORT_SYMBOL(aironet4500_RID_Select_Active_Config);
+EXPORT_SYMBOL(aironet4500_RID_Select_Capabilities);
+EXPORT_SYMBOL(aironet4500_RID_Select_AP_Info);
+EXPORT_SYMBOL(aironet4500_RID_Select_Radio_Info);
+EXPORT_SYMBOL(aironet4500_RID_Select_Status);
+EXPORT_SYMBOL(aironet4500_RID_Select_16_stats);
+EXPORT_SYMBOL(aironet4500_RID_Select_16_stats_delta);
+EXPORT_SYMBOL(aironet4500_RID_Select_16_stats_clear);
+EXPORT_SYMBOL(aironet4500_RID_Select_32_stats);
+EXPORT_SYMBOL(aironet4500_RID_Select_32_stats_delta);
+EXPORT_SYMBOL(aironet4500_RID_Select_32_stats_clear);
+
+
+struct awc_rid_dir awc_rids_temp[]={
+ // following MUST be consistent with awc_rids_setup !!!
+ {&aironet4500_RID_Select_General_Config, 0x100 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_SSID_list, 0x68 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_AP_list, 0x20 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Driver_name, 0x12 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Encapsulation, 0x22 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Active_Config, 0x100 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Capabilities, 0x80 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Status, 0x6c , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_AP_Info, 0x06 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_32_stats, 0x184 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_32_stats_delta, 0x184 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_32_stats_clear, 0x184 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_WEP_volatile, 0x1c , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_WEP_nonvolatile, 0x1c , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_Modulation, 0x04 , NULL, NULL, NULL,0 },
+
+#ifdef AWC_USE_16BIT_STATS
+ {&aironet4500_RID_Select_16_stats, 0xC2 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_16_stats_delta, 0xC2 , NULL, NULL, NULL,0 },
+ {&aironet4500_RID_Select_16_stats_clear, 0xC2 , NULL, NULL, NULL,0 },
+#else
+ {NULL},{NULL},{NULL},
+#endif
+
+ {0}
+
+
+};
+
+
+
+int
+awc_readrid(struct NET_DEVICE * dev, struct aironet4500_RID * rid, void *pBuf ){
+ struct awc_command cmd;
+
+ int sleep_state ;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_readrid ");
+ if (!rid) return -1;
+ if (!rid->selector) return -1;
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x21, rid->selector->selector,
+ rid->selector->selector, rid->offset, (rid->bits / 8),pBuf);
+
+ sleep_state = cmd.priv->sleeping_bap ;
+ cmd.priv->sleeping_bap = 1;
+ udelay(500);
+ if (awc_issue_command_and_block(&cmd)) goto final;
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+ udelay(1);
+ if (awc_bap_setup(&cmd)) goto final;
+ udelay(1);
+ if (awc_bap_read(&cmd)) goto final;
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_BAP_UNLOCK(cmd);
+
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+int
+awc_writerid(struct NET_DEVICE * dev, struct aironet4500_RID * rid, void *pBuf){
+
+ struct awc_command cmd;
+ int sleep_state ;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_writerid ");
+
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x21, rid->selector->selector,
+ rid->selector->selector,rid->offset, rid->bits/8,pBuf);
+
+ sleep_state = cmd.priv->sleeping_bap ;
+ cmd.priv->sleeping_bap = 1;
+
+ udelay(500);
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+ if (awc_issue_command_and_block(&cmd)) goto final;
+ udelay(10);
+ if (awc_bap_setup(&cmd)) goto final;
+ udelay(10);
+ if (awc_bap_write(&cmd)) goto final;
+ udelay(10);
+ cmd.command=0x121;
+ if (awc_issue_command_and_block(&cmd)) goto final;
+ AWC_BAP_UNLOCK(cmd);
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+int
+awc_readrid_dir(struct NET_DEVICE * dev, struct awc_rid_dir * rid ){
+ struct awc_command cmd;
+ int sleep_state;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awcreadrid_dir ");
+
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x21, rid->selector->selector,
+ rid->selector->selector,0, rid->bufflen,rid->buff);
+
+ sleep_state = cmd.priv->sleeping_bap ;
+ cmd.priv->sleeping_bap = 1;
+
+ udelay(500);
+ if (awc_issue_command_and_block(&cmd)) goto final;
+
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_read(&cmd)) goto final;
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_BAP_UNLOCK(cmd);
+
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+int
+awc_writerid_dir(struct NET_DEVICE * dev, struct awc_rid_dir * rid){
+
+ struct awc_command cmd;
+ int sleep_state ;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_writerid_dir ");
+
+
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x21, rid->selector->selector,
+ rid->selector->selector,0, rid->bufflen,((char *)rid->buff));
+
+ sleep_state = cmd.priv->sleeping_bap ;
+ cmd.priv->sleeping_bap = 1;
+
+ udelay(500);
+ if (awc_issue_command_and_block(&cmd)) goto final;
+
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_write(&cmd)) goto final;
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_BAP_UNLOCK(cmd);
+
+ cmd.command=0x121;
+ udelay(500);
+ if (awc_issue_command_and_block(&cmd)) goto final;
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ cmd.priv->sleeping_bap = sleep_state;
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+EXPORT_SYMBOL(awc_readrid);
+EXPORT_SYMBOL(awc_writerid);
+EXPORT_SYMBOL(awc_readrid_dir);
+EXPORT_SYMBOL(awc_writerid_dir);
+
+/***************************** STARTUP *******************/
+
+
+inline
+int
+awc_issue_blocking_command(struct NET_DEVICE * dev,u16 comm){
+
+ struct awc_command cmd;
+// struct awc_private * priv = (struct awc_private *)dev->priv;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_issue_blocking_command ");
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,comm,0, 0, 0, 0 ,0 );
+
+ if (awc_issue_command_and_block(&cmd))
+ goto final;
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+
+};
+
+int
+awc_issue_soft_reset(struct NET_DEVICE * dev){
+
+ u16 status ;
+// int i= 0;
+
+/* outw(inw(dev->base_addr + 0x30), dev->base_addr + 0x32);
+ udelay(10);
+ outw(inw(dev->base_addr + 0x30), dev->base_addr + 0x34);
+
+ for (i=0; i< 32; i++)
+ outw(0,dev->base_addr + i*2);
+ udelay(100);
+ outw(0x6,dev->base_addr + 0x34);
+ udelay(100);
+ outw(0x6,dev->base_addr + 0x34);
+ outw(0x6,dev->base_addr + 0x34);
+ WAIT61x3;
+ AWC_IN(dev->base_addr + 0x26);
+ AWC_OUT(dev->base_addr + 0x26, 0);
+ WAIT61x3;
+ udelay(60);
+
+
+ outw(0x4, dev->base_addr);
+ udelay(1000);
+ WAIT61x3;
+ AWC_IN(dev->base_addr + 0x26);
+ AWC_OUT(dev->base_addr + 0x26, 0);
+ WAIT61x3;
+ udelay(60);
+*/
+
+ status = awc_issue_command_no_ack(dev, AWC_COMMAND_SOFT_RESET,0,0,0);
+
+// awc_command_busy_clear_wait(dev);
+
+ return status;
+};
+
+int
+awc_issue_noop(struct NET_DEVICE * dev){
+ int retval;
+ AWC_OUT(dev->base_addr + 0x28, 0);
+ AWC_OUT(dev->base_addr + 0x2A, 0);
+ udelay(1000);
+ retval= awc_issue_blocking_command(dev, AWC_COMMAND_NOOP);
+ udelay(1000);
+ return retval;
+};
+
+EXPORT_SYMBOL(awc_enable_MAC);
+
+int
+awc_enable_MAC(struct NET_DEVICE * dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_enable_MAC ");
+
+ if (priv->mac_enabled){
+
+ AWC_ENTRY_EXIT_DEBUG(" mac already enabled exit \n");
+ return 0;
+ }
+ udelay(500);
+ if (awc_issue_blocking_command(dev, AWC_COMMAND_ENABLE)){
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+ }
+ udelay(500);
+
+ priv->mac_enabled = 1;
+
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+};
+
+EXPORT_SYMBOL(awc_disable_MAC);
+int
+awc_disable_MAC(struct NET_DEVICE * dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_disable_MAC ");
+
+ if (!priv->mac_enabled){
+ AWC_ENTRY_EXIT_DEBUG(" mac allready disabled exit \n");
+ return 0;
+ }
+ udelay(1000);
+ if (awc_issue_blocking_command(dev, AWC_COMMAND_DISABLE)){
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+ }
+ udelay(1000);
+ priv->mac_enabled = 0;
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+};
+
+
+
+int
+awc_read_all_rids(struct NET_DEVICE * dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ int status,i;
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_read_all_rids ");
+
+ for (i=0; i< AWC_NOF_RIDS && priv->rid_dir[i].selector ; i++){
+ status = awc_readrid_dir(dev,&priv->rid_dir[i]);
+ udelay(50);
+ if (status) return status;
+
+ }
+ priv->rids_read = 1;
+
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+}
+
+int
+awc_write_all_rids(struct NET_DEVICE * dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ int i,status ;
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_write_all_rids ");
+
+ for (i=0;i < 5 && i< AWC_NOF_RIDS && priv->rid_dir[i].selector ; i++){
+ status = awc_writerid_dir(dev,&priv->rid_dir[i]);
+ udelay(10);
+ if(status) return status;
+ }
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+}
+
+/************************** FID QUEUES ****************************/
+/**************************** TX ALLOC / DEALLOC ***************/
+
+
+
+int awc_tx_alloc(struct NET_DEVICE * dev) {
+
+ struct awc_command cmd;
+ int k=0;
+ int tot=0;
+ struct awc_fid * fid = NULL;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_tx_alloc ");
+
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x0A,0, 0,0,0,NULL);
+ cmd.par0 = dev->mtu + AWC_TX_HEAD_SIZE + 8 ;
+
+ DEBUG(32,"about to allocate %x bytes ",cmd.priv->large_buff_mem);
+ DEBUG(32,"in %x large buffers ",cmd.priv->large_buff_mem / (dev->mtu + AWC_TX_HEAD_SIZE + 8) );
+
+ k=0;tot=0;
+ while (k < cmd.priv->large_buff_mem / (dev->mtu + AWC_TX_HEAD_SIZE + 8) ) {
+
+ fid = kmalloc(sizeof(struct awc_fid),GFP_KERNEL );
+ if (!fid) goto final;
+ memset(fid, 0, sizeof(struct awc_fid));
+
+ if (awc_issue_command_and_block(&cmd)) goto final;
+
+ while ( awc_event_status_Alloc(cmd.port) == 0) ;
+ fid->u.tx.fid = awc_Tx_Allocated_Fid(cmd.port);
+ fid->u.tx.fid_size = dev->mtu + AWC_TX_HEAD_SIZE ;
+
+ DEBUG(32,"allocated large tx fid %x ",fid->u.tx.fid);
+ if(fid->u.tx.fid == 0
+ || cmd.status != 0xA){
+ printk(KERN_ERR "%s bad tx_alloc\n",dev->name);
+ fid->busy =1;
+ goto final;
+ } else {
+ fid->busy =0;
+ tot++;
+ }
+ awc_event_ack_Alloc(cmd.port);
+
+ // shoudlnt goto final after that
+ awc_fid_queue_push_tail(&cmd.priv->tx_large_ready,fid);
+
+ k++;
+ }
+ cmd.priv->tx_buffs_total = tot;
+ DEBUG(32,"allocated %d large tx buffs\n",tot);
+
+ cmd.par0 = AWC_TX_ALLOC_SMALL_SIZE ;
+ k =0; tot = 0;
+
+ while (k < cmd.priv->small_buff_no) {
+
+ fid = kmalloc(sizeof(struct awc_fid),GFP_KERNEL );
+ if (!fid) goto final;
+ memset(fid, 0, sizeof(struct awc_fid));
+
+ cmd.par0 = AWC_TX_ALLOC_SMALL_SIZE ;
+
+ if (awc_issue_command_and_block(&cmd)) goto final;
+
+ while ( awc_event_status_Alloc(cmd.port) == 0) ;
+ fid->u.tx.fid = awc_Tx_Allocated_Fid(cmd.port);
+ fid->u.tx.fid_size = AWC_TX_ALLOC_SMALL_SIZE;
+
+ DEBUG(32,"allocated large tx fid %x ",fid->u.tx.fid);
+ if(fid->u.tx.fid == 0
+ || cmd.status != 0xA){
+ printk(KERN_ERR "%s bad tx_alloc\n",dev->name);
+ fid->busy =1;
+ goto final;
+ } else {
+ fid->busy =0;
+ tot++;
+ }
+ awc_event_ack_Alloc(cmd.port);
+
+ // shoudlnt goto final after that
+ awc_fid_queue_push_tail(&cmd.priv->tx_small_ready,fid);
+
+ k++;
+ }
+
+ cmd.priv->tx_small_buffs_total = tot;
+ DEBUG(32,"allocated %d small tx buffs\n",tot);
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+
+ final:
+ if (fid )
+ kfree(fid);
+ printk(KERN_CRIT "%s awc tx prealloc failed \n",dev->name);
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+
+};
+
+int
+awc_tx_dealloc_fid(struct NET_DEVICE * dev,struct awc_fid * fid){
+
+ struct awc_command cmd;
+ int fid_handle = 0;
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0x0C,0, 0,0,0,NULL);
+
+ if (fid->u.tx.fid){
+ fid_handle = cmd.par0 = fid->u.tx.fid;
+ fid->u.tx.fid = 0;
+ fid->busy =0;
+ kfree(fid);
+
+ if (!cmd.priv->ejected)
+ if (awc_issue_command_and_block(&cmd)) goto final;
+ //awc_event_ack_Alloc(cmd.port);
+ }
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+
+ final:
+ printk(KERN_ERR "awc_tx_dealloc failed for fid %x \n",fid_handle);
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+
+
+};
+
+int
+awc_tx_dealloc(struct NET_DEVICE * dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+
+
+
+// int k=0;
+ struct awc_fid * fid;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_tx_dealloc ");
+
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->tx_large_ready)))
+ awc_tx_dealloc_fid(dev,fid);
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->tx_small_ready)))
+ awc_tx_dealloc_fid(dev,fid);
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->tx_post_process)))
+ awc_tx_dealloc_fid(dev,fid);
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->tx_in_transmit)))
+ awc_tx_dealloc_fid(dev,fid);
+
+ return 0;
+
+};
+
+
+
+inline struct awc_fid *
+awc_tx_fid_lookup_and_remove(struct NET_DEVICE * dev, u16 fid_handle){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+// int k = 0;
+ unsigned long flags;
+ struct awc_fid * fid = NULL;
+ int cnt=0;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_tx_fid_lookup ");
+
+ my_spin_lock_irqsave(&(priv->queues_lock),flags);
+
+
+ fid = priv->tx_in_transmit.head;
+ cnt = 0;
+ while (fid){
+ if (fid->u.tx.fid == fid_handle){
+ awc_fid_queue_remove(&priv->tx_in_transmit, fid);
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return fid;
+ }
+ fid = fid->next;
+ // printk("iT\n");
+ if (cnt++ > 200) {
+ // printk("bbb in awc_fid_queue\n");
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return 0;
+ };
+ };
+
+ cnt=0;
+ fid = priv->tx_post_process.head;
+ while (fid){
+ if (fid->u.tx.fid == fid_handle){
+ awc_fid_queue_remove(&priv->tx_post_process, fid);
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return fid;
+ }
+ fid = fid->next;
+ // printk("pp\n");
+ if (cnt++ > 200) {
+ // printk("bbb in awc_fid_queue\n");
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return 0;
+ };
+
+ };
+
+ cnt=0;
+ fid = priv->tx_large_ready.head;
+ while (fid){
+ if (fid->u.tx.fid == fid_handle){
+ awc_fid_queue_remove(&priv->tx_large_ready, fid);
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return fid;
+ }
+ fid = fid->next;
+ // printk("lr\n");
+ if (cnt++ > 200) {
+ // printk("bbb in awc_fid_queue\n");
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return 0;
+ };
+
+ };
+ cnt=0;
+ fid = priv->tx_small_ready.head;
+ while (fid){
+ if (fid->u.tx.fid == fid_handle){
+ awc_fid_queue_remove(&priv->tx_small_ready, fid);
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return fid;
+ }
+ fid = fid->next;
+ // printk("sr\n");
+ if (cnt++ > 200) {
+ // printk("bbb in awc_fid_queue\n");
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+ return 0;
+ };
+
+ };
+
+ my_spin_unlock_irqrestore(&(priv->queues_lock),flags);
+
+ printk(KERN_ERR "%s tx fid %x not found \n",dev->name, fid_handle);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return NULL;
+}
+
+
+
+
+
+int
+awc_queues_init(struct NET_DEVICE * dev){
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ struct awc_fid * fid = NULL;
+ int retv =0;
+ int k = 0;
+
+ awc_fid_queue_init(&priv->tx_in_transmit);
+ awc_fid_queue_init(&priv->tx_post_process);
+ awc_fid_queue_init(&priv->tx_large_ready);
+ awc_fid_queue_init(&priv->tx_small_ready);
+ awc_fid_queue_init(&priv->rx_ready);
+ awc_fid_queue_init(&priv->rx_post_process);
+
+ retv = awc_tx_alloc(dev);
+
+ k = 0;
+ while (k < AWC_RX_BUFFS){
+ fid = kmalloc(sizeof(struct awc_fid),GFP_KERNEL);
+ if (!fid) return -1;
+ awc_fid_queue_push_tail(&priv->rx_ready,fid);
+ k++;
+ };
+
+ if (retv) return retv;
+
+ return 0;
+};
+
+
+int
+awc_queues_destroy(struct NET_DEVICE * dev){
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ struct awc_fid * fid = NULL;
+ int retv =0;
+
+
+
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->rx_ready))){
+ kfree(fid);
+ }
+ while (NULL != (fid = awc_fid_queue_pop_head(&priv->rx_post_process))){
+ kfree(fid);
+ }
+
+ retv = awc_tx_dealloc(dev);
+
+ return retv;
+};
+
+
+
+/****************************** 802.11router ******************/
+inline int
+awc_802_11_copy_path_skb(struct NET_DEVICE * dev, struct awc_fid * rx_buff){
+
+ struct awc_private * priv = (struct awc_private * )dev->priv;
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_copy_path_skb");
+
+ if (rx_buff->pkt_len < 22 ) rx_buff->pkt_len = 22;
+
+// if (!rx_buff->skb)
+ rx_buff->skb = dev_alloc_skb(rx_buff->pkt_len + 12 +2);
+
+
+ if (rx_buff->skb == NULL) {
+ printk(KERN_CRIT "couldnt alloc rx_buff->skb in rx event \n");
+ priv->stats.rx_dropped++;
+ return -1;
+ }
+ rx_buff->type |= p80211copy_path_skb;
+
+ rx_buff->skb->dev = dev;
+
+// skb_reserve(rx_buff->skb, rx_buff->pkt_len + 12 );
+
+ rx_buff->u.rx.payload = skb_put(rx_buff->skb, rx_buff->pkt_len + 12 ) ;
+ rx_buff->u.rx.payload = ((char *)rx_buff->u.rx.payload ) +12;
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+
+ return 0;
+
+
+};
+
+
+int
+awc_802_11_find_copy_path(struct NET_DEVICE * dev, struct awc_fid * rx_buff){
+
+// struct awc_private * priv = (struct awc_private * )dev->priv;
+// u8 is_802_3 = 0;
+// int i = 0;
+
+ rx_buff->type =0;
+
+ return awc_802_11_copy_path_skb(dev,rx_buff);
+};
+
+
+/* called from INTERRUPT context,
+
+ must deliver the packet to where it was meant by
+ awc_802_11_find_copy_path
+
+ SHOULD be efficient and
+ queue the packet if operations take longer
+
+*/
+
+
+int parse_not_8023= 0;
+
+void
+awc_802_11_router_rx(struct NET_DEVICE * dev,struct awc_fid * rx_buff){
+
+ struct awc_private * priv = (struct awc_private * )dev->priv;
+ struct sk_buff * skb = rx_buff->skb;
+ u8 * payload = rx_buff->u.rx.payload;
+// u8 * p802_3_macs_place = payload -12;
+ u16 pkt_len = rx_buff->pkt_len;
+ struct ieee_802_11_802_1H_header * bridge = NULL;
+ struct ieee_802_11_snap_header * snap = NULL;
+ struct ieee_802_11_802_1H_header * bridge_tmp;
+ struct ieee_802_11_snap_header * snap_tmp;
+
+ u16 ptr = 0;
+ u16 len;
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_router_rx");
+
+// if (rx_buff->type & p80211_8023)
+ rx_buff->mac = rx_buff->u.rx.ieee_802_3.dst_mac;
+// else
+// rx_buff->mac = rx_buff->u.rx.ieee_802_11.mac1;
+
+ if ( rx_buff->u.rx.ieee_802_11.frame_control == 0x8 )
+ memcpy(priv->bssid,rx_buff->u.rx.ieee_802_11.mac3,6);
+
+ while ((ptr < pkt_len - 1 ) && payload && parse_not_8023){
+
+ bridge_tmp = (struct ieee_802_11_802_1H_header*) &payload[ptr];
+ snap_tmp = (struct ieee_802_11_snap_header*) &payload[ptr];
+ len = ntohs( *((u16*)&payload[ptr]) );
+
+
+
+ if ( len < 0x5DC) { // not a protocol
+
+ if ( len != pkt_len-2 - ptr){
+ printk(KERN_ERR "%s bad encapsulation lenght %x at pkt offset %x \n",dev->name,len,ptr);
+ goto bad_packet;
+ }
+ DEBUG(1,"parisng packet of size %x\n",len);
+ ptr +=2;
+ continue;
+ }
+
+ DEBUG(1,"parisng packet of proto %x\n",len);
+
+ if (snap_tmp->dsap == 0xaa && snap_tmp->ssap == 0xaa &&
+ pkt_len - ptr > sizeof(struct ieee_802_11_snap_header) ){
+
+ DEBUG(0x200,"%s SNAP ",dev->name);
+ if (snap_tmp->ctrl != 0x03){
+ printk(KERN_ERR "%s unknown snap ctrl %x \n",dev->name,snap_tmp->ctrl);
+ goto bad_packet;
+ };
+ if (snap_tmp->oui[0] == 0 && // LLC RFC1042
+ snap_tmp->oui[1] == 0 &&
+ snap_tmp->oui[2] == 0 ){
+ snap = snap_tmp;
+ ptr += sizeof(struct ieee_802_11_snap_header);
+ DEBUG(0x200,"%s LLC RFC1042 \n",dev->name);
+ continue;
+ }
+ if (snap_tmp->oui[0] == 0 && // LLC 802.1H
+ snap_tmp->oui[1] == 0 &&
+ snap_tmp->oui[2] == 0x78){
+ snap = snap_tmp;
+ DEBUG(0x200,"%s LLC 802.1H \n",dev->name);
+ ptr += sizeof(struct ieee_802_11_snap_header);
+ continue;
+ };
+ if (snap_tmp->oui[0] == 0x00 && // 802.1H itself
+ snap_tmp->oui[1] == 0x40 &&
+ snap_tmp->oui[2] == 0x96){
+ ptr += sizeof(struct ieee_802_11_802_1H_header);
+ if (ptr >= pkt_len){
+ goto bad_packet;
+ DEBUG(1,"%s invalid packet len in 802.1H SNAP OUI check \n",dev->name);
+ }
+ DEBUG(0x200,"%s OUI 004096 \n",dev->name);
+ DEBUG(0x200," 802.1H uknown1 %x ",ntohs(bridge_tmp->unknown1));
+ DEBUG(0x200," 802.1H uknw type %x \n",0xf000 & ntohs(bridge_tmp->unknown2));
+ DEBUG(0x200," 802.1H payloadsize %x \n",0x0fff & ntohs(bridge_tmp->unknown2));
+
+ //goto bad_packet; // TODO
+
+ bridge = bridge_tmp;
+ if (bridge_tmp->unknown1 == 0x0000 &&
+ ((ntohs(bridge_tmp->unknown2) & 0xf000) == 0x1000 ) ){
+ rx_buff->type |= p80211_8021H;
+ rx_buff->mac = &payload[ptr];
+ DEBUG(0x200," 802.1H DATA packet of size %x\n",0xf000 & ntohs(bridge_tmp->unknown2) );
+ memcpy(priv->p2p,rx_buff->u.rx.ieee_802_11.mac2, 6);
+ ptr +=12;
+ continue;
+ };
+ DEBUG(0x200,"%s droping unknown 004096 packet \n ",dev->name);
+ goto bad_packet;
+
+
+ }
+ goto bad_packet;
+ }
+ if ( len > 0x5DC){
+ // packet without linklevel header for us
+
+ if ( len == 0x8000 || len == 0x8006){
+
+ DEBUG(0x200,"Non IP packet %x \n",ntohs(len));
+
+ };
+ goto good_packet;
+
+ };
+
+ goto good_packet;
+ }
+
+ good_packet:
+
+ if (ptr > pkt_len) goto bad_packet;
+
+ if ( rx_buff->mac != (payload + ptr -12) )
+ memcpy( payload +ptr -12, rx_buff->mac , 12);
+
+
+
+ if (!payload || !skb || !rx_buff->skb || !rx_buff->u.rx.payload)
+ return ;
+ //skb->ip_summed = CHECKSUM_NONE;
+ skb->data = payload + ptr -12;
+ skb->len += ptr ;
+
+ rx_buff->skb->protocol = eth_type_trans(rx_buff->skb,dev);
+ DEBUG(0x200,"eth_type_trans decided: %x\n",rx_buff->skb->protocol);
+ rx_buff->skb = NULL;
+ rx_buff->u.rx.payload = NULL;
+ priv->stats.rx_packets++;
+ netif_rx(skb);
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+ return ;
+
+ bad_packet:
+ DEBUG(0x200,"%s packet dropped in packet hdr parse \n ",dev->name);
+ if (rx_buff->skb && (rx_buff->type & p80211copy_path_skb)){
+
+ FREE_SKB(rx_buff->skb);
+ rx_buff->skb = NULL;
+ rx_buff->u.rx.payload = NULL;
+ };
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+
+};
+
+void
+awc_802_11_failed_rx_copy(struct NET_DEVICE * dev,struct awc_fid * rx_buff){
+ struct awc_private * priv = (struct awc_private * )dev->priv;
+
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_failed_rx_copy");
+ if (rx_buff->skb)
+ FREE_SKB(rx_buff->skb);
+ rx_buff->skb = NULL;
+ rx_buff->u.rx.payload = NULL;
+ priv->stats.rx_errors++;
+
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+};
+
+/*
+ called from kernel->driver tx routine
+ must decide where and how to post the packet
+ must post the packet to wherever it decides
+ either copy to card or enqueue to destination queue
+
+*/
+
+
+int
+awc_802_11_tx_find_path_and_post(struct NET_DEVICE * dev,
+ struct sk_buff * skb){
+
+
+ struct awc_private * priv = (struct awc_private * )dev->priv;
+ int i;
+ int len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; /* check min length*/
+ struct awc_fid * fid = NULL;
+// u16 saved_fid ;
+ u16 p2p_direct =priv->p2p_found;
+// struct iphdr * ip_hdr;
+ //buffer = skb->data;
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_tx_find_path_and_post");
+
+ DOWN(&priv->tx_buff_semaphore);
+ if (len > dev->mtu + 16 ) {
+ printk(KERN_ERR "%s packet size too large %d \n",dev->name, len);
+ goto final;
+ }
+
+ if (len + AWC_TX_HEAD_SIZE < AWC_TX_ALLOC_SMALL_SIZE )
+ fid = awc_fid_queue_pop_head(&priv->tx_small_ready);
+
+ if (!fid)
+ fid = awc_fid_queue_pop_head(&priv->tx_large_ready);
+
+ if (!fid) {
+ DEBUG(32,"%s buffs in use \n",dev->name);
+ goto no_space;
+ }
+/*
+ if (fid->u.tx.fid_size < len + AWC_TX_HEAD_SIZE){
+ awc_fid_queue_push_tail(&priv->tx_small_ready, fid);
+ fid = awc_fid_queue_pop_head(&priv->tx_large_ready);
+ }
+*/
+ if (!fid) {
+ DEBUG(32,"%s buffs in use \n",dev->name);
+ goto no_space;
+ }
+
+ if (fid->u.tx.fid_size < len + AWC_TX_HEAD_SIZE - 14){
+ printk(KERN_ERR "found too small tx fid size %d, pktlen %d \n",fid->u.tx.fid_size, len);
+ }
+ memset(&fid->u.tx.radio_tx, 0,sizeof(struct aironet4500_radio_tx_header));
+ memset(&fid->u.tx.ieee_802_11, 0,sizeof(struct ieee_802_11_header));
+ memset(&fid->u.tx.ieee_802_3, 0,sizeof(struct ieee_802_3_header));
+ fid->u.tx.payload =NULL;
+ fid->u.tx.gap_length =0;
+ fid->busy = 1;
+
+
+ priv->tx_buffs_in_use++;
+ DEBUG(32,"found large buff %x \n",fid->u.tx.fid);
+
+/*
+ fid->type |= p80211_llc_snap;
+ fid->snap.dsap = 0xaa;
+ fid->snap.ssap = 0xaa;
+ fid->snap.ctrl = 0x03;
+ fid->snap.oui[0] = 0x0;
+ fid->snap.oui[1] = 0x0;
+ fid->snap.oui[2] = 0x0;
+*/
+ fid->skb = skb;
+
+
+ if (priv->p2p_uc && !priv->p2p_found){ // we go without encapsulation to neighbour;
+
+ for (i=0; i < 6; i++)
+ if (priv->p2p[i] != skb->data[i]){
+ p2p_direct = 1;
+ break;
+ }
+ };
+
+ if (tx_rate == 2 || tx_rate == 4 || tx_rate== 20 || tx_rate == 22)
+ fid->u.tx.radio_tx.tx_bit_rate = tx_rate;
+ fid->u.tx.radio_tx.TX_Control =
+ aironet4500_tx_control_tx_ok_event_enable |
+ aironet4500_tx_control_tx_fail_event_enable |
+ aironet4500_tx_control_no_release;
+
+/* if (len < 100){
+ fid->u.tx.radio_tx.TX_Control |=
+ aironet4500_tx_control_use_rts;
+ };
+*/
+/* ip_hdr = skb->data + 14;
+ if (ip_hdr && skb->data[12] == 0x80 ){
+ if (ip_hdr->tos & IPTOS_RELIABILITY)
+ fid->u.tx.radio_tx.TX_Control |=
+ aironet4500_tx_control_use_rts;
+ if (ip_hdr->tos & IPTOS_THROUGHPUT)
+ fid->u.tx.radio_tx.TX_Control |=
+ aironet4500_tx_control_no_retries;
+ };
+*/
+ if (priv->p802_11_send || memcmp(dev->dev_addr, skb->data +6, 6) ){
+ fid->u.tx.radio_tx.TX_Control |=
+ aironet4500_tx_control_header_type_802_11;
+ DEBUG(0x200,"%s bridging, forcing 802_11 send \n ",dev->name);
+ }
+
+
+ if (!priv->p2p_uc || p2p_direct) {
+ if ((fid->u.tx.radio_tx.TX_Control &
+ aironet4500_tx_control_header_type_802_11 )){
+
+ // including 802.3 header into 802.11 packet
+ fid->u.tx.radio_tx.PayloadLength = len -12;
+ fid->u.tx.ieee_802_3.payload_length = len -12 ;
+ fid->pkt_len = len -12;
+ fid->u.tx.payload = skb->data +12;
+
+ if (!memcmp(dev->dev_addr, skb->data +6, 6)){
+ memcpy(fid->u.tx.ieee_802_11.mac1,skb->data,6);
+ memcpy(fid->u.tx.ieee_802_11.mac2,skb->data +6,6);
+ memcpy(fid->u.tx.ieee_802_11.mac3,priv->status.CurrentBssid ,6);
+ memset(fid->u.tx.ieee_802_11.mac4,0,6);
+ fid->u.tx.ieee_802_11.frame_control = 0x8;
+ fid->u.tx.ieee_802_11.gapLen=6;
+ } else {
+
+ memcpy(fid->u.tx.ieee_802_11.mac1,skb->data,6);
+ memcpy(fid->u.tx.ieee_802_11.mac2,dev->dev_addr,6);
+ memcpy(fid->u.tx.ieee_802_11.mac3,skb->data +6 ,6);
+ memset(fid->u.tx.ieee_802_11.mac4,0 ,6);
+ fid->u.tx.ieee_802_11.frame_control = 0x108;
+ fid->u.tx.ieee_802_11.gapLen=6;
+
+ }
+ } else { // plain old 802.3, with hdr copied
+ fid->u.tx.radio_tx.PayloadLength = len -12;
+ fid->u.tx.ieee_802_3.payload_length = len -12;
+ fid->pkt_len = len - 12;
+ fid->u.tx.payload = skb->data +12;
+ };
+ memcpy(fid->u.tx.ieee_802_3.dst_mac,skb->data, 12);
+ DEBUG(0x200,"%s tx simply 802.3 type \n ",dev->name);
+
+ } else {// 802.1H bridgeing
+ fid->type |= p80211_8021H;
+ fid->bridge_size = len + sizeof(fid->bridge) ;
+ fid->bridge.dsap = 0xaa;
+ fid->bridge.ssap = 0xaa;
+ fid->bridge.ctrl = 0x03;
+ fid->bridge.oui[0] = 0x0;
+ fid->bridge.oui[1] = 0x40;
+ fid->bridge.oui[2] = 0x96;
+ fid->bridge.unknown1= 0x0000;
+ fid->bridge.unknown2= htons((len) & 0x1000);
+ fid->u.tx.radio_tx.PayloadLength = fid->bridge_size + 2;
+ fid->u.tx.ieee_802_3.payload_length = fid->u.tx.radio_tx.PayloadLength ;
+
+
+ fid->u.tx.payload = skb->data +12;
+ if ((fid->u.tx.radio_tx.TX_Control &
+ aironet4500_tx_control_header_type_802_11 )){
+
+ memcpy(fid->u.tx.ieee_802_11.mac1,priv->p2p,6);
+ memcpy(fid->u.tx.ieee_802_11.mac2,skb->data +6,6);
+ memcpy(fid->u.tx.ieee_802_11.mac3,priv->bssid ,6);
+ memset(fid->u.tx.ieee_802_11.mac4,0,6);
+ fid->u.tx.ieee_802_11.gapLen=6;
+
+ fid->u.tx.ieee_802_11.frame_control = 0x8;
+ }
+ memcpy(fid->u.tx.ieee_802_3.dst_mac,priv->p2p, 6);
+ memcpy(fid->u.tx.ieee_802_3.src_mac,dev->dev_addr, 6);
+ fid->u.tx.payload = skb->data + 2 + sizeof(fid->bridge);
+ fid->pkt_len = len ;
+
+ DEBUG(0x200,"%s tx simply 802.1H type \n ",dev->name);
+
+ };
+
+
+
+ awc_fid_queue_push_tail(&priv->tx_in_transmit,fid);
+ udelay(1);
+ awc_transmit_packet(dev,fid);
+ UP(&priv->tx_buff_semaphore);
+ if (priv->tx_large_ready.size > 0 ){
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+ return 0;
+
+
+ no_space:
+ DEBUG(32,"%s tx buffs not found \n ",dev->name);
+ #ifdef AWC_DEBUG
+// printk("s");
+ #endif
+ dev->tbusy = 1; //weell, here it must be set anyway and before
+ //priv->stats.tx_fifo_errors++;
+ UP(&priv->tx_buff_semaphore);
+ AWC_ENTRY_EXIT_DEBUG("NoSpaceExit\n");
+ return 1 ;
+ final:
+ priv->stats.tx_errors++;
+ UP(&priv->tx_buff_semaphore);
+ dev->tbusy = 0;
+ FREE_SKB(skb);
+ mark_bh(NET_BH);
+ AWC_ENTRY_EXIT_DEBUG("BADExit\n");
+ return -1;
+
+};
+
+/*
+ called from low level driver->card tx copy routine
+ probably wants to free skbuf if failed transmits won't be
+ resubmitted to another device (if more than one path)
+ or tried again (if tx buffer in card needs to be filled again)
+*/
+
+
+void
+awc_802_11_after_tx_packet_to_card_write(struct NET_DEVICE * dev,
+ struct awc_fid * tx_buff){
+
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_after_tx_packet_to_card_write");
+
+ if (!tx_buff){
+ DEBUG(1,"%s no damn tx_buff in awc_802_11_after_tx_packet_to_card_write \n",dev->name);
+ };
+
+ if(tx_buff->skb){
+ FREE_SKB(tx_buff->skb);
+ tx_buff->skb = NULL;
+ }
+ mark_bh(NET_BH);
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+};
+
+/*
+ called from low level driver->card tx copy routine
+ probably wants to free skbuf if failed writes won't be
+ resubmitted to another device (if more than one path)
+ or tried again (if tx buffer in card needs to be filled again)
+*/
+
+void
+awc_802_11_after_failed_tx_packet_to_card_write(struct NET_DEVICE * dev,
+ struct awc_fid * tx_buff){
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_after_failed_tx_packet_to_card_write");
+
+ if (!tx_buff){
+ DEBUG(1,"%s no damn tx_buff in awc_802_11_after_failed_tx_packet_to_card_write \n",dev->name);
+ };
+
+ if(tx_buff->skb){
+ FREE_SKB(tx_buff->skb);
+ tx_buff->skb = NULL;
+ tx_buff->busy =0;
+ printk(KERN_ERR "%s packet to card write failed \n",dev->name);
+ }
+
+ awc_fid_queue_remove(&priv->tx_in_transmit,tx_buff);
+
+ if (tx_buff->u.tx.fid_size <= AWC_TX_ALLOC_SMALL_SIZE)
+ awc_fid_queue_push_tail(&priv->tx_small_ready,tx_buff);
+ else
+ awc_fid_queue_push_tail(&priv->tx_large_ready,tx_buff);
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+
+};
+
+void
+awc_802_11_after_tx_complete(struct NET_DEVICE * dev, struct awc_fid * tx_buff){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+
+ AWC_ENTRY_EXIT_DEBUG("awc_802_11_after_tx_complete");
+
+ DEBUG(32,"tx complete status %x \n ",tx_buff->u.tx.radio_tx.Status);
+
+ #ifdef AWC_DEBUG
+ if (tx_buff->u.tx.radio_tx.Status)
+ printk("tf%x ",tx_buff->u.tx.radio_tx.Status);
+ #endif
+ if (tx_buff->u.tx.fid_size <= AWC_TX_ALLOC_SMALL_SIZE){
+ awc_fid_queue_push_tail(&priv->tx_small_ready,tx_buff);
+ priv->tx_small_buffs_in_use--;
+ } else {
+ awc_fid_queue_push_tail(&priv->tx_large_ready,tx_buff);
+ priv->tx_buffs_in_use--;
+ }
+
+ tx_buff->busy = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+
+ AWC_ENTRY_EXIT_DEBUG("exit\n");
+};
+
+
+
+
+/******************************** R X ***********************/
+
+
+
+inline int
+awc_receive_packet(struct NET_DEVICE * dev){
+
+ struct awc_command cmd;
+ u16 Fid;
+// struct sk_buff *skb = NULL;
+ struct awc_fid * rx_buff;
+
+
+ struct awc_private * priv ;
+ int i;
+
+ priv= (struct awc_private *)dev->priv;
+ rx_buff = priv->rx_ready.head ;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_receive_packet ");
+
+ Fid = awc_Rx_Fid(dev->base_addr);
+
+ DEBUG(128," RX FID %x \n",Fid);
+
+ if (!Fid){
+ printk(KERN_CRIT "No RxFid when rx event \n");
+ return -1;
+ }
+
+
+
+ if (!rx_buff){
+ printk(KERN_CRIT "No rx_buff in rx event \n");
+ return -1;
+ }
+
+ rx_buff->type = 0;
+
+
+ AWC_INIT_COMMAND(AWC_CLI,cmd,dev,0,0,
+ Fid, 0, 0x14 , &(rx_buff->u.rx.radio_rx));
+
+
+// header reading , order is important
+ AWC_BAP_LOCK_UNDER_CLI(cmd);
+
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_read(&cmd)) goto final;
+
+ DEBUG(128, "rx receive radio header, length %x \n",rx_buff->u.rx.radio_rx.PayloadLength);
+
+ cmd.buff = &(rx_buff->u.rx.ieee_802_11);
+ cmd.len = 0x20;
+
+ if (awc_bap_read(&cmd)) goto final;
+
+ DEBUG(128, "rx receive 802_11 header, framecontrol %x \n",rx_buff->u.rx.ieee_802_11.frame_control);
+
+ if (rx_buff->u.rx.ieee_802_11.gapLen > 8) {
+ printk(KERN_ERR "%s: 802.11 gap lenght huge %d \n",dev->name,rx_buff->u.rx.ieee_802_11.gapLen);
+ goto final;
+ }
+ DEBUG(128,"SeqCtl %x, 802_11 macs: ",rx_buff->u.rx.ieee_802_11.SeqCtl);
+ if (awc_debug & 0x7000){
+ DEBUG(0x7000, " %s mac1 ",dev->name); for (i = 0; i < 6; i++) DEBUG(0x7000, "%02x:",((unsigned char)rx_buff->u.rx.ieee_802_11.mac1[i] )) ;
+ DEBUG(0x7000, " %s mac2 ",dev->name); for (i = 0; i < 6; i++) DEBUG(0x7000, "%02x:",((unsigned char)rx_buff->u.rx.ieee_802_11.mac2[i] )) ;
+ DEBUG(0x7000, " %s mac3 ",dev->name); for (i = 0; i < 6; i++) DEBUG(0x7000, "%02x:",((unsigned char)rx_buff->u.rx.ieee_802_11.mac3[i] )) ;
+ DEBUG(0x7000, " %s mac4 ",dev->name); for (i = 0; i < 6; i++) DEBUG(0x7000, "%02x:",((unsigned char)rx_buff->u.rx.ieee_802_11.mac4[i] )) ;
+ }
+ DEBUG(128,"\n GapLen %d ",rx_buff->u.rx.ieee_802_11.gapLen );
+
+ if (rx_buff->u.rx.ieee_802_11.gapLen > 0) {
+ cmd.buff = rx_buff->u.rx.ieee_802_11.gap;
+ cmd.len = rx_buff->u.rx.ieee_802_11.gapLen;
+ if (awc_bap_read(&cmd)) goto final;
+ DEBUG(128, "rx receive gap header , gap length %x \n",rx_buff->u.rx.gap_length);
+ }
+ for (i = 0; i < rx_buff->u.rx.ieee_802_11.gapLen ; i++) DEBUG(128,"%x",((unsigned char)rx_buff->u.rx.ieee_802_11.gap[i] )) ;
+
+
+ if ( !(priv->config.ReceiveMode & RXMODE_DISABLE_802_3_HEADER )
+ ){
+ cmd.buff = &(rx_buff->u.rx.ieee_802_3);
+ cmd.len = 0x10;
+ rx_buff->type |= p80211_8023;
+ if (awc_bap_read(&cmd)) goto final;
+ DEBUG(128, "rx receive 802_3 header, payload length %x \n",rx_buff->u.rx.ieee_802_3.payload_length);
+ DEBUG(128,"\n 802_3 status %x ",rx_buff->u.rx.ieee_802_3.status );
+ DEBUG(128," RX payloadLen %x, dst,src: ",rx_buff->u.rx.ieee_802_3.payload_length);
+ if (awc_debug & 0x7000){
+ for (i = 0; i < 6; i++) printk("%02x:",((unsigned char)rx_buff->u.rx.ieee_802_3.dst_mac[i] )) ;
+ for (i = 0; i < 6; i++) printk("%02x:",((unsigned char)rx_buff->u.rx.ieee_802_3.src_mac[i] )) ;
+ }
+ };
+
+ rx_buff->pkt_len = rx_buff->u.rx.radio_rx.PayloadLength;
+
+ if (priv->config.OperatingMode & MODE_LLC_HOST)
+ rx_buff->type |= p80211_llc_snap;
+
+
+ if (awc_802_11_find_copy_path(dev,rx_buff)) goto final;
+
+
+ if (rx_buff->u.rx.payload ){
+ cmd.buff = rx_buff->u.rx.payload;
+ cmd.len = rx_buff->pkt_len;
+ if (awc_bap_read(&cmd)) goto final;
+ DEBUG(128, "rx payload read %x \n",rx_buff->u.rx.ieee_802_3.payload_length);
+ };
+
+ AWC_BAP_UNLOCK(cmd);
+
+ DEBUG(128,"\n payload hdr %x ",rx_buff->u.rx.ieee_802_3.status );
+ if (awc_debug && rx_buff->u.rx.payload)
+ for (i = 0; i < 20; i++) DEBUG(128,"%x",((unsigned char)rx_buff->u.rx.payload[i] )) ;
+ DEBUG(128,"%c",'\n');
+
+ awc_802_11_router_rx(dev,rx_buff);
+
+ AWC_RELEASE_COMMAND(cmd);
+// awc_event_ack_Rx(dev->base_addr);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+
+ awc_802_11_failed_rx_copy(dev,rx_buff);
+ // if (skb) dev_kfree_skb(skb, FREE_WRITE);
+ AWC_RELEASE_COMMAND(cmd);
+// awc_event_ack_Rx(dev->base_addr);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+
+};
+
+
+int
+awc_transmit_packet(struct NET_DEVICE * dev, struct awc_fid * tx_buff) {
+
+ struct awc_command cmd;
+ u16 size ;
+// unsigned long flags;
+ int i;
+ struct awc_private * priv= (struct awc_private *)dev->priv;
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_transmit_packet ");
+
+ if (priv->link_status_changed ){
+ priv->link_status_changed =0;
+ awc_readrid_dir(dev,&priv->rid_dir[7]);
+ }
+
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0xB, tx_buff->u.tx.fid,
+ tx_buff->u.tx.fid, 0, 0x14 , &(tx_buff->u.tx.radio_tx));
+
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+
+#ifdef AWC_BY_BOOK
+#warning By books is bad, AWC_BY_BOOK
+#error cli sti bad here
+ if ( !(tx_buff->type &(p80211_llc_snap|p80211_8021H) )
+ && !(tx_buff->u.tx.radio_tx.TX_Control &
+ aironet4500_tx_control_header_type_802_11 )){
+
+ cmd.buff=&(tx_buff->u.tx.radio_tx.TX_Control);
+ cmd.len = 0x2 ;
+ cmd.offset = 0x8;
+ save_flags(flags);
+ cli();
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_write(&cmd)) goto final;
+
+ cmd.buff=&(tx_buff->u.tx.ieee_802_3.payload_length);
+ cmd.len = 14;
+ cmd.offset = 0x36;
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_write(&cmd)) goto final;
+ restore_flags(flags);
+
+ } else {
+#endif
+
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_write(&cmd)) goto final;
+
+ DEBUG(64," wrote radio tx header for fid %x \n",tx_buff->u.tx.fid);
+
+ // 802.11
+ cmd.buff=&(tx_buff->u.tx.ieee_802_11);
+ cmd.len = 0x20;
+ if (awc_bap_write(&cmd)) goto final;
+
+ // Gap
+ if (tx_buff->u.tx.ieee_802_11.gapLen) {
+ cmd.buff=&(tx_buff->u.tx.ieee_802_11.gap);
+ cmd.len = tx_buff->u.tx.ieee_802_11.gapLen;
+ if (awc_bap_write(&cmd)) goto final;
+ }
+ // 802.3
+ if ( ! (tx_buff->u.tx.radio_tx.TX_Control &
+ aironet4500_tx_control_header_type_802_11 )){
+
+ cmd.buff=&(tx_buff->u.tx.ieee_802_3);
+ if (awc_debug & 0x7000){
+ printk("%s TX dst ",dev->name);
+ for (i=0; i < 6; i++) printk ("%02x:",(unsigned char) tx_buff->u.tx.ieee_802_3.dst_mac[i]);
+ printk(" src ");
+ for (i=0; i < 6; i++) printk ("%02x:",(unsigned char) tx_buff->u.tx.ieee_802_3.src_mac[i]);
+ printk(" \n ");
+ }
+ cmd.len = 0x10;
+ if (awc_bap_write(&cmd)) goto final;
+ };
+
+ if (tx_buff->type & p80211_llc_snap) {
+ cmd.buff= & tx_buff->snap;
+ cmd.len = sizeof(tx_buff->snap);
+ if (awc_bap_write(&cmd)) goto final;
+ };
+
+ if (tx_buff->type & p80211_8021H) {
+ size = htons(tx_buff->bridge_size);
+ // size = tx_buff->bridge_size;// to seasure raw speed of f** UC
+ cmd.buff= & size;
+ cmd.len = 2 ;
+ if (awc_bap_write(&cmd)) goto final;
+
+ cmd.buff= & tx_buff->bridge;
+ cmd.len = sizeof(tx_buff->bridge);
+ if (awc_bap_write(&cmd)) goto final;
+ };
+
+#ifdef AWC_BY_BOOK
+
+ }
+#endif
+ cmd.buff= tx_buff->u.tx.payload;
+ cmd.len = tx_buff->pkt_len;
+
+ if (awc_bap_write(&cmd)) goto final;
+
+
+ AWC_BAP_UNLOCK(cmd);
+
+ if (awc_issue_command_and_block(&cmd)) goto final;
+// if (awc_issue_command(&cmd)) goto final;
+ tx_buff->transmit_start_time = jiffies;
+ awc_802_11_after_tx_packet_to_card_write(dev,tx_buff);
+ // issue the transmit command
+
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+ final:
+ awc_802_11_after_failed_tx_packet_to_card_write(dev,tx_buff);
+
+ printk(KERN_CRIT "%s awc tx command failed \n",dev->name);
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+
+inline int
+awc_tx_complete_check(struct NET_DEVICE * dev){
+
+ struct awc_fid * fid;
+ struct awc_command cmd;
+
+
+ AWC_ENTRY_EXIT_DEBUG(" entry awc_tx_complete_check ");
+
+
+
+ fid = awc_fid_queue_pop_head(&((struct awc_private *)dev->priv)->tx_post_process);
+
+ if (!fid) {
+ printk("awc_tx_complete_check with empty queue \n ");
+ return -1;
+ }
+
+ DEBUG(64," tx_complete fid %x \n",fid->u.tx.fid);
+
+ AWC_INIT_COMMAND(AWC_NOT_CLI,cmd,dev,0,0, fid->u.tx.fid,
+ 0, 0x14 , &(fid->u.tx.radio_tx));
+
+ fid->state |= awc_tx_fid_complete_read;
+
+ AWC_BAP_LOCK_NOT_CLI(cmd);
+ if (awc_bap_setup(&cmd)) goto final;
+ if (awc_bap_read(&cmd)) goto final;
+ AWC_BAP_UNLOCK(cmd);
+
+ awc_802_11_after_tx_complete(dev,fid);
+
+
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+ return 0;
+
+ final:
+ awc_802_11_after_tx_complete(dev,fid);
+ printk(KERN_ERR "%s awc_tx_complete_check failed \n",dev->name);
+ AWC_RELEASE_COMMAND(cmd);
+ AWC_ENTRY_EXIT_DEBUG(" BAD exit \n");
+ return -1; ;
+}
+
+
+#define AWC_QUEUE_BH {\
+ if (!priv->bh_active && !priv->bh_running){\
+ priv->bh_active = 1;\
+ queue_task(&priv->immediate_bh, &tq_immediate);\
+ mark_bh(IMMEDIATE_BH);\
+ }\
+ }
+
+
+void
+awc_bh(struct NET_DEVICE *dev){
+
+ struct awc_private * priv = (struct awc_private *)dev->priv;
+ int active_interrupts;
+ int enabled_interrupts;
+// u16 tx_status;
+ int multi_ints = 0;
+// u16 tx_fid = 0;
+// unsigned long flags;
+
+ DEBUG(8, "awc_bh awoken on jiffie %ld \n",jiffies);
+
+ priv->bh_running = 1;
+
+ active_interrupts = awc_event_status(dev->base_addr);
+
+ enabled_interrupts = awc_ints_enabled(dev->base_addr);
+
+ DEBUG(8, "awc_bh active ints %x \n",active_interrupts);
+
+ if (test_and_set_bit( 0, (void *) &priv->tx_chain_active) ) {
+// printk(KERN_ERR "tx chain active in bh \n");
+// queue_task(&priv->immediate_bh, &tq_immediate);
+ goto bad_end;
+ }
+start:
+ if (active_interrupts == 0xffff){
+
+ printk(KERN_CRIT "%s device ejected in interrupt, disabling\n",dev->name);
+ dev->tbusy = 1;
+ dev->start = 0;
+ if (priv->command_semaphore_on){
+ priv->command_semaphore_on--;
+ UP(&priv->command_semaphore);
+ }
+ priv->tx_chain_active =0;
+ goto bad_end;
+
+ }
+
+ if (priv->unlock_command_postponed ){
+
+ priv->unlock_command_postponed-- ;
+ if( priv->command_semaphore_on ){
+
+ awc_read_response((&priv->cmd));
+ priv->async_command_start = 0;
+ if (priv->command_semaphore_on){
+
+ priv->command_semaphore_on--;
+ UP(&priv->command_semaphore);
+ }
+ }
+ };
+
+/* if ( active_interrupts & 0x1 ){
+ awc_receive_packet(dev) ;
+ awc_event_ack_Rx(dev->base_addr);
+ priv->waiting_interrupts &= ~0x1;
+ }
+*/
+ while (priv->tx_post_process.size)
+ if (awc_tx_complete_check(dev)) break;
+
+ active_interrupts = awc_event_status(dev->base_addr);
+
+ if (priv->command_semaphore_on || priv->tx_post_process.size){
+ if (multi_ints++ < 10000){
+ goto start;
+ }
+ };
+ priv->bh_active = 0;
+ priv->bh_running = 0;
+
+ priv->tx_chain_active = 0;
+
+
+
+ bad_end:
+// if (!priv->tx_chain_active)
+// wake_up(&priv->tx_chain_wait_queue);
+
+ priv->bh_running = 0;
+ priv->bh_active = 0;
+ return ;
+};
+
+
+inline int
+awc_interrupt_process(struct NET_DEVICE * dev){
+
+ struct awc_private * priv ;
+ int active_interrupts;
+ int enabled_interrupts;
+ u16 tx_status;
+ int multi_ints = 0;
+ u16 tx_fid = 0;
+// u16 ints_to_ack =0;
+ struct awc_fid * fid = NULL;
+// int interrupt_reenter = 0;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+// disable_irq(dev->irq);
+
+ DEBUG(2," entering interrupt handler %s ",dev->name);
+
+ if (!dev) {
+ printk(KERN_ERR "No dev in interrupt \n");
+ goto bad_end;
+ };
+
+ priv = (struct awc_private *)dev->priv;
+
+ if (!priv) {
+ printk(KERN_ERR "No PRIV in interrupt \n");
+ goto bad_end;
+ };
+
+
+ enabled_interrupts = awc_ints_enabled(dev->base_addr);
+ active_interrupts = awc_event_status(dev->base_addr);
+
+ DEBUG(2,"entry: processing interrupts waiting %x \n",priv->waiting_interrupts);
+ DEBUG(2,"entry: processing interrupts active %x \n",active_interrupts);
+ DEBUG(2,"entry: processing interrupts enabled %x \n",enabled_interrupts);
+// printk("ikka interruptis\n");
+
+
+ if (test_and_set_bit( 0, (void *) &dev->interrupt) ) {
+ printk("RI\n");
+ goto reenter_end_here;
+ }
+ priv->interrupt_count++;
+ if (priv->interrupt_count > 1 )
+ printk(" interrupt count on\n ");
+
+
+
+ if (priv->waiting_interrupts & active_interrupts)
+ printk(KERN_ERR "double interrupt waiting %x active %x \n",
+ priv->waiting_interrupts, active_interrupts);
+
+ // priv->waiting_interrupts |= active_interrupts;
+
+
+
+
+
+start:
+ DEBUG(2,"Start processing int, times %d\n",multi_ints);
+
+ if (active_interrupts == 0xffff){
+
+ printk(KERN_CRIT "%s device ejected, got interrupt, disabling\n",dev->name);
+ //priv->
+ dev->tbusy = 1;
+ dev->start = 0;
+ priv->ejected = 1;
+ if (priv->bh_active || priv->bh_running){
+ priv->interrupt_count--;
+ dev->interrupt = 0;
+ goto bad_end;
+ } else if (priv->command_semaphore_on){
+
+ printk(KERN_ERR "ejected, last BH fired \n");
+
+ AWC_QUEUE_BH;
+ }
+ priv->interrupt_count--;
+ dev->interrupt = 0;
+ goto bad_end;
+ }
+
+
+
+ if (active_interrupts & 0x100 ){
+ awc_event_ack_Awaken(dev->base_addr);
+ udelay(10);
+ DEBUG(1,"%s device awoke \n",dev->name);
+ priv->waiting_interrupts &= ~0x100;
+ };
+ if (active_interrupts & 0x80 ){
+
+ priv->link_status = awc_Link_Status(dev->base_addr);
+ DEBUG(1,"link status changed %x \n",priv->link_status);
+ awc_event_ack_Link(dev->base_addr);
+ priv->waiting_interrupts &= ~0x80;
+ if(priv->link_status == 0x400)
+ printk(KERN_INFO "%s Associated\n",dev->name );
+ else {
+ printk(KERN_INFO "%s Link status change : %s \n",dev->name, awc_print_string(awc_link_status_names, priv->link_status) );
+ if ( priv->link_status & 0x8100 ||
+ priv->link_status & 0x0100 ||
+ priv->link_status & 0x8200 ||
+ priv->link_status & 0x8400 ||
+ priv->link_status & 0x0300 )
+ printk(KERN_INFO "%s Link status change reason : %s \n",dev->name, awc_print_string(awc_link_failure_reason_names, priv->link_status & 0xff) );
+
+ }
+ };
+
+
+ if (active_interrupts & 0x10 & enabled_interrupts ){
+
+// printk(KERN_ERR "cmd int shouldnt be active in interrupt routine\n");
+
+ awc_event_ack_Cmd(priv->cmd.port);
+
+ if ( priv->enabled_interrupts & 0x10)
+ priv->enabled_interrupts &= ~0x10;
+
+ enabled_interrupts = awc_ints_enabled(dev->base_addr);
+
+ if (enabled_interrupts & 0x10){
+ awc_ints_enable(dev->base_addr, enabled_interrupts & ~0x10);
+ }
+
+ if (priv->command_semaphore_on){
+ priv->unlock_command_postponed++;
+
+ AWC_QUEUE_BH;
+ }
+ }
+
+ if ((active_interrupts & 0x10) && !(0x10 & enabled_interrupts) ){
+
+// printk(KERN_ERR "%s: aironet4500: cmd int shouldnt be active in interrupt routine\n",dev->name);
+
+ //awc_event_ack_Cmd(priv->cmd.port);
+ }
+
+
+// active_interrupts = awc_event_status(dev->base_addr);
+
+ tx_status = active_interrupts & 0x6 ;
+
+
+
+ if (tx_status) {
+
+ tx_fid = awc_Tx_Compl_Fid(dev->base_addr);
+ if (!tx_fid){
+ udelay(10);
+ tx_fid = awc_Tx_Compl_Fid(dev->base_addr);
+ }
+ if (!tx_fid)
+ printk(KERN_ERR "No tx fid when tx int active\n");
+
+ fid = awc_tx_fid_lookup_and_remove(dev, tx_fid);
+ if (fid) {
+ if (priv->process_tx_results) {
+ awc_fid_queue_push_tail(&priv->tx_post_process,fid);
+ AWC_QUEUE_BH;
+ }else {
+ if (fid->u.tx.fid_size <= AWC_TX_ALLOC_SMALL_SIZE)
+ awc_fid_queue_push_tail(&priv->tx_small_ready,fid);
+ else
+ awc_fid_queue_push_tail(&priv->tx_large_ready,fid);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ } else
+ printk(KERN_ERR "awc fid %x not found\n",tx_fid);
+
+
+ if (tx_status & 2){
+ awc_event_ack_Tx(dev->base_addr);
+ priv->stats.tx_packets++;
+ priv->waiting_interrupts &= ~0x2;
+ }
+ if (tx_status & 4){
+ priv->stats.tx_errors++;
+ awc_event_ack_TxExc(dev->base_addr);
+ priv->waiting_interrupts &= ~0x4;
+ }
+ if ((tx_status&6) == 6)
+ printk(KERN_NOTICE "%s: both tx and txExc up\n",dev->name);
+
+
+ }
+
+// active_interrupts = awc_event_status(dev->base_addr);
+
+ if ( active_interrupts & 0x1 ){
+ awc_receive_packet(dev);
+ awc_event_ack_Rx(dev->base_addr);
+ priv->waiting_interrupts &= ~0x1;
+ }
+
+ active_interrupts = awc_event_status(dev->base_addr);
+
+ if ((active_interrupts & 0x7) &&
+ !priv->bh_active &&
+ !priv->bh_running ){
+ if (multi_ints++ < 5)
+ goto start;
+ }
+ if (multi_ints >=5 )
+ printk(KERN_ERR "%s multi_ints > 5 interrupts still active %x\n",dev->name,active_interrupts);
+
+
+ priv->interrupt_count--;
+ dev->interrupt = 0;
+
+ awc_ints_enable(dev->base_addr, 0x0000);
+
+
+ DEBUG(0x8, " enabling ints in interrupt_process %x \n",
+ priv->enabled_interrupts & ~priv->waiting_interrupts);
+
+
+
+ AWC_ENTRY_EXIT_DEBUG(" exit \n");
+
+ awc_ints_enable(dev->base_addr,
+ priv->enabled_interrupts);
+
+//end_here:
+
+// enable_irq(dev->irq);
+ restore_flags(flags);
+
+ return 0;
+reenter_end_here:
+
+ AWC_ENTRY_EXIT_DEBUG(" reenter-bad end exit \n");
+// enable_irq(dev->irq);
+ restore_flags(flags);
+ return 0;
+
+bad_end:
+ dev->interrupt = 0;
+ AWC_ENTRY_EXIT_DEBUG(" bad_end exit \n");
+// enable_irq(dev->irq);
+ restore_flags(flags);
+ return -1;
+
+
+};
+
+static const char *aironet4500_core_version =
+"aironet4500.c v0.1 1/1/99 Elmer Joandi, elmer@ylenurme.ee.\n";
+
+struct NET_DEVICE * aironet4500_devices[MAX_AWCS] = {NULL,NULL,NULL,NULL};
+
+static int awc_debug = 0; // 0xffffff;
+static int p802_11_send = 0; // 1
+
+static int awc_process_tx_results = 0;
+int tx_queue_len = 10;
+int tx_rate = 0;
+static int channel = 5;
+//static int tx_full_rate = 0;
+static int max_mtu = 2312;
+static int adhoc = 0;
+static int large_buff_mem = 1700 * 10;
+static int small_buff_no = 20;
+static int awc_full_stats = 0;
+static char SSID[33] = {0};
+static int master= 0;
+static int slave = 0;
+// int debug =0;
+
+#if LINUX_VERSION_CODE >= 0x20100
+
+MODULE_PARM(awc_debug,"i");
+MODULE_PARM(rx_queue_len,"i");
+MODULE_PARM(tx_rate,"i");
+MODULE_PARM(channel,"i");
+MODULE_PARM(tx_full_rate,"i");
+MODULE_PARM(adhoc,"i");
+MODULE_PARM(master,"i");
+MODULE_PARM(slave,"i");
+MODULE_PARM(max_mtu,"i");
+MODULE_PARM(large_buff_mem,"i");
+MODULE_PARM(small_buff_no,"i");
+MODULE_PARM(SSID,"1-4c31");
+#endif
+
+/*EXPORT_SYMBOL(tx_queue_len);
+EXPORT_SYMBOL(awc_debug);
+ */
+EXPORT_SYMBOL(awc_init);
+EXPORT_SYMBOL(awc_open);
+EXPORT_SYMBOL(awc_close);
+EXPORT_SYMBOL(awc_reset);
+EXPORT_SYMBOL(awc_config);
+
+EXPORT_SYMBOL(aironet4500_devices);
+EXPORT_SYMBOL(awc_debug);
+//EXPORT_SYMBOL();
+
+EXPORT_SYMBOL(awc_private_init);
+EXPORT_SYMBOL(awc_tx_timeout);
+EXPORT_SYMBOL(awc_start_xmit);
+EXPORT_SYMBOL(awc_tx_done);
+EXPORT_SYMBOL(awc_rx);
+EXPORT_SYMBOL(awc_interrupt);
+EXPORT_SYMBOL(awc_get_stats);
+EXPORT_SYMBOL(awc_change_mtu);
+EXPORT_SYMBOL(awc_set_multicast_list);
+
+EXPORT_SYMBOL(awc_proc_set_fun);
+EXPORT_SYMBOL(awc_proc_unset_fun);
+EXPORT_SYMBOL(awc_register_proc);
+EXPORT_SYMBOL(awc_unregister_proc);
+
+
+/*************************** RESET INIT CONFIG ***********************/
+
+
+ void awc_reset(struct NET_DEVICE *dev)
+{
+
+ long long jiff;
+
+ DEBUG(2, " awc_reset dev %x \n", (int)dev);
+ DEBUG(2, "%s: awc_reset \n", dev->name);
+
+ awc_issue_soft_reset(dev);
+
+ jiff = jiffies;
+ udelay(1000);
+ while (awc_command_read(dev->base_addr)){
+ udelay(1000);
+ if (jiffies - jiff > 5*HZ){
+ printk(KERN_CRIT "%s bad reset\n",dev->name);
+ break;
+ }
+ };
+
+}
+
+ int awc_config(struct NET_DEVICE *dev)
+{
+// struct awc_private *priv = (struct awc_private *)dev->priv;
+
+ DEBUG(2, "%s: awc_config \n", dev->name);
+
+
+ if( awc_disable_MAC(dev)) goto final;
+ udelay(100);
+ if( awc_write_all_rids(dev) ) goto final;
+ udelay(100);
+ if( awc_enable_MAC(dev)) goto final;
+
+ return 0;
+ final:
+ return -1;
+}
+
+
+char name[] = "ElmerLinux";
+
+ int awc_init(struct NET_DEVICE *dev){
+ struct awc_private *priv = (struct awc_private *)dev->priv;
+ int i;
+ const char * radioType;
+
+ DEBUG(2, "%s: awc_init \n", dev->name);
+
+
+
+ //awc_dump_registers(dev);
+
+ if (adhoc & !max_mtu)
+ max_mtu= 2250;
+ else if (!max_mtu)
+ max_mtu= 1500;
+
+ priv->sleeping_bap = 1;
+
+
+ priv->enabled_interrupts = awc_ints_enabled(dev->base_addr);
+
+ if( awc_issue_noop(dev) ) goto final;
+
+ awc_ints_enable(dev->base_addr,0);
+
+ if( awc_disable_MAC(dev) ) goto final;
+
+
+// awc_rids_setup(dev);
+ i=0;
+ while ( i < AWC_NOF_RIDS){
+ if (awc_rids_temp[i].selector)
+ memcpy(&priv->rid_dir[i],&awc_rids_temp[i],sizeof(priv->rid_dir[0]) );
+ else priv->rid_dir[i].selector = NULL;
+ i++;
+ }
+
+ // following MUST be consistent with awc_rids !!!
+ priv->rid_dir[0].buff = &priv->config; // card RID mirrors
+ priv->rid_dir[1].buff = &priv->SSIDs;
+ priv->rid_dir[2].buff = &priv->fixed_APs;
+ priv->rid_dir[3].buff = &priv->driver_name;
+ priv->rid_dir[4].buff = &priv->enc_trans;
+ priv->rid_dir[5].buff = &priv->general_config; //
+ priv->rid_dir[6].buff = &priv->capabilities;
+ priv->rid_dir[7].buff = &priv->status;
+ priv->rid_dir[8].buff = &priv->AP;
+ priv->rid_dir[9].buff = &priv->statistics;
+ priv->rid_dir[10].buff = &priv->statistics_delta;
+ priv->rid_dir[11].buff = &priv->statistics_delta_clear;
+ priv->rid_dir[12].buff = &priv->wep_volatile;
+ priv->rid_dir[13].buff = &priv->wep_nonvolatile;
+ priv->rid_dir[14].buff = &priv->modulation;
+
+ priv->rid_dir[15].buff = &priv->statistics16;
+ priv->rid_dir[16].buff = &priv->statistics16_delta;
+ priv->rid_dir[17].buff = &priv->statistics16_delta_clear;
+
+ priv->rid_dir[0].bufflen = sizeof(priv->config); // card RID mirrors
+ priv->rid_dir[1].bufflen = sizeof(priv->SSIDs);
+ priv->rid_dir[2].bufflen = sizeof(priv->fixed_APs);
+ priv->rid_dir[3].bufflen = sizeof(priv->driver_name);
+ priv->rid_dir[4].bufflen = sizeof(priv->enc_trans);
+ priv->rid_dir[5].bufflen = sizeof(priv->general_config); //
+ priv->rid_dir[6].bufflen = sizeof(priv->capabilities);
+ priv->rid_dir[7].bufflen = sizeof(priv->status);
+ priv->rid_dir[8].bufflen = sizeof(priv->AP);
+ priv->rid_dir[9].bufflen = sizeof(priv->statistics);
+ priv->rid_dir[10].bufflen = sizeof(priv->statistics_delta);
+ priv->rid_dir[11].bufflen = sizeof(priv->statistics_delta_clear);
+ priv->rid_dir[12].bufflen = sizeof(priv->wep_volatile);
+ priv->rid_dir[13].bufflen = sizeof(priv->wep_nonvolatile);
+ priv->rid_dir[14].bufflen = sizeof(priv->modulation);
+
+ priv->rid_dir[15].bufflen = sizeof(priv->statistics16);
+ priv->rid_dir[16].bufflen = sizeof(priv->statistics16_delta);
+ priv->rid_dir[17].bufflen = sizeof(priv->statistics16_delta_clear);
+
+
+ if( awc_read_all_rids(dev) ) goto final;
+
+
+ priv->config.OperatingMode = 0;// MODE_LLC_HOST;
+ DEBUG(1,"ReceiveMode %x \n",priv->config.ReceiveMode);
+ // priv->config.ReceiveMode = RXMODE_DISABLE_802_3_HEADER;
+
+ if (!adhoc)
+ priv->config.OperatingMode = MODE_STA_ESS;
+// priv->config.OperatingMode = MODE_AP;
+// priv->config.Rates[0] =0x82;
+// priv->config.Rates[1] =0x4;
+// priv->config.Rates[2] =tx_full_rate;
+// priv->config.Rates[3] =0;
+// priv->config.Rates[4] =0;
+// priv->config.Rates[5] =0;
+// priv->config.Rates[6] =0;
+// priv->config.Rates[7] =0;
+ priv->config.Channel = channel;
+ if (adhoc && master){
+ priv->config.JoinNetTimeout = 0x1;//0 is facotry default
+ } else if (adhoc && slave){
+ priv->config.JoinNetTimeout = 0xffff;
+ };
+// priv->config.AuthenticationType = 1;
+ priv->config.Stationary =1;
+// priv->config.ScanMode = 1;
+// priv->config.LinkLossDelay = 100;
+ priv->config.FragmentThreshold = 1700;
+ priv->config.RtsThreshold = 1700;
+ memcpy(priv->config.NodeName, name, 10);
+
+ DEBUG(1,"%s supported Rates \n",dev->name);
+ for (i=0; i< 8; i++)
+ DEBUG(1,"%x ",priv->capabilities.SupportedRates[i]);
+ DEBUG(1,"%c",'\n');
+ DEBUG(1,"%s default Rates \n",dev->name);
+ for (i=0; i< 8; i++)
+ DEBUG(1,"%x ",priv->config.Rates[i]);
+ DEBUG(1,"%c",'\n');
+
+
+ // here we go, bad aironet
+ memset(&priv->SSIDs,0,sizeof(priv->SSIDs));
+
+ memset(&priv->queues_lock,0,sizeof(priv->queues_lock));
+
+ priv->SSIDs.ridLen =0;
+ if (!SSID) {
+ priv->SSIDs.SSID[0].SSID[0] ='a';
+ priv->SSIDs.SSID[0].SSID[1] ='b';
+ priv->SSIDs.SSID[0].SSID[2] ='c';
+ priv->SSIDs.SSID[0].lenght =3;
+ } else {
+ int sidlen = strlen(SSID);
+ memcpy(priv->SSIDs.SSID[0].SSID,SSID,sidlen);
+ priv->SSIDs.SSID[0].lenght = sidlen;
+ };
+
+ priv->SSIDs.SSID[1].lenght =0;
+ priv->SSIDs.SSID[1].SSID[0] =0;
+ priv->SSIDs.SSID[1].SSID[1] =0;
+ priv->SSIDs.SSID[2].lenght =0;
+ priv->SSIDs.SSID[2].SSID[0] =0;
+ priv->SSIDs.SSID[2].SSID[1] =0;
+
+
+// priv->enc_trans.rules[0].etherType= 0x0008;
+// priv->enc_trans.rules[0].Action = 1;
+
+ memcpy( priv->config.StationMacAddress,
+ priv->capabilities.FactoryAddress, 6 );
+
+ memcpy(dev->dev_addr, priv->config.StationMacAddress, 6);
+
+ DEBUG(2, "%s: awc_init success \n", dev->name);
+
+ if (priv->capabilities.RadioType == 1) radioType = "802.11 Frequency Hoping";
+ else if (priv->capabilities.RadioType == 2) radioType = "802.11 Direct Sequence";
+ else if (priv->capabilities.RadioType == 4) radioType = "LM2000";
+ else radioType = "Multiple Radio Types";
+
+ printk("%s: %s %s found @ 0x%lx irq %d firmwareVersion %d \n",dev->name,
+ priv->capabilities.ProductName,radioType,
+ dev->base_addr,dev->irq,
+ priv->capabilities.SoftwareVersion);
+
+ return 0;
+ final:
+ printk(KERN_ERR "aironet init failed \n");
+ return NODEV;
+
+ };
+
+int awc_private_init(struct NET_DEVICE * dev){
+ struct awc_private * priv = (struct awc_private *) dev->priv;
+ int i = 0;
+
+ DEBUG(2, "%s: awc_private_init \n", dev->name);
+
+
+ memset(priv, 0, sizeof(struct awc_private));
+
+ priv->bap0.select = dev->base_addr + awc_Select0_register;
+ priv->bap0.offset = dev->base_addr + awc_Offset0_register;
+ priv->bap0.data = dev->base_addr + awc_Data0_register;
+ priv->bap0.lock = 0;
+ priv->bap0.status = 0;
+ init_MUTEX(&priv->bap0.sem);
+ priv->bap1.select = dev->base_addr + awc_Select1_register;
+ priv->bap1.offset = dev->base_addr + awc_Offset1_register;
+ priv->bap1.data = dev->base_addr + awc_Data1_register;
+ priv->bap1.lock = 0;
+ priv->bap1.status = 0;
+ init_MUTEX(&priv->bap1.sem);
+ priv->sleeping_bap = 1;
+
+ init_MUTEX(&priv->command_semaphore);
+ priv->command_semaphore_on = 0;
+ priv->unlock_command_postponed = 0;
+ priv->immediate_bh.next = NULL;
+ priv->immediate_bh.sync = 0;
+ priv->immediate_bh.routine = (void *)(void *)awc_bh;
+ priv->immediate_bh.data = dev;
+ priv->bh_running = 0;
+ priv->bh_active = 0;
+ priv->tx_chain_active = 0;
+ priv->enabled_interrupts= 0x00;
+ priv->waiting_interrupts= 0x00;
+
+
+ init_MUTEX(&priv->tx_buff_semaphore);
+ priv->tx_buffs_in_use = 0;
+ priv->tx_small_buffs_in_use = 0;
+ priv->mac_enabled =0;
+ priv->link_status =0;
+ priv->large_buff_mem = large_buff_mem;
+ if (priv->large_buff_mem < max_mtu + AWC_TX_HEAD_SIZE + 10 )
+ priv->large_buff_mem = max_mtu + AWC_TX_HEAD_SIZE + 10;
+ priv->small_buff_no = small_buff_no;
+ if (priv->small_buff_no < 1 )
+ priv->small_buff_no = 1 ;
+
+ priv->process_tx_results = awc_process_tx_results;
+
+ //init_waitqueue(&priv->tx_chain_wait_queue);
+
+ for (i=0; i< 6 ; i++ ) {
+ priv->p2p[i] = 0xff;
+ priv->bssid[i] =0;
+ }
+// priv->p2p_uc =1;
+ priv->p2p_found =0;
+
+ priv->p802_11_send =p802_11_send;
+
+
+ priv->ejected =0;
+ dev->interrupt =0;
+ priv->interrupt_count =0;
+
+ return 0;
+
+};
+
+/**************************** OPEN CLOSE **********************/
+
+
+ int awc_open(struct NET_DEVICE *dev)
+{
+ struct awc_private *priv = (struct awc_private *)dev->priv;
+
+
+
+ DEBUG(2, "%s: awc_open \n", dev->name);
+
+ dev->interrupt = 0; dev->tbusy = 1; dev->start = 0;
+
+
+ if( awc_queues_init(dev) ) goto final;
+ if( awc_config(dev) ) goto final;
+
+ memcpy(dev->dev_addr, priv->config.StationMacAddress, 6);
+
+ priv->enabled_interrupts = 0x87;
+ awc_ints_enable(dev->base_addr,priv->enabled_interrupts);
+
+// priv->p8022_client = register_8022_client;
+// priv->snap_client = register_snap_client;
+ DEBUG(2, "%s: opened \n", dev->name);
+
+ priv->sleeping_bap = 0;
+
+
+ MOD_INC_USE_COUNT;
+// kernel_thread(awc_thread,dev,0);
+
+ dev->tbusy = 0; dev->start = 1;
+ return 0; /* Always succeed */
+
+ final:
+ dev->tbusy = 0; dev->start = 0;
+ printk(KERN_ERR "aironet open failed \n");
+ return -1;
+}
+
+
+ int awc_close(struct NET_DEVICE *dev)
+{
+ struct awc_private * priv = (struct awc_private *) dev->priv;
+
+ DEBUG(2, "%s: closing device.\n", dev->name);
+
+ dev->start = 0;
+ dev->tbusy=1;
+
+ awc_disable_MAC(dev);
+ awc_queues_destroy(dev);
+
+ awc_reset(dev);
+
+ udelay(10000);
+
+ DOWN(&priv->command_semaphore);
+
+ MOD_DEC_USE_COUNT;
+
+ UP(&priv->command_semaphore);
+
+ return 0;
+}
+
+
+
+/****************************** TX RX STUFF ******************/
+
+
+
+ void awc_tx_timeout(struct NET_DEVICE *dev)
+{
+ struct awc_private *priv = (struct awc_private *)dev->priv;
+
+ DEBUG(2, "%s: awc_tx_timeout \n", dev->name);
+
+ printk(KERN_NOTICE "%s: Transmit timed out , buffs %d %d, queues tx %d pp %d lrg %d sm %d \n ",
+ dev->name,priv->tx_small_buffs_total ,priv->tx_buffs_total,
+ priv->tx_in_transmit.size,priv->tx_post_process.size,
+ priv->tx_large_ready.size,priv->tx_small_ready.size);
+ priv->stats.tx_errors++;
+
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+}
+
+long long last_tx_q_hack = 0;
+int direction = 1;
+
+ int awc_start_xmit(struct sk_buff *skb, struct NET_DEVICE *dev) {
+
+ struct awc_private *priv = (struct awc_private *)dev->priv;
+ int retval = 0;
+ unsigned long flags;
+ struct awc_fid * fid = NULL;
+ int cnt=0;
+
+ DEBUG(2, "%s: awc_start_xmit \n", dev->name);
+
+
+ if (!dev) {
+ DEBUG(1, " xmit dev=NULL, jiffie %ld \n",jiffies);
+ return -1;
+ };
+
+ /* Transmitter timeout, serious problems. */
+ if (test_and_set_bit( 0, (void *) &dev->tbusy) ) {
+ if (jiffies - dev->trans_start > 3* HZ ){
+ save_flags(flags);
+ cli();
+ fid = priv->tx_in_transmit.head;
+ cnt = 0;
+ while (fid){
+ if (jiffies - fid->transmit_start_time > (HZ)){
+ // printk(KERN_ERR "%s staled tx_buff found, age %uld jiffies\n",dev->name,
+ // jiffies - fid->transmit_start_time );
+ awc_fid_queue_remove(&priv->tx_in_transmit, fid);
+ if (fid->u.tx.fid_size <= AWC_TX_ALLOC_SMALL_SIZE)
+ awc_fid_queue_push_tail(&priv->tx_small_ready,fid);
+ else
+ awc_fid_queue_push_tail(&priv->tx_large_ready,fid);
+ dev->tbusy = 0;
+ }
+ fid = fid->next;
+ if (cnt++ > 200) {
+ printk("bbb in awc_fid_queue\n");
+ restore_flags(flags);
+ return -1;
+ };
+
+ }
+ restore_flags(flags);
+ //debug =0x8;
+ };
+ if (jiffies - dev->trans_start >= (5* HZ) ) {
+ awc_tx_timeout(dev);
+ }
+ return 1;
+ }
+
+ if (!skb) {
+ DEBUG(1, " xmit skb=NULL, jiffie %ld \n",jiffies);
+ return -1;
+ };
+
+ if (test_and_set_bit( 0, (void *) &priv->tx_chain_active) ) {
+ dev->tbusy=0;
+ return 1;
+ }
+
+ dev->trans_start = jiffies;
+ retval = awc_802_11_tx_find_path_and_post(dev,skb);
+ priv->tx_chain_active = 0;
+// wake_up_interruptible(&priv->tx_chain_wait_queue);
+
+// if (!dev->tbusy) dev_tint(dev);
+ return retval;
+}
+
+int awc_tx_done(struct awc_fid * rx_fid){
+
+// dev->tbusy = 0;
+ mark_bh(NET_BH);
+
+ return 0;
+};
+
+int awc_rx(struct NET_DEVICE *dev, struct awc_fid * rx_fid) {
+
+// struct awc_private *lp = (struct awc_private *)dev->priv;
+
+ DEBUG(3, "%s: in rx_packet \n",dev->name);
+
+ if (!rx_fid ){
+ DEBUG(3, "%s: not rx_buff in rx_packet \n",dev->name);
+ return -1;
+ };
+ if ( !rx_fid->skb){
+ DEBUG(3, "%s: not rx_buff->skb in rx_packet \n",dev->name);
+ return -1;
+ };
+
+
+ rx_fid->skb->protocol = eth_type_trans(rx_fid->skb,dev);
+ netif_rx(rx_fid->skb);
+ rx_fid = NULL;
+
+ return 0;
+}
+
+
+ void awc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct NET_DEVICE *dev = (struct NET_DEVICE *)dev_id;
+// struct awc_private *lp;
+// unsigned long flags;
+
+// if ((dev == NULL)) return;
+
+// lp = (struct awc_private *)dev->priv;
+
+
+
+
+ DEBUG(2, "%s: awc_interrupt \n", dev->name);
+
+ awc_interrupt_process(dev);
+
+ return;
+}
+
+
+
+/************************ STATS, MULTICAST & STUFF ****************/
+
+
+
+ struct enet_statistics *awc_get_stats(struct NET_DEVICE *dev)
+{
+ struct awc_private *priv = (struct awc_private *)dev->priv;
+ unsigned long flags;
+// int cnt = 0;
+// int unlocked_stats_in_interrupt=0;
+
+ DEBUG(2, "%s: awc_get_stats \n", dev->name);
+
+ if (!dev->start) {
+ return 0;
+ }
+ save_flags(flags);
+ cli();
+ if (awc_full_stats)
+ awc_readrid_dir(dev, &priv->rid_dir[9]);
+ restore_flags(flags);
+
+ // the very following is the very wrong very probably
+ if (awc_full_stats){
+ priv->stats.rx_fifo_errors = priv->statistics.RxOverrunErr ;
+ priv->stats.rx_crc_errors = priv->statistics.RxPlcpCrcErr + priv->statistics.RxMacCrcErr ;
+ priv->stats.rx_frame_errors = priv->statistics.RxPlcpFormat ;
+ priv->stats.rx_length_errors = priv->statistics.RxPlcpLength ;
+ priv->stats.rx_missed_errors = priv->statistics.RxAged ;
+ priv->stats.rx_over_errors = priv->statistics.RxOverrunErr ;
+
+ priv->stats.collisions = priv->statistics.TxSinColl;
+ priv->stats.tx_aborted_errors = priv->statistics.TxAged ;
+ priv->stats.tx_fifo_errors = priv->statistics.HostTxFail ;
+ priv->stats.tx_window_errors = priv->statistics.TxMulColl ;
+ priv->stats.tx_heartbeat_errors = priv->statistics.DefersProt +priv->statistics.DefersEngy ;
+ priv->stats.tx_carrier_errors = priv->statistics.RetryLong +priv->statistics.RetryShort ;
+ priv->stats.multicast = priv->statistics.HostRxMc;
+ }
+
+
+// printk("rx_packets %d\n",priv->stats.rx_packets);
+ return &(priv->stats);
+}
+
+
+int awc_change_mtu(struct NET_DEVICE *dev, int new_mtu){
+
+// struct awc_private *priv = (struct awc_private *)dev->priv;
+ unsigned long flags;
+
+ if ((new_mtu < 256 ) || (new_mtu > 2312) || (max_mtu && new_mtu > max_mtu) )
+ return -EINVAL;
+
+ if (dev->start) {
+ printk("PLEASE, ifconfig %s down for mtu change\n",dev->name);
+
+ };
+ if (dev->mtu != new_mtu) {
+ save_flags(flags);
+ cli();
+ awc_disable_MAC(dev);
+ awc_tx_dealloc(dev);
+ dev->mtu = new_mtu;
+ awc_tx_alloc(dev);
+ awc_enable_MAC(dev);
+ restore_flags(flags);
+
+ printk("%s mtu has been changed to %d \n ",dev->name,dev->mtu);
+
+ }
+
+ return 0;
+
+};
+
+
+ void
+awc_set_multicast_list(struct NET_DEVICE *dev) {
+// int ioaddr = dev->base_addr;
+
+/* if (dev->flags & IFF_PROMISC)
+ promisc
+ else if (dev->mc_count || (dev->flags & IFF_ALLMULTI))
+ allmulti
+ else
+ normal
+ */
+
+
+}
+
+
+
+int (* awc_proc_set_fun) (int) = NULL;
+int (* awc_proc_unset_fun) (int) = NULL;
+
+
+int awc_register_proc(int (*awc_proc_set_device)(int),int (*awc_proc_unset_device)(int)){
+
+ AWC_ENTRY_EXIT_DEBUG("awc_register_proc");
+ awc_proc_set_fun = awc_proc_set_device;
+ awc_proc_unset_fun = awc_proc_unset_device;
+ AWC_ENTRY_EXIT_DEBUG("exit");
+ return 0;
+};
+
+int awc_unregister_proc(void){
+
+ AWC_ENTRY_EXIT_DEBUG("awc_unregister_proc");
+
+ awc_proc_set_fun = NULL;
+ awc_proc_unset_fun = NULL;
+ AWC_ENTRY_EXIT_DEBUG("exit");
+ return 0;
+};
+
+#ifdef MODULE
+
+int init_module(void)
+{
+// unsigned long flags;
+
+// debug = awc_debug;
+ printk(KERN_INFO"%s", aironet4500_core_version);
+ return 0;
+
+
+}
+
+void cleanup_module(void)
+{
+ printk(KERN_INFO "aironet4500 unloading core module \n");
+
+}
+
+#endif
+
\ No newline at end of file
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)