aboutsummaryrefslogtreecommitdiffstats
AgeCommit message (Expand)AuthorFilesLines
2009-11-10Logic Way patch from Arndt Kritznermichael1-15/+9
2009-11-05added protocol details to PICGraphic driver, fixed typos is generic headerspeterbailey3-30/+80
2009-11-03updating Makefile.am for PICGraphic driver, reverting mistaken commit of aclo...peterbailey2-61/+137
2009-11-03adding PICGraphic driverpeterbailey5-138/+506
2009-11-02svn_version bumped upmichael1-1/+1
2009-11-01Logic Way GmbH ABP08 ABP09 enhancements by Arndt Kritznermichael1-25/+236
2009-10-19driver for Logic Way GmbH ABP08 (serial) and ABP09 (USB) appliance control pa...michael9-17/+401
2009-09-25additional debug outputvolker3-9/+39
2009-09-23autoconf patches from PT M.michael11-9104/+17487
2009-09-23indentationmichael1-4/+4
2009-09-23indentationmichael1-1/+2
2009-09-23suppress compiler warningmichael1-9/+4
2009-09-23warn() => info()michael1-1/+1
2009-09-22added support for Z-10 speaker to G-15 drivervolker1-13/+54
2009-08-21evaluator bug fixed (thanks to lleo19)michael1-0/+1
2009-07-18indent runmichael2-325/+236
2009-07-18asterix extensions by gabrielortizlour@gmail.commichael1-0/+116
2009-05-19icon frame patch by Marcus Brownmichael2-5/+14
2009-05-11update autoconfmichux4-3144/+4091
2009-05-11add qnap log plugin, thanks to rolf tralowmichux3-0/+537
2009-04-15indentmichux1-232/+225
2009-04-15timer fix for vnc drivermichux1-1/+11
2009-04-15more timer fixesmichux1-8/+8
2009-04-15make the code more sexy...michux1-1/+2
2009-04-15update make filesmichux3-4/+252
2009-04-15fix timestamp loop issuemichux1-0/+8
2009-04-14add widget samplemichux1-0/+10
2009-04-14add mpris dbus plugin, untested!michux4-2/+367
2009-04-06indentmichux2-6/+5
2009-04-06update autoconf scriptsmichux4-11/+36
2009-04-06add shuttle VFD drivermichux4-4/+454
2009-03-31update option filemichux1-0/+2
2009-03-31drv_vnc: http port as optionmichux1-0/+5
2009-03-31drv_vnc: run as http daemonmichux1-2/+12
2009-03-30fix fps delay errormichux1-1/+1
2009-03-30add fps limitermichux2-2/+33
2009-03-30add UNSECURE password option to drv_vnc - SSH would be the better choice.michux2-1/+17
2009-03-28add keypadframe color as parameter, some minor fixupsmichux2-7/+21
2009-03-28update keypad handling, add more options, remove compiler waarningsmichux2-20/+40
2009-03-27update lcd4linux sample configmichux1-11/+11
2009-03-27add server port as config option, rename optionsmichux2-29/+37
2009-03-27vnc driver: keypad works nowmichux1-29/+35
2009-03-27some more keypad stuff for vnc driver...michux2-24/+50
2009-03-27update keypad function for vnc drivermichux1-5/+79
2009-03-26vnc driver: add beta keypad support michux3-11/+57
2009-03-26remove unneeded libsmichux2-2/+2
2009-03-26add sample cfg for vnc driver, indentmichux3-6/+16
2009-03-26add max client optionmichux1-7/+16
2009-03-26ignore security check for cygwinmichux1-0/+2
2009-03-26string compare is not case sensitive anymoremichux1-1/+1
.highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*
 * Auxiliary declarations and functions imported from iwlib in order to
 * process and parse scan events. This code is copied with little change
 * from wireless tools 30. It remains here until the wext code will be
 * replaced by corresponding netlink calls.
 */
#include "iw_if.h"
#include <search.h>		/* lsearch(3) */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include "iw_nl80211.h"

/* GLOBAL VARIABLES */
static struct nl_sock *scan_wait_sk;


/*
 * Ordering functions for scan results: all return true for a < b.
 */

/* Order by frequency. */
static bool cmp_freq(const struct scan_entry *a, const struct scan_entry *b)
{
	return a->freq < b->freq;
}

/* Order by signal strength. */
static bool cmp_sig(const struct scan_entry *a, const struct scan_entry *b)
{
	if (!a->bss_signal && !b->bss_signal)
		return a->bss_signal_qual < b->bss_signal_qual;
	return a->bss_signal < b->bss_signal;
}

/* Order by ESSID, organize entries with same ESSID by frequency and signal. */
static bool cmp_essid(const struct scan_entry *a, const struct scan_entry *b)
{
	int res = strncmp(a->essid, b->essid, IW_ESSID_MAX_SIZE);

	return res == 0 ? (a->freq == b->freq ? cmp_sig(a, b) : cmp_freq(a, b))
			: res < 0;
}

/* Order by MAC address */
static bool cmp_mac(const struct scan_entry *a, const struct scan_entry *b)
{
	return memcmp(&a->ap_addr, &b->ap_addr, sizeof(a->ap_addr)) < 0;
}

/* Order by frequency, grouping channels by ESSID. */
static bool cmp_chan(const struct scan_entry *a, const struct scan_entry *b)
{
	return a->freq == b->freq ? cmp_essid(a, b) : cmp_freq(a, b);
}

/* Order by frequency first, then by signal strength. */
static bool cmp_chan_sig(const struct scan_entry *a, const struct scan_entry *b)
{
	return a->freq == b->freq ? cmp_sig(a, b) : cmp_chan(a, b);
}

/* Order by openness (open access points frist). */
static bool cmp_open(const struct scan_entry *a, const struct scan_entry *b)
{
	return a->has_key < b->has_key;
}

/* Sort (open) access points by signal strength. */
static bool cmp_open_sig(const struct scan_entry *a, const struct scan_entry *b)
{
	return a->has_key == b->has_key ? cmp_sig(a, b) : cmp_open(a, b);
}

static bool (*scan_cmp[])(const struct scan_entry *, const struct scan_entry *) = {
	[SO_CHAN]	= cmp_chan,
	[SO_SIGNAL]	= cmp_sig,
	[SO_MAC]        = cmp_mac,
	[SO_ESSID]	= cmp_essid,
	[SO_OPEN]	= cmp_open,
	[SO_CHAN_SIG]	= cmp_chan_sig,
	[SO_OPEN_SIG]	= cmp_open_sig
};

/*
 * Scan event handling
 */

/* Callback event handler */
static int wait_event(struct nl_msg *msg, void *arg)
{
	struct wait_event *wait = arg;
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	int i;

	for (i = 0; i < wait->n_cmds; i++) {
		if (gnlh->cmd == wait->cmds[i])
			wait->cmd = gnlh->cmd;
	}
	return NL_SKIP;
}

/**
 * Wait for scan result notification sent by the kernel
 * Returns true if scan results are available, false if scan was aborted.
 * Taken from iw:event.c:__do_listen_events
 */
static bool wait_for_scan_events(struct scan_result *sr)
{
	static const uint32_t cmds[] = {
		NL80211_CMD_NEW_SCAN_RESULTS,
		NL80211_CMD_SCAN_ABORTED,
	};
	struct wait_event wait_ev = {
		.cmds   = cmds,
		.n_cmds = ARRAY_SIZE(cmds),
		.cmd    = 0
	};
	struct nl_cb *cb;

	if (!scan_wait_sk)
		scan_wait_sk = alloc_nl_mcast_sk("scan");

	cb = nl_cb_alloc(IW_NL_CB_DEBUG ? NL_CB_DEBUG : NL_CB_DEFAULT);
	if (!cb)
		err_sys("failed to allocate netlink callbacks");

	/* no sequence checking for multicast messages */
	nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, wait_event, &wait_ev);

	while (!wait_ev.cmd)
		nl_recvmsgs(scan_wait_sk, cb);
	nl_cb_put(cb);

	return wait_ev.cmd == NL80211_CMD_NEW_SCAN_RESULTS;
}

/**
 * Scan result handler. Stolen from iw:scan.c
 * This also updates the scan-result statistics.
 */
int scan_dump_handler(struct nl_msg *msg, void *arg)
{
	struct scan_result *sr = (struct scan_result *)arg;
	struct scan_entry *new = calloc(1, sizeof(*new));
	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
	struct nlattr *tb[NL80211_ATTR_MAX + 1];
	struct nlattr *bss[NL80211_BSS_MAX + 1];
	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
		[NL80211_BSS_TSF]                  = { .type = NLA_U64 },
		[NL80211_BSS_FREQUENCY]            = { .type = NLA_U32 },
		[NL80211_BSS_BSSID]                = { },
		[NL80211_BSS_BEACON_INTERVAL]      = { .type = NLA_U16 },
		[NL80211_BSS_CAPABILITY]           = { .type = NLA_U16 },
		[NL80211_BSS_INFORMATION_ELEMENTS] = { },
		[NL80211_BSS_SIGNAL_MBM]           = { .type = NLA_U32 },
		[NL80211_BSS_SIGNAL_UNSPEC]        = { .type = NLA_U8  },
		[NL80211_BSS_STATUS]               = { .type = NLA_U32 },
		[NL80211_BSS_SEEN_MS_AGO]          = { .type = NLA_U32 },
		[NL80211_BSS_BEACON_IES]           = { },
	};

	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
		  genlmsg_attrlen(gnlh, 0), NULL);

	if (!tb[NL80211_ATTR_BSS])
		return NL_SKIP;

	if (nla_parse_nested(bss, NL80211_BSS_MAX,
			     tb[NL80211_ATTR_BSS],
			     bss_policy))
		return NL_SKIP;

	if (!bss[NL80211_BSS_BSSID])
		return NL_SKIP;

	new = calloc(1, sizeof(*new));
	if (!new)
		err_sys("failed to allocate scan entry");

	memcpy(&new->ap_addr, nla_data(bss[NL80211_BSS_BSSID]), sizeof(new->ap_addr));

	if (bss[NL80211_BSS_FREQUENCY]) {
		new->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
		new->chan = ieee80211_frequency_to_channel(new->freq);
	}

	if (bss[NL80211_BSS_SIGNAL_UNSPEC])
		new->bss_signal_qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);


	if (bss[NL80211_BSS_SIGNAL_MBM]) {
		int s = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
		new->bss_signal = s / 100;
	}

	if (bss[NL80211_BSS_CAPABILITY]) {
		new->bss_capa = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
		new->has_key  = (new->bss_capa & WLAN_CAPABILITY_PRIVACY) != 0;
	}

	if (bss[NL80211_BSS_SEEN_MS_AGO])
		new->last_seen = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);

	if (bss[NL80211_BSS_TSF])
		new->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);

	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
		uint8_t *ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
		int ielen   = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
		uint8_t len = ie[1];

 		while (ielen >= 2 && ielen >= ie[1]) {
			switch (ie[0]) {
			case 0:	/* SSID */
				if (len > 0 && len <= 32)
					print_ssid_escaped(new->essid, sizeof(new->essid),
							   ie+2, len);
				break;
			case 11: /* BSS Load */
				if (len >= 5) {
					new->bss_sta_count  = ie[3] << 8 | ie[2];
					new->bss_chan_usage = ie[4];
				}
				break;
			}
			ielen -= ie[1] + 2;
			ie    += ie[1] + 2;
        	}
	}

	/* Update stats */
	new->next = sr->head;
	sr->head  = new;
	if (str_is_ascii(new->essid))
		sr->max_essid_len = clamp(strlen(new->essid),
					  sr->max_essid_len,
					  IW_ESSID_MAX_SIZE);

	if (new->freq > 45000)	/* 802.11ad 60GHz spectrum */
		err_quit("FIXME: can not handle %d MHz spectrum yet", new->freq);
	else if (new->freq >= 5000)
		sr->num.five_gig++;
	else if (new->freq >= 2000)
		sr->num.two_gig++;
	sr->num.entries += 1;
	sr->num.open    += !new->has_key;

	return NL_SKIP;
}

static int iw_nl80211_scan_trigger(void)
{
	static struct cmd cmd_trigger_scan = {
		.cmd = NL80211_CMD_TRIGGER_SCAN,
	};

	return handle_cmd(&cmd_trigger_scan);
}

static int iw_nl80211_get_scan_data(struct scan_result *sr)
{
	static struct cmd cmd_scan_dump = {
		.cmd	 = NL80211_CMD_GET_SCAN,
		.flags	 = NLM_F_DUMP,
		.handler = scan_dump_handler
	};

	memset(sr, 0, sizeof(*sr));
	cmd_scan_dump.handler_arg = sr;

	return handle_cmd(&cmd_scan_dump);
}

/*
 * Simple sort routine.
 * FIXME: use hash or tree to store entries, a list to display them.
 */
void sort_scan_list(struct scan_entry **headp)
{
	struct scan_entry *head = NULL, *cur, *new = *headp, **prev;

	while (new) {
		for (cur = head, prev = &head; cur &&
		     conf.scan_sort_asc == scan_cmp[conf.scan_sort_order](cur, new);
		     prev = &cur->next, cur = cur->next)
			;
		*prev = new;
		new = new->next;
		(*prev)->next = cur;
	}
	*headp = head;
}

static void free_scan_list(struct scan_entry *head)
{
	if (head) {
		free_scan_list(head->next);
		free(head);
	}
}

static void clear_scan_list(struct scan_result *sr)
{
	pthread_mutex_lock(&sr->mutex);
	free_scan_list(sr->head);
	free(sr->channel_stats);
	sr->head          = NULL;
	sr->channel_stats = NULL;
	sr->msg[0]        = '\0';
	sr->max_essid_len = MAX_ESSID_LEN;
	memset(&(sr->num), 0, sizeof(sr->num));
	pthread_mutex_unlock(&sr->mutex);
}

/*
 * 	Channel statistics shown at the bottom of scan screen.
 */

/*
 * For lsearch, it compares key value with array member, needs to
 * return 0 if they are the same, non-0 otherwise.
 */
static int cmp_key(const void *a, const void *b)
{
	return ((struct cnt *)a)->val - ((struct cnt *)b)->val;
}

/* For quick-sorting the array in descending order of counts */
static int cmp_cnt(const void *a, const void *b)
{
	if (conf.scan_sort_order == SO_CHAN && !conf.scan_sort_asc)
		return ((struct cnt *)a)->count - ((struct cnt *)b)->count;
	return ((struct cnt *)b)->count - ((struct cnt *)a)->count;
}

/**
 * Fill in sr->channel_stats (must not have been allocated yet).
 */
static void compute_channel_stats(struct scan_result *sr)
{
	struct scan_entry *cur;
	struct cnt *bin, key = {0, 0};
	size_t n = 0;

	if (!sr->num.entries)
		return;

	sr->channel_stats = calloc(sr->num.entries, sizeof(key));
	for (cur = sr->head; cur; cur = cur->next) {
		if (cur->chan >= 0) {
			key.val = cur->chan;
			bin = lsearch(&key, sr->channel_stats, &n, sizeof(key), cmp_key);
			if (bin)
				bin->count++;
		}
	}

	if (n > 0) {
		qsort(sr->channel_stats, n, sizeof(key), cmp_cnt);
	} else {
		free(sr->channel_stats);
		sr->channel_stats = NULL;
	}
	sr->num.ch_stats = n < MAX_CH_STATS ? n : MAX_CH_STATS;
}

/*
 *	Scan results.
 */
void scan_result_init(struct scan_result *sr)
{
	pthread_mutexattr_t ma;

	memset(sr, 0, sizeof(*sr));
	pthread_mutexattr_init(&ma);
	if (pthread_mutexattr_setrobust(&ma, PTHREAD_MUTEX_ROBUST) < 0)
		err_sys("Failed to set the mutex robust attribute");
	pthread_mutex_init(&sr->mutex, &ma);
}

void scan_result_fini(struct scan_result *sr)
{
	/* FIXME: this may have a bug on resource de-allocation, if the main thread still holds the lock */
	free_scan_list(sr->head);
	free(sr->channel_stats);
	pthread_mutex_destroy(&sr->mutex);
}

/** The actual scan thread. */
void *do_scan(void *sr_ptr)
{
	struct scan_result *sr = sr_ptr;
	sigset_t blockmask;
	int ret = 0;

	/* SIGWINCH is supposed to be handled in the main thread. */
	sigemptyset(&blockmask);
	sigaddset(&blockmask, SIGWINCH);
	pthread_sigmask(SIG_BLOCK, &blockmask, NULL);

	pthread_detach(pthread_self());
	do {
		clear_scan_list(sr);

		ret = iw_nl80211_scan_trigger();
		switch(-ret) {
		case 0:
		case EBUSY:
			/* Trigger returns -EBUSY if a scan request is pending or ready. */
			if (!wait_for_scan_events(sr)) {
				snprintf(sr->msg, sizeof(sr->msg), "Waiting for scan data...");
			} else {
				pthread_mutex_lock(&sr->mutex);
				ret = iw_nl80211_get_scan_data(sr);
				if (ret < 0) {
					snprintf(sr->msg, sizeof(sr->msg),
						 "Scan failed on %s: %s", conf_ifname(), strerror(-ret));
				} else if (!sr->head) {
					snprintf(sr->msg, sizeof(sr->msg), "Empty scan results on %s", conf_ifname());
				}
				compute_channel_stats(sr);
				pthread_mutex_unlock(&sr->mutex);
			}
			break;
		case EPERM:
			if (!has_net_admin_capability())
				snprintf(sr->msg, sizeof(sr->msg),
					 "This screen requires CAP_NET_ADMIN permissions");
			return NULL;
		case EFAULT:
			/* EFAULT can occur after a window resizing event: temporary, fall through. */
		case EINTR:
		case EAGAIN:
			/* Temporary errors. */
			snprintf(sr->msg, sizeof(sr->msg), "Waiting for device to become ready ...");
			break;
		case ENETDOWN:
			if (!if_is_up(conf_ifname())) {
				snprintf(sr->msg, sizeof(sr->msg), "Interface %s is down - setting it up ...", conf_ifname());
				if (if_set_up(conf_ifname()) < 0)
					err_sys("Can not bring up interface '%s'", conf_ifname());
				break;
			}
			/* fall through */
		default:
			snprintf(sr->msg, sizeof(sr->msg),
				 "Scan trigger failed on %s: %s", conf_ifname(), strerror(-ret));
		}
	} while (usleep(conf.stat_iv * 1000) == 0);

	return NULL;
}