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

Commit 07cefe7a authored by David Kilroy's avatar David Kilroy Committed by John W. Linville
Browse files

orinoco_usb: implement fw download



This involves some refactorring of the common fw download code to
substitute ezusb versions of various functions.

Note that WPA-enabled firmwares (9.xx series) will not work fully with
orinoco_usb yet.

Signed-off-by: default avatarDavid Kilroy <kilroyd@googlemail.com>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent fc97431a
Loading
Loading
Loading
Loading
+4 −4
Original line number Original line Diff line number Diff line
@@ -121,7 +121,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
	dev_dbg(dev, "Attempting to download firmware %s\n", firmware);
	dev_dbg(dev, "Attempting to download firmware %s\n", firmware);


	/* Read current plug data */
	/* Read current plug data */
	err = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 0);
	err = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
	dev_dbg(dev, "Read PDA returned %d\n", err);
	dev_dbg(dev, "Read PDA returned %d\n", err);
	if (err)
	if (err)
		goto free;
		goto free;
@@ -148,7 +148,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
	}
	}


	/* Enable aux port to allow programming */
	/* Enable aux port to allow programming */
	err = hermesi_program_init(hw, le32_to_cpu(hdr->entry_point));
	err = hw->ops->program_init(hw, le32_to_cpu(hdr->entry_point));
	dev_dbg(dev, "Program init returned %d\n", err);
	dev_dbg(dev, "Program init returned %d\n", err);
	if (err != 0)
	if (err != 0)
		goto abort;
		goto abort;
@@ -176,7 +176,7 @@ orinoco_dl_firmware(struct orinoco_private *priv,
		goto abort;
		goto abort;


	/* Tell card we've finished */
	/* Tell card we've finished */
	err = hermesi_program_end(hw);
	err = hw->ops->program_end(hw);
	dev_dbg(dev, "Program end returned %d\n", err);
	dev_dbg(dev, "Program end returned %d\n", err);
	if (err != 0)
	if (err != 0)
		goto abort;
		goto abort;
@@ -223,7 +223,7 @@ symbol_dl_image(struct orinoco_private *priv, const struct fw_info *fw,
		if (!pda)
		if (!pda)
			return -ENOMEM;
			return -ENOMEM;


		ret = hermes_read_pda(hw, pda, fw->pda_addr, fw->pda_size, 1);
		ret = hw->ops->read_pda(hw, pda, fw->pda_addr, fw->pda_size);
		if (ret)
		if (ret)
			goto free;
			goto free;
	}
	}
+208 −0
Original line number Original line Diff line number Diff line
@@ -51,6 +51,26 @@
#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
#define CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */
#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */
#define ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */


/*
 * AUX port access.  To unlock the AUX port write the access keys to the
 * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
 * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
 */
#define HERMES_AUX_ENABLE	0x8000	/* Enable auxiliary port access */
#define HERMES_AUX_DISABLE	0x4000	/* Disable to auxiliary port access */
#define HERMES_AUX_ENABLED	0xC000	/* Auxiliary port is open */
#define HERMES_AUX_DISABLED	0x0000	/* Auxiliary port is closed */

#define HERMES_AUX_PW0	0xFE01
#define HERMES_AUX_PW1	0xDC23
#define HERMES_AUX_PW2	0xBA45

/* HERMES_CMD_DOWNLD */
#define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)

/*
/*
 * Debugging helpers
 * Debugging helpers
 */
 */
@@ -170,6 +190,7 @@ void hermes_struct_init(hermes_t *hw, void __iomem *address, int reg_spacing)
	hw->iobase = address;
	hw->iobase = address;
	hw->reg_spacing = reg_spacing;
	hw->reg_spacing = reg_spacing;
	hw->inten = 0x0;
	hw->inten = 0x0;
	hw->eeprom_pda = false;
	hw->ops = &hermes_ops_local;
	hw->ops = &hermes_ops_local;
}
}
EXPORT_SYMBOL(hermes_struct_init);
EXPORT_SYMBOL(hermes_struct_init);
@@ -529,6 +550,189 @@ static int hermes_write_ltv(hermes_t *hw, int bap, u16 rid,
	return err;
	return err;
}
}


/*** Hermes AUX control ***/

static inline void
hermes_aux_setaddr(hermes_t *hw, u32 addr)
{
	hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
	hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
}

static inline int
hermes_aux_control(hermes_t *hw, int enabled)
{
	int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
	int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
	int i;

	/* Already open? */
	if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
		return 0;

	hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
	hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
	hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
	hermes_write_reg(hw, HERMES_CONTROL, action);

	for (i = 0; i < 20; i++) {
		udelay(10);
		if (hermes_read_reg(hw, HERMES_CONTROL) ==
		    desired_state)
			return 0;
	}

	return -EBUSY;
}

/*** Hermes programming ***/

/* About to start programming data (Hermes I)
 * offset is the entry point
 *
 * Spectrum_cs' Symbol fw does not require this
 * wl_lkm Agere fw does
 * Don't know about intersil
 */
static int hermesi_program_init(hermes_t *hw, u32 offset)
{
	int err;

	/* Disable interrupts?*/
	/*hw->inten = 0x0;*/
	/*hermes_write_regn(hw, INTEN, 0);*/
	/*hermes_set_irqmask(hw, 0);*/

	/* Acknowledge any outstanding command */
	hermes_write_regn(hw, EVACK, 0xFFFF);

	/* Using init_cmd_wait rather than cmd_wait */
	err = hw->ops->init_cmd_wait(hw,
				     0x0100 | HERMES_CMD_INIT,
				     0, 0, 0, NULL);
	if (err)
		return err;

	err = hw->ops->init_cmd_wait(hw,
				     0x0000 | HERMES_CMD_INIT,
				     0, 0, 0, NULL);
	if (err)
		return err;

	err = hermes_aux_control(hw, 1);
	pr_debug("AUX enable returned %d\n", err);

	if (err)
		return err;

	pr_debug("Enabling volatile, EP 0x%08x\n", offset);
	err = hw->ops->init_cmd_wait(hw,
				     HERMES_PROGRAM_ENABLE_VOLATILE,
				     offset & 0xFFFFu,
				     offset >> 16,
				     0,
				     NULL);
	pr_debug("PROGRAM_ENABLE returned %d\n", err);

	return err;
}

/* Done programming data (Hermes I)
 *
 * Spectrum_cs' Symbol fw does not require this
 * wl_lkm Agere fw does
 * Don't know about intersil
 */
static int hermesi_program_end(hermes_t *hw)
{
	struct hermes_response resp;
	int rc = 0;
	int err;

	rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);

	pr_debug("PROGRAM_DISABLE returned %d, "
		 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
		 rc, resp.resp0, resp.resp1, resp.resp2);

	if ((rc == 0) &&
	    ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
		rc = -EIO;

	err = hermes_aux_control(hw, 0);
	pr_debug("AUX disable returned %d\n", err);

	/* Acknowledge any outstanding command */
	hermes_write_regn(hw, EVACK, 0xFFFF);

	/* Reinitialise, ignoring return */
	(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
				      0, 0, 0, NULL);

	return rc ? rc : err;
}

static int hermes_program_bytes(struct hermes *hw, const char *data,
				u32 addr, u32 len)
{
	/* wl lkm splits the programming into chunks of 2000 bytes.
	 * This restriction appears to come from USB. The PCMCIA
	 * adapters can program the whole lot in one go */
	hermes_aux_setaddr(hw, addr);
	hermes_write_bytes(hw, HERMES_AUXDATA, data, len);
	return 0;
}

/* Read PDA from the adapter */
static int hermes_read_pda(hermes_t *hw, __le16 *pda, u32 pda_addr, u16 pda_len)
{
	int ret;
	u16 pda_size;
	u16 data_len = pda_len;
	__le16 *data = pda;

	if (hw->eeprom_pda) {
		/* PDA of spectrum symbol is in eeprom */

		/* Issue command to read EEPROM */
		ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
		if (ret)
			return ret;
	} else {
		/* wl_lkm does not include PDA size in the PDA area.
		 * We will pad the information into pda, so other routines
		 * don't have to be modified */
		pda[0] = cpu_to_le16(pda_len - 2);
			/* Includes CFG_PROD_DATA but not itself */
		pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
		data_len = pda_len - 4;
		data = pda + 2;
	}

	/* Open auxiliary port */
	ret = hermes_aux_control(hw, 1);
	pr_debug("AUX enable returned %d\n", ret);
	if (ret)
		return ret;

	/* Read PDA */
	hermes_aux_setaddr(hw, pda_addr);
	hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);

	/* Close aux port */
	ret = hermes_aux_control(hw, 0);
	pr_debug("AUX disable returned %d\n", ret);

	/* Check PDA length */
	pda_size = le16_to_cpu(pda[0]);
	pr_debug("Actual PDA length %d, Max allowed %d\n",
		 pda_size, pda_len);
	if (pda_size > pda_len)
		return -EINVAL;

	return 0;
}

static void hermes_lock_irqsave(spinlock_t *lock,
static void hermes_lock_irqsave(spinlock_t *lock,
				unsigned long *flags) __acquires(lock)
				unsigned long *flags) __acquires(lock)
{
{
@@ -561,6 +765,10 @@ static const struct hermes_ops hermes_ops_local = {
	.write_ltv = hermes_write_ltv,
	.write_ltv = hermes_write_ltv,
	.bap_pread = hermes_bap_pread,
	.bap_pread = hermes_bap_pread,
	.bap_pwrite = hermes_bap_pwrite,
	.bap_pwrite = hermes_bap_pwrite,
	.read_pda = hermes_read_pda,
	.program_init = hermesi_program_init,
	.program_end = hermesi_program_end,
	.program = hermes_program_bytes,
	.lock_irqsave = hermes_lock_irqsave,
	.lock_irqsave = hermes_lock_irqsave,
	.unlock_irqrestore = hermes_unlock_irqrestore,
	.unlock_irqrestore = hermes_unlock_irqrestore,
	.lock_irq = hermes_lock_irq,
	.lock_irq = hermes_lock_irq,
+7 −0
Original line number Original line Diff line number Diff line
@@ -393,6 +393,12 @@ struct hermes_ops {
			 u16 id, u16 offset);
			 u16 id, u16 offset);
	int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
	int (*bap_pwrite)(struct hermes *hw, int bap, const void *buf,
			  int len, u16 id, u16 offset);
			  int len, u16 id, u16 offset);
	int (*read_pda)(struct hermes *hw, __le16 *pda,
			u32 pda_addr, u16 pda_len);
	int (*program_init)(struct hermes *hw, u32 entry_point);
	int (*program_end)(struct hermes *hw);
	int (*program)(struct hermes *hw, const char *buf,
		       u32 addr, u32 len);
	void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
	void (*lock_irqsave)(spinlock_t *lock, unsigned long *flags);
	void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
	void (*unlock_irqrestore)(spinlock_t *lock, unsigned long *flags);
	void (*lock_irq)(spinlock_t *lock);
	void (*lock_irq)(spinlock_t *lock);
@@ -406,6 +412,7 @@ typedef struct hermes {
#define HERMES_16BIT_REGSPACING	0
#define HERMES_16BIT_REGSPACING	0
#define HERMES_32BIT_REGSPACING	1
#define HERMES_32BIT_REGSPACING	1
	u16 inten; /* Which interrupts should be enabled? */
	u16 inten; /* Which interrupts should be enabled? */
	bool eeprom_pda;
	const struct hermes_ops *ops;
	const struct hermes_ops *ops;
	void *priv;
	void *priv;
} hermes_t;
} hermes_t;
+9 −234
Original line number Original line Diff line number Diff line
@@ -46,37 +46,11 @@


#define PFX "hermes_dld: "
#define PFX "hermes_dld: "


/*
 * AUX port access.  To unlock the AUX port write the access keys to the
 * PARAM0-2 registers, then write HERMES_AUX_ENABLE to the HERMES_CONTROL
 * register.  Then read it and make sure it's HERMES_AUX_ENABLED.
 */
#define HERMES_AUX_ENABLE	0x8000	/* Enable auxiliary port access */
#define HERMES_AUX_DISABLE	0x4000	/* Disable to auxiliary port access */
#define HERMES_AUX_ENABLED	0xC000	/* Auxiliary port is open */
#define HERMES_AUX_DISABLED	0x0000	/* Auxiliary port is closed */

#define HERMES_AUX_PW0	0xFE01
#define HERMES_AUX_PW1	0xDC23
#define HERMES_AUX_PW2	0xBA45

/* HERMES_CMD_DOWNLD */
#define HERMES_PROGRAM_DISABLE             (0x0000 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_ENABLE_VOLATILE     (0x0100 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_ENABLE_NON_VOLATILE (0x0200 | HERMES_CMD_DOWNLD)
#define HERMES_PROGRAM_NON_VOLATILE        (0x0300 | HERMES_CMD_DOWNLD)

/* End markers used in dblocks */
/* End markers used in dblocks */
#define PDI_END		0x00000000	/* End of PDA */
#define PDI_END		0x00000000	/* End of PDA */
#define BLOCK_END	0xFFFFFFFF	/* Last image block */
#define BLOCK_END	0xFFFFFFFF	/* Last image block */
#define TEXT_END	0x1A		/* End of text header */
#define TEXT_END	0x1A		/* End of text header */


/* Limit the amout we try to download in a single shot.
 * Size is in bytes.
 */
#define MAX_DL_SIZE 1024
#define LIMIT_PROGRAM_SIZE 0

/*
/*
 * The following structures have little-endian fields denoted by
 * The following structures have little-endian fields denoted by
 * the leading underscore.  Don't access them directly - use inline
 * the leading underscore.  Don't access them directly - use inline
@@ -165,41 +139,6 @@ pdi_len(const struct pdi *pdi)
	return 2 * (le16_to_cpu(pdi->len) - 1);
	return 2 * (le16_to_cpu(pdi->len) - 1);
}
}


/*** Hermes AUX control ***/

static inline void
hermes_aux_setaddr(hermes_t *hw, u32 addr)
{
	hermes_write_reg(hw, HERMES_AUXPAGE, (u16) (addr >> 7));
	hermes_write_reg(hw, HERMES_AUXOFFSET, (u16) (addr & 0x7F));
}

static inline int
hermes_aux_control(hermes_t *hw, int enabled)
{
	int desired_state = enabled ? HERMES_AUX_ENABLED : HERMES_AUX_DISABLED;
	int action = enabled ? HERMES_AUX_ENABLE : HERMES_AUX_DISABLE;
	int i;

	/* Already open? */
	if (hermes_read_reg(hw, HERMES_CONTROL) == desired_state)
		return 0;

	hermes_write_reg(hw, HERMES_PARAM0, HERMES_AUX_PW0);
	hermes_write_reg(hw, HERMES_PARAM1, HERMES_AUX_PW1);
	hermes_write_reg(hw, HERMES_PARAM2, HERMES_AUX_PW2);
	hermes_write_reg(hw, HERMES_CONTROL, action);

	for (i = 0; i < 20; i++) {
		udelay(10);
		if (hermes_read_reg(hw, HERMES_CONTROL) ==
		    desired_state)
			return 0;
	}

	return -EBUSY;
}

/*** Plug Data Functions ***/
/*** Plug Data Functions ***/


/*
/*
@@ -271,62 +210,7 @@ hermes_plug_pdi(hermes_t *hw, const struct pdr *first_pdr,
		return -EINVAL;
		return -EINVAL;


	/* do the actual plugging */
	/* do the actual plugging */
	hermes_aux_setaddr(hw, pdr_addr(pdr));
	hw->ops->program(hw, pdi->data, pdr_addr(pdr), pdi_len(pdi));
	hermes_write_bytes(hw, HERMES_AUXDATA, pdi->data, pdi_len(pdi));

	return 0;
}

/* Read PDA from the adapter */
int hermes_read_pda(hermes_t *hw,
		    __le16 *pda,
		    u32 pda_addr,
		    u16 pda_len,
		    int use_eeprom) /* can we get this into hw? */
{
	int ret;
	u16 pda_size;
	u16 data_len = pda_len;
	__le16 *data = pda;

	if (use_eeprom) {
		/* PDA of spectrum symbol is in eeprom */

		/* Issue command to read EEPROM */
		ret = hw->ops->cmd_wait(hw, HERMES_CMD_READMIF, 0, NULL);
		if (ret)
			return ret;
	} else {
		/* wl_lkm does not include PDA size in the PDA area.
		 * We will pad the information into pda, so other routines
		 * don't have to be modified */
		pda[0] = cpu_to_le16(pda_len - 2);
			/* Includes CFG_PROD_DATA but not itself */
		pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */
		data_len = pda_len - 4;
		data = pda + 2;
	}

	/* Open auxiliary port */
	ret = hermes_aux_control(hw, 1);
	pr_debug(PFX "AUX enable returned %d\n", ret);
	if (ret)
		return ret;

	/* read PDA from EEPROM */
	hermes_aux_setaddr(hw, pda_addr);
	hermes_read_words(hw, HERMES_AUXDATA, data, data_len / 2);

	/* Close aux port */
	ret = hermes_aux_control(hw, 0);
	pr_debug(PFX "AUX disable returned %d\n", ret);

	/* Check PDA length */
	pda_size = le16_to_cpu(pda[0]);
	pr_debug(PFX "Actual PDA length %d, Max allowed %d\n",
		 pda_size, pda_len);
	if (pda_size > pda_len)
		return -EINVAL;


	return 0;
	return 0;
}
}
@@ -389,101 +273,13 @@ hermes_blocks_length(const char *first_block, const void *end)


/*** Hermes programming ***/
/*** Hermes programming ***/


/* About to start programming data (Hermes I)
 * offset is the entry point
 *
 * Spectrum_cs' Symbol fw does not require this
 * wl_lkm Agere fw does
 * Don't know about intersil
 */
int hermesi_program_init(hermes_t *hw, u32 offset)
{
	int err;

	/* Disable interrupts?*/
	/*hw->inten = 0x0;*/
	/*hermes_write_regn(hw, INTEN, 0);*/
	/*hermes_set_irqmask(hw, 0);*/

	/* Acknowledge any outstanding command */
	hermes_write_regn(hw, EVACK, 0xFFFF);

	/* Using init_cmd_wait rather than cmd_wait */
	err = hw->ops->init_cmd_wait(hw,
				     0x0100 | HERMES_CMD_INIT,
				     0, 0, 0, NULL);
	if (err)
		return err;

	err = hw->ops->init_cmd_wait(hw,
				     0x0000 | HERMES_CMD_INIT,
				     0, 0, 0, NULL);
	if (err)
		return err;

	err = hermes_aux_control(hw, 1);
	pr_debug(PFX "AUX enable returned %d\n", err);

	if (err)
		return err;

	pr_debug(PFX "Enabling volatile, EP 0x%08x\n", offset);
	err = hw->ops->init_cmd_wait(hw,
				     HERMES_PROGRAM_ENABLE_VOLATILE,
				     offset & 0xFFFFu,
				     offset >> 16,
				     0,
				     NULL);
	pr_debug(PFX "PROGRAM_ENABLE returned %d\n", err);

	return err;
}

/* Done programming data (Hermes I)
 *
 * Spectrum_cs' Symbol fw does not require this
 * wl_lkm Agere fw does
 * Don't know about intersil
 */
int hermesi_program_end(hermes_t *hw)
{
	struct hermes_response resp;
	int rc = 0;
	int err;

	rc = hw->ops->cmd_wait(hw, HERMES_PROGRAM_DISABLE, 0, &resp);

	pr_debug(PFX "PROGRAM_DISABLE returned %d, "
		 "r0 0x%04x, r1 0x%04x, r2 0x%04x\n",
		 rc, resp.resp0, resp.resp1, resp.resp2);

	if ((rc == 0) &&
	    ((resp.status & HERMES_STATUS_CMDCODE) != HERMES_CMD_DOWNLD))
		rc = -EIO;

	err = hermes_aux_control(hw, 0);
	pr_debug(PFX "AUX disable returned %d\n", err);

	/* Acknowledge any outstanding command */
	hermes_write_regn(hw, EVACK, 0xFFFF);

	/* Reinitialise, ignoring return */
	(void) hw->ops->init_cmd_wait(hw, 0x0000 | HERMES_CMD_INIT,
				      0, 0, 0, NULL);

	return rc ? rc : err;
}

/* Program the data blocks */
/* Program the data blocks */
int hermes_program(hermes_t *hw, const char *first_block, const void *end)
int hermes_program(hermes_t *hw, const char *first_block, const void *end)
{
{
	const struct dblock *blk;
	const struct dblock *blk;
	u32 blkaddr;
	u32 blkaddr;
	u32 blklen;
	u32 blklen;
#if LIMIT_PROGRAM_SIZE
	int err = 0;
	u32 addr;
	u32 len;
#endif


	blk = (const struct dblock *) first_block;
	blk = (const struct dblock *) first_block;


@@ -498,30 +294,10 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end)
		pr_debug(PFX "Programming block of length %d "
		pr_debug(PFX "Programming block of length %d "
			 "to address 0x%08x\n", blklen, blkaddr);
			 "to address 0x%08x\n", blklen, blkaddr);


#if !LIMIT_PROGRAM_SIZE
		err = hw->ops->program(hw, blk->data, blkaddr, blklen);
		/* wl_lkm driver splits this into writes of 2000 bytes */
		if (err)
		hermes_aux_setaddr(hw, blkaddr);
			break;
		hermes_write_bytes(hw, HERMES_AUXDATA, blk->data,

				   blklen);
#else
		len = (blklen < MAX_DL_SIZE) ? blklen : MAX_DL_SIZE;
		addr = blkaddr;

		while (addr < (blkaddr + blklen)) {
			pr_debug(PFX "Programming subblock of length %d "
				 "to address 0x%08x. Data @ %p\n",
				 len, addr, &blk->data[addr - blkaddr]);

			hermes_aux_setaddr(hw, addr);
			hermes_write_bytes(hw, HERMES_AUXDATA,
					   &blk->data[addr - blkaddr],
					   len);

			addr += len;
			len = ((blkaddr + blklen - addr) < MAX_DL_SIZE) ?
				(blkaddr + blklen - addr) : MAX_DL_SIZE;
		}
#endif
		blk = (const struct dblock *) &blk->data[blklen];
		blk = (const struct dblock *) &blk->data[blklen];


		if ((void *) blk > (end - sizeof(*blk)))
		if ((void *) blk > (end - sizeof(*blk)))
@@ -530,7 +306,7 @@ int hermes_program(hermes_t *hw, const char *first_block, const void *end)
		blkaddr = dblock_addr(blk);
		blkaddr = dblock_addr(blk);
		blklen = dblock_len(blk);
		blklen = dblock_len(blk);
	}
	}
	return 0;
	return err;
}
}


/*** Default plugging data for Hermes I ***/
/*** Default plugging data for Hermes I ***/
@@ -690,9 +466,8 @@ int hermes_apply_pda_with_defaults(hermes_t *hw,
			if ((pdi_len(pdi) == pdr_len(pdr)) &&
			if ((pdi_len(pdi) == pdr_len(pdr)) &&
			    ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
			    ((void *) pdi->data + pdi_len(pdi) < pda_end)) {
				/* do the actual plugging */
				/* do the actual plugging */
				hermes_aux_setaddr(hw, pdr_addr(pdr));
				hw->ops->program(hw, pdi->data, pdr_addr(pdr),
				hermes_write_bytes(hw, HERMES_AUXDATA,
						 pdi_len(pdi));
						   pdi->data, pdi_len(pdi));
			}
			}
		}
		}


+120 −0
Original line number Original line Diff line number Diff line
@@ -184,6 +184,11 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");
#define EZUSB_RID_RX			0x0701
#define EZUSB_RID_RX			0x0701
#define EZUSB_RID_INIT1			0x0702
#define EZUSB_RID_INIT1			0x0702
#define EZUSB_RID_ACK			0x0710
#define EZUSB_RID_ACK			0x0710
#define EZUSB_RID_READ_PDA		0x0800
#define EZUSB_RID_PROG_INIT		0x0852
#define EZUSB_RID_PROG_SET_ADDR		0x0853
#define EZUSB_RID_PROG_BYTES		0x0854
#define EZUSB_RID_PROG_END		0x0855
#define EZUSB_RID_DOCMD			0x0860
#define EZUSB_RID_DOCMD			0x0860


/* Recognize info frames */
/* Recognize info frames */
@@ -198,6 +203,8 @@ MODULE_FIRMWARE("orinoco_ezusb_fw");


#define BULK_BUF_SIZE			2048
#define BULK_BUF_SIZE			2048


#define MAX_DL_SIZE (BULK_BUF_SIZE - sizeof(struct ezusb_packet))

#define FW_BUF_SIZE			64
#define FW_BUF_SIZE			64
#define FW_VAR_OFFSET_PTR		0x359
#define FW_VAR_OFFSET_PTR		0x359
#define FW_VAR_VALUE			0
#define FW_VAR_VALUE			0
@@ -1077,6 +1084,115 @@ static int ezusb_bap_pread(struct hermes *hw, int bap,
	return 0;
	return 0;
}
}


static int ezusb_read_pda(struct hermes *hw, __le16 *pda,
			  u32 pda_addr, u16 pda_len)
{
	struct ezusb_priv *upriv = hw->priv;
	struct request_context *ctx;
	__le16 data[] = {
		cpu_to_le16(pda_addr & 0xffff),
		cpu_to_le16(pda_len - 4)
	};
	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_READ_PDA, EZUSB_RID_READ_PDA);
	if (!ctx)
		return -ENOMEM;

	/* wl_lkm does not include PDA size in the PDA area.
	 * We will pad the information into pda, so other routines
	 * don't have to be modified */
	pda[0] = cpu_to_le16(pda_len - 2);
	/* Includes CFG_PROD_DATA but not itself */
	pda[1] = cpu_to_le16(0x0800); /* CFG_PROD_DATA */

	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
				EZUSB_FRAME_CONTROL, &pda[2], pda_len - 4,
				NULL);
}

static int ezusb_program_init(struct hermes *hw, u32 entry_point)
{
	struct ezusb_priv *upriv = hw->priv;
	struct request_context *ctx;
	__le32 data = cpu_to_le32(entry_point);

	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_INIT, EZUSB_RID_ACK);
	if (!ctx)
		return -ENOMEM;

	return ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
}

static int ezusb_program_end(struct hermes *hw)
{
	struct ezusb_priv *upriv = hw->priv;
	struct request_context *ctx;

	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_END, EZUSB_RID_ACK);
	if (!ctx)
		return -ENOMEM;

	return ezusb_access_ltv(upriv, ctx, 0, NULL,
				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
}

static int ezusb_program_bytes(struct hermes *hw, const char *buf,
			       u32 addr, u32 len)
{
	struct ezusb_priv *upriv = hw->priv;
	struct request_context *ctx;
	__le32 data = cpu_to_le32(addr);
	int err;

	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_SET_ADDR, EZUSB_RID_ACK);
	if (!ctx)
		return -ENOMEM;

	err = ezusb_access_ltv(upriv, ctx, sizeof(data), &data,
			       EZUSB_FRAME_CONTROL, NULL, 0, NULL);
	if (err)
		return err;

	ctx = ezusb_alloc_ctx(upriv, EZUSB_RID_PROG_BYTES, EZUSB_RID_ACK);
	if (!ctx)
		return -ENOMEM;

	return ezusb_access_ltv(upriv, ctx, len, buf,
				EZUSB_FRAME_CONTROL, NULL, 0, NULL);
}

static int ezusb_program(struct hermes *hw, const char *buf,
			 u32 addr, u32 len)
{
	u32 ch_addr;
	u32 ch_len;
	int err = 0;

	/* We can only send 2048 bytes out of the bulk xmit at a time,
	 * so we have to split any programming into chunks of <2048
	 * bytes. */

	ch_len = (len < MAX_DL_SIZE) ? len : MAX_DL_SIZE;
	ch_addr = addr;

	while (ch_addr < (addr + len)) {
		pr_debug("Programming subblock of length %d "
			 "to address 0x%08x. Data @ %p\n",
			 ch_len, ch_addr, &buf[ch_addr - addr]);

		err = ezusb_program_bytes(hw, &buf[ch_addr - addr],
					  ch_addr, ch_len);
		if (err)
			break;

		ch_addr += ch_len;
		ch_len = ((addr + len - ch_addr) < MAX_DL_SIZE) ?
			(addr + len - ch_addr) : MAX_DL_SIZE;
	}

	return err;
}

static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
static netdev_tx_t ezusb_xmit(struct sk_buff *skb, struct net_device *dev)
{
{
	struct orinoco_private *priv = ndev_priv(dev);
	struct orinoco_private *priv = ndev_priv(dev);
@@ -1440,6 +1556,10 @@ static const struct hermes_ops ezusb_ops = {
	.read_ltv = ezusb_read_ltv,
	.read_ltv = ezusb_read_ltv,
	.write_ltv = ezusb_write_ltv,
	.write_ltv = ezusb_write_ltv,
	.bap_pread = ezusb_bap_pread,
	.bap_pread = ezusb_bap_pread,
	.read_pda = ezusb_read_pda,
	.program_init = ezusb_program_init,
	.program_end = ezusb_program_end,
	.program = ezusb_program,
	.lock_irqsave = ezusb_lock_irqsave,
	.lock_irqsave = ezusb_lock_irqsave,
	.unlock_irqrestore = ezusb_unlock_irqrestore,
	.unlock_irqrestore = ezusb_unlock_irqrestore,
	.lock_irq = ezusb_lock_irq,
	.lock_irq = ezusb_lock_irq,
Loading