Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4db666cc authored by Mike Isely's avatar Mike Isely Committed by Mauro Carvalho Chehab
Browse files

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: default avatarMike Isely <isely@pobox.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 401c27ce
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -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;
+1 −0
Original line number Diff line number Diff line
@@ -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;
+125 −25
Original line number Diff line number Diff line
@@ -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,12 +2697,16 @@ 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. */
			if (hdw->fw_cpu_flag) {
				/* Now release the CPU.  It will disconnect
				   and reconnect later. */
				pvr2_hdw_cpureset_assert(hdw,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;
@@ -2640,16 +2722,34 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
			/* download the firmware from address 0000-1fff in 2048
			   (=0x800) bytes chunk. */

		pvr2_trace(PVR2_TRACE_FIRMWARE,"Grabbing CPU firmware");
			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,
			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);
						      hdw->fw_buffer+address,
						      0x800,HZ);
				if (ret < 0) break;
			}

		pvr2_trace(PVR2_TRACE_FIRMWARE,"Done grabbing CPU firmware");
			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");
		}

	} while (0); LOCK_GIVE(hdw->big_lock);
}
+7 −5
Original line number Diff line number Diff line
@@ -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 *);