00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
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
00092
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
00103 control(handle, 0xc0, 0xa9, 0, 1, buffer, 2, 100);
00104 }
00105 else {
00106
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
00114
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
00131
00132
00133
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
00141
00142
00143
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
00158 bool process(struct usb_device *dev, bool is_pearl)
00159 {
00160 bool apply = false;
00161 printf("Found device #%s...", dev->filename);
00162
00163
00164 usb_dev_handle *handle = usb_open(dev);
00165 if( !handle ) {
00166 printf("unable to open device\n");
00167 return false;
00168 }
00169
00170
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
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
00202 if( apply ) {
00203 if( usb_set_configuration(handle, BLACKBERRY_CONFIGURATION) < 0 )
00204 driver_conflict(handle);
00205
00206
00207
00208 if( is_pearl || force_dual ) {
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
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
00234 usb_close(handle);
00235 return apply;
00236 }
00237
00238 bool power_write(const std::string &file, const std::string &value)
00239 {
00240
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
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320 void resume()
00321 {
00322 if( udev_devpath.size() == 0 )
00323 return;
00324
00325
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
00342
00343
00344
00345
00346
00347
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':
00356 force_dual = true;
00357 old_style_pearl = false;
00358 break;
00359
00360 case 'o':
00361 force_dual = false;
00362 old_style_pearl = true;
00363 break;
00364
00365 case 'p':
00366 udev_devpath = optarg;
00367 break;
00368
00369 case 's':
00370 sysfs_path = optarg;
00371 break;
00372
00373 case 'h':
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
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