bcharge.cc

Go to the documentation of this file.
00001 ///
00002 /// \file       bcharge.cc
00003 ///             Talk to the Blackberry just enough to change the Max Power
00004 ///             to 500mA.  Cycles through all devices attached to USB,
00005 ///             attempting to set all matching Blackberry devices to charge.
00006 ///
00007 ///             This file is part of the Barry project:
00008 ///
00009 ///             http://www.netdirect.ca/software/packages/barry/index.php
00010 ///             http://sourceforge.net/projects/barry
00011 ///
00012 ///             Compile with the following command (needs libusb):
00013 ///
00014 ///             g++ -o bcharge bcharge.cc -lusb
00015 ///
00016 
00017 /*
00018     Copyright (C) 2006-2008, Net Direct Inc. (http://www.netdirect.ca/)
00019 
00020     This program is free software; you can redistribute it and/or modify
00021     it under the terms of the GNU General Public License as published by
00022     the Free Software Foundation; either version 2 of the License, or
00023     (at your option) any later version.
00024 
00025     This program is distributed in the hope that it will be useful,
00026     but WITHOUT ANY WARRANTY; without even the implied warranty of
00027     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00028 
00029     See the GNU General Public License in the COPYING file at the
00030     root directory of this project for more details.
00031 */
00032 
00033 #include <usb.h>
00034 #include <stdio.h>
00035 #include <string.h>
00036 #include <unistd.h>
00037 #include <errno.h>
00038 #include <sys/types.h>
00039 #include <sys/stat.h>
00040 #include <fcntl.h>
00041 #include <string>
00042 
00043 #define VENDOR_RIM              0x0fca
00044 #define PRODUCT_RIM_BLACKBERRY  0x0001
00045 #define PRODUCT_RIM_PEARL_DUAL  0x0004
00046 #define PRODUCT_RIM_PEARL_8120  0x8004
00047 #define PRODUCT_RIM_PEARL       0x0006
00048 
00049 #define IPRODUCT_RIM_HANDHELD           2
00050 #define IPRODUCT_RIM_MASS_STORAGE       4
00051 #define IPRODUCT_RIM_COMPOSITE          5
00052 
00053 #define BLACKBERRY_INTERFACE            0
00054 #define BLACKBERRY_CONFIGURATION        1
00055 
00056 bool old_style_pearl = false;
00057 bool force_dual = false;
00058 std::string udev_devpath;
00059 std::string sysfs_path = "/sys";
00060 
00061 void Usage()
00062 {
00063         printf(
00064         "bcharge - Adjust Blackberry charging modes\n"
00065         "          Copyright 2006-2008, Net Direct Inc. (http://www.netdirect.ca/)\n"
00066         "\n"
00067         "   -d          Dual mode (mode 0004) (default)\n"
00068         "   -o          Set a Pearl to old Blackberry mode (0001)\n"
00069         "\n"
00070         "   -h          This help message\n"
00071         "   -p devpath  The devpath argument from udev.  If specified, will attempt\n"
00072         "               to adjust USB suspend settings to keep the device charging.\n"
00073         "   -s path     The path where sysfs is mounted.  Defaults to '/sys'\n"
00074         "\n"
00075         );
00076 }
00077 
00078 void control(usb_dev_handle *dev, int requesttype, int request, int value,
00079         int index, char *bytes, int size, int timeout)
00080 {
00081         int result = usb_control_msg(dev, requesttype, request, value, index,
00082                 bytes, size, timeout);
00083         if( result < 0 ) {
00084                 printf("\nusb_control_msg failed: code: %d, %s\n", result,
00085                         usb_strerror());
00086         }
00087 }
00088 
00089 void charge(struct usb_dev_handle *handle)
00090 {
00091         // the special sauce... these steps seem to do the trick
00092         // for the 7750 series... needs testing on others
00093         char buffer[2];
00094         control(handle, 0xc0, 0xa5, 0, 1, buffer, 2, 100);
00095         control(handle, 0x40, 0xa2, 0, 1, buffer, 0, 100);
00096 }
00097 
00098 void pearl_mode(struct usb_dev_handle *handle)
00099 {
00100         char buffer[2];
00101         if( old_style_pearl ) {
00102                 // use this for "old style" interface: product ID 0001
00103                 control(handle, 0xc0, 0xa9, 0, 1, buffer, 2, 100);
00104         }
00105         else {
00106                 // Product ID 0004
00107                 control(handle, 0xc0, 0xa9, 1, 1, buffer, 2, 100);
00108         }
00109 }
00110 
00111 int find_mass_storage_interface(struct usb_dev_handle *handle)
00112 {
00113         // search the configuration descriptor for a Mass Storage
00114         // interface (class 8)
00115         struct usb_device *dev = usb_device(handle);
00116         struct usb_config_descriptor *cfg = dev ? dev->config : 0;
00117 
00118         if( cfg ) {
00119 
00120                 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
00121                         struct usb_interface *iface = &cfg->interface[i];
00122                         for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
00123                                 struct usb_interface_descriptor *id = &iface->altsetting[a];
00124                                 if( id->bInterfaceClass == USB_CLASS_MASS_STORAGE )
00125                                         return id->bInterfaceNumber;
00126                         }
00127                 }
00128         }
00129 
00130         // if we get here, then we didn't find the Mass Storage interface
00131         // ... this should never happen, but if it does, assume
00132         // the device is s showing product ID 0006, and the Mass Storage
00133         // interface is interface #0
00134         printf("Can't find Mass Storage interface, assuming 0.\n");
00135         return 0;
00136 }
00137 
00138 void driver_conflict(struct usb_dev_handle *handle)
00139 {
00140         // this is called if the first usb_set_configuration()
00141         // failed... this most probably means that usb_storage
00142         // has already claimed the Mass Storage interface,
00143         // in which case we politely tell it to away.
00144 
00145 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
00146         printf("Detecting possible kernel driver conflict, trying to resolve...\n");
00147 
00148         int iface = find_mass_storage_interface(handle);
00149         if( usb_detach_kernel_driver_np(handle, iface) < 0 )
00150                 printf("usb_detach_kernel_driver_np() failed: %s\n", usb_strerror());
00151 
00152         if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
00153                 printf("usb_set_configuration() failed: %s\n", usb_strerror());
00154 #endif
00155 }
00156 
00157 // returns true if device mode was modified, false otherwise
00158 bool process(struct usb_device *dev, bool is_pearl)
00159 {
00160         bool apply = false;
00161         printf("Found device #%s...", dev->filename);
00162 
00163         // open
00164         usb_dev_handle *handle = usb_open(dev);
00165         if( !handle ) {
00166                 printf("unable to open device\n");
00167                 return false;
00168         }
00169 
00170         // adjust power
00171         if( dev->config &&
00172             dev->descriptor.bNumConfigurations >= 1 &&
00173             dev->config[0].MaxPower < 250 ) {
00174                 printf("adjusting charge setting");
00175                 charge(handle);
00176                 apply = true;
00177         }
00178         else {
00179                 printf("already at 500mA");
00180         }
00181 
00182         // adjust Pearl mode
00183         if( is_pearl || force_dual ) {
00184                 int desired_mode = old_style_pearl
00185                         ? PRODUCT_RIM_BLACKBERRY : PRODUCT_RIM_PEARL_DUAL;
00186 
00187                 if( desired_mode != dev->descriptor.idProduct ) {
00188                         printf("...adjusting Pearl mode to %s",
00189                                 old_style_pearl ? "single" : "dual");
00190                         pearl_mode(handle);
00191                         apply = true;
00192                 }
00193                 else {
00194                         printf("...already in desired Pearl mode");
00195                 }
00196         }
00197         else {
00198                 printf("...no Pearl adjustment");
00199         }
00200 
00201         // apply changes
00202         if( apply ) {
00203                 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
00204                         driver_conflict(handle);
00205 
00206                 // the Blackberry Pearl doesn't reset itself after the above,
00207                 // so do it ourselves
00208                 if( is_pearl || force_dual ) {
00209                         //
00210                         // It has been observed that the 8830 behaves both like
00211                         // a Pearl device (in that it has mass storage +
00212                         // database modes) as well as a Classic device in
00213                         // that it resets itself and doesn't need an explicit
00214                         // reset call.
00215                         //
00216                         // In order to deal with this, we insert a brief sleep.
00217                         // If it is an 8830, it will reset itself and the
00218                         // following reset call will fail.  If it is a Pearl,
00219                         // the reset will work as normal.
00220                         //
00221                         sleep(1);
00222                         if( usb_reset(handle) < 0 ) {
00223                                 printf("\nusb_reset failed: %s\n", usb_strerror());
00224                         }
00225                 }
00226 
00227                 printf("...done\n");
00228         }
00229         else {
00230                 printf("...no change\n");
00231         }
00232 
00233         // cleanup
00234         usb_close(handle);
00235         return apply;
00236 }
00237 
00238 bool power_write(const std::string &file, const std::string &value)
00239 {
00240         // attempt to open the state file
00241         int fd = open(file.c_str(), O_RDWR);
00242         if( fd == -1 ) {
00243                 printf("autosuspend adjustment failure: (file: %s): %s\n",
00244                         file.c_str(),
00245                         strerror(errno));
00246                 return false;
00247         }
00248 
00249         int written = write(fd, value.data(), value.size());
00250         int error = errno;
00251         close(fd);
00252 
00253         if( written < 0 || (size_t)written != value.size() ) {
00254                 printf("autosuspend adjustment failure (write): (file: %s): %s\n",
00255                         file.c_str(),
00256                         strerror(error));
00257                 return false;
00258         }
00259 
00260         printf("autosuspend adjustment: wrote %s to %s\n",
00261                 value.c_str(), file.c_str());
00262         return true;
00263 }
00264 
00265 //
00266 // Checks for USB suspend, and enables the device if suspended.
00267 //
00268 // Kernel 2.6.21 behaves with autosuspend=0 meaning off, while 2.6.22
00269 // and higher needs autosuspend=-1 to turn it off.  In 2.6.22, a value
00270 // of 0 means "immediate" instead of "never".
00271 //
00272 // Version 2.6.22 adds variables internal to the system called
00273 // autosuspend_disabled and autoresume_disabled.  These are controlled by the
00274 // /sys/class/usb_device/*/device/power/level file.  (See below)
00275 // 
00276 // Here's a summary of files under device/power.  These may or may not exist
00277 // depending on your kernel version and configuration.
00278 // 
00279 // 
00280 //        autosuspend
00281 //                -1 or 0 means off, depending on kernel,
00282 //                otherwise it is the number of seconds to
00283 //                autosuspend
00284 //
00285 //        level
00286 //                with the settings:
00287 //
00288 //                on      - suspend is disabled, device is fully powered
00289 //                auto    - suspend is controlled by the kernel (default)
00290 //                suspend - suspend is enabled permanently
00291 //
00292 //                You can write these strings to the file to control
00293 //                behaviour on a per-device basis.
00294 //
00295 //                echo on > /sys/usb_device/.../device/power/level
00296 //
00297 //        state
00298 //                current state of device
00299 //                0 - fully powered
00300 //                2 - suspended
00301 //
00302 //                You can write these numbers to control behaviour, but
00303 //                any change you make here might change automatically
00304 //                if autosuspend is on.
00305 //
00306 //                echo -n 0 > /sys/usb_device/.../device/power/state
00307 //
00308 //        wakeup
00309 //                unknown
00310 //
00311 //
00312 // Given the above facts, use the following heuristics to try to disable
00313 // autosuspend for the Blackberry:
00314 //
00315 //      - if level exists, write "on" to it
00316 //      - if autosuspend exists, write -1 to it
00317 //              - if error, write 0 to it
00318 //      - if neither of the above work, and state exists, write 0 to it
00319 //
00320 void resume()
00321 {
00322         if( udev_devpath.size() == 0 )
00323                 return; // nothing to do
00324 
00325         // let sysfs settle a bit
00326         sleep(5);
00327 
00328         std::string power_path = sysfs_path + "/" + udev_devpath + "/device/power/";
00329 
00330         if( !power_write(power_path + "level", "on\n") )
00331                 if( !power_write(power_path + "autosuspend", "-1\n") )
00332                         if( !power_write(power_path + "autosuspend", "0\n") )
00333                                 power_write(power_path + "state", "0");
00334 }
00335 
00336 int main(int argc, char *argv[])
00337 {
00338         struct usb_bus *busses;
00339 
00340         //
00341         // allow -o command line switch to choose which mode to use for
00342         // Blackberry Pearls:
00343         //      Dual(default):  0004    -d
00344         //      With switch:    0001    -o
00345         //
00346 
00347         // process command line options
00348         for(;;) {
00349                 int cmd = getopt(argc, argv, "dop:s:h");
00350                 if( cmd == -1 )
00351                         break;
00352 
00353                 switch( cmd )
00354                 {
00355                 case 'd':       // Dual (default)
00356                         force_dual = true;
00357                         old_style_pearl = false;
00358                         break;
00359 
00360                 case 'o':       // Old style pearl
00361                         force_dual = false;
00362                         old_style_pearl = true;
00363                         break;
00364 
00365                 case 'p':       // udev devpath
00366                         udev_devpath = optarg;
00367                         break;
00368 
00369                 case 's':       // where sysfs is mounted
00370                         sysfs_path = optarg;
00371                         break;
00372 
00373                 case 'h':       // help!
00374                 default:
00375                         Usage();
00376                         return 0;
00377                 }
00378         }
00379 
00380         usb_init();
00381         if( usb_find_busses() < 0 || usb_find_devices() < 0 ) {
00382                 printf("\nUnable to scan devices: %s\n", usb_strerror());
00383                 return 1;
00384         }
00385         busses = usb_get_busses();
00386 
00387         printf("Scanning for Blackberry devices...\n");
00388 
00389         struct usb_bus *bus;
00390         for( bus = busses; bus; bus = bus->next ) {
00391                 struct usb_device *dev;
00392 
00393                 for (dev = bus->devices; dev; dev = dev->next) {
00394                         // Is this a blackberry?
00395                         if( dev->descriptor.idVendor == VENDOR_RIM ) {
00396                                 switch(dev->descriptor.idProduct)
00397                                 {
00398                                 case PRODUCT_RIM_BLACKBERRY:
00399                                         if( !process(dev, false) )
00400                                                 resume();
00401                                         break;
00402 
00403                                 case PRODUCT_RIM_PEARL_DUAL:
00404                                 case PRODUCT_RIM_PEARL:
00405                                         if( !process(dev, true) )
00406                                                 resume();
00407                                         break;
00408                                 }
00409                         }
00410                 }
00411         }
00412 }
00413 

Generated on Wed Sep 24 21:27:31 2008 for Barry by  doxygen 1.5.1