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