• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/madwimax-0.1.1/src/
1/*
2 * This is a reverse-engineered driver for mobile WiMAX (802.16e) devices
3 * based on Samsung CMC-730 chip.
4 * Copyright (C) 2008-2009 Alexander Gordeev <lasaine@lvk.cs.msu.su>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include <errno.h>
22#include <fcntl.h>
23#include <getopt.h>
24#include <poll.h>
25#include <signal.h>
26#include <string.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <unistd.h>
30#include <sys/time.h>
31#include <sys/wait.h>
32
33#include <libusb.h>
34
35#include "config.h"
36#include "logging.h"
37#include "protocol.h"
38#include "wimax.h"
39#include "tap_dev.h"
40
41#include <dirent.h>
42
43#define SCAN_INTERVAL 1
44
45/* variables for the command-line parameters */
46static int daemonize = 0;
47static int diode_on = 1;
48static int detach_dvd = 0;
49static char *ssid = "@yota.ru";
50static char *event_script = "/usr/sbin/event.sh";
51
52static FILE *logfile = NULL;
53
54#define MATCH_BY_LIST		0
55#define MATCH_BY_VID_PID	1
56#define MATCH_BY_BUS_DEV	2
57
58static int match_method = MATCH_BY_LIST;
59
60/* for matching by list... */
61typedef struct usb_device_id_t {
62	unsigned short vendorID;
63	unsigned short productID;
64} usb_device_id_t;
65
66/* list of all known devices */
67static usb_device_id_t wimax_dev_ids[] = {
68	{ 0x04e8, 0x6761 },
69	{ 0x04e9, 0x6761 },
70	{ 0x04e8, 0x6731 },
71	{ 0x04e8, 0x6780 },
72};
73
74/* for other methods of matching... */
75static union {
76	struct {
77		unsigned short vid;
78		unsigned short pid;
79	};
80	struct {
81		unsigned int bus;
82		unsigned int dev;
83	};
84} match_params;
85
86/* USB-related parameters */
87#define IF_MODEM		0
88#define IF_DVD			1
89
90#define EP_IN			(2 | LIBUSB_ENDPOINT_IN)
91#define EP_OUT			(4 | LIBUSB_ENDPOINT_OUT)
92
93#define MAX_PACKET_LEN		0x4000
94
95/* information collector */
96static struct wimax_dev_status wd_status;
97
98char *wimax_states[] = {"INIT", "SYNC", "NEGO", "NORMAL", "SLEEP", "IDLE", "HHO", "FBSS", "RESET", "RESERVED", "UNDEFINED", "BE", "NRTPS", "RTPS", "ERTPS", "UGS", "INITIAL_RNG", "BASIC", "PRIMARY", "SECONDARY", "MULTICAST", "NORMAL_MULTICAST", "SLEEP_MULTICAST", "IDLE_MULTICAST", "FRAG_BROADCAST", "BROADCAST", "MANAGEMENT", "TRANSPORT"};
99
100/* libusb stuff */
101static struct libusb_context *ctx = NULL;
102static struct libusb_device_handle *devh = NULL;
103static struct libusb_transfer *req_transfer = NULL;
104static int kernel_driver_active = 0;
105
106static unsigned char read_buffer[MAX_PACKET_LEN];
107
108static int tap_fd = -1;
109//static char tap_dev[20] = "wimax%d";
110static char tap_dev[20] = "eth%d";
111static int tap_if_up = 0;
112
113static nfds_t nfds;
114static struct pollfd* fds = NULL;
115
116static int first_nego_flag = 0;
117static int device_disconnected = 0;
118
119
120#define CHECK_NEGATIVE(x) {if((r = (x)) < 0) return r;}
121#define CHECK_DISCONNECTED(x) {if((r = (x)) == LIBUSB_ERROR_NO_DEVICE) exit_release_resources(0);}
122
123static void exit_release_resources(int code);
124
125static struct libusb_device_handle* find_wimax_device(void)
126{
127	struct libusb_device **devs;
128	struct libusb_device *found = NULL;
129	struct libusb_device *dev;
130	struct libusb_device_handle *handle = NULL;
131	int i = 0;
132	int r;
133
134	if (libusb_get_device_list(ctx, &devs) < 0)
135		return NULL;
136
137	while (!found && (dev = devs[i++]) != NULL) {
138		struct libusb_device_descriptor desc;
139		unsigned int j = 0;
140		unsigned short dev_vid, dev_pid;
141
142		r = libusb_get_device_descriptor(dev, &desc);
143		if (r < 0) {
144			continue;
145		}
146		dev_vid = libusb_le16_to_cpu(desc.idVendor);
147		dev_pid = libusb_le16_to_cpu(desc.idProduct);
148		wmlog_msg(1, "Bus %03d Device %03d: ID %04x:%04x", libusb_get_bus_number(dev), libusb_get_device_address(dev), dev_vid, dev_pid);
149		switch (match_method) {
150			case MATCH_BY_LIST: {
151				for (j = 0; j < sizeof(wimax_dev_ids) / sizeof(usb_device_id_t); j++) {
152					if (dev_vid == wimax_dev_ids[j].vendorID && dev_pid == wimax_dev_ids[j].productID) {
153						found = dev;
154						break;
155					}
156				}
157				break;
158			}
159			case MATCH_BY_VID_PID: {
160				if (dev_vid == match_params.vid && dev_pid == match_params.pid) {
161					found = dev;
162				}
163				break;
164			}
165			case MATCH_BY_BUS_DEV: {
166				if (libusb_get_bus_number(dev) == match_params.bus && libusb_get_device_address(dev) == match_params.dev) {
167					found = dev;
168				}
169				break;
170			}
171		}
172	}
173
174	if (found) {
175		r = libusb_open(found, &handle);
176		if (r < 0)
177			handle = NULL;
178	}
179
180	libusb_free_device_list(devs, 1);
181	return handle;
182}
183
184static int set_data(unsigned char* data, int size)
185{
186	int r;
187	int transferred;
188
189	wmlog_dumphexasc(3, data, size, "Bulk write:");
190
191	r = libusb_bulk_transfer(devh, EP_OUT, data, size, &transferred, 0);
192	if (r < 0) {
193		wmlog_msg(1, "bulk write error %d", r);
194		if (r == LIBUSB_ERROR_NO_DEVICE) {
195			exit_release_resources(0);
196		}
197		return r;
198	}
199	if (transferred < size) {
200		wmlog_msg(1, "short write (%d)", r);
201		return -1;
202	}
203	return r;
204}
205
206static void cb_req(struct libusb_transfer *transfer)
207{
208	if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
209		wmlog_msg(1, "async bulk read error %d", transfer->status);
210		if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) {
211			device_disconnected = 1;
212			return;
213		}
214	} else {
215		wmlog_dumphexasc(3, transfer->buffer, transfer->actual_length, "Async read:");
216		process_response(&wd_status, transfer->buffer, transfer->actual_length);
217	}
218	if (libusb_submit_transfer(req_transfer) < 0) {
219		wmlog_msg(1, "async read transfer sumbit failed");
220	}
221}
222
223/* get link_status */
224int get_link_status()
225{
226	return wd_status.link_status;
227}
228
229/* set close-on-exec flag on the file descriptor */
230int set_coe(int fd)
231{
232	int flags;
233
234	flags = fcntl(fd, F_GETFD);
235	if (flags == -1)
236	{
237		wmlog_msg(1, "failed to set close-on-exec flag on fd %d", fd);
238		return -1;
239	}
240	flags |= FD_CLOEXEC;
241	if (fcntl(fd, F_SETFD, flags) == -1)
242	{
243		wmlog_msg(1, "failed to set close-on-exec flag on fd %d", fd);
244		return -1;
245	}
246
247	return 0;
248}
249
250/* run specified script */
251static int raise_event(char *event)
252{
253	int pid = fork();
254
255	if(pid < 0) { // error
256		return -1;
257	} else if (pid > 0) { // parent
258		return pid;
259	} else { // child
260wmlog_msg(0, "raise_event: %s %s.", event, tap_dev);
261		char *args[] = {event_script, event, tap_dev, NULL};
262		char *env[1] = {NULL};
263		// run the program
264		execve(args[0], args, env);
265		exit(1);
266	}//*/
267	return 0;
268}
269
270/* brings interface up and runs a user-supplied script */
271static int if_create()
272{
273	tap_fd = tap_open(tap_dev);
274	if (tap_fd < 0) {
275		wmlog_msg(0, "failed to allocate tap interface");
276		wmlog_msg(0,
277				"You should have TUN/TAP driver compiled in the kernel or as a kernel module.\n"
278				"If 'modprobe tun' doesn't help then recompile your kernel.");
279		exit_release_resources(1);
280	}
281	tap_set_hwaddr(tap_fd, tap_dev, wd_status.mac);
282	tap_set_mtu(tap_fd, tap_dev, 1386);
283	set_coe(tap_fd);
284	wmlog_msg(0, "Allocated tap interface: %s", tap_dev);
285	wmlog_msg(2, "Starting if-create script...");
286	raise_event("if-create");
287	return 0;
288}
289
290/* brings interface up and runs a user-supplied script */
291static int if_up()
292{
293	tap_bring_up(tap_fd, tap_dev);
294	wmlog_msg(2, "Starting if-up script...");
295	raise_event("if-up");
296	tap_if_up = 1;
297	return 0;
298}
299
300/* brings interface down and runs a user-supplied script */
301static int if_down()
302{
303	if (!tap_if_up) return 0;
304	tap_if_up = 0;
305	wmlog_msg(2, "Starting if-down script...");
306	raise_event("if-down");
307	tap_bring_down(tap_fd, tap_dev);
308	return 0;
309}
310
311/* brings interface down and runs a user-supplied script */
312static int if_release()
313{
314	wmlog_msg(2, "Starting if-release script...");
315	raise_event("if-release");
316	tap_close(tap_fd, tap_dev);
317	return 0;
318}
319
320/* set link_status */
321void set_link_status(int link_status)
322{
323	wd_status.info_updated |= WDS_LINK_STATUS;
324
325	if (wd_status.link_status == link_status) return;
326
327	if (wd_status.link_status < 2 && link_status == 2) {
328		if_up();
329	}
330	if (wd_status.link_status == 2 && link_status < 2) {
331		if_down();
332	}
333	if (link_status == 1) {
334		first_nego_flag = 1;
335	}
336
337	wd_status.link_status = link_status;
338}
339
340/* get state */
341int get_state()
342{
343	return wd_status.state;
344}
345
346/* set state */
347void set_state(int state)
348{
349	wd_status.state = state;
350	wd_status.info_updated |= WDS_STATE;
351	if (state >= 1 && state <= 3 && wd_status.link_status != (state - 1)) {
352		set_link_status(state - 1);
353	}
354}
355
356static int alloc_transfers(void)
357{
358	req_transfer = libusb_alloc_transfer(0);
359	if (!req_transfer)
360		return -ENOMEM;
361
362	libusb_fill_bulk_transfer(req_transfer, devh, EP_IN, read_buffer,
363		sizeof(read_buffer), cb_req, NULL, 0);
364
365	return 0;
366}
367
368int write_netif(const void *buf, int count)
369{
370	return tap_write(tap_fd, buf, count);
371}
372
373static int read_tap()
374{
375	unsigned char buf[MAX_PACKET_LEN];
376	int hlen = get_header_len();
377	int r;
378	int len;
379
380	r = tap_read(tap_fd, buf + hlen, MAX_PACKET_LEN - hlen);
381
382	if (r < 0)
383	{
384		wmlog_msg(1, "Error while reading from TAP interface");
385		return r;
386	}
387
388	if (r == 0)
389	{
390		return 0;
391	}
392
393	len = fill_data_packet_header(buf, r);
394	wmlog_dumphexasc(4, buf, len, "Outgoing packet:");
395	r = set_data(buf, len);
396
397	return r;
398}
399
400static int process_events_once(int timeout)
401{
402	struct timeval tv = {0, 0};
403	int r;
404	int libusb_delay;
405	int delay;
406	unsigned int i;
407	char process_libusb = 0;
408
409	r = libusb_get_next_timeout(ctx, &tv);
410	if (r == 1 && tv.tv_sec == 0 && tv.tv_usec == 0)
411	{
412		r = libusb_handle_events_timeout(ctx, &tv);
413	}
414
415	delay = libusb_delay = tv.tv_sec * 1000 + tv.tv_usec;
416	if (delay <= 0 || delay > timeout)
417	{
418		delay = timeout;
419	}
420
421	CHECK_NEGATIVE(poll(fds, nfds, delay));
422
423	process_libusb = (r == 0 && delay == libusb_delay);
424
425	for (i = 0; i < nfds; ++i)
426	{
427		if (fds[i].fd == tap_fd) {
428			if (fds[i].revents)
429			{
430				CHECK_NEGATIVE(read_tap());
431			}
432			continue;
433		}
434		process_libusb |= fds[i].revents;
435	}
436
437	if (process_libusb)
438	{
439		struct timeval tv = {.tv_sec = 0, .tv_usec = 0};
440		CHECK_NEGATIVE(libusb_handle_events_timeout(ctx, &tv));
441	}
442
443	return 0;
444}
445
446/* handle events until timeout is reached or all of the events in event_mask happen */
447static int process_events_by_mask(int timeout, unsigned int event_mask)
448{
449	struct timeval start, curr;
450	int r;
451	int delay = timeout;
452
453	CHECK_NEGATIVE(gettimeofday(&start, NULL));
454
455	wd_status.info_updated &= ~event_mask;
456
457	while ((event_mask == 0 || (wd_status.info_updated & event_mask) != event_mask) && delay >= 0) {
458		long a;
459
460		CHECK_NEGATIVE(process_events_once(delay));
461
462		if (device_disconnected) {
463			exit_release_resources(0);
464		}
465
466		CHECK_NEGATIVE(gettimeofday(&curr, NULL));
467
468		a = (curr.tv_sec - start.tv_sec) * 1000 + (curr.tv_usec - start.tv_usec) / 1000;
469		delay = timeout - a;
470	}
471
472	wd_status.info_updated &= ~event_mask;
473
474	return (delay > 0) ? delay : 0;
475}
476
477int alloc_fds()
478{
479	int i;
480	const struct libusb_pollfd **usb_fds = libusb_get_pollfds(ctx);
481
482	if (!usb_fds)
483	{
484		return -1;
485	}
486
487	nfds = 0;
488	while (usb_fds[nfds])
489	{
490		nfds++;
491	}
492	if (tap_fd != -1) {
493		nfds++;
494	}
495
496	if(fds != NULL) {
497		free(fds);
498	}
499
500	fds = (struct pollfd*)calloc(nfds, sizeof(struct pollfd));
501	for (i = 0; usb_fds[i]; ++i)
502	{
503		fds[i].fd = usb_fds[i]->fd;
504		fds[i].events = usb_fds[i]->events;
505		set_coe(usb_fds[i]->fd);
506	}
507	if (tap_fd != -1) {
508		fds[i].fd = tap_fd;
509		fds[i].events = POLLIN;
510		fds[i].revents = 0;
511	}
512
513	free(usb_fds);
514
515	return 0;
516}
517
518void cb_add_pollfd(int fd, short events, void *user_data)
519{
520	alloc_fds();
521}
522
523void cb_remove_pollfd(int fd, void *user_data)
524{
525	alloc_fds();
526}
527
528static int init(void)
529{
530	unsigned char req_data[MAX_PACKET_LEN];
531	int len;
532	int r;
533
534	alloc_transfers();
535
536	wmlog_msg(2, "Continuous async read start...");
537	CHECK_DISCONNECTED(libusb_submit_transfer(req_transfer));
538
539	len = fill_protocol_info_req(req_data,
540			USB_HOST_SUPPORT_SELECTIVE_SUSPEND | USB_HOST_SUPPORT_DL_SIX_BYTES_HEADER |
541			USB_HOST_SUPPORT_UL_SIX_BYTES_HEADER | USB_HOST_SUPPORT_DL_MULTI_PACKETS);
542	set_data(req_data, len);
543
544	process_events_by_mask(500, WDS_PROTO_FLAGS);
545
546	len = fill_mac_lowlevel_req(req_data);
547	set_data(req_data, len);
548
549	process_events_by_mask(500, WDS_OTHER);
550
551	len = fill_init_cmd(req_data);
552	set_data(req_data, len);
553
554	len = fill_string_info_req(req_data);
555	set_data(req_data, len);
556
557	process_events_by_mask(500, WDS_CHIP | WDS_FIRMWARE);
558
559	wmlog_msg(1, "Chip info: %s", wd_status.chip);
560	wmlog_msg(1, "Firmware info: %s", wd_status.firmware);
561
562	len = fill_diode_control_cmd(req_data, diode_on);
563	set_data(req_data, len);
564
565	len = fill_mac_req(req_data);
566	set_data(req_data, len);
567
568	process_events_by_mask(500, WDS_MAC);
569
570	wmlog_msg(1, "MAC: %02x:%02x:%02x:%02x:%02x:%02x", wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
571
572	len = fill_string_info_req(req_data);
573	set_data(req_data, len);
574
575	process_events_by_mask(500, WDS_CHIP | WDS_FIRMWARE);
576
577	len = fill_auth_policy_req(req_data);
578	set_data(req_data, len);
579
580	process_events_by_mask(500, WDS_OTHER);
581
582	len = fill_auth_method_req(req_data);
583	set_data(req_data, len);
584
585	process_events_by_mask(500, WDS_OTHER);
586
587	len = fill_auth_set_cmd(req_data, ssid);
588	set_data(req_data, len);
589
590	return 0;
591}
592
593static int scan_loop(void)
594{
595	unsigned char req_data[MAX_PACKET_LEN];
596	int len;
597	DIR *wimax_dir;
598	FILE *wimax_file;
599
600	while (1)
601	{
602		if((wimax_dir = opendir("/tmp/wimax")) == NULL)
603			mkdir("/tmp/wimax", 0777);
604		else
605			closedir(wimax_dir);
606
607		if (wd_status.link_status == 0) {
608			len = fill_find_network_req(req_data, 1);
609			set_data(req_data, len);
610
611			process_events_by_mask(5000, WDS_LINK_STATUS);
612
613			if((wimax_file = fopen("/tmp/wimax/link_status", "w+")) != NULL){
614				fprintf(wimax_file, "%d", wd_status.link_status);
615				fclose(wimax_file);
616			}
617
618			wmlog_msg(2, "Network not found.");
619		} else {
620			len = fill_connection_params_req(req_data);
621			set_data(req_data, len);
622
623			process_events_by_mask(500, WDS_RSSI | WDS_CINR | WDS_TXPWR | WDS_FREQ | WDS_BSID);
624
625			wmlog_msg(0, "RSSI: %d   CINR: %f   TX Power: %d   Frequency: %d", wd_status.rssi, wd_status.cinr, wd_status.txpwr, wd_status.freq);
626			wmlog_msg(0, "BSID: %02x:%02x:%02x:%02x:%02x:%02x", wd_status.bsid[0], wd_status.bsid[1], wd_status.bsid[2], wd_status.bsid[3], wd_status.bsid[4], wd_status.bsid[5]);
627
628			if((wimax_file = fopen("/tmp/wimax/link_status", "w+")) != NULL){
629				fprintf(wimax_file, "%d", wd_status.link_status);
630				fclose(wimax_file);
631			}
632
633			if((wimax_file = fopen("/tmp/wimax/mac", "w+")) != NULL){
634				fprintf(wimax_file, "%02x:%02x:%02x:%02x:%02x:%02x", wd_status.mac[0], wd_status.mac[1], wd_status.mac[2], wd_status.mac[3], wd_status.mac[4], wd_status.mac[5]);
635				fclose(wimax_file);
636			}
637
638			if((wimax_file = fopen("/tmp/wimax/rssi", "w+")) != NULL){
639				fprintf(wimax_file, "%hd", wd_status.rssi);
640				fclose(wimax_file);
641			}
642
643			if((wimax_file = fopen("/tmp/wimax/cinr", "w+")) != NULL){
644				fprintf(wimax_file, "%f", wd_status.cinr);
645				fclose(wimax_file);
646			}
647
648			if((wimax_file = fopen("/tmp/wimax/bsid", "w+")) != NULL){
649				fprintf(wimax_file, "%02x:%02x:%02x:%02x:%02x:%02x", wd_status.bsid[0], wd_status.bsid[1], wd_status.bsid[2], wd_status.bsid[3], wd_status.bsid[4], wd_status.bsid[5]);
650				fclose(wimax_file);
651			}
652
653			if((wimax_file = fopen("/tmp/wimax/txpwr", "w+")) != NULL){
654				fprintf(wimax_file, "%hu", wd_status.txpwr);
655				fclose(wimax_file);
656			}
657
658			if((wimax_file = fopen("/tmp/wimax/freq", "w+")) != NULL){
659				fprintf(wimax_file, "%u", wd_status.freq);
660				fclose(wimax_file);
661			}
662
663			if((wimax_file = fopen("/tmp/wimax/state", "w+")) != NULL){
664				fprintf(wimax_file, "%d", wd_status.state);
665				fclose(wimax_file);
666			}
667
668			len = fill_state_req(req_data);
669			set_data(req_data, len);
670
671			process_events_by_mask(500, WDS_STATE);
672
673			wmlog_msg(2, "State: %s   Number: %d   Response: %d", wimax_states[wd_status.state], wd_status.state, wd_status.link_status);
674
675			if (first_nego_flag) {
676				first_nego_flag = 0;
677				len = fill_find_network_req(req_data, 2);
678				set_data(req_data, len);
679			}
680
681			process_events_by_mask(5000, WDS_LINK_STATUS);
682		}
683
684		sleep(SCAN_INTERVAL);
685	}
686
687	return 0;
688}
689
690/* print usage information */
691void usage(const char *progname)
692{
693	printf("Usage: %s [options]\n", progname);
694	printf("Options:\n");
695	printf("  -v, --verbose               increase the log level\n");
696	printf("  -q, --quiet                 switch off logging\n");
697	printf("  -d, --daemonize             daemonize after startup\n");
698	printf("  -l, --log-file=FILE         write log to the FILE instead of the other\n");
699	printf("                              methods\n");
700	printf("  -o, --diode-off             turn off the diode (diode is on by default)\n");
701	printf("  -f, --detach-dvd            detach pseudo-DVD kernel driver on startup\n");
702	printf("      --device=VID:PID        specify the USB device by VID:PID\n");
703	printf("      --exact-device=BUS/DEV  specify the exact USB bus/device (use with care!)\n");
704	printf("  -V, --version               print the version number\n");
705	printf("      --ssid                  specify SSID, a friendly name that identifies a\n");
706	printf("                              particular 802.16e wireless network\n");
707	printf("  -e, --event-script=FILE     specify path to the event script\n");
708	printf("  -h, --help                  display this help\n");
709}
710
711/* print version */
712void version()
713{
714	printf("%s %s\n", PACKAGE_NAME, get_madwimax_version());
715}
716
717static void parse_args(int argc, char **argv)
718{
719	while (1)
720	{
721		int c;
722		/* getopt_long stores the option index here. */
723		int option_index = 0;
724		static struct option long_options[] =
725		{
726			{"verbose",		no_argument,		0, 'v'},
727			{"quiet",		no_argument,		0, 'q'},
728			{"daemonize",		no_argument,		0, 'd'},
729			{"log-file",		required_argument,	0, 'l'},
730			{"diode-off",		no_argument,		0, 'o'},
731			{"detach-dvd",		no_argument,		0, 'f'},
732			{"device",		required_argument,	0, 1},
733			{"exact-device",	required_argument,	0, 2},
734			{"version",		no_argument,		0, 'V'},
735			{"ssid",		required_argument,	0, 3},
736			{"event-script",	required_argument,	0, 'e'},
737			{"help",		no_argument,		0, 'h'},
738			{"debug",		no_argument,		0, 'D'},
739			{0, 0, 0, 0}
740		};
741
742		c = getopt_long(argc, argv, "vqdl:ofVe:hD", long_options, &option_index);
743
744		/* detect the end of the options. */
745		if (c == -1)
746			break;
747
748		switch (c)
749		{
750			case 'D': {
751					set_wmlog_level(2);
752					break;
753				}
754			case 'v': {
755					inc_wmlog_level();
756					break;
757				}
758			case 'q': {
759					set_wmlog_level(-1);
760					break;
761				}
762			case 'd': {
763					daemonize = 1;
764					break;
765				}
766			case 'l': {
767					logfile = fopen(optarg, "a");
768					if (logfile == NULL) {
769						fprintf(stderr, "Error opening log file '%s': ", optarg);
770						perror(NULL);
771						exit(1);
772					}
773					break;
774				}
775			case 'o': {
776					diode_on = 0;
777					break;
778				}
779			case 'f': {
780					detach_dvd = 1;
781					break;
782				}
783			case 'V': {
784					version();
785					exit(0);
786					break;
787				}
788			case 'h': {
789					usage(argv[0]);
790					exit(0);
791					break;
792				}
793			case 1: {
794					char *delim = strchr(optarg, ':');
795
796					if (delim != NULL) {
797						unsigned long int vid, pid;
798						char *c1, *c2;
799
800						*delim = 0;
801
802						vid = strtoul(optarg, &c1, 16);
803						pid = strtoul(delim + 1, &c2, 16);
804						if (!*c1 && !*c2 && vid < 0x10000 && pid < 0x10000) {
805							match_method = MATCH_BY_VID_PID;
806							match_params.vid = vid;
807							match_params.pid = pid;
808							break;
809						}
810					}
811
812					fprintf(stderr, "Error parsing VID:PID combination.\n");
813					exit(1);
814					break;
815				}
816			case 2: {
817					char *delim = strchr(optarg, '/');
818
819					if (delim != NULL) {
820						unsigned long int bus, dev;
821						char *c1, *c2;
822
823						*delim = 0;
824
825						bus = strtoul(optarg, &c1, 10);
826						dev = strtoul(delim + 1, &c2, 10);
827						if (!*c1 && !*c2) {
828							match_method = MATCH_BY_BUS_DEV;
829							match_params.bus = bus;
830							match_params.dev = dev;
831							break;
832						}
833					}
834
835					fprintf(stderr, "Error parsing BUS/DEV combination.\n");
836					exit(1);
837					break;
838				}
839			case 3: {
840					ssid = optarg;
841					break;
842				}
843			case 'e': {
844					event_script = optarg;
845					break;
846				}
847			case '?': {
848					/* getopt_long already printed an error message. */
849					usage(argv[0]);
850					exit(1);
851					break;
852				}
853			default: {
854					exit(1);
855				}
856		}
857	}
858}
859
860static void exit_release_resources(int code)
861{
862	if(tap_fd >= 0) {
863		if_down();
864		while (wait(NULL) > 0) {}
865		if_release();
866		while (wait(NULL) > 0) {}
867	}
868	if(ctx != NULL) {
869		if(req_transfer != NULL) {
870			libusb_cancel_transfer(req_transfer);
871			libusb_free_transfer(req_transfer);
872		}
873		libusb_set_pollfd_notifiers(ctx, NULL, NULL, NULL);
874		if(fds != NULL) {
875			free(fds);
876		}
877		if(devh != NULL) {
878			libusb_release_interface(devh, 0);
879			if (kernel_driver_active)
880				libusb_attach_kernel_driver(devh, 0);
881			libusb_unlock_events(ctx);
882			libusb_close(devh);
883		}
884		libusb_exit(ctx);
885	}
886	if(logfile != NULL) {
887		fclose(logfile);
888	}
889	exit(code);
890}
891
892static void sighandler_exit(int signum) {
893	exit_release_resources(0);
894}
895
896static void sighandler_wait_child(int signum) {
897	int status;
898	wait3(&status, WNOHANG, NULL);
899	wmlog_msg(2, "Child exited with status %d", status);
900}
901
902int main(int argc, char **argv)
903{
904	struct sigaction sigact;
905	int r = 1;
906
907	parse_args(argc, argv);
908
909	sigact.sa_handler = sighandler_exit;
910	sigemptyset(&sigact.sa_mask);
911	sigact.sa_flags = 0;
912	sigaction(SIGINT, &sigact, NULL);
913	sigaction(SIGTERM, &sigact, NULL);
914	sigaction(SIGQUIT, &sigact, NULL);
915	sigact.sa_handler = sighandler_wait_child;
916	sigaction(SIGCHLD, &sigact, NULL);
917
918	if (logfile != NULL) {
919		set_wmlogger(argv[0], WMLOGGER_FILE, logfile);
920	} else if (daemonize) {
921		set_wmlogger(argv[0], WMLOGGER_SYSLOG, NULL);
922	} else {
923		set_wmlogger(argv[0], WMLOGGER_FILE, stderr);
924	}
925
926	if (daemonize) {
927		daemon(0, 0);
928	}
929
930	r = libusb_init(&ctx);
931	if (r < 0) {
932		wmlog_msg(0, "failed to initialise libusb");
933		exit_release_resources(1);
934	}
935
936	devh = find_wimax_device();
937	if (devh == NULL) {
938		wmlog_msg(0, "Could not find/open device");
939		exit_release_resources(1);
940	}
941
942	wmlog_msg(0, "Device found");
943
944	if (detach_dvd && libusb_kernel_driver_active(devh, IF_DVD) == 1) {
945		r = libusb_detach_kernel_driver(devh, IF_DVD);
946		if (r < 0) {
947			wmlog_msg(0, "kernel driver detach error %d", r);
948		} else {
949			wmlog_msg(0, "detached pseudo-DVD kernel driver");
950		}
951	}
952
953	if (libusb_kernel_driver_active(devh, IF_MODEM) == 1) {
954		kernel_driver_active = 1;
955		r = libusb_detach_kernel_driver(devh, IF_MODEM);
956		if (r < 0) {
957			wmlog_msg(0, "kernel driver detach error %d", r);
958		} else {
959			wmlog_msg(0, "detached modem kernel driver");
960		}
961	}
962
963	r = libusb_claim_interface(devh, IF_MODEM);
964	if (r < 0) {
965		wmlog_msg(0, "Claim usb interface error %d", r);
966		exit_release_resources(1);
967	}
968	wmlog_msg(0, "Claimed interface");
969
970	alloc_fds();
971	libusb_set_pollfd_notifiers(ctx, cb_add_pollfd, cb_remove_pollfd, NULL);
972
973	r = init();
974	if (r < 0) {
975		wmlog_msg(0, "init error %d", r);
976		exit_release_resources(1);
977	}
978
979	if_create();
980	cb_add_pollfd(tap_fd, POLLIN, NULL);
981
982	r = scan_loop();
983	if (r < 0) {
984		wmlog_msg(0, "scan_loop error %d", r);
985		exit_release_resources(1);
986	}
987
988	exit_release_resources(0);
989	return 0;
990}
991
992