/* license: public domain, author Ducrot Bruno, modified by Jacek Pliszka provided "as is" - use at your own risk */ #include #include /* #include */ #include #define EC_DATA 0x62 #define EC_SC 0x66 /* Various bit mask for EC_SC (R) */ /* * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | * | IGN | SMI_EVT | SCI_EVT | BURST | CMD | IGN | IBF | OBF | * 80 40 20 10 08 04 02 01 */ #define OBF 0x01 #define IBF 0x02 #define CMD 0x08 #define BURST 0x10 #define SCI_EVT 0x20 #define SMI_EVT 0x40 /* Commands for EC_SC (W) */ #define RD_EC 0x80 #define WR_EC 0x81 #define BE_EC 0x82 #define BD_EC 0x83 #define QR_EC 0x84 #define UDELAY 100 #define EC_UDELAY 900 #define my_cli() do {asm ("cli");} while (0) #define my_sti() do {asm ("sti");} while (0) typedef unsigned char u8; typedef unsigned int u32; static inline int ec_wait(u8 event) { u8 status = 0; u32 i = UDELAY; switch (event) { case OBF: do { status = inb(EC_SC); if (status & OBF) return 0; usleep(EC_UDELAY); } while (--i>0); break; case IBF: do { status = inb(EC_SC); if (!(status & IBF)) return 0; usleep(EC_UDELAY); } while (--i>0); break; default: return -EINVAL; } return -ETIME; } static int ec_read(u8 address, u8 *data) { int status = 0; int result = 0; unsigned long flags; u32 glk = 0; *data = 0; my_cli(); outb(RD_EC, EC_SC); result = ec_wait(IBF); if(result) goto err; outb(address, EC_DATA); result = ec_wait(OBF); if (result) goto err; *data = inb(EC_DATA); goto end; err: errno = -result; end: my_sti(); return result; } static int ec_write(u8 address, u8 data) { int result = 0; int status = 0; unsigned long flags = 0; my_cli(); outb(WR_EC, EC_SC); result = ec_wait(IBF); if (result) goto end; outb(address, EC_DATA); result = ec_wait(IBF); if (result) goto end; outb(data, EC_DATA); result = ec_wait(IBF); if (result) goto end; end: my_sti(); return result; } static inline char *itob(int a) { static char buff[9]; char *p; int i; if (a < 0 || a > 255) return NULL; for (p = buff, i = 0; i < 8; a <<= 1, i++) *p++ = (a & 0x80) ? '1' : '0'; *p = 0; return buff; } int get_data(int a,u8 *data_l,u8 *data_h){ int i; int result; i = 5; do { result = ec_read(a, data_l); } while (result && --i); i = 5; do { result = ec_read(a + 1, data_h); } while (result && --i); usleep(1); return(result); } int main(int argc, char** argv) { u8 data_l, data_h; int a; int i; int result; int verbose; if (iopl(3)) { perror("iopl"); exit (1); } if(argc<2){ printf("Usage:\n"); printf(" battery v \n"); printf(" where v is verbose level=0,1,2,3\n"); printf("Warning! Do not use if ACPI is enabled in the kernel.\n"); printf("Do not run more than one instance at a time.\n"); } else verbose=strtol(argv[1],0,10); if(verbose==3){ printf("\nBattery\n"); printf("-------\n"); for (a = 0xc0 ; a <= 0xcf ; a += 2) { i = 5; do { printf("."); fflush(stdout); result = ec_read(a, &data_l); } while (result && --i); for (;i ; --i) printf(" "), fflush(stdout); i = 5; do { printf(":"); fflush(stdout); result = ec_read(a + 1, &data_h); } while (result && --i); for (;i ; --i) printf(" "), fflush(stdout); if (!result) { printf("0x%.2x 0x%.2x:%.2x %.3d:%.3d (%5d)\n", a, data_h, data_l, data_h, data_l, (data_h << 8) + data_l); } else { printf("Error reading 0x%.2x !\n", a); } usleep(1); } } else if(verbose==2) { /* This is written by Jacek Pliszka */ result=get_data(0xc0,&data_l,&data_h); if (!result) { if(data_l == 16 ) printf("NIMH "); else if(data_l == 90 ) printf("LI-ION "); else printf("Unknown type %d ",data_l); if(data_h == 0) { printf("battery is fully charged."); } else if(data_h == 1) { printf("battery is discharging."); } else if(data_h == 2) { printf("battery is charging."); } else if(data_h == 4) { printf("battery is LOW but charging."); } else if(data_h == 5) { printf("battery is LOW and discharging!"); } else { printf("Wrong battery status code %x. Program output is not reliable.\n",data_h); exit(1); } } get_data(0xce,&data_l,&data_h); printf(" Currently at %d\%\n",data_l); get_data(0xc2,&data_l,&data_h); printf("Capacity: remaining %d mAh ",(data_h<<8)+data_l); get_data(0xcc,&data_l,&data_h); printf("last full %d mAh ",(data_h<<8)+data_l); get_data(0xca,&data_l,&data_h); printf("design %d mAh\n",(data_h<<8)+data_l); get_data(0xc6,&data_l,&data_h); printf("Voltage: current %d mV ",(data_h<<8)+data_l); get_data(0xc8,&data_l,&data_h); printf("design minimum %d mV\n",(data_h<<8)+data_l); } else if(verbose==1) { /* This is written by Jacek Pliszka */ result=get_data(0xc0,&data_l,&data_h); if (!result) { if(data_l == 16 ) printf("NIMH "); else if(data_l == 90 ) printf("LI-ION "); else printf("Unknown "); if(data_h == 0) { printf("= "); } else if(data_h == 1) { printf("- "); } else if(data_h == 2) { printf("+ "); } else if(data_h == 4) { printf("+ "); } else if(data_h == 5) { printf("- "); } else { printf("Wrong battery status code %x. Program output is not reliable.\n",data_h); exit(1); } } get_data(0xce,&data_l,&data_h); printf("%d%% ",data_l); get_data(0xc2,&data_l,&data_h); printf("%d/",(data_h<<8)+data_l); get_data(0xcc,&data_l,&data_h); printf("%d(",(data_h<<8)+data_l); get_data(0xca,&data_l,&data_h); printf("%d)mAh ",(data_h<<8)+data_l); get_data(0xc6,&data_l,&data_h); printf("%d/",(data_h<<8)+data_l); get_data(0xc8,&data_l,&data_h); printf("%dmV\n",(data_h<<8)+data_l); } else if(verbose==0) { /* This is written by Jacek Pliszka */ result=get_data(0xc0,&data_l,&data_h); if (!result) { if(data_h == 0) { printf("="); } else if(data_h == 1) { printf("-"); } else if(data_h == 2) { printf("+"); } else if(data_h == 4) { printf("+"); } else if(data_h == 5) { printf("-"); } else { printf("Wrong battery status code %x. Program output is not reliable.\n",data_h); exit(1); } } get_data(0xce,&data_l,&data_h); printf("%d%%",data_l); } end: return result; } /* More details from Bruno Program written by Ducrot Bruno . : 0xc0 0x00:90 000:144 ( 144) ^^^^ ^^^^^^^ ^^^^^^^ ^^^^^ the ec |2 bytes|the same,|the same in adress.|in hexa|but in |decimal, but |decimal |as a word instead |of bytes. 0xc0: give actually two bytes. ------------------------------ 0x00:xx if the battery is full, 0x01:xx if the battery is discharging, 0x02:xx if the battery is charging. 0x..:90 is the type of the battery. For me, it is LI-ION based. Dont know however for other kind. 0xc2: is for Remain capacity. I have 3784 mAh for now. 0xc4: is for ?? (well, I don't know, sorry) 0xc6: is for current voltage. I have 16637 mV for now. 0xc8: is for design voltage. This battery is designed to deliver at least 148000 mV 0xca: is for design capacity. This battery is designed to have 3900 mAh. 0xcc: is for the last full capacity. 0xce: give actually 2 informations. read the 000:100 The number 100 is the gauge. I have for now 100% Don't know actually for what is the 000. A final warning: don't enable ACPI, or you can have some trouble. Actually, if you want ACPI and the app. I attach, you need to port this app. as a kernel module, since the same I/O are used. Also, be sure to launch only one instance of this app., or you can have some troubles for the same reason. */