This is something I wanted to do for a while. I claimed this printer when it stopped working and upon opening it realizing the thing was flooded with ink and beyond economical repair. However the motherboard and scanner module was okay.
The victim in question. A Lexmark Pro 805 printer, the last picture of it before I cracked the thing open.
I chose it for a few reasons:
1. It was firmware upgradable
2. The firmware was easy to open and disassemble
3. The cpu architecture was ARM
4. If I could repurpose it, its basically a free computer.
5. I am a beginner to hardware.
You can download the firmware upgrade from
here, they do not have any protection or anything on it so you can just dump it into a disassembler.
Well I started like anybody would on the motherboard. You can see in this image I have it almost fully working (asking for the freaking "duplex module") outside of its case.
I do not have an oscilloscope, but I wanted to probe around for a serial port. My solution to this was to wire up a speaker and use it as a probe while the thing boots up. Theory behind it would be if I hear varying tones I could be looking at a serial data pad. And wouldn't you know it after a bit of probing around near a port that was labeled "JTAG" (no JTAG hardware folks, sorry) I found some pads near a chip "25l6405dmi-12G". After asking around on the help forum earlier this week (
here) I found it was a chip with an SPI bus on it.
Datasheet can be found hereI hooked up my logic analyzer and took a look at what was going on.
Yeah I had no clue.
When I started I did not know what SPI was. A bit of Google magic and a few read articles and I sort of knew what to do so I just went for it. I have two microcontroller boards, MSP430 and an Arduino. The Arduino was my choice because I found
this. I went through the code and the datasheet and started typing away some code to attempt to do something simple, just read the manufacturing id.
After some tinkering, accidentally causing the printer to fail to boot (just a short, no worries) I had something coming out of the serial out on the Arduino serial monitor.
40 00 03Well.... That is not what the datasheet says. Okay well maybe I just have a slightly different chip. Lets just go for dumping a sector of the chips memory and see what I get.
Well crap, every dump is different. And dumping is sooooo sloooow. Lets come back at this later....
*one day later*
I thought I should check to see if the printer still boots. I unwired everything and it boots fine, I didn't bust the chip. So I rewire it and I open the serial monitor to find this
C2 20 17Well what the hell its working? I did one of two things, either when I shorted the chip I caused it to go into some mode where I could not read from it, or my wiring was dodgy and when I rewired the thing I fixed whatever what was wrong. I was ready to write my own SPI library too. So lets try to make a dump of the chip.....
Ladies and gentlemen I give you the printers bootloader. 4096 Bytes of it at least, like I said it takes forever to dump so I did a small portion. Even better, I did a few dumps and it is consistent!
Well where am I going from here?
I have some reverse engineering to do. I want to bypass the message that tells me to insert the "duplex unit" first off, the printer wont let me use it until I do that. I had tried to block the sensors that detect it but I had forgotten to take note of which was the right one so finding it has been a pain. If all goes well I could either just continue to use it just as a scanner or I could repurpose the board to do something fun with the motors. Its got a fully working wifi and bluetooth module in it so we will see what I can do provided I don't brick the thing.
First things first though, I need to figure out how to speed up the dumping process because its an 8mb chip and I became impatient after a 3 hour dump try.Overall this has been a pretty neat experience so far.
Here is a bit of code for you guys too.
// MX25L6405 SPI
// Datasheet http://www.macronix.com/QuickPlace/hq/PageLibrary4825740B00298A3B.nsf/$defaultview/3F21BAC2E121E17848257639003A3146/$File/MX25L6405,%203V,%2064Mb,%20v1.3.pdf?OpenElement
// Some code from http://arduino.cc/en/Tutorial/SPIEEPROM
// Pin Definitions
#define DATAOUT 11 // MOSI
#define DATAIN 12 // MISO
#define SPICLOCK 13 // sck
#define SLAVESELECT 10 // ss
// Command Definitions
#define WREN 0x06 // Sets the (WEL) write enable latch bit
#define WRDI 0x04 // Reset the (WEL) write enable latch bit
#define RDID 0x9F // Output the manufacturer ID and 2-byte device ID (outputs 3 bytes)
#define RDSR 0x05 // To read out the status register (outputs 1 byte)
#define WRSR 0x01 // To write new values to the status register
#define READ 0x03 // N bytes read out until CS# goes high (requires input address of 3 bytes) (outputs ? bytes)
#define FAST_READ 0x0B // (requires input address of 3 bytes, optional fourth?) (outputs ? bytes)
#define PARALLEL_MODE 0x55 // Enter and stay in parallel mode until power off
#define SE 0x20 // Sector erase (input 3 bytes address)
#define SE_ALT 0xD8
#define CE 0x60 // Chip erase
#define CE_ALT 0xC7
#define PP 0x02 // Page Program (input 3 byte address)
#define DP 0xB9 // Deep Power Down
#define EN4K 0xB5 // Enter 4Kb sector
#define EX4K 0xB5 // Exit 4Kb sector
#define RDP 0xAB // Release from deep power down
#define RES 0xAB // Read electronic id (optional 3 byte input?)
#define REMS 0x90 // Read electronic manufacturer & device id (2 byte optional? third byte if 0 will
// output manufacturer id first, 1 will output device id first)
char fmt[16]; // Some place to sprintf into
byte eeprom_output_data = 0;
byte eeprom_input_data = 0;
byte clr = 0;
byte spi_transfer(volatile byte data)
{
SPDR = data; // Start the transmission
while (!(SPSR & (1 << SPIF))) // Wait the end of the transmission
{
};
return SPDR; // return the received byte
}
void setup()
{
Serial.begin(9600);
pinMode(DATAOUT , OUTPUT);
pinMode(DATAIN , INPUT);
pinMode(SPICLOCK , OUTPUT);
pinMode(SLAVESELECT , OUTPUT);
// Data sheet says this must be high
digitalWrite(SLAVESELECT, HIGH);
// SPCR = 01010000
//interrupt disabled, spi enabled, msb 1st, master, clk low when idle,
//sample on leading edge of clk, system clock/4 rate (fastest)
SPCR = (1 << SPE)|(1 << MSTR);
clr = SPSR;
clr = SPDR;
delay(1000);
}
void ReadID() {
digitalWrite(SLAVESELECT, LOW);
spi_transfer(RDID);
int b1 = spi_transfer(0xFF);
int b2 = spi_transfer(0xFF);
int b3 = spi_transfer(0xFF);
digitalWrite(SLAVESELECT, HIGH);
sprintf(fmt, "%02X %02X %02X\n", b1, b2, b3);
Serial.print(fmt);
}
void ReadElectronicId() {
digitalWrite(SLAVESELECT, LOW);
spi_transfer(RES);
spi_transfer(0x00);
spi_transfer(0x00);
spi_transfer(0x00);
int b1 = spi_transfer(0xFF);
digitalWrite(SLAVESELECT, HIGH);
sprintf(fmt, "%02X\n", b1);
Serial.print(fmt);
}
void ReadManufactureId() {
digitalWrite(SLAVESELECT, LOW);
spi_transfer(REMS);
spi_transfer(0x00);
spi_transfer(0x00);
spi_transfer(0x00);
int b1 = spi_transfer(0xFF);
int b2 = spi_transfer(0xFF);
int b3 = spi_transfer(0xFF);
digitalWrite(SLAVESELECT, HIGH);
sprintf(fmt, "%02X %02X %02X\n", b1, b2, b3);
Serial.print(fmt);
}
void ReadStatusReg() {
digitalWrite(SLAVESELECT, LOW);
spi_transfer(RDSR);
int b1 = spi_transfer(0xFF);
digitalWrite(SLAVESELECT, HIGH);
sprintf(fmt, "%02X\n", b1);
Serial.print(fmt);
}
void Dump() {
int data;
digitalWrite(SLAVESELECT, LOW);
spi_transfer(READ); // Address auto increments so only need to do a read instruction once
// Address 0x000000
spi_transfer(0x00);
spi_transfer(0x00);
spi_transfer(0x00);
for (int i = 0, x = 0; i < 4096; i++, x++) {
if (x >= 16) {
Serial.print('\n');
x = 0;
}
sprintf(fmt, "%02X ", spi_transfer(0xFF));
Serial.print(fmt);
}
digitalWrite(SLAVESELECT, HIGH);
}
void loop()
{
ReadID(); // RDID
//ReadElectronicId(); // RES
//ReadManufactureId(); // REMS
//Dump();
while (1) {
delay(1000);
}
}