Added ppp and test directories to Makefile.am EXTRA_DIST
[barry/pauldeden.git] / tools / bcharge.cc
blob0138a9a352326dbe1b92e77c8db47ae66b5a09c2
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/index.php
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-2008, 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>
43 #define VENDOR_RIM 0x0fca
44 #define PRODUCT_RIM_BLACKBERRY 0x0001
45 #define PRODUCT_RIM_PEARL_DUAL 0x0004
46 #define PRODUCT_RIM_PEARL_8120 0x8004
47 #define PRODUCT_RIM_PEARL 0x0006
49 #define IPRODUCT_RIM_HANDHELD 2
50 #define IPRODUCT_RIM_MASS_STORAGE 4
51 #define IPRODUCT_RIM_COMPOSITE 5
53 #define BLACKBERRY_INTERFACE 0
54 #define BLACKBERRY_CONFIGURATION 1
56 bool old_style_pearl = false;
57 bool force_dual = false;
58 std::string udev_devpath;
59 std::string sysfs_path = "/sys";
61 void Usage()
63 printf(
64 "bcharge - Adjust Blackberry charging modes\n"
65 " Copyright 2006-2008, Net Direct Inc. (http://www.netdirect.ca/)\n"
66 "\n"
67 " -d Dual mode (mode 0004) (default)\n"
68 " -o Set a Pearl to old Blackberry mode (0001)\n"
69 "\n"
70 " -h This help message\n"
71 " -p devpath The devpath argument from udev. If specified, will attempt\n"
72 " to adjust USB suspend settings to keep the device charging.\n"
73 " -s path The path where sysfs is mounted. Defaults to '/sys'\n"
74 "\n"
78 void control(usb_dev_handle *dev, int requesttype, int request, int value,
79 int index, char *bytes, int size, int timeout)
81 int result = usb_control_msg(dev, requesttype, request, value, index,
82 bytes, size, timeout);
83 if( result < 0 ) {
84 printf("\nusb_control_msg failed: code: %d, %s\n", result,
85 usb_strerror());
89 void charge(struct usb_dev_handle *handle)
91 // the special sauce... these steps seem to do the trick
92 // for the 7750 series... needs testing on others
93 char buffer[2];
94 control(handle, 0xc0, 0xa5, 0, 1, buffer, 2, 100);
95 control(handle, 0x40, 0xa2, 0, 1, buffer, 0, 100);
98 void pearl_mode(struct usb_dev_handle *handle)
100 char buffer[2];
101 if( old_style_pearl ) {
102 // use this for "old style" interface: product ID 0001
103 control(handle, 0xc0, 0xa9, 0, 1, buffer, 2, 100);
105 else {
106 // Product ID 0004
107 control(handle, 0xc0, 0xa9, 1, 1, buffer, 2, 100);
111 int find_mass_storage_interface(struct usb_dev_handle *handle)
113 // search the configuration descriptor for a Mass Storage
114 // interface (class 8)
115 struct usb_device *dev = usb_device(handle);
116 struct usb_config_descriptor *cfg = dev ? dev->config : 0;
118 if( cfg ) {
120 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
121 struct usb_interface *iface = &cfg->interface[i];
122 for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
123 struct usb_interface_descriptor *id = &iface->altsetting[a];
124 if( id->bInterfaceClass == USB_CLASS_MASS_STORAGE )
125 return id->bInterfaceNumber;
130 // if we get here, then we didn't find the Mass Storage interface
131 // ... this should never happen, but if it does, assume
132 // the device is s showing product ID 0006, and the Mass Storage
133 // interface is interface #0
134 printf("Can't find Mass Storage interface, assuming 0.\n");
135 return 0;
138 void driver_conflict(struct usb_dev_handle *handle)
140 // this is called if the first usb_set_configuration()
141 // failed... this most probably means that usb_storage
142 // has already claimed the Mass Storage interface,
143 // in which case we politely tell it to away.
145 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
146 printf("Detecting possible kernel driver conflict, trying to resolve...\n");
148 int iface = find_mass_storage_interface(handle);
149 if( usb_detach_kernel_driver_np(handle, iface) < 0 )
150 printf("usb_detach_kernel_driver_np() failed: %s\n", usb_strerror());
152 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
153 printf("usb_set_configuration() failed: %s\n", usb_strerror());
154 #endif
157 // returns true if device mode was modified, false otherwise
158 bool process(struct usb_device *dev, bool is_pearl)
160 bool apply = false;
161 printf("Found device #%s...", dev->filename);
163 // open
164 usb_dev_handle *handle = usb_open(dev);
165 if( !handle ) {
166 printf("unable to open device\n");
167 return false;
170 // adjust power
171 if( dev->config &&
172 dev->descriptor.bNumConfigurations >= 1 &&
173 dev->config[0].MaxPower < 250 ) {
174 printf("adjusting charge setting");
175 charge(handle);
176 apply = true;
178 else {
179 printf("already at 500mA");
182 // adjust Pearl mode
183 if( is_pearl || force_dual ) {
184 int desired_mode = old_style_pearl
185 ? PRODUCT_RIM_BLACKBERRY : PRODUCT_RIM_PEARL_DUAL;
187 if( desired_mode != dev->descriptor.idProduct ) {
188 printf("...adjusting Pearl mode to %s",
189 old_style_pearl ? "single" : "dual");
190 pearl_mode(handle);
191 apply = true;
193 else {
194 printf("...already in desired Pearl mode");
197 else {
198 printf("...no Pearl adjustment");
201 // apply changes
202 if( apply ) {
203 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
204 driver_conflict(handle);
206 // the Blackberry Pearl doesn't reset itself after the above,
207 // so do it ourselves
208 if( is_pearl || force_dual ) {
210 // It has been observed that the 8830 behaves both like
211 // a Pearl device (in that it has mass storage +
212 // database modes) as well as a Classic device in
213 // that it resets itself and doesn't need an explicit
214 // reset call.
216 // In order to deal with this, we insert a brief sleep.
217 // If it is an 8830, it will reset itself and the
218 // following reset call will fail. If it is a Pearl,
219 // the reset will work as normal.
221 sleep(1);
222 if( usb_reset(handle) < 0 ) {
223 printf("\nusb_reset failed: %s\n", usb_strerror());
227 printf("...done\n");
229 else {
230 printf("...no change\n");
233 // cleanup
234 usb_close(handle);
235 return apply;
238 bool power_write(const std::string &file, const std::string &value)
240 // attempt to open the state file
241 int fd = open(file.c_str(), O_RDWR);
242 if( fd == -1 ) {
243 printf("autosuspend adjustment failure: (file: %s): %s\n",
244 file.c_str(),
245 strerror(errno));
246 return false;
249 int written = write(fd, value.data(), value.size());
250 int error = errno;
251 close(fd);
253 if( written < 0 || (size_t)written != value.size() ) {
254 printf("autosuspend adjustment failure (write): (file: %s): %s\n",
255 file.c_str(),
256 strerror(error));
257 return false;
260 printf("autosuspend adjustment: wrote %s to %s\n",
261 value.c_str(), file.c_str());
262 return true;
266 // Checks for USB suspend, and enables the device if suspended.
268 // Kernel 2.6.21 behaves with autosuspend=0 meaning off, while 2.6.22
269 // and higher needs autosuspend=-1 to turn it off. In 2.6.22, a value
270 // of 0 means "immediate" instead of "never".
272 // Version 2.6.22 adds variables internal to the system called
273 // autosuspend_disabled and autoresume_disabled. These are controlled by the
274 // /sys/class/usb_device/*/device/power/level file. (See below)
276 // Here's a summary of files under device/power. These may or may not exist
277 // depending on your kernel version and configuration.
280 // autosuspend
281 // -1 or 0 means off, depending on kernel,
282 // otherwise it is the number of seconds to
283 // autosuspend
285 // level
286 // with the settings:
288 // on - suspend is disabled, device is fully powered
289 // auto - suspend is controlled by the kernel (default)
290 // suspend - suspend is enabled permanently
292 // You can write these strings to the file to control
293 // behaviour on a per-device basis.
295 // echo on > /sys/usb_device/.../device/power/level
297 // state
298 // current state of device
299 // 0 - fully powered
300 // 2 - suspended
302 // You can write these numbers to control behaviour, but
303 // any change you make here might change automatically
304 // if autosuspend is on.
306 // echo -n 0 > /sys/usb_device/.../device/power/state
308 // wakeup
309 // unknown
312 // Given the above facts, use the following heuristics to try to disable
313 // autosuspend for the Blackberry:
315 // - if level exists, write "on" to it
316 // - if autosuspend exists, write -1 to it
317 // - if error, write 0 to it
318 // - if neither of the above work, and state exists, write 0 to it
320 void resume()
322 if( udev_devpath.size() == 0 )
323 return; // nothing to do
325 // let sysfs settle a bit
326 sleep(5);
328 std::string power_path = sysfs_path + "/" + udev_devpath + "/device/power/";
330 if( !power_write(power_path + "level", "on\n") )
331 if( !power_write(power_path + "autosuspend", "-1\n") )
332 if( !power_write(power_path + "autosuspend", "0\n") )
333 power_write(power_path + "state", "0");
336 int main(int argc, char *argv[])
338 struct usb_bus *busses;
341 // allow -o command line switch to choose which mode to use for
342 // Blackberry Pearls:
343 // Dual(default): 0004 -d
344 // With switch: 0001 -o
347 // process command line options
348 for(;;) {
349 int cmd = getopt(argc, argv, "dop:s:h");
350 if( cmd == -1 )
351 break;
353 switch( cmd )
355 case 'd': // Dual (default)
356 force_dual = true;
357 old_style_pearl = false;
358 break;
360 case 'o': // Old style pearl
361 force_dual = false;
362 old_style_pearl = true;
363 break;
365 case 'p': // udev devpath
366 udev_devpath = optarg;
367 break;
369 case 's': // where sysfs is mounted
370 sysfs_path = optarg;
371 break;
373 case 'h': // help!
374 default:
375 Usage();
376 return 0;
380 usb_init();
381 if( usb_find_busses() < 0 || usb_find_devices() < 0 ) {
382 printf("\nUnable to scan devices: %s\n", usb_strerror());
383 return 1;
385 busses = usb_get_busses();
387 printf("Scanning for Blackberry devices...\n");
389 struct usb_bus *bus;
390 for( bus = busses; bus; bus = bus->next ) {
391 struct usb_device *dev;
393 for (dev = bus->devices; dev; dev = dev->next) {
394 // Is this a blackberry?
395 if( dev->descriptor.idVendor == VENDOR_RIM ) {
396 switch(dev->descriptor.idProduct)
398 case PRODUCT_RIM_BLACKBERRY:
399 if( !process(dev, false) )
400 resume();
401 break;
403 case PRODUCT_RIM_PEARL_DUAL:
404 case PRODUCT_RIM_PEARL:
405 if( !process(dev, true) )
406 resume();
407 break;