Dell XPS M1530 Battery charging – bios downgrade to A09 can help

I took delivery of a Dell XPS M1530 laptop on 31 January 2008. Very pleased with it – ran OpenSolaris almost straight out of the box (sole exception being the Marvell Yukon ethernet, now supported by yge). Things went reasonably well until the SAGE-AU conference last year, when I noticed that the charging function wasn’t. Totally unchargable.

The battery and AC adapter, it turns out, were under a 1 year warranty, not 2 years like the rest of the system. Got a new 3rd party battery (from laptopbattery.net.au) and a new Dell AC adapter, and the battery lasted until about a fortnight ago.

Got another 3rd party battery, this time somewhat cheaper via a Melbourne-based EBay.com.au store. It arrived 60% charged, and seemed to charge up ok when I plugged it all in. Yay, I thought, problem solved.

Not so. As it happens, my laptop’s charging circuit inside the case (not the AC adapter) appears to be unable to recognise any sort of AC adapter, so I get the dreaded “AC Adapter – Unknown device installed” message in the bios, and under bios version A12, NO CHARGING TAKES PLACE WHATSOEVER.

None.

Rotating the power plug in the socket doesn’t help either, even though it’s a genuine Dell AC adapter.

A bunch of searching this evening turned up this thread, which said that it was worth trying to downgrade the bios to version A09, and perhaps run the bios flash program with “/forceit” as an option.

This is about the only reason that I keep the Vista partition hanging around. Though sadly, when you run the 1530_A09.exe program (even with /forceit and /forcetype) in Vista, with the battery at less than 10%, you get an error message telling you to charge the battery to at least 10% before trying again. Very frustrating!

Fortunately, one of the posters in that thread had a link to a Win98 rescue cd image which contains the bios flasher… pulled it down, burned it, booted it, flashed the bios (it’s on the logical D: drive) and now I’m sitting here watching the laptop bios battery screen increment the charge status every few minutes.

The AC Adapter is still “unknown” – but at least I’m getting charge in. And I don’t have to go out and buy a new laptop (at least, not just yet).

This is the sort of thing which really annoys the heck out of me. Not only is there no opportunity for me to get the bios flashed from OpenSolaris using fwflash but there’s clearly some “enhancement” in A12 which is actually a regression and serious functional misfeature compared to A09. Let’s not start on the hardware problems in the AC adapter, either.




An exercise in frustration

Or, what to do when you want to buy a printer.

Last year our trusty Lexmark Optra S 1855 (networked, extra paper tray and duplexer) got to the end of its cartridge, and we found to our annoyance that a replacement would set us back close to AUD450. Not keen to take that option, we decided to get a schmick multifunction inkjet.

It was unfortunate that I didn’t check www.linuxprinting.org first, because I bought the Canon Pixma MP620. Sure, it’s a nice printer with respectable photo and plain paper printing, good copying and scanning as well. BUT if you don’t want to run a WindowsXP/Vista/7 or Mac to be a print server, then your option is running linux and using Canon’s binary blob libraries linked in with their (imnsho) poorly written ps to Canon converter coupled with cups-bjnp, the reverse-engineered BubbleJet Network Protocol implementation.

I’m not opposed to running linux, not by any stretch. However, I really do prefer to run the OS that I actually help develop, as much as humanly possible.

With that in mind, I pulled down the source for the “linux drivers” from Canon ran a bunch of configure scripts (remembering to use gcc, and adding LDFLAGS=”-lnsl -lsocket” to the env) and got started. All proceeded reasonably well until I got to what turns out to be the actual kernel of the problem: cnijfilter. This utility is the one which takes ghostscript’s ppmraw output and turns it into Canon-specific control codes which you can then send across the wire using cups-bjnp.

To get this to compile (as distinct from linking, more on that shortly), I had to make these changes to cnijfilter/src/getipc.c:

44c44
<     struct sockaddr_un        sun;
-
>     struct sockaddr_un    cifsun;
55,57c55,57
<     sun.sun_family = AF_UNIX;
<     strncpy(sun.sun_path, sname, sizeof(sun.sun_path) );
<     sun.sun_path[sizeof(sun.sun_path) - 1] = ‘\0′;
-
>     cifsun.sun_family = AF_UNIX;
>     strncpy(cifsun.sun_path, sname, sizeof(cifsun.sun_path) );
>     cifsun.sun_path[sizeof(cifsun.sun_path) - 1] = ‘\0′;
59c59
<     adrlen = sizeof(sun.sun_family) + strlen(sun.sun_path);
-
>     adrlen = sizeof(cifsun.sun_family) + strlen(cifsun.sun_path);
61c61
<     if (bind(s, (struct sockaddr *)&sun, adrlen))
-
>     if (bind(s, (struct sockaddr *)&cifsun, adrlen))
67c67
<     while ((c = accept(s, (struct sockaddr *)&sun, &adrlen)) >= 0) {
-
>     while ((c = accept(s, (struct sockaddr *)&cifsun, &adrlen)) >= 0) {

which helped with some symbol name clashes (at least, it did prior to build 143).

That left me with the binary blob linking problems. Fortunately, Canon actually supplies libraries which do the real work of the translation. Unfortunately (for me), those libraries all seemed to need linux’ libc.so.6, libdl.so.2 and libpthread.so.0. There are two specific symbols which they required: __errno_location(), and stderr. Now this I could do something about.

The link phase for the binary throws up these errors:

$   /opt/SUNWspro/SS12/bin/cc -O2 -L../../319/libs_bin -o cif bjferror.o bjfilter.o bjfimage.o bjfoption.o bjfpos.o bjfrcaccess.o getipc.o bjflist.o -lcnbpcmcm319 -lcnbpess319 -lm -ldl -ltiff -lpng -lcnbpcnclapi319 -lcnbpcnclbjcmd319 -lcnbpcnclui319 -lpopt
ld: warning: file libc.so.6: required by ../../319/libs_bin/libcnbpcmcm319.so, not found
ld: warning: file libpthread.so.0: required by ../../319/libs_bin/libcnbpess319.so, not found
ld: warning: file libdl.so.2: required by ../../319/libs_bin/libcnbpess319.so, not found
Undefined   first referenced
symbol      in file
bind        getipc.o
accept      getipc.o
listen      getipc.o
socket      getipc.o
stderr      ../../319/libs_bin/libcnbpcnclapi319.so
__errno_location ../../319/libs_bin/libcnbpcmcm319.so
ld: fatal: symbol referencing errors. No output written to cif
gmake: *** [cif] Error 1

Which is annoying at best. To get past this problem, I crafted up this little file:

#include <stdio.h>
int *
__errno_location(void) {
    return (int *)NULL;
}

#define stdin           (&_iob[0])
#define stdout          (&_iob[1])
#define stderr          (&_iob[2])

which I called fakery.c. I also created this mapfile to use when linking it:

$mapfile_version 2
SYMBOL_SCOPE {
    global:
        __errno_location;
        stderr;
};
SYMBOL_VERSION GLIBC_2.3.2 {} GLIBC_2.1.3;
SYMBOL_VERSION GLIBC_2.1.3 {} GLIBC_2.1;
SYMBOL_VERSION GLIBC_2.1 {} GLIBC_2.0 ;
SYMBOL_VERSION GLIBC_2.0 {} GLIBC_PRIVATE;
SYMBOL_VERSION GLIBC_PRIVATE { local: *;};

I then compiled fakery.c using

cc -G -Kpic -M MAPFILE.libc -R/opt/local/lib/canon -o libc.so.6 fakery.c

Lo and behold, I now had a libc.so.6:

$ file libc.so.6
libc.so.6:    ELF 32-bit LSB dynamic lib 80386 Version 1, dynamically linked, not stripped

with the requisite symbols available:

$ nm libc.so.6 |egrep "WEAK|GLOB"
[55]    |      1632|        28|FUNC |GLOB |0    |8      |__errno_location
[53]    |     67268|         0|OBJT |GLOB |0    |13     |_DYNAMIC
[46]    |     67532|         0|OBJT |GLOB |0    |16     |_edata
[50]    |     67536|         0|OBJT |GLOB |0    |17     |_end
[52]    |

      1720|         0|OBJT |GLOB |0    |11     |_etext
[51]    |     67256|         0|OBJT |GLOB |3    |12     |_GLOBAL_OFFSET_TABLE_
[54]    |         0|         0|OBJT |GLOB |0    |ABS    |_PROCEDURE_LINKAGE_TABLE_
[47]    |         0|         0|OBJT |WEAK |0    |ABS    |GLIBC_2.0
[48]    |         0|         0|OBJT |WEAK |0    |ABS    |GLIBC_2.1
[49]    |         0|         0|OBJT |WEAK |0    |ABS    |GLIBC_2.1.3
[43]    |         0|         0|OBJT |WEAK |0    |ABS    |GLIBC_2.3.2
[44]    |         0|         0|OBJT |GLOB |0    |ABS    |GLIBC_PRIVATE
[45]    |     67532|         4|OBJT |GLOB |0    |17     |stderr

Yay. Now for lib_dl.c and lib_pthread.c (the same file), and their mapfile:

$mapfile_version 2
SYMBOL_VERSION GLIBC_2.3.2 {} GLIBC_2.1.3;
SYMBOL_VERSION GLIBC_2.1.3 {} GLIBC_2.1;
SYMBOL_VERSION GLIBC_2.1 {} GLIBC_2.0 ;
SYMBOL_VERSION GLIBC_2.0 {} GLIBC_PRIVATE;
SYMBOL_VERSION GLIBC_PRIVATE { local: *;};

So I could do the same trick to generate libdl.so.2 and libpthread.so.0:

cc -G -Kpic -M MAPFILE.libs -R/opt/local/lib/canon -o libdl.so.2 otherlibs.c

Then I re-ran the cnijfilter link stage again, by hand:

$ /opt/SUNWspro/SS12/bin/cc -lnsl -lsocket -O2 -L../../319/libs_bin \
    -o cif bjferror.o bjfilter.o bjfimage.o bjfoption.o bjfpos.o \
    bjfrcaccess.o getipc.o bjflist.o -lcnbpcmcm319 -lcnbpess319 \
    -lm -ldl -ltiff -lpng -lcnbpcnclapi319 -lcnbpcnclbjcmd319 \
    -lcnbpcnclui319 -lpopt **-R/opt/local/lib/canon -L../../FAKE/**

Note the last bit – I’m adding an rpath (runtime path) so the binary will search for the libraries in /opt/local/lib/canon, and I added the directory for these fake libraries to the linker search path. Now the bits compile and link, and with an LD_LIBRARY_PATH entry in my environment I can see all the required libraries being found:

$ LD_LIBRARY_PATH=/opt/local/lib/canon ldd cif
    libnsl.so.1 =>     /lib/libnsl.so.1
    libsocket.so.1 =>     /lib/libsocket.so.1
    libcnbpcmcm319.so =>     /opt/local/lib/canon/libcnbpcmcm319.so
    libcnbpess319.so =>     /opt/local/lib/canon/libcnbpess319.so
    libm.so.2 =>     /lib/libm.so.2
    libdl.so.1 =>     /lib/libdl.so.1
    libtiff.so.3 =>     /usr/lib/libtiff.so.3
    libpng12.so.0 =>     /usr/lib/libpng12.so.0
    libcnbpcnclapi319.so =>     /opt/local/lib/canon/libcnbpcnclapi319.so
    libcnbpcnclbjcmd319.so =>     /opt/local/lib/canon/libcnbpcnclbjcmd319.so
    libcnbpcnclui319.so =>     /opt/local/lib/canon/libcnbpcnclui319.so
    libpopt.so.0 =>     /usr/lib/libpopt.so.0
    libc.so.1 =>     /lib/libc.so.1
    libmp.so.2 =>     /lib/libmp.so.2
    libmd.so.1 =>     /lib/libmd.so.1
    libc.so.6 =>     /opt/local/lib/canon/libc.so.6
    libdl.so.2 =>     /opt/local/lib/canon/libdl.so.2
    libpthread.so.0 =>     /opt/local/lib/canon/libpthread.so.0
    libjpeg.so.62 =>     /usr/lib/libjpeg.so.62
    libz.so.1 =>     /lib/libz.so.1

Still needed a wrapper script with it otherwise those binary blob libraries in /opt/local/lib/canon would fail to find libc.so.6:

#!/bin/bash
LD_LIBRARY_PATH=/opt/local/lib/canon exec /opt/local/bin/cifmp610.bin $*

But otherwise it seemed to be a reasonable start. It took a few reads of the Linker and Libraries Guide, and some back-n-forth help from the linker aliens but now I had a working binary translator.

Next step – cups-bjnp, but this actually turned out to be the bit which stopped me in my tracks.

I could hack the source ok to make it compile using getifaddrs:

558a559
> #ifndef __sun
562a564,569
> #else
>     if ((interface->ifa_addr == NULL) || (interface->ifa_broadaddr == NULL) ||
>         (interface->ifa_addr->ss_family != AF_INET) ||
>         (((struct sockaddr_in *) interface->ifa_addr)->sin_addr.s_addr ==
>         htonl(INADDR_LOOPBACK)))
> #endif

But it turns out that the Solaris behaviour is different to the linux behaviour – and I just do not have the time or energy to actually go and figure out how the packets are being misinterpreted. Yes, I snooped a bunch of the network traffic, but I’m stumped.

So I’m giving up on this printer, and Canon’s “open source” linux bits (did I mention they ship under GPLv2?). Until I can get my bones together to get an Epson (tx810fw is in the linuxprinting.org fully supported list, and Epson’s source for their translator is all open), I’ll run up my archlinux vbox and print using that instead.

I’m not going to buy another Canon printer, and I strongly suggest that if you want to have a printer which works under an open source operating system (using CUPS, of course) then you check out what is listed at www.linuxprinting.org/printers before making a purchase.


I’d like to thank Rod Evans, Ali Bahrami and Enrico Perla for all their help and patience with trying to get this figured out. With their help I’ve learnt quite a bit more about our linker capabilities, and the utilities (elfedit, elfdump, lari, crle and moe amongst others) which they’ve written to make it possible for me to turn code into something that runs.




And now for those who prefer unified diffs…

Grumble!

cnijfilter/src/getipc.c:

$ diff -U 3 getipc.c getipc.c.j
--- getipc.c        Tue May  8 18:45:16 2007
+++ getipc.c.j      Tue Jul 13 23:48:04 2010
@@ -41,7 +41,7 @@
short GetIPCData(LPIPCU pipc, char *sname)
{
-   struct sockaddr_un              sun;
+   struct sockaddr_un      cifsun;
int                                         s, c;
char                                        buf[128];
size_t                                      adrlen;
@@ -52,19 +52,19 @@
unlink(sname);
-   sun.sun_family = AF_UNIX;
-   strncpy(sun.sun_path, sname, sizeof(sun.sun_path) );
-   sun.sun_path[sizeof(sun.sun_path) - 1] = '\0';
+   cifsun.sun_family = AF_UNIX;
+   strncpy(cifsun.sun_path, sname, sizeof(cifsun.sun_path) );
+   cifsun.sun_path[sizeof(cifsun.sun_path) - 1] = '\0';
-   adrlen = sizeof(sun.sun_family) + strlen(sun.sun_path);
+   adrlen = sizeof(cifsun.sun_family) + strlen(cifsun.sun_path);
-   if (bind(s, (struct sockaddr *)&sun, adrlen))
+   if (bind(s, (struct sockaddr *)&cifsun, adrlen))
return RET_ERROR;
if (listen(s, 5))
return RET_ERROR;
-   while ((c = accept(s, (struct sockaddr *)&sun, &adrlen)) >= 0) {
+   while ((c = accept(s, (struct sockaddr *)&cifsun, &adrlen)) >= 0) {
/* read command first */
read(c, buf, IPCCMDLEN);

And cups-bjnp-0.5.4/bjnp-io.c:

$ diff -U 3 bjnp-io.c bjnp-io.c.j
--- bjnp-io.c       Sun May 17 05:40:57 2009
+++ bjnp-io.c.j     Wed Jul 21 22:34:52 2010
@@ -556,10 +556,17 @@
{
/* send broadcast packet to each suitable  interface */
+#ifndef __sun
if ((interface->ifa_addr == NULL) || (interface->ifa_broadaddr == NULL) ||
(interface->ifa_addr->sa_family != AF_INET) ||
(((struct sockaddr_in *) interface->ifa_addr)->sin_addr.s_addr ==
htonl(INADDR_LOOPBACK)))
+#else
+   if ((interface->ifa_addr == NULL) || (interface->ifa_broadaddr == NULL) ||
+       (interface->ifa_addr->ss_family != AF_INET) ||
+       (((struct sockaddr_in *) interface->ifa_addr)->sin_addr.s_addr ==
+       htonl(INADDR_LOOPBACK)))
+#endif
{
/* not an IPv4 capable interface */



We managed to survive today, just.

Gastro – you can keep it.

At 23:30 last night I suddenly lost both lunch and dinner – mostly into the bathroom sink. Not pleasant at all. Must have got some more sleep because at 0200 this morning, lost a bunch of fluid in the same manner. Ditto 0400.

Then at 0430 J started hurling too. C woke up at about 0615, so I went to change her and bring her in to bed with us. Good thing J reminded me to take the bucket with me, because the effort of getting C out of her cot and onto the changetable was more than enough to put me on the floor. Bile, this time.

C, poor thing, was scared witless and started screaming, and J managed to summon enough energy to finish putting the new nappy on.

Not what you’d call a pleasant night.

Managed to keep awake until about 1030, when J took over until 12. I woke up feeling quite groggy, but actually a bit better. Half a piece of toast was all either J or I could countenance, and C decided that the best way to eat her spaghetti (yay for leftovers and a microwave) was strand by strand. Fortunately our little darling then managed to sleep from about 12:30 until 3:45, so we all woke up feeling a bit better.

I wish I hadn’t burnt the vegie soup I was making for dinner, though – I was really looking forward to it.

With any luck we’ll all be feeling a lot better tomorrow.




Two in, one in the deep freeze

Of Tuesday’s 5 embryos, 2 were looking good enough first thing this morning to install. One was looking good enough to put in the freezer, and the other two just weren’t.

J’s zonked out courtesy of Warren’s transfer protocol (2 valium before, 2 after… usually with a snifter of brandy), and C is at daycare. Now we’re in the two week wait period. Ugh.




Amazing – HAL actually useful for something

The battery in my laptop (Dell XPS M1530) died a week or so ago, and yesterday I finally organised to get a new one, which arrived this morning. Yay ebay and AusPost or something.

Since the laptop lives downstairs, I figured it would be nice to see if I could check on the status from upstairs, as needed. In the distant past Casper had some ACPI-related utils which worked quite nicely on AMD systems, but they’ve been deprecated for quite some time now. A quick q into an internal channel put me onto the /usr/sbin/lshal utility, which lets me run this command:

/usr/sbin/lshal -l -u '/org/freedesktop/Hal/devices/pseudo/acpi_drv_0_battery0_0'

And produces output like this:

udi = ‘/org/freedesktop/Hal/devices/pseudo/acpi_drv_0_battery0_0′
  battery.reporting_last_full = 5943  (0×1737)  (int)
  battery.remaining_time = 0  (0×0)  (int)
  battery.charge_level.percentage = 100  (0×64)  (int)
  battery.charge_level.current = 65967  (0x101af)  (int)
  battery.charge_level.last_full = 65967  (0x101af)  (int)
  battery.charge_level.rate = 0  (0×0)  (int)
  battery.voltage.present = 12543  (0x30ff)  (int)
  battery.reporting.rate = 355  (0×163)  (int)
  battery.reporting.current = 6600  (0x19c8)  (int)
  battery.rechargeable.is_discharging = false  (bool)
  battery.rechargeable.is_charging = false  (bool)
  battery.is_rechargeable = true  (bool)
  battery.charge_level.granularity_2 = 732  (0x2dc)  (int)
  battery.charge_level.granularity_1 = 732  (0x2dc)  (int)
  battery.charge_level.low = 2220  (0x8ac)  (int)
  battery.charge_level.warning = 7326  (0x1c9e)  (int)
  battery.charge_level.design = 73260  (0x11e2c)  (int)
  battery.charge_level.unit = ‘mWh’  (string)
  battery.voltage.design = 11100  (0x2b5c)  (int)
  battery.reporting.granularity_2 = 66  (0×42)  (int)
  battery.reporting.granularity_1 = 66  (0×42)  (int)
  battery.reporting.low = 200  (0xc8)  (int)
  battery.reporting.warning = 660  (0×294)  (int)
  battery.reporting.design = 6600  (0x19c8)  (int)
  battery.reporting.last_full = 5943  (0×1737)  (int)
  battery.reporting.unit = ‘mAh’  (string)
  battery.model = ‘DELL 00′  (string)
  battery.serial = ’3213′  (string)
  battery.technology = ‘lithium-ion’  (string)
  battery.reporting.technology = ‘LION’  (string)
  battery.vendor = ”  (string)
  info.capabilities = {‘battery’} (string list)
  battery.type = ‘primary’  (string)
  battery.present = true  (bool)
  info.category = ‘battery’  (string)
  info.solaris.driver = ‘acpi_drv’  (string)
  solaris.devfs_path = ‘/pseudo/acpi_drv@0:battery0′  (string)
  info.product = ‘Battery Bay’  (string)
  info.udi = ‘/org/freedesktop/Hal/devices/pseudo/acpi_drv_0_battery0_0′  (string)
  info.parent = ‘/org/freedesktop/Hal/devices/pseudo/acpi_drv_0_0′  (string)

Finally – a non-annoying thing to do with HAL!




ET tomorrow

Embryo transfer tomorrow, 0715 for an 0730 insertion. J’s fertility specialist has an interesting post-transfer protocol – 2 valium and a shot of brandy. First time we went through a transfer with him, this post-transfer protocol rendered her pretty much unconscious for the next 4 hours.

The last time we did an embryo transfer (earlier this year) we were in a rather different state due to the 100% attrition on the embryos, so it didn’t really make much difference. We think we’ve got 5 ready to push in tomorrow, but we won’t know for sure until we get in there and she has her feet in the stirrups.

What I do know is that with C at daycare tomorrow and Friday, it’ll be an ideal opportunity for J to actually have a decent uninterrupted afternoon nap – which she badly needs. Then it’s the two week wait. Hmmmph!




It’s 60% time

At the start of the year I set a goal of riding 70km per week, every week. I also wanted to lose 14-15kg from my 1/Jan fatbastard weight of 89kg. So far I’m about 60% for both: with sportstracker and my new garmin edge 705 unit I’ve now tracked approx 1500km (2500km would have been on target), and I’m down to 80-81kg. So it’s generally good, but I could be doing better.Having last week mostly sick didn’t help on the distance front either – gastro+flu virus recovery recommendation from my doc is “don’t do anything strenuous for the next 10 days – or I’ll be seeing you again too soon!”

I can’t afford the time off ill, and I’d really like to get back in the saddle sooner rather than later, so it doesn’t seem unreasonable to do it with at least half a brain cell.




Most of an alphabet

I recall listening to, I think, Flanders and Swann, declaiming a new phonetic alphabet. It goes something like this:

Update 22 July: Courtesy of Andre, I’ve now got A, Q and W sorted

A for 'orses

B for mutton

C forth Islanders

D for dumb

E for brick

F for vescent

G for police

H for respect

I for novello

J for oranges

K for ahnsis

L for leather

M for sis

N for

O for the garden wall

P for a penny

Q for a bus

R for mo

S for you (*ess for you too!)

T for two

U for films

V for La France

W or quits

X for breakfast

Y for Pete’s sake

Z for breezes

Now I can’t quite recall Q, but I do remember that the non-reciting member of the duo interrupted with “no no, W for a bob” – and so I’m stuck for Q. If you know it (or who performed it if not F+S), please leave a comment below.




Outer chainring, w00t!

Went for a spin with Elissa this morning, on a very nicely fixed road bike. Got into the outer chainring with ease, even though the shifter seems to be a 3speed jobbie, and cruised along quite nicely. We stopped for a coffee at Josh’s place, Cup on Russell St in West End. His roaster arrived during the week, so I think now he’s all set up.

Had an espresso of his current Nicaraguan roast (he does single-origin) – seemed a lot heavier in texture to the Guatamalan I had last weekend. But it was still a very, very nice coffee – and nicer with some milk in it. I think I’m going to have to get a bag or two from him in the future, because (a) it’s good stuff, and (b) a roaster who gets it right (like Josh does) most definitely deserves my custom!

Once Elissa’s settled into her new job (on Montague St) she’s planning to ride to and from 3 or perhaps 4 days a week – and I’m hoping to go along for the rides with her. A 40ish km round trip 3 days a week will be a very nice thing to have under my belt, especially if I can grab a coffee at Josh’s place once a week as a treat. So, all in all a successful Saturday. I’m looking forward to more like it.