gboolean
wifi_wext_is_wifi (const char *iface)
{
	int fd;
	struct iwreq iwr;
	gboolean is_wifi = FALSE;

	/* performing an ioctl on a non-existing name may cause the automatic
	 * loading of kernel modules, which should be avoided.
	 *
	 * Usually, we should thus make sure that an inteface with this name
	 * exists.
	 *
	 * Note that wifi_wext_is_wifi() has only one caller which just verified
	 * that an interface with this name exists.
	 */

	fd = socket (PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
	if (fd >= 0) {
		nm_utils_ifname_cpy (iwr.ifr_ifrn.ifrn_name, iface);
		if (ioctl (fd, SIOCGIWNAME, &iwr) == 0)
			is_wifi = TRUE;
		close (fd);
	}
	return is_wifi;
}
static NM80211Mode
wifi_wext_get_mode (WifiData *data)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);

	if (ioctl (wext->fd, SIOCGIWMODE, &wrq) < 0) {
		if (errno != ENODEV) {
			nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
			             "(%s): error %d getting card mode",
			             wext->parent.iface, errno);
		}
		return NM_802_11_MODE_UNKNOWN;
	}

	switch (wrq.u.mode) {
	case IW_MODE_ADHOC:
		return NM_802_11_MODE_ADHOC;
	case IW_MODE_MASTER:
		return NM_802_11_MODE_AP;
	case IW_MODE_INFRA:
	case IW_MODE_AUTO: /* hack for WEXT devices reporting IW_MODE_AUTO */
		return NM_802_11_MODE_INFRA;
	default:
		break;
	}
	return NM_802_11_MODE_UNKNOWN;
}
static gboolean
ethtool_get (const char *name, gpointer edata)
{
	struct ifreq ifr;
	int fd;

	if (!name || !*name)
		return FALSE;

	if (!nmp_utils_device_exists (name))
		return FALSE;

	/* nmp_utils_device_exists() already errors out if @name is invalid. */
	nm_assert (strlen (name) < IFNAMSIZ);

	memset (&ifr, 0, sizeof (ifr));
	nm_utils_ifname_cpy (ifr.ifr_name, name);
	ifr.ifr_data = edata;

	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		nm_log_err (LOGD_PLATFORM, "ethtool: Could not open socket.");
		return FALSE;
	}

	if (ioctl (fd, SIOCETHTOOL, &ifr) < 0) {
		nm_log_dbg (LOGD_PLATFORM, "ethtool: Request failed: %s", strerror (errno));
		close (fd);
		return FALSE;
	}

	close (fd);
	return TRUE;
}
static gboolean
wifi_wext_set_mesh_ssid (WifiData *data, const guint8 *ssid, gsize len)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;
	char buf[IW_ESSID_MAX_SIZE + 1];

	memset (buf, 0, sizeof (buf));
	memcpy (buf, ssid, MIN (sizeof (buf) - 1, len));

	wrq.u.essid.pointer = (caddr_t) buf;
	wrq.u.essid.length = len;
	wrq.u.essid.flags = (len > 0) ? 1 : 0; /* 1=enable SSID, 0=disable/any */

	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCSIWESSID, &wrq) == 0)
		return TRUE;

	if (errno != ENODEV) {
		nm_log_err (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
		            "(%s): error setting SSID to '%s': %s",
		            wext->parent.iface,
		            ssid ? nm_utils_escape_ssid (ssid, len) : "(null)",
		            strerror (errno));
	}

	return FALSE;
}
static guint32
wifi_wext_get_rate (WifiData *data)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;
	int err;

	memset (&wrq, 0, sizeof (wrq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	err = ioctl (wext->fd, SIOCGIWRATE, &wrq);
	return ((err == 0) ? wrq.u.bitrate.value / 1000 : 0);
}
static gboolean
wext_can_scan (WifiDataWext *wext)
{
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCSIWSCAN, &wrq) < 0) {
		if (errno == EOPNOTSUPP)
			return FALSE;
	}
	return TRUE;
}
gboolean
nmp_utils_device_exists (const char *name)
{
#define SYS_CLASS_NET "/sys/class/net/"
	char sysdir[NM_STRLEN (SYS_CLASS_NET) + IFNAMSIZ];

	if (   !name
	    || strlen (name) >= IFNAMSIZ
	    || !nm_utils_is_valid_path_component (name))
		g_return_val_if_reached (FALSE);

	memcpy (sysdir, SYS_CLASS_NET, NM_STRLEN (SYS_CLASS_NET));
	nm_utils_ifname_cpy (&sysdir[NM_STRLEN (SYS_CLASS_NET)], name);
	return g_file_test (sysdir, G_FILE_TEST_EXISTS);
}
gboolean
nmp_utils_mii_supports_carrier_detect (const char *ifname)
{
	int fd, errsv;
	struct ifreq ifr;
	struct mii_ioctl_data *mii;
	gboolean supports_mii = FALSE;

	if (!ifname)
		return FALSE;

	if (!nmp_utils_device_exists (ifname))
		return FALSE;

	fd = socket (PF_INET, SOCK_DGRAM, 0);
	if (fd < 0) {
		nm_log_err (LOGD_PLATFORM, "mii: couldn't open control socket (%s)", ifname);
		return FALSE;
	}

	memset (&ifr, 0, sizeof (struct ifreq));
	nm_utils_ifname_cpy (ifr.ifr_name, ifname);

	errno = 0;
	if (ioctl (fd, SIOCGMIIPHY, &ifr) < 0) {
		errsv = errno;
		nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIPHY failed: %s (%d) (%s)", strerror (errsv), errsv, ifname);
		goto out;
	}

	/* If we can read the BMSR register, we assume that the card supports MII link detection */
	mii = (struct mii_ioctl_data *) &ifr.ifr_ifru;
	mii->reg_num = MII_BMSR;

	if (ioctl (fd, SIOCGMIIREG, &ifr) == 0) {
		nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG result 0x%X (%s)", mii->val_out, ifname);
		supports_mii = TRUE;
	} else {
		errsv = errno;
		nm_log_dbg (LOGD_PLATFORM, "mii: SIOCGMIIREG failed: %s (%d) (%s)", strerror (errsv), errsv, ifname);
	}

out:
	close (fd);
	nm_log_dbg (LOGD_PLATFORM, "mii: MII %s supported (%s)", supports_mii ? "is" : "not", ifname);
	return supports_mii;
}
static gboolean
wifi_wext_get_bssid (WifiData *data, guint8 *out_bssid)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (wrq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCGIWAP, &wrq) < 0) {
		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
		             "(%s): error getting associated BSSID: %s",
		             wext->parent.iface, strerror (errno));
		return FALSE;
	}
	memcpy (out_bssid, &(wrq.u.ap_addr.sa_data), ETH_ALEN);
	return TRUE;
}
static guint32
wifi_wext_get_freq (WifiData *data)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCGIWFREQ, &wrq) < 0) {
		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
		             "(%s): error getting frequency: %s",
		             wext->parent.iface, strerror (errno));
		return 0;
	}

	return iw_freq_to_uint32 (&wrq.u.freq);
}
static gboolean
wext_get_range (WifiDataWext *wext,
                struct iw_range *range,
                guint32 *response_len)
{
	int i = 26;
	gboolean success = FALSE;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	wrq.u.data.pointer = (caddr_t) range;
	wrq.u.data.length = sizeof (struct iw_range);

	/* Need to give some drivers time to recover after suspend/resume
	 * (ex ipw3945 takes a few seconds to talk to its regulatory daemon;
	 * see rh bz#362421)
	 */
	while (i-- > 0) {
		if (ioctl (wext->fd, SIOCGIWRANGE, &wrq) == 0) {
			if (response_len)
				*response_len = wrq.u.data.length;
			success = TRUE;
			break;
		} else if (errno != EAGAIN) {
			nm_log_err (LOGD_PLATFORM | LOGD_WIFI,
			            "(%s): couldn't get driver range information (%d).",
			            wext->parent.iface, errno);
			break;
		}

		g_usleep (G_USEC_PER_SEC / 4);
	}

	if (i <= 0) {
		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
		             "(%s): driver took too long to respond to IWRANGE query.",
		             wext->parent.iface);
	}

	return success;
}
static int
wifi_wext_get_qual (WifiData *data)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;
	struct iw_statistics stats;

	memset (&stats, 0, sizeof (stats));
	wrq.u.data.pointer = &stats;
	wrq.u.data.length = sizeof (stats);
	wrq.u.data.flags = 1;  /* Clear updated flag */
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);

	if (ioctl (wext->fd, SIOCGIWSTATS, &wrq) < 0) {
		nm_log_warn (LOGD_PLATFORM | LOGD_WIFI,
		             "(%s): error getting signal strength: %s",
		             wext->parent.iface, strerror (errno));
		return -1;
	}

	return wext_qual_to_percent (&stats.qual, &wext->max_qual);
}
static gboolean
wifi_wext_set_powersave (WifiData *data, guint32 powersave)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	if (powersave == 1) {
		wrq.u.power.flags = IW_POWER_ALL_R;
	} else
		wrq.u.power.disabled = 1;

	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCSIWPOWER, &wrq) < 0) {
		if (errno != ENODEV) {
			nm_log_err (LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting powersave %" G_GUINT32_FORMAT,
			            wext->parent.iface, powersave);
		}
		return FALSE;
	}

	return TRUE;
}
static gboolean
wifi_wext_set_mesh_channel (WifiData *data, guint32 channel)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	memset (&wrq, 0, sizeof (struct iwreq));
	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);

	if (channel > 0) {
		wrq.u.freq.flags = IW_FREQ_FIXED;
		wrq.u.freq.e = 0;
		wrq.u.freq.m = channel;
	}

	if (ioctl (wext->fd, SIOCSIWFREQ, &wrq) < 0) {
		nm_log_err (LOGD_PLATFORM | LOGD_WIFI | LOGD_OLPC,
		            "(%s): error setting channel to %d: %s",
		            wext->parent.iface, channel, strerror (errno));
		return FALSE;
	}

	return TRUE;
}
static gboolean
wifi_wext_set_mode (WifiData *data, const NM80211Mode mode)
{
	WifiDataWext *wext = (WifiDataWext *) data;
	struct iwreq wrq;

	if (wifi_wext_get_mode (data) == mode)
		return TRUE;

	memset (&wrq, 0, sizeof (struct iwreq));
	switch (mode) {
	case NM_802_11_MODE_ADHOC:
		wrq.u.mode = IW_MODE_ADHOC;
		break;
	case NM_802_11_MODE_AP:
		wrq.u.mode = IW_MODE_MASTER;
		break;
	case NM_802_11_MODE_INFRA:
		wrq.u.mode = IW_MODE_INFRA;
		break;
	default:
		g_warn_if_reached ();
		return FALSE;
	}

	nm_utils_ifname_cpy (wrq.ifr_name, wext->parent.iface);
	if (ioctl (wext->fd, SIOCSIWMODE, &wrq) < 0) {
		if (errno != ENODEV) {
			nm_log_err (LOGD_PLATFORM | LOGD_WIFI, "(%s): error setting mode %d",
			            wext->parent.iface, mode);
		}
		return FALSE;
	}

	return TRUE;
}