Using Proc Filesystem in Drivers




The alternative method for creating a /proc entry is to create and initialize a proc_dir_entry structure and pass it to proc_register_dynamic(version 2.0) or proc_register (version 2.2, which assumes a dynamic file if the inode number in the structure is 0). As an example, consider the following code that scull uses when compiled against 2.0 headers:
struct airo_info {
......
struct proc_dir_entry *proc_entry;
....

}

Then in setup_proc_entry(dev, dev->priv ) function, a parent directory with the dev name and some files to read information

apriv->proc_entry = create_proc_entry(dev->name,
S_IFDIR|airo_perm,
airo_entry);
S_IFDIR Shows this is a directory named with device name. See Appendix

/* Setup the Status */
entry = create_proc_entry("Status",
S_IFREG | (S_IRUGO&proc_perm),
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_status_ops);

/* Setup the Timestamp */
entry = create_proc_entry("TimeStamps",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_stamps_ops);
Above show a regular file (S_IFREG) named "Status" is created under the directory, and the permission is set as S_IRUGO&proc_perm,
the SETPROC_OPS(entry, proc_status_ops)  is from "#define SETPROC_OPS(entry, ops) (entry)->proc_fops = &(ops)", and proc_status_ops is defined as:
static struct file_operations proc_status_ops = {
.read = proc_read,
.open = proc_status_open,
.release = proc_close
};
static struct file_operations proc_stamps_ops = {
.read = proc_read,
.open = proc_stamps_open,
.release = proc_close
};
There is no "write" operation in above abstract becasue the status of the card is read-only.

static int proc_status_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *apriv = dev->priv;
CapabilityRid cap_rid;
StatusRid status_rid;
int i;

if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(file->private_data, 0, sizeof(struct proc_data));
data = (struct proc_data *)file->private_data;
if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
kfree (file->private_data);
return -ENOMEM;
}

readStatusRid(apriv, &status_rid, 1);
readCapabilityRid(apriv, &cap_rid);

i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
status_rid.mode & 1 ? "CFG ": "",
status_rid.mode & 2 ? "ACT ": "",
status_rid.mode & 0x10 ? "SYN ": "",
status_rid.mode & 0x20 ? "LNK ": "",
status_rid.mode & 0x40 ? "LEAP ": "",
status_rid.mode & 0x80 ? "PRIV ": "",
status_rid.mode & 0x100 ? "KEY ": "",
status_rid.mode & 0x200 ? "WEP ": "",
status_rid.mode & 0x8000 ? "ERR ": "");
sprintf( data->rbuffer+i, "Mode: %x\n"
"Signal Strength: %d\n"
"Signal Quality: %d\n"
"SSID: %-.*s\n"
"AP: %-.16s\n"
"Freq: %d\n"
"BitRate: %dmbs\n"
"Driver Version: %s\n"
"Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
"Radio type: %x\nCountry: %x\nHardware Version: %x\n"
"Software Version: %x\nSoftware Subversion: %x\n"
"Boot block version: %x\n",
(int)status_rid.mode,
(int)status_rid.normalizedSignalStrength,
(int)status_rid.signalQuality,
(int)status_rid.SSIDlen,
status_rid.SSID,
status_rid.apName,
(int)status_rid.channel,
(int)status_rid.currentXmitRate/2,
version,
cap_rid.prodName,
cap_rid.manName,
cap_rid.prodVer,
cap_rid.radioType,
cap_rid.country,
cap_rid.hardVer,
(int)cap_rid.softVer,
(int)cap_rid.softSubVer,
(int)cap_rid.bootBlockVer );
data->readlen = strlen( data->rbuffer );
return 0;
}
static int proc_stamps_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *apriv = dev->priv;
CapabilityRid cap_rid;
StatusRid status_rid;
int i;

if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(file->private_data, 0, sizeof(struct proc_data));
data = (struct proc_data *)file->private_data;
if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
kfree (file->private_data);
return -ENOMEM;
}


sprintf( data->rbuffer, "Mode: Empty\n"
"NO DATA NOW\n");

data->readlen = strlen( data->rbuffer );
return 0;
}
The open function has a lot of operations, basically, the pointer file->private_data is pointed to the proc_data structure filled here.
data->rbuffer is allocated as 2048 Bytes (enough for a status information).
data->readlen = strlen ( data->rbuffer);


struct proc_data {
int release_buffer;
int readlen;
char *rbuffer;
int writelen;
int maxwritelen;
char *wbuffer;
void (*on_close) (struct inode *, struct file *);
};

#ifndef SETPROC_OPS
#define SETPROC_OPS(entry, ops) (entry)->proc_fops = &(ops)
#endif

static int setup_proc_entry( struct net_device *dev,
struct airo_info *apriv ) {
struct proc_dir_entry *entry;
/* First setup the device directory */
apriv->proc_entry = create_proc_entry(dev->name,
S_IFDIR|airo_perm,
airo_entry);
apriv->proc_entry->uid = proc_uid;
apriv->proc_entry->gid = proc_gid;
apriv->proc_entry->owner = THIS_MODULE;

/* Setup the StatsDelta */
entry = create_proc_entry("StatsDelta",
S_IFREG | (S_IRUGO&proc_perm),
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_statsdelta_ops);

/* Setup the Stats */
entry = create_proc_entry("Stats",
S_IFREG | (S_IRUGO&proc_perm),
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_stats_ops);

/* Setup the Status */
entry = create_proc_entry("Status",
S_IFREG | (S_IRUGO&proc_perm),
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_status_ops);

/* Setup the Config */
entry = create_proc_entry("Config",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_config_ops);

/* Setup the SSID */
entry = create_proc_entry("SSID",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_SSID_ops);

/* Setup the APList */
entry = create_proc_entry("APList",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_APList_ops);

/* Setup the BSSList */
entry = create_proc_entry("BSSList",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_BSSList_ops);

/* Setup the WepKey */
entry = create_proc_entry("WepKey",
S_IFREG | proc_perm,
apriv->proc_entry);
entry->uid = proc_uid;
entry->gid = proc_gid;
entry->data = dev;
entry->owner = THIS_MODULE;
SETPROC_OPS(entry, proc_wepkey_ops);

return 0;
}

static int takedown_proc_entry( struct net_device *dev,
struct airo_info *apriv ) {
if ( !apriv->proc_entry->namelen ) return 0;
remove_proc_entry("Stats",apriv->proc_entry);
remove_proc_entry("StatsDelta",apriv->proc_entry);
remove_proc_entry("Status",apriv->proc_entry);
remove_proc_entry("Config",apriv->proc_entry);
remove_proc_entry("SSID",apriv->proc_entry);
remove_proc_entry("APList",apriv->proc_entry);
remove_proc_entry("BSSList",apriv->proc_entry);
remove_proc_entry("WepKey",apriv->proc_entry);
remove_proc_entry(dev->name,airo_entry);
return 0;
}

/*
* What we want from the proc_fs is to be able to efficiently read
* and write the configuration. To do this, we want to read the
* configuration when the file is opened and write it when the file is
* closed. So basically we allocate a read buffer at open and fill it
* with data, and allocate a write buffer and read it at close.
*/

/*
* The read routine is generic, it relies on the preallocated rbuffer
* to supply the data.
*/
static ssize_t proc_read( struct file *file,
char *buffer,
size_t len,
loff_t *offset )
{
int i;
int pos;
struct proc_data *priv = (struct proc_data*)file->private_data;

if( !priv->rbuffer ) return -EINVAL;

pos = *offset;
for( i = 0; i+pos < priv->readlen && i < len; i++ ) {
if (put_user( priv->rbuffer[i+pos], buffer+i ))
return -EFAULT;
}
*offset += i;
return i;
}

/*
* The write routine is generic, it fills in a preallocated rbuffer
* to supply the data.
*/
static ssize_t proc_write( struct file *file,
const char *buffer,
size_t len,
loff_t *offset )
{
int i;
int pos;
struct proc_data *priv = (struct proc_data*)file->private_data;

if ( !priv->wbuffer ) {
return -EINVAL;
}

pos = *offset;

for( i = 0; i + pos < priv->maxwritelen &&
i < len; i++ ) {
if (get_user( priv->wbuffer[i+pos], buffer + i ))
return -EFAULT;
}
if ( i+pos > priv->writelen ) priv->writelen = i+file->f_pos;
*offset += i;
return i;
}

static int proc_status_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *apriv = dev->priv;
CapabilityRid cap_rid;
StatusRid status_rid;
int i;

if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(file->private_data, 0, sizeof(struct proc_data));
data = (struct proc_data *)file->private_data;
if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
kfree (file->private_data);
return -ENOMEM;
}

readStatusRid(apriv, &status_rid, 1);
readCapabilityRid(apriv, &cap_rid);

i = sprintf(data->rbuffer, "Status: %s%s%s%s%s%s%s%s%s\n",
status_rid.mode & 1 ? "CFG ": "",
status_rid.mode & 2 ? "ACT ": "",
status_rid.mode & 0x10 ? "SYN ": "",
status_rid.mode & 0x20 ? "LNK ": "",
status_rid.mode & 0x40 ? "LEAP ": "",
status_rid.mode & 0x80 ? "PRIV ": "",
status_rid.mode & 0x100 ? "KEY ": "",
status_rid.mode & 0x200 ? "WEP ": "",
status_rid.mode & 0x8000 ? "ERR ": "");
sprintf( data->rbuffer+i, "Mode: %x\n"
"Signal Strength: %d\n"
"Signal Quality: %d\n"
"SSID: %-.*s\n"
"AP: %-.16s\n"
"Freq: %d\n"
"BitRate: %dmbs\n"
"Driver Version: %s\n"
"Device: %s\nManufacturer: %s\nFirmware Version: %s\n"
"Radio type: %x\nCountry: %x\nHardware Version: %x\n"
"Software Version: %x\nSoftware Subversion: %x\n"
"Boot block version: %x\n",
(int)status_rid.mode,
(int)status_rid.normalizedSignalStrength,
(int)status_rid.signalQuality,
(int)status_rid.SSIDlen,
status_rid.SSID,
status_rid.apName,
(int)status_rid.channel,
(int)status_rid.currentXmitRate/2,
version,
cap_rid.prodName,
cap_rid.manName,
cap_rid.prodVer,
cap_rid.radioType,
cap_rid.country,
cap_rid.hardVer,
(int)cap_rid.softVer,
(int)cap_rid.softSubVer,
(int)cap_rid.bootBlockVer );
data->readlen = strlen( data->rbuffer );
return 0;
}

static int proc_stats_rid_open(struct inode*, struct file*, u16);
static int proc_statsdelta_open( struct inode *inode,
struct file *file ) {
if (file->f_mode&FMODE_WRITE) {
return proc_stats_rid_open(inode, file, RID_STATSDELTACLEAR);
}
return proc_stats_rid_open(inode, file, RID_STATSDELTA);
}

static int proc_stats_open( struct inode *inode, struct file *file ) {
return proc_stats_rid_open(inode, file, RID_STATS);
}

static int proc_stats_rid_open( struct inode *inode,
struct file *file,
u16 rid ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *apriv = dev->priv;
StatsRid stats;
int i, j;
u32 *vals = stats.vals;

if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(file->private_data, 0, sizeof(struct proc_data));
data = (struct proc_data *)file->private_data;
if ((data->rbuffer = kmalloc( 4096, GFP_KERNEL )) == NULL) {
kfree (file->private_data);
return -ENOMEM;
}

readStatsRid(apriv, &stats, rid, 1);

j = 0;
for(i=0; statsLabels[i]!=(char *)-1 &&
i*4<stats.len; i++){
if (!statsLabels[i]) continue;
if (j+strlen(statsLabels[i])+16>4096) {
printk(KERN_WARNING
"airo: Potentially disasterous buffer overflow averted!\n");
break;
}
j+=sprintf(data->rbuffer+j, "%s: %u\n", statsLabels[i], vals[i]);
}
if (i*4>=stats.len){
printk(KERN_WARNING
"airo: Got a short rid\n");
}
data->readlen = j;
return 0;
}

static int get_dec_u16( char *buffer, int *start, int limit ) {
u16 value;
int valid = 0;
for( value = 0; buffer[*start] >= '0' &&
buffer[*start] <= '9' &&
*start < limit; (*start)++ ) {
valid = 1;
value *= 10;
value += buffer[*start] - '0';
}
if ( !valid ) return -1;
return value;
}

static int airo_config_commit(struct net_device *dev,
struct iw_request_info *info, void *zwrq,
char *extra);

static void proc_config_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data = file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *ai = dev->priv;
char *line;

if ( !data->writelen ) return;

readConfigRid(ai, 1);
set_bit (FLAG_COMMIT, &ai->flags);

line = data->wbuffer;
while( line[0] ) {
/*** Mode processing */
if ( !strncmp( line, "Mode: ", 6 ) ) {
line += 6;
if ((ai->config.rmode & 0xff) >= RXMODE_RFMON)
set_bit (FLAG_RESET, &ai->flags);
ai->config.rmode &= 0xfe00;
clear_bit (FLAG_802_11, &ai->flags);
ai->config.opmode &= 0xFF00;
ai->config.scanMode = SCANMODE_ACTIVE;
if ( line[0] == 'a' ) {
ai->config.opmode |= 0;
} else {
ai->config.opmode |= 1;
if ( line[0] == 'r' ) {
ai->config.rmode |= RXMODE_RFMON | RXMODE_DISABLE_802_3_HEADER;
ai->config.scanMode = SCANMODE_PASSIVE;
set_bit (FLAG_802_11, &ai->flags);
} else if ( line[0] == 'y' ) {
ai->config.rmode |= RXMODE_RFMON_ANYBSS | RXMODE_DISABLE_802_3_HEADER;
ai->config.scanMode = SCANMODE_PASSIVE;
set_bit (FLAG_802_11, &ai->flags);
} else if ( line[0] == 'l' )
ai->config.rmode |= RXMODE_LANMON;
}
set_bit (FLAG_COMMIT, &ai->flags);
}

/*** Radio status */
else if (!strncmp(line,"Radio: ", 7)) {
line += 7;
if (!strncmp(line,"off",3)) {
set_bit (FLAG_RADIO_OFF, &ai->flags);
} else {
clear_bit (FLAG_RADIO_OFF, &ai->flags);
}
}
/*** NodeName processing */
else if ( !strncmp( line, "NodeName: ", 10 ) ) {
int j;

line += 10;
memset( ai->config.nodeName, 0, 16 );
/* Do the name, assume a space between the mode and node name */
for( j = 0; j < 16 && line[j] != '\n'; j++ ) {
ai->config.nodeName[j] = line[j];
}
set_bit (FLAG_COMMIT, &ai->flags);
}

/*** PowerMode processing */
else if ( !strncmp( line, "PowerMode: ", 11 ) ) {
line += 11;
if ( !strncmp( line, "PSPCAM", 6 ) ) {
ai->config.powerSaveMode = POWERSAVE_PSPCAM;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "PSP", 3 ) ) {
ai->config.powerSaveMode = POWERSAVE_PSP;
set_bit (FLAG_COMMIT, &ai->flags);
} else {
ai->config.powerSaveMode = POWERSAVE_CAM;
set_bit (FLAG_COMMIT, &ai->flags);
}
} else if ( !strncmp( line, "DataRates: ", 11 ) ) {
int v, i = 0, k = 0; /* i is index into line,
k is index to rates */

line += 11;
while((v = get_dec_u16(line, &i, 3))!=-1) {
ai->config.rates[k++] = (u8)v;
line += i + 1;
i = 0;
}
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "Channel: ", 9 ) ) {
int v, i = 0;
line += 9;
v = get_dec_u16(line, &i, i+3);
if ( v != -1 ) {
ai->config.channelSet = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
}
} else if ( !strncmp( line, "XmitPower: ", 11 ) ) {
int v, i = 0;
line += 11;
v = get_dec_u16(line, &i, i+3);
if ( v != -1 ) {
ai->config.txPower = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
}
} else if ( !strncmp( line, "WEP: ", 5 ) ) {
line += 5;
switch( line[0] ) {
case 's':
ai->config.authType = (u16)AUTH_SHAREDKEY;
break;
case 'e':
ai->config.authType = (u16)AUTH_ENCRYPT;
break;
default:
ai->config.authType = (u16)AUTH_OPEN;
break;
}
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "LongRetryLimit: ", 16 ) ) {
int v, i = 0;

line += 16;
v = get_dec_u16(line, &i, 3);
v = (v<0) ? 0 : ((v>255) ? 255 : v);
ai->config.longRetryLimit = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "ShortRetryLimit: ", 17 ) ) {
int v, i = 0;

line += 17;
v = get_dec_u16(line, &i, 3);
v = (v<0) ? 0 : ((v>255) ? 255 : v);
ai->config.shortRetryLimit = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "RTSThreshold: ", 14 ) ) {
int v, i = 0;

line += 14;
v = get_dec_u16(line, &i, 4);
v = (v<0) ? 0 : ((v>2312) ? 2312 : v);
ai->config.rtsThres = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "TXMSDULifetime: ", 16 ) ) {
int v, i = 0;

line += 16;
v = get_dec_u16(line, &i, 5);
v = (v<0) ? 0 : v;
ai->config.txLifetime = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "RXMSDULifetime: ", 16 ) ) {
int v, i = 0;

line += 16;
v = get_dec_u16(line, &i, 5);
v = (v<0) ? 0 : v;
ai->config.rxLifetime = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "TXDiversity: ", 13 ) ) {
ai->config.txDiversity =
(line[13]=='l') ? 1 :
((line[13]=='r')? 2: 3);
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "RXDiversity: ", 13 ) ) {
ai->config.rxDiversity =
(line[13]=='l') ? 1 :
((line[13]=='r')? 2: 3);
set_bit (FLAG_COMMIT, &ai->flags);
} else if ( !strncmp( line, "FragThreshold: ", 15 ) ) {
int v, i = 0;

line += 15;
v = get_dec_u16(line, &i, 4);
v = (v<256) ? 256 : ((v>2312) ? 2312 : v);
v = v & 0xfffe; /* Make sure its even */
ai->config.fragThresh = (u16)v;
set_bit (FLAG_COMMIT, &ai->flags);
} else if (!strncmp(line, "Modulation: ", 12)) {
line += 12;
switch(*line) {
case 'd': ai->config.modulation=MOD_DEFAULT; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'c': ai->config.modulation=MOD_CCK; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'm': ai->config.modulation=MOD_MOK; set_bit(FLAG_COMMIT, &ai->flags); break;
default:
printk( KERN_WARNING "airo: Unknown modulation\n" );
}
} else if (!strncmp(line, "Preamble: ", 10)) {
line += 10;
switch(*line) {
case 'a': ai->config.preamble=PREAMBLE_AUTO; set_bit(FLAG_COMMIT, &ai->flags); break;
case 'l': ai->config.preamble=PREAMBLE_LONG; set_bit(FLAG_COMMIT, &ai->flags); break;
case 's': ai->config.preamble=PREAMBLE_SHORT; set_bit(FLAG_COMMIT, &ai->flags); break;
default: printk(KERN_WARNING "airo: Unknown preamble\n");
}
} else {
printk( KERN_WARNING "Couldn't figure out %s\n", line );
}
while( line[0] && line[0] != '\n' ) line++;
if ( line[0] ) line++;
}
airo_config_commit(dev, NULL, NULL, NULL);
}

static char *get_rmode(u16 mode) {
switch(mode&0xff) {
case RXMODE_RFMON: return "rfmon";
case RXMODE_RFMON_ANYBSS: return "yna (any) bss rfmon";
case RXMODE_LANMON: return "lanmon";
}
return "ESS";
}

static int proc_config_open( struct inode *inode, struct file *file ) {
struct proc_data *data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *ai = dev->priv;
int i;

if ((file->private_data = kmalloc(sizeof(struct proc_data ), GFP_KERNEL)) == NULL)
return -ENOMEM;
memset(file->private_data, 0, sizeof(struct proc_data));
data = (struct proc_data *)file->private_data;
if ((data->rbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
kfree (file->private_data);
return -ENOMEM;
}
if ((data->wbuffer = kmalloc( 2048, GFP_KERNEL )) == NULL) {
kfree (data->rbuffer);
kfree (file->private_data);
return -ENOMEM;
}
memset( data->wbuffer, 0, 2048 );
data->maxwritelen = 2048;
data->on_close = proc_config_on_close;

readConfigRid(ai, 1);

i = sprintf( data->rbuffer,
"Mode: %s\n"
"Radio: %s\n"
"NodeName: %-16s\n"
"PowerMode: %s\n"
"DataRates: %d %d %d %d %d %d %d %d\n"
"Channel: %d\n"
"XmitPower: %d\n",
(ai->config.opmode & 0xFF) == 0 ? "adhoc" :
(ai->config.opmode & 0xFF) == 1 ? get_rmode(ai->config.rmode):
(ai->config.opmode & 0xFF) == 2 ? "AP" :
(ai->config.opmode & 0xFF) == 3 ? "AP RPTR" : "Error",
test_bit(FLAG_RADIO_OFF, &ai->flags) ? "off" : "on",
ai->config.nodeName,
ai->config.powerSaveMode == 0 ? "CAM" :
ai->config.powerSaveMode == 1 ? "PSP" :
ai->config.powerSaveMode == 2 ? "PSPCAM" : "Error",
(int)ai->config.rates[0],
(int)ai->config.rates[1],
(int)ai->config.rates[2],
(int)ai->config.rates[3],
(int)ai->config.rates[4],
(int)ai->config.rates[5],
(int)ai->config.rates[6],
(int)ai->config.rates[7],
(int)ai->config.channelSet,
(int)ai->config.txPower
);
sprintf( data->rbuffer + i,
"LongRetryLimit: %d\n"
"ShortRetryLimit: %d\n"
"RTSThreshold: %d\n"
"TXMSDULifetime: %d\n"
"RXMSDULifetime: %d\n"
"TXDiversity: %s\n"
"RXDiversity: %s\n"
"FragThreshold: %d\n"
"WEP: %s\n"
"Modulation: %s\n"
"Preamble: %s\n",
(int)ai->config.longRetryLimit,
(int)ai->config.shortRetryLimit,
(int)ai->config.rtsThres,
(int)ai->config.txLifetime,
(int)ai->config.rxLifetime,
ai->config.txDiversity == 1 ? "left" :
ai->config.txDiversity == 2 ? "right" : "both",
ai->config.rxDiversity == 1 ? "left" :
ai->config.rxDiversity == 2 ? "right" : "both",
(int)ai->config.fragThresh,
ai->config.authType == AUTH_ENCRYPT ? "encrypt" :
ai->config.authType == AUTH_SHAREDKEY ? "shared" : "open",
ai->config.modulation == 0 ? "default" :
ai->config.modulation == MOD_CCK ? "cck" :
ai->config.modulation == MOD_MOK ? "mok" : "error",
ai->config.preamble == PREAMBLE_AUTO ? "auto" :
ai->config.preamble == PREAMBLE_LONG ? "long" :
ai->config.preamble == PREAMBLE_SHORT ? "short" : "error"
);
data->readlen = strlen( data->rbuffer );
return 0;
}

static void proc_SSID_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data = (struct proc_data *)file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *ai = dev->priv;
SsidRid SSID_rid;
Resp rsp;
int i;
int offset = 0;

if ( !data->writelen ) return;

memset( &SSID_rid, 0, sizeof( SSID_rid ) );

for( i = 0; i < 3; i++ ) {
int j;
for( j = 0; j+offset < data->writelen && j < 32 &&
data->wbuffer[offset+j] != '\n'; j++ ) {
SSID_rid.ssids[i].ssid[j] = data->wbuffer[offset+j];
}
if ( j == 0 ) break;
SSID_rid.ssids[i].len = j;
offset += j;
while( data->wbuffer[offset] != '\n' &&
offset < data->writelen ) offset++;
offset++;
}
if (i)
SSID_rid.len = sizeof(SSID_rid);
disable_MAC(ai, 1);
writeSsidRid(ai, &SSID_rid);
enable_MAC(ai, &rsp, 1);
}

inline static u8 hexVal(char c) {
if (c>='0' && c<='9') return c -= '0';
if (c>='a' && c<='f') return c -= 'a'-10;
if (c>='A' && c<='F') return c -= 'A'-10;
return 0;
}

static void proc_APList_on_close( struct inode *inode, struct file *file ) {
struct proc_data *data = (struct proc_data *)file->private_data;
struct proc_dir_entry *dp = PDE(inode);
struct net_device *dev = dp->data;
struct airo_info *ai = dev->priv;
APListRid APList_rid;
Resp rsp;
int i;

if ( !data->writelen ) return;

memset( &APList_rid, 0, sizeof(APList_rid) );
APList_rid.len = sizeof(APList_rid);

for( i = 0; i < 4 && data->writelen >= (i+1)*6*3; i++ ) {
int j;
for( j = 0; j < 6*3 && data->wbuffer[j+i*6*3]; j++ ) {
switch(j%3) {
case 0:
APList_rid.ap[i][j/3]=
hexVal(data->wbuffer[j+i*6*3])<<4;
break;
case 1:
APList_rid.ap[i][j/3]|=
hexVal(data->wbuffer[j+i*6*3]);
break;
}
}
}
disable_MAC(ai, 1);
writeAPListRid(ai, &APList_rid);
enable_MAC(ai, &rsp, 1);
}


Appendix


#define S_IFMT 0170000 /* type of file (mask for following) */
#define S_IFIFO 0010000 /* first-in/first-out (pipe) */
#define S_IFCHR 0020000 /* character-special file */
#define S_IFDIR 0040000 /* directory */
#define S_IFBLK 0060000 /* blocking device (not used on NetWare) */
#define S_IFREG 0100000 /* regular */
#define S_IFLNK 0120000 /* symbolic link (not used on NetWare) */
#define S_IFSOCK 0140000 /* Berkeley socket */

/* POSIX file modes: owner (user) permission */
#define S_IRWXU 0000700
#define S_IRUSR 0000400
#define S_IWUSR 0000200
#define S_IXUSR 0000100

#define S_IREAD S_IRUSR
#define S_IWRITE S_IWUSR
#define S_IEXEC S_IXUSR

/* POSIX file modes: group permission */
#define S_IRWXG 0000070
#define S_IRGRP 0000040
#define S_IWGRP 0000020
#define S_IXGRP 0000010

/* POSIX file modes: other permission */
#define S_IRWXO 0000007
#define S_IROTH 0000004
#define S_IWOTH 0000002
#define S_IXOTH 0000001