dvd.rw-tools/rpl8.cpp

351 lines
9.1 KiB
C++
Raw Blame History

#if 0
#
# The code is in public domain, May-September 2002.
#
# RICOH Program Loader 8 (primarily DVD+RW units) for Linux 2.4 and
# upwards. So far has been tested with:
# - HP dvd100i
# - Ricoh MP5120A
# - Philips DVDRW208
# - HP dvd200i
#
# This is second version adding support for Program Loader 9 and
# firmware consistency check.
#
# The program never updates Program Loader code so that you should
# always be able to downgrade your firmware in case something goes
# wrong.
#
# - make sure you have no media in drive, optionally go to single user
# mode;
# - run 'rpl8 /dev/cdrom firmware.image.bin';
# - observe the program backing up your current firmware and uploading
# new one;
# - take system down and cycle the power;
#
# There is -dump-only option which only backs up firmware. Note that
# you might have to cycle the power even after -dump-only.
#
# "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted"
# surrounded by other nasty messages logged on console is "normal."
#
/bin/sh << EOS
MODNAME=\`expr "/$0" : '\(.*[^/]\)/*$' : '.*/\(..*\)' : '\(.*\)\..*$'\`
case "`uname -s`" in
SunOS) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0" -lvolmgt) ;;
*) (set -x; g++ -fno-exceptions -O -o \$MODNAME "$0") ;;
esac
EOS
exit
#endif
#if defined(__linux)
#define _XOPEN_SOURCE 500
#endif
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <poll.h>
#include <signal.h>
#include "transport.hxx"
#define BUFFER_BLOCK (16*1024)
#define FIRMWARE_SIZE (1024*1024)
#define MAGIC_OFFSET (32*1024)
#if FIRMWARE_SIZE%BUFFER_BLOCK!=0
#error "Invalid BUFFER_BLOCK"
#endif
#if MAGIC_OFFSET%BUFFER_BLOCK!=0
#error "Invalid BUFFER_BLOCK"
#endif
int main (int argc,char *argv[])
{ int fd,in,out,off,j;
struct stat sb;
char dump[64],*meaning;
unsigned char *buffer,*firmware,sigfile[6],sigunit[6],ascq='\xFF';
struct {
unsigned char type,skip[7];
char vendor[8],ident[16],revision[4];
} inq;
Scsi_Command cmd;
unsigned char *sense=cmd.sense();
if (argc<3)
{ fprintf (stderr,"usage: %s /dev/cdrom "
"[-[dump-only]|firmware.bin]\n",argv[0]);
return 1;
}
if ((fd=open ("/dev/zero",O_RDWR)) < 0)
{ fprintf (stderr,"unable to open(\"/dev/zero\"): "), perror(NULL);
return 1;
}
buffer = (unsigned char *)mmap(NULL,BUFFER_BLOCK,PROT_READ|PROT_WRITE,
MAP_PRIVATE,fd,0);
if (buffer==MAP_FAILED)
{ fprintf (stderr,"unable to anonymously mmap %d bytes: ",BUFFER_BLOCK),
perror (NULL);
return 1;
}
close (fd);
if (!cmd.associate(argv[1]))
{ fprintf (stderr,"%s: unable to open: ",argv[1]), perror (NULL);
return 1;
}
cmd[0]=0x12; // INQUIRY
cmd[4]=sizeof(inq);
cmd[5]=0;
if (cmd.transport(READ,(unsigned char *)&inq,sizeof(inq)))
{ perror("unable to INQUIRY, examine /var/log/message");
return 1;
}
if ((inq.type&0x1F) != 5)
{ fprintf (stderr,"\"%s\" is not a CD-ROM device\n",argv[1]);
return 1;
}
cmd[0]=0x00; // TEST UNIT READY
cmd[5]=0x80;
if (!cmd.transport())
{ fprintf (stderr,"\"%s\" is ready, is media present?\n",argv[1]);
return 1;
}
if (sense[12] != 0x3A) // should reply with "NO MEDIA"
{ fprintf (stderr,"\"%s\": unexpected ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
return 1;
}
sprintf (dump,"%.8s%.16s%.4s.bin",inq.vendor,inq.ident,inq.revision);
for(j=0;dump[j];j++) if (dump[j]==' ') dump[j]='_';
in=-1, firmware=NULL;
if (argv[2][0] != '-')
{ unsigned short cksum;
int i;
if ((in=open(argv[2],O_RDONLY)) < 0)
{ fprintf (stderr,"unable to open(\"%s\"): ",argv[2]), perror (NULL);
return 1;
}
if (fstat(in,&sb) < 0)
{ fprintf (stderr,"unable to stat(\"%s\"): ",argv[2]), perror (NULL);
return 1;
}
if (sb.st_size != FIRMWARE_SIZE)
{ fprintf (stderr,"\"%s\": size is insane\n",argv[2]);
return 1;
}
firmware=(unsigned char *)mmap(NULL,FIRMWARE_SIZE,PROT_READ,
MAP_PRIVATE,in,0);
if (firmware==MAP_FAILED)
{ fprintf (stderr,"unable to mmap(\"%s\"): ",argv[2]), perror (NULL);
return 1;
}
close (in);
memcpy(sigfile,firmware+MAGIC_OFFSET+4,6);
if (memcmp(sigfile,"MMP5",4))
{ fprintf (stderr,"\"%s\": signature is insane\n",argv[2]);
return 1;
}
for (cksum=0,i=MAGIC_OFFSET;i<FIRMWARE_SIZE-2;i++)
cksum += firmware[i];
if ((cksum&0xFF) != firmware[FIRMWARE_SIZE-1] ||
(cksum>>8) != firmware[FIRMWARE_SIZE-2]
)
{ fprintf (stderr,"\"%s\" is corrupted or encrypted\n",argv[2]);
return 1;
}
}
if ((out=open(dump,O_WRONLY|O_CREAT|O_EXCL,0666)) < 0)
{ fprintf (stderr,"unable to creat(\"%s\"): ",dump), perror (NULL);
return 1;
}
cmd[0]=0xE4; // INVOKE PROGRAM LOADER
cmd[9]=(1<<6)&0x40;
if (cmd.transport())
{ fprintf (stderr,"\"%s\": unable to invoke RICOH Program Loader "
"[ASC=%02xh/ASCQ=%02xh]\n",
argv[1],sense[12],sense[13]);
return 1;
}
do { // not really a loop, just a way to get rid of goto's...
poll(NULL,0,250);
cmd[0]=0xE4; // INVOKE PROGRAM LOADER
cmd[9]=(1<<6)&0x40;
if (cmd.transport())
{ fprintf (stderr,"\"%s\"[E4]: unexpected ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
break;
}
poll(NULL,0,250);
cmd[0]=0x12; // INQUIRY
cmd[4]=sizeof(inq);
cmd[5]=0;
if (cmd.transport(READ,(unsigned char *)&inq,sizeof(inq)))
{ perror("unable to INQUIRY, examine /var/log/message");
break;
}
if (memcmp(&inq.vendor,"RICOH Program Loader 8",24) &&
memcmp(&inq.vendor,"RICOH Program Loader 9",24) )
{ fprintf (stderr,"\"%s\": unknown Program Loader "
"%.8s|%.16s|%.4s\n",
argv[1],inq.vendor,inq.ident,inq.revision);
break;
}
fprintf (stderr,"\"%s\": dumping firmware image to \"%s\" ",
argv[1],dump);
for (off=0;off<FIRMWARE_SIZE;off+=BUFFER_BLOCK)
{ cmd[0]=0xE3; // DOWNLOAD FIRMWARE PAGE
cmd[2]=(off>>24)&0xFF;
cmd[3]=(off>>16)&0xFF;
cmd[4]=(off>>8)&0xFF;
cmd[5]=(off)&0xFF;
cmd[7]=(BUFFER_BLOCK>>8)&0xFF;
cmd[8]=(BUFFER_BLOCK)&0xFF;
cmd[9]=0;
if (cmd.transport(READ,buffer,BUFFER_BLOCK))
{ fprintf (stderr,"\"%s\"[E3]: unexpected "
"ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
in=-1;
break;
}
if (write(out,buffer,BUFFER_BLOCK) != BUFFER_BLOCK)
{ fprintf (stderr,"unable to write(\"%s\"): ",dump),
perror(NULL);
in=-1;
break;
}
fprintf (stderr,".");
if (off==MAGIC_OFFSET) { memcpy(sigunit,buffer+4,6); }
}
fprintf (stderr,"\n");
if (in==-1) break; /* dump only or we failed to backup */
#if 1
if (memcmp(sigunit,sigfile,6))
{ fprintf (stderr,"\"%s\": firmware signature mismatch "
"[%.6s|%.6s]\n",
argv[1],sigunit,sigfile);
break;
}
#endif
fprintf (stderr,"\"%s\": uploading firmware image",argv[1]);
for (off=0;off<FIRMWARE_SIZE;off+=BUFFER_BLOCK)
{ cmd[0]=0xE2; // UPLOAD FIRMWARE PAGE
cmd[5]=(BUFFER_BLOCK>>16)&0xFF;
cmd[6]=(BUFFER_BLOCK>>8)&0xFF;
cmd[7]=(BUFFER_BLOCK)&0xFF;
cmd[11]=0;
if (cmd.transport(WRITE,firmware+off,BUFFER_BLOCK))
{ fprintf (stderr,"\"%s\"[E2]: unexpected "
"ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
in=-1;
break;
}
fprintf (stderr,".");
}
fprintf (stderr,"\n");
if (in==-1) break; /* failed to upload */
fprintf (stderr,"\"%s\": unlocking firmware\n",argv[1]);
cmd[0]=0xE0; // UNLOCK FIRMWARE
cmd[3]=0;
cmd[9]=0;
if (cmd.transport())
{ fprintf (stderr,"\"%s\"[E0]: unexpected "
"ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
break;
}
fprintf (stderr,"\"%s\": <20>POINT OF NO RETURN!\n",argv[1]);
poll(NULL,0,3000);
signal (SIGINT,SIG_IGN);
signal (SIGTERM,SIG_IGN);
cmd[0]=0xE1; // FIRE IT UP
// -----v----- don't update the Program Loader code
cmd[9]=(0<<6)&0x40;
if (cmd.transport())
{ fprintf (stderr,"\"%s\"[E1]: unexpected "
"ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
break;
}
while(1)
{ poll(NULL,0,250);
cmd[0]=0x00; // TEST UNIT READY
cmd[5]=0x80;
if (!cmd.transport()) continue; // never happens?
if (sense[12] == 0xF7)
{ if (ascq != sense[13])
{ switch (ascq=sense[13])
{ case 0: meaning="preparing"; break;
case 1: meaning="erasing contents"; break;
case 2: meaning="writing contents"; break;
case 3: meaning="verifying contents"; break;
default:meaning="unknown meaning"; break;
}
fprintf (stderr,"\"%s\": %s\n",argv[1],meaning);
}
}
else if (sense[12] == 0x3A) /* no media */
{ fprintf (stderr,"\"%s\": firmware upgrade succeeded:-)\n",
argv[1]),
fprintf (stderr,"\"%s\": "
"<EFBFBD>CYCLE THE POWER BEFORE PROCEEDING!\n",
argv[1]);
break;
}
else
{ fprintf (stderr,"\"%s\"[0]: unexpected "
"ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
in=-1;
break;
}
}
} while (0);
cmd[0]=0xE4; // TERMINATE PROGRAM LOADER
cmd[9]=(0<<6)&0x40;
if (cmd.transport())
{ fprintf (stderr,"\"%s\": unexpected ASC=%02xh/ASCQ=%02xh\n",
argv[1],sense[12],sense[13]);
return 1;
}
return in<0;
}