chiark / gitweb /
cdrom_id: open non-mounted optical media with O_EXCL
[elogind.git] / extras / modem-modeswitch / modem-modeswitch.c
1 /*
2  * Modem mode switcher
3  *
4  * Copyright (C) 2008  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 #include "option.h"
34
35 struct usb_dev_handle *handle = NULL;
36
37 typedef struct usb_device * (*FindFunc) (int vid, int pid);
38 typedef int (*SwitchFunc) (struct usb_dev_handle *dh, struct usb_device *dev);
39
40 typedef enum {
41         ST_UNKNOWN = 0,
42         ST_OPTION_ZEROCD,
43         ST_MA8280P
44 } SwitchType;
45
46 typedef struct SwitchEntry {
47         SwitchType st;
48         const char *clopt;
49         FindFunc find_func;
50         SwitchFunc switch_func;
51 } SwitchEntry;
52
53 static SwitchEntry switch_types[] = {
54         { ST_OPTION_ZEROCD, "option-zerocd", option_zerocd_find, option_zerocd_switch },
55         { ST_MA8280P, "mobile-action-8280p", NULL, ma8280p_switch },
56         { ST_UNKNOWN, NULL, NULL }
57 };
58
59 static struct usb_device *
60 generic_find (int vid, int pid)
61 {
62         struct usb_bus *bus;
63         struct usb_device *dev;
64
65         for (bus = usb_get_busses(); bus; bus = bus->next) {
66                 for (dev = bus->devices; dev; dev = dev->next) {
67                         if (dev->descriptor.idVendor == vid && dev->descriptor.idProduct == pid) {
68                                 debug ("Found device '%s'", dev->filename);
69                                 return dev;
70                         }
71                 }
72         }
73         return NULL;
74 }
75
76 static void
77 release_usb_device (int param)
78 {
79         usb_release_interface (handle, 0);
80         usb_close (handle);
81 }
82
83 static void
84 print_usage (void)
85 {
86         printf ("Usage: modem-modeswitch [-hdq] [-l <file>] -v <vendor-id> -p <product-id> -t <type>\n"
87                 " -h, --help               show this help message\n"
88                 " -v, --vendor <n>         target USB vendor ID\n"
89                 " -p, --product <n>        target USB product ID\n"
90                 " -t, --type <type>        type of switch to attempt, varies by device:\n"
91                 "                               option-zerocd         - For many newer Option N.V. devices\n"
92                 "                               mobile-action-8280p   - For Mobile Action 8xxxP USB cables\n"
93                 " -l, --log <file>         log output to a file\n"
94                 " -q, --quiet              don't print anything to stdout\n"
95                 " -d, --debug              display debugging messages\n\n"
96                 "Examples:\n"
97                 "   modem-modeswitch -v 0x0af0 -p 0xc031 -t option-zerocd\n");
98 }
99
100 static SwitchEntry *
101 parse_type (const char *s)
102 {
103         SwitchEntry *entry = &switch_types[0];
104
105         while (entry->clopt) {
106                 if (!strcmp (entry->clopt, s))
107                         return entry;
108                 entry++;
109         }
110
111         return NULL;
112 }
113
114 static void
115 do_exit (int val)
116 {
117         log_shutdown ();
118         exit (val);
119 }
120
121 int main(int argc, char **argv)
122 {
123         static struct option options[] = {
124                 { "help",        no_argument,       NULL, 'h' },
125                 { "vendor",  required_argument, NULL, 'v' },
126                 { "product", required_argument, NULL, 'p' },
127                 { "type",    required_argument, NULL, 't' },
128                 { "log",     required_argument, NULL, 'l' },
129                 { "debug",   no_argument,       NULL, 'd' },
130                 { "quiet",   no_argument,       NULL, 'q' },
131                 { NULL, 0, NULL, 0}
132         };
133
134         struct usb_device *dev;
135         int vid = 0, pid = 0;
136         const char *logpath = NULL;
137         char buffer[256];
138         int ret, quiet = 0, debug = 0;
139         SwitchEntry *sentry = NULL;
140
141         while (1) {
142                 int option;
143
144                 option = getopt_long(argc, argv, "hv:p:l:t:dq", options, NULL);
145                 if (option == -1)
146                         break;
147
148                 switch (option) {
149                 case 'v':
150                         vid = strtol (optarg, NULL, 0);
151                         break;
152                 case 'p':
153                         pid = strtol (optarg, NULL, 0);
154                         break;
155                 case 't':
156                         sentry = parse_type (optarg);
157                         if (!sentry) {
158                                 error ("unknown switch type '%s'", optarg);
159                                 print_usage ();
160                                 exit (1);
161                         }
162                         break;
163                 case 'l':
164                         logpath = optarg;
165                         break;
166                 case 'q':
167                         quiet = 1;
168                         break;
169                 case 'd':
170                         debug = 1;
171                         break;
172                 case 'h':
173                 default:
174                         print_usage ();
175                         exit (1);
176                 }
177         }
178
179         if (log_startup (logpath, debug, quiet)) {
180                 fprintf (stderr, "Couldn't open/create logfile %s", logpath);
181                 exit (2);
182         }
183
184         if (!sentry) {
185                 if (!quiet)
186                         print_usage ();
187                 else
188                         error ("missing device switch type.");
189                 do_exit (3);
190         }
191
192         if (!vid || !pid) {
193                 if (!quiet)
194                         print_usage ();
195                 else
196                         error ("missing vendor and device IDs.");
197                 do_exit (3);
198         }
199
200         usb_init();
201
202         if (usb_find_busses() < 0) {
203                 error ("no USB busses found.");
204                 do_exit (4);
205         }
206
207         if (usb_find_devices() < 0) {
208                 error ("no USB devices found.");
209                 do_exit (4);
210         }
211
212         if (sentry->find_func)
213                 dev = (*sentry->find_func) (vid, pid);
214         else
215                 dev = generic_find (vid, pid);
216         if (dev == NULL) {
217                 error ("no device found.");
218                 do_exit (5);
219         }
220
221         handle = usb_open (dev);
222         if (handle == NULL) {
223                 error ("%s: could not access the device.",
224                          dev->filename);
225                 do_exit (6);
226         }
227
228         /* detach running default driver */
229         signal (SIGTERM, release_usb_device);
230         ret = usb_get_driver_np (handle, 0, buffer, sizeof (buffer));
231         if (ret == 0) {
232                 debug ("%s: found already attached driver '%s'", dev->filename, buffer);
233
234                 ret = usb_detach_kernel_driver_np (handle, 0);
235                 if (ret != 0) {
236                         debug ("%s: error: unable to detach current driver.", dev->filename);
237                         usb_close (handle);
238                         do_exit (7);
239                 }
240         }
241
242         ret = usb_claim_interface (handle, 0);
243         if (ret != 0) {
244                 debug ("%s: couldn't claim device's USB interface: %d.",
245                        dev->filename, ret);
246                 usb_close (handle);
247                 do_exit (8);
248         }
249
250         ret = (*sentry->switch_func) (handle, dev);
251         if (ret < 0) {
252                 debug ("%s: failed to switch device to modem mode.", dev->filename);
253                 usb_release_interface (handle, 0);
254                 usb_close (handle);
255                 do_exit(9);
256         }
257
258         usb_release_interface (handle, 0);
259
260         ret = usb_close (handle);
261         if (ret < 0)
262                 debug ("%s: failed to close the device.", dev->filename);
263
264         do_exit (0);
265         return 0;
266 }