From: Herbert Xu <herbert@gondor.apana.org.au>

This is because aic7xxx does not unregister itself properly if no devices
are found.  This patch fixes the problem.
DESC
aic7xxx-unload-fix-fix
EDESC


---

 25-akpm/drivers/scsi/aic7xxx/aic7770_osm.c |   25 ++++++++++++-------------
 25-akpm/drivers/scsi/aic7xxx/aic7xxx_osm.c |   23 +++++++++++++++++++----
 25-akpm/drivers/scsi/aic7xxx/aic7xxx_osm.h |    2 +-
 3 files changed, 32 insertions(+), 18 deletions(-)

diff -puN drivers/scsi/aic7xxx/aic7770_osm.c~aic7xxx-unload-fix drivers/scsi/aic7xxx/aic7770_osm.c
--- 25/drivers/scsi/aic7xxx/aic7770_osm.c~aic7xxx-unload-fix	2004-04-06 21:09:31.106206568 -0700
+++ 25-akpm/drivers/scsi/aic7xxx/aic7770_osm.c	2004-04-06 21:09:31.114205352 -0700
@@ -73,7 +73,7 @@ typedef void *aic7770_dev_t;
 static int aic7770_linux_config(struct aic7770_identity *entry,
 				aic7770_dev_t dev, u_int eisaBase);
 
-void
+int
 ahc_linux_eisa_init(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
@@ -82,7 +82,7 @@ ahc_linux_eisa_init(void)
 	int i;
 
 	if (aic7xxx_probe_eisa_vl == 0)
-		return;
+		return -ENODEV;
 
 	/*
 	 * Linux requires the EISA IDs to be specified in
@@ -93,7 +93,7 @@ ahc_linux_eisa_init(void)
 					 (ahc_num_aic7770_devs + 1),
 					 M_DEVBUF, M_NOWAIT);
 	if (aic7770_driver.id_table == NULL)
-		return;
+		return -ENOMEM;
 
 	for (eid = (struct eisa_device_id *)aic7770_driver.id_table,
 	     id = aic7770_ident_table, i = 0;
@@ -109,15 +109,16 @@ ahc_linux_eisa_init(void)
 	}
 	eid->sig[0] = 0;
 
-	eisa_driver_register(&aic7770_driver);
+	return eisa_driver_register(&aic7770_driver);
 #else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) */
 	struct aic7770_identity *entry;
 	u_int  slot;
 	u_int  eisaBase;
 	u_int  i;
+	int ret = -ENODEV;
 
 	if (aic7xxx_probe_eisa_vl == 0)
-		return;
+		return ret;
 
 	eisaBase = 0x1000 + AHC_EISA_SLOT_OFFSET;
 	for (slot = 1; slot < NUMSLOTS; eisaBase+=0x1000, slot++) {
@@ -146,9 +147,12 @@ ahc_linux_eisa_init(void)
 			continue;  /* no EISA card in slot */
 
 		entry = aic7770_find_device(eisa_id);
-		if (entry != NULL)
+		if (entry != NULL) {
 			aic7770_linux_config(entry, NULL, eisaBase);
+			ret = 0;
+		}
 	}
+	return ret;
 #endif
 }
 
@@ -156,13 +160,8 @@ void
 ahc_linux_eisa_exit(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-	if (aic7xxx_probe_eisa_vl == 0)
-		return;
-
-	if (aic7770_driver.id_table != NULL) {
-		eisa_driver_unregister(&aic7770_driver);
-		free(aic7770_driver.id_table, M_DEVBUF);
-	}
+	eisa_driver_unregister(&aic7770_driver);
+	free(aic7770_driver.id_table, M_DEVBUF);
 #endif
 }
 
diff -puN drivers/scsi/aic7xxx/aic7xxx_osm.c~aic7xxx-unload-fix drivers/scsi/aic7xxx/aic7xxx_osm.c
--- 25/drivers/scsi/aic7xxx/aic7xxx_osm.c~aic7xxx-unload-fix	2004-04-06 21:09:31.108206264 -0700
+++ 25-akpm/drivers/scsi/aic7xxx/aic7xxx_osm.c	2004-04-06 21:09:31.118204744 -0700
@@ -892,18 +892,25 @@ ahc_linux_detect(Scsi_Host_Template *tem
 	ahc_list_lockinit();
 
 #ifdef CONFIG_PCI
-	ahc_linux_pci_init();
+	found = ahc_linux_pci_init();
+	if (found)
+		goto out;
 #endif
 
 #ifdef CONFIG_EISA
-	ahc_linux_eisa_init();
+	found = ahc_linux_eisa_init();
+	if (found) {
+#ifdef CONFIG_PCI
+		ahc_linux_pci_exit();
+#endif
+		goto out;
+	}
 #endif
 
 	/*
 	 * Register with the SCSI layer all
 	 * controllers we've found.
 	 */
-	found = 0;
 	TAILQ_FOREACH(ahc, &ahc_tailq, links) {
 
 		if (ahc_linux_register_host(ahc, template) == 0)
@@ -913,6 +920,8 @@ ahc_linux_detect(Scsi_Host_Template *tem
 	spin_lock_irq(&io_request_lock);
 #endif
 	aic7xxx_detect_complete++;
+
+out:
 	return (found);
 }
 
@@ -5073,11 +5082,17 @@ ahc_platform_dump_card_state(struct ahc_
 	}
 }
 
+static void __exit ahc_linux_exit(void);
+
 static int __init
 ahc_linux_init(void)
 {
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-	return (ahc_linux_detect(&aic7xxx_driver_template) ? 0 : -ENODEV);
+	int rc = ahc_linux_detect(&aic7xxx_driver_template);
+	if (rc)
+		return rc;
+	ahc_linux_exit();
+	return -ENODEV;
 #else
 	scsi_register_module(MODULE_SCSI_HA, &aic7xxx_driver_template);
 	if (aic7xxx_driver_template.present == 0) {
diff -puN drivers/scsi/aic7xxx/aic7xxx_osm.h~aic7xxx-unload-fix drivers/scsi/aic7xxx/aic7xxx_osm.h
--- 25/drivers/scsi/aic7xxx/aic7xxx_osm.h~aic7xxx-unload-fix	2004-04-06 21:09:31.110205960 -0700
+++ 25-akpm/drivers/scsi/aic7xxx/aic7xxx_osm.h	2004-04-06 21:09:31.119204592 -0700
@@ -840,7 +840,7 @@ typedef enum
 
 #ifdef CONFIG_EISA
 extern uint32_t aic7xxx_probe_eisa_vl;
-void			 ahc_linux_eisa_init(void);
+int			 ahc_linux_eisa_init(void);
 void			 ahc_linux_eisa_exit(void);
 int			 aic7770_map_registers(struct ahc_softc *ahc,
 					       u_int port);

_