V4L/DVB (6208): pvrusb2: Implement programmatic means to extract prom contents

The pvrusb2 driver already has a method for extracting the FX2's
program memory back out to a user application; this ability is used to
facilitate manual firmware extraction as per the procedure documented
on the pvrusb2 web site.  This change follows that pattern and
implements a corresponding method to grab the binary contents of the
PVR USB2 prom (which for PVR USB2 devices can contain information in
addition to the usual Hauppauge metadata).

Signed-off-by: Mike Isely <isely@pobox.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Mike Isely 2007-09-08 22:16:27 -03:00 committed by Mauro Carvalho Chehab
parent 401c27ce96
commit 4db666cc3d
4 changed files with 150 additions and 35 deletions

View File

@ -397,10 +397,22 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
count -= scnt; buf += scnt;
if (!wptr) return -EINVAL;
if (debugifc_match_keyword(wptr,wlen,"fetch")) {
pvr2_hdw_cpufw_set_enabled(hdw,!0);
scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
if (scnt && wptr) {
count -= scnt; buf += scnt;
if (debugifc_match_keyword(wptr,wlen,"prom")) {
pvr2_hdw_cpufw_set_enabled(hdw,!0,!0);
} else if (debugifc_match_keyword(wptr,wlen,
"ram")) {
pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
} else {
return -EINVAL;
}
}
pvr2_hdw_cpufw_set_enabled(hdw,0,!0);
return 0;
} else if (debugifc_match_keyword(wptr,wlen,"done")) {
pvr2_hdw_cpufw_set_enabled(hdw,0);
pvr2_hdw_cpufw_set_enabled(hdw,0,0);
return 0;
} else {
return -EINVAL;

View File

@ -238,6 +238,7 @@ struct pvr2_hdw {
// CPU firmware info (used to help find / save firmware data)
char *fw_buffer;
unsigned int fw_size;
int fw_cpu_flag; /* True if we are dealing with the CPU */
// Which subsystem pieces have been enabled / configured
unsigned long subsys_enabled_mask;

View File

@ -2605,7 +2605,85 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
} while (0); LOCK_GIVE(hdw->big_lock);
}
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
/* Grab EEPROM contents, needed for direct method. */
#define EEPROM_SIZE 8192
#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw)
{
struct i2c_msg msg[2];
u8 *eeprom;
u8 iadd[2];
u8 addr;
u16 eepromSize;
unsigned int offs;
int ret;
int mode16 = 0;
unsigned pcnt,tcnt;
eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
if (!eeprom) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"Failed to allocate memory"
" required to read eeprom");
return NULL;
}
trace_eeprom("Value for eeprom addr from controller was 0x%x",
hdw->eeprom_addr);
addr = hdw->eeprom_addr;
/* Seems that if the high bit is set, then the *real* eeprom
address is shifted right now bit position (noticed this in
newer PVR USB2 hardware) */
if (addr & 0x80) addr >>= 1;
/* FX2 documentation states that a 16bit-addressed eeprom is
expected if the I2C address is an odd number (yeah, this is
strange but it's what they do) */
mode16 = (addr & 1);
eepromSize = (mode16 ? EEPROM_SIZE : 256);
trace_eeprom("Examining %d byte eeprom at location 0x%x"
" using %d bit addressing",eepromSize,addr,
mode16 ? 16 : 8);
msg[0].addr = addr;
msg[0].flags = 0;
msg[0].len = mode16 ? 2 : 1;
msg[0].buf = iadd;
msg[1].addr = addr;
msg[1].flags = I2C_M_RD;
/* We have to do the actual eeprom data fetch ourselves, because
(1) we're only fetching part of the eeprom, and (2) if we were
getting the whole thing our I2C driver can't grab it in one
pass - which is what tveeprom is otherwise going to attempt */
memset(eeprom,0,EEPROM_SIZE);
for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
pcnt = 16;
if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
offs = tcnt + (eepromSize - EEPROM_SIZE);
if (mode16) {
iadd[0] = offs >> 8;
iadd[1] = offs;
} else {
iadd[0] = offs;
}
msg[1].len = pcnt;
msg[1].buf = eeprom+tcnt;
if ((ret = i2c_transfer(&hdw->i2c_adap,
msg,ARRAY_SIZE(msg))) != 2) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"eeprom fetch set offs err=%d",ret);
kfree(eeprom);
return NULL;
}
}
return eeprom;
}
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw,
int prom_flag,
int enable_flag)
{
int ret;
u16 address;
@ -2619,38 +2697,60 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
kfree(hdw->fw_buffer);
hdw->fw_buffer = NULL;
hdw->fw_size = 0;
/* Now release the CPU. It will disconnect and
reconnect later. */
pvr2_hdw_cpureset_assert(hdw,0);
if (hdw->fw_cpu_flag) {
/* Now release the CPU. It will disconnect
and reconnect later. */
pvr2_hdw_cpureset_assert(hdw,0);
}
break;
}
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Preparing to suck out CPU firmware");
hdw->fw_size = 0x2000;
hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
if (!hdw->fw_buffer) {
hdw->fw_size = 0;
break;
hdw->fw_cpu_flag = (prom_flag == 0);
if (hdw->fw_cpu_flag) {
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Preparing to suck out CPU firmware");
hdw->fw_size = 0x2000;
hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL);
if (!hdw->fw_buffer) {
hdw->fw_size = 0;
break;
}
/* We have to hold the CPU during firmware upload. */
pvr2_hdw_cpureset_assert(hdw,1);
/* download the firmware from address 0000-1fff in 2048
(=0x800) bytes chunk. */
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Grabbing CPU firmware");
pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
for(address = 0; address < hdw->fw_size;
address += 0x800) {
ret = usb_control_msg(hdw->usb_dev,pipe,
0xa0,0xc0,
address,0,
hdw->fw_buffer+address,
0x800,HZ);
if (ret < 0) break;
}
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Done grabbing CPU firmware");
} else {
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Sucking down EEPROM contents");
hdw->fw_buffer = pvr2_full_eeprom_fetch(hdw);
if (!hdw->fw_buffer) {
pvr2_trace(PVR2_TRACE_FIRMWARE,
"EEPROM content suck failed.");
break;
}
hdw->fw_size = EEPROM_SIZE;
pvr2_trace(PVR2_TRACE_FIRMWARE,
"Done sucking down EEPROM contents");
}
/* We have to hold the CPU during firmware upload. */
pvr2_hdw_cpureset_assert(hdw,1);
/* download the firmware from address 0000-1fff in 2048
(=0x800) bytes chunk. */
pvr2_trace(PVR2_TRACE_FIRMWARE,"Grabbing CPU firmware");
pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
for(address = 0; address < hdw->fw_size; address += 0x800) {
ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0xc0,
address,0,
hdw->fw_buffer+address,0x800,HZ);
if (ret < 0) break;
}
pvr2_trace(PVR2_TRACE_FIRMWARE,"Done grabbing CPU firmware");
} while (0); LOCK_GIVE(hdw->big_lock);
}

View File

@ -197,11 +197,13 @@ void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
/* Enable / disable retrieval of CPU firmware. This must be enabled before
pvr2_hdw_cpufw_get() will function. Note that doing this may prevent
the device from running (and leaving this mode may imply a device
reset). */
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, int enable_flag);
/* Enable / disable retrieval of CPU firmware or prom contents. This must
be enabled before pvr2_hdw_cpufw_get() will function. Note that doing
this may prevent the device from running (and leaving this mode may
imply a device reset). */
void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *,
int prom_flag,
int enable_flag);
/* Return true if we're in a mode for retrieval CPU firmware */
int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *);