# 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
    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-2011, 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 #include <barry/common.h>
    00043 #include "i18n.h"
    00044
    00045 enum ModeType {
    00046         NO_CHANGE,
    00047         PEARL_CLASSIC_MODE_0001,        // force classic mode
    00048         PEARL_DUAL_MODE_0004,           // force dual mode
    00049         CONDITIONAL_DUAL_MODE,          // set dual mode if no class 255
    00050                                         // interface is found (database iface)
    00051 };
    00052
    00053 std::string udev_devpath;
    00054 std::string sysfs_path = "/sys";
    00055
    00056 void Usage()
    00057 {
    00058         printf(
    00059         "bcharge - Adjust Blackberry charging modes\n"
    00060         "          Copyright 2006-2011, Net Direct Inc. (http://www.netdirect.ca/)\n"
    00061         "\n"
    00062         "   -d          Set to dual mode (0004)\n"
    00063         "   -o          Set a Pearl to old Blackberry mode (0001)\n"
    00064         "   -g          Set dual mode only if database interface class 255\n"
    00065         "               is not found\n"
    00066         "\n"
    00067         "   -h          This help message\n"
    00068         "   -p devpath  The devpath argument from udev.  If specified, will attempt\n"
    00069         "               to adjust USB suspend settings to keep the device charging.\n"
    00070         "   -s path     The path where sysfs is mounted.  Defaults to '/sys'\n"
    00071         "\n"
    00072         );
    00073 }
    00074
    00075 void control(usb_dev_handle *dev, int requesttype, int request, int value,
    00076         int index, char *bytes, int size, int timeout)
    00077 {
    00078         int result = usb_control_msg(dev, requesttype, request, value, index,
    00079                 bytes, size, timeout);
    00080         if( result < 0 ) {
    00081                 printf("\nusb_control_msg failed: code: %d, %s\n", result,
    00082                         usb_strerror());
    00083         }
    00084 }
    00085
    00086 void charge(struct usb_dev_handle *handle)
    00087 {
    00088         // the special sauce... these steps seem to do the trick
    00089         // for the 7750 series... needs testing on others
    00090         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(struct usb_dev_handle *handle)
    00096 {
    00097         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(struct usb_dev_handle *handle)
    00103 {
    00104         char buffer[2];
    00105         // Product ID 0004
    00106         control(handle, 0xc0, 0xa9, 1, 1, buffer, 2, 100);
    00107 }
    00108
    00109 int find_interface(struct usb_dev_handle *handle, int iface_class)
    00110 {
    00111         // search the configuration descriptor for the given class ID
    00112         struct usb_device *dev = usb_device(handle);
    00113         struct usb_config_descriptor *cfg = dev ? dev->config : 0;
    00114
    00115         if( cfg ) {
    00116
    00117                 for( unsigned i = 0; cfg->interface && i < cfg->bNumInterfaces; i++ ) {
    00118                         struct usb_interface *iface = &cfg->interface[i];
    00119                         for( int a = 0; iface->altsetting && a < iface->num_altsetting; a++ ) {
    00120                                 struct usb_interface_descriptor *id = &iface->altsetting[a];
    00121                                 if( id->bInterfaceClass == iface_class )
    00122                                         return id->bInterfaceNumber;
    00123                         }
    00124                 }
    00125         }
    00126
    00127         return -1;
    00128 }
    00129
    00130 int find_mass_storage_interface(struct usb_dev_handle *handle)
    00131 {
    00132         int iface = find_interface(handle, USB_CLASS_MASS_STORAGE);
    00133
    00134         if( iface == -1 ) {
    00135                 // if we get here, then we didn't find the Mass Storage
    00136                 // interface ... this should never happen, but if it does,
    00137                 // assume the device is showing product ID 0006, and the
    00138                 // Mass Storage interface is interface #0
    00139                 printf("Can't find Mass Storage interface, assuming 0.\n");
    00140                 return 0;
    00141         }
    00142         else {
    00143                 return iface;
    00144         }
    00145 }
    00146
    00147 void driver_conflict(struct usb_dev_handle *handle)
    00148 {
    00149         // this is called if the first usb_set_configuration()
    00150         // failed... this most probably means that usb_storage
    00151         // has already claimed the Mass Storage interface,
    00152         // in which case we politely tell it to go away.
    00153
    00154 #if LIBUSB_HAS_DETACH_KERNEL_DRIVER_NP
    00155         printf("Detecting possible kernel driver conflict, trying to resolve...\n");
    00156
    00157         int iface = find_mass_storage_interface(handle);
    00158         if( usb_detach_kernel_driver_np(handle, iface) < 0 )
    00159                 printf("usb_detach_kernel_driver_np() failed: %s\n", usb_strerror());
    00160
    00161         if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
    00162                 printf("usb_set_configuration() failed: %s\n", usb_strerror());
    00163 #endif
    00164 }
    00165
    00166 // returns true if device mode was modified, false otherwise
    00167 bool process(struct usb_device *dev, ModeType mode)
    00168 {
    00169         bool apply = false;
    00170         printf("Found device #%s...", dev->filename);
    00171
    00172         // open
    00173         usb_dev_handle *handle = usb_open(dev);
    00174         if( !handle ) {
    00175                 printf("unable to open device\n");
    00176                 return false;
    00177         }
    00178
    00179         // adjust power
    00180         if( dev->config &&
    00181             dev->descriptor.bNumConfigurations >= 1 &&
    00182             dev->config[0].MaxPower < 250 ) {
    00183                 printf("adjusting charge setting");
    00184                 charge(handle);
    00185                 apply = true;
    00186         }
    00187         else {
    00188                 printf("already at 500mA");
    00189         }
    00190         printf("\n");
    00191
    00192         // adjust Pearl mode
    00193         switch( mode )
    00194         {
    00195         case NO_CHANGE:
    00196                 printf("...no Pearl mode adjustment");
    00197                 break;
    00198
    00199         case PEARL_CLASSIC_MODE_0001:
    00200                 if( dev->descriptor.idProduct != PRODUCT_RIM_BLACKBERRY ) {
    00201                         printf("...adjusting Pearl mode to single");
    00202                         pearl_classic_mode(handle);
    00203                         apply = true;
    00204                 }
    00205                 else {
    00206                         printf("...already in classic/single mode");
    00207                 }
    00208                 break;
    00209
    00210         case PEARL_DUAL_MODE_0004:
    00211                 if( dev->descriptor.idProduct != PRODUCT_RIM_PEARL_DUAL ) {
    00212                         printf("...adjusting Pearl mode to dual");
    00213                         pearl_dual_mode(handle);
    00214                         apply = true;
    00215                 }
    00216                 else {
    00217                         printf("...already in dual mode");
    00218                 }
    00219                 break;
    00220
    00221         case CONDITIONAL_DUAL_MODE:
    00222                 if( find_interface(handle, 255) == -1 ) {
    00223                         printf("...no database iface found, setting dual mode");
    00224                         pearl_dual_mode(handle);
    00225                         apply = true;
    00226                 }
    00227                 else {
    00228                         printf("...found database iface, no change");
    00229                 }
    00230                 break;
    00231
    00232         default:
    00233                 printf("Bug: default case");
    00234                 break;
    00235         }
    00236         printf("\n");
    00237
    00238         // apply changes
    00239         if( apply ) {
    00240                 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
    00241                         driver_conflict(handle);
    00242
    00243                 // the Blackberry Pearl doesn't reset itself after the above,
    00244                 // so do it ourselves
    00245                 if( mode == PEARL_DUAL_MODE_0004 ) {
    00246                         //
    00247                         // It has been observed that the 8830 behaves both like
    00248                         // a Pearl device (in that it has mass storage +
    00249                         // database modes) as well as a Classic device in
    00250                         // that it resets itself and doesn't need an explicit
    00251                         // reset call.
    00252                         //
    00253                         // In order to deal with this, we insert a brief sleep.
    00254                         // If it is an 8830, it will reset itself and the
    00255                         // following reset call will fail.  If it is a Pearl,
    00256                         // the reset will work as normal.
    00257                         //
    00258                         sleep(1);
    00259                         if( usb_reset(handle) < 0 ) {
    00260                                 printf("\nusb_reset failed: %s\n", usb_strerror());
    00261                         }
    00262                 }
    00263
    00264                 printf("...done");
    00265         }
    00266         else {
    00267                 printf("...no change");
    00268         }
    00269         printf("\n");
    00270
    00271         // cleanup
    00272         usb_close(handle);
    00273         return apply;
    00274 }
    00275
    00276 bool power_write(const std::string &file, const std::string &value)
    00277 {
    00278         // attempt to open the state file
    00279         int fd = open(file.c_str(), O_RDWR);
    00280         if( fd == -1 ) {
    00281                 printf("autosuspend adjustment failure: (file: %s): %s\n",
    00282                         file.c_str(),
    00283                         strerror(errno));
    00284                 return false;
    00285         }
    00286
    00287         int written = write(fd, value.data(), value.size());
    00288         int error = errno;
    00289         close(fd);
    00290
    00291         if( written < 0 || (size_t)written != value.size() ) {
    00292                 printf("autosuspend adjustment failure (write): (file: %s): %s\n",
    00293                         file.c_str(),
    00294                         strerror(error));
    00295                 return false;
    00296         }
    00297
    00298         printf("autosuspend adjustment: wrote %s to %s\n",
    00299                 value.c_str(), file.c_str());
    00300         return true;
    00301 }
    00302
    00303 //
    00304 // Checks for USB suspend, and enables the device if suspended.
    00305 //
    00306 // Kernel 2.6.21 behaves with autosuspend=0 meaning off, while 2.6.22
    00307 // and higher needs autosuspend=-1 to turn it off.  In 2.6.22, a value
    00308 // of 0 means "immediate" instead of "never".
    00309 //
    00310 // Version 2.6.22 adds variables internal to the system called
    00311 // autosuspend_disabled and autoresume_disabled.  These are controlled by the
    00312 // /sys/class/usb_device/*/device/power/level file.  (See below)
    00313 //
    00314 // Here's a summary of files under device/power.  These may or may not exist
    00315 // depending on your kernel version and configuration.
    00316 //
    00317 //
    00318 //        autosuspend
    00319 //                -1 or 0 means off, depending on kernel,
    00320 //                otherwise it is the number of seconds to
    00321 //                autosuspend
    00322 //
    00323 //        level
    00324 //                with the settings:
    00325 //
    00326 //                on      - suspend is disabled, device is fully powered
    00327 //                auto    - suspend is controlled by the kernel (default)
    00328 //                suspend - suspend is enabled permanently
    00329 //
    00330 //                You can write these strings to the file to control
    00331 //                behaviour on a per-device basis.
    00332 //
    00333 //                echo on > /sys/usb_device/.../device/power/level
    00334 //
    00335 //        state
    00336 //                current state of device
    00337 //                0 - fully powered
    00338 //                2 - suspended
    00339 //
    00340 //                You can write these numbers to control behaviour, but
    00341 //                any change you make here might change automatically
    00342 //                if autosuspend is on.
    00343 //
    00344 //                echo -n 0 > /sys/usb_device/.../device/power/state
    00345 //
    00346 //        wakeup
    00347 //                unknown
    00348 //
    00349 //
    00350 // Given the above facts, use the following heuristics to try to disable
    00351 // autosuspend for the Blackberry:
    00352 //
    00353 //      - if level exists, write "on" to it
    00354 //      - if autosuspend exists, write -1 to it
    00355 //              - if error, write 0 to it
    00356 //      - if neither of the above work, and state exists, write 0 to it
    00357 //
    00358 void resume()
    00359 {
    00360         if( udev_devpath.size() == 0 )
    00361                 return; // nothing to do
    00362
    00363         // let sysfs settle a bit
    00364         sleep(5);
    00365
    00366         std::string power_path = sysfs_path + "/" + udev_devpath + "/device/power/";
    00367
    00368         if( !power_write(power_path + "level", "on\n") )
    00369                 if( !power_write(power_path + "autosuspend", "-1\n") )
    00370                         if( !power_write(power_path + "autosuspend", "0\n") )
    00371                                 power_write(power_path + "state", "0");
    00372 }
    00373
    00374 int main(int argc, char *argv[])
    00375 {
    00376         struct usb_bus *busses;
    00377         ModeType mode = NO_CHANGE;
    00378
    00379         INIT_I18N(PACKAGE);
    00380
    00381         //
    00382         // allow -o command line switch to choose which mode to use for
    00383         // Blackberry Pearls:
    00384         //      Dual:           0004    -d
    00385         //      With switch:    0001    -o
    00386         //
    00387
    00388         // process command line options
    00389         for(;;) {
    00390                 int cmd = getopt(argc, argv, "dogp:s:h");
    00391                 if( cmd == -1 )
    00392                         break;
    00393
    00394                 switch( cmd )
    00395                 {
    00396                 case 'd':       // Dual
    00397                         mode = PEARL_DUAL_MODE_0004;
    00398                         break;
    00399
    00400                 case 'o':       // Classic style pearl
    00401                         mode = PEARL_CLASSIC_MODE_0001;
    00402                         break;
    00403
    00404                 case 'g':       // Guess whether dual is needed
    00405                         mode = CONDITIONAL_DUAL_MODE;
    00406                         break;
    00407
    00408                 case 'p':       // udev devpath
    00409                         udev_devpath = optarg;
    00410                         break;
    00411
    00412                 case 's':       // where sysfs is mounted
    00413                         sysfs_path = optarg;
    00414                         break;
    00415
    00416                 case 'h':       // help!
    00417                 default:
    00418                         Usage();
    00419                         return 0;
    00420                 }
    00421         }
    00422
    00423         usb_init();
    00424         if( usb_find_busses() < 0 || usb_find_devices() < 0 ) {
    00425                 printf("\nUnable to scan devices: %s\n", usb_strerror());
    00426                 return 1;
    00427         }
    00428         busses = usb_get_busses();
    00429
    00430         printf("Scanning for Blackberry devices...\n");
    00431
    00432         struct usb_bus *bus;
    00433         for( bus = busses; bus; bus = bus->next ) {
    00434                 struct usb_device *dev;
    00435
    00436                 for (dev = bus->devices; dev; dev = dev->next) {
    00437                         // Is this a blackberry?
    00438                         if( dev->descriptor.idVendor == VENDOR_RIM ) {
    00439                                 if( !process(dev, mode) )
    00440                                         resume();
    00441                         }
    00442                 }
    00443         }
    00444 }
    00445