| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Small test for gobi modem handles. Avoids many dependencies. |
| // Code liberally cribbed from gobi-modem-reset.c |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <linux/limits.h> |
| #include <stdarg.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <syslog.h> |
| #include <unistd.h> |
| |
| #include "gobi/GobiConnectionMgmtAPI.h" |
| |
| static void log(const char *format, ...) { |
| va_list args; |
| va_start(args, format); |
| vfprintf(stderr, format, args); |
| fputc('\n', stderr); |
| fflush(stderr); |
| vsyslog(LOG_INFO, format, args); |
| va_end(args); |
| } |
| |
| /* Returns true if the supplied devid string is valid. Valid devid strings are |
| * of the form: [0-9]+ '-' [0-9]+ */ |
| static bool isdevid(const char *devid) { |
| const char *d = devid; |
| |
| /* Check the part before the dash... */ |
| if (!d || !isdigit(*d++)) |
| return false; |
| |
| while (*d && isdigit(*d)) |
| d++; |
| |
| /* ... the dash itself ... */ |
| if (*d++ != '-') |
| return false; |
| |
| /* ... and the part after the dash. */ |
| if (!isdigit(*d++)) |
| return false; |
| |
| while (*d && isdigit(*d)) |
| d++; |
| |
| return *d == '\0'; |
| } |
| |
| static int reset(const char *dev) { |
| char path[PATH_MAX]; |
| int fd = -1; |
| int to_return = 1; |
| |
| if (snprintf(path, sizeof(path), "%s/authorized", dev) == sizeof(path)) { |
| log("Could not build path"); |
| goto done; |
| } |
| |
| fd = open(path, O_WRONLY | O_TRUNC | O_NOFOLLOW); |
| if (fd < 0) { |
| log("Could not open authorized file: %s", path); |
| goto done; |
| } |
| |
| log("deauthorizing: %s", path); |
| if (write(fd, "0", 1) != 1) { |
| log("write(0) failed: %d", errno); |
| goto done; |
| } |
| |
| log("sleeping for 1 second"); |
| sleep(1); |
| |
| log("reauthorizing: %s", path); |
| if (write(fd, "1", 1) != 1) { |
| log("write(1) failed: %d", errno); |
| goto done; |
| } |
| to_return = 0; |
| |
| done: |
| if (fd >= 0) { |
| close(fd); |
| } |
| return to_return; |
| } |
| |
| static void usage(const char *progname) { |
| fprintf(stderr, "Usage: %s <file | api> <usb-dev-id> <qcqmi-dev>\n", |
| progname); |
| fprintf(stderr, "\tExample: %s api 1-2 /dev/qcqmi0\n", progname); |
| fprintf(stderr, "To determine the usb dev id, run \n"); |
| fprintf(stderr, "\tls -d /sys/bus/usb/drivers/{QCUSBNet2k,gobi}/*-*\n"); |
| fprintf(stderr, "and use the string before the :\n"); |
| } |
| |
| struct DEVICE_ELEMENT { |
| char deviceNode[256]; |
| char deviceKey[16]; |
| }; |
| |
| // Connect to first modem present. Returns 0 on success. |
| int connect_modem() { |
| const int MAX_MODEMS = 16; |
| ULONG rc; |
| |
| DEVICE_ELEMENT devices[MAX_MODEMS]; |
| BYTE num_devices = MAX_MODEMS; |
| |
| rc = QCWWANEnumerateDevices(&num_devices, |
| reinterpret_cast<BYTE*>(devices)); |
| if (rc != 0) { |
| log("Could not enumerate: %d", rc); |
| return -1; |
| } |
| |
| if (num_devices == 0) { |
| log("No devices found"); |
| return -1; |
| } |
| |
| rc = QCWWANConnect(devices[0].deviceNode, devices[0].deviceKey); |
| if (rc !=0) { |
| log("Could not QCWWANConnect to modem: %d", rc); |
| return -1; |
| } |
| return 0; |
| } |
| |
| |
| int main(int argc, char *argv[]) { |
| char usb_path[PATH_MAX]; |
| bool use_qcwwan = 1; |
| |
| openlog("gobi_handle_tester", LOG_PID, LOG_USER); |
| if (argc != 4) { |
| usage(argv[0]); |
| return 1; |
| } |
| |
| if (getuid() != 0) { |
| fprintf(stderr, "This program must be run as root\n"); |
| return 1; |
| } |
| |
| int argument = 1; |
| const char *operation = argv[argument++]; |
| const char *usb_device_id = argv[argument++]; |
| const char *qmi_device_path = argv[argument++]; |
| |
| if (!isdevid(usb_device_id)) { |
| fprintf(stderr, "Could not parse device id %s\n", usb_device_id); |
| usage(argv[0]); |
| return 1; |
| } |
| |
| if (strcmp(operation, "file") == 0) { |
| // Just open /dev/qcqmi0 as a file |
| use_qcwwan = 0; |
| } else if (strcmp(operation, "api") == 0) { |
| // Use QCWWANConnect to open a connection to the device |
| use_qcwwan = 1; |
| } else { |
| fprintf(stderr, "Could not understand operation: %s\n", operation); |
| usage(argv[0]); |
| return 1; |
| } |
| |
| snprintf(usb_path, |
| sizeof(usb_path), |
| "/sys/bus/usb/devices/%s", usb_device_id); |
| |
| log("operation: %s use_qcwwan: %d", operation, use_qcwwan); |
| log("USB path: %s", usb_path); |
| log("device path: %s", qmi_device_path); |
| |
| int fd = -1; |
| ULONG rc; |
| |
| if (use_qcwwan) { |
| if (connect_modem() != 0) { |
| log("Failure connecting to modem"); |
| return 4; |
| } |
| } else { |
| fd = open(qmi_device_path, O_RDWR); |
| if (fd < 0) { |
| fprintf(stderr, "Could not open device %s\n", qmi_device_path); |
| return 3; |
| } |
| } |
| |
| if (reset(usb_path) != 0) { |
| log("Reset failed. Exiting"); |
| return 6; |
| } |
| |
| log("sleeping while waiting for kernel handles to expire"); |
| |
| for (int i = 0 ; i < 45; ++i) { |
| sleep(1); |
| fprintf(stderr, "."); |
| } |
| fputc('\n', stderr); |
| |
| log("closing"); |
| |
| if (use_qcwwan) { |
| rc = QCWWANDisconnect(); |
| if (rc != 0) { |
| log("Failed on disconnect: %d", rc); |
| } |
| } |
| |
| if (fd >= 0) { |
| rc = close(fd); |
| if (rc != 0) { |
| log("Failed on close: %d", rc); |
| } |
| } |
| |
| log("exiting"); |
| return 0; |
| } |