/* $Id: wi_bsd.c 562 2004-12-03 18:29:41Z benny $ */ /*- * Copyright (c) 2003 Benedikt Meurer <benny@xfce.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) #include <sys/types.h> #include <sys/cdefs.h> #include <sys/param.h> #include <sys/socket.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <net/if.h> #include <net/if_media.h> #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include <net/if_var.h> #include <net/ethernet.h> #include <dev/wi/if_wavelan_ieee.h> #if __FreeBSD_version >= 500033 #include <sys/endian.h> #endif #else #include <netinet/in.h> #include <netinet/if_ether.h> #ifdef __NetBSD__ #include <net80211/ieee80211.h> #include <net80211/ieee80211_ioctl.h> #include <dev/ic/wi_ieee.h> #else #if !defined(__OpenBSD__) #include <dev/pcmcia/if_wavelan_ieee.h> #endif #endif #ifdef __OpenBSD__ #include <net/if_ieee80211.h> #include <dev/ic/if_wi_ieee.h> #define le16toh(x) letoh16(x) #endif #endif #if defined(__GLIBC__) #define strlcpy(dst, src, size) g_strlcpy(dst, src, size) #include <byteswap.h> #include <endian.h> #if __BYTE_ORDER == __LITTLE_ENDIAN #define le16toh(x) ((uint16_t)(x)) #elif __BYTE_ORDER == __BIG_ENDIAN #define le16toh(x) bswap16((x)) #else #error unknown ENDIAN #endif #endif /* __GLIBC__ */ #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <err.h> #include <wi.h> struct wi_device { char interface[WI_MAXSTRLEN]; int socket; }; static int _wi_carrier(const struct wi_device *); static int _wi_getval(const struct wi_device *, struct wi_req *); static int _wi_vendor(const struct wi_device *, char *, size_t); static int _wi_netname(const struct wi_device *, char *, size_t); static int _wi_quality(const struct wi_device *, int *); static int _wi_rate(const struct wi_device *, int *); struct wi_device * wi_open(const char *interface) { struct wi_device *device = NULL; if (interface != NULL) { if ((device = (struct wi_device *)calloc(1, sizeof(*device))) != NULL) { strlcpy(device->interface, interface, WI_MAXSTRLEN); if ((device->socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { free(device); device = NULL; } } } return(device); } void wi_close(struct wi_device *device) { if (device != NULL) { close(device->socket); free(device); } } int wi_query(struct wi_device *device, struct wi_stats *stats) { int result; if (device == NULL || stats == NULL) return(WI_INVAL); /* clear stats first */ bzero((void *)stats, sizeof(*stats)); /* check vendor (independent of carrier state) */ if ((result = _wi_vendor(device, stats->ws_vendor, WI_MAXSTRLEN)) != WI_OK) return(result); /* check carrier */ if ((result = _wi_carrier(device)) != WI_OK) return(result); else { /* check netname (depends on carrier state) */ if ((result = _wi_netname(device,stats->ws_netname, WI_MAXSTRLEN)) != WI_OK) return(result); /* check quality (depends on carrier state) */ if ((result = _wi_quality(device, &stats->ws_quality)) != WI_OK) return(result); /* check rate (depends on carrier state) */ if ((result = _wi_rate(device, &stats->ws_rate)) != WI_OK) return(result); /* everything ok, stats are up-to-date */ return(WI_OK); } } static int _wi_carrier(const struct wi_device *device) { struct ifmediareq ifmr; bzero((void*)&ifmr, sizeof(ifmr)); strlcpy(ifmr.ifm_name, device->interface, sizeof(ifmr.ifm_name)); if (ioctl(device->socket, SIOCGIFMEDIA, &ifmr) < 0) { /* * Interface does not support SIOCGIFMEDIA ioctl, * assume no such device. */ return(WI_NOSUCHDEV); } if ((ifmr.ifm_status & IFM_AVALID) == 0) { /* * Interface doesn't report media-valid status. * assume no such device. */ return(WI_NOSUCHDEV); } /* otherwise, return ok for active, not-ok if not active. */ return((ifmr.ifm_status & IFM_ACTIVE) != 0 ? WI_OK : WI_NOCARRIER); } static int _wi_getval(const struct wi_device *device, struct wi_req *wr) { struct ifreq ifr; bzero((void*)&ifr, sizeof(ifr)); strlcpy(ifr.ifr_name, device->interface, sizeof(ifr.ifr_name)); ifr.ifr_data = (void*)wr; if (ioctl(device->socket, SIOCGWAVELAN, &ifr) < 0) return(WI_NOSUCHDEV); return(WI_OK); } static int _wi_vendor(const struct wi_device *device, char *buffer, size_t len) { #define WI_RID_STA_IDENTITY_LUCENT 0x1 #define WI_RID_STA_IDENTITY_PRISMII 0x2 #define WI_RID_STA_IDENTITY_SAMSUNG 0x3 #define WI_RID_STA_IDENTITY_DLINK 0x6 const char* vendor = "Unknown"; struct wi_req wr; int result; bzero((void*)&wr, sizeof(wr)); wr.wi_len = WI_MAX_DATALEN; wr.wi_type = WI_RID_STA_IDENTITY; if ((result = _wi_getval(device, &wr)) != WI_OK){ /* For the Atheros, IDENTITY does not work. */ if (strcmp(device->interface, "ath") != 0) return(result); } else if (wr.wi_len < 4) return(WI_NOSUCHDEV); switch (wr.wi_val[1]) { case WI_RID_STA_IDENTITY_LUCENT: vendor = "Lucent"; break; case WI_RID_STA_IDENTITY_PRISMII: vendor = "generic PRISM II"; break; case WI_RID_STA_IDENTITY_SAMSUNG: vendor = "Samsung"; break; case WI_RID_STA_IDENTITY_DLINK: vendor = "D-Link"; break; } snprintf(buffer, len, "%s (ID %d, version %d.%d)", vendor, wr.wi_val[0], wr.wi_val[2], wr.wi_val[3]); return(WI_OK); } static int _wi_netname(const struct wi_device *device, char *buffer, size_t len) { struct wi_req wr; int result; bzero((void *)&wr, sizeof(wr)); wr.wi_len = WI_MAX_DATALEN; wr.wi_type = WI_RID_CURRENT_SSID; if ((result = _wi_getval(device, &wr)) != WI_OK) return(result); strlcpy(buffer, (char *)&wr.wi_val[1], MIN(len, le16toh(wr.wi_val[0]) + 1)); return(WI_OK); } static int _wi_quality(const struct wi_device *device, int *quality) { struct wi_req wr; int result; bzero((void *)&wr, sizeof(wr)); wr.wi_len = WI_MAX_DATALEN; wr.wi_type = WI_RID_COMMS_QUALITY; if ((result = _wi_getval(device, &wr)) != WI_OK) return(result); if (strcmp(device->interface, "ath") == 0) /* For the Atheros Cards */ *quality = le16toh(wr.wi_val[1]); else *quality = le16toh(wr.wi_val[0]); return(WI_OK); } static int _wi_rate(const struct wi_device *device, int *rate) { struct wi_req wr; int result; bzero((void *)&wr, sizeof(wr)); wr.wi_len = WI_MAX_DATALEN; wr.wi_type = WI_RID_CUR_TX_RATE; if ((result = _wi_getval(device, &wr)) != WI_OK) return(result); *rate = le16toh(wr.wi_val[0]); return(WI_OK); } #endif /* !defined(__NetBSD__) && !defined(__FreeBSD__) */