Patchwork + RFC: Use shutdown callback mechanism to shutdown programmers

login
register
about
Submitter David Hendricks
Date 2011-06-13 22:45:13
Message ID <BANLkTi=EE3tN8bfA+k+GS==NFq7TtGZYEBne9W1ivzxyMqvD9w@mail.gmail.com>
Download mbox | patch
Permalink /patch/3128/
State Accepted
Headers show

Comments

David Hendricks - 2011-06-13 22:45:13
Thanks again for the additional comments. I made the changes (comments
below) and re-compiled with the extra programmers enabled.

New version of the patch attached.
Signed-off-by: David Hendricks <dhendrix@google.com>

On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> Am 10.06.2011 23:55 schrieb David Hendricks:
> > As always, thanks for the thorough review! Comments in-line, and a
> revised
> > patch is attached.
> >
> > New patch is:
> > Signed-off-by: David Hendricks <dhendrix@google.com>
> >
>
> Thanks, looks good. I found one compile issue:
> nicnatsemi.c:61:24: error: incompatible pointer types passing 'int
> (void)' to parameter of type 'int (*)(void *)'
>        if (register_shutdown(nicnatsemi_shutdown, NULL))
>                              ^~~~~~~~~~~~~~~~~~~
> In file included from nicnatsemi.c:24:
> ./flash.h:43:29: note: passing argument to parameter 'function' here
> int register_shutdown(int (*function) (void *data), void *data);
>                            ^
> 1 error generated.
> make: *** [nicnatsemi.o] Fehler 1
>
>
> The compilation test was run like this:
> make CONFIG_ATAHPT=yes CONFIG_NICNATSEMI=yes CONFIG_DEDIPROG=yes
> CONFIG_PRINT_WIKI=yes
>

eek--i forgot there are targets being changed that are not included in the
compilation by default. Basically I just forgot to update the
nicnatsemi_shutdown() function to take a void *. This was a change from the
second iteration, IIRC.

There was also a typo in dediprog.c that caused a compilation failure on my
machine (maybe only a warning on others).

On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger wrote:
> >
> >> To be honest, I don't know serprog well enough to say something useful
> >> about it. And the massive code move in serprog makes review a bit hard.
> >>
> > Yeah, that one was quite a bit more painful than the others. Maybe I
> re-do
> > serprog using a forward declaration for serprog_shutdown() and leave the
> > rest as is? We can remove the forward declaration and move the code in a
> > follow-up patch to reduce diffs in this one.
> >
>
> Good idea.
>

Done. We now see that the changes to serprog.c are almost trivial. The
insertion of register_shutdown() is done after some command parsing. The
shutdown function is pretty simple; I don't think there's much to think
about in terms of ordering dependency.

On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> Other comments:
> drkaiser and satasii are missing physunmap in shutdown.
>

Fixed.

On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> nicintel_spi has the register_shutdown too early.
> nicintel will not unmap the nicintel_bar if mapping nicintel_control_bar
> fails (error case handling needs a second label which unmaps nicintel_bar).
>

Good catch -- I moved the register_shutdown call toward the bottom of the
function, but before the bitbang_spi_init call so that failure in
bitbang_spi_init will not prevent nicintel_spi_shutdown from being called.
Carl-Daniel Hailfinger - 2011-06-14 00:13:54
Am 14.06.2011 00:45 schrieb David Hendricks:
> Thanks again for the additional comments. I made the changes (comments
> below) and re-compiled with the extra programmers enabled.
>
> New version of the patch attached.
> Signed-off-by: David Hendricks <dhendrix@google.com>
>   

Thanks, looks really good.


> On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger wrote:
>   
>> nicintel_spi has the register_shutdown too early.
>> nicintel will not unmap the nicintel_bar if mapping nicintel_control_bar
>> fails (error case handling needs a second label which unmaps nicintel_bar).
>>     
> Good catch -- I moved the register_shutdown call toward the bottom of the
> function, but before the bitbang_spi_init call so that failure in
> bitbang_spi_init will not prevent nicintel_spi_shutdown from being called.
>   

Not exactly what I meant. I'll try to explain it better with a diff on
top of your current diff. Apply that, and we're good to go:

> --- nicintel.c~	2011-06-14 01:58:17.000000000 +0200
> +++ nicintel.c	2011-06-14 01:59:57.000000000 +0200
> @@ -77,7 +77,7 @@
>  	nicintel_control_bar = physmap("Intel NIC control/status reg",
>  	                               addr, NICINTEL_CONTROL_MEMMAP_SIZE);
>  	if (nicintel_control_bar == ERROR_PTR)
> -		goto error_out;
> +		goto error_out_unmap;
>  
>  	if (register_shutdown(nicintel_shutdown, NULL))
>  		return 1;
> @@ -99,6 +99,8 @@
>  
>  	return 0;
>  
> +error_out_unmap:
> +	physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE);
>  error_out:
>  	pci_cleanup(pacc);
>  	release_io_perms();
>   


One comment about satasii.c:
> Index: satasii.c
> ===================================================================
> --- satasii.c	(revision 1327)
> +++ satasii.c	(working copy)
> @@ -59,7 +69,8 @@
>  		reg_offset = 0x50;
>  	}
>  
> -	sii_bar = physmap("SATA SIL registers", addr, 0x100) + reg_offset;
> +	sii_bar = reg_offset +
> +	          physmap("SATA SIL registers", addr, SATASII_MEMMAP_SIZE);
>   

Could you keep the old ordering? AFAIK the usual way to specify a
location is base+offset, not the other way round.


> Index: flashrom.c
> ===================================================================
> --- flashrom.c	(revision 1327)
> +++ flashrom.c	(working copy)
> @@ -543,13 +525,15 @@
>  
>  int programmer_shutdown(void)
>  {
> +	int rc = 0;
>   

int ret please.


> +
>  	/* Registering shutdown functions is no longer allowed. */
>  	may_register_shutdown = 0;
>  	while (shutdown_fn_count > 0) {
>  		int i = --shutdown_fn_count;
> -		shutdown_fn[i].func(shutdown_fn[i].data);
> +		rc |= shutdown_fn[i].func(shutdown_fn[i].data);
>  	}
> -	return programmer_table[programmer].shutdown();
> +	return rc;
>  }
>  
>  void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
>   


With those minor changes, the patch is
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>

Please go ahead and commit.

Regards,
Carl-Daniel
David Hendricks - 2011-06-14 01:35:58
On Mon, Jun 13, 2011 at 5:13 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> Am 14.06.2011 00:45 schrieb David Hendricks:
> > Thanks again for the additional comments. I made the changes (comments
> > below) and re-compiled with the extra programmers enabled.
> >
> > New version of the patch attached.
> > Signed-off-by: David Hendricks <dhendrix@google.com>
> >
>
> Thanks, looks really good.
>
>
> > On Sat, Jun 11, 2011 at 5:09 AM, Carl-Daniel Hailfinger wrote:
> >
> >> nicintel_spi has the register_shutdown too early.
> >> nicintel will not unmap the nicintel_bar if mapping nicintel_control_bar
> >> fails (error case handling needs a second label which unmaps
> nicintel_bar).
> >>
> > Good catch -- I moved the register_shutdown call toward the bottom of the
> > function, but before the bitbang_spi_init call so that failure in
> > bitbang_spi_init will not prevent nicintel_spi_shutdown from being
> called.
> >
>
> Not exactly what I meant. I'll try to explain it better with a diff on
> top of your current diff. Apply that, and we're good to go:
>
> > --- nicintel.c~       2011-06-14 01:58:17.000000000 +0200
> > +++ nicintel.c        2011-06-14 01:59:57.000000000 +0200
> > @@ -77,7 +77,7 @@
> >       nicintel_control_bar = physmap("Intel NIC control/status reg",
> >                                      addr, NICINTEL_CONTROL_MEMMAP_SIZE);
> >       if (nicintel_control_bar == ERROR_PTR)
> > -             goto error_out;
> > +             goto error_out_unmap;
> >
> >       if (register_shutdown(nicintel_shutdown, NULL))
> >               return 1;
> > @@ -99,6 +99,8 @@
> >
> >       return 0;
> >
> > +error_out_unmap:
> > +     physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE);
> >  error_out:
> >       pci_cleanup(pacc);
> >       release_io_perms();
> >
>

D'oh, I should've read your comment more closely. I've added your changes.


>
>
> One comment about satasii.c:
> > Index: satasii.c
> > ===================================================================
> > --- satasii.c (revision 1327)
> > +++ satasii.c (working copy)
> > @@ -59,7 +69,8 @@
> >               reg_offset = 0x50;
> >       }
> >
> > -     sii_bar = physmap("SATA SIL registers", addr, 0x100) + reg_offset;
> > +     sii_bar = reg_offset +
> > +               physmap("SATA SIL registers", addr, SATASII_MEMMAP_SIZE);
> >
>
> Could you keep the old ordering? AFAIK the usual way to specify a
> location is base+offset, not the other way round.
>

Done.


>
>
> > Index: flashrom.c
> > ===================================================================
> > --- flashrom.c        (revision 1327)
> > +++ flashrom.c        (working copy)
> > @@ -543,13 +525,15 @@
> >
> >  int programmer_shutdown(void)
> >  {
> > +     int rc = 0;
> >
>
> int ret please.
>

Done.


>
>
> > +
> >       /* Registering shutdown functions is no longer allowed. */
> >       may_register_shutdown = 0;
> >       while (shutdown_fn_count > 0) {
> >               int i = --shutdown_fn_count;
> > -             shutdown_fn[i].func(shutdown_fn[i].data);
> > +             rc |= shutdown_fn[i].func(shutdown_fn[i].data);
> >       }
> > -     return programmer_table[programmer].shutdown();
> > +     return rc;
> >  }
> >
> >  void *programmer_map_flash_region(const char *descr, unsigned long
> phys_addr,
> >
>
>
> With those minor changes, the patch is
> Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
>
> Please go ahead and commit.
>

Thanks, committed in r1338.
Carl-Daniel Hailfinger - 2011-06-14 02:17:18
Am 14.06.2011 03:35 schrieb David Hendricks:
> On Mon, Jun 13, 2011 at 5:13 PM, Carl-Daniel Hailfinger wrote:
>   
>> Am 14.06.2011 00:45 schrieb David Hendricks:
>>     
>>> Thanks again for the additional comments. I made the changes (comments
>>> below) and re-compiled with the extra programmers enabled.
>>>
>>> New version of the patch attached.
>>> Signed-off-by: David Hendricks <dhendrix@google.com>
>>>       
>> With those minor changes, the patch is
>> Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
>>
>> Please go ahead and commit.
>>     
> Thanks, committed in r1338.
>   

I am really happy to have this patch merged.
Now let's hope we won't see any fallout.

Regards,
Carl-Daniel

Patch

Index: ogp_spi.c
===================================================================
--- ogp_spi.c	(revision 1327)
+++ ogp_spi.c	(working copy)
@@ -93,6 +93,15 @@ 
 	.release_bus = ogp_release_spibus,
 };
 
+static int ogp_spi_shutdown(void *data)
+{
+	physunmap(ogp_spibar, 4096);
+	pci_cleanup(pacc);
+	release_io_perms();
+
+	return 0;
+}
+
 int ogp_spi_init(void)
 {
 	char *type;
@@ -124,18 +133,12 @@ 
 
 	ogp_spibar = physmap("OGP registers", io_base_addr, 4096);
 
+	if (register_shutdown(ogp_spi_shutdown, NULL))
+		return 1;
+
 	/* no delay for now. */
 	if (bitbang_spi_init(&bitbang_spi_master_ogp, 0))
 		return 1;
 
 	return 0;
 }
-
-int ogp_spi_shutdown(void)
-{
-	physunmap(ogp_spibar, 4096);
-	pci_cleanup(pacc);
-	release_io_perms();
-
-	return 0;
-}
Index: hwaccess.c
===================================================================
--- hwaccess.c	(revision 1327)
+++ hwaccess.c	(working copy)
@@ -202,7 +202,7 @@ 
 	};
 };
 
-void undo_mmio_write(void *p)
+int undo_mmio_write(void *p)
 {
 	struct undo_mmio_write_data *data = p;
 	msg_pdbg("Restoring MMIO space at %p\n", data->addr);
@@ -219,6 +219,7 @@ 
 	}
 	/* p was allocated in register_undo_mmio_write. */
 	free(p);
+	return 0;
 }
 
 #define register_undo_mmio_write(a, c)					\
Index: flash.h
===================================================================
--- flash.h	(revision 1327)
+++ flash.h	(working copy)
@@ -40,7 +40,7 @@ 
 
 typedef unsigned long chipaddr;
 
-int register_shutdown(void (*function) (void *data), void *data);
+int register_shutdown(int (*function) (void *data), void *data);
 void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
 				  size_t len);
 void programmer_unmap_flash_region(void *virt_addr, size_t len);
Index: drkaiser.c
===================================================================
--- drkaiser.c	(revision 1327)
+++ drkaiser.c	(working copy)
@@ -27,6 +27,8 @@ 
 #define PCI_MAGIC_DRKAISER_ADDR		0x50
 #define PCI_MAGIC_DRKAISER_VALUE	0xa971
 
+#define DRKAISER_MEMMAP_SIZE           (1024 * 128)
+
 /* Mask to restrict flash accesses to the 128kB memory window. */
 #define DRKAISER_MEMMAP_MASK		((1 << 17) - 1)
 
@@ -37,6 +39,15 @@ 
 
 static uint8_t *drkaiser_bar;
 
+static int drkaiser_shutdown(void *data)
+{
+	physunmap(drkaiser_bar, DRKAISER_MEMMAP_SIZE);
+	/* Flash write is disabled automatically by PCI restore. */
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+};
+
 int drkaiser_init(void)
 {
 	uint32_t addr;
@@ -51,21 +62,15 @@ 
 
 	/* Map 128kB flash memory window. */
 	drkaiser_bar = physmap("Dr. Kaiser PC-Waechter flash memory",
-			       addr, 128 * 1024);
+			       addr, DRKAISER_MEMMAP_SIZE);
 
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 
+	if (register_shutdown(drkaiser_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int drkaiser_shutdown(void)
-{
-	/* Flash write is disabled automatically by PCI restore. */
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-};
-
 void drkaiser_chip_writeb(uint8_t val, chipaddr addr)
 {
 	pci_mmio_writeb(val, drkaiser_bar + (addr & DRKAISER_MEMMAP_MASK));
Index: pcidev.c
===================================================================
--- pcidev.c	(revision 1327)
+++ pcidev.c	(working copy)
@@ -270,7 +270,7 @@ 
 	};
 };
 
-void undo_pci_write(void *p)
+int undo_pci_write(void *p)
 {
 	struct undo_pci_write_data *data = p;
 	msg_pdbg("Restoring PCI config space for %02x:%02x:%01x reg 0x%02x\n",
@@ -288,6 +288,7 @@ 
 	}
 	/* p was allocated in register_undo_pci_write. */
 	free(p);
+	return 0;
 }
 
 #define register_undo_pci_write(a, b, c) 				\
Index: gfxnvidia.c
===================================================================
--- gfxnvidia.c	(revision 1327)
+++ gfxnvidia.c	(working copy)
@@ -29,6 +29,7 @@ 
  * FIXME: Is this size a one-fits-all or card dependent?
  */
 #define GFXNVIDIA_MEMMAP_MASK		((1 << 17) - 1)
+#define GFXNVIDIA_MEMMAP_SIZE		(16 * 1024 * 1024)
 
 uint8_t *nvidia_bar;
 
@@ -60,6 +61,17 @@ 
 	{},
 };
 
+static int gfxnvidia_shutdown(void *data)
+{
+	physunmap(nvidia_bar, GFXNVIDIA_MEMMAP_SIZE);
+	/* Flash interface access is disabled (and screen enabled) automatically
+	 * by PCI restore.
+	 */
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int gfxnvidia_init(void)
 {
 	uint32_t reg32;
@@ -71,13 +83,17 @@ 
 	io_base_addr += 0x300000;
 	msg_pinfo("Detected NVIDIA I/O base address: 0x%x.\n", io_base_addr);
 
+	nvidia_bar = physmap("NVIDIA", io_base_addr, GFXNVIDIA_MEMMAP_SIZE);
+
+	/* must be done before rpci calls */
+	if (register_shutdown(gfxnvidia_shutdown, NULL))
+		return 1;
+
 	/* Allow access to flash interface (will disable screen). */
 	reg32 = pci_read_long(pcidev_dev, 0x50);
 	reg32 &= ~(1 << 0);
 	rpci_write_long(pcidev_dev, 0x50, reg32);
 
-	nvidia_bar = physmap("NVIDIA", io_base_addr, 16 * 1024 * 1024);
-
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 
 	/* Write/erase doesn't work. */
@@ -86,16 +102,6 @@ 
 	return 0;
 }
 
-int gfxnvidia_shutdown(void)
-{
-	/* Flash interface access is disabled (and screen enabled) automatically
-	 * by PCI restore.
-	 */
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr)
 {
 	pci_mmio_writeb(val, nvidia_bar + (addr & GFXNVIDIA_MEMMAP_MASK));
Index: serprog.c
===================================================================
--- serprog.c	(revision 1327)
+++ serprog.c	(working copy)
@@ -39,6 +39,13 @@ 
 
 #define MSGHEADER "serprog:"
 
+/*
+ * FIXME: This prototype was added to help reduce diffs for the shutdown
+ * registration patch, which shifted many lines of code to place
+ * serprog_shutdown() before serprog_init(). It should be removed soon.
+ */
+static int serprog_shutdown(void *data);
+
 #define S_ACK 0x06
 #define S_NAK 0x15
 #define S_CMD_NOP		0x00	/* No operation                                 */
@@ -373,6 +380,9 @@ 
 		return 1;
 	}
 
+	if (register_shutdown(serprog_shutdown, NULL))
+		return 1;
+
 	msg_pdbg(MSGHEADER "connected - attempting to synchronize\n");
 
 	sp_check_avail_automatic = 0;
@@ -555,7 +565,7 @@ 
 	sp_flush_stream();
 }
 
-int serprog_shutdown(void)
+static int serprog_shutdown(void *data)
 {
 	msg_pspew("%s\n", __func__);
 	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
Index: nicrealtek.c
===================================================================
--- nicrealtek.c	(revision 1327)
+++ nicrealtek.c	(working copy)
@@ -36,6 +36,14 @@ 
 	{},
 };
 
+static int nicrealtek_shutdown(void *data)
+{
+	/* FIXME: We forgot to disable software access again. */
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int nicrealtek_init(void)
 {
 	get_io_perms();
@@ -44,17 +52,11 @@ 
 
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 
+	if (register_shutdown(nicrealtek_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int nicrealtek_shutdown(void)
-{
-	/* FIXME: We forgot to disable software access again. */
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void nicrealtek_chip_writeb(uint8_t val, chipaddr addr)
 {
 	/* Output addr and data, set WE to 0, set OE to 1, set CS to 0,
Index: satamv.c
===================================================================
--- satamv.c	(revision 1327)
+++ satamv.c	(working copy)
@@ -40,6 +40,14 @@ 
 #define PCI_BAR2_CONTROL		0x00c08
 #define GPIO_PORT_CONTROL		0x104f0
 
+static int satamv_shutdown(void *data)
+{
+	physunmap(mv_bar, 0x20000);
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 /*
  * Random notes:
  * FCE#		Flash Chip Enable
@@ -73,6 +81,9 @@ 
 	if (mv_bar == ERROR_PTR)
 		goto error_out;
 
+	if (register_shutdown(satamv_shutdown, NULL))
+		return 1;
+
 	tmp = pci_mmio_readl(mv_bar + FLASH_PARAM);
 	msg_pspew("Flash Parameters:\n");
 	msg_pspew("TurnOff=0x%01x\n", (tmp >> 0) & 0x7);
@@ -139,14 +150,6 @@ 
 	return 1;
 }
 
-int satamv_shutdown(void)
-{
-	physunmap(mv_bar, 0x20000);
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 /* BAR2 (MEM) can map NVRAM and flash. We set it to flash in the init function.
  * If BAR2 is disabled, it still can be accessed indirectly via BAR1 (I/O).
  * This code only supports indirect accesses for now.
Index: Makefile
===================================================================
--- Makefile	(revision 1327)
+++ Makefile	(working copy)
@@ -177,7 +177,8 @@ 
 CONFIG_NICREALTEK ?= yes
 
 # Disable National Semiconductor NICs until support is complete and tested.
-CONFIG_NICNATSEMI ?= no
+# FIXME: Set to "yes" for build testing shutdown patch
+CONFIG_NICNATSEMI ?= yes
 
 # Always enable Intel NICs for now.
 CONFIG_NICINTEL ?= yes
Index: dummyflasher.c
===================================================================
--- dummyflasher.c	(revision 1327)
+++ dummyflasher.c	(working copy)
@@ -73,6 +73,23 @@ 
 	.read = default_spi_read,
 	.write_256 = dummy_spi_write_256,
 };
+
+static int dummy_shutdown(void *data)
+{
+	msg_pspew("%s\n", __func__);
+#if EMULATE_CHIP
+	if (emu_chip != EMULATE_NONE) {
+		if (emu_persistent_image) {
+			msg_pdbg("Writing %s\n", emu_persistent_image);
+			write_buf_to_file(flashchip_contents, emu_chip_size,
+					  emu_persistent_image);
+		}
+		free(flashchip_contents);
+	}
+#endif
+	return 0;
+}
+
 int dummy_init(void)
 {
 	char *bustext = NULL;
@@ -126,7 +143,7 @@ 
 	if (!tmp) {
 		msg_pdbg("Not emulating any flash chip.\n");
 		/* Nothing else to do. */
-		return 0;
+		goto dummy_init_out;
 	}
 #if EMULATE_SPI_CHIP
 	if (!strcmp(tmp, "M25P10.RES")) {
@@ -180,13 +197,14 @@ 
 		msg_perr("Out of memory!\n");
 		return 1;
 	}
+
 	msg_pdbg("Filling fake flash chip with 0xff, size %i\n", emu_chip_size);
 	memset(flashchip_contents, 0xff, emu_chip_size);
 
 	emu_persistent_image = extract_programmer_param("image");
 	if (!emu_persistent_image) {
 		/* Nothing else to do. */
-		return 0;
+		goto dummy_init_out;
 	}
 	if (!stat(emu_persistent_image, &image_stat)) {
 		msg_pdbg("Found persistent image %s, size %li ",
@@ -201,22 +219,12 @@ 
 		}
 	}
 #endif
-	return 0;
-}
 
-int dummy_shutdown(void)
-{
-	msg_pspew("%s\n", __func__);
-#if EMULATE_CHIP
-	if (emu_chip != EMULATE_NONE) {
-		if (emu_persistent_image) {
-			msg_pdbg("Writing %s\n", emu_persistent_image);
-			write_buf_to_file(flashchip_contents, emu_chip_size,
-					  emu_persistent_image);
-		}
+dummy_init_out:
+	if (register_shutdown(dummy_shutdown, NULL)) {
 		free(flashchip_contents);
+		return 1;
 	}
-#endif
 	return 0;
 }
 
Index: cli_classic.c
===================================================================
--- cli_classic.c	(revision 1327)
+++ cli_classic.c	(working copy)
@@ -360,6 +360,7 @@ 
 
 	if (programmer_init(pparam)) {
 		fprintf(stderr, "Error: Programmer initialization failed.\n");
+		programmer_shutdown();
 		exit(1);
 	}
 
Index: internal.c
===================================================================
--- internal.c	(revision 1327)
+++ internal.c	(working copy)
@@ -127,6 +127,12 @@ 
 int is_laptop = 0;
 int laptop_ok = 0;
 
+static int internal_shutdown(void *data)
+{
+	release_io_perms();
+	return 0;
+}
+
 int internal_init(void)
 {
 #if __FLASHROM_LITTLE_ENDIAN__
@@ -178,6 +184,8 @@ 
 	free(arg);
 
 	get_io_perms();
+	if (register_shutdown(internal_shutdown, NULL))
+		return 1;
 
 	/* Default to Parallel/LPC/FWH flash devices. If a known host controller
 	 * is found, the init routine sets the buses_supported bitfield.
@@ -287,13 +295,6 @@ 
 	return 1;
 #endif
 }
-
-int internal_shutdown(void)
-{
-	release_io_perms();
-
-	return 0;
-}
 #endif
 
 void internal_chip_writeb(uint8_t val, chipaddr addr)
Index: nicintel_spi.c
===================================================================
--- nicintel_spi.c	(revision 1327)
+++ nicintel_spi.c	(working copy)
@@ -139,6 +139,25 @@ 
 	.release_bus = nicintel_release_spibus,
 };
 
+static int nicintel_spi_shutdown(void *data)
+{
+	uint32_t tmp;
+
+	/* Disable writes manually. See the comment about EECD in
+	 * nicintel_spi_init() for details.
+	 */
+	tmp = pci_mmio_readl(nicintel_spibar + EECD);
+	tmp &= ~FLASH_WRITES_ENABLED;
+	tmp |= FLASH_WRITES_DISABLED;
+	pci_mmio_writel(tmp, nicintel_spibar + EECD);
+
+	physunmap(nicintel_spibar, 4096);
+	pci_cleanup(pacc);
+	release_io_perms();
+
+	return 0;
+}
+
 int nicintel_spi_init(void)
 {
 	uint32_t tmp;
@@ -159,28 +178,12 @@ 
 	tmp |= FLASH_WRITES_ENABLED;
 	pci_mmio_writel(tmp, nicintel_spibar + EECD);
 
+	if (register_shutdown(nicintel_spi_shutdown, NULL))
+		return 1;
+
 	/* 1 usec halfperiod delay for now. */
 	if (bitbang_spi_init(&bitbang_spi_master_nicintel, 1))
 		return 1;
 
 	return 0;
 }
-
-int nicintel_spi_shutdown(void)
-{
-	uint32_t tmp;
-
-	/* Disable writes manually. See the comment about EECD in
-	 * nicintel_spi_init() for details.
-	 */
-	tmp = pci_mmio_readl(nicintel_spibar + EECD);
-	tmp &= ~FLASH_WRITES_ENABLED;
-	tmp |= FLASH_WRITES_DISABLED;
-	pci_mmio_writel(tmp, nicintel_spibar + EECD);
-
-	physunmap(nicintel_spibar, 4096);
-	pci_cleanup(pacc);
-	release_io_perms();
-
-	return 0;
-}
Index: nicnatsemi.c
===================================================================
--- nicnatsemi.c	(revision 1327)
+++ nicnatsemi.c	(working copy)
@@ -35,6 +35,13 @@ 
 	{},
 };
 
+static int nicnatsemi_shutdown(void *data)
+{
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int nicnatsemi_init(void)
 {
 	get_io_perms();
@@ -51,16 +58,11 @@ 
 	 */
 	max_rom_decode.parallel = 131072;
 
+	if (register_shutdown(nicnatsemi_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int nicnatsemi_shutdown(void)
-{
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void nicnatsemi_chip_writeb(uint8_t val, chipaddr addr)
 {
 	OUTL((uint32_t)addr & 0x0001FFFF, io_base_addr + BOOT_ROM_ADDR);
Index: dediprog.c
===================================================================
--- dediprog.c	(revision 1327)
+++ dediprog.c	(working copy)
@@ -536,6 +536,25 @@ 
 	.write_256 = dediprog_spi_write_256,
 };
 
+static int dediprog_shutdown(void *data)
+{
+	msg_pspew("%s\n", __func__);
+
+	/* URB 28. Command Set SPI Voltage to 0. */
+	if (dediprog_set_spi_voltage(0x0))
+		return 1;
+
+	if (usb_release_interface(dediprog_handle, 0)) {
+		msg_perr("Could not release USB interface!\n");
+		return 1;
+	}
+	if (usb_close(dediprog_handle)) {
+		msg_perr("Could not close USB device!\n");
+		return 1;
+	}
+	return 0;
+}
+
 /* URB numbers refer to the first log ever captured. */
 int dediprog_init(void)
 {
@@ -587,6 +606,9 @@ 
 	}
 	dediprog_endpoint = 2;
 	
+	if (register_shutdown(dediprog_shutdown, NULL))
+		return 1;
+
 	dediprog_set_leds(PASS_ON|BUSY_ON|ERROR_ON);
 
 	/* URB 6. Command A. */
@@ -681,22 +703,3 @@ 
 	return 0;
 }
 #endif	
-
-int dediprog_shutdown(void)
-{
-	msg_pspew("%s\n", __func__);
-
-	/* URB 28. Command Set SPI Voltage to 0. */
-	if (dediprog_set_spi_voltage(0x0))
-		return 1;
-
-	if (usb_release_interface(dediprog_handle, 0)) {
-		msg_perr("Could not release USB interface!\n");
-		return 1;
-	}
-	if (usb_close(dediprog_handle)) {
-		msg_perr("Could not close USB device!\n");
-		return 1;
-	}
-	return 0;
-}
Index: it85spi.c
===================================================================
--- it85spi.c	(revision 1327)
+++ it85spi.c	(working copy)
@@ -226,6 +226,14 @@ 
 #endif
 }
 
+static int it85xx_shutdown(void *data)
+{
+	msg_pdbg("%s():%d\n", __func__, __LINE__);
+	it85xx_exit_scratch_rom();
+
+	return 0;	/* FIXME: Should probably return something meaningful */
+}
+
 static int it85xx_spi_common_init(struct superio s)
 {
 	chipaddr base;
@@ -233,6 +241,9 @@ 
 	msg_pdbg("%s():%d superio.vendor=0x%02x\n", __func__, __LINE__,
 	         s.vendor);
 
+	if (register_shutdown(it85xx_shutdown, NULL))
+		return 1;
+
 #ifdef LPC_IO
 	/* Get LPCPNP of SHM. That's big-endian */
 	sio_write(s.port, LDNSEL, 0x0F); /* Set LDN to SHM (0x0F) */
@@ -300,13 +311,6 @@ 
 	return ret;
 }
 
-int it85xx_shutdown(void)
-{
-	msg_pdbg("%s():%d\n", __func__, __LINE__);
-	it85xx_exit_scratch_rom();
-	return 0;
-}
-
 /* According to ITE 8502 document, the procedure to follow mode is following:
  *   1. write 0x00 to LPC/FWH address 0xffff_fexxh (drive CE# high)
  *   2. write data to LPC/FWH address 0xffff_fdxxh (drive CE# low and MOSI
Index: buspirate_spi.c
===================================================================
--- buspirate_spi.c	(revision 1327)
+++ buspirate_spi.c	(working copy)
@@ -111,6 +111,41 @@ 
 	{NULL,		0x0}
 };
 
+static int buspirate_spi_shutdown(void *data)
+{
+	unsigned char buf[5];
+	int ret = 0;
+
+	/* Exit raw SPI mode (enter raw bitbang mode) */
+	buf[0] = 0x00;
+	ret = buspirate_sendrecv(buf, 1, 5);
+	if (ret)
+		return ret;
+	if (memcmp(buf, "BBIO", 4)) {
+		msg_perr("Entering raw bitbang mode failed!\n");
+		return 1;
+	}
+	msg_pdbg("Raw bitbang mode version %c\n", buf[4]);
+	if (buf[4] != '1') {
+		msg_perr("Can't handle raw bitbang mode version %c!\n",
+			buf[4]);
+		return 1;
+	}
+	/* Reset Bus Pirate (return to user terminal) */
+	buf[0] = 0x0f;
+	ret = buspirate_sendrecv(buf, 1, 0);
+	if (ret)
+		return ret;
+
+	/* Shut down serial port communication */
+	ret = serialport_shutdown(NULL);
+	if (ret)
+		return ret;
+	msg_pdbg("Bus Pirate shutdown completed.\n");
+
+	return 0;
+}
+
 int buspirate_spi_init(void)
 {
 	unsigned char buf[512];
@@ -148,6 +183,9 @@ 
 		return ret;
 	free(dev);
 
+	if (register_shutdown(buspirate_spi_shutdown, NULL))
+		return 1;
+
 	/* This is the brute force version, but it should work. */
 	for (i = 0; i < 19; i++) {
 		/* Enter raw bitbang mode */
@@ -253,41 +291,6 @@ 
 	return 0;
 }
 
-int buspirate_spi_shutdown(void)
-{
-	unsigned char buf[5];
-	int ret = 0;
-
-	/* Exit raw SPI mode (enter raw bitbang mode) */
-	buf[0] = 0x00;
-	ret = buspirate_sendrecv(buf, 1, 5);
-	if (ret)
-		return ret;
-	if (memcmp(buf, "BBIO", 4)) {
-		msg_perr("Entering raw bitbang mode failed!\n");
-		return 1;
-	}
-	msg_pdbg("Raw bitbang mode version %c\n", buf[4]);
-	if (buf[4] != '1') {
-		msg_perr("Can't handle raw bitbang mode version %c!\n",
-			buf[4]);
-		return 1;
-	}
-	/* Reset Bus Pirate (return to user terminal) */
-	buf[0] = 0x0f;
-	ret = buspirate_sendrecv(buf, 1, 0);
-	if (ret)
-		return ret;
-
-	/* Shut down serial port communication */
-	ret = serialport_shutdown();
-	if (ret)
-		return ret;
-	msg_pdbg("Bus Pirate shutdown completed.\n");
-
-	return 0;
-}
-
 static int buspirate_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 		const unsigned char *writearr, unsigned char *readarr)
 {
Index: serial.c
===================================================================
--- serial.c	(revision 1327)
+++ serial.c	(working copy)
@@ -180,7 +180,7 @@ 
 	return;
 }
 
-int serialport_shutdown(void)
+int serialport_shutdown(void *data)
 {
 #ifdef _WIN32
 	CloseHandle(sp_fd);
Index: atahpt.c
===================================================================
--- atahpt.c	(revision 1327)
+++ atahpt.c	(working copy)
@@ -38,6 +38,14 @@ 
 	{},
 };
 
+static int atahpt_shutdown(void *data)
+{
+	/* Flash access is disabled automatically by PCI restore. */
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int atahpt_init(void)
 {
 	uint32_t reg32;
@@ -53,17 +61,11 @@ 
 
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 
+	if (register_shutdown(atahpt_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int atahpt_shutdown(void)
-{
-	/* Flash access is disabled automatically by PCI restore. */
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void atahpt_chip_writeb(uint8_t val, chipaddr addr)
 {
 	OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
Index: nic3com.c
===================================================================
--- nic3com.c	(revision 1327)
+++ nic3com.c	(working copy)
@@ -55,6 +55,21 @@ 
 	{},
 };
 
+static int nic3com_shutdown(void *data)
+{
+	/* 3COM 3C90xB cards need a special fixup. */
+	if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
+	    || id == 0x9006 || id == 0x900a || id == 0x905a || id == 0x9058) {
+		/* Select register window 3 and restore the receiver status. */
+		OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
+		OUTL(internal_conf, io_base_addr + INTERNAL_CONFIG);
+	}
+
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int nic3com_init(void)
 {
 	get_io_perms();
@@ -84,24 +99,11 @@ 
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 	max_rom_decode.parallel = 128 * 1024;
 
+	if (register_shutdown(nic3com_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int nic3com_shutdown(void)
-{
-	/* 3COM 3C90xB cards need a special fixup. */
-	if (id == 0x9055 || id == 0x9001 || id == 0x9004 || id == 0x9005
-	    || id == 0x9006 || id == 0x900a || id == 0x905a || id == 0x9058) {
-		/* Select register window 3 and restore the receiver status. */
-		OUTW(SELECT_REG_WINDOW + 3, io_base_addr + INT_STATUS);
-		OUTL(internal_conf, io_base_addr + INTERNAL_CONFIG);
-	}
-
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void nic3com_chip_writeb(uint8_t val, chipaddr addr)
 {
 	OUTL((uint32_t)addr, io_base_addr + BIOS_ROM_ADDR);
Index: satasii.c
===================================================================
--- satasii.c	(revision 1327)
+++ satasii.c	(working copy)
@@ -26,6 +26,8 @@ 
 
 #define PCI_VENDOR_ID_SII	0x1095
 
+#define SATASII_MEMMAP_SIZE	0x100
+
 uint8_t *sii_bar;
 static uint16_t id;
 
@@ -40,6 +42,14 @@ 
 	{},
 };
 
+static int satasii_shutdown(void *data)
+{
+	physunmap(sii_bar, SATASII_MEMMAP_SIZE);
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int satasii_init(void)
 {
 	uint32_t addr;
@@ -59,7 +69,8 @@ 
 		reg_offset = 0x50;
 	}
 
-	sii_bar = physmap("SATA SIL registers", addr, 0x100) + reg_offset;
+	sii_bar = reg_offset +
+	          physmap("SATA SIL registers", addr, SATASII_MEMMAP_SIZE);
 
 	/* Check if ROM cycle are OK. */
 	if ((id != 0x0680) && (!(pci_mmio_readl(sii_bar) & (1 << 26))))
@@ -67,16 +78,11 @@ 
 
 	buses_supported = CHIP_BUSTYPE_PARALLEL;
 
+	if (register_shutdown(satasii_shutdown, NULL))
+		return 1;
 	return 0;
 }
 
-int satasii_shutdown(void)
-{
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void satasii_chip_writeb(uint8_t val, chipaddr addr)
 {
 	uint32_t ctrl_reg, data_reg;
Index: nicintel.c
===================================================================
--- nicintel.c	(revision 1327)
+++ nicintel.c	(working copy)
@@ -39,8 +39,19 @@ 
 #define NICINTEL_MEMMAP_SIZE (128 * 1024)
 #define NICINTEL_MEMMAP_MASK (NICINTEL_MEMMAP_SIZE - 1)
 
+#define NICINTEL_CONTROL_MEMMAP_SIZE	0x10 
+
 #define CSR_FCR 0x0c
 
+static int nicintel_shutdown(void *data)
+{
+	physunmap(nicintel_control_bar, NICINTEL_CONTROL_MEMMAP_SIZE);
+	physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE);
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int nicintel_init(void)
 {
 	uintptr_t addr;
@@ -63,10 +74,14 @@ 
 	/* FIXME: Using pcidev_dev _will_ cause pretty explosions in the future. */
 	addr = pcidev_validate(pcidev_dev, PCI_BASE_ADDRESS_0, nics_intel);
 	/* FIXME: This is not an aligned mapping. Use 4k? */
-	nicintel_control_bar = physmap("Intel NIC control/status reg", addr, 0x10);
+	nicintel_control_bar = physmap("Intel NIC control/status reg",
+	                               addr, NICINTEL_CONTROL_MEMMAP_SIZE);
 	if (nicintel_control_bar == ERROR_PTR)
 		goto error_out;
 
+	if (register_shutdown(nicintel_shutdown, NULL))
+		return 1;
+
 	/* FIXME: This register is pretty undocumented in all publicly available
 	 * documentation from Intel. Let me quote the complete info we have:
 	 * "Flash Control Register: The Flash Control register allows the CPU to
@@ -90,14 +105,6 @@ 
 	return 1;
 }
 
-int nicintel_shutdown(void)
-{
-	physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE);
-	pci_cleanup(pacc);
-	release_io_perms();
-	return 0;
-}
-
 void nicintel_chip_writeb(uint8_t val, chipaddr addr)
 {
 	pci_mmio_writeb(val, nicintel_bar + (addr & NICINTEL_MEMMAP_MASK));
Index: flashrom.c
===================================================================
--- flashrom.c	(revision 1327)
+++ flashrom.c	(working copy)
@@ -129,7 +129,6 @@ 
 	{
 		.name			= "internal",
 		.init			= internal_init,
-		.shutdown		= internal_shutdown,
 		.map_flash_region	= physmap,
 		.unmap_flash_region	= physunmap,
 		.chip_readb		= internal_chip_readb,
@@ -148,7 +147,6 @@ 
 	{
 		.name			= "dummy",
 		.init			= dummy_init,
-		.shutdown		= dummy_shutdown,
 		.map_flash_region	= dummy_map,
 		.unmap_flash_region	= dummy_unmap,
 		.chip_readb		= dummy_chip_readb,
@@ -167,7 +165,6 @@ 
 	{
 		.name			= "nic3com",
 		.init			= nic3com_init,
-		.shutdown		= nic3com_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= nic3com_chip_readb,
@@ -188,7 +185,6 @@ 
 		.name                   = "nicrealtek",
 		//.name                   = "nicsmc1211",
 		.init                   = nicrealtek_init,
-		.shutdown               = nicrealtek_shutdown,
 		.map_flash_region       = fallback_map,
 		.unmap_flash_region     = fallback_unmap,
 		.chip_readb             = nicrealtek_chip_readb,
@@ -207,7 +203,6 @@ 
 	{
 		.name                   = "nicnatsemi",
 		.init                   = nicnatsemi_init,
-		.shutdown               = nicnatsemi_shutdown,
 		.map_flash_region       = fallback_map,
 		.unmap_flash_region     = fallback_unmap,
 		.chip_readb             = nicnatsemi_chip_readb,
@@ -226,7 +221,6 @@ 
 	{
 		.name			= "gfxnvidia",
 		.init			= gfxnvidia_init,
-		.shutdown		= gfxnvidia_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= gfxnvidia_chip_readb,
@@ -245,7 +239,6 @@ 
 	{
 		.name			= "drkaiser",
 		.init			= drkaiser_init,
-		.shutdown		= drkaiser_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= drkaiser_chip_readb,
@@ -264,7 +257,6 @@ 
 	{
 		.name			= "satasii",
 		.init			= satasii_init,
-		.shutdown		= satasii_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= satasii_chip_readb,
@@ -283,7 +275,6 @@ 
 	{
 		.name			= "atahpt",
 		.init			= atahpt_init,
-		.shutdown		= atahpt_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= atahpt_chip_readb,
@@ -302,7 +293,6 @@ 
 	{
 		.name			= "ft2232_spi",
 		.init			= ft2232_spi_init,
-		.shutdown		= noop_shutdown, /* Missing shutdown */
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= noop_chip_readb,
@@ -321,7 +311,6 @@ 
 	{
 		.name			= "serprog",
 		.init			= serprog_init,
-		.shutdown		= serprog_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= serprog_chip_readb,
@@ -340,7 +329,6 @@ 
 	{
 		.name			= "buspirate_spi",
 		.init			= buspirate_spi_init,
-		.shutdown		= buspirate_spi_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= noop_chip_readb,
@@ -359,7 +347,6 @@ 
 	{
 		.name			= "dediprog",
 		.init			= dediprog_init,
-		.shutdown		= dediprog_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= noop_chip_readb,
@@ -378,7 +365,6 @@ 
 	{
 		.name			= "rayer_spi",
 		.init			= rayer_spi_init,
-		.shutdown		= noop_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= noop_chip_readb,
@@ -397,7 +383,6 @@ 
 	{
 		.name			= "nicintel",
 		.init			= nicintel_init,
-		.shutdown		= nicintel_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= nicintel_chip_readb,
@@ -416,7 +401,6 @@ 
 	{
 		.name = "nicintel_spi",
 		.init = nicintel_spi_init,
-		.shutdown = nicintel_spi_shutdown,
 		.map_flash_region = fallback_map,
 		.unmap_flash_region = fallback_unmap,
 		.chip_readb = noop_chip_readb,
@@ -435,7 +419,6 @@ 
 	{
 		.name = "ogp_spi",
 		.init = ogp_spi_init,
-		.shutdown = ogp_spi_shutdown,
 		.map_flash_region = fallback_map,
 		.unmap_flash_region = fallback_unmap,
 		.chip_readb = noop_chip_readb,
@@ -454,7 +437,6 @@ 
 	{
 		.name			= "satamv",
 		.init			= satamv_init,
-		.shutdown		= satamv_shutdown,
 		.map_flash_region	= fallback_map,
 		.unmap_flash_region	= fallback_unmap,
 		.chip_readb		= satamv_chip_readb,
@@ -475,7 +457,7 @@ 
 #define SHUTDOWN_MAXFN 32
 static int shutdown_fn_count = 0;
 struct shutdown_func_data {
-	void (*func) (void *data);
+	int (*func) (void *data);
 	void *data;
 } static shutdown_fn[SHUTDOWN_MAXFN];
 /* Initialize to 0 to make sure nobody registers a shutdown function before
@@ -491,7 +473,7 @@ 
  * Please note that the first (void *data) belongs to the function signature of
  * the function passed as first parameter.
  */
-int register_shutdown(void (*function) (void *data), void *data)
+int register_shutdown(int (*function) (void *data), void *data)
 {
 	if (shutdown_fn_count >= SHUTDOWN_MAXFN) {
 		msg_perr("Tried to register more than %i shutdown functions.\n",
@@ -543,13 +525,15 @@ 
 
 int programmer_shutdown(void)
 {
+	int rc = 0;
+
 	/* Registering shutdown functions is no longer allowed. */
 	may_register_shutdown = 0;
 	while (shutdown_fn_count > 0) {
 		int i = --shutdown_fn_count;
-		shutdown_fn[i].func(shutdown_fn[i].data);
+		rc |= shutdown_fn[i].func(shutdown_fn[i].data);
 	}
-	return programmer_table[programmer].shutdown();
+	return rc;
 }
 
 void *programmer_map_flash_region(const char *descr, unsigned long phys_addr,
Index: programmer.h
===================================================================
--- programmer.h	(revision 1327)
+++ programmer.h	(working copy)
@@ -89,7 +89,6 @@ 
 	const char *name;
 
 	int (*init) (void);
-	int (*shutdown) (void);
 
 	void * (*map_flash_region) (const char *descr, unsigned long phys_addr,
 				    size_t len);
@@ -305,7 +304,6 @@ 
 void probe_superio(void);
 int register_superio(struct superio s);
 int internal_init(void);
-int internal_shutdown(void);
 void internal_chip_writeb(uint8_t val, chipaddr addr);
 void internal_chip_writew(uint16_t val, chipaddr addr);
 void internal_chip_writel(uint32_t val, chipaddr addr);
@@ -363,7 +361,6 @@ 
 /* dummyflasher.c */
 #if CONFIG_DUMMY == 1
 int dummy_init(void);
-int dummy_shutdown(void);
 void *dummy_map(const char *descr, unsigned long phys_addr, size_t len);
 void dummy_unmap(void *virt_addr, size_t len);
 void dummy_chip_writeb(uint8_t val, chipaddr addr);
@@ -379,7 +376,6 @@ 
 /* nic3com.c */
 #if CONFIG_NIC3COM == 1
 int nic3com_init(void);
-int nic3com_shutdown(void);
 void nic3com_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t nic3com_chip_readb(const chipaddr addr);
 extern const struct pcidev_status nics_3com[];
@@ -388,7 +384,6 @@ 
 /* gfxnvidia.c */
 #if CONFIG_GFXNVIDIA == 1
 int gfxnvidia_init(void);
-int gfxnvidia_shutdown(void);
 void gfxnvidia_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t gfxnvidia_chip_readb(const chipaddr addr);
 extern const struct pcidev_status gfx_nvidia[];
@@ -397,7 +392,6 @@ 
 /* drkaiser.c */
 #if CONFIG_DRKAISER == 1
 int drkaiser_init(void);
-int drkaiser_shutdown(void);
 void drkaiser_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t drkaiser_chip_readb(const chipaddr addr);
 extern const struct pcidev_status drkaiser_pcidev[];
@@ -406,7 +400,6 @@ 
 /* nicrealtek.c */
 #if CONFIG_NICREALTEK == 1
 int nicrealtek_init(void);
-int nicrealtek_shutdown(void);
 void nicrealtek_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t nicrealtek_chip_readb(const chipaddr addr);
 extern const struct pcidev_status nics_realtek[];
@@ -415,7 +408,6 @@ 
 /* nicnatsemi.c */
 #if CONFIG_NICNATSEMI == 1
 int nicnatsemi_init(void);
-int nicnatsemi_shutdown(void);
 void nicnatsemi_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t nicnatsemi_chip_readb(const chipaddr addr);
 extern const struct pcidev_status nics_natsemi[];
@@ -424,7 +416,6 @@ 
 /* nicintel.c */
 #if CONFIG_NICINTEL == 1
 int nicintel_init(void);
-int nicintel_shutdown(void);
 void nicintel_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t nicintel_chip_readb(const chipaddr addr);
 extern const struct pcidev_status nics_intel[];
@@ -433,7 +424,6 @@ 
 /* nicintel_spi.c */
 #if CONFIG_NICINTEL_SPI == 1
 int nicintel_spi_init(void);
-int nicintel_spi_shutdown(void);
 int nicintel_spi_send_command(unsigned int writecnt, unsigned int readcnt,
 	const unsigned char *writearr, unsigned char *readarr);
 void nicintel_spi_chip_writeb(uint8_t val, chipaddr addr);
@@ -443,14 +433,12 @@ 
 /* ogp_spi.c */
 #if CONFIG_OGP_SPI == 1
 int ogp_spi_init(void);
-int ogp_spi_shutdown(void);
 extern const struct pcidev_status ogp_spi[];
 #endif
 
 /* satamv.c */
 #if CONFIG_SATAMV == 1
 int satamv_init(void);
-int satamv_shutdown(void);
 void satamv_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t satamv_chip_readb(const chipaddr addr);
 extern const struct pcidev_status satas_mv[];
@@ -459,7 +447,6 @@ 
 /* satasii.c */
 #if CONFIG_SATASII == 1
 int satasii_init(void);
-int satasii_shutdown(void);
 void satasii_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t satasii_chip_readb(const chipaddr addr);
 extern const struct pcidev_status satas_sii[];
@@ -468,7 +455,6 @@ 
 /* atahpt.c */
 #if CONFIG_ATAHPT == 1
 int atahpt_init(void);
-int atahpt_shutdown(void);
 void atahpt_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t atahpt_chip_readb(const chipaddr addr);
 extern const struct pcidev_status ata_hpt[];
@@ -500,13 +486,11 @@ 
 /* buspirate_spi.c */
 #if CONFIG_BUSPIRATE_SPI == 1
 int buspirate_spi_init(void);
-int buspirate_spi_shutdown(void);
 #endif
 
 /* dediprog.c */
 #if CONFIG_DEDIPROG == 1
 int dediprog_init(void);
-int dediprog_shutdown(void);
 #endif
 
 /* flashrom.c */
@@ -591,7 +575,6 @@ 
 
 /* it85spi.c */
 int it85xx_spi_init(struct superio s);
-int it85xx_shutdown(void);
 
 /* it87spi.c */
 void enter_conf_mode_ite(uint16_t port);
@@ -612,7 +595,6 @@ 
 /* serprog.c */
 #if CONFIG_SERPROG == 1
 int serprog_init(void);
-int serprog_shutdown(void);
 void serprog_chip_writeb(uint8_t val, chipaddr addr);
 uint8_t serprog_chip_readb(const chipaddr addr);
 void serprog_chip_readn(uint8_t *buf, const chipaddr addr, size_t len);
@@ -630,7 +612,8 @@ 
 fdtype sp_openserport(char *dev, unsigned int baud);
 void __attribute__((noreturn)) sp_die(char *msg);
 extern fdtype sp_fd;
-int serialport_shutdown(void);
+/* expose serialport_shutdown as it's currently used by buspirate */
+int serialport_shutdown(void *data);
 int serialport_write(unsigned char *buf, unsigned int writecnt);
 int serialport_read(unsigned char *buf, unsigned int readcnt);