/****************************************************************************
 * Copyright (C) 2004 Leonid Zolotarev
 *
 * Licensed under the terms of the BSD license, see file COPYING
 * for details.
 *
 * ACX Library
 *
 * $Id: acxlib.c,v 1.5 2004/10/17 20:53:24 leoz Exp $
 ***************************************************************************/

#include <stdlib.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net80211/ieee80211.h>
#include <net80211/ieee80211_ioctl.h>
#include <dev/wi/if_wavelan_ieee.h>
#include <acx/if_acxioctl.h>
#include "acxlib.h"

#define ACX_IFACE_NAME    "acx0"

char* _acx_iface_name = NULL;
int   _acx_iface_sock = -1;

char* acx_interface_default ()
{
	/* FIX ME ! */
	return ACX_IFACE_NAME;
}

int acx_interface_open ( const char* name )
{
	if ( ! _acx_iface_name && name ) {
		_acx_iface_name = malloc ( sizeof ( char ) * IFNAMSIZ );
		bzero ( _acx_iface_name, sizeof ( char ) * IFNAMSIZ );
		memcpy ( _acx_iface_name, name, sizeof ( name ) );
	}
	if ( _acx_iface_sock == -1 ) {
		_acx_iface_sock = socket ( AF_INET, SOCK_DGRAM, 0 );
	}
	if ( _acx_iface_name && _acx_iface_sock > -1 ) {
		return ACX_NO_ERROR;
	}
	return ACX_IFACE_OPEN_ERROR;
}

int acx_interface_close ()
{
	if ( _acx_iface_name ) {
		free ( _acx_iface_name );
		_acx_iface_name = NULL;
	}
	if ( _acx_iface_sock > -1 ) {
		close ( _acx_iface_sock );
		_acx_iface_sock = -1;
	}
	return ACX_NO_ERROR;
}

/*
 * Set/Get
 */
int acx_sgioc ( u_int16_t i_type, void *i_data )
{
    struct ieee80211req	ireq;      
    bzero ( &ireq, sizeof ( ireq ) );
    strncpy ( ireq.i_name, _acx_iface_name, IFNAMSIZ );

    ireq.i_data = (void *)    i_data;
    ireq.i_type = (u_int16_t) i_type;
    ireq.i_val  = (int16_t)   0;
    ireq.i_len  = (int16_t)   0;

    if ( ioctl ( _acx_iface_sock, ACX_IOCTL_SGIOC, &ireq ) < 0 ) {
        return ACX_IOCTL_ERROR;
    }
    return ACX_NO_ERROR;
}

/*
 * Get
 */
int net_wi_gioc ( struct wi_req *wreq )
{
	struct ifreq ifr;
    bzero ( &ifr, sizeof ( ifr ) );
    strncpy ( ifr.ifr_name, _acx_iface_name, IFNAMSIZ );

	ifr.ifr_data = ( caddr_t ) wreq;

    if ( ioctl ( _acx_iface_sock, SIOCGWAVELAN, &ifr ) < 0 ) {
        return ACX_IOCTL_ERROR;
    }
    return ACX_NO_ERROR;
}

/*
 * Calculate level as windows driver (feb 2003)
 */
int acx_user_level ( int rawlevel )
{
	int winlevel= ( int ) ( 0.5 + 0.625 * rawlevel );
	if ( winlevel > 100 ) {
		winlevel = 100;
	}
	return winlevel;
}

int acx_interface_get_stat ( acx_interface_stat* stat )
{
    int result = ACX_NO_ERROR;
    struct acx_ioctl_stats stats;
    bzero ( &stats, sizeof ( stats ) );
	if ( stat ) {
	    bzero ( stat, sizeof ( acx_interface_stat ) );
	    result = acx_sgioc ( ACX_IOCTL_GET_STATS, &stats );
		if ( result == ACX_NO_ERROR ) {
			stat->is_strength       = stats.rx_levelcurrent;
			stat->is_strength_avg   = stats.rx_level / stats.rx_count;
			stat->is_strength_max   = stats.rx_levelmax;
			stat->is_noise          = stats.rx_snrcurrent;
			stat->is_noise_avg      = stats.rx_snr   / stats.rx_count;
			stat->is_noise_max      = stats.rx_snrmax;
			stat->is_bytes_received = stats.rx_bytes;
			stat->is_bytes_sent     = stats.tx_bytes;
			stat->is_rates          = stats.tx_rates;
		}
	}
	return result;
}

int acx_rate_supported ( acx_rate rate, int rates )
{
	int result = 0;
	switch ( rate ) {
	case acx_rate_1:
		result = ( ( rates & ACX_IOCTL_1MBPS ) == ACX_IOCTL_1MBPS );
		break;
	case acx_rate_2:
		result = ( ( rates & ACX_IOCTL_2MBPS ) == ACX_IOCTL_2MBPS );
		break;
	case acx_rate_5:
		result = ( ( rates & ACX_IOCTL_5MBPS ) == ACX_IOCTL_5MBPS );
		break;
	case acx_rate_11:
		result = ( ( rates & ACX_IOCTL_11MBPS ) == ACX_IOCTL_11MBPS );
		break;
	case acx_rate_22:
		result = ( ( rates & ACX_IOCTL_22MBPS ) == ACX_IOCTL_22MBPS );
		break;
	default:
		break;
	}
	return result;
}

#define ACX_BSSID_LEN 20

char* net_wi_get_bssid ()
{
	int i = 0;

    static char bssid [ ACX_BSSID_LEN + 1 ];
    static char buff  [ ACX_BSSID_LEN + 1 ];
	static unsigned char* c = NULL;

    struct wi_req wreq;
    bzero ( &wreq, sizeof ( wreq ) );
	wreq.wi_len  = WI_MAX_DATALEN;
	wreq.wi_type = WI_RID_CURRENT_BSSID;

    bzero ( bssid, sizeof ( bssid ) );

	if ( net_wi_gioc ( &wreq ) == ACX_NO_ERROR ) {
		c = ( unsigned char* ) &wreq.wi_val;

		for ( i = 0; i < (wreq.wi_len - 1) * 2; i++ ) {
		    bzero   ( buff, sizeof ( buff ) );
			sprintf ( buff, "%02X", c [ i ] );
			strcat ( bssid, buff );
			if ( i < ( ( wreq.wi_len - 1 ) * 2 ) - 1 ) {
				strcat ( bssid, ":" );
			}
		}
	}

	return bssid;
}

#define ACX_BSSID_EMPTY "00:00:00:00:00:00"

char* net_wi_get_status ( const char* bssid )
{
	if ( strcmp ( bssid, ACX_BSSID_EMPTY ) != 0 ) {
		return "Associated";
	}
	return "Not Associated";
}

#define ACX_BUFF_LEN 10

int net_wi_words_to_int ( struct wi_req *wreq )
{
	int i = 0;

	static char buff_res [ ACX_BUFF_LEN ];
	static char buff_tmp [ ACX_BUFF_LEN ];
	bzero ( buff_res, sizeof ( buff_res ) );

	for ( i = 0; i < wreq->wi_len - 1; i++) {
		bzero ( buff_tmp, sizeof ( buff_tmp ) );
		sprintf ( buff_tmp, "%d", wreq->wi_val [ i ] );
		strcat  ( buff_res, buff_tmp );
	}
	return strtoimax ( buff_res, NULL, 10 );
}

int net_wi_get_txrate ()
{
    struct wi_req wreq;
    bzero ( &wreq, sizeof ( wreq ) );
	wreq.wi_len  = WI_MAX_DATALEN;
	wreq.wi_type = WI_RID_CUR_TX_RATE;

	if ( net_wi_gioc ( &wreq ) == ACX_NO_ERROR ) {
		return net_wi_words_to_int ( &wreq );
	}

	return 0;
}

int net_wi_get_channel ()
{
    struct wi_req wreq;
    bzero ( &wreq, sizeof ( wreq ) );
	wreq.wi_len  = WI_MAX_DATALEN;
	wreq.wi_type = WI_RID_CURRENT_CHAN;

	if ( net_wi_gioc ( &wreq ) == ACX_NO_ERROR ) {
		return net_wi_words_to_int ( &wreq );
	}

	return 0;
}

char* net_80211_get_ssid ()
{
    static char ssid [ IEEE80211_NWID_LEN + 1 ];

    struct ieee80211req	ireq;      
    bzero ( &ireq, sizeof ( ireq ) );
    strncpy ( ireq.i_name, _acx_iface_name, IFNAMSIZ );

    bzero ( ssid, sizeof ( ssid ) );

    ireq.i_data = &ssid;                /* buffer for SSID */
    ireq.i_type = IEEE80211_IOC_SSID;   /* request SSID */
    ireq.i_val  = -1;                   /* require active SSID */
    if ( ioctl ( _acx_iface_sock, SIOCG80211, &ireq ) < 0 ) {
		/* Error */
	    return ssid;
	}
    ssid [ ireq.i_len ] = '\0';
    return ssid;
}

/***************************************************************************/

char* net_get_media_status ()
{
	struct ifmediareq ireq;
    bzero ( &ireq, sizeof ( ireq ) );
    strncpy ( ireq.ifm_name, _acx_iface_name, IFNAMSIZ );

    if ( ioctl ( _acx_iface_sock, SIOCGIFMEDIA, &ireq ) < 0)
        return "Error";

    if ( ! ( ireq.ifm_status & IFM_AVALID ) )
        return "Invalid";

    if ( ! ( ireq.ifm_status & IFM_ACTIVE ) )
        return "Inactive";

    return "Active";
}

/***************************************************************************/

int acx_get_clockrate ()
{
	u_int16_t value = 0;
	size_t    len   = sizeof ( value );
	sysctlbyname ( "kern.clockrate",
                   (void *) &value, &len , NULL, 0 );
	return value;
}

/***************************************************************************/

typedef enum acx_sysctl {
	acx_sysctl_starts_fast = 0,
	acx_sysctl_scan_period,
	acx_sysctl_threshold,
	acx_sysctl_min_time,
	acx_sysctl_max_time
} acx_sysctl;

/***************************************************************************/

const char* _acx_sysctl_name [] = {
	"hw.acx.starts_fast",
	"hw.acx.scan_period",
	"hw.acx.threshold"  ,
	"hw.acx.min_time"   ,
	"hw.acx.max_time"
};

/***************************************************************************/

int acx_get_starts_fast ()
{
	int    value = 0;
	size_t len   = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_starts_fast ],
                   (void *) &value, &len , NULL, 0 );
	return value;
}

int acx_get_scan_period ()
{
	int    rate  = acx_get_clockrate ();
	int    value = 0;
	size_t len   = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_scan_period ],
                   (void *) &value, &len , NULL, 0 );
	if ( rate != 0 ) {
		/* convert to milliseconds */
		value = ( value * 1000 ) / rate;
	}
	return value;
}

int acx_get_threshold ()
{
	int    value = 0;
	size_t len   = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_threshold ],
                   (void *) &value, &len , NULL, 0 );
	return value;
}

int acx_get_min_time ()
{
	int    value = 0;
	size_t len   = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_min_time ],
                   (void *) &value, &len , NULL, 0 );
	return value;
}

int acx_get_max_time ()
{
	int    value = 0;
	size_t len   = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_max_time ],
                   (void *) &value, &len , NULL, 0 );
	return value;
}

/***************************************************************************/

void acx_set_starts_fast ( int value )
{
	size_t len = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_starts_fast ],
                   NULL, NULL, &value, len );
}

void acx_set_scan_period ( int value )
{
	int    rate  = acx_get_clockrate ();
	size_t len   = sizeof ( value );
	/* convert from milliseconds */
	value = ( value * rate ) / 1000;
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_scan_period ],
                   NULL, NULL, &value, len );
}

void acx_set_threshold ( int value )
{
	size_t len = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_threshold ],
                   NULL, NULL, &value, len );
}

void acx_set_min_time ( int value )
{
	size_t len = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_min_time ],
                   NULL, NULL, &value, len );
}

void acx_set_max_time ( int value )
{
	size_t len = sizeof ( value );
	sysctlbyname ( _acx_sysctl_name [ acx_sysctl_max_time ],
                   NULL, NULL, &value, len );
}

/***************************************************************************/


syntax highlighted by Code2HTML, v. 0.9.1