blob: b1bf7d3b29dd2b37d89ff1e1e2a4f98e98e7f24a [file] [log] [blame]
/*
* Wireless Tools
*
* Jean II - HPLB 97->99 - HPL 99->01
*
* Common subroutines to all the wireless tools...
*
* This file is released under the GPL license.
* Copyright (c) 1997-2002 Jean Tourrilhes <jt@hpl.hp.com>
*/
/***************************** INCLUDES *****************************/
#include "iwlib.h" /* Header */
#ifdef __ANDROID__
#include <sys/endian.h>
#endif
/************************ CONSTANTS & MACROS ************************/
/* Various versions information */
/* Recommended Wireless Extension version */
#define WE_VERSION 15
/* Version of Wireless Tools */
#define WT_VERSION 25
/*
* Verify a few things about Wireless Extensions.
* I try to maximise backward and forward compatibility, but things are
* tricky because I'm fixing bugs and adding new features.
* Wireless Tools *must* be compiled with the same version of WE
* as the driver. Sometime, the size or layout of some structure changes,
* and might produce interesting results.
* Wireless Tools will usually compile properly against different
* versions of WE, thanks to the zillions of #ifdefs in my code.
* Jean II
*/
#if WIRELESS_EXT < 9
#error "Wireless Extension v9 or newer required :-("
#error "Use Wireless Tools v19 or update your kernel headers !"
#endif
#if WIRELESS_EXT < WE_VERSION && !defined(WEXT_HEADER)
#warning "Wireless Extension earlier than v15 detected,"
#warning "Not all tools features will be compiled in !"
#warning "No worry, I'll try to make the best of it ;-)"
#endif
#if WIRELESS_EXT > WE_VERSION && !defined(WEXT_HEADER)
#warning "Wireless Extension later than v15 detected,"
#warning "Maybe you should get a more recent version"
#warning "of the Wireless Tools package !"
#endif
/**************************** VARIABLES ****************************/
const char * const iw_operation_mode[] = { "Auto",
"Ad-Hoc",
"Managed",
"Master",
"Repeater",
"Secondary",
"Monitor" };
/* Disable runtime version warning in iw_get_range_info() */
int iw_ignore_version = 0;
/************************ SOCKET SUBROUTINES *************************/
/*------------------------------------------------------------------*/
/*
* Open a socket.
* Depending on the protocol present, open the right socket. The socket
* will allow us to talk to the driver.
*/
int
iw_sockets_open(void)
{
static const int families[] = {
AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
};
unsigned int i;
int sock;
/*
* Now pick any (exisiting) useful socket family for generic queries
* Note : don't open all the socket, only returns when one matches,
* all protocols might not be valid.
* Workaround by Jim Kaba <jkaba@sarnoff.com>
* Note : in 99% of the case, we will just open the inet_sock.
* The remaining 1% case are not fully correct...
*/
/* Try all families we support */
for(i = 0; i < sizeof(families)/sizeof(int); ++i)
{
/* Try to open the socket, if success returns it */
sock = socket(families[i], SOCK_DGRAM, 0);
if(sock >= 0)
return sock;
}
return -1;
}
/*------------------------------------------------------------------*/
/*
* Extract the interface name out of /proc/net/wireless or /proc/net/dev.
*/
static inline char *
iw_get_ifname(char * name, /* Where to store the name */
int nsize, /* Size of name buffer */
char * buf) /* Current position in buffer */
{
char * end;
/* Skip leading spaces */
while(isspace(*buf))
buf++;
#ifndef IW_RESTRIC_ENUM
/* Get name up to the last ':'. Aliases may contain ':' in them,
* but the last one should be the separator */
end = strrchr(buf, ':');
#else
/* Get name up to ": "
* Note : we compare to ": " to make sure to process aliased interfaces
* properly. Doesn't work on /proc/net/dev, because it doesn't guarantee
* a ' ' after the ':'*/
end = strstr(buf, ": ");
#endif
/* Not found ??? To big ??? */
if((end == NULL) || (((end - buf) + 1) > nsize))
return(NULL);
/* Copy */
memcpy(name, buf, (end - buf));
name[end - buf] = '\0';
return(end + 2);
}
/*------------------------------------------------------------------*/
/*
* Enumerate devices and call specified routine
* The new way just use /proc/net/wireless, so get all wireless interfaces,
* whether configured or not. This is the default if available.
* The old way use SIOCGIFCONF, so get only configured interfaces (wireless
* or not).
*/
void
iw_enum_devices(int skfd,
iw_enum_handler fn,
char * args[],
int count)
{
char buff[1024];
FILE * fh;
struct ifconf ifc;
struct ifreq *ifr;
int i;
#ifndef IW_RESTRIC_ENUM
/* Check if /proc/net/wireless is available */
fh = fopen(PROC_NET_DEV, "r");
#else
/* Check if /proc/net/wireless is available */
fh = fopen(PROC_NET_WIRELESS, "r");
#endif
if(fh != NULL)
{
/* Success : use data from /proc/net/wireless */
/* Eat 2 lines of header */
fgets(buff, sizeof(buff), fh);
fgets(buff, sizeof(buff), fh);
/* Read each device line */
while(fgets(buff, sizeof(buff), fh))
{
char name[IFNAMSIZ + 1];
char *s;
/* Extract interface name */
s = iw_get_ifname(name, sizeof(name), buff);
if(!s)
/* Failed to parse, complain and continue */
fprintf(stderr, "Cannot parse " PROC_NET_WIRELESS "\n");
else
/* Got it, print info about this interface */
(*fn)(skfd, name, args, count);
}
fclose(fh);
}
else
{
/* Get list of configured devices using "traditional" way */
ifc.ifc_len = sizeof(buff);
ifc.ifc_buf = buff;
if(ioctl(skfd, SIOCGIFCONF, &ifc) < 0)
{
fprintf(stderr, "SIOCGIFCONF: %s\n", strerror(errno));
return;
}
ifr = ifc.ifc_req;
/* Print them */
for(i = ifc.ifc_len / sizeof(struct ifreq); --i >= 0; ifr++)
(*fn)(skfd, ifr->ifr_name, args, count);
}
}
/*********************** WIRELESS SUBROUTINES ************************/
/*------------------------------------------------------------------*/
/*
* Get the range information out of the driver
*/
int
iw_get_range_info(int skfd,
char * ifname,
iwrange * range)
{
struct iwreq wrq;
char buffer[sizeof(iwrange) * 2]; /* Large enough */
/* Cleanup */
memset(buffer, 0, sizeof(buffer));
wrq.u.data.pointer = (caddr_t) buffer;
wrq.u.data.length = sizeof(buffer);
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
return(-1);
/* Copy stuff at the right place, ignore extra */
memcpy((char *) range, buffer, sizeof(iwrange));
/* Lots of people have driver and tools out of sync as far as Wireless
* Extensions are concerned. It's because /usr/include/linux/wireless.h
* and /usr/src/linux/include/linux/wireless.h are different.
* We try to catch this stuff here... */
if(!iw_ignore_version)
{
/* For new versions, we can check the version directly, for old versions
* we use magic. 300 bytes is a also magic number, don't touch... */
if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
{
#if WIRELESS_EXT > 10
/* Version verification - for new versions */
if(range->we_version_compiled != WIRELESS_EXT)
{
fprintf(stderr, "Warning: Driver for device %s has been compiled with version %d\n", ifname, range->we_version_compiled);
fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
fprintf(stderr, "Some things may be broken...\n\n");
}
/* Driver version verification */
if(range->we_version_compiled < range->we_version_source)
{
fprintf(stderr, "Warning: Driver for device %s recommend version %d of Wireless Extension,\n", ifname, range->we_version_source);
fprintf(stderr, "but has been compiled with version %d, therefore some driver features\n", range->we_version_compiled);
fprintf(stderr, "may not be available...\n\n");
}
#endif /* WIRELESS_EXT > 10 */
}
else
{
/* Version verification - for old versions */
if(wrq.u.data.length != sizeof(iwrange))
{
fprintf(stderr, "Warning: Driver for device %s has been compiled with an ancient version\n", ifname);
fprintf(stderr, "of Wireless Extension, while this program is using version %d.\n", WIRELESS_EXT);
fprintf(stderr, "Some things may be broken...\n\n");
}
}
}
/* Don't complain twice.
* In theory, the test apply to each individual driver, but usually
* all drivers are compiled from the same kernel, and most often
* problem is the system/glibc headers. */
iw_ignore_version = 1;
/* Note : we are only trying to catch compile difference, not source.
* If the driver source has not been updated to the latest, it doesn't
* matter because the new fields are set to zero */
return(0);
}
/*------------------------------------------------------------------*/
/*
* Print the WE versions of the interface.
*/
static int
print_iface_version_info(int skfd,
char * ifname,
char * args[], /* Command line args */
int count) /* Args count */
{
struct iwreq wrq;
char buffer[sizeof(iwrange) * 2]; /* Large enough */
struct iw_range * range;
/* Avoid "Unused parameter" warning */
args = args; count = count;
/* Cleanup */
memset(buffer, 0, sizeof(buffer));
wrq.u.data.pointer = (caddr_t) buffer;
wrq.u.data.length = sizeof(buffer);
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWRANGE, &wrq) < 0)
return(-1);
/* Copy stuff at the right place, ignore extra */
range = (struct iw_range *) buffer;
/* For new versions, we can check the version directly, for old versions
* we use magic. 300 bytes is a also magic number, don't touch... */
if((WIRELESS_EXT > 10) && (wrq.u.data.length >= 300))
{
#if WIRELESS_EXT > 10
printf("%-8.8s Recommend Wireless Extension v%d or later,\n",
ifname, range->we_version_source);
printf(" Currently compiled with Wireless Extension v%d.\n\n",
range->we_version_compiled);
#endif /* WIRELESS_EXT > 10 */
}
else
{
#if 0
fprintf(stderr, "%-8.8s no Wireless Extension version information.\n\n",
ifname);
#endif
}
return(0);
}
/*------------------------------------------------------------------*/
/*
* Print the WE versions of the tools.
*/
int
iw_print_version_info(char * toolname)
{
int skfd; /* generic raw socket desc. */
/* Create a channel to the NET kernel. */
if((skfd = iw_sockets_open()) < 0)
{
perror("socket");
return -1;
}
/* Information about the tools themselves */
if(toolname != NULL)
printf("%-8.8s Version %d\n", toolname, WT_VERSION);
printf(" Compatible with Wireless Extension v%d or earlier,\n",
WE_VERSION);
printf(" Currently compiled with Wireless Extension v%d.\n\n",
WIRELESS_EXT);
/* Version for each device */
iw_enum_devices(skfd, &print_iface_version_info, NULL, 0);
close(skfd);
return 0;
}
/*------------------------------------------------------------------*/
/*
* Get information about what private ioctls are supported by the driver
*/
int
iw_get_priv_info(int skfd,
char * ifname,
iwprivargs * priv,
int maxpriv)
{
struct iwreq wrq;
/* Ask the driver */
wrq.u.data.pointer = (caddr_t) priv;
wrq.u.data.length = maxpriv;
wrq.u.data.flags = 0;
//printf("iw_get_priv_info pointer=%08x length=%d\n",wrq.u.data.pointer,wrq.u.data.length);
if(iw_get_ext(skfd, ifname, SIOCGIWPRIV, &wrq) < 0)
return(-1);
//printf("iw_get_priv_info wrq.u.data.length=%d\n",wrq.u.data.length);
/* Return the number of ioctls */
return(wrq.u.data.length);
}
/*------------------------------------------------------------------*/
/*
* Get essential wireless config from the device driver
* We will call all the classical wireless ioctl on the driver through
* the socket to know what is supported and to get the settings...
* Note : compare to the version in iwconfig, we extract only
* what's *really* needed to configure a device...
*/
int
iw_get_basic_config(int skfd,
char * ifname,
wireless_config * info)
{
struct iwreq wrq;
memset((char *) info, 0, sizeof(struct wireless_config));
/* Get wireless name */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
/* If no wireless name : no wireless extensions */
return(-1);
else
{
strncpy(info->name, wrq.u.name, IFNAMSIZ);
info->name[IFNAMSIZ] = '\0';
}
/* Get network ID */
if(iw_get_ext(skfd, ifname, SIOCGIWNWID, &wrq) >= 0)
{
info->has_nwid = 1;
memcpy(&(info->nwid), &(wrq.u.nwid), sizeof(iwparam));
}
/* Get frequency / channel */
if(iw_get_ext(skfd, ifname, SIOCGIWFREQ, &wrq) >= 0)
{
info->has_freq = 1;
info->freq = iw_freq2float(&(wrq.u.freq));
}
/* Get encryption information */
wrq.u.data.pointer = (caddr_t) info->key;
wrq.u.data.length = IW_ENCODING_TOKEN_MAX;
wrq.u.data.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWENCODE, &wrq) >= 0)
{
info->has_key = 1;
info->key_size = wrq.u.data.length;
info->key_flags = wrq.u.data.flags;
}
/* Get ESSID */
wrq.u.essid.pointer = (caddr_t) info->essid;
wrq.u.essid.length = IW_ESSID_MAX_SIZE + 1;
wrq.u.essid.flags = 0;
if(iw_get_ext(skfd, ifname, SIOCGIWESSID, &wrq) >= 0)
{
info->has_essid = 1;
info->essid_on = wrq.u.data.flags;
}
/* Get operation mode */
if(iw_get_ext(skfd, ifname, SIOCGIWMODE, &wrq) >= 0)
{
info->mode = wrq.u.mode;
if((info->mode < 6) && (info->mode >= 0))
info->has_mode = 1;
}
return(0);
}
/*------------------------------------------------------------------*/
/*
* Set essential wireless config in the device driver
* We will call all the classical wireless ioctl on the driver through
* the socket to know what is supported and to set the settings...
* We support only the restricted set as above...
*/
int
iw_set_basic_config(int skfd,
char * ifname,
wireless_config * info)
{
struct iwreq wrq;
int ret = 0;
/* Get wireless name (check if interface is valid) */
if(iw_get_ext(skfd, ifname, SIOCGIWNAME, &wrq) < 0)
/* If no wireless name : no wireless extensions */
return(-2);
/* Set Network ID, if available (this is for non-802.11 cards) */
if(info->has_nwid)
{
memcpy(&(wrq.u.nwid), &(info->nwid), sizeof(iwparam));
wrq.u.nwid.fixed = 1; /* Hum... When in Rome... */
if(iw_set_ext(skfd, ifname, SIOCSIWNWID, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWNWID: %s\n", strerror(errno));
ret = -1;
}
}
/* Set frequency / channel */
if(info->has_freq)
{
iw_float2freq(info->freq, &(wrq.u.freq));
if(iw_set_ext(skfd, ifname, SIOCSIWFREQ, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWFREQ: %s\n", strerror(errno));
ret = -1;
}
}
/* Set encryption information */
if(info->has_key)
{
int flags = info->key_flags;
/* Check if there is a key index */
if((flags & IW_ENCODE_INDEX) > 0)
{
/* Set the index */
wrq.u.data.pointer = (caddr_t) NULL;
wrq.u.data.flags = (flags & (IW_ENCODE_INDEX)) | IW_ENCODE_NOKEY;
wrq.u.data.length = 0;
if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
errno, strerror(errno));
ret = -1;
}
}
/* Mask out index to minimise probability of reject when setting key */
flags = flags & (~IW_ENCODE_INDEX);
/* Set the key itself (set current key in this case) */
wrq.u.data.pointer = (caddr_t) info->key;
wrq.u.data.length = info->key_size;
wrq.u.data.flags = flags;
if(iw_set_ext(skfd, ifname, SIOCSIWENCODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWENCODE(%d): %s\n",
errno, strerror(errno));
ret = -1;
}
}
/* Set ESSID (extended network), if available */
if(info->has_essid)
{
wrq.u.essid.pointer = (caddr_t) info->essid;
wrq.u.essid.length = strlen(info->essid) + 1;
wrq.u.data.flags = info->essid_on;
if(iw_set_ext(skfd, ifname, SIOCSIWESSID, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWESSID: %s\n", strerror(errno));
ret = -1;
}
}
/* Set the current mode of operation */
if(info->has_mode)
{
strncpy(wrq.ifr_name, ifname, IFNAMSIZ-1);
wrq.ifr_name[IFNAMSIZ-1] = '\0';
wrq.u.mode = info->mode;
if(iw_get_ext(skfd, ifname, SIOCSIWMODE, &wrq) < 0)
{
fprintf(stderr, "SIOCSIWMODE: %s\n", strerror(errno));
ret = -1;
}
}
return(ret);
}
/*********************** PROTOCOL SUBROUTINES ***********************/
/*
* Fun stuff with protocol identifiers (SIOCGIWNAME).
* We assume that drivers are returning sensible values in there,
* which is not always the case :-(
*/
/*------------------------------------------------------------------*/
/*
* Compare protocol identifiers.
* We don't want to know if the two protocols are the exactly same,
* but if they interoperate at some level, and also if they accept the
* same type of config (ESSID vs NWID, freq...).
* This is supposed to work around the alphabet soup.
* Return 1 if protocols are compatible
*/
int
iw_protocol_compare(char * protocol1,
char * protocol2)
{
char * dot11 = "IEEE 802.11";
char * dot11_ds = "Dbg";
/* If the strings are the same -> easy */
if(!strncmp(protocol1, protocol2, IFNAMSIZ))
return(1);
/* Are we dealing with one of the 802.11 variant ? */
if( (!strncmp(protocol1, dot11, strlen(dot11))) &&
(!strncmp(protocol2, dot11, strlen(dot11))) )
{
char * sub1 = protocol1 + strlen(dot11);
char * sub2 = protocol2 + strlen(dot11);
/* Skip optional separator */
if(*sub1 == '-')
sub1++;
if(*sub2 == '-')
sub2++;
/* Check if they are both 2.4 GHz Direct Sequence compatible */
if( (strchr(dot11_ds, *sub1) != NULL) &&
(strchr(dot11_ds, *sub2) != NULL) )
return(1);
}
/* Not compatible */
return(0);
}
/********************** FREQUENCY SUBROUTINES ***********************/
/*
* Note : the two functions below are the cause of troubles on
* various embeeded platforms, as they are the reason we require
* libm (math library).
* In this case, please use enable BUILD_NOLIBM in the makefile
*
* FIXME : check negative mantissa and exponent
*/
/*------------------------------------------------------------------*/
/*
* Convert a floating point the our internal representation of
* frequencies.
* The kernel doesn't want to hear about floating point, so we use
* this custom format instead.
*/
void
iw_float2freq(double in,
iwfreq * out)
{
/* Version without libm : slower */
out->e = 0;
while(in > 1e9)
{
in /= 10;
out->e++;
}
out->m = (long) in;
}
/*------------------------------------------------------------------*/
/*
* Convert our internal representation of frequencies to a floating point.
*/
double
iw_freq2float(iwfreq * in)
{
/* Version without libm : slower */
int i;
double res = (double) in->m;
for(i = 0; i < in->e; i++)
res *= 10;
return(res);
}
/*------------------------------------------------------------------*/
/*
* Output a frequency with proper scaling
*/
void
iw_print_freq(char * buffer,
double freq)
{
if(freq < KILO)
sprintf(buffer, "Channel:%g", freq);
else
{
if(freq >= GIGA)
sprintf(buffer, "Frequency:%gGHz", freq / GIGA);
else
{
if(freq >= MEGA)
sprintf(buffer, "Frequency:%gMHz", freq / MEGA);
else
sprintf(buffer, "Frequency:%gkHz", freq / KILO);
}
}
}
/*------------------------------------------------------------------*/
/*
* Convert a frequency to a channel (negative -> error)
*/
int
iw_freq_to_channel(double freq,
struct iw_range * range)
{
double ref_freq;
int k;
/* Check if it's a frequency or not already a channel */
if(freq < KILO)
return(-1);
/* We compare the frequencies as double to ignore differences
* in encoding. Slower, but safer... */
for(k = 0; k < range->num_frequency; k++)
{
ref_freq = iw_freq2float(&(range->freq[k]));
if(freq == ref_freq)
return(range->freq[k].i);
}
/* Not found */
return(-2);
}
/*********************** BITRATE SUBROUTINES ***********************/
/*------------------------------------------------------------------*/
/*
* Output a bitrate with proper scaling
*/
void
iw_print_bitrate(char * buffer,
int bitrate)
{
double rate = bitrate;
if(rate >= GIGA)
sprintf(buffer, "%gGb/s", rate / GIGA);
else
if(rate >= MEGA)
sprintf(buffer, "%gMb/s", rate / MEGA);
else
sprintf(buffer, "%gkb/s", rate / KILO);
}
/************************ POWER SUBROUTINES *************************/
/*------------------------------------------------------------------*/
#ifndef __ANDROID__
/*
* Convert a value in dBm to a value in milliWatt.
*/
int
iw_dbm2mwatt(int in)
{
//return((int) (floor(pow(10.0, (((double) in) / 10.0)))));
}
/*------------------------------------------------------------------*/
/*
* Convert a value in milliWatt to a value in dBm.
*/
int
iw_mwatt2dbm(int in)
{
// return((int) (ceil(10.0 * log10((double) in))));
}
#endif
/********************** STATISTICS SUBROUTINES **********************/
/*------------------------------------------------------------------*/
/*
* Read /proc/net/wireless to get the latest statistics
*/
int
iw_get_stats(int skfd,
char * ifname,
iwstats * stats)
{
#if WIRELESS_EXT > 11
struct iwreq wrq;
wrq.u.data.pointer = (caddr_t) stats;
wrq.u.data.length = 0;
wrq.u.data.flags = 1; /* Clear updated flag */
strncpy(wrq.ifr_name, ifname, IFNAMSIZ-1);
wrq.ifr_name[IFNAMSIZ-1] = '\0';
if(iw_get_ext(skfd, ifname, SIOCGIWSTATS, &wrq) < 0)
return(-1);
return(0);
#else /* WIRELESS_EXT > 11 */
FILE * f = fopen(PROC_NET_WIRELESS, "r");
char buf[256];
char * bp;
int t;
if(f==NULL)
return -1;
/* Loop on all devices */
while(fgets(buf,255,f))
{
bp=buf;
while(*bp&&isspace(*bp))
bp++;
/* Is it the good device ? */
if(strncmp(bp,ifname,strlen(ifname))==0 && bp[strlen(ifname)]==':')
{
/* Skip ethX: */
bp=strchr(bp,':');
bp++;
/* -- status -- */
bp = strtok(bp, " ");
sscanf(bp, "%X", &t);
stats->status = (unsigned short) t;
/* -- link quality -- */
bp = strtok(NULL, " ");
if(strchr(bp,'.') != NULL)
stats->qual.updated |= 1;
sscanf(bp, "%d", &t);
stats->qual.qual = (unsigned char) t;
/* -- signal level -- */
bp = strtok(NULL, " ");
if(strchr(bp,'.') != NULL)
stats->qual.updated |= 2;
sscanf(bp, "%d", &t);
stats->qual.level = (unsigned char) t;
/* -- noise level -- */
bp = strtok(NULL, " ");
if(strchr(bp,'.') != NULL)
stats->qual.updated += 4;
sscanf(bp, "%d", &t);
stats->qual.noise = (unsigned char) t;
/* -- discarded packets -- */
bp = strtok(NULL, " ");
sscanf(bp, "%d", &stats->discard.nwid);
bp = strtok(NULL, " ");
sscanf(bp, "%d", &stats->discard.code);
bp = strtok(NULL, " ");
sscanf(bp, "%d", &stats->discard.misc);
fclose(f);
return 0;
}
}
fclose(f);
return -1;
#endif /* WIRELESS_EXT > 11 */
}
/*------------------------------------------------------------------*/
/*
* Output the link statistics, taking care of formating
*/
void
iw_print_stats(char * buffer,
iwqual * qual,
iwrange * range,
int has_range)
{
/* Just do it */
if(has_range && (qual->level != 0))
{
/* If the statistics are in dBm */
if(qual->level > range->max_qual.level)
{
/* Statistics are in dBm (absolute power measurement) */
sprintf(buffer,
"Quality:%d/%d Signal level:%d dBm Noise level:%d dBm%s",
qual->qual, range->max_qual.qual,
qual->level - 0x100, qual->noise - 0x100,
(qual->updated & 0x7) ? " (updated)" : "");
}
else
{
/* Statistics are relative values (0 -> max) */
sprintf(buffer,
"Quality:%d/%d Signal level:%d/%d Noise level:%d/%d%s",
qual->qual, range->max_qual.qual,
qual->level, range->max_qual.level,
qual->noise, range->max_qual.noise,
(qual->updated & 0x7) ? " (updated)" : "");
}
}
else
{
/* We can't read the range, so we don't know... */
sprintf(buffer, "Quality:%d Signal level:%d Noise level:%d%s",
qual->qual, qual->level, qual->noise,
(qual->updated & 0x7) ? " (updated)" : "");
}
}
/*********************** ENCODING SUBROUTINES ***********************/
/*------------------------------------------------------------------*/
/*
* Output the encoding key, with a nice formating
*/
void
iw_print_key(char * buffer,
unsigned char * key,
int key_size,
int key_flags)
{
int i;
/* Is the key present ??? */
if(key_flags & IW_ENCODE_NOKEY)
{
/* Nope : print on or dummy */
if(key_size <= 0)
strcpy(buffer, "on");
else
{
strcpy(buffer, "**");
buffer +=2;
for(i = 1; i < key_size; i++)
{
if((i & 0x1) == 0)
strcpy(buffer++, "-");
strcpy(buffer, "**");
buffer +=2;
}
}
}
else
{
/* Yes : print the key */
sprintf(buffer, "%.2X", key[0]);
buffer +=2;
for(i = 1; i < key_size; i++)
{
if((i & 0x1) == 0)
strcpy(buffer++, "-");
sprintf(buffer, "%.2X", key[i]);
buffer +=2;
}
}
}
/*------------------------------------------------------------------*/
/*
* Convert a passphrase into a key
* ### NOT IMPLEMENTED ###
* Return size of the key, or 0 (no key) or -1 (error)
*/
int
iw_pass_key(char * input,
unsigned char * key)
{
input = input; key = key;
fprintf(stderr, "Error: Passphrase not implemented\n");
return(-1);
}
/*------------------------------------------------------------------*/
/*
* Parse a key from the command line.
* Return size of the key, or 0 (no key) or -1 (error)
*/
int
iw_in_key(char * input,
unsigned char * key)
{
int keylen = 0;
char * buff;
char * p;
int temp;
/* Check the type of key */
if(!strncmp(input, "s:", 2))
{
/* First case : as an ASCII string (Lucent/Agere cards) */
keylen = strlen(input + 2); /* skip "s:" */
if(keylen > IW_ENCODING_TOKEN_MAX)
keylen = IW_ENCODING_TOKEN_MAX;
strncpy((char *)key, input + 2, keylen);
}
else
if(!strncmp(input, "p:", 2))
{
/* Second case : as a passphrase (PrismII cards) */
return(iw_pass_key(input + 2, key)); /* skip "p:" */
}
else
{
/* Third case : as hexadecimal digits */
buff = malloc(strlen(input) + 1);
if(buff == NULL)
{
fprintf(stderr, "Malloc failed (string too long ?)\n");
return(-1);
}
/* Preserve original buffer */
strcpy(buff, input);
/* Parse */
p = strtok(buff, "-:;.,");
while((p != (char *) NULL) && (keylen < IW_ENCODING_TOKEN_MAX))
{
if(sscanf(p, "%2X", &temp) != 1)
return(-1); /* Error */
key[keylen++] = (unsigned char) (temp & 0xFF);
if(strlen(p) > 2) /* Token not finished yet */
p += 2;
else
p = strtok((char *) NULL, "-:;.,");
}
free(buff);
}
return(keylen);
}
/******************* POWER MANAGEMENT SUBROUTINES *******************/
/*------------------------------------------------------------------*/
/*
* Output a power management value with all attributes...
*/
void
iw_print_pm_value(char * buffer,
int value,
int flags)
{
/* Modifiers */
if(flags & IW_POWER_MIN)
{
strcpy(buffer, " min");
buffer += 4;
}
if(flags & IW_POWER_MAX)
{
strcpy(buffer, " max");
buffer += 4;
}
/* Type */
if(flags & IW_POWER_TIMEOUT)
{
strcpy(buffer, " timeout:");
buffer += 9;
}
else
{
strcpy(buffer, " period:");
buffer += 8;
}
/* Display value without units */
if(flags & IW_POWER_RELATIVE)
sprintf(buffer, "%g", ((double) value) / MEGA);
else
{
/* Display value with units */
if(value >= (int) MEGA)
sprintf(buffer, "%gs", ((double) value) / MEGA);
else
if(value >= (int) KILO)
sprintf(buffer, "%gms", ((double) value) / KILO);
else
sprintf(buffer, "%dus", value);
}
}
/*------------------------------------------------------------------*/
/*
* Output a power management mode
*/
void
iw_print_pm_mode(char * buffer,
int flags)
{
/* Print the proper mode... */
switch(flags & IW_POWER_MODE)
{
case IW_POWER_UNICAST_R:
strcpy(buffer, "mode:Unicast only received");
break;
case IW_POWER_MULTICAST_R:
strcpy(buffer, "mode:Multicast only received");
break;
case IW_POWER_ALL_R:
strcpy(buffer, "mode:All packets received");
break;
case IW_POWER_FORCE_S:
strcpy(buffer, "mode:Force sending");
break;
case IW_POWER_REPEATER:
strcpy(buffer, "mode:Repeat multicasts");
break;
default:
break;
}
}
/***************** RETRY LIMIT/LIFETIME SUBROUTINES *****************/
#if WIRELESS_EXT > 10
/*------------------------------------------------------------------*/
/*
* Output a retry value with all attributes...
*/
void
iw_print_retry_value(char * buffer,
int value,
int flags)
{
/* Modifiers */
if(flags & IW_RETRY_MIN)
{
strcpy(buffer, " min");
buffer += 4;
}
if(flags & IW_RETRY_MAX)
{
strcpy(buffer, " max");
buffer += 4;
}
/* Type lifetime of limit */
if(flags & IW_RETRY_LIFETIME)
{
strcpy(buffer, " lifetime:");
buffer += 10;
/* Display value without units */
if(flags & IW_POWER_RELATIVE)
sprintf(buffer, "%g", ((double) value) / MEGA);
else
{
/* Display value with units */
if(value >= (int) MEGA)
sprintf(buffer, "%gs", ((double) value) / MEGA);
else
if(value >= (int) KILO)
sprintf(buffer, "%gms", ((double) value) / KILO);
else
sprintf(buffer, "%dus", value);
}
}
else
sprintf(buffer, " limit:%d", value);
}
#endif /* WIRELESS_EXT > 10 */
/************************* TIME SUBROUTINES *************************/
/*------------------------------------------------------------------*/
/*
* Print timestamps
* Inspired from irdadump...
*/
void
iw_print_timeval(char * buffer,
const struct timeval * time)
{
int s;
s = (time->tv_sec) % 86400;
sprintf(buffer, "%02d:%02d:%02d.%06u ",
s / 3600, (s % 3600) / 60,
s % 60, (u_int32_t) time->tv_usec);
}
/*********************** ADDRESS SUBROUTINES ************************/
/*
* This section is mostly a cut & past from net-tools-1.2.0
* manage address display and input...
*/
/*------------------------------------------------------------------*/
/*
* Check if interface support the right MAC address type...
*/
int
iw_check_mac_addr_type(int skfd,
char * ifname)
{
struct ifreq ifr;
/* Get the type of hardware address */
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if((ioctl(skfd, SIOCGIFHWADDR, &ifr) < 0) ||
(ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER))
{
/* Deep trouble... */
fprintf(stderr, "Interface %s doesn't support MAC addresses\n",
ifname);
return(-1);
}
#ifdef DEBUG
printf("Hardware : %d - %s\n", ifr.ifr_hwaddr.sa_family,
iw_ether_ntoa((struct ether_addr *) ifr.ifr_hwaddr.sa_data));
#endif
return(0);
}
/*------------------------------------------------------------------*/
/*
* Check if interface support the right interface address type...
*/
int
iw_check_if_addr_type(int skfd,
char * ifname)
{
struct ifreq ifr;
/* Get the type of interface address */
strncpy(ifr.ifr_name, ifname, IFNAMSIZ-1);
ifr.ifr_name[IFNAMSIZ-1] = '\0';
if((ioctl(skfd, SIOCGIFADDR, &ifr) < 0) ||
(ifr.ifr_addr.sa_family != AF_INET))
{
/* Deep trouble... */
fprintf(stderr, "Interface %s doesn't support IP addresses\n", ifname);
return(-1);
}
#ifdef DEBUG
printf("Interface : %d - 0x%lX\n", ifr.ifr_addr.sa_family,
*((unsigned long *) ifr.ifr_addr.sa_data));
#endif
return(0);
}
#if 0
/*------------------------------------------------------------------*/
/*
* Check if interface support the right address types...
*/
int
iw_check_addr_type(int skfd,
char * ifname)
{
/* Check the interface address type */
if(iw_check_if_addr_type(skfd, ifname) < 0)
return(-1);
/* Check the interface address type */
if(iw_check_mac_addr_type(skfd, ifname) < 0)
return(-1);
return(0);
}
#endif
/*------------------------------------------------------------------*/
/*
* Display an Ethernet address in readable format.
*/
void
iw_ether_ntop(const struct ether_addr* eth, char* buf)
{
sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
eth->ether_addr_octet[0], eth->ether_addr_octet[1],
eth->ether_addr_octet[2], eth->ether_addr_octet[3],
eth->ether_addr_octet[4], eth->ether_addr_octet[5]);
}
/*------------------------------------------------------------------*/
/*
* Display an Ethernet address in readable format.
* Same with a static buffer
*/
char *
iw_ether_ntoa(const struct ether_addr* eth)
{
static char buf[20];
iw_ether_ntop(eth, buf);
return buf;
}
/*------------------------------------------------------------------*/
/*
* Input an Ethernet address and convert to binary.
*/
int
iw_ether_aton(const char *orig, struct ether_addr *eth)
{
const char *bufp;
int i;
i = 0;
for(bufp = orig; *bufp != '\0'; ++bufp) {
unsigned int val;
unsigned char c = *bufp++;
if (isdigit(c)) val = c - '0';
else if (c >= 'a' && c <= 'f') val = c - 'a' + 10;
else if (c >= 'A' && c <= 'F') val = c - 'A' + 10;
else break;
val <<= 4;
c = *bufp++;
if (isdigit(c)) val |= c - '0';
else if (c >= 'a' && c <= 'f') val |= c - 'a' + 10;
else if (c >= 'A' && c <= 'F') val |= c - 'A' + 10;
else break;
eth->ether_addr_octet[i] = (unsigned char) (val & 0377);
if(++i == ETH_ALEN) {
/* That's it. Any trailing junk? */
if (*bufp != '\0') {
#ifdef DEBUG
fprintf(stderr, "iw_ether_aton(%s): trailing junk!\n", orig);
errno = EINVAL;
return(0);
#endif
}
#ifdef DEBUG
fprintf(stderr, "iw_ether_aton(%s): %s\n",
orig, ether_ntoa(eth));
#endif
return(1);
}
if (*bufp != ':')
break;
}
#ifdef DEBUG
fprintf(stderr, "iw_ether_aton(%s): invalid ether address!\n", orig);
#endif
errno = EINVAL;
return(0);
}
/*------------------------------------------------------------------*/
/*
* Input an Internet address and convert to binary.
*/
int
iw_in_inet(char *name, struct sockaddr *sap)
{
struct hostent *hp;
struct netent *np;
struct sockaddr_in *sin = (struct sockaddr_in *) sap;
/* Grmpf. -FvK */
sin->sin_family = AF_INET;
sin->sin_port = 0;
/* Default is special, meaning 0.0.0.0. */
if (!strcmp(name, "default")) {
sin->sin_addr.s_addr = INADDR_ANY;
return(1);
}
/* Try the NETWORKS database to see if this is a known network. */
if ((np = getnetbyname(name)) != (struct netent *)NULL) {
sin->sin_addr.s_addr = htonl(np->n_net);
strcpy(name, np->n_name);
return(1);
}
/* Always use the resolver (DNS name + IP addresses) */
if ((hp = gethostbyname(name)) == (struct hostent *)NULL) {
errno = h_errno;
return(-1);
}
memcpy((char *) &sin->sin_addr, (char *) hp->h_addr_list[0], hp->h_length);
strcpy(name, hp->h_name);
return(0);
}
/*------------------------------------------------------------------*/
/*
* Input an address and convert to binary.
*/
int
iw_in_addr(int skfd,
char * ifname,
char * bufp,
struct sockaddr *sap)
{
/* Check if it is a hardware or IP address */
if(index(bufp, ':') == NULL)
{
struct sockaddr if_address;
struct arpreq arp_query;
/* Check if we have valid interface address type */
if(iw_check_if_addr_type(skfd, ifname) < 0)
{
fprintf(stderr, "%-8.8s Interface doesn't support IP addresses\n", ifname);
return(-1);
}
/* Read interface address */
if(iw_in_inet(bufp, &if_address) < 0)
{
fprintf(stderr, "Invalid interface address %s\n", bufp);
return(-1);
}
/* Translate IP addresses to MAC addresses */
memcpy((char *) &(arp_query.arp_pa),
(char *) &if_address,
sizeof(struct sockaddr));
arp_query.arp_ha.sa_family = 0;
arp_query.arp_flags = 0;
/* The following restrict the search to the interface only */
/* For old kernels which complain, just comment it... */
strncpy(arp_query.arp_dev, ifname, IFNAMSIZ-1);
arp_query.arp_dev[IFNAMSIZ-1] = '\0';
if((ioctl(skfd, SIOCGARP, &arp_query) < 0) ||
!(arp_query.arp_flags & ATF_COM))
{
fprintf(stderr, "Arp failed for %s on %s... (%d)\nTry to ping the address before setting it.\n",
bufp, ifname, errno);
return(-1);
}
/* Store new MAC address */
memcpy((char *) sap,
(char *) &(arp_query.arp_ha),
sizeof(struct sockaddr));
#ifdef DEBUG
printf("IP Address %s => Hw Address = %s\n",
bufp, iw_ether_ntoa((struct ether_addr *) sap->sa_data));
#endif
}
else /* If it's an hardware address */
{
/* Check if we have valid mac address type */
if(iw_check_mac_addr_type(skfd, ifname) < 0)
{
fprintf(stderr, "%-8.8s Interface doesn't support MAC addresses\n", ifname);
return(-1);
}
/* Get the hardware address */
if(iw_in_ether(bufp, sap) < 0)
{
fprintf(stderr, "Invalid hardware address %s\n", bufp);
return(-1);
}
}
#ifdef DEBUG
printf("Hw Address = %s\n", iw_ether_ntoa((struct ether_addr *) sap->sa_data));
#endif
return(0);
}
/************************* MISC SUBROUTINES **************************/
/* Size (in bytes) of various events */
static const int priv_type_size[] = {
0, /* IW_PRIV_TYPE_NONE */
1, /* IW_PRIV_TYPE_BYTE */
1, /* IW_PRIV_TYPE_CHAR */
0, /* Not defined */
sizeof(__u32), /* IW_PRIV_TYPE_INT */
sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */
sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */
0, /* Not defined */
};
/*------------------------------------------------------------------*/
/*
* Max size in bytes of an private argument.
*/
int
iw_get_priv_size(int args)
{
int num = args & IW_PRIV_SIZE_MASK;
int type = (args & IW_PRIV_TYPE_MASK) >> 12;
return(num * priv_type_size[type]);
}
/************************ EVENT SUBROUTINES ************************/
/*
* The Wireless Extension API 14 and greater define Wireless Events,
* that are used for various events and scanning.
* Those functions help the decoding of events, so are needed only in
* this case.
*/
#if WIRELESS_EXT > 13
/* Type of headers we know about (basically union iwreq_data) */
#define IW_HEADER_TYPE_NULL 0 /* Not available */
#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */
#define IW_HEADER_TYPE_UINT 4 /* __u32 */
#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */
#define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */
#define IW_HEADER_TYPE_POINT 8 /* struct iw_point */
#define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */
#define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */
/* Headers for the various requests */
static const char standard_ioctl_hdr[] = {
IW_HEADER_TYPE_NULL, /* SIOCSIWCOMMIT */
IW_HEADER_TYPE_CHAR, /* SIOCGIWNAME */
IW_HEADER_TYPE_PARAM, /* SIOCSIWNWID */
IW_HEADER_TYPE_PARAM, /* SIOCGIWNWID */
IW_HEADER_TYPE_FREQ, /* SIOCSIWFREQ */
IW_HEADER_TYPE_FREQ, /* SIOCGIWFREQ */
IW_HEADER_TYPE_UINT, /* SIOCSIWMODE */
IW_HEADER_TYPE_UINT, /* SIOCGIWMODE */
IW_HEADER_TYPE_PARAM, /* SIOCSIWSENS */
IW_HEADER_TYPE_PARAM, /* SIOCGIWSENS */
IW_HEADER_TYPE_NULL, /* SIOCSIWRANGE */
IW_HEADER_TYPE_POINT, /* SIOCGIWRANGE */
IW_HEADER_TYPE_NULL, /* SIOCSIWPRIV */
IW_HEADER_TYPE_POINT, /* SIOCGIWPRIV */
IW_HEADER_TYPE_NULL, /* SIOCSIWSTATS */
IW_HEADER_TYPE_POINT, /* SIOCGIWSTATS */
IW_HEADER_TYPE_POINT, /* SIOCSIWSPY */
IW_HEADER_TYPE_POINT, /* SIOCGIWSPY */
IW_HEADER_TYPE_NULL, /* -- hole -- */
IW_HEADER_TYPE_NULL, /* -- hole -- */
IW_HEADER_TYPE_ADDR, /* SIOCSIWAP */
IW_HEADER_TYPE_ADDR, /* SIOCGIWAP */
IW_HEADER_TYPE_NULL, /* -- hole -- */
IW_HEADER_TYPE_POINT, /* SIOCGIWAPLIST */
IW_HEADER_TYPE_PARAM, /* SIOCSIWSCAN */
IW_HEADER_TYPE_POINT, /* SIOCGIWSCAN */
IW_HEADER_TYPE_POINT, /* SIOCSIWESSID */
IW_HEADER_TYPE_POINT, /* SIOCGIWESSID */
IW_HEADER_TYPE_POINT, /* SIOCSIWNICKN */
IW_HEADER_TYPE_POINT, /* SIOCGIWNICKN */
IW_HEADER_TYPE_NULL, /* -- hole -- */
IW_HEADER_TYPE_NULL, /* -- hole -- */
IW_HEADER_TYPE_PARAM, /* SIOCSIWRATE */
IW_HEADER_TYPE_PARAM, /* SIOCGIWRATE */
IW_HEADER_TYPE_PARAM, /* SIOCSIWRTS */
IW_HEADER_TYPE_PARAM, /* SIOCGIWRTS */
IW_HEADER_TYPE_PARAM, /* SIOCSIWFRAG */
IW_HEADER_TYPE_PARAM, /* SIOCGIWFRAG */
IW_HEADER_TYPE_PARAM, /* SIOCSIWTXPOW */
IW_HEADER_TYPE_PARAM, /* SIOCGIWTXPOW */
IW_HEADER_TYPE_PARAM, /* SIOCSIWRETRY */
IW_HEADER_TYPE_PARAM, /* SIOCGIWRETRY */
IW_HEADER_TYPE_POINT, /* SIOCSIWENCODE */
IW_HEADER_TYPE_POINT, /* SIOCGIWENCODE */
IW_HEADER_TYPE_PARAM, /* SIOCSIWPOWER */
IW_HEADER_TYPE_PARAM, /* SIOCGIWPOWER */
};
static const unsigned int standard_ioctl_num = sizeof(standard_ioctl_hdr);
/*
* Meta-data about all the additional standard Wireless Extension events
* we know about.
*/
static const char standard_event_hdr[] = {
IW_HEADER_TYPE_ADDR, /* IWEVTXDROP */
IW_HEADER_TYPE_QUAL, /* IWEVQUAL */
IW_HEADER_TYPE_POINT, /* IWEVCUSTOM */
IW_HEADER_TYPE_ADDR, /* IWEVREGISTERED */
IW_HEADER_TYPE_ADDR, /* IWEVEXPIRED */
};
static const unsigned int standard_event_num = sizeof(standard_event_hdr);
/* Size (in bytes) of various events */
static const int event_type_size[] = {
IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */
0,
IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */
0,
IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */
IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */
IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */
0,
IW_EV_POINT_LEN, /* Without variable payload */
IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */
IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */
};
/*------------------------------------------------------------------*/
/*
* Initialise the struct stream_descr so that we can extract
* individual events from the event stream.
*/
void
iw_init_event_stream(struct stream_descr * stream, /* Stream of events */
char * data,
int len)
{
/* Cleanup */
memset((char *) stream, '\0', sizeof(struct stream_descr));
/* Set things up */
stream->current = data;
stream->end = data + len;
}
/*------------------------------------------------------------------*/
/*
* Extract the next event from the event stream.
*/
int
iw_extract_event_stream(struct stream_descr * stream, /* Stream of events */
struct iw_event * iwe) /* Extracted event */
{
int event_type = 0;
unsigned int event_len = 1; /* Invalid */
char * pointer;
/* Don't "optimise" the following variable, it will crash */
unsigned cmd_index; /* *MUST* be unsigned */
/* Check for end of stream */
if((stream->current + IW_EV_LCP_LEN) > stream->end)
return(0);
#if 0
printf("DBG - stream->current = %p, stream->value = %p, stream->end = %p\n",
stream->current, stream->value, stream->end);
#endif
/* Extract the event header (to get the event id).
* Note : the event may be unaligned, therefore copy... */
memcpy((char *) iwe, stream->current, IW_EV_LCP_LEN);
#if 0
printf("DBG - iwe->cmd = 0x%X, iwe->len = %d\n",
iwe->cmd, iwe->len);
#endif
/* Check invalid events */
if(iwe->len <= IW_EV_LCP_LEN)
return(-1);
/* Get the type and length of that event */
if(iwe->cmd <= SIOCIWLAST)
{
cmd_index = iwe->cmd - SIOCIWFIRST;
if(cmd_index < standard_ioctl_num)
event_type = standard_ioctl_hdr[cmd_index];
}
else
{
cmd_index = iwe->cmd - IWEVFIRST;
if(cmd_index < standard_event_num)
event_type = standard_event_hdr[cmd_index];
}
/* Unknown events -> event_type=0 => IW_EV_LCP_LEN */
event_len = event_type_size[event_type];
/* Check if we know about this event */
if(event_len <= IW_EV_LCP_LEN)
{
/* Skip to next event */
stream->current += iwe->len;
return(2);
}
event_len -= IW_EV_LCP_LEN;
/* Set pointer on data */
if(stream->value != NULL)
pointer = stream->value; /* Next value in event */
else
pointer = stream->current + IW_EV_LCP_LEN; /* First value in event */
#if 0
printf("DBG - event_type = %d, event_len = %d, pointer = %p\n",
event_type, event_len, pointer);
#endif
/* Copy the rest of the event (at least, fixed part) */
if((pointer + event_len) > stream->end)
{
/* Go to next event */
stream->current += iwe->len;
return(-2);
}
memcpy((char *) iwe + IW_EV_LCP_LEN, pointer, event_len);
/* Skip event in the stream */
pointer += event_len;
/* Special processing for iw_point events */
if(event_type == IW_HEADER_TYPE_POINT)
{
/* Check the length of the payload */
if((iwe->len - (event_len + IW_EV_LCP_LEN)) > 0)
/* Set pointer on variable part (warning : non aligned) */
iwe->u.data.pointer = pointer;
else
/* No data */
iwe->u.data.pointer = NULL;
/* Go to next event */
stream->current += iwe->len;
}
else
{
/* Is there more value in the event ? */
if((pointer + event_len) <= (stream->current + iwe->len))
/* Go to next value */
stream->value = pointer;
else
{
/* Go to next event */
stream->value = NULL;
stream->current += iwe->len;
}
}
return(1);
}
#endif /* WIRELESS_EXT > 13 */