diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0e6e6d3 --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +# Makefile for the Smart Battery refurbishment utils +# by Frank Rysanek + +CC=gcc +# the -O2 optimization is necessary - the programs won't compile without it +CFLAGS=-O2 + +PROGS=read_batt reset-bq2092 reset-bq2040 bq2040_capacity eeprom +all: $(PROGS) + +clean: + rm -f $(PROGS) + +tgz: + rm -f ./i2c-progs.tgz + tar cvzf i2c-progs.tgz *.c Makefile README.eeprom battery.sh help.txt schematic.txt diff --git a/README.eeprom b/README.eeprom new file mode 100644 index 0000000..481d60b --- /dev/null +++ b/README.eeprom @@ -0,0 +1,85 @@ +You can use this program to read/write to i2c-eeproms +like the popular 24C16, 24C08, 24C04,.. In contrast to lm_sensor's eeprommer +which supports 24C256-type eeproms 24C16ss use 1-byte addresses! + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +!!! ! +!!! This program should only be used on external busses such as i2c-pport. ! +!!! ! +!!! Your computer may contain i2c-eeproms for saving data vital to its ! +!!! operation. If you are not careful you might overwrite this data with ! +!!! this program and your computer may no longer boot! ! +!!! ! +!!! An example are the EEPROMS on your SDRAM DIMMs, your computer may no ! +!!! longer detect the RAM module rendering it essentially USELESS! ! +!!! ! +!!! IBM Thinkpads are said to store their configuration data in a eeprom, ! +!!! if you manage to overwrite this eeprom you will have to send your ! +!!! computer to the manufacturer for a costly repair! ! +!!! ! +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Warning !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +It has several options: + + -d devicenode + + set this to the device-node of the i2c-bus + you want to use like /dev/i2c-0. + Use /dev/i2c-1 for the second bus, i2c-2 for the third... + + The default /dev/i2c-0 should work most of the time. + + -a address + + set this to the device-address of your + eeprom. For a 24C16 the address is hardcoded to + 0x50, which is -you guessed it- the default. + + For a 24C08 and smaller types you can choose which + addresses they occupy by forcing the address-pins + of the chip to High or Low so here the address may differ. + + -p number_of_pages + + set this to the number of pages you want to read + from or write to the eeprom. The 24C16 maps it's + pages to consecutive addresses on the i2c-bus so + we will try to read 256 bytes from every i2c + address between 'address' (inclusive) and + 'address + number_of_pages' (exclusive)... + + A 24C16 has 8 pages so that's the default for this + parameter. + + -f filename + + read data from this file (when writing to eeprom) or + write data to this file (when reading from eeprom). + + When reading a file that's smaller than the + eeprom's storage size we will pad the eeprom + with zeroes. + + If no file is given we will just read the + eeprom (while in read-mode) and test it's presence + this way. In write-mode we will just write zeroes + to the eeprom. + + -w When '-w' is present we will *write* to the eeprom. + If you do not specify '-w' we will read the contents + of the eeprom. + + -y This flag will suppress the warning when you write to the + eeprom. You will not be required to enter 'yes' so be careful + when using this switch! + + +I wrote that program to clear a 24C16 eeprom that sit's in my crappy +satellite receiver because sometimes its Z80 processor likes to +write garbage to it and then crash.... + +No further testing besides writing a long series of "The quick brown +fox jumps over the lazy dog!" and reading it back has been done so +of course this comes without any warranty. + + Chris diff --git a/battery.sh b/battery.sh new file mode 100644 index 0000000..caf5de1 --- /dev/null +++ b/battery.sh @@ -0,0 +1,311 @@ +#!/bin/bash + +### Functions + +bq2092_stuff() { + clear + echo "Connect your SMBC, SMBD and GND to the battery's external port." + echo "Press 'y' and [ENTER] when ready." + echo + echo -en " > " + read LINE + echo + if [ "$LINE" = "y" ]; then + echo "Okay, trying to reset the battery." + sleep 2 + ./reset-bq2092 + echo + echo "Reset attempt over." + echo "I cannot really detect whether or not the reset was successful." + echo "The 'reset' is an i2c write transaction that always fails." + echo "If the indication LED's on your battery went cockoo, that's" + echo "a good sign. To finish the reset, disconnect the controller PCB" + echo "from the battery cells and reconnect, to power-cycle the chip." + echo "Check out the SBS registers dump - the 'actual capacity' should" + echo "already be all the way up or at least should not resist going up" + echo "during the first charge cycle." + echo "You still need to run the pack through a few charge cycles" + echo "IN THE NOTEBOOK to see if the battery returns to full capacity." + echo + echo "Press ENTER to continue." + read + cat < discharge_warning.txt + echo + echo "Press ENTER to return to the menu." + read + else + echo "Okay, giving up." + sleep 2 + fi +} + + + +bq2040_stuff() { + clear + echo " Please select one of the following: (and ENTER)" + echo + echo " 1 attempt to reset the BQ2040 using the 'normal' procedure (defunct?)" + echo " 2 set a new 'actual capacity' value into the on-board Flash memory " + echo + echo " q cancel - return to the main menu " + echo + echo -n " > " + + read LINE + case $LINE in + "1") + echo + echo "Please note that this soft reset doesn't seem to work with the " + echo "BQ2040, despite being described in its data sheet. " + echo "Consider using the more complicated workaround via the onboad " + echo "CMOS Flash chip. " + echo + echo "Connect your SMBC, SMBD and GND to the battery's external port." + echo "Press 'y' and [ENTER] when ready." + echo + echo -en " > " + read LINE + echo + if [ "$LINE" = "y" ]; then + echo "Okay, trying to reset the battery." + sleep 2 + ./reset-bq2040 + echo + echo "Reset attempt over." + echo "I cannot really detect whether or not the reset was successful." + echo "The 'reset' is an i2c write transaction that always fails." + echo "If the indication LED's on your battery went cockoo, that's" + echo "a good sign. To finish the reset, disconnect the controller PCB" + echo "from the battery cells and reconnect, to power-cycle the chip." + echo "Check out the SBS registers dump - the 'actual capacity' should" + echo "already be all the way up or at least should not resist going up" + echo "during the first charge cycle." + echo "You still need to run the pack through a few charge cycles" + echo "IN THE NOTEBOOK to see if the battery returns to full capacity." + echo + echo "Press ENTER to continue." + read + cat < discharge_warning.txt + echo + echo "Press ENTER to return to the menu." + read + else + echo "Okay, giving up." + sleep 2 + fi + ;; + "2") + clear + echo + echo "Most smart battery controller IC's, including the BQ2040, use" + echo "a stand-alone EEPROM memory chip (AKA CMOS Flash) to store their" + echo "initial data and a few runtime values - one of them is the learned" + echo "battery capacity. This allows us to use a simple workaround, if" + echo "the regular reset doesn't work. We can reprogram the learned capacity" + echo "to a higher value." + echo + echo "This operation WILL NOT WORK using the SMBUS that's available on the" + echo "external connector of the battery pack. You have to wiretap the CMOS" + echo "Flash chip - the smaller (eight-legged) chip that's on the tiny PCB" + echo "inside the battery pack, next to the battery controller IC." + echo "While talking straight to the CMOS Flash, power down the battery" + echo "controller chip, to prevent it from intervening - disconnect the" + echo "controller PCB from the cells. The power to the CMOS Flash will be" + echo "provided externally, e.g. from the +5V line of the keyboard port." + echo + echo "Press ENTER for more detailed instructions..." + read + echo "To sum up :" + echo "Disconnect the cells from the gas-gauge PCB." + echo "Tap your SMBC (SCL), SMBD (SDA), +5V (from keyboard plug) and GND" + echo "to the respective pins of the onboard CMOS Flash chip (24C01, 24C02)." + cat < schematic.txt + echo "Press 'y' and [ENTER] when ready." + echo + echo -en " > " + read LINE + echo + if [ "$LINE" = "y" ]; then + echo "Please enter the new capacity to be programmed, in units of mAh." + echo "Add perhaps 10% or more above the nominal capacity of the new cells," + echo "so that the controller has some workspace for the initial learning:" + echo + echo -n " New capacity: " + read NEW_CAPACITY + echo "New capacity requested to be programmed: $NEW_CAPACITY mAh" + sleep 2 + if ./bq2040_capacity -u $NEW_CAPACITY; then + echo + echo "The return code from bq2040_capacity signals success." + sleep 3 + clear + cat < discharge_warning.txt + else + echo + echo "The return code from bq2040_capacity signals failure." + echo "Please note the error messages for debugging purposes." + fi + echo + echo "Press ENTER to return to the menu." + read + else + echo "Okay, giving up." + sleep 2 + fi + ;; + "q") + ;; + esac + +} + + + +eeprom_stuff() { + # download contents of a 24C02 into a file + clear + echo "This doesn't work using the external port of the battery - you" + echo "must wiretap the 24Cxx chip directly!!!" + echo + echo "Disconnect the cells from the gas-gauge PCB." + echo "Tap your SMBC (SCL), SMBD (SDA), +5V (from keyboard plug) and GND" + echo "to the respective pins of the onboard CMOS Flash chip (perhaps a 24C02)." + cat < schematic.txt + echo "Press ENTER when ready." + read + + echo + echo -n "Enter the EEPROM chip type (e.g. 24C02) : " + read EEPROM_TYPE + echo + if [ "$EEPROM_TYPE" = "" ]; then + echo "You have not entered a chip type. Assuming 24C02." + EEPROM_TYPE="24C02" + echo + fi + + if ./eeprom -t $EEPROM_TYPE -f /var/battery/CMOS-dump.bin; then + echo + echo "The binary dump will be saved in a file at" + echo " /var/battery/CMOS-dump.bin" + echo "where you can view it e.g. using the hex mode of" + echo "the Midnight Commander (F3, F4)." + echo "The 'eeprom' util called from this menu can also write an EEPROM" + echo "image into the chip, e.g. after you have edited the image in 'mc'." + echo "I want this to be done only by people who know what they're doing," + echo "therefore this option is not in the menu. The command to do that is" + echo " eeprom -p 1 -f -w " + else + echo + echo "Failed to dump the 24Cxx contents." + fi + echo + echo "Press ENTER to continue." + read +} + + + + + + +### Script body + +clear +echo " This is " +echo " " +echo " a Smart Battery refurbishment suite " +echo " for BQ2092 and BQ2040 " +echo " by Frank Rysanek " +echo +echo " This software is not guaranteed to work! " +echo " The reset features are not a part of the SBS standard!" +echo " This software may ruin your battery alltogether! " +echo " Use this buch of hacks at your own risk! " +echo +echo " This CD works only using an external I2C adapter that" +echo " connects to the parallel port (printer port, LPT1). " +echo +echo " This is a set of C programs, run from a menu operated" +echo " by a shell script. The operating system where all of " +echo " this is running is Linux. There are six consoles, " +echo " accessible by Alt+F1 to Alt+F6. Feel free to switch " +echo " to another console and run whatever you want. There's" +echo " mc and vim installed in the system, along with most " +echo " of the common unix shell commands you would expect. " +echo +echo " Press ENTER to continue. " +read + +DONE="0" + +while [ "$DONE" = "0" ]; do + clear + + echo " Please select one of the following: (and ENTER)" + echo + echo " 1 help " + echo " 2 BQ2092 stuff " + echo " 3 BQ2040 stuff " + echo " 4 dump the standard SBS registers " + echo " 5 save contents of the 24Cxx EEPROM to a file " + echo " 6 run the Midnight Commander (F10 to quit) " + echo " 7 mount a floppy at /mnt/floppy (DOS, ext2, minix)" + echo " 8 un-mount the floppy from /mnt/floppy " + echo + echo " q quit" + echo + echo -n " > " + + read LINE + case $LINE in + "1") + clear + echo "Press 'q' to Quit from the help." + sleep 3 + less help.txt + ;; + "2") + bq2092_stuff + ;; + "3") + bq2040_stuff + ;; + "4") + # SBS Smart Battery dump + clear + echo "Connect your SMBC, SMBD and GND to the battery's external port." + echo "Press ENTER when ready." + read + echo "Press 'q' to Quit from the listing." + sleep 3 + ./read_batt 2>&1 | less + ;; + "5") + eeprom_stuff + ;; + "6") + mc + ;; + "7") + mount /mnt/floppy + ;; + "8") + umount /mnt/floppy + ;; + "q") + echo + echo "Bye :)" + echo + sleep 1 + exit 0 + ;; + esac +done + +# Whut? We should never get here. +exit 1 + + + diff --git a/bq2040_capacity.c b/bq2040_capacity.c new file mode 100644 index 0000000..23063e5 --- /dev/null +++ b/bq2040_capacity.c @@ -0,0 +1,188 @@ +/* + + Program bq2040_capacity + + Before running this program, attach your parallel port i2c plug + to the internal CMOS Flash on your battery controller PCB. + This is a delicate wiretapping operation. + The internal CMOS Flash is definitely NOT accessible using + the external connector of the battery. + + Put together by Frank Rysanek + Based heavily on the "eeprom" program by Chris , + as distributed with the lm_sensors package. + +*/ + + +/* +This program is hereby placed into the public domain. +Of course the program is provided without warranty of any kind. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_I2C_BUS "/dev/i2c-0" +#define DEFAULT_EEPROM_ADDR 0x50 /* the 24C16 sits on i2c address 0x50 */ +#define MAX_BYTES 8 /* max number of bytes to write in one piece */ + +/* write len bytes (stored in buf) to eeprom at address addr, page-offset offset */ +/* if len=0 (buf may be NULL in this case) you can reposition the eeprom's read-pointer */ +/* return 0 on success, -1 on failure */ +int eeprom_write(int fd, + unsigned int addr, + unsigned int offset, + unsigned char *buf, + unsigned char len +){ + struct i2c_rdwr_ioctl_data msg_rdwr; + struct i2c_msg i2cmsg; + int i; + char _buf[MAX_BYTES + 1]; + + if(len>MAX_BYTES){ + fprintf(stderr,"I can only write MAX_BYTES bytes at a time!\n"); + return -1; + } + + if(len+offset >256){ + fprintf(stderr,"Sorry, len(%d)+offset(%d) > 256 (page boundary)\n", + len,offset); + return -1; + } + + _buf[0]=offset; /* _buf[0] is the offset into the eeprom page! */ + for(i=0;i _buf[1..n+1] */ + _buf[1+i]=buf[i]; + + msg_rdwr.msgs = &i2cmsg; + msg_rdwr.nmsgs = 1; + + i2cmsg.addr = addr; + i2cmsg.flags = 0; + i2cmsg.len = 1+len; + i2cmsg.buf = _buf; + + if((i=ioctl(fd,I2C_RDWR,&msg_rdwr))<0){ + perror("ioctl()"); + fprintf(stderr,"ioctl returned %d\n",i); + return -1; + } + + if(len>0) + fprintf(stderr,"Wrote %d bytes to eeprom at 0x%02x, offset %08x\n", + len,addr,offset); + else + fprintf(stderr,"Positioned pointer in eeprom at 0x%02x to offset %08x\n", + addr,offset); + + return 0; +} + + + + +int main(int argc, char **argv){ + + /* filedescriptor and name of device */ + int d; + int unlock=0; + char i = 0; + char *dn=DEFAULT_I2C_BUS; + unsigned char buf[3]; + + unsigned int addr=DEFAULT_EEPROM_ADDR; + int rwmode=0; + int capacity=1234; + + while((i=getopt(argc,argv,"hc:d:u"))>=0){ + switch(i){ + case 'h': + fprintf(stderr,"\n"); + fprintf(stderr,"The purpose of this util is to help during refurbishments of battery\n"); + fprintf(stderr,"packs equipped with the BQ2040 gas gauge chip.\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"You'll need the primitive parallel port I2C adapter described with\n"); + fprintf(stderr,"the i2c-pport driver. Don't use this on the internal busses such as\n"); + fprintf(stderr,"i2c-piix4 - you'd nuke your RAM DIMMs!!!\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"Use this util to talk to the 24C01 flash of BQ2040 - NOT to the BQ2040 itself!\n"); + fprintf(stderr,"I.e., you need to tap the flash chip's pins directly with a soldering iron.\n"); + fprintf(stderr,"The external SMBUS of the battery is not the right one for this purpose!\n"); + fprintf(stderr,"\n"); + fprintf(stderr,"### Usage: ###\n"); + fprintf(stderr,"\n"); + fprintf(stderr," %s [-c capacity] [-d /dev/i2c-whatever] [-u]\n",argv[0]); + fprintf(stderr,"\n"); + fprintf(stderr,"The default capacity (in mAh) is 1234, the default bus is i2c-0.\n"); + fprintf(stderr,"Use the -u option to turn off the write protection of BQ2040.\n"); + fprintf(stderr,"(seems no use, really, as the reset doesn't seem to work.)\n\n"); + exit(1); + break; + case 'c': + if(sscanf(optarg,"%d",&capacity)!=1) + { + fprintf(stderr,"Cannot parse '%s' as new capacity in mAh.\n", + optarg); + exit(1); + } + break; + case 'd': + dn=optarg; + break; + case 'u': + unlock=1; + break; + default: + break; + } + + } + + if((d=open(dn,O_RDWR))<0){ + fprintf(stderr,"Could not open i2c at %s\n",dn); + perror(dn); + exit(1); + } + + fprintf(stderr,"i2c-devicenode is : %s\n",dn); + + if (unlock == 1) + { + fprintf(stderr,"Removing the write-protect lock - writing 0x0000 at 0x3C...\n"); + + buf[0] = 0x00; + buf[1] = 0x00; + buf[2] = 0x00; + if(eeprom_write(d,DEFAULT_EEPROM_ADDR,0x3C,buf,2)<0) + { + fprintf(stderr,"Failed to write the data at 0x3C. Exiting.\n"); + exit(1); + } + fprintf(stderr,"Write operation successful.\n"); + } + + fprintf(stderr,"Desired new capacity: %d\n - writing it at 0x60...", capacity); + + buf[0] = capacity & 0x000000FF; + buf[1] = (capacity & 0x0000FF00) >> 8; + buf[2] = 0; + + if(eeprom_write(d,DEFAULT_EEPROM_ADDR,0x60,buf,2)<0) + { + fprintf(stderr,"Failed to write the new capacity at 0x60. Exiting.\n"); + exit(1); + } + fprintf(stderr,"Write operation successful.\n"); + + close(d); + + fprintf(stderr,"The write operation appears to have succeeded.\n"); + exit(0); +} diff --git a/eeprom.c b/eeprom.c new file mode 100644 index 0000000..9d3deaf --- /dev/null +++ b/eeprom.c @@ -0,0 +1,343 @@ +/* Written by Chris */ +/* distributed with the lm_sensors package, in the prog/eepromer/ directory */ +/* This version was taken from lm_sensors 2.6.4 (still the same in 2.7.0) */ +/* and was updated to support 24c01 and 24c02 (MAX_BYTES=8) by */ +/* Frank Rysanek */ +/* This patch should appear in the next version of lm_sensors after 2.7.0. */ + +/* +This program is hereby placed into the public domain. +Of course the program is provided without warranty of any kind. +*/ +#include +#include +#include +#include +#include +#include +#include +#include + +/* + this program can read 24C16 (and probably smaller ones, too) + I wrote it as a quick and dirty hack because my satellite receiver + hung again... so I had to reprogram the eeprom where is stores it's + settings. + */ + +#define DEFAULT_I2C_BUS "/dev/i2c-0" +#define DEFAULT_EEPROM_ADDR 0x50 /* the 24C16 sits on i2c address 0x50 */ +#define DEFAULT_NUM_PAGES 8 /* we default to a 24C16 eeprom which has 8 pages */ +//#define BYTES_PER_PAGE 256 /* one eeprom page is 256 byte */ +#define MAX_BYTES 8 /* max number of bytes to write in one piece */ + +/* write len bytes (stored in buf) to eeprom at address addr, page-offset offset */ +/* if len=0 (buf may be NULL in this case) you can reposition the eeprom's read-pointer */ +/* return 0 on success, -1 on failure */ +int eeprom_write(int fd, + unsigned int addr, + unsigned int offset, + unsigned char *buf, + unsigned char len +){ + struct i2c_rdwr_ioctl_data msg_rdwr; + struct i2c_msg i2cmsg; + int i; + char _buf[MAX_BYTES + 1]; + + if(len>MAX_BYTES){ + fprintf(stderr,"I can only write MAX_BYTES bytes at a time!\n"); + return -1; + } + + if(len+offset >256){ + fprintf(stderr,"Sorry, len(%d)+offset(%d) > 256 (page boundary)\n", + len,offset); + return -1; + } + + _buf[0]=offset; /* _buf[0] is the offset into the eeprom page! */ + for(i=0;i _buf[1..n+1] */ + _buf[1+i]=buf[i]; + + msg_rdwr.msgs = &i2cmsg; + msg_rdwr.nmsgs = 1; + + i2cmsg.addr = addr; + i2cmsg.flags = 0; + i2cmsg.len = 1+len; + i2cmsg.buf = _buf; + + if((i=ioctl(fd,I2C_RDWR,&msg_rdwr))<0){ + perror("ioctl()"); + fprintf(stderr,"ioctl returned %d\n",i); + return -1; + } + + if(len>0) + fprintf(stderr,"Wrote %d bytes to eeprom at 0x%02x, offset %08x\n", + len,addr,offset); + else + fprintf(stderr,"Positioned pointer in eeprom at 0x%02x to offset %08x\n", + addr,offset); + + return 0; +} + +/* read len bytes stored in eeprom at address addr, offset offset in array buf */ +/* return -1 on error, 0 on success */ +int eeprom_read(int fd, + unsigned int addr, + unsigned int offset, + unsigned char *buf, + unsigned char len +){ + struct i2c_rdwr_ioctl_data msg_rdwr; + struct i2c_msg i2cmsg; + int i; + + if(len>MAX_BYTES){ + fprintf(stderr,"I can only write MAX_BYTES bytes at a time!\n"); + return -1; + } + + if(eeprom_write(fd,addr,offset,NULL,0)<0) + return -1; + + msg_rdwr.msgs = &i2cmsg; + msg_rdwr.nmsgs = 1; + + i2cmsg.addr = addr; + i2cmsg.flags = I2C_M_RD; + i2cmsg.len = len; + i2cmsg.buf = buf; + + if((i=ioctl(fd,I2C_RDWR,&msg_rdwr))<0){ + perror("ioctl()"); + fprintf(stderr,"ioctl returned %d\n",i); + return -1; + } + + fprintf(stderr,"Read %d bytes from eeprom at 0x%02x, offset %08x\n", + len,addr,offset); + + return 0; +} + + + +int main(int argc, char **argv){ + int bytes_per_page = 256; + int i,j; + + /* filedescriptor and name of device */ + int d; + char *dn=DEFAULT_I2C_BUS; + + /* filedescriptor and name of data file */ + int f=-1; + char *fn=NULL; + + unsigned int addr=DEFAULT_EEPROM_ADDR; + int rwmode=0; + int pages=DEFAULT_NUM_PAGES; + int pgs_specified = 0; + + int force=0; /* suppress warning on write! */ + + while((i=getopt(argc,argv,"d:a:p:t:wyf:h"))>=0){ + switch(i){ + case 'h': + fprintf(stderr,"%s [-d dev] [-a adr] [-p pgs] [-t type] [-w] [-y] [-f file]\n",argv[0]); + fprintf(stderr,"\tdev: device, e.g. /dev/i2c-0 (def)\n"); + fprintf(stderr,"\tadr: base address of eeprom, eg 0xA0 (def)\n"); + fprintf(stderr,"\tpgs: number of pages to read, eg 8 (def)\n"); + fprintf(stderr,"\ttype: chip type string, e.g. 24c16 or 24C02.\n"); + fprintf(stderr,"\t-w : write to eeprom (default is reading!)\n"); + fprintf(stderr,"\t-y : suppress warning when writing (default is to warn!)\n"); + fprintf(stderr,"\t-f file: copy eeprom contents to/from file\n"); + fprintf(stderr,"\t (default for read is test only; for write is all zeros)\n"); + fprintf(stderr,"\t-h : this help screen\n"); + fprintf(stderr,"Note on bytes vs. bits: e.g. 24c02 = 2 kilo bits = 256 Bytes.\n"); + fprintf(stderr,"Note on pages/addresses:\n"); + fprintf(stderr,"\teeproms with more than 256 byte appear as if they\n"); + fprintf(stderr,"\twere several eeproms with consecutive addresses on the bus\n"); + fprintf(stderr,"\tso we might as well address several seperate eeproms with\n"); + fprintf(stderr,"\tincreasing addresses....\n"); + fprintf(stderr,"\tSpecify 'type' to set 'pgs' automagically.\n"); + fprintf(stderr,"Note on chip types:\n"); + fprintf(stderr,"\tthis parameter is somewhat redundant. Sets pgs to match the\n"); + fprintf(stderr,"\tchip's full capacity. Also, the 24c01 has only 128 bytes\n"); + fprintf(stderr,"\tper page, whereas the others have 256.\n\n"); + exit(1); + break; + case 'd': + dn=optarg; + break; + case 'a': + if(sscanf(optarg,"0x%x",&addr)!=1){ + fprintf(stderr,"Cannot parse '%s' as addrs., example: 0xa0\n", + optarg); + exit(1); + } + break; + case 't': + if (!(strncmp(optarg,"24c01",5) && strncmp(optarg,"24C01",5))){ + if (pgs_specified == 0) pages = 1; + bytes_per_page = 128; + fprintf(stderr,"chip type : 24C01"); + fprintf(stderr," (=> Setting bytes_per_page to 128.)\n"); + } + else if (!(strncmp(optarg,"24c02",5) && strncmp(optarg,"24C02",5))){ + if (pgs_specified == 0) pages = 1; + fprintf(stderr,"chip type : 24C02\n"); + } + else if (!(strncmp(optarg,"24c04",5) && strncmp(optarg,"24C04",5))){ + if (pgs_specified == 0) pages = 2; + fprintf(stderr,"chip type : 24C04\n"); + } + else if (!(strncmp(optarg,"24c08",5) && strncmp(optarg,"24C08",5))){ + if (pgs_specified == 0) pages = 4; + fprintf(stderr,"chip type : 24C08\n"); + } + else if (!(strncmp(optarg,"24c16",5) && strncmp(optarg,"24C16",5))){ + if (pgs_specified == 0) pages = 8; + fprintf(stderr,"chip type : 24C16\n"); + } + else { + fprintf(stderr,"Cannot parse '%s' as chip type (example: 24C02)\n", + optarg); + exit(1); + } + + break; + case 'p': + if(sscanf(optarg,"%d",&pages)!=1){ + fprintf(stderr,"Cannot parse '%s' as number of pages, example: 8\n", + optarg); + exit(1); + } + pgs_specified = 1; + break; + case 'w': + rwmode++; + break; + case 'f': + fn=optarg; + break; + case 'y': + force++; + break; + } + + } + + fprintf(stderr,"base-address of eeproms : 0x%02x\n",addr); + fprintf(stderr,"number of pages to read : %d (0x%02x .. 0x%02x)\n", + pages,addr,addr+pages-1); + + if(fn){ + if(!rwmode) /* if we are reading, *WRITE* to file */ + f=open(fn,O_WRONLY|O_CREAT,0666); + else /* if we are writing to eeprom, *READ* from file */ + f=open(fn,O_RDONLY); + if(f<0){ + fprintf(stderr,"Could not open data-file %s for reading or writing\n",fn); + perror(fn); + exit(1); + } + fprintf(stderr,"file opened for %7s : %s\n",rwmode?"reading":"writing",fn); + fprintf(stderr," on filedescriptor : %d\n",f); + } + + if((d=open(dn,O_RDWR))<0){ + fprintf(stderr,"Could not open i2c at %s\n",dn); + perror(dn); + exit(1); + } + + fprintf(stderr,"i2c-devicenode is : %s\n",dn); + fprintf(stderr," on filedescriptor : %d\n\n",d); + + /*** + *** I'm not the one to blame of you screw your computer! + ***/ + if(rwmode & ! force){ + unsigned char warnbuf[4]; + fprintf(stderr,"**WARNING**\n"); + fprintf(stderr," - \tYou have chosen to WRITE to this eeprom.\n"); + fprintf(stderr,"\tMake sure that this tiny chip is *NOT* vital to the\n"); + fprintf(stderr,"\toperation of your computer as you can easily corrupt\n"); + fprintf(stderr,"\tthe configuration memory of your SDRAM-memory-module,\n"); + fprintf(stderr,"\tyour IBM ThinkPad or whatnot...! Fixing these errors can be\n"); + fprintf(stderr,"\ta time-consuming and very costly process!\n\n"); + fprintf(stderr,"Things to consider:\n"); + fprintf(stderr," - \tYou can have more than one i2c-bus, check in /proc/bus/i2c\n"); + fprintf(stderr,"\tand specify the correct one with -d\n"); + fprintf(stderr,"\tright now you have chosen to use '%s'\n",dn); + fprintf(stderr," - \tA eeprom can occupy several i2c-addresses (one per page)\n"); + fprintf(stderr,"\tso please make sure that there is no vital eeprom in your computer\n"); + fprintf(stderr,"\tsitting at addresses between 0x%02x and 0x%02x\n",addr,addr+pages-1); + + fprintf(stderr,"Enter 'yes' to continue:"); + fflush(stderr); + if(!fgets(warnbuf,sizeof(warnbuf),stdin)){ + fprintf(stderr,"\nCould not read confirmation from stdin!\n"); + exit(1); + } + if(strncmp(warnbuf,"yes",3)){ + fprintf(stderr,"\n** ABORTING WRITE! **, you did not answer 'yes'\n"); + exit(1); + } + } + + for(i=0;i=0){ + j=read(f,buf,sizeof(buf)); + if(j<0){ + fprintf(stderr,"Cannot read from file '%s'\n",fn); + perror(fn); + exit(1); + } + if(j!=sizeof(buf)){ + fprintf(stderr,"File '%s' is too small, padding eeprom with zeroes\n",fn); + while(j=0){ + j=write(f,buf,sizeof(buf)); + if(j!=sizeof(buf)){ + fprintf(stderr,"Cannot write to file '%s'\n",fn); + perror(fn); + exit(1); + } + } + + } + + if(f>=0) + close(f); + + close(d); + + exit(0); + +} diff --git a/help.txt b/help.txt new file mode 100644 index 0000000..c42639b --- /dev/null +++ b/help.txt @@ -0,0 +1,349 @@ +About this CD +============= + +The system booting from this CD is a minimalistic Linux, +based on RedHat 8.0. +It contains many system utils and can be used as a rescue +CD for general Linux distributions. + +The CD starts with six consoles open, accessible via +ALT+F1 to ALT+F6. There's no login prompt - all the +six consoles are started with a straight root shell. +Only the first console runs the battery refurbishment +wrapper script - the five remaining consoles offer +plain bash prompt. + +The root directory is running off a ramdisk and is +therefore writeable. The CD is mounted under /usr +- therefore, /usr is read-only. The /bin and /sbin +directories are symlinks to /usr/bin and /usr/sbin +and are therefore read-only, too. +You can put your own stuff in /var or /tmp. + +The i2c/battery stuff is in /usr/battery - apart from +the binaries, there's C source code for all the utils +and there's a Makefile. Feel free to hack it in any +way you want. Feel free to run the utils by hand +if the scripted options don't satisfy your needs. + +The dump files from the `eeprom' util are stored +in /var/battery. + +If you want to transfer files to some non-volatile +media, you can always mount a floppy or a system +hard disk - the Kernel should be able to mount +MSDOS FAT partitions from FAT12 to FAT32/VFAT, +EXT2, EXT3 and minix. + + +About smart battery controller IC's (AKA gas gauge IC's) +======================================================== + +There's a standard called Smart Battery Subsystem (SBS). +It says that a battery pack in a notebook shall contain +a Smart Battery controller chip (the "gas gauge"), there +should be a Smart Charger in the notebook, and the +notebook itself should have a host bridge to talk to +one or multiple smart batteries and smart chargers. + +All the smart devices are chatting among themselves +over a bus called the SMBus (the System Management Bus). +The SMBus is a variation of the Philips I2C, originating +in consumer electronics - a two-wire (plus common ground) +multi-master serial bus, rather slow and without galvanic +insulation, allowing chips on one board to talk to each +other in a uniform way, especially for the purpose of +microprocessor control (so that the microprocessor can +control various devices in the piece of equipment using +just two I/O pins and a uniform bus protocol). + +In I2C, the two live wires are called SDA (Serial DAta) +and SCL (Serial CLock). In SMBus, they're called SMBD +(SMBus Data) and SMBC (SMBus Clock). + +The SMBus is otherwise present on the motherboards of +modern PC's, where it allows programs running on the +main CPU to talk to a number of devices - temperature +and fan control sensors, bus/CPU clock synthesizer IC's, +the ID EPROMs in RAM DIMMs etc. + +There is a general-purpose Linux implementation of +I2C/SMBus, consisting of a basic "i2c" drivers package +and a "utils" package called lm_sensors at + + http://www.lm-sensors.nu + +It supports a number of I2C-to-PC bus adapters. +Many north bridges in PC chipsets have an SMBus port +that's accessible via drivers in the operating system, +the same applies to many TV/Radio tuner cards. +There are dedicated I2C interface boards etc. + +Unfortunately, even in notebooks that have an SMBus-capable +chipset, the battery SMBus is usually different from the +"system" SMBus. The battery SMBus is either not accessible +from the operating system or at least in a completely +obscure and proprietary way (see e.g. the Compaq notebooks +with the battery SMB being connected to the keyboard +controller MCU). + +The most straightforward way to talk to your smart battery +is via some external I2C/SMBus adapter that can be handled +by Linux i2c/lm_sensors. +Perhaps the simplest i2c adapter available is the +"primitive parallel port i2c adapter", connecting to the +Canon DSUB25F printer connector. All you need is a Canon +DSUB25M, three wires and three pieces of brass to serve +as battery contacts - and a soldering iron. This is the +wiring description, quoted from the documentation of +the i2c-pport driver: + +SDA - connect to pin 14 (Auto Linefeed) +SCL - connect to pin 16 (Initialize Printer) +GND - connect to pin 18-25 + +If you need to tap the EEPROM IC directly, you will +also need +5V to feed it - I suggest to take it +from pin 4 in a PS/2 keyboard connector. + +I also recommend that you make two separate plugs: +- one for the external SMBus, with brass contacts + that fit into the pack's female connector, +- and another one for the EEPROM, this one with + thin enamel wire "pigtails" for easy wiretapping. + +You'll need to do a bit of investigation to find +the right way to hook up your cables to the external +SMBus of the battery pack (via the pack's external +connector). Each battery model is different. +Using a datasheet for your IC and an ohm-meter, try +to find out which connector pins correspond to your +gas gauge IC's SDA and SCL pins. +The common ground is the battery pack ground. + +See /usr/battery/schematic.txt for a way to wiretap +the serial EEPROM IC. + +See the hardware book (http://www.hardwarebook.net) +for PC connector drawings and pinouts: +http://www.hardwarebook.net/connector/userinput/keyboardpc6.html +http://www.hardwarebook.net/connector/parallel/parallelpc.html + +See the PNG images and data sheets enclosed in /usr/battery/ +on this CD for more inspiration. + +Most SMBus gas gauge IC's use an external I2C EEPROM +(AKA CMOS Flash) to store their data that needs to be +non-volatile. This external EEPROM IC (usually a 24C02 +or 24C01) is hooked up to the gas gauge IC via a private +I2C bus, that is not accessible from the outer SMBus +port of the battery pack. In other words, there are +two I2C/SMBus busses in the battery pack: one outbound, +to the notebook system, and another one private, that's +supposed to be hidden from the outside world. + +During battery refurbishments, you need to reset the +gas gauge chip - to make it re-learn the battery capacity +upwards, rather than downwards. + +Some gas gauge chips can be reset using a software command +applied via the external SMBus, perhaps with the help of a +power-cycle applied to the gas gauge IC (disconnect and +reconnect the cells after the software reset). +Some gas gauge chips can only be fooled into relearning the +refurbished capacity by overwriting their "actual capacity" +value in the external EEPROM. In such cases, you need to +wiretap the EEPROM IC itself, which might turn out to be +a bit of a fine microscopic surgery - unless you have an +in-circuit attacheable socket, you need to solder +thin enamel wires to the legs of the EEPROM IC. + +Either way, you will need documentation for the gas gauge +IC. BQ chips are particularly well documented - this CD +has dedicated utils for the BQ2092 and the BQ2040, +and a general-purpose I2C EEPROM-reading/writing util +that can be used for other chips as well, if you know +exactly what you're doing. +There are battery packs equipped with gas-gauge circuits +build around general-purpose MCU's that are undocumented +by virtue - the only standard that they must comply to is +the SBS, which means that they have the standard +SBS commands/registers visible through the outbound SMBus. + +RESET is not an SBS-standardized operation. + +If your gas gauge chip is not documented, you can still +try to guess the right memory position in the external +I2C EEPROM, if there is one, by comparing the SBS registers +dump with the EEPROM dump. Never tried that, though. + +I can even imagine a brute-force hacking approach - but +you'd need to combine two I2C busses on one primitive parallel +port (can be done, apparently) and to add a computer-controlled +power switch to power-cycle the gas gauge IC after each +hacking attempt... + +It seems that the BQ2092 can be reset in software (combined +with a power-cycle to the gas gauge IC). +It seems that the BQ2040 can not be reset in software, not +even after the write lock is removed via an EEPROM hack. +It seems that the BQ2040 responds well to the "actual capacity" +EEPROM hack. +I don't have information about any other ICs. + +One more note about hacking the EEPROM directly: +As the gas gauge chip only provides power supply to the +EEPROM when it needs to talk to it, you need to tap its +Ucc+ line too, and feed it from your computer's +5V rail. +I recommend the +5V line available at pin4 of a PS/2 keyboard +socket. +While you're talking directly to the EEPROM, you'd better +keep the gas gauge IC powered off, to prevent it from +intervening. +It seems that the BQ2040 doesn't object against having +its EEPROM PWR pin pulled high externally - it doesn't +blow and it doesn't draw any current under such conditions. + + +Tools available on the CD +========================= +The things below are largely based on sample code from the +lm_sensors and i2c packages. Nothing is written by me from +scratch. + +- reset-bq2092 - a regular reset of the BQ2092 + +- reset-bq2040 - a regular reset of the BQ2040 (defunct?) + +- bq2040_capacity - an EEPROM hack for the BQ2040 + Sets the "actual capacity" to a user-supplied value + and can optionally remove the write-protect lock. + + Run with the '-h' parameter to get more help. + +- read_batt - dumps the standard Smart Battery registers + From here, you can read what the gas gauge thinks about the battery. + This can be used with any SBS-compliant gas-gauge with + a two-wire SMBus/I2C. + +- eeprom - reads or writes a 24Cxx eeprom + + Run with the '-w' parameter to WRITE the EEPROM. + Run with the '-h' parameter to get more help. + +- mc and vim +- many other UNIX command-line tools + +Please note that the reset command and the EEPROM memory map +are different between the BQ2092 and the BQ2040, let alone +other chips - you must not use the dedicated utils (those with +chip names in them) for other IC's without modification +in the source code and a recompile. + + +When it doesn't work +==================== + +I have noticed that some parallel ports don't work. +In such cases, all you get is "i2c transaction failed". +The bit-banging doesn't get through to the battery or back. + +To check if your i2c-pport essentially works on the hardware +or not, connect your battery's external connector to the +parallel port adapter and choose the "SBS registers dump" +from the menu. If you get some data, your parallel port +hardware is viable for I2C traffic. If you get a million +lines saying "i2c transaction failed", try some other PC. +Especially notebooks PC's seem to be quite tolerant. + +The "primitive parallel port i2c adapter" doesn't use +the port in exactly the standard way :) Essentially, it's +doing *input* on the port in an out-only configuration +(with the SDA/SCL pins not tri-stated). + +Most modern parallel ports (probably all in machines that +will be able to boot this CD) can be switched to several +different modes of operation in the BIOS Setup - the most +common modes are: +- Standard (or SPP) - the 8 data pins are out-only +- Bidirectional (not quite a standard mode, whatever it is) +- EPP (can be switched to bidirectional mode in a standard way) +- ECP (addressing mode, bidirectional, DMA-capable) +Some provide hardware can even do different versions of +EPP/ECP, the ECP can use different DMA channels etc. + +These are my particular results: +1) Compaq Presario 1622 notebook, Pentium233MMX, + Intel TX chipset (82371AB ISA bridge): + I2C works in "standard" and "bidirectional" mode + +2) noname vanilla desktop PC, Athlon XP 1700+, + VIA KT333 (VT8233A ISA bridge): + I2C doesn't work in any mode (SPP/EPP/ECP) + +3) Acer TravelMate 270 notebook, P4 @ 1.4 GHz, + SiS chipset (SiS 85C503/5513 ISA bridge): + I2C works in "standard", "bidirectional" and ECP mode + +4) noname vanilla desktop PC, Pentium-S @ 225 MHz + Intel VX Natoma/Triton II chipset (82371SB ISA bridge) + I2C works in "normal" mode + +5) Advantech industrial processor board, P4 @ 2.4 GHz, + Intel 845G/GL chipset (82201 ISA bridge) + I2C works in "normal" mode (different from "SPP"!?) + +6) noname dual-P3 server, 2x PIII @ 1.4 GHz + ServerWorks chipset (OSB4 ISA bridge, W83977 I/O) + My i386 refurbishment CD doesn't finish booting in + the first place, it looks like a glibc incompatibility. + When booted from a hard disk with i586 libraries, + I2C doesn't work in any mode. + +To get to know your chipset, switch to another console +(e.g., using ALT+F2) and type 'lspci '. This +will give you a listing of devices visible on the PCI +bus (if you do have a PCI bus, obviously). + + +Hints for your own development +============================== +If you need to compile the i2c/lm_sensors for a different +kernel, always disable the stock kernel's i2c support +and get a recent version of the i2c package from the +lm_sensors website at http://www.lm-sensors.nu +Unless you know what you're doing. + +VIn 2.4 kernels and i2c-2.7.0, you have to turn the entire +parellel port support off to make the i2c-pport work. +If memory serves, in 2.2 kernels and i2c-2.6.4 you could +(or had to?) leave the basic parallel port support compiled +in. The tools on this CD compile against both. + +Feel free to hack any of the utils from this CD to suit +your needs. The license is GPL. + +If you want to modify the CD, feel free - in Linux, copy +its contents to some directory on your hard drive (use +`cp -dpR' or even `tar -czf' to keep the attributes), +replace/add parts, and re-package the whole thing +using mkisofs: + +mkisofs -o bootcd.iso -b isolinux/isolinux.bin -c isolinux/boot.cat \ +-no-emul-boot -boot-load-size 4 -boot-info-table -R -J -L /your/directory + + +Maintainer contact +================== +The CD was put together by + +Frantisek Rysanek + +employed with: +FCC Prumyslove Systemy s.r.o. +SNP 8 +Usti nad Labem +400 11 +Czech Republic + +Frantisek.Rysanek@post.cz diff --git a/read_batt.c b/read_batt.c new file mode 100644 index 0000000..a29f141 --- /dev/null +++ b/read_batt.c @@ -0,0 +1,131 @@ +/* + + Program read_batt + + Use this to read the standard registers of a Smart Battery. + Originally, this was written for a battery based on the + Benchmarq / Unitrode / Texas Instruments chip called BQ2092, + later tested with BQ2040. + + Put together by Frank Rysanek + Heavily based on the example C code that came with i2c 2.6.4. + +*/ + +#include +#undef __NFDBITS +#undef __FDMASK +#undef off_t +#include "/usr/local/include/linux/i2c.h" +#include "/usr/local/include/linux/i2c-dev.h" +#define O_RDWR 0x0002 +/* #include */ +/* #include */ +/* #include */ + + + int file; + int adapter_nr = 0; /* probably dynamically determined */ + char filename[20]; + +int main(void) +{ + int addr = 0x0b; /* 11(dec) - The I2C address */ + /*int addr = 0x50; nah, not accessible */ /* 80(dec), 01010000(bin) */ + __u8 reg = 0x08; /* i2c command - just an example - temperature, in 0.1 K */ + __s32 res; + char buf[30]; + int values_read = 0; + char res_buf[3]; + sprintf(filename,"/dev/i2c%d",adapter_nr); + + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - couldn't open file.\n"); + exit(1); + } + + /* int addr = 0x16; */ /* 22(dec) - The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - ioctl failed.\n"); + exit(1); + } + +/* +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. +*/ + + /* __u8 reg = 0x08; */ /* Device register to access */ + /* Using SMBus commands */ + + for (reg=0; reg <= 0x3f; reg++) + /*for (reg=0; reg <= 0xFE; reg++) // there are a number of undocumented registers.*/ + { + res = i2c_smbus_read_word_data(file,reg); + if (res < 0) { + printf("I2C transaction failed at reg %d\n", reg); + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* res contains the read word */ + printf("Reg 0x%02X: Read 0x%04X %5d\n", reg, res, res); + /* + *(int*)res_buf = res; + res_buf[3] = 0; + printf("Reg 0x%02X: Read 0x%04X %5d %s\n", reg, res, res, res_buf); + */ + } + } + + values_read = i2c_smbus_read_i2c_block_data(file,0x20,buf); + if (values_read > 0) + { + buf[values_read+1] = 0; + printf("Manufacturer name: %s\n",buf); + } + else + { + printf("Block read failed: %d.\n", values_read); + } + + values_read = i2c_smbus_read_i2c_block_data(file,0x21,buf); + if (values_read > 0) + { + buf[values_read+1] = 0; + printf("Device name: %s\n",buf); + } + else + { + printf("Block read failed: %d.\n", values_read); + } + + values_read = i2c_smbus_read_i2c_block_data(file,0x22,buf); + if (values_read > 0) + { + buf[values_read+1] = 0; + printf("Device chemistry: %s\n",buf); + } + else + { + printf("Block read failed: %d.\n", values_read); + } + + values_read = i2c_smbus_read_i2c_block_data(file,0x23,buf); + if (values_read > 0) + { + buf[values_read+1] = 0; + printf("Manufacturer data: %s\n",buf); + } + else + { + printf("Block read failed: %d.\n", values_read); + } + + exit(0); +} +/* +IMPORTANT: because of the use of inline functions, you *have* to use +'-O' or some variation when you compile your program! +*/ diff --git a/reset-bq2040.c b/reset-bq2040.c new file mode 100644 index 0000000..065b17f --- /dev/null +++ b/reset-bq2040.c @@ -0,0 +1,104 @@ +/* + + Program reset_bq2040 + + Using this code, I was attempting to reset the BQ2040, based on + the soft reset procedure described in the BQ2040 datasheet. + My attempts failed even after I have removed the write-protection, + but I'm publishing this code nevertheless, so that anyone can see + if it works for him or not. + After you try this, don't forget to disconnect and reconnect + the battery cells to power-cycle the IC - well it doesn't + seem to matter with the BQ2040, but it did help with a BQ2092... + If indeed your reset attempt fails too, try tapping the Flash + and try talking to it using the other attached program. + + Put together by Frank Rysanek + Heavily based on the example C code that came with i2c 2.6.4. + +*/ + + + +#include +#undef __NFDBITS +#undef __FDMASK +#undef off_t +#include "/usr/local/include/linux/i2c.h" +#include "/usr/local/include/linux/i2c-dev.h" +#define O_RDWR 0x0002 +/* #include */ +/* #include */ +/* #include */ + + + int file; + int adapter_nr = 0; /* probably dynamically determined */ + char filename[20]; + +int main(void) +{ + int addr = 0x0b; /* 11(dec) - The I2C address */ + /*int addr = 0x50; nah, not accessible */ /* 80(dec), 01010000(bin) */ + __u8 reg = 0x08; /* i2c command - just an example - temperature, in 0.1 K */ + __s32 res; + char buf[30]; + int values_read = 0; + char res_buf[3]; + sprintf(filename,"/dev/i2c%d",adapter_nr); + + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - couldn't open file.\n"); + exit(1); + } + + /* int addr = 0x16; */ /* 22(dec) - The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - ioctl failed.\n"); + exit(1); + } + +/* +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. +*/ + + /* Using SMBus commands */ + + /* RESET */ + /* Please check manually that SBD Seal is set to 0. */ + /* Now we can set MaxError: */ + reg = 0x0c; + res = i2c_smbus_write_word_data(file,reg,0x0000); + if (res < 0) { + printf("I2C transaction failed at reg %d\n", reg); + /* ERROR HANDLING: i2c transaction failed */ + //exit(1); + } + else + { + printf("Reg %d - MaxError - written successfully.\n", reg); + } + + /* Now we can write the special value into the reset register: */ + reg = 0x64; + res = i2c_smbus_write_word_data(file,reg,0x8009); + if (res < 0) { + printf("I2C transaction failed at reg %d\n", reg); + /* ERROR HANDLING: i2c transaction failed */ + exit(1); + } + else + { + printf("Reg %d - RESET - written successfully.\n", reg); + } + + exit(0); +} +/* +IMPORTANT: because of the use of inline functions, you *have* to use +'-O' or some variation when you compile your program! +*/ diff --git a/reset-bq2092.c b/reset-bq2092.c new file mode 100644 index 0000000..90358f4 --- /dev/null +++ b/reset-bq2092.c @@ -0,0 +1,104 @@ +/* + + Program reset_bq2092 + + Using this code, I have succeeded to reset the BQ2040, based on + the soft reset procedure described in the BQ2092 datasheet. + I have succeeded to reset the IC despite the fact that, according + to the hints from the BQ2092 datasheet, the IC was write-protected. + + When you run this program, the indicator LEDs on your battery + should go somewhat nuts (if there are any). To complete the reset, + you have to disconnect and reconnect the controller PCB from the + battery cells - to power-cycle the IC. + + Put together by Frank Rysanek + Heavily based on the example C code that came with i2c 2.6.4. + +*/ + + + +#include +#undef __NFDBITS +#undef __FDMASK +#undef off_t +#include "/usr/local/include/linux/i2c.h" +#include "/usr/local/include/linux/i2c-dev.h" +#define O_RDWR 0x0002 +/* #include */ +/* #include */ +/* #include */ + + + int file; + int adapter_nr = 0; /* probably dynamically determined */ + char filename[20]; + +int main(void) +{ + int addr = 0x0b; /* 11(dec) - The I2C address */ + /*int addr = 0x50; nah, not accessible */ /* 80(dec), 01010000(bin) */ + __u8 reg = 0x08; /* i2c command - just an example - temperature, in 0.1 K */ + __s32 res; + char buf[30]; + int values_read = 0; + char res_buf[3]; + sprintf(filename,"/dev/i2c%d",adapter_nr); + + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - couldn't open file.\n"); + exit(1); + } + + /* int addr = 0x16; */ /* 22(dec) - The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + printf("Something went wrong - ioctl failed.\n"); + exit(1); + } + +/* +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. +*/ + + /* Using SMBus commands */ + + /* RESET */ + /* Please check manually that WRINH is set to 0. */ + /* Now we can set MaxError: */ + reg = 0x0c; + res = i2c_smbus_write_word_data(file,reg,0x0000); + res = 0; + if (res < 0) { + printf("I2C transaction failed at reg %d\n", reg); + /* ERROR HANDLING: i2c transaction failed */ + exit(1); + } + else + { + printf("Reg %d - MaxError - written successfully.\n", reg); + } + + /* Now we can write the special value into the reset register: */ + reg = 0x44; + res = i2c_smbus_write_word_data(file,reg,8009); + if (res < 0) { + printf("I2C transaction failed at reg %d\n", reg); + /* ERROR HANDLING: i2c transaction failed */ + exit(1); + } + else + { + printf("Reg %d - RESET - written successfully.\n", reg); + } + + exit(0); +} +/* +IMPORTANT: because of the use of inline functions, you *have* to use +'-O' or some variation when you compile your program! +*/ diff --git a/schematic.txt b/schematic.txt new file mode 100644 index 0000000..6ef70d6 --- /dev/null +++ b/schematic.txt @@ -0,0 +1,11 @@ + + .-----._,-----. + ==| O |== +5V (= keyboad connector PS/2 MiniDIN6 pin 4) + | | + ==| |== (GND = printer port Canon DSUB25 pin 18) + | 24C02 | + ==| |== SCL (= printer port Canon DSUB25 pin 16) + | | +GND ==| |== SDA (= printer port Canon DSUB25 pin 14) + `-------------' +