maint: added make-redirect.sh script to generate htaccess redirects
[barry/progweb.git] / tools / bcharge_libusb_1_0.cc
blob2ced8f325068bf353a0e83fbc6fd1bd16c41c4da
1 ///
2 /// \file bcharge_libusb_1_0.cc
3 /// Talk to the Blackberry just enough to change the Max Power
4 /// to 500mA. Cycles through all devices attached to USB,
5 /// attempting to set all matching Blackberry devices to charge.
6 ///
7 /// This file is part of the Barry project:
8 ///
9 /// http://www.netdirect.ca/software/packages/barry
10 /// http://sourceforge.net/projects/barry
11 ///
12 /// Compile with the following command (needs libusb 1.0):
13 ///
14 /// g++ -o bcharge bcharge.cc -I/usr/include/libusb -lusb-1.0
15 ///
18 Copyright (C) 2006-2011, Net Direct Inc. (http://www.netdirect.ca/)
19 Portions Copyright (C) 2011, RealVNC Ltd.
21 This program is free software; you can redistribute it and/or modify
22 it under the terms of the GNU General Public License as published by
23 the Free Software Foundation; either version 2 of the License, or
24 (at your option) any later version.
26 This program is distributed in the hope that it will be useful,
27 but WITHOUT ANY WARRANTY; without even the implied warranty of
28 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
30 See the GNU General Public License in the COPYING file at the
31 root directory of this project for more details.
34 #include <libusb.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <errno.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <string>
43 #include <barry/common.h>
44 #include "i18n.h"
46 enum ModeType {
47 NO_CHANGE,
48 PEARL_CLASSIC_MODE_0001, // force classic mode
49 PEARL_DUAL_MODE_0004, // force dual mode
50 CONDITIONAL_DUAL_MODE, // set dual mode if no class 255
51 // interface is found (database iface)
54 std::string udev_devpath;
55 std::string sysfs_path = "/sys";
57 void Usage()
59 printf(
60 "bcharge - Adjust Blackberry charging modes\n"
61 " Copyright 2006-2011, Net Direct Inc. (http://www.netdirect.ca/)\n"
62 "\n"
63 " -d Set to dual mode (0004)\n"
64 " -o Set a Pearl to old Blackberry mode (0001)\n"
65 " -g Set dual mode only if database interface class 255\n"
66 " is not found\n"
67 "\n"
68 " -h This help message\n"
69 " -p devpath The devpath argument from udev. If specified, will attempt\n"
70 " to adjust USB suspend settings to keep the device charging.\n"
71 " -s path The path where sysfs is mounted. Defaults to '/sys'\n"
72 "\n"
76 void control(libusb_device_handle *dev, int requesttype, int request, int value,
77 int index, unsigned char *bytes, int size, int timeout)
79 int result = libusb_control_transfer(dev, requesttype, request, value, index,
80 bytes, size, timeout);
81 if( result < 0 ) {
82 printf("\nusb_control_transfer failed: code: %d\n", result);
86 void charge(libusb_device_handle *handle)
88 // the special sauce... these steps seem to do the trick
89 // for the 7750 series... needs testing on others
90 unsigned char buffer[2];
91 control(handle, 0xc0, 0xa5, 0, 1, buffer, 2, 100);
92 control(handle, 0x40, 0xa2, 0, 1, buffer, 0, 100);
95 void pearl_classic_mode(libusb_device_handle *handle)
97 unsigned char buffer[2];
98 // use this for "old style" interface: product ID 0001
99 control(handle, 0xc0, 0xa9, 0, 1, buffer, 2, 100);
102 void pearl_dual_mode(libusb_device_handle *handle)
104 unsigned char buffer[2];
105 // Product ID 0004
106 control(handle, 0xc0, 0xa9, 1, 1, buffer, 2, 100);
109 int find_interface(libusb_device_handle *handle, int iface_class)
111 // search the configuration descriptor for the given class ID
112 libusb_device *dev = libusb_get_device(handle);
113 struct libusb_config_descriptor *cfg = NULL;
114 int err = libusb_get_config_descriptor(dev, BLACKBERRY_CONFIGURATION, &cfg);
115 int ret = -1;
117 if( err == 0 && cfg ) {
119 for( unsigned i = 0;
120 cfg->interface && ret == -1 &&
121 i < cfg->bNumInterfaces;
122 i++ ) {
123 const struct libusb_interface *iface = &cfg->interface[i];
124 for( int a = 0;
125 iface->altsetting && ret == -1 &&
126 a < iface->num_altsetting;
127 a++ ) {
128 const struct libusb_interface_descriptor *id = &iface->altsetting[a];
129 if( id->bInterfaceClass == iface_class )
130 ret = id->bInterfaceNumber;
133 libusb_free_config_descriptor(cfg);
134 cfg = NULL;
137 return ret;
140 int find_mass_storage_interface(libusb_device_handle *handle)
142 int iface = find_interface(handle, LIBUSB_CLASS_MASS_STORAGE);
144 if( iface == -1 ) {
145 // if we get here, then we didn't find the Mass Storage
146 // interface ... this should never happen, but if it does,
147 // assume the device is showing product ID 0006, and the
148 // Mass Storage interface is interface #0
149 printf("Can't find Mass Storage interface, assuming 0.\n");
150 return 0;
152 else {
153 return iface;
157 void driver_conflict(libusb_device_handle *handle)
159 // this is called if the first usb_set_configuration()
160 // failed... this most probably means that usb_storage
161 // has already claimed the Mass Storage interface,
162 // in which case we politely tell it to go away.
163 printf("Detecting possible kernel driver conflict, trying to resolve...\n");
165 int iface = find_mass_storage_interface(handle);
166 int err = libusb_detach_kernel_driver(handle, iface);
167 if( err != 0 )
168 printf("libusb_detach_kernel_driver() failed: %d\n", err);
170 err = libusb_set_configuration(handle, BLACKBERRY_CONFIGURATION);
171 if( err != 0 )
172 printf("libusb_set_configuration() failed: %d\n", err);
175 // returns true if device mode was modified, false otherwise
176 bool process(libusb_device *dev, ModeType mode)
178 bool apply = false;
179 printf("Found device #%d-%d...",
180 libusb_get_bus_number(dev),
181 libusb_get_device_address(dev));
183 // open
184 libusb_device_handle *handle;
185 int err = libusb_open(dev, &handle);
186 if( err != 0 ) {
187 printf("unable to open device, %d\n", err);
188 return false;
191 struct libusb_config_descriptor *config = NULL;
192 err = libusb_get_active_config_descriptor(dev, &config);
193 if( err == 0 ) {
194 // adjust power
195 if( config && config->MaxPower < 250 ) {
196 printf("adjusting charge setting");
197 charge(handle);
198 apply = true;
200 else {
201 printf("already at 500mA");
203 printf("\n");
204 libusb_free_config_descriptor(config);
205 config = NULL;
206 } else {
207 printf("failed to discover power level\n");
210 // Retrieve the device descriptor
211 struct libusb_device_descriptor desc;
212 err = libusb_get_device_descriptor(dev, &desc);
213 if( err != 0 ) {
214 printf("failed to get device descriptor: %d\n", err);
215 return false;
218 // adjust Pearl mode
219 switch( mode )
221 case NO_CHANGE:
222 printf("...no Pearl mode adjustment");
223 break;
225 case PEARL_CLASSIC_MODE_0001:
226 if( desc.idProduct != PRODUCT_RIM_BLACKBERRY ) {
227 printf("...adjusting Pearl mode to single");
228 pearl_classic_mode(handle);
229 apply = true;
231 else {
232 printf("...already in classic/single mode");
234 break;
236 case PEARL_DUAL_MODE_0004:
237 if( desc.idProduct != PRODUCT_RIM_PEARL_DUAL ) {
238 printf("...adjusting Pearl mode to dual");
239 pearl_dual_mode(handle);
240 apply = true;
242 else {
243 printf("...already in dual mode");
245 break;
247 case CONDITIONAL_DUAL_MODE:
248 if( find_interface(handle, 255) == -1 ) {
249 printf("...no database iface found, setting dual mode");
250 pearl_dual_mode(handle);
251 apply = true;
253 else {
254 printf("...found database iface, no change");
256 break;
258 default:
259 printf("Bug: default case");
260 break;
262 printf("\n");
264 // apply changes
265 if( apply ) {
266 if( libusb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
267 driver_conflict(handle);
269 // the Blackberry Pearl doesn't reset itself after the above,
270 // so do it ourselves
271 if( mode == PEARL_DUAL_MODE_0004 ) {
273 // It has been observed that the 8830 behaves both like
274 // a Pearl device (in that it has mass storage +
275 // database modes) as well as a Classic device in
276 // that it resets itself and doesn't need an explicit
277 // reset call.
279 // In order to deal with this, we insert a brief sleep.
280 // If it is an 8830, it will reset itself and the
281 // following reset call will fail. If it is a Pearl,
282 // the reset will work as normal.
284 sleep(1);
285 err = libusb_reset_device(handle);
286 if( err != 0 ) {
287 printf("\nlibusb_reset_device() failed: %d\n", err);
291 printf("...done");
293 else {
294 printf("...no change");
296 printf("\n");
298 // cleanup
299 libusb_close(handle);
300 return apply;
303 bool power_write(const std::string &file, const std::string &value)
305 // attempt to open the state file
306 int fd = open(file.c_str(), O_RDWR);
307 if( fd == -1 ) {
308 printf("autosuspend adjustment failure: (file: %s): %s\n",
309 file.c_str(),
310 strerror(errno));
311 return false;
314 int written = write(fd, value.data(), value.size());
315 int error = errno;
316 close(fd);
318 if( written < 0 || (size_t)written != value.size() ) {
319 printf("autosuspend adjustment failure (write): (file: %s): %s\n",
320 file.c_str(),
321 strerror(error));
322 return false;
325 printf("autosuspend adjustment: wrote %s to %s\n",
326 value.c_str(), file.c_str());
327 return true;
331 // Checks for USB suspend, and enables the device if suspended.
333 // Kernel 2.6.21 behaves with autosuspend=0 meaning off, while 2.6.22
334 // and higher needs autosuspend=-1 to turn it off. In 2.6.22, a value
335 // of 0 means "immediate" instead of "never".
337 // Version 2.6.22 adds variables internal to the system called
338 // autosuspend_disabled and autoresume_disabled. These are controlled by the
339 // /sys/class/usb_device/*/device/power/level file. (See below)
341 // Here's a summary of files under device/power. These may or may not exist
342 // depending on your kernel version and configuration.
345 // autosuspend
346 // -1 or 0 means off, depending on kernel,
347 // otherwise it is the number of seconds to
348 // autosuspend
350 // level
351 // with the settings:
353 // on - suspend is disabled, device is fully powered
354 // auto - suspend is controlled by the kernel (default)
355 // suspend - suspend is enabled permanently
357 // You can write these strings to the file to control
358 // behaviour on a per-device basis.
360 // echo on > /sys/usb_device/.../device/power/level
362 // state
363 // current state of device
364 // 0 - fully powered
365 // 2 - suspended
367 // You can write these numbers to control behaviour, but
368 // any change you make here might change automatically
369 // if autosuspend is on.
371 // echo -n 0 > /sys/usb_device/.../device/power/state
373 // wakeup
374 // unknown
377 // Given the above facts, use the following heuristics to try to disable
378 // autosuspend for the Blackberry:
380 // - if level exists, write "on" to it
381 // - if autosuspend exists, write -1 to it
382 // - if error, write 0 to it
383 // - if neither of the above work, and state exists, write 0 to it
385 void resume()
387 if( udev_devpath.size() == 0 )
388 return; // nothing to do
390 // let sysfs settle a bit
391 sleep(5);
393 std::string power_path = sysfs_path + "/" + udev_devpath + "/device/power/";
395 if( !power_write(power_path + "level", "on\n") )
396 if( !power_write(power_path + "autosuspend", "-1\n") )
397 if( !power_write(power_path + "autosuspend", "0\n") )
398 power_write(power_path + "state", "0");
401 int main(int argc, char *argv[])
403 ModeType mode = NO_CHANGE;
405 INIT_I18N(PACKAGE);
408 // allow -o command line switch to choose which mode to use for
409 // Blackberry Pearls:
410 // Dual: 0004 -d
411 // With switch: 0001 -o
414 // process command line options
415 for(;;) {
416 int cmd = getopt(argc, argv, "dogp:s:h");
417 if( cmd == -1 )
418 break;
420 switch( cmd )
422 case 'd': // Dual
423 mode = PEARL_DUAL_MODE_0004;
424 break;
426 case 'o': // Classic style pearl
427 mode = PEARL_CLASSIC_MODE_0001;
428 break;
430 case 'g': // Guess whether dual is needed
431 mode = CONDITIONAL_DUAL_MODE;
432 break;
434 case 'p': // udev devpath
435 udev_devpath = optarg;
436 break;
438 case 's': // where sysfs is mounted
439 sysfs_path = optarg;
440 break;
442 case 'h': // help!
443 default:
444 Usage();
445 return 0;
450 libusb_context *usbctx = NULL;
451 int err = libusb_init(&usbctx);
452 if( err != 0 ) {
453 printf("Failed to start up USB: %d\n", err);
454 return 1;
457 libusb_device **devices = NULL;
458 int count = libusb_get_device_list(usbctx, &devices);
460 printf("Scanning for Blackberry devices...\n");
462 for( int i = 0; i < count; ++i ) {
463 // Is this a blackberry?
464 struct libusb_device_descriptor desc;
465 err = libusb_get_device_descriptor(devices[i], &desc);
466 if( err == 0 && desc.idVendor == VENDOR_RIM ) {
467 if( !process(devices[i], mode) )
468 resume();
472 if( devices )
473 libusb_free_device_list(devices, 1);
474 libusb_exit(usbctx);