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

Commit 89d32c90 authored by Larry Finger's avatar Larry Finger Committed by Kalle Valo
Browse files

rtlwifi: Download firmware as bytes rather than as dwords



The firmware is read from disk as a little-endian byte string. The code
that loads the firmware into the device transfers it as 4-byte quantities.
The routines that write multi-byte quantities on BE hardware assume that
the data are in CPU order, and automatically do the conversion to the LE
order required by the device. As a result, the firmware is transmitted
incorrectly. Rather than do multiple byte swaps on the data, the download
routine is revised to transmit bytes rather than dwords. Although the
number of I/O operations is increased, the firmware is not often loaded.

All drivers have the same bug, and use essentially the same code to
download firmware. These routines have been moved into rtlwifi.

Some CamelCase variables have been renamed.

Signed-off-by: default avatarLarry Finger <Larry.Finger@lwfinger.net>
Cc: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 9336d376
Loading
Loading
Loading
Loading
+45 −0
Original line number Diff line number Diff line
@@ -31,6 +31,9 @@ static const u8 MAX_PGPKT_SIZE = 9;
static const u8 PGPKT_DATA_SIZE = 8;
static const int EFUSE_MAX_SIZE = 512;

#define START_ADDRESS		0x1000
#define REG_MCUFWDL		0x0080

static const struct efuse_map RTL8712_SDIO_EFUSE_TABLE[] = {
	{0, 0, 0, 2},
	{0, 1, 0, 2},
@@ -1320,3 +1323,45 @@ int rtl_get_hwinfo(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv,
	return 0;
}
EXPORT_SYMBOL_GPL(rtl_get_hwinfo);

void rtl_fw_block_write(struct ieee80211_hw *hw, const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 *pu4byteptr = (u8 *)buffer;
	u32 i;

	for (i = 0; i < size; i++)
		rtl_write_byte(rtlpriv, (START_ADDRESS + i), *(pu4byteptr + i));
}
EXPORT_SYMBOL_GPL(rtl_fw_block_write);

void rtl_fw_page_write(struct ieee80211_hw *hw, u32 page, const u8 *buffer,
		       u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 value8;
	u8 u8page = (u8)(page & 0x07);

	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;

	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
	rtl_fw_block_write(hw, buffer, size);
}
EXPORT_SYMBOL_GPL(rtl_fw_page_write);

void rtl_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
{
	u32 fwlen = *pfwlen;
	u8 remain = (u8)(fwlen % 4);

	remain = (remain == 0) ? 0 : (4 - remain);

	while (remain > 0) {
		pfwbuf[fwlen] = 0;
		fwlen++;
		remain--;
	}

	*pfwlen = fwlen;
}
EXPORT_SYMBOL_GPL(rtl_fill_dummy);
+4 −0
Original line number Diff line number Diff line
@@ -111,5 +111,9 @@ void efuse_force_write_vendor_Id(struct ieee80211_hw *hw);
void efuse_re_pg_section(struct ieee80211_hw *hw, u8 section_idx);
int rtl_get_hwinfo(struct ieee80211_hw *hw, struct rtl_priv *rtlpriv,
		   int max_size, u8 *hwinfo, int *params);
void rtl_fill_dummy(u8 *pfwbuf, u32 *pfwlen);
void rtl_fw_page_write(struct ieee80211_hw *hw, u32 page, const u8 *buffer,
		       u32 size);
void rtl_fw_block_write(struct ieee80211_hw *hw, const u8 *buffer, u32 size);

#endif
+5 −62
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include "../pci.h"
#include "../base.h"
#include "../core.h"
#include "../efuse.h"
#include "reg.h"
#include "def.h"
#include "fw.h"
@@ -53,63 +54,6 @@ static void _rtl88e_enable_fw_download(struct ieee80211_hw *hw, bool enable)
	}
}

static void _rtl88e_fw_block_write(struct ieee80211_hw *hw,
				   const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u32 blocksize = sizeof(u32);
	u8 *bufferptr = (u8 *)buffer;
	u32 *pu4BytePtr = (u32 *)buffer;
	u32 i, offset, blockcount, remainsize;

	blockcount = size / blocksize;
	remainsize = size % blocksize;

	for (i = 0; i < blockcount; i++) {
		offset = i * blocksize;
		rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
				*(pu4BytePtr + i));
	}

	if (remainsize) {
		offset = blockcount * blocksize;
		bufferptr += offset;
		for (i = 0; i < remainsize; i++) {
			rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
						 offset + i), *(bufferptr + i));
		}
	}
}

static void _rtl88e_fw_page_write(struct ieee80211_hw *hw,
				  u32 page, const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 value8;
	u8 u8page = (u8) (page & 0x07);

	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;

	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
	_rtl88e_fw_block_write(hw, buffer, size);
}

static void _rtl88e_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
{
	u32 fwlen = *pfwlen;
	u8 remain = (u8) (fwlen % 4);

	remain = (remain == 0) ? 0 : (4 - remain);

	while (remain > 0) {
		pfwbuf[fwlen] = 0;
		fwlen++;
		remain--;
	}

	*pfwlen = fwlen;
}

static void _rtl88e_write_fw(struct ieee80211_hw *hw,
			     enum version_8188e version, u8 *buffer, u32 size)
{
@@ -120,7 +64,7 @@ static void _rtl88e_write_fw(struct ieee80211_hw *hw,

	RT_TRACE(rtlpriv, COMP_FW, DBG_LOUD, "FW size is %d bytes,\n", size);

	_rtl88e_fill_dummy(bufferptr, &size);
	rtl_fill_dummy(bufferptr, &size);

	pagenums = size / FW_8192C_PAGE_SIZE;
	remainsize = size % FW_8192C_PAGE_SIZE;
@@ -130,15 +74,14 @@ static void _rtl88e_write_fw(struct ieee80211_hw *hw,

	for (page = 0; page < pagenums; page++) {
		offset = page * FW_8192C_PAGE_SIZE;
		_rtl88e_fw_page_write(hw, page, (bufferptr + offset),
		rtl_fw_page_write(hw, page, (bufferptr + offset),
				  FW_8192C_PAGE_SIZE);
	}

	if (remainsize) {
		offset = pagenums * FW_8192C_PAGE_SIZE;
		page = pagenums;
		_rtl88e_fw_page_write(hw, page, (bufferptr + offset),
				      remainsize);
		rtl_fw_page_write(hw, page, (bufferptr + offset), remainsize);
	}
}

+7 −63
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#include "../pci.h"
#include "../base.h"
#include "../core.h"
#include "../efuse.h"
#include "../rtl8192ce/reg.h"
#include "../rtl8192ce/def.h"
#include "fw_common.h"
@@ -68,63 +69,6 @@ static void _rtl92c_enable_fw_download(struct ieee80211_hw *hw, bool enable)
	}
}

static void _rtl92c_fw_block_write(struct ieee80211_hw *hw,
				   const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u32 blocksize = sizeof(u32);
	u8 *bufferptr = (u8 *)buffer;
	u32 *pu4byteptr = (u32 *)buffer;
	u32 i, offset, blockcount, remainsize;

	blockcount = size / blocksize;
	remainsize = size % blocksize;

	for (i = 0; i < blockcount; i++) {
		offset = i * blocksize;
		rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
				*(pu4byteptr + i));
	}

	if (remainsize) {
		offset = blockcount * blocksize;
		bufferptr += offset;
		for (i = 0; i < remainsize; i++) {
			rtl_write_byte(rtlpriv, (FW_8192C_START_ADDRESS +
						 offset + i), *(bufferptr + i));
		}
	}
}

static void _rtl92c_fw_page_write(struct ieee80211_hw *hw,
				  u32 page, const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 value8;
	u8 u8page = (u8) (page & 0x07);

	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;

	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
	_rtl92c_fw_block_write(hw, buffer, size);
}

static void _rtl92c_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
{
	u32 fwlen = *pfwlen;
	u8 remain = (u8) (fwlen % 4);

	remain = (remain == 0) ? 0 : (4 - remain);

	while (remain > 0) {
		pfwbuf[fwlen] = 0;
		fwlen++;
		remain--;
	}

	*pfwlen = fwlen;
}

static void _rtl92c_write_fw(struct ieee80211_hw *hw,
			     enum version_8192c version, u8 *buffer, u32 size)
{
@@ -140,7 +84,7 @@ static void _rtl92c_write_fw(struct ieee80211_hw *hw,
		u32 page, offset;

		if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192CE)
			_rtl92c_fill_dummy(bufferptr, &size);
			rtl_fill_dummy(bufferptr, &size);

		pageNums = size / FW_8192C_PAGE_SIZE;
		remainsize = size % FW_8192C_PAGE_SIZE;
@@ -150,18 +94,18 @@ static void _rtl92c_write_fw(struct ieee80211_hw *hw,

		for (page = 0; page < pageNums; page++) {
			offset = page * FW_8192C_PAGE_SIZE;
			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
			rtl_fw_page_write(hw, page, (bufferptr + offset),
					  FW_8192C_PAGE_SIZE);
		}

		if (remainsize) {
			offset = pageNums * FW_8192C_PAGE_SIZE;
			page = pageNums;
			_rtl92c_fw_page_write(hw, page, (bufferptr + offset),
			rtl_fw_page_write(hw, page, (bufferptr + offset),
					  remainsize);
		}
	} else {
		_rtl92c_fw_block_write(hw, buffer, size);
		rtl_fw_block_write(hw, buffer, size);
	}
}

+9 −61
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include "../wifi.h"
#include "../pci.h"
#include "../base.h"
#include "../efuse.h"
#include "reg.h"
#include "def.h"
#include "fw.h"
@@ -59,84 +60,31 @@ static void _rtl92d_enable_fw_download(struct ieee80211_hw *hw, bool enable)
	}
}

static void _rtl92d_fw_block_write(struct ieee80211_hw *hw,
				   const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u32 blocksize = sizeof(u32);
	u8 *bufferptr = (u8 *) buffer;
	u32 *pu4BytePtr = (u32 *) buffer;
	u32 i, offset, blockCount, remainSize;

	blockCount = size / blocksize;
	remainSize = size % blocksize;
	for (i = 0; i < blockCount; i++) {
		offset = i * blocksize;
		rtl_write_dword(rtlpriv, (FW_8192D_START_ADDRESS + offset),
				*(pu4BytePtr + i));
	}
	if (remainSize) {
		offset = blockCount * blocksize;
		bufferptr += offset;
		for (i = 0; i < remainSize; i++) {
			rtl_write_byte(rtlpriv, (FW_8192D_START_ADDRESS +
						 offset + i), *(bufferptr + i));
		}
	}
}

static void _rtl92d_fw_page_write(struct ieee80211_hw *hw,
				  u32 page, const u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	u8 value8;
	u8 u8page = (u8) (page & 0x07);

	value8 = (rtl_read_byte(rtlpriv, REG_MCUFWDL + 2) & 0xF8) | u8page;
	rtl_write_byte(rtlpriv, (REG_MCUFWDL + 2), value8);
	_rtl92d_fw_block_write(hw, buffer, size);
}

static void _rtl92d_fill_dummy(u8 *pfwbuf, u32 *pfwlen)
{
	u32 fwlen = *pfwlen;
	u8 remain = (u8) (fwlen % 4);

	remain = (remain == 0) ? 0 : (4 - remain);
	while (remain > 0) {
		pfwbuf[fwlen] = 0;
		fwlen++;
		remain--;
	}
	*pfwlen = fwlen;
}

static void _rtl92d_write_fw(struct ieee80211_hw *hw,
			     enum version_8192d version, u8 *buffer, u32 size)
{
	struct rtl_priv *rtlpriv = rtl_priv(hw);
	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
	u8 *bufferPtr = buffer;
	u32 pagenums, remainSize;
	u8 *bufferptr = buffer;
	u32 pagenums, remainsize;
	u32 page, offset;

	RT_TRACE(rtlpriv, COMP_FW, DBG_TRACE, "FW size is %d bytes,\n", size);
	if (rtlhal->hw_type == HARDWARE_TYPE_RTL8192DE)
		_rtl92d_fill_dummy(bufferPtr, &size);
		rtl_fill_dummy(bufferptr, &size);
	pagenums = size / FW_8192D_PAGE_SIZE;
	remainSize = size % FW_8192D_PAGE_SIZE;
	remainsize = size % FW_8192D_PAGE_SIZE;
	if (pagenums > 8)
		pr_err("Page numbers should not greater then 8\n");
	for (page = 0; page < pagenums; page++) {
		offset = page * FW_8192D_PAGE_SIZE;
		_rtl92d_fw_page_write(hw, page, (bufferPtr + offset),
		rtl_fw_page_write(hw, page, (bufferptr + offset),
				  FW_8192D_PAGE_SIZE);
	}
	if (remainSize) {
	if (remainsize) {
		offset = pagenums * FW_8192D_PAGE_SIZE;
		page = pagenums;
		_rtl92d_fw_page_write(hw, page, (bufferPtr + offset),
				      remainSize);
		rtl_fw_page_write(hw, page, (bufferptr + offset), remainsize);
	}
}

Loading