blob: 36d7d81a1126dbbf283918a55f966315842b7904 [file] [log] [blame]
/*
* Mobile action cable mode switcher
*
* Copyright (C) 2008 - 2010 Dan Williams <dcbw@redhat.com>
* Copyright (C) 2008 Peter Henn <support@option.com>
*
* Heavily based on the 'ozerocdoff' tool by Peter Henn.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details:
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdarg.h>
#include <getopt.h>
#include <usb.h>
#include "utils.h"
#include "ma8280p_us.h"
struct usb_dev_handle *handle = NULL;
typedef struct usb_device * (*FindFunc) (int vid, int pid);
typedef int (*SwitchFunc) (struct usb_dev_handle *dh, struct usb_device *dev);
typedef enum {
ST_UNKNOWN = 0,
ST_MA8280P
} SwitchType;
typedef struct SwitchEntry {
SwitchType st;
const char *clopt;
FindFunc find_func;
SwitchFunc switch_func;
} SwitchEntry;
static SwitchEntry switch_types[] = {
{ ST_MA8280P, "mobile-action-8280p", NULL, ma8280p_switch },
{ ST_UNKNOWN, NULL, NULL }
};
static struct usb_device *
generic_find (int vid, int pid)
{
struct usb_bus *bus;
struct usb_device *dev;
for (bus = usb_get_busses(); bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
if (dev->descriptor.idVendor == vid && dev->descriptor.idProduct == pid) {
debug ("Found device '%s'", dev->filename);
return dev;
}
}
}
return NULL;
}
static void
release_usb_device (int param)
{
usb_release_interface (handle, 0);
usb_close (handle);
}
static void
print_usage (void)
{
printf ("Usage: mobile-action-modeswitch [-hdq] [-l <file>] -v <vendor-id> -p <product-id> -t <type>\n"
" -h, --help show this help message\n"
" -v, --vendor <n> target USB vendor ID\n"
" -p, --product <n> target USB product ID\n"
" -t, --type <type> type of switch to attempt, varies by device:\n"
" mobile-action-8280p - For Mobile Action 8xxxP USB cables\n"
" -l, --log <file> log output to a file\n"
" -q, --quiet don't print anything to stdout\n"
" -d, --debug display debugging messages\n\n"
"Examples:\n"
" mobile-action-modeswitch -v 0x0df7 -p 0x8000 -t mobile-action-8280p\n");
}
static SwitchEntry *
parse_type (const char *s)
{
SwitchEntry *entry = &switch_types[0];
while (entry->clopt) {
if (!strcmp (entry->clopt, s))
return entry;
entry++;
}
return NULL;
}
static void
do_exit (int val)
{
log_shutdown ();
exit (val);
}
int main(int argc, char **argv)
{
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "vendor", required_argument, NULL, 'v' },
{ "product", required_argument, NULL, 'p' },
{ "type", required_argument, NULL, 't' },
{ "log", required_argument, NULL, 'l' },
{ "debug", no_argument, NULL, 'd' },
{ "quiet", no_argument, NULL, 'q' },
{ NULL, 0, NULL, 0}
};
struct usb_device *dev;
int vid = 0, pid = 0;
const char *logpath = NULL;
char buffer[256];
int ret, quiet = 0, debug = 0;
SwitchEntry *sentry = NULL;
while (1) {
int option;
option = getopt_long(argc, argv, "hv:p:l:t:dq", options, NULL);
if (option == -1)
break;
switch (option) {
case 'v':
vid = strtol (optarg, NULL, 0);
break;
case 'p':
pid = strtol (optarg, NULL, 0);
break;
case 't':
sentry = parse_type (optarg);
if (!sentry) {
error ("unknown switch type '%s'", optarg);
print_usage ();
exit (1);
}
break;
case 'l':
logpath = optarg;
break;
case 'q':
quiet = 1;
break;
case 'd':
debug = 1;
break;
case 'h':
default:
print_usage ();
exit (1);
}
}
if (log_startup (logpath, debug, quiet)) {
fprintf (stderr, "Couldn't open/create logfile %s", logpath);
exit (2);
}
if (!sentry) {
if (!quiet)
print_usage ();
else
error ("missing device switch type.");
do_exit (3);
}
if (!vid || !pid) {
if (!quiet)
print_usage ();
else
error ("missing vendor and device IDs.");
do_exit (3);
}
usb_init();
if (usb_find_busses() < 0) {
error ("no USB busses found.");
do_exit (4);
}
if (usb_find_devices() < 0) {
error ("no USB devices found.");
do_exit (4);
}
if (sentry->find_func)
dev = (*sentry->find_func) (vid, pid);
else
dev = generic_find (vid, pid);
if (dev == NULL) {
error ("no device found.");
do_exit (5);
}
handle = usb_open (dev);
if (handle == NULL) {
error ("%s: could not access the device.",
dev->filename);
do_exit (6);
}
/* detach running default driver */
signal (SIGTERM, release_usb_device);
ret = usb_get_driver_np (handle, 0, buffer, sizeof (buffer));
if (ret == 0) {
debug ("%s: found already attached driver '%s'", dev->filename, buffer);
ret = usb_detach_kernel_driver_np (handle, 0);
if (ret != 0) {
debug ("%s: error: unable to detach current driver.", dev->filename);
usb_close (handle);
do_exit (7);
}
}
ret = usb_claim_interface (handle, 0);
if (ret != 0) {
debug ("%s: couldn't claim device's USB interface: %d.",
dev->filename, ret);
usb_close (handle);
do_exit (8);
}
ret = (*sentry->switch_func) (handle, dev);
if (ret < 0) {
debug ("%s: failed to switch device to serial mode.", dev->filename);
usb_release_interface (handle, 0);
usb_close (handle);
do_exit(9);
}
usb_release_interface (handle, 0);
ret = usb_close (handle);
if (ret < 0)
debug ("%s: failed to close the device.", dev->filename);
do_exit (0);
return 0;
}