bcharge_libusb_1_0.cc

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