Subject: [PATCH 31/32] rt2x00: Correctly initialization and uninitialization of device

From: Ivo van Doorn <[email-masked]>
Date: Fri, 28 Apr 2006 00:03:20 +0200

From: Ivo van Doorn <[email-masked]>

Fix several hardware initialization and uninitalization
problems by incorrectly flushing workqueues.
Fix the memory leak when freeing the ieee80211_hw structure.
Allow device to connect to 802.11g networks by default
instead of 802.11b.

Signed-off-by: Ivo van Doorn <[email-masked]>

diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 22:05:21.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2400pci.c	2006-04-27 22:05:52.000000000 +0200
@@ -1719,6 +1719,11 @@ rt2400pci_stop(struct net_device *net_de
 	}
 
 	/*
+	 * Flush out all pending work.
+	 */
+	flush_workqueue(rt2x00pci->workqueue);
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2400pci_free_rings(rt2x00pci);
@@ -2441,7 +2446,7 @@ rt2400pci_initialize(struct pci_dev *pci
 
 	rt2x00pci->workqueue = create_singlethread_workqueue(DRV_NAME);
 	if (!rt2x00pci->workqueue)
-		goto exit_iounmap;
+		goto exit;
 
 	/*
 	 * Initialize cofniguration work.
@@ -2457,21 +2462,25 @@ rt2400pci_initialize(struct pci_dev *pci
 	if (rt2400pci_init_eeprom(rt2x00pci)
 	|| rt2400pci_init_mac(rt2x00pci)
 	|| rt2400pci_init_hw(rt2x00pci))
-		goto exit_destroy_workqueue;
+		goto exit;
 
 	/*
 	 * If required start hardware button polling.
 	 */
 	rt2x00pci_button_start(rt2x00pci, rt2400pci_button_poll);
 
-	return 0;
+	/*
+	 * Register hardware.
+	 */
+	if (ieee80211_register_hw(net_dev, &rt2x00pci->hw)) {
+		ERROR("Failed to register device.\n");
+		goto exit;
+	}
 
-exit_destroy_workqueue:
-	destroy_workqueue(rt2x00pci->workqueue);
+	return 0;
 
-exit_iounmap:
-	iounmap(rt2x00pci->csr_addr);
-	rt2x00pci->csr_addr = NULL;
+exit:
+	rt2400pci_uninitialize(net_dev);
 
 	return -ENODEV;
 }
@@ -2482,26 +2491,39 @@ rt2400pci_uninitialize(struct net_device
 	struct rt2x00_pci	*rt2x00pci = ieee80211_dev_hw_data(net_dev);
 
 	/*
+	 * Unregister hardware.
+	 */
+	ieee80211_unregister_hw(net_dev);
+
+	/*
 	 * Shutdown poll_timer for hardware button.
 	 */
 	rt2x00pci_button_stop(rt2x00pci);
 
 	kfree(rt2x00pci->eeprom);
 
+	/*
+	 * Release CSR memory.
+	 */
 	if (likely(rt2x00pci->csr_addr)) {
 		iounmap(rt2x00pci->csr_addr);
 		rt2x00pci->csr_addr = NULL;
 	}
 
+	/*
+	 * Free workqueue.
+	 */
 	if (likely(rt2x00pci->workqueue)) {
-		flush_workqueue(rt2x00pci->workqueue);
 		destroy_workqueue(rt2x00pci->workqueue);
 		rt2x00pci->workqueue = NULL;
 	}
 
+	/*
+	 * Free ieee80211_hw memory.
+	 */
 	if (likely(rt2x00pci->hw.modes)) {
-			kfree(rt2x00pci->hw.modes->channels);
-			kfree(rt2x00pci->hw.modes->rates);
+		kfree(rt2x00pci->hw.modes->channels);
+		kfree(rt2x00pci->hw.modes->rates);
 		kfree(rt2x00pci->hw.modes);
 		rt2x00pci->hw.modes = NULL;
 	}
@@ -2514,7 +2536,6 @@ static int
 rt2400pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 {
 	struct net_device	*net_dev;
-	struct rt2x00_pci	*rt2x00pci;
 	int			status;
 
 	if (unlikely(id->driver_data != RT2460)) {
@@ -2559,19 +2580,9 @@ rt2400pci_probe(struct pci_dev *pci_dev,
 		ERROR("Failed to initialize device.\n");
 		goto exit_free_device;
 	}
-	rt2x00pci = ieee80211_dev_hw_data(net_dev);
-
-	status = ieee80211_register_hw(net_dev, &rt2x00pci->hw);
-	if (status) {
-		ERROR("Failed to register device.\n");
-		goto exit_uninitialize_device;
-	}
 
 	return 0;
 
-exit_uninitialize_device:
-	rt2400pci_uninitialize(net_dev);
-
 exit_free_device:
 	ieee80211_free_hw(net_dev);
 
@@ -2592,7 +2603,7 @@ rt2400pci_remove(struct pci_dev *pci_dev
 {
 	struct net_device	*net_dev = pci_get_drvdata(pci_dev);
 
-	ieee80211_unregister_hw(net_dev);
+	net_dev->stop(net_dev);
 
 	rt2400pci_uninitialize(net_dev);
 
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 22:05:21.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500pci.c	2006-04-27 22:05:52.000000000 +0200
@@ -1845,6 +1845,11 @@ rt2500pci_stop(struct net_device *net_de
 	}
 
 	/*
+	 * Flush out all pending work.
+	 */
+	flush_workqueue(rt2x00pci->workqueue);
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2500pci_free_rings(rt2x00pci);
@@ -2675,22 +2680,22 @@ rt2500pci_init_hw(struct rt2x00_pci *rt2
 	}
 
 	/*
-	 * Intitialize 802.11b
-	 * Rates: CCK.
+	 * Intitialize 802.11g
+	 * Rates: CCK, OFDM.
 	 * Channels: OFDM.
 	 */
-	hw->modes[0].mode = MODE_IEEE80211B;
+	hw->modes[0].mode = MODE_IEEE80211G;
 	hw->modes[0].num_channels = 14;
-	hw->modes[0].num_rates = 4;
+	hw->modes[0].num_rates = 12;
 
 	/*
-	 * Intitialize 802.11g
-	 * Rates: CCK, OFDM.
+	 * Intitialize 802.11b
+	 * Rates: CCK.
 	 * Channels: OFDM.
 	 */
-	hw->modes[1].mode = MODE_IEEE80211G;
+	hw->modes[1].mode = MODE_IEEE80211B;
 	hw->modes[1].num_channels = 14;
-	hw->modes[1].num_rates = 12;
+	hw->modes[1].num_rates = 4;
 	hw->modes[1].channels = hw->modes[0].channels;
 	hw->modes[1].rates = hw->modes[0].rates;
 
@@ -2747,7 +2752,7 @@ rt2500pci_initialize(struct pci_dev *pci
 
 	rt2x00pci->workqueue = create_singlethread_workqueue(DRV_NAME);
 	if (!rt2x00pci->workqueue)
-		goto exit_iounmap;
+		goto exit;
 
 	/*
 	 * Initialize cofniguration work.
@@ -2763,21 +2768,25 @@ rt2500pci_initialize(struct pci_dev *pci
 	if (rt2500pci_init_eeprom(rt2x00pci)
 	|| rt2500pci_init_mac(rt2x00pci)
 	|| rt2500pci_init_hw(rt2x00pci))
-		goto exit_destroy_workqueue;
+		goto exit;
 
 	/*
 	 * If required start hardware button polling.
 	 */
 	rt2x00pci_button_start(rt2x00pci, rt2500pci_button_poll);
 
-	return 0;
+	/*
+	 * Register hardware.
+	 */
+	if (ieee80211_register_hw(net_dev, &rt2x00pci->hw)) {
+		ERROR("Failed to register device.\n");
+		goto exit;
+	}
 
-exit_destroy_workqueue:
-	destroy_workqueue(rt2x00pci->workqueue);
+	return 0;
 
-exit_iounmap:
-	iounmap(rt2x00pci->csr_addr);
-	rt2x00pci->csr_addr = NULL;
+exit:
+	rt2500pci_uninitialize(net_dev);
 
 	return -ENODEV;
 }
@@ -2788,25 +2797,42 @@ rt2500pci_uninitialize(struct net_device
 	struct rt2x00_pci	*rt2x00pci = ieee80211_dev_hw_data(net_dev);
 
 	/*
+	 * Unregister hardware.
+	 */
+	ieee80211_unregister_hw(net_dev);
+
+	/*
 	 * Shutdown poll_timer for hardware button.
 	 */
 	rt2x00pci_button_stop(rt2x00pci);
 
 	kfree(rt2x00pci->eeprom);
 
+	/*
+	 * Release CSR memory.
+	 */
 	if (likely(rt2x00pci->csr_addr)) {
 		iounmap(rt2x00pci->csr_addr);
 		rt2x00pci->csr_addr = NULL;
 	}
 
+	/*
+	 * Free workqueue.
+	 */
 	if (likely(rt2x00pci->workqueue)) {
-		flush_workqueue(rt2x00pci->workqueue);
 		destroy_workqueue(rt2x00pci->workqueue);
 		rt2x00pci->workqueue = NULL;
 	}
 
-	kfree(rt2x00pci->hw.modes);
-	rt2x00pci->hw.modes = NULL;
+	/*
+	 * Free ieee80211_hw memory.
+	 */
+	if (likely(rt2x00pci->hw.modes)) {
+		kfree(rt2x00pci->hw.modes->channels);
+		kfree(rt2x00pci->hw.modes->rates);
+		kfree(rt2x00pci->hw.modes);
+		rt2x00pci->hw.modes = NULL;
+	}
 }
 
 /*
@@ -2816,7 +2842,6 @@ static int
 rt2500pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *id)
 {
 	struct net_device	*net_dev;
-	struct rt2x00_pci	*rt2x00pci;
 	int			status;
 
 	if (unlikely(id->driver_data != RT2560)) {
@@ -2861,19 +2886,9 @@ rt2500pci_probe(struct pci_dev *pci_dev,
 		ERROR("Failed to initialize device.\n");
 		goto exit_free_device;
 	}
-	rt2x00pci = ieee80211_dev_hw_data(net_dev);
-
-	status = ieee80211_register_hw(net_dev, &rt2x00pci->hw);
-	if (status) {
-		ERROR("Failed to register device.\n");
-		goto exit_uninitialize_device;
-	}
 
 	return 0;
 
-exit_uninitialize_device:
-	rt2500pci_uninitialize(net_dev);
-
 exit_free_device:
 	ieee80211_free_hw(net_dev);
 
@@ -2894,7 +2909,7 @@ rt2500pci_remove(struct pci_dev *pci_dev
 {
 	struct net_device	*net_dev = pci_get_drvdata(pci_dev);
 
-	ieee80211_unregister_hw(net_dev);
+	net_dev->stop(net_dev);
 
 	rt2500pci_uninitialize(net_dev);
 
diff -uprN wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c
--- wireless-dev-rt2x00/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 22:05:21.000000000 +0200
+++ wireless-dev-rt2x00-patch/drivers/net/wireless/d80211/rt2x00/rt2500usb.c	2006-04-27 22:05:52.000000000 +0200
@@ -1480,8 +1480,6 @@ static int
 rt2500usb_stop(struct net_device *net_dev)
 {
 	struct rt2x00_usb	*rt2x00usb = ieee80211_dev_hw_data(net_dev);
-	struct data_entry	*entry;
-	int			counter;
 	u16			reg;
 
 	CLEAR_FLAG(rt2x00usb, RADIO_ENABLED);
@@ -1498,11 +1496,6 @@ rt2500usb_stop(struct net_device *net_de
 	 */
 	rt2500usb_disable_led(rt2x00usb);
 
-	entry = (struct data_entry*)rt2x00usb->ring[RING_RX].entry;
-	for (counter = 0; counter < rt2x00usb->ring[RING_RX].stats.limit; counter++) {
-		usb_kill_urb(entry[counter].urb);
-	}
-
 	rt2x00_vendor_request(rt2x00usb, USB_RX_CONTROL,
 		USB_VENDOR_REQUEST_OUT, 0x00, 0x00, NULL, 0);
 
@@ -1518,6 +1511,11 @@ rt2500usb_stop(struct net_device *net_de
 	}
 
 	/*
+	 * Flush out all pending work.
+	 */
+	flush_workqueue(rt2x00usb->workqueue);
+
+	/*
 	 * Free DMA rings.
 	 */
 	rt2500usb_free_rings(rt2x00usb);
@@ -2296,22 +2294,23 @@ rt2500usb_init_hw(struct rt2x00_usb *rt2
 	}
 
 	/*
-	 * Intitialize 802.11b
-	 * Rates: CCK.
+	 * Intitialize 802.11g
+	 * Rates: CCK, OFDM.
 	 * Channels: OFDM.
 	 */
-	hw->modes[0].mode = MODE_IEEE80211B;
+	hw->modes[0].mode = MODE_IEEE80211G;
 	hw->modes[0].num_channels = 14;
-	hw->modes[0].num_rates = 4;
+	hw->modes[0].num_rates = 12;
+
 
 	/*
-	 * Intitialize 802.11g
-	 * Rates: CCK, OFDM.
+	 * Intitialize 802.11b
+	 * Rates: CCK.
 	 * Channels: OFDM.
 	 */
-	hw->modes[1].mode = MODE_IEEE80211G;
+	hw->modes[1].mode = MODE_IEEE80211B;
 	hw->modes[1].num_channels = 14;
-	hw->modes[1].num_rates = 12;
+	hw->modes[1].num_rates = 4;
 	hw->modes[1].channels = hw->modes[0].channels;
 	hw->modes[1].rates = hw->modes[0].rates;
 
@@ -2376,12 +2375,23 @@ rt2500usb_initialize(struct usb_interfac
 
 	if (rt2500usb_init_eeprom(rt2x00usb)
 	|| rt2500usb_init_mac(rt2x00usb)
-	|| rt2500usb_init_hw(rt2x00usb)) {
-		destroy_workqueue(rt2x00usb->workqueue);
-		return -ENODEV;
+	|| rt2500usb_init_hw(rt2x00usb))
+		goto exit;
+
+	/*
+	 * Register hardware.
+	 */
+	if (ieee80211_register_hw(net_dev, &rt2x00usb->hw)) {
+		ERROR("Failed to register device.\n");
+		goto exit;
 	}
 
 	return 0;
+
+exit:
+	rt2500usb_uninitialize(net_dev);
+
+	return -ENODEV;
 }
 
 static void
@@ -2389,16 +2399,30 @@ rt2500usb_uninitialize(struct net_device
 {
 	struct rt2x00_usb	*rt2x00usb = ieee80211_dev_hw_data(net_dev);
 
+	/*
+	 * Unregister hardware.
+	 */
+	ieee80211_unregister_hw(net_dev);
+
 	kfree(rt2x00usb->eeprom);
 
+	/*
+	 * Free workqueue.
+	 */
 	if (likely(rt2x00usb->workqueue)) {
-		flush_workqueue(rt2x00usb->workqueue);
 		destroy_workqueue(rt2x00usb->workqueue);
 		rt2x00usb->workqueue = NULL;
 	}
 
-	kfree(rt2x00usb->hw.modes);
-	rt2x00usb->hw.modes = NULL;
+	/*
+	 * Free ieee80211_hw memory.
+	 */
+	if (likely(rt2x00usb->hw.modes)) {
+		kfree(rt2x00usb->hw.modes->channels);
+		kfree(rt2x00usb->hw.modes->rates);
+		kfree(rt2x00usb->hw.modes);
+		rt2x00usb->hw.modes = NULL;
+	}
 }
 
 /*
@@ -2409,7 +2433,6 @@ rt2500usb_probe(struct usb_interface *us
 {
 	struct usb_device	*usb_dev = interface_to_usbdev(usb_intf);
 	struct net_device	*net_dev;
-	struct rt2x00_usb	*rt2x00usb;
 	int			status;
 
 	if (unlikely(id->driver_info != RT2570)) {
@@ -2433,19 +2456,9 @@ rt2500usb_probe(struct usb_interface *us
 		ERROR("Failed to initialize device.\n");
 		goto exit_free_device;
 	}
-	rt2x00usb = ieee80211_dev_hw_data(net_dev);
-
-	status = ieee80211_register_hw(net_dev, &rt2x00usb->hw);
-	if (status) {
-		ERROR("Failed to register device.\n");
-		goto exit_uninitialize_device;
-	}
 
 	return 0;
 
-exit_uninitialize_device:
-	rt2500usb_uninitialize(net_dev);
-
 exit_free_device:
 	ieee80211_free_hw(net_dev);
 
@@ -2460,7 +2473,7 @@ rt2500usb_disconnect(struct usb_interfac
 {
 	struct net_device	*net_dev = usb_get_intfdata(usb_intf);
 
-	ieee80211_unregister_hw(net_dev);
+	net_dev->stop(net_dev);
 
 	rt2500usb_uninitialize(net_dev);