Commit 74553aed authored by Daniel Drake's avatar Daniel Drake Committed by John W. Linville
Browse files

[PATCH] zd1211rw: Defer firmware load until first ifup



While playing with the firmware a while back, I discovered a way to
access the device's entire address space before the firmware has been
loaded.

Previously we were loading the firmware early on (during probe) so that
we could read the MAC address from the EEPROM and register a netdevice.
Now that we can read the EEPROM without having firmware, we can defer
firmware loading until later while still reading the MAC address early
on.

This has the advantage that zd1211rw can now be built into the kernel --
previously if this was the case, zd1211rw would be loaded before the
filesystem is available and firmware loading would fail.

Firmware load and other device initialization operations now happen the
first time the interface is brought up.

Some architectural changes were needed: handling of the is_zd1211b flag
was moved into the zd_usb structure, MAC address handling was obviously
changed, and a preinit_hw stage was added (the order is now: init,
preinit_hw, init_hw).

Signed-off-by: default avatarDaniel Drake <dsd@gentoo.org>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 93f510bb
Loading
Loading
Loading
Loading
+15 −73
Original line number Diff line number Diff line
@@ -49,8 +49,9 @@ void zd_chip_clear(struct zd_chip *chip)
	ZD_MEMCLEAR(chip, sizeof(*chip));
}

static int scnprint_mac_oui(const u8 *addr, char *buffer, size_t size)
static int scnprint_mac_oui(struct zd_chip *chip, char *buffer, size_t size)
{
	u8 *addr = zd_usb_to_netdev(&chip->usb)->dev_addr;
	return scnprintf(buffer, size, "%02x-%02x-%02x",
		         addr[0], addr[1], addr[2]);
}
@@ -61,10 +62,10 @@ static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size)
	int i = 0;

	i = scnprintf(buffer, size, "zd1211%s chip ",
		      chip->is_zd1211b ? "b" : "");
		      zd_chip_is_zd1211b(chip) ? "b" : "");
	i += zd_usb_scnprint_id(&chip->usb, buffer+i, size-i);
	i += scnprintf(buffer+i, size-i, " ");
	i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i);
	i += scnprint_mac_oui(chip, buffer+i, size-i);
	i += scnprintf(buffer+i, size-i, " ");
	i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i);
	i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type,
@@ -366,64 +367,9 @@ static int read_pod(struct zd_chip *chip, u8 *rf_type)
	return r;
}

static int _read_mac_addr(struct zd_chip *chip, u8 *mac_addr,
	                  const zd_addr_t *addr)
{
	int r;
	u32 parts[2];

	r = zd_ioread32v_locked(chip, parts, (const zd_addr_t *)addr, 2);
	if (r) {
		dev_dbg_f(zd_chip_dev(chip),
			"error: couldn't read e2p macs. Error number %d\n", r);
		return r;
	}

	mac_addr[0] = parts[0];
	mac_addr[1] = parts[0] >>  8;
	mac_addr[2] = parts[0] >> 16;
	mac_addr[3] = parts[0] >> 24;
	mac_addr[4] = parts[1];
	mac_addr[5] = parts[1] >>  8;

	return 0;
}

static int read_e2p_mac_addr(struct zd_chip *chip)
{
	static const zd_addr_t addr[2] = { E2P_MAC_ADDR_P1, E2P_MAC_ADDR_P2 };

	ZD_ASSERT(mutex_is_locked(&chip->mutex));
	return _read_mac_addr(chip, chip->e2p_mac, (const zd_addr_t *)addr);
}

/* MAC address: if custom mac addresses are to to be used CR_MAC_ADDR_P1 and
 *              CR_MAC_ADDR_P2 must be overwritten
 */
void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr)
{
	mutex_lock(&chip->mutex);
	memcpy(mac_addr, chip->e2p_mac, ETH_ALEN);
	mutex_unlock(&chip->mutex);
}

static int read_mac_addr(struct zd_chip *chip, u8 *mac_addr)
{
	static const zd_addr_t addr[2] = { CR_MAC_ADDR_P1, CR_MAC_ADDR_P2 };
	return _read_mac_addr(chip, mac_addr, (const zd_addr_t *)addr);
}

int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr)
{
	int r;

	dev_dbg_f(zd_chip_dev(chip), "\n");
	mutex_lock(&chip->mutex);
	r = read_mac_addr(chip, mac_addr);
	mutex_unlock(&chip->mutex);
	return r;
}

int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)
{
	int r;
@@ -444,12 +390,6 @@ int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr)

	mutex_lock(&chip->mutex);
	r = zd_iowrite32a_locked(chip, reqs, ARRAY_SIZE(reqs));
#ifdef DEBUG
	{
		u8 tmp[ETH_ALEN];
		read_mac_addr(chip, tmp);
	}
#endif /* DEBUG */
	mutex_unlock(&chip->mutex);
	return r;
}
@@ -809,7 +749,7 @@ static int zd1211b_hw_reset_phy(struct zd_chip *chip)

static int hw_reset_phy(struct zd_chip *chip)
{
	return chip->is_zd1211b ? zd1211b_hw_reset_phy(chip) :
	return zd_chip_is_zd1211b(chip) ? zd1211b_hw_reset_phy(chip) :
		                  zd1211_hw_reset_phy(chip);
}

@@ -874,7 +814,7 @@ static int hw_init_hmac(struct zd_chip *chip)
	if (r)
		return r;

	return chip->is_zd1211b ?
	return zd_chip_is_zd1211b(chip) ?
		zd1211b_hw_init_hmac(chip) : zd1211_hw_init_hmac(chip);
}

@@ -1136,8 +1076,15 @@ static int read_fw_regs_offset(struct zd_chip *chip)
	return 0;
}

/* Read mac address using pre-firmware interface */
int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr)
{
	dev_dbg_f(zd_chip_dev(chip), "\n");
	return zd_usb_read_fw(&chip->usb, E2P_MAC_ADDR_P1, addr,
		ETH_ALEN);
}

int zd_chip_init_hw(struct zd_chip *chip, u8 device_type)
int zd_chip_init_hw(struct zd_chip *chip)
{
	int r;
	u8 rf_type;
@@ -1145,7 +1092,6 @@ int zd_chip_init_hw(struct zd_chip *chip, u8 device_type)
	dev_dbg_f(zd_chip_dev(chip), "\n");

	mutex_lock(&chip->mutex);
	chip->is_zd1211b = (device_type == DEVICE_ZD1211B) != 0;

#ifdef DEBUG
	r = test_init(chip);
@@ -1201,10 +1147,6 @@ int zd_chip_init_hw(struct zd_chip *chip, u8 device_type)
		goto out;
#endif /* DEBUG */

	r = read_e2p_mac_addr(chip);
	if (r)
		goto out;

	r = read_cal_int_tables(chip);
	if (r)
		goto out;
@@ -1259,7 +1201,7 @@ static int update_channel_integration_and_calibration(struct zd_chip *chip,
	r = update_pwr_int(chip, channel);
	if (r)
		return r;
	if (chip->is_zd1211b) {
	if (zd_chip_is_zd1211b(chip)) {
		static const struct zd_ioreq16 ioreqs[] = {
			{ CR69, 0x28 },
			{},
+8 −5
Original line number Diff line number Diff line
@@ -704,7 +704,6 @@ struct zd_chip {
	struct mutex mutex;
	/* Base address of FW_REG_ registers */
	zd_addr_t fw_regs_base;
	u8 e2p_mac[ETH_ALEN];
	/* EepSetPoint in the vendor driver */
	u8 pwr_cal_values[E2P_CHANNEL_COUNT];
	/* integration values in the vendor driver */
@@ -715,7 +714,7 @@ struct zd_chip {
	unsigned int pa_type:4,
		patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
		new_phy_layout:1, al2230s_bit:1,
		is_zd1211b:1, supports_tx_led:1;
		supports_tx_led:1;
};

static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
@@ -734,9 +733,15 @@ void zd_chip_init(struct zd_chip *chip,
	         struct net_device *netdev,
	         struct usb_interface *intf);
void zd_chip_clear(struct zd_chip *chip);
int zd_chip_init_hw(struct zd_chip *chip, u8 device_type);
int zd_chip_read_mac_addr_fw(struct zd_chip *chip, u8 *addr);
int zd_chip_init_hw(struct zd_chip *chip);
int zd_chip_reset(struct zd_chip *chip);

static inline int zd_chip_is_zd1211b(struct zd_chip *chip)
{
	return chip->usb.is_zd1211b;
}

static inline int zd_ioread16v_locked(struct zd_chip *chip, u16 *values,
	                              const zd_addr_t *addresses,
				      unsigned int count)
@@ -825,8 +830,6 @@ static inline u8 _zd_chip_get_channel(struct zd_chip *chip)
}
u8  zd_chip_get_channel(struct zd_chip *chip);
int zd_read_regdomain(struct zd_chip *chip, u8 *regdomain);
void zd_get_e2p_mac_addr(struct zd_chip *chip, u8 *mac_addr);
int zd_read_mac_addr(struct zd_chip *chip, u8 *mac_addr);
int zd_write_mac_addr(struct zd_chip *chip, const u8 *mac_addr);
int zd_chip_switch_radio_on(struct zd_chip *chip);
int zd_chip_switch_radio_off(struct zd_chip *chip);
+32 −14
Original line number Diff line number Diff line
@@ -86,28 +86,33 @@ static int reset_channel(struct zd_mac *mac)
	return r;
}

int zd_mac_init_hw(struct zd_mac *mac, u8 device_type)
int zd_mac_preinit_hw(struct zd_mac *mac)
{
	int r;
	struct zd_chip *chip = &mac->chip;
	u8 addr[ETH_ALEN];

	r = zd_chip_read_mac_addr_fw(&mac->chip, addr);
	if (r)
		return r;

	memcpy(mac->netdev->dev_addr, addr, ETH_ALEN);
	return 0;
}

int zd_mac_init_hw(struct zd_mac *mac)
{
	int r;
	struct zd_chip *chip = &mac->chip;
	u8 default_regdomain;

	r = zd_chip_enable_int(chip);
	if (r)
		goto out;
	r = zd_chip_init_hw(chip, device_type);
	r = zd_chip_init_hw(chip);
	if (r)
		goto disable_int;

	zd_get_e2p_mac_addr(chip, addr);
	r = zd_write_mac_addr(chip, addr);
	if (r)
		goto disable_int;
	ZD_ASSERT(!irqs_disabled());
	spin_lock_irq(&mac->lock);
	memcpy(mac->netdev->dev_addr, addr, ETH_ALEN);
	spin_unlock_irq(&mac->lock);

	r = zd_read_regdomain(chip, &default_regdomain);
	if (r)
@@ -167,14 +172,25 @@ int zd_mac_open(struct net_device *netdev)
{
	struct zd_mac *mac = zd_netdev_mac(netdev);
	struct zd_chip *chip = &mac->chip;
	struct zd_usb *usb = &chip->usb;
	int r;

	if (!usb->initialized) {
		r = zd_usb_init_hw(usb);
		if (r)
			goto out;
	}

	tasklet_enable(&mac->rx_tasklet);

	r = zd_chip_enable_int(chip);
	if (r < 0)
		goto out;

	r = zd_write_mac_addr(chip, netdev->dev_addr);
	if (r)
		goto disable_int;

	r = zd_chip_set_basic_rates(chip, CR_RATES_80211B | CR_RATES_80211G);
	if (r < 0)
		goto disable_int;
@@ -254,9 +270,11 @@ int zd_mac_set_mac_address(struct net_device *netdev, void *p)
	dev_dbg_f(zd_mac_dev(mac),
		  "Setting MAC to " MAC_FMT "\n", MAC_ARG(addr->sa_data));

	if (netdev->flags & IFF_UP) {
		r = zd_write_mac_addr(chip, addr->sa_data);
		if (r)
			return r;
	}

	spin_lock_irqsave(&mac->lock, flags);
	memcpy(netdev->dev_addr, addr->sa_data, ETH_ALEN);
@@ -858,7 +876,7 @@ static int fill_ctrlset(struct zd_mac *mac,
	/* ZD1211B: Computing the length difference this way, gives us
	 * flexibility to compute the packet length.
	 */
	cs->packet_length = cpu_to_le16(mac->chip.is_zd1211b ?
	cs->packet_length = cpu_to_le16(zd_chip_is_zd1211b(&mac->chip) ?
			packet_length - frag_len : packet_length);

	/*
+2 −1
Original line number Diff line number Diff line
@@ -189,7 +189,8 @@ int zd_mac_init(struct zd_mac *mac,
		struct usb_interface *intf);
void zd_mac_clear(struct zd_mac *mac);

int zd_mac_init_hw(struct zd_mac *mac, u8 device_type);
int zd_mac_preinit_hw(struct zd_mac *mac);
int zd_mac_init_hw(struct zd_mac *mac);

int zd_mac_open(struct net_device *netdev);
int zd_mac_stop(struct net_device *netdev);
+1 −1
Original line number Diff line number Diff line
@@ -424,7 +424,7 @@ int zd_rf_init_al2230(struct zd_rf *rf)
	struct zd_chip *chip = zd_rf_to_chip(rf);

	rf->switch_radio_off = al2230_switch_radio_off;
	if (chip->is_zd1211b) {
	if (zd_chip_is_zd1211b(chip)) {
		rf->init_hw = zd1211b_al2230_init_hw;
		rf->set_channel = zd1211b_al2230_set_channel;
		rf->switch_radio_on = zd1211b_al2230_switch_radio_on;
Loading