test: if using -pedantic, also use -ansi, so gettext.h can detect it correctly
[barry.git] / tools / bcharge.cc
blobc2cb206c47023b88fab7f9d70ed1f5f495bb7bc6
1 ///
2 /// \file bcharge.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):
13 ///
14 /// g++ -o bcharge bcharge.cc -lusb
15 ///
18 Copyright (C) 2006-2010, Net Direct Inc. (http://www.netdirect.ca/)
20 This program is free software; you can redistribute it and/or modify
21 it under the terms of the GNU General Public License as published by
22 the Free Software Foundation; either version 2 of the License, or
23 (at your option) any later version.
25 This program is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
29 See the GNU General Public License in the COPYING file at the
30 root directory of this project for more details.
33 #include <usb.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <fcntl.h>
41 #include <string>
42 #include <barry/common.h>
43 #include "i18n.h"
45 enum ModeType {
46 NO_CHANGE,
47 PEARL_CLASSIC_MODE_0001, // force classic mode
48 PEARL_DUAL_MODE_0004, // force dual mode
49 CONDITIONAL_DUAL_MODE, // set dual mode if no class 255
50 // interface is found (database iface)
53 std::string udev_devpath;
54 std::string sysfs_path = "/sys";
56 void Usage()
58 printf(
59 "bcharge - Adjust Blackberry charging modes\n"
60 " Copyright 2006-2010, Net Direct Inc. (http://www.netdirect.ca/)\n"
61 "\n"
62 " -d Set to dual mode (0004)\n"
63 " -o Set a Pearl to old Blackberry mode (0001)\n"
64 " -g Set dual mode only if database interface class 255\n"
65 " is not found\n"
66 "\n"
67 " -h This help message\n"
68 " -p devpath The devpath argument from udev. If specified, will attempt\n"
69 " to adjust USB suspend settings to keep the device charging.\n"
70 " -s path The path where sysfs is mounted. Defaults to '/sys'\n"
71 "\n"
75 void control(usb_dev_handle *dev, int requesttype, int request, int value,
76 int index, char *bytes, int size, int timeout)
78 int result = usb_control_msg(dev, requesttype, request, value, index,
79 bytes, size, timeout);
80 if( result < 0 ) {
81 printf("\nusb_control_msg failed: code: %d, %s\n", result,
82 usb_strerror());
86 void charge(struct usb_dev_handle *handle)
88 // the special sauce... these steps seem to do the trick
89 // for the 7750 series... needs testing on others
90 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(struct usb_dev_handle *handle)
97 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(struct usb_dev_handle *handle)
104 char buffer[2];
105 // Product ID 0004
106 control(handle, 0xc0, 0xa9, 1, 1, buffer, 2, 100);
109 int find_interface(struct usb_dev_handle *handle, int iface_class)
111 // search the configuration descriptor for the given class ID
112 struct usb_device *dev = usb_device(handle);
113 struct usb_config_descriptor *cfg = dev ? dev->config : 0;
115 if( cfg ) {
117 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
118 struct usb_interface *iface = &cfg->interface[i];
119 for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
120 struct usb_interface_descriptor *id = &iface->altsetting[a];
121 if( id->bInterfaceClass == iface_class )
122 return id->bInterfaceNumber;
127 return -1;
130 int find_mass_storage_interface(struct usb_dev_handle *handle)
132 int iface = find_interface(handle, USB_CLASS_MASS_STORAGE);
134 if( iface == -1 ) {
135 // if we get here, then we didn't find the Mass Storage
136 // interface ... this should never happen, but if it does,
137 // assume the device is showing product ID 0006, and the
138 // Mass Storage interface is interface #0
139 printf("Can't find Mass Storage interface, assuming 0.\n");
140 return 0;
142 else {
143 return iface;
147 void driver_conflict(struct usb_dev_handle *handle)
149 // this is called if the first usb_set_configuration()
150 // failed... this most probably means that usb_storage
151 // has already claimed the Mass Storage interface,
152 // in which case we politely tell it to go away.
154 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
155 printf("Detecting possible kernel driver conflict, trying to resolve...\n");
157 int iface = find_mass_storage_interface(handle);
158 if( usb_detach_kernel_driver_np(handle, iface) < 0 )
159 printf("usb_detach_kernel_driver_np() failed: %s\n", usb_strerror());
161 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
162 printf("usb_set_configuration() failed: %s\n", usb_strerror());
163 #endif
166 // returns true if device mode was modified, false otherwise
167 bool process(struct usb_device *dev, ModeType mode)
169 bool apply = false;
170 printf("Found device #%s...", dev->filename);
172 // open
173 usb_dev_handle *handle = usb_open(dev);
174 if( !handle ) {
175 printf("unable to open device\n");
176 return false;
179 // adjust power
180 if( dev->config &&
181 dev->descriptor.bNumConfigurations >= 1 &&
182 dev->config[0].MaxPower < 250 ) {
183 printf("adjusting charge setting");
184 charge(handle);
185 apply = true;
187 else {
188 printf("already at 500mA");
190 printf("\n");
192 // adjust Pearl mode
193 switch( mode )
195 case NO_CHANGE:
196 printf("...no Pearl mode adjustment");
197 break;
199 case PEARL_CLASSIC_MODE_0001:
200 if( dev->descriptor.idProduct != PRODUCT_RIM_BLACKBERRY ) {
201 printf("...adjusting Pearl mode to single");
202 pearl_classic_mode(handle);
203 apply = true;
205 else {
206 printf("...already in classic/single mode");
208 break;
210 case PEARL_DUAL_MODE_0004:
211 if( dev->descriptor.idProduct != PRODUCT_RIM_PEARL_DUAL ) {
212 printf("...adjusting Pearl mode to dual");
213 pearl_dual_mode(handle);
214 apply = true;
216 else {
217 printf("...already in dual mode");
219 break;
221 case CONDITIONAL_DUAL_MODE:
222 if( find_interface(handle, 255) == -1 ) {
223 printf("...no database iface found, setting dual mode");
224 pearl_dual_mode(handle);
225 apply = true;
227 else {
228 printf("...found database iface, no change");
230 break;
232 default:
233 printf("Bug: default case");
234 break;
236 printf("\n");
238 // apply changes
239 if( apply ) {
240 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
241 driver_conflict(handle);
243 // the Blackberry Pearl doesn't reset itself after the above,
244 // so do it ourselves
245 if( mode == PEARL_DUAL_MODE_0004 ) {
247 // It has been observed that the 8830 behaves both like
248 // a Pearl device (in that it has mass storage +
249 // database modes) as well as a Classic device in
250 // that it resets itself and doesn't need an explicit
251 // reset call.
253 // In order to deal with this, we insert a brief sleep.
254 // If it is an 8830, it will reset itself and the
255 // following reset call will fail. If it is a Pearl,
256 // the reset will work as normal.
258 sleep(1);
259 if( usb_reset(handle) < 0 ) {
260 printf("\nusb_reset failed: %s\n", usb_strerror());
264 printf("...done");
266 else {
267 printf("...no change");
269 printf("\n");
271 // cleanup
272 usb_close(handle);
273 return apply;
276 bool power_write(const std::string &file, const std::string &value)
278 // attempt to open the state file
279 int fd = open(file.c_str(), O_RDWR);
280 if( fd == -1 ) {
281 printf("autosuspend adjustment failure: (file: %s): %s\n",
282 file.c_str(),
283 strerror(errno));
284 return false;
287 int written = write(fd, value.data(), value.size());
288 int error = errno;
289 close(fd);
291 if( written < 0 || (size_t)written != value.size() ) {
292 printf("autosuspend adjustment failure (write): (file: %s): %s\n",
293 file.c_str(),
294 strerror(error));
295 return false;
298 printf("autosuspend adjustment: wrote %s to %s\n",
299 value.c_str(), file.c_str());
300 return true;
304 // Checks for USB suspend, and enables the device if suspended.
306 // Kernel 2.6.21 behaves with autosuspend=0 meaning off, while 2.6.22
307 // and higher needs autosuspend=-1 to turn it off. In 2.6.22, a value
308 // of 0 means "immediate" instead of "never".
310 // Version 2.6.22 adds variables internal to the system called
311 // autosuspend_disabled and autoresume_disabled. These are controlled by the
312 // /sys/class/usb_device/*/device/power/level file. (See below)
314 // Here's a summary of files under device/power. These may or may not exist
315 // depending on your kernel version and configuration.
318 // autosuspend
319 // -1 or 0 means off, depending on kernel,
320 // otherwise it is the number of seconds to
321 // autosuspend
323 // level
324 // with the settings:
326 // on - suspend is disabled, device is fully powered
327 // auto - suspend is controlled by the kernel (default)
328 // suspend - suspend is enabled permanently
330 // You can write these strings to the file to control
331 // behaviour on a per-device basis.
333 // echo on > /sys/usb_device/.../device/power/level
335 // state
336 // current state of device
337 // 0 - fully powered
338 // 2 - suspended
340 // You can write these numbers to control behaviour, but
341 // any change you make here might change automatically
342 // if autosuspend is on.
344 // echo -n 0 > /sys/usb_device/.../device/power/state
346 // wakeup
347 // unknown
350 // Given the above facts, use the following heuristics to try to disable
351 // autosuspend for the Blackberry:
353 // - if level exists, write "on" to it
354 // - if autosuspend exists, write -1 to it
355 // - if error, write 0 to it
356 // - if neither of the above work, and state exists, write 0 to it
358 void resume()
360 if( udev_devpath.size() == 0 )
361 return; // nothing to do
363 // let sysfs settle a bit
364 sleep(5);
366 std::string power_path = sysfs_path + "/" + udev_devpath + "/device/power/";
368 if( !power_write(power_path + "level", "on\n") )
369 if( !power_write(power_path + "autosuspend", "-1\n") )
370 if( !power_write(power_path + "autosuspend", "0\n") )
371 power_write(power_path + "state", "0");
374 int main(int argc, char *argv[])
376 struct usb_bus *busses;
377 ModeType mode = NO_CHANGE;
379 INIT_I18N(PACKAGE);
382 // allow -o command line switch to choose which mode to use for
383 // Blackberry Pearls:
384 // Dual: 0004 -d
385 // With switch: 0001 -o
388 // process command line options
389 for(;;) {
390 int cmd = getopt(argc, argv, "dogp:s:h");
391 if( cmd == -1 )
392 break;
394 switch( cmd )
396 case 'd': // Dual
397 mode = PEARL_DUAL_MODE_0004;
398 break;
400 case 'o': // Classic style pearl
401 mode = PEARL_CLASSIC_MODE_0001;
402 break;
404 case 'g': // Guess whether dual is needed
405 mode = CONDITIONAL_DUAL_MODE;
406 break;
408 case 'p': // udev devpath
409 udev_devpath = optarg;
410 break;
412 case 's': // where sysfs is mounted
413 sysfs_path = optarg;
414 break;
416 case 'h': // help!
417 default:
418 Usage();
419 return 0;
423 usb_init();
424 if( usb_find_busses() < 0 || usb_find_devices() < 0 ) {
425 printf("\nUnable to scan devices: %s\n", usb_strerror());
426 return 1;
428 busses = usb_get_busses();
430 printf("Scanning for Blackberry devices...\n");
432 struct usb_bus *bus;
433 for( bus = busses; bus; bus = bus->next ) {
434 struct usb_device *dev;
436 for (dev = bus->devices; dev; dev = dev->next) {
437 // Is this a blackberry?
438 if( dev->descriptor.idVendor == VENDOR_RIM ) {
439 if( !process(dev, mode) )
440 resume();