chiark / gitweb /
36d7d81a1126dbbf283918a55f966315842b7904
[elogind.git] / mobile-action-modeswitch.c
1 /*
2  * Mobile action cable mode switcher
3  *
4  * Copyright (C) 2008 - 2010  Dan Williams <dcbw@redhat.com>
5  * Copyright (C) 2008  Peter Henn <support@option.com>
6  *
7  * Heavily based on the 'ozerocdoff' tool by Peter Henn.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details:
18  */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <unistd.h>
24 #include <signal.h>
25 #include <stdarg.h>
26 #include <getopt.h>
27
28 #include <usb.h>
29
30 #include "utils.h"
31
32 #include "ma8280p_us.h"
33
34 struct usb_dev_handle *handle = NULL;
35
36 typedef struct usb_device * (*FindFunc) (int vid, int pid);
37 typedef int (*SwitchFunc) (struct usb_dev_handle *dh, struct usb_device *dev);
38
39 typedef enum {
40         ST_UNKNOWN = 0,
41         ST_MA8280P
42 } SwitchType;
43
44 typedef struct SwitchEntry {
45         SwitchType st;
46         const char *clopt;
47         FindFunc find_func;
48         SwitchFunc switch_func;
49 } SwitchEntry;
50
51 static SwitchEntry switch_types[] = {
52         { ST_MA8280P, "mobile-action-8280p", NULL, ma8280p_switch },
53         { ST_UNKNOWN, NULL, NULL }
54 };
55
56 static struct usb_device *
57 generic_find (int vid, int pid)
58 {
59         struct usb_bus *bus;
60         struct usb_device *dev;
61
62         for (bus = usb_get_busses(); bus; bus = bus->next) {
63                 for (dev = bus->devices; dev; dev = dev->next) {
64                         if (dev->descriptor.idVendor == vid && dev->descriptor.idProduct == pid) {
65                                 debug ("Found device '%s'", dev->filename);
66                                 return dev;
67                         }
68                 }
69         }
70         return NULL;
71 }
72
73 static void
74 release_usb_device (int param)
75 {
76         usb_release_interface (handle, 0);
77         usb_close (handle);
78 }
79
80 static void
81 print_usage (void)
82 {
83         printf ("Usage: mobile-action-modeswitch [-hdq] [-l <file>] -v <vendor-id> -p <product-id> -t <type>\n"
84                 " -h, --help               show this help message\n"
85                 " -v, --vendor <n>         target USB vendor ID\n"
86                 " -p, --product <n>        target USB product ID\n"
87                 " -t, --type <type>        type of switch to attempt, varies by device:\n"
88                 "                               mobile-action-8280p   - For Mobile Action 8xxxP USB cables\n"
89                 " -l, --log <file>         log output to a file\n"
90                 " -q, --quiet              don't print anything to stdout\n"
91                 " -d, --debug              display debugging messages\n\n"
92                 "Examples:\n"
93                 "   mobile-action-modeswitch -v 0x0df7 -p 0x8000 -t mobile-action-8280p\n");
94 }
95
96 static SwitchEntry *
97 parse_type (const char *s)
98 {
99         SwitchEntry *entry = &switch_types[0];
100
101         while (entry->clopt) {
102                 if (!strcmp (entry->clopt, s))
103                         return entry;
104                 entry++;
105         }
106
107         return NULL;
108 }
109
110 static void
111 do_exit (int val)
112 {
113         log_shutdown ();
114         exit (val);
115 }
116
117 int main(int argc, char **argv)
118 {
119         static struct option options[] = {
120                 { "help",        no_argument,       NULL, 'h' },
121                 { "vendor",  required_argument, NULL, 'v' },
122                 { "product", required_argument, NULL, 'p' },
123                 { "type",    required_argument, NULL, 't' },
124                 { "log",     required_argument, NULL, 'l' },
125                 { "debug",   no_argument,       NULL, 'd' },
126                 { "quiet",   no_argument,       NULL, 'q' },
127                 { NULL, 0, NULL, 0}
128         };
129
130         struct usb_device *dev;
131         int vid = 0, pid = 0;
132         const char *logpath = NULL;
133         char buffer[256];
134         int ret, quiet = 0, debug = 0;
135         SwitchEntry *sentry = NULL;
136
137         while (1) {
138                 int option;
139
140                 option = getopt_long(argc, argv, "hv:p:l:t:dq", options, NULL);
141                 if (option == -1)
142                         break;
143
144                 switch (option) {
145                 case 'v':
146                         vid = strtol (optarg, NULL, 0);
147                         break;
148                 case 'p':
149                         pid = strtol (optarg, NULL, 0);
150                         break;
151                 case 't':
152                         sentry = parse_type (optarg);
153                         if (!sentry) {
154                                 error ("unknown switch type '%s'", optarg);
155                                 print_usage ();
156                                 exit (1);
157                         }
158                         break;
159                 case 'l':
160                         logpath = optarg;
161                         break;
162                 case 'q':
163                         quiet = 1;
164                         break;
165                 case 'd':
166                         debug = 1;
167                         break;
168                 case 'h':
169                 default:
170                         print_usage ();
171                         exit (1);
172                 }
173         }
174
175         if (log_startup (logpath, debug, quiet)) {
176                 fprintf (stderr, "Couldn't open/create logfile %s", logpath);
177                 exit (2);
178         }
179
180         if (!sentry) {
181                 if (!quiet)
182                         print_usage ();
183                 else
184                         error ("missing device switch type.");
185                 do_exit (3);
186         }
187
188         if (!vid || !pid) {
189                 if (!quiet)
190                         print_usage ();
191                 else
192                         error ("missing vendor and device IDs.");
193                 do_exit (3);
194         }
195
196         usb_init();
197
198         if (usb_find_busses() < 0) {
199                 error ("no USB busses found.");
200                 do_exit (4);
201         }
202
203         if (usb_find_devices() < 0) {
204                 error ("no USB devices found.");
205                 do_exit (4);
206         }
207
208         if (sentry->find_func)
209                 dev = (*sentry->find_func) (vid, pid);
210         else
211                 dev = generic_find (vid, pid);
212         if (dev == NULL) {
213                 error ("no device found.");
214                 do_exit (5);
215         }
216
217         handle = usb_open (dev);
218         if (handle == NULL) {
219                 error ("%s: could not access the device.",
220                          dev->filename);
221                 do_exit (6);
222         }
223
224         /* detach running default driver */
225         signal (SIGTERM, release_usb_device);
226         ret = usb_get_driver_np (handle, 0, buffer, sizeof (buffer));
227         if (ret == 0) {
228                 debug ("%s: found already attached driver '%s'", dev->filename, buffer);
229
230                 ret = usb_detach_kernel_driver_np (handle, 0);
231                 if (ret != 0) {
232                         debug ("%s: error: unable to detach current driver.", dev->filename);
233                         usb_close (handle);
234                         do_exit (7);
235                 }
236         }
237
238         ret = usb_claim_interface (handle, 0);
239         if (ret != 0) {
240                 debug ("%s: couldn't claim device's USB interface: %d.",
241                        dev->filename, ret);
242                 usb_close (handle);
243                 do_exit (8);
244         }
245
246         ret = (*sentry->switch_func) (handle, dev);
247         if (ret < 0) {
248                 debug ("%s: failed to switch device to serial mode.", dev->filename);
249                 usb_release_interface (handle, 0);
250                 usb_close (handle);
251                 do_exit(9);
252         }
253
254         usb_release_interface (handle, 0);
255
256         ret = usb_close (handle);
257         if (ret < 0)
258                 debug ("%s: failed to close the device.", dev->filename);
259
260         do_exit (0);
261         return 0;
262 }