| /* |
| * rfkill userspace tool |
| */ |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <stdbool.h> |
| #include <sys/types.h> |
| #include <sys/poll.h> |
| #include <sys/time.h> |
| |
| #include "rfkill.h" |
| #include "core.h" |
| |
| static void rfkill_event(void) |
| { |
| struct rfkill_event event; |
| struct timeval tv; |
| struct pollfd p; |
| ssize_t len; |
| int fd, n; |
| |
| fd = open("/dev/rfkill", O_RDONLY); |
| if (fd < 0) { |
| perror("Can't open RFKILL control device"); |
| return; |
| } |
| |
| memset(&p, 0, sizeof(p)); |
| p.fd = fd; |
| p.events = POLLIN | POLLHUP; |
| |
| while (1) { |
| n = poll(&p, 1, -1); |
| if (n < 0) { |
| perror("Failed to poll RFKILL control device"); |
| break; |
| } |
| |
| if (n == 0) |
| continue; |
| |
| len = read(fd, &event, sizeof(event)); |
| if (len < 0) { |
| perror("Reading of RFKILL events failed"); |
| break; |
| } |
| |
| if (len != RFKILL_EVENT_SIZE_V1) { |
| fprintf(stderr, "Wrong size of RFKILL event\n"); |
| continue; |
| } |
| |
| gettimeofday(&tv, NULL); |
| printf("%ld.%06u: idx %u type %u op %u soft %u hard %u\n", |
| (long) tv.tv_sec, (unsigned int) tv.tv_usec, |
| event.idx, event.type, event.op, event.soft, event.hard); |
| fflush(stdout); |
| } |
| |
| close(fd); |
| } |
| |
| static const char *get_name(__u32 idx) |
| { |
| static char name[128]; |
| ssize_t len; |
| char *pos, filename[64]; |
| int fd; |
| |
| snprintf(filename, sizeof(filename) - 1, |
| "/sys/class/rfkill/rfkill%u/name", idx); |
| |
| fd = open(filename, O_RDONLY); |
| if (fd < 0) |
| return NULL; |
| |
| memset(name, 0, sizeof(name)); |
| len = read(fd, name, sizeof(name) - 1); |
| |
| pos = strchr(name, '\n'); |
| if (pos) |
| *pos = '\0'; |
| |
| close(fd); |
| |
| return name; |
| } |
| |
| static const char *type2string(enum rfkill_type type) |
| { |
| switch (type) { |
| case RFKILL_TYPE_ALL: |
| return "All"; |
| case RFKILL_TYPE_WLAN: |
| return "Wireless LAN"; |
| case RFKILL_TYPE_BLUETOOTH: |
| return "Bluetooth"; |
| case RFKILL_TYPE_UWB: |
| return "Ultra-Wideband"; |
| case RFKILL_TYPE_WIMAX: |
| return "WiMAX"; |
| case RFKILL_TYPE_WWAN: |
| return "Wireless WAN"; |
| case RFKILL_TYPE_GPS: |
| return "GPS"; |
| case RFKILL_TYPE_FM: |
| return "FM"; |
| case NUM_RFKILL_TYPES: |
| return NULL; |
| } |
| |
| return NULL; |
| } |
| |
| struct rfkill_type_str { |
| enum rfkill_type type; |
| const char *name; |
| }; |
| static const struct rfkill_type_str rfkill_type_strings[] = { |
| { .type = RFKILL_TYPE_ALL, .name = "all" }, |
| { .type = RFKILL_TYPE_WLAN, .name = "wifi" }, |
| { .type = RFKILL_TYPE_WLAN, .name = "wlan" }, /* alias */ |
| { .type = RFKILL_TYPE_BLUETOOTH, .name = "bluetooth" }, |
| { .type = RFKILL_TYPE_UWB, .name = "uwb" }, |
| { .type = RFKILL_TYPE_UWB, .name = "ultrawideband" }, /* alias */ |
| { .type = RFKILL_TYPE_WIMAX, .name = "wimax" }, |
| { .type = RFKILL_TYPE_WWAN, .name = "wwan" }, |
| { .type = RFKILL_TYPE_GPS, .name = "gps" }, |
| { .type = RFKILL_TYPE_FM, .name = "fm" }, |
| { .name = NULL } |
| }; |
| |
| struct rfkill_id { |
| union { |
| enum rfkill_type type; |
| __u32 index; |
| }; |
| enum { |
| RFKILL_IS_INVALID, |
| RFKILL_IS_TYPE, |
| RFKILL_IS_INDEX, |
| } result; |
| }; |
| |
| static struct rfkill_id rfkill_id_to_type(const char *s) |
| { |
| const struct rfkill_type_str *p; |
| struct rfkill_id ret; |
| |
| if (islower(*s)) { |
| for (p = rfkill_type_strings; p->name != NULL; p++) { |
| if ((strlen(s) == strlen(p->name)) && (!strcmp(s,p->name))) { |
| ret.type = p->type; |
| ret.result = RFKILL_IS_TYPE; |
| return ret; |
| } |
| } |
| } else if (isdigit(*s)) { |
| /* assume a numeric character implies an index. */ |
| ret.index = atoi(s); |
| ret.result = RFKILL_IS_INDEX; |
| return ret; |
| } |
| |
| ret.result = RFKILL_IS_INVALID; |
| return ret; |
| } |
| |
| static int rfkill_list(const char *param) |
| { |
| struct rfkill_id id = { .result = RFKILL_IS_INVALID }; |
| struct rfkill_event event; |
| const char *name; |
| ssize_t len; |
| int fd; |
| |
| if (param) { |
| id = rfkill_id_to_type(param); |
| if (id.result == RFKILL_IS_INVALID) { |
| fprintf(stderr, "Bogus %s argument '%s'.\n", "list", param); |
| return 2; |
| } |
| /* don't filter "all" */ |
| if (id.result == RFKILL_IS_TYPE && id.type == RFKILL_TYPE_ALL) |
| id.result = RFKILL_IS_INVALID; |
| } |
| |
| fd = open("/dev/rfkill", O_RDONLY); |
| if (fd < 0) { |
| perror("Can't open RFKILL control device"); |
| return 1; |
| } |
| |
| if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) { |
| perror("Can't set RFKILL control device to non-blocking"); |
| close(fd); |
| return 1; |
| } |
| |
| while (1) { |
| len = read(fd, &event, sizeof(event)); |
| if (len < 0) { |
| if (errno == EAGAIN) |
| break; |
| perror("Reading of RFKILL events failed"); |
| break; |
| } |
| |
| if (len != RFKILL_EVENT_SIZE_V1) { |
| fprintf(stderr, "Wrong size of RFKILL event\n"); |
| continue; |
| } |
| |
| if (event.op != RFKILL_OP_ADD) |
| continue; |
| |
| /* filter out unwanted results */ |
| switch (id.result) |
| { |
| case RFKILL_IS_TYPE: |
| if (event.type != id.type) |
| continue; |
| break; |
| case RFKILL_IS_INDEX: |
| if (event.idx != id.index) |
| continue; |
| break; |
| case RFKILL_IS_INVALID:; /* must be last */ |
| } |
| |
| name = get_name(event.idx); |
| |
| printf("%u: %s: %s\n", event.idx, name, |
| type2string(event.type)); |
| printf("\tSoft blocked: %s\n", event.soft ? "yes" : "no"); |
| printf("\tHard blocked: %s\n", event.hard ? "yes" : "no"); |
| } |
| |
| close(fd); |
| return 0; |
| } |
| |
| static int rfkill_block(__u8 block, const char *param) |
| { |
| struct rfkill_id id; |
| struct rfkill_event event; |
| ssize_t len; |
| int fd; |
| |
| id = rfkill_id_to_type(param); |
| if (id.result == RFKILL_IS_INVALID) { |
| fprintf(stderr, "Bogus %s argument '%s'.\n", block ? "block" : "unblock", param); |
| return 2; |
| } |
| |
| fd = open("/dev/rfkill", O_RDWR); |
| if (fd < 0) { |
| perror("Can't open RFKILL control device"); |
| return 1; |
| } |
| |
| memset(&event, 0, sizeof(event)); |
| switch (id.result) { |
| case RFKILL_IS_TYPE: |
| event.op = RFKILL_OP_CHANGE_ALL; |
| event.type = id.type; |
| break; |
| case RFKILL_IS_INDEX: |
| event.op = RFKILL_OP_CHANGE; |
| event.idx = id.index; |
| break; |
| case RFKILL_IS_INVALID:; /* must be last */ |
| } |
| event.soft = block; |
| |
| len = write(fd, &event, sizeof(event)); |
| if (len < 0) |
| perror("Failed to change RFKILL state"); |
| |
| close(fd); |
| return 0; |
| } |
| |
| static const char *argv0; |
| |
| static void usage(void) |
| { |
| const struct rfkill_type_str *p; |
| |
| fprintf(stderr, "Usage:\t%s [options] command\n", argv0); |
| fprintf(stderr, "Options:\n"); |
| fprintf(stderr, "\t--version\tshow version (%s)\n", rfkill_version); |
| fprintf(stderr, "Commands:\n"); |
| fprintf(stderr, "\thelp\n"); |
| fprintf(stderr, "\tevent\n"); |
| fprintf(stderr, "\tlist [IDENTIFIER]\n"); |
| fprintf(stderr, "\tblock IDENTIFIER\n"); |
| fprintf(stderr, "\tunblock IDENTIFIER\n"); |
| fprintf(stderr, "where IDENTIFIER is the index no. of an rfkill switch or one of:\n"); |
| fprintf(stderr, "\t<idx>"); |
| for (p = rfkill_type_strings; p->name != NULL; p++) |
| fprintf(stderr, " %s", p->name); |
| fprintf(stderr, "\n"); |
| } |
| |
| static void version(void) |
| { |
| printf("rfkill %s\n", rfkill_version); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int ret = 0; |
| |
| /* strip off self */ |
| argc--; |
| argv0 = *argv++; |
| |
| if (argc > 0 && strcmp(*argv, "--version") == 0) { |
| version(); |
| return 0; |
| } |
| |
| if (argc == 0 || strcmp(*argv, "help") == 0) { |
| usage(); |
| return 0; |
| } |
| |
| if (strcmp(*argv, "event") == 0) { |
| rfkill_event(); |
| } else if (strcmp(*argv, "list") == 0) { |
| argc--; |
| argv++; |
| rfkill_list(*argv); /* NULL is acceptable */ |
| } else if (strcmp(*argv, "block") == 0 && argc > 1) { |
| argc--; |
| argv++; |
| ret = rfkill_block(1,*argv); |
| } else if (strcmp(*argv, "unblock") == 0 && argc > 1) { |
| argc--; |
| argv++; |
| ret = rfkill_block(0,*argv); |
| } else { |
| usage(); |
| return 1; |
| } |
| |
| return ret; |
| } |