<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -22,11 +22,20 @@
  * Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
-#include &lt;usb.h&gt;
-#include &lt;unistd.h&gt;
-#include &lt;string.h&gt;
-#include &lt;stdio.h&gt;
+#define _GNU_SOURCE
+
 #include &lt;errno.h&gt;
+#include &lt;getopt.h&gt;
+#include &lt;signal.h&gt;
+#include &lt;stdarg.h&gt;
+#include &lt;stdio.h&gt;
+#include &lt;string.h&gt;
+#include &lt;syslog.h&gt;
+#include &lt;unistd.h&gt;
+#include &lt;usb.h&gt;
+
+#define PACKAGE_NAME &quot;mf626-helper&quot;
+#define PACKAGE_VERSION &quot;0.1&quot;
 
 #define ZTE_VENDOR 0x19d2
 #define ZTE_PRODUCT_MF626 0x0031
@@ -40,6 +49,73 @@
 
 #define USB_TIMEOUT 200
 
+int force;
+int use_syslog;
+int verbose;
+
+int gotsignal;
+
+void sighandler(int signal)
+{
+    gotsignal = 1;
+}
+
+void perr(const char* format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    if(use_syslog)
+    {
+        char* msg;
+        if(-1 == vasprintf(&amp;msg, format, ap))
+            abort();
+        syslog(LOG_ERR, &quot;%s: %s&quot;, msg, strerror(errno));
+        free(msg);
+    }
+    else
+    {
+        fputs(&quot;E: &quot;, stderr);
+        vfprintf(stderr, format, ap);
+        fprintf(stderr, &quot;: %s\n&quot;, strerror(errno));
+    }
+    va_end(ap);
+    exit(1);
+}
+
+void err(const char* format, ...)
+{
+    va_list ap;
+    va_start(ap, format);
+    if(use_syslog)
+        vsyslog(LOG_ERR, format, ap);
+    else
+    {
+        fputs(&quot;E: &quot;, stderr);
+        vfprintf(stderr, format, ap);
+        fputc('\n', stderr);
+    }
+    va_end(ap);
+    exit(1);
+}
+
+void info(const char* format, ...)
+{
+    if(!verbose)
+        return;
+
+    va_list ap;
+    va_start(ap, format);
+    if(use_syslog)
+        vsyslog(LOG_INFO, format, ap);
+    else
+    {
+        fputs(&quot;I: &quot;, stderr);
+        vfprintf(stderr, format, ap);
+        fputc('\n', stderr);
+    }
+    va_end(ap);
+}
+
 int usb_bulk_pwrite(usb_dev_handle* dev, int ep, char* bytes, int size, int timeout)
 {
     int towrite = size;
@@ -54,36 +130,79 @@ int usb_bulk_pwrite(usb_dev_handle* dev, int ep, char* bytes, int size, int time
     return size;
 }
 
-int main(void)
+static struct option longopts[] = {
+    { &quot;force&quot;, 0, NULL, 0 },
+    { &quot;syslog&quot;, 0, NULL, 0 },
+    { &quot;verbose&quot;, 0, NULL, 0 },
+    { &quot;version&quot;, 0, NULL, 0 },
+    { &quot;help&quot;, 0, NULL, 0 },
+    { 0, 0, 0, 0 }
+};
+
+void version()
+{
+    printf(PACKAGE_NAME &quot; &quot; PACKAGE_VERSION &quot;\n&quot;);
+    exit(0);
+}
+
+void usage()
+{
+    printf(&quot;Usage: &quot; PACKAGE_NAME &quot; [OPTIONS] &lt;USB bus number&gt; &lt;USB device number&gt;\n&quot;);
+    printf(&quot;Connect to MF626 device on specified USB address and send magic string to it.\n&quot;);
+    printf(&quot;\n&quot;);
+    printf(&quot;  -f, --force    Proceed even if USB device is not recognised as a modem\n&quot;);
+    printf(&quot;                 or claimed by unknown driver\n&quot;);
+    printf(&quot;  -s, --syslog   Use syslog instead of stderr for logging\n&quot;);
+    printf(&quot;  -v, --verbose  Be verbose\n&quot;);
+    printf(&quot;  -V, --version  Print version and exit\n&quot;);
+    printf(&quot;  -h, --help     Print this help message and exit\n&quot;);
+    printf(&quot;\n&quot;);
+    printf(&quot;Bus number and device number must be an unsigned integers.\n&quot;);
+    printf(&quot;Both numbers must be in range [0..255].\n&quot;);
+}
+
+usb_dev_handle* open_modem(struct usb_device* d)
 {
-    struct usb_bus* bus;
-    struct usb_device* device;
-    usb_dev_handle* dev = NULL;
+    if(d-&gt;descriptor.idVendor == ZTE_VENDOR
+       &amp;&amp; d-&gt;descriptor.idProduct == ZTE_PRODUCT_MF626)
+        info(&quot;Found ZTE626 modem&quot;);
+    else
+    {
+        if(!force)
+            err(&quot;USB address is used by unknown device: %04x:%04x.&quot;,
+                d-&gt;descriptor.idVendor, d-&gt;descriptor.idProduct);
+
+        info(&quot;USB address is used by unknown device: %04x:%04x. &quot;
+             &quot;Proceeding as requested.&quot;,
+             d-&gt;descriptor.idVendor, d-&gt;descriptor.idProduct);
+    }
+
+    usb_dev_handle* dev = usb_open(d);
+    if(!dev)
+        perr(&quot;Unable to open USB device: usb_open&quot;);
+    return dev;
+}
+
+usb_dev_handle* find_modem(const char* busstr, const char* devstr)
+{
+    struct usb_bus* b;
+    struct usb_device* d;
 
     usb_init();
     usb_find_busses();
     usb_find_devices();
 
-    for(bus = usb_busses; bus; bus = bus-&gt;next)
-        for(device = bus-&gt;devices; device; device = device-&gt;next)
-            if(device-&gt;descriptor.idVendor == ZTE_VENDOR
-               &amp;&amp; device-&gt;descriptor.idProduct == ZTE_PRODUCT_MF626)
-            {
-                dev = usb_open(device);
-                if(!dev)
-                {
-                    perror(&quot;usb_open&quot;);
-                    return 1;
-                }
-                break;
-            }
+    for(b = usb_busses; b; b = b-&gt;next)
+        if(!strcmp(b-&gt;dirname, busstr))
+            for(d = b-&gt;devices; d; d = d-&gt;next)
+                if(!strcmp(d-&gt;filename, devstr))
+                    return open_modem(d);
 
-    if(!dev)
-    {
-        fprintf(stderr, &quot;Unable to find modem.\n&quot;);
-        return 2;
-    }
+    return NULL;
+}
 
+void setup_modem(usb_dev_handle* dev)
+{
     if(usb_claim_interface(dev, ZTE_MF626_CONTROL_INTERFACE) &lt; 0)
     {
         if(errno == EBUSY)
@@ -91,70 +210,164 @@ int main(void)
             char driver[256];
             if(usb_get_driver_np(dev, ZTE_MF626_CONTROL_INTERFACE,
                                  driver, sizeof(driver)) &lt; 0)
-            {
-                perror(&quot;usb_get_driver_np&quot;);
-                return 1;
-            }
+                perr(&quot;usb_get_driver_np&quot;);
 
-            if(strcmp(driver, &quot;onda&quot;) &amp;&amp; strcmp(driver, &quot;option&quot;))
+            if(strcmp(driver, &quot;onda&quot;)
+               &amp;&amp; strcmp(driver, &quot;option&quot;)
+               &amp;&amp; strcmp(driver, &quot;usb-serial&quot;))
             {
-                fprintf(stderr, &quot;Unknown driver claimed the interface: %s\n&quot;,
-                        driver);
-                return 1;
+                if(force)
+                    info(&quot;Device is claimed by unknown driver: %s. &quot;
+                         &quot;Proceeding as requested.&quot;, driver);
+                else
+                    err(&quot;Device is claimed by unknown driver: %s.&quot;, driver);
             }
 
             if(usb_detach_kernel_driver_np(dev, ZTE_MF626_CONTROL_INTERFACE) &lt; 0)
-            {
-                perror(&quot;usb_detach_kernel_driver_np&quot;);
-                return 1;
-            }
+                perr(&quot;usb_detach_kernel_driver_np&quot;);
 
             if(usb_claim_interface(dev, ZTE_MF626_CONTROL_INTERFACE) &lt; 0)
-            {
-                perror(&quot;usb_claim_interface&quot;);
-                return 1;
-            }
+                perr(&quot;usb_claim_interface&quot;);
         }
         else
-        {
-            perror(&quot;usb_claim_interface&quot;);
-            return 1;
-        }
+            perr(&quot;usb_claim_interface&quot;);
     }
 
     if(usb_set_altinterface(dev, ZTE_MF626_CONTROL_INTERFACE_SETTING) &lt; 0)
-    {
-        perror(&quot;usb_set_altinterface&quot;);
-        return 1;
-    }
+        perr(&quot;usb_set_altinterface&quot;);
 
     if(usb_clear_halt(dev, ZTE_MF626_CONTROL_ENDPOINT) &lt; 0)
+        perr(&quot;usb_clear_halt&quot;);
+}
+
+int ping_modem(usb_dev_handle* dev)
+{
+    if(usb_bulk_pwrite(dev, ZTE_MF626_CONTROL_ENDPOINT,
+                       MAGIC_STRING, strlen(MAGIC_STRING),
+                       USB_TIMEOUT) &lt; 0)
     {
-        perror(&quot;usb_clear_halt&quot;);
-        return 1;
+        if(errno == ENODEV)
+        {
+            usb_close(dev);
+            return 0;
+        }
+        else if(errno == EAGAIN)
+            return 1;
+        else
+        {
+            usb_close(dev);
+            perr(&quot;usb_bulk_write&quot;);
+        }
     }
 
+    return 1;
+}
+
+int main(int argc, char** argv)
+{
+    openlog(&quot;mf626-helper&quot;, LOG_PID, LOG_DAEMON);
+
     for(;;)
     {
-        if(usb_bulk_pwrite(dev, ZTE_MF626_CONTROL_ENDPOINT,
-                           MAGIC_STRING, strlen(MAGIC_STRING),
-                           USB_TIMEOUT) &lt; 0)
+        int option_index = 0;
+        int c = getopt_long(argc, argv, &quot;fsvVh&quot;, longopts, &amp;option_index);
+        if(c == -1)
+            break;
+
+        switch(c)
         {
-            if(errno == ENODEV)
-            {
-                fprintf(stderr, &quot;Modem was disconnected. Exiting.\n&quot;);
-                usb_close(dev);
-                return 0;
-            }
-            else if(errno == EAGAIN)
-                continue;
-            else
+        case 0:
+            switch(option_index)
             {
-                perror(&quot;usb_bulk_write&quot;);
-                usb_close(dev);
-                return 1;
+            case 0:
+                force = 1;
+                break;
+            case 1:
+                use_syslog = 1;
+                break;
+            case 2:
+                verbose = 1;
+                break;
+            case 3:
+                version();
+            case 4:
+                usage();
+                exit(0);
+            default:
+                err(&quot;Error in getopt: unexpected long option %d&quot;, option_index);
+                exit(1);
             }
+        case 'f':
+            force = 1;
+            break;
+        case 's':
+            use_syslog = 1;
+            break;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'V':
+            version();
+        case 'h':
+            usage();
+            exit(0);
+        case '?':
+            usage();
+            exit(1);
+        default:
+            err(&quot;Error in getopt: unexpected return code 0%o&quot;, c);
+        }
+    }
+
+    if(optind != argc - 2)
+    {
+        err(&quot;Exactly two non-option arguments is required, got %d. Try --help.&quot;,
+            argc - optind);
+    }
+
+    unsigned int busnum;
+    if(!sscanf(argv[optind], &quot;%u&quot;, &amp;busnum))
+        err(&quot;Unable to parse USB bus number: %s&quot;, argv[optind]);
+    if(busnum &gt; 255)
+        err(&quot;USB bus number is out of range [0,255]: %s&quot;, argv[optind]);
+
+    char busstr[4];
+    sprintf(busstr, &quot;%03u&quot;, busnum);
+
+    unsigned int devnum;
+    if(!sscanf(argv[optind+1], &quot;%u&quot;, &amp;devnum))
+        err(&quot;Unable to parse USB device number: %s&quot;, argv[optind+1]);
+    if(devnum &gt; 255)
+        err(&quot;USB device number is out of range [0,255]: %s&quot;, argv[optind+1]);
+
+    char devstr[4];
+    sprintf(devstr, &quot;%03u&quot;, devnum);
+
+    info(&quot;Trying to open %s:%s&quot;, busstr, devstr);
+
+    usb_dev_handle* dev = find_modem(busstr, devstr);
+    if(!dev)
+        err(&quot;Unable to find modem.&quot;);
+
+    setup_modem(dev);
+
+    struct sigaction act;
+    memset(&amp;act, 0, sizeof(act));
+    act.sa_handler = &amp;sighandler;
+    sigaction(SIGTERM, &amp;act, NULL);
+    sigaction(SIGINT, &amp;act, NULL);
+
+    while(ping_modem(dev))
+    {
+        if(gotsignal)
+        {
+            info(&quot;Got signal. Terminating&quot;);
+            exit(0);
         }
+
         sleep(MAGIC_STRING_INTERVAL);
     }
+
+    info(&quot;Modem %s:%s has been disconnected. Terminating.&quot;, busstr, devstr);
+    exit(0);
 }</diff>
      <filename>mf626-helper.c</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>ee2242638e1955244917c2be9511c6fee9fbe573</id>
    </parent>
  </parents>
  <author>
    <name>Mikhail Gusarov</name>
    <email>dottedmag@dottedmag.net</email>
  </author>
  <url>http://github.com/dottedmag/mf626-helper/commit/d11c9c4e6bbf56196317bd2af980ac34fba0f300</url>
  <id>d11c9c4e6bbf56196317bd2af980ac34fba0f300</id>
  <committed-date>2008-12-09T13:51:36-08:00</committed-date>
  <authored-date>2008-12-09T13:51:36-08:00</authored-date>
  <message>helper reorganized and improved</message>
  <tree>ff83f1d86ca59dbf82c11830214bf436309d8960</tree>
  <committer>
    <name>Mikhail Gusarov</name>
    <email>dottedmag@dottedmag.net</email>
  </committer>
</commit>
