/* * Aironet 4500 Pcmcia driver * * Elmer Joandi, Januar 1999 * Copyright Elmer Joandi, all rights restricted * * * Revision 0.1 ,started 30.12.1998 * * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS #include #else #error awc driver needs CONFIG_PROC_FS #endif #include "aironet4500_rid.c" #define AWC_STR_SIZE 0x2ff0 #define DEV_AWC_INFO 1 #define DEV_AWC 1 struct awc_proc_private{ struct ctl_table_header * sysctl_header; struct ctl_table * proc_table; struct ctl_table proc_table_device_root[2]; struct ctl_table proc_table_sys_root[2]; char proc_name[10]; }; static char awc_drive_info[AWC_STR_SIZE]="Zcom \n\0"; static char awc_proc_buff[AWC_STR_SIZE]="\0"; static int awc_int_buff; static struct awc_proc_private awc_proc_priv[MAX_AWCS]; extern int awc_proc_unset_device(int device_number); int awc_proc_format_array(int write,char * buff, size_t * len, struct awc_rid_dir * rid_dir, struct aironet4500_RID * rid){ u8 * data = rid_dir->buff + rid->offset; int pos = 0; int null_past = 0; int hex = ((rid->mask == 0xff) && (rid->value == 0x0 )); int string = ((rid->mask == 0) && (rid->value == 0 )); u32 val =0; int bytes = (rid->bits / 8); int ch =0; int i,k; int array_len = rid->array; int nullX = 0; AWC_ENTRY_EXIT_DEBUG("awc_proc_format_array"); if (rid->bits %8 ) bytes +=1; if (bytes > 4 && rid->array == 1){ array_len = bytes; bytes = 1; hex = 1; }; if (bytes < 1 || bytes > 4){ printk(KERN_ERR " weird number of bytes %d in aironet rid \n",bytes); return -1; }; DEBUG(0x20000,"awc proc array bytes %d",bytes); DEBUG(0x20000," hex %d",hex); DEBUG(0x20000," string %d",string); DEBUG(0x20000," array_len %d \n",array_len); DEBUG(0x20000," offset %d \n",rid->offset); if (!write){ for (i=0; i < array_len ; i++){ if (bytes <= 1 ) val = data[i*bytes]; else if (bytes <= 2 ) val = *((u16 *)&data[i*bytes]); else if (bytes <= 4 ) val = *((u32 *)&data[i*bytes]); if (rid->null_terminated && !val) null_past =1; if (hex && !string) for (k=0; k = '0' && ch <='9') ch -= '0'; if (ch >= 'A' && ch <='F') ch -= 'A'+ 0xA; if (ch >= 'a' && ch <='f') ch -= 'a'+ 0xA; val += ch <<4; k++; ch = *(buff + 2*i*bytes +k + nullX); if (val == 0 && (ch == 'X' || ch == 'x')){ nullX=2; val = 0; k = -1; continue; }; if (ch >= '0' && ch <='9') ch -= '0'; if (ch >= 'A' && ch <='F') ch -= 'A'+ 0xA; if (ch >= 'a' && ch <='f') ch -= 'a'+ 0xA; val += ch; if (i*bytes > *len ) val = 0; } if (rid->bits <=8 ) data[i*bytes] = val; else if (rid->bits <=16 ) *((u16 *)&data[i*bytes]) = val; else if (rid->bits <=32 ) *((u32 *)&data[i*bytes]) = val; if (!val) null_past=1; } else { for (k=0; k < bytes; k++){ data[i*bytes +k] = *(buff + i*bytes +k); if (i*bytes +k > *len || !data[i*bytes +k]) null_past = 1;; } } if (null_past){ if (rid->bits <=8 ) data[i*bytes] = 0; else if (rid->bits <=16 ) *((u16 *)&data[i*bytes]) = 0; else if (rid->bits <=32 ) *((u32 *)&data[i*bytes]) = 0; } } }; // *len = pos; AWC_ENTRY_EXIT_DEBUG("awc_proc_format_array"); return 0; }; int awc_proc_format_bits(int write,u32 * buff, size_t* lenp, struct awc_rid_dir * rid_dir, struct aironet4500_RID * rid){ u8 * data = rid_dir->buff + rid->offset; u32 val = 0; int not_bool = 0; AWC_ENTRY_EXIT_DEBUG("awc_proc_format_bits"); if ((rid->bits == 8 && rid->mask == 0xff) || (rid->bits == 16 && rid->mask == 0xffff) || (rid->bits == 32 && rid->mask == 0xffffffff) ) not_bool = 1; if (rid->bits <=8 ) val = *data; else if (rid->bits <=16 ) val = *((u16 *)data); else if (rid->bits <=32 ) val = *((u32 *)data); DEBUG(0x20000,"awc proc int enter data %x \n",val); DEBUG(0x20000,"awc proc int enter buff %x \n",*buff); DEBUG(0x20000,"awc proc int enter intbuff %x \n",awc_int_buff); DEBUG(0x20000,"awc proc int enter lenp %x \n",*lenp); if (!write){ if (rid->mask) val &= rid->mask; if (!not_bool && rid->mask && ((val & rid->mask) == (rid->value & rid->mask))) *buff = 1; else if (!not_bool) *buff = 0; else *buff = val; } else { if (not_bool){ val &= ~rid->mask; val |= (*buff & rid->mask); } else { if (*buff){ val &= ~rid->mask; if (rid->value) val |= rid->mask & rid->value; else val |= rid->mask & ~rid->value; } else val &= ~rid->mask; }; if (rid->bits == 8) *data = val & 0xff; if (rid->bits == 16) *((u16*)data) = val &0xffff; if (rid->bits == 32) *((u32*)data) = val &0xffffffff; } DEBUG(0x20000,"awc proc int buff %x \n",awc_int_buff); if (rid->bits <=8 ) val = *data; else if (rid->bits <=16 ) val = *((u16 *)data); else if (rid->bits <=32 ) val = *((u32 *)data); DEBUG(0x20000,"awc proc int data %x \n",val); // *lenp = sizeof(int); *lenp += 1; AWC_ENTRY_EXIT_DEBUG("exit"); return 0; }; int awc_proc_fun(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp) { int retv =-1; struct awc_private *priv = NULL; unsigned long flags; // int device_number = (int ) ctl->extra1; struct awc_rid_dir * rid_dir; struct NET_DEVICE * dev= NULL; struct aironet4500_RID * rid = (struct aironet4500_RID * ) ctl->extra2; AWC_ENTRY_EXIT_DEBUG("awc_proc_fun"); if (!write && filp) if (filp->f_pos){ // printk(KERN_CRIT "Oversize read\n"); *lenp = 0;// hack against reading til eof return 0; } MOD_INC_USE_COUNT; rid_dir = ((struct awc_rid_dir *)ctl->extra1); dev = rid_dir->dev; if (!dev){ printk(KERN_ERR " NO device here \n"); goto final; } if(ctl->procname == NULL || awc_drive_info == NULL ){ printk(KERN_WARNING " procname is NULL in sysctl_table or awc_mib_info is NULL \n at awc module\n "); MOD_DEC_USE_COUNT; return -1; } priv = (struct awc_private * ) dev->priv; if ((rid->selector->read_only || rid->read_only) && write){ printk(KERN_ERR "This value is read-only \n"); goto final; }; if (!write && rid->selector->may_change) { save_flags(flags); cli(); awc_readrid(dev,rid,rid_dir->buff + rid->offset); restore_flags(flags); }; if (rid->array > 1 || rid->bits > 32){ if (write){ retv = proc_dostring(ctl, write, filp, buffer, lenp); if (retv) goto final; retv = awc_proc_format_array(write, awc_proc_buff, lenp, rid_dir, rid); if (retv) goto final; } else { retv = awc_proc_format_array(write, awc_proc_buff, lenp, rid_dir, rid); if (retv) goto final; retv = proc_dostring(ctl, write, filp, buffer, lenp); if (retv) goto final; } } else { if (write){ retv = proc_dointvec(ctl, write, filp, buffer, lenp); if (retv) goto final; retv = awc_proc_format_bits(write, &awc_int_buff, lenp, rid_dir, rid); if (retv) goto final; } else { retv = awc_proc_format_bits(write, &awc_int_buff, lenp,rid_dir, rid); if (retv) goto final; retv = proc_dointvec(ctl, write, filp, buffer, lenp); if (retv) goto final; } } if (write) { save_flags(flags); cli(); if (rid->selector->MAC_Disable_at_write){ awc_disable_MAC(dev); }; awc_writerid(dev,rid,rid_dir->buff + rid->offset); if (rid->selector->MAC_Disable_at_write){ awc_enable_MAC(dev); }; restore_flags(flags); }; DEBUG(0x20000,"awc proc ret %x \n",retv); DEBUG(0x20000,"awc proc lenp %x \n",*lenp); MOD_DEC_USE_COUNT; return retv; final: AWC_ENTRY_EXIT_DEBUG("exit"); MOD_DEC_USE_COUNT; return -1 ; } char conf_reset_result[200]; ctl_table awc_exdev_table[] = { {0, NULL, NULL,0, 0400, NULL}, {0} }; ctl_table awc_exroot_table[] = { {254, "aironet4500", NULL, 0, 0555, NULL}, {0} }; ctl_table awc_driver_proc_table[] = { {1, "debug" , &awc_debug, sizeof(awc_debug), 0600,NULL, proc_dointvec}, {2, "bap_sleep" , &bap_sleep, sizeof(bap_sleep), 0600,NULL, proc_dointvec}, {3, "bap_sleep_after_setup" , &bap_sleep_after_setup, sizeof(bap_sleep_after_setup), 0600,NULL, proc_dointvec}, {4, "sleep_before_command" , &sleep_before_command, sizeof(sleep_before_command), 0600,NULL, proc_dointvec}, {5, "bap_sleep_before_write" , &bap_sleep_before_write, sizeof(bap_sleep_before_write), 0600,NULL, proc_dointvec}, {6, "sleep_in_command" , &sleep_in_command , sizeof(sleep_in_command), 0600,NULL, proc_dointvec}, {0} }; ctl_table awc_root_table[] = { {254, "aironet4500", NULL, 0, 0555, awc_driver_proc_table}, {0} }; struct ctl_table_header * awc_driver_sysctl_header = NULL; const char awc_procname[]= "awc5"; int awc_proc_set_device(int device_number){ int group =0; int rid = 0; struct awc_priv * priv; AWC_ENTRY_EXIT_DEBUG("awc_proc_set_device"); if (!aironet4500_devices[device_number] || (awc_nof_rids <=0 )) return -1 ; priv = (struct awc_priv * )aironet4500_devices[device_number]->priv; awc_rids_setup(aironet4500_devices[device_number]); memcpy(&(awc_proc_priv[device_number].proc_table_sys_root[0]), awc_exroot_table,sizeof(struct ctl_table)*2); awc_proc_priv[device_number].proc_table_sys_root[0].ctl_name = 254 - device_number; memcpy(awc_proc_priv[device_number].proc_table_device_root, awc_exdev_table,sizeof(awc_exdev_table) ); awc_proc_priv[device_number].proc_table_device_root[0].ctl_name = device_number+1; awc_proc_priv[device_number].proc_table_sys_root->child = awc_proc_priv[device_number].proc_table_device_root; memcpy(awc_proc_priv[device_number].proc_name,(struct awc_priv * )aironet4500_devices[device_number]->name,5); awc_proc_priv[device_number].proc_name[4]=0; // awc_proc_priv[device_number].proc_name[3]=48+device_number; awc_proc_priv[device_number].proc_table_device_root[0].procname = &(awc_proc_priv[device_number].proc_name[0]); awc_proc_priv[device_number].proc_table = kmalloc(sizeof(struct ctl_table) * (awc_nof_rids+2),GFP_KERNEL); if (!awc_proc_priv[device_number].proc_table){ printk(KERN_CRIT "Out of memory on aironet4500_proc huge table alloc \n"); return -1; } awc_proc_priv[device_number].proc_table_device_root[0].child=awc_proc_priv[device_number].proc_table; if (awc_debug) printk("device %d of %d proc interface setup ",device_number, awc_nof_rids); while (awc_rids[group].selector && group < awc_nof_rids){ if (awc_debug & 0x20000) printk(KERN_CRIT "ridgroup %s size %d \n", awc_rids[group].selector->name,awc_rids[group].size); awc_proc_priv[device_number].proc_table[group].ctl_name = group +1; awc_proc_priv[device_number].proc_table[group+1].ctl_name = 0; awc_proc_priv[device_number].proc_table[group].procname = awc_rids[group].selector->name; awc_proc_priv[device_number].proc_table[group].data = awc_proc_buff; awc_proc_priv[device_number].proc_table[group].maxlen = sizeof(awc_proc_buff) -1; awc_proc_priv[device_number].proc_table[group].mode = 0600; awc_proc_priv[device_number].proc_table[group].child = kmalloc(sizeof(struct ctl_table) * (awc_rids[group].size +2), GFP_KERNEL); awc_proc_priv[device_number].proc_table[group].proc_handler = NULL; awc_proc_priv[device_number].proc_table[group].strategy = NULL; awc_proc_priv[device_number].proc_table[group].de = NULL; awc_proc_priv[device_number].proc_table[group].extra1 = NULL; awc_proc_priv[device_number].proc_table[group].extra2 = NULL; if (!awc_proc_priv[device_number].proc_table[group].child) { awc_proc_priv[device_number].proc_table[group].ctl_name = 0; printk(KERN_CRIT "Out of memory on aironet4500_proc huge table alloc \n"); return 0; } rid=0; while (awc_rids[group].rids[rid].selector && (rid < awc_rids[group].size -1)){ // DEBUG(0x20000,"rid %s \n", awc_rids[group].rids[rid].name); awc_proc_priv[device_number].proc_table[group].child[rid].ctl_name = rid +1; awc_proc_priv[device_number].proc_table[group].child[rid+1].ctl_name = 0; awc_proc_priv[device_number].proc_table[group].child[rid].procname = awc_rids[group].rids[rid].name; if (awc_rids[group].rids[rid].array > 1 || awc_rids[group].rids[rid].bits > 32 ){ awc_proc_priv[device_number].proc_table[group].child[rid].data = awc_proc_buff; awc_proc_priv[device_number].proc_table[group].child[rid].maxlen = sizeof(awc_proc_buff) -1; } else { awc_proc_priv[device_number].proc_table[group].child[rid].data = &awc_int_buff; awc_proc_priv[device_number].proc_table[group].child[rid].maxlen = sizeof(awc_int_buff); } if ( awc_rids[group].rids[rid].read_only || awc_rids[group].rids[rid].selector->read_only ) awc_proc_priv[device_number].proc_table[group].child[rid].mode = 0400; else awc_proc_priv[device_number].proc_table[group].child[rid].mode = 0600; awc_proc_priv[device_number].proc_table[group].child[rid].child = NULL; awc_proc_priv[device_number].proc_table[group].child[rid].proc_handler = awc_proc_fun; awc_proc_priv[device_number].proc_table[group].child[rid].strategy = NULL; awc_proc_priv[device_number].proc_table[group].child[rid].de = NULL; awc_proc_priv[device_number].proc_table[group].child[rid].extra1 = (void *) &(((struct awc_private* )aironet4500_devices[device_number]->priv)->rid_dir[group]); awc_proc_priv[device_number].proc_table[group].child[rid].extra2 = (void *) &(awc_rids[group].rids[rid]); rid++; } group++; }; awc_proc_priv[device_number].sysctl_header = register_sysctl_table(awc_proc_priv[device_number].proc_table_sys_root,0); AWC_ENTRY_EXIT_DEBUG("exit"); if (awc_proc_priv[device_number].sysctl_header) return 0; return 1; }; int awc_proc_unset_device(int device_number){ int k; AWC_ENTRY_EXIT_DEBUG("awc_proc_unset_device"); if (awc_proc_priv[device_number].sysctl_header){ unregister_sysctl_table(awc_proc_priv[device_number].sysctl_header); awc_proc_priv[device_number].sysctl_header = NULL; } if (awc_proc_priv[device_number].proc_table){ for (k=0; awc_proc_priv[device_number].proc_table[k].ctl_name ; k++ ){ if (awc_proc_priv[device_number].proc_table[k].child) kfree(awc_proc_priv[device_number].proc_table[k].child); } kfree(awc_proc_priv[device_number].proc_table); awc_proc_priv[device_number].proc_table = NULL; } if (awc_proc_priv[device_number].proc_table_device_root[0].ctl_name) awc_proc_priv[device_number].proc_table_device_root[0].ctl_name = 0; if (awc_proc_priv[device_number].proc_table_sys_root[0].ctl_name) awc_proc_priv[device_number].proc_table_sys_root[0].ctl_name = 0; AWC_ENTRY_EXIT_DEBUG("exit"); return 0; }; int init_module(void) { int i=0; AWC_ENTRY_EXIT_DEBUG("init_module"); for (i=0; i < MAX_AWCS; i++){ awc_proc_set_device(i); } awc_register_proc(awc_proc_set_device, awc_proc_unset_device); awc_driver_sysctl_header = register_sysctl_table(awc_root_table,0); AWC_ENTRY_EXIT_DEBUG("exit"); return 0; }; void cleanup_module(void){ int i=0; AWC_ENTRY_EXIT_DEBUG("cleanup_module"); awc_unregister_proc(); for (i=0; i < MAX_AWCS; i++){ awc_proc_unset_device(i); } if (awc_driver_sysctl_header) unregister_sysctl_table(awc_driver_sysctl_header); AWC_ENTRY_EXIT_DEBUG("exit"); }; #endif // whole proc system styff