Patchwork + RFC: Use shutdown callback mechanism to shutdown programmers

login
register
about
Submitter David Hendricks
Date 2011-06-10 21:55:12
Message ID <BANLkTinEULpZwfoQ1DSUZKYfD3Au+_mwTMqZOJPKeY2bm-ETrg@mail.gmail.com>
Download mbox | patch
Permalink /patch/3104/
State Superseded
Headers show

Comments

David Hendricks - 2011-06-10 21:55:12
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>

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > - programmer_shutdown() checks the return code of all shutdown callbacks.
> >
>
> I'm not 100% sure about this one. If we plan to make libflashrom viable
> in cases where various programmers are initialized more than once (of
> course with programmer shutdown in between), this is essential to refuse
> further programmer init calls once an init/shutdown screwed up. OTOH, we
> have no libflashrom user for now.
>

That sounds more like a job for higher-level logic. Currently, none of the
callers of programmer_shutdown() bother to check its return code.

Also, the callers of register_shutdown() check return code, which will fail
if programmer_shutdown() is used beforehand since may_register_shutdown will
be set to 0. Hmmmm...

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > A few minor questions:
> > - If programmer_init() fails when called in cli_classic(), should we call
> > programmer_shutdown()? As Carl-Daniel noted earlier, this could
> potentially
> > be used to do stuff like release_io_perms(), or perhaps free resources
> > allocated by an init routine that fails.
> >
>
> I think so, yes. Especially for libflashrom.
>

Done.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > - drkaiser - shutdown function missing a physunmap?
> >
>
> Nice find! Same for gfxnvidia.
>
> I wonder if this applies to the various internal chipset functions as
> well... and if we should eventually move towards implicit unmap
> registration. If we decide to do that implicit registration, we should
> create a separate patch for it to keep this patch reviewable.
>

I didn't notice other obvious cases where this applied. Implicit unmapping
(or maybe rphysmap()?) sounds like it might be good in the long-run. That,
in conjunction with rpci_* and rmmio_* functions, could certainly help to
make shutdown functions less prone to error w.r.t. ordering.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> 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.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> IIRC some of your error returns were inconsistent: return -1 vs. return
> 1. I can't find the offender right now, but please recheck that.
>

Nice catch, though I only found it once in (nicnatsemi).

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> A general comment first... I expect some more code changes in programmer
> init (other pending patches for programmer init), and I wonder if it
> would make sense to keep the shutdown functions in the code where they
> are, and just add forward declarations. It would definitely make the
> patch easier to review, but it might also help with code history (svn
> blame). OTOH forward declarations are ugly...
>

Someone (Stefan?) requested that I move the code in an earlier revision. I
think the only real pain here is from serprog due to the large amount of
code that shifted by a few lines.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> >  int ogp_spi_init(void)
> >  {
> >       char *type;
> > @@ -120,6 +129,9 @@
> >
> >       get_io_perms();
> >
> > +     if (register_shutdown(ogp_spi_shutdown, NULL))
> >
>
> Move it after the physmap call or you'll unmap an uninitialized address.
>

Done.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > Index: gfxnvidia.c
> > ===================================================================
> > --- gfxnvidia.c       (revision 1299)
> > +++ gfxnvidia.c       (working copy)
> > @@ -60,12 +60,26 @@
> >       {},
> >  };
> >
> > +static int gfxnvidia_shutdown(void *data)
> > +{
> > +     /* 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;
> >
> >       get_io_perms();
> >
> > +     /* must be done before rpci calls */
> > +     if (register_shutdown(gfxnvidia_shutdown, NULL))
> >
>
> If you change shutdown to include unmap, you'll have to move the
> register_shutdown after physmap, and that's unfortunately after rpci...
> boom! Can you reorder this a bit to call physmap before using rpci? That
> would make sense anyway because we don't want to keep the screen
> disabled in case physmap fails.
>

Fixed. Give it one more glance to make sure I put the physunmap() in a
sensible place as well. I assumed it should be done before pci_cleanup().

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> >  int dummy_init(void)
> >  {
> >       char *bustext = NULL;
> > @@ -180,6 +197,11 @@
> >               msg_perr("Out of memory!\n");
> >               return 1;
> >       }
> > +
> > +     /* shutdown will free the flashchip contents */
> > +     if (register_shutdown(dummy_shutdown, NULL))
> >
>
> A bit earlier we already have a return 0 for the non-emulation case.
> This means register_shutdown has to happen there as well, or we replace
> the various return 0 with goto out (create an out: label at the end of
> the init function) and add register_shutdown to the end of the init
> function.
>

Fixed. I also added free(flashchip_contents) in case shutdown callback
registration fails.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> >  int nic3com_init(void)
> >  {
> >       get_io_perms();
> > @@ -84,24 +99,9 @@
> >       buses_supported = CHIP_BUSTYPE_PARALLEL;
> >       max_rom_decode.parallel = 128 * 1024;
> >
> > -     return 0;
> > +     return register_shutdown(nic3com_shutdown, NULL);
> >
>
> I like the following idiom (which you used everywhere else) better:
> if (register_shutdown(...))
>    return 1;
> return 0;
>

Fixed. It was only intended as a shortcut to save 2 lines since we're ending
the function in this case.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > Index: nicintel.c
> > ===================================================================
> > --- nicintel.c        (revision 1299)
> > +++ nicintel.c        (working copy)
> > @@ -41,6 +41,14 @@
> >
> >  #define CSR_FCR 0x0c
> >
> > +static int nicintel_shutdown(void *data)
> > +{
> > +     physunmap(nicintel_bar, NICINTEL_MEMMAP_SIZE);
> >
>
> physunmap(nicintel_control_bar) is missing.
>

I added a fix for that, and moved register_shutdown() below the physmap call
in the init function as per your other comment.

On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
c-d.hailfinger.devel.2006@gmx.net> wrote:

> > Index: flashrom.c
> > ===================================================================
> > --- flashrom.c        (revision 1299)
> > +++ flashrom.c        (working copy)
> > @@ -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);
> >
>
> That's a big question. Should we proceed even if one shutdown function
> failed, or should we stop? Both options make sense, and it's all about
> which one will screw the user less. Error cases suck if you're dealing
> with hardware.


Good point, but I don't think we should try to solve it in this patch. The
way it's coded now at least preserves the current behavior which doesn't
check return code at all. We can address the issue later when we have more
concrete use cases.
Carl-Daniel Hailfinger - 2011-06-11 12:09:32
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


> On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger wrote
>>> - programmer_shutdown() checks the return code of all shutdown callbacks.
>>>       
>> I'm not 100% sure about this one. If we plan to make libflashrom viable
>> in cases where various programmers are initialized more than once (of
>> course with programmer shutdown in between), this is essential to refuse
>> further programmer init calls once an init/shutdown screwed up. OTOH, we
>> have no libflashrom user for now.
>>     
> That sounds more like a job for higher-level logic. Currently, none of the
> callers of programmer_shutdown() bother to check its return code.
>
> Also, the callers of register_shutdown() check return code, which will fail
> if programmer_shutdown() is used beforehand since may_register_shutdown will
> be set to 0. Hmmmm...
>   

I'd say we handle this in a followup patch.


> On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger wrote:
>   
>>> - drkaiser - shutdown function missing a physunmap?
>>>       
>> Nice find! Same for gfxnvidia.
>>
>> I wonder if this applies to the various internal chipset functions as
>> well... and if we should eventually move towards implicit unmap
>> registration. If we decide to do that implicit registration, we should
>> create a separate patch for it to keep this patch reviewable.
>>     
> I didn't notice other obvious cases where this applied. Implicit unmapping
> (or maybe rphysmap()?) sounds like it might be good in the long-run. That,
> in conjunction with rpci_* and rmmio_* functions, could certainly help to
> make shutdown functions less prone to error w.r.t. ordering.
>   

Indeed.


> 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.


> On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger wrote:
>   
>> A general comment first... I expect some more code changes in programmer
>> init (other pending patches for programmer init), and I wonder if it
>> would make sense to keep the shutdown functions in the code where they
>> are, and just add forward declarations. It would definitely make the
>> patch easier to review, but it might also help with code history (svn
>> blame). OTOH forward declarations are ugly...
>>     
> Someone (Stefan?) requested that I move the code in an earlier revision. I
> think the only real pain here is from serprog due to the large amount of
> code that shifted by a few lines.
>   

Hm yes.


> Fixed. Give it one more glance to make sure I put the physunmap() in a
> sensible place as well. I assumed it should be done before pci_cleanup().
>   

Looks good.


> On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger <
> c-d.hailfinger.devel.2006@gmx.net> wrote:
>
>   
>>>  int dummy_init(void)
>>>  {
>>>       char *bustext = NULL;
>>> @@ -180,6 +197,11 @@
>>>               msg_perr("Out of memory!\n");
>>>               return 1;
>>>       }
>>> +
>>> +     /* shutdown will free the flashchip contents */
>>> +     if (register_shutdown(dummy_shutdown, NULL))
>>>
>>>       
>> A bit earlier we already have a return 0 for the non-emulation case.
>> This means register_shutdown has to happen there as well, or we replace
>> the various return 0 with goto out (create an out: label at the end of
>> the init function) and add register_shutdown to the end of the init
>> function.
>>     
> Fixed. I also added free(flashchip_contents) in case shutdown callback
> registration fails.

Good catch.


> On Thu, Jun 9, 2011 at 5:33 PM, Carl-Daniel Hailfinger wrote:
>   
>>> Index: flashrom.c
>>> ===================================================================
>>> --- flashrom.c        (revision 1299)
>>> +++ flashrom.c        (working copy)
>>> @@ -543,13 +525,15 @@
>>>
>>>  int programmer_shutdown(void)
>> That's a big question. Should we proceed even if one shutdown function
>> failed, or should we stop? Both options make sense, and it's all about
>> which one will screw the user less. Error cases suck if you're dealing
>> with hardware.
>>     
> Good point, but I don't think we should try to solve it in this patch. The
> way it's coded now at least preserves the current behavior which doesn't
> check return code at all. We can address the issue later when we have more
> concrete use cases.
>   

Agreed.

Other comments:
drkaiser and satasii are missing physunmap in shutdown.
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).

This looks pretty good, I think the next iteration will get a straight ack.

Regards,
Carl-Daniel

Patch

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: 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: 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)
@@ -37,6 +37,14 @@ 
 
 static uint8_t *drkaiser_bar;
 
+static int drkaiser_shutdown(void *data)
+{
+	/* Flash write is disabled automatically by PCI restore. */
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+};
+
 int drkaiser_init(void)
 {
 	uint32_t addr;
@@ -55,17 +63,11 @@ 
 
 	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: 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: serprog.c
===================================================================
--- serprog.c	(revision 1327)
+++ serprog.c	(working copy)
@@ -295,6 +295,190 @@ 
 	return 0;
 }
 
+/* Move an in flashrom buffer existing write-n operation to	*
+ * the on-device operation buffer.				*/
+static void sp_pass_writen(void)
+{
+	unsigned char header[7];
+	msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n",
+		     sp_write_n_bytes, sp_write_n_addr);
+	if (sp_streamed_transmit_bytes >=
+	    (7 + sp_write_n_bytes + sp_device_serbuf_size))
+		sp_flush_stream();
+	/* In case it's just a single byte send it as a single write. */
+	if (sp_write_n_bytes == 1) {
+		sp_write_n_bytes = 0;
+		header[0] = (sp_write_n_addr >> 0) & 0xFF;
+		header[1] = (sp_write_n_addr >> 8) & 0xFF;
+		header[2] = (sp_write_n_addr >> 16) & 0xFF;
+		header[3] = sp_write_n_buf[0];
+		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header);
+		sp_opbuf_usage += 5;
+		return;
+	}
+	header[0] = S_CMD_O_WRITEN;
+	header[1] = (sp_write_n_bytes >> 0) & 0xFF;
+	header[2] = (sp_write_n_bytes >> 8) & 0xFF;
+	header[3] = (sp_write_n_bytes >> 16) & 0xFF;
+	header[4] = (sp_write_n_addr >> 0) & 0xFF;
+	header[5] = (sp_write_n_addr >> 8) & 0xFF;
+	header[6] = (sp_write_n_addr >> 16) & 0xFF;
+	if (write(sp_fd, header, 7) != 7)
+		sp_die("Error: cannot write write-n command\n");
+	if (write(sp_fd, sp_write_n_buf, sp_write_n_bytes) !=
+	    sp_write_n_bytes)
+		sp_die("Error: cannot write write-n data");
+	sp_streamed_transmit_bytes += 7 + sp_write_n_bytes;
+	sp_streamed_transmit_ops += 1;
+	sp_opbuf_usage += 7 + sp_write_n_bytes;
+	sp_write_n_bytes = 0;
+	sp_prev_was_write = 0;
+}
+
+static void sp_execute_opbuf_noflush(void)
+{
+	if ((sp_max_write_n) && (sp_write_n_bytes))
+		sp_pass_writen();
+	sp_stream_buffer_op(S_CMD_O_EXEC, 0, NULL);
+	msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n",
+		     sp_opbuf_usage);
+	sp_opbuf_usage = 0;
+	sp_prev_was_write = 0;
+	return;
+}
+
+static void sp_execute_opbuf(void)
+{
+	sp_execute_opbuf_noflush();
+	sp_flush_stream();
+}
+
+static void sp_check_opbuf_usage(int bytes_to_be_added)
+{
+	if (sp_device_opbuf_size <= (sp_opbuf_usage + bytes_to_be_added)) {
+		sp_execute_opbuf();
+		/* If this happens in the mid of an page load the page load *
+		 * will probably fail.					    */
+		msg_pdbg(MSGHEADER "Warning: executed operation buffer due to size reasons\n");
+	}
+}
+
+void serprog_chip_writeb(uint8_t val, chipaddr addr)
+{
+	msg_pspew("%s\n", __func__);
+	if (sp_max_write_n) {
+		if ((sp_prev_was_write)
+		    && (addr == (sp_write_n_addr + sp_write_n_bytes))) {
+			sp_write_n_buf[sp_write_n_bytes++] = val;
+		} else {
+			if ((sp_prev_was_write) && (sp_write_n_bytes))
+				sp_pass_writen();
+			sp_prev_was_write = 1;
+			sp_write_n_addr = addr;
+			sp_write_n_bytes = 1;
+			sp_write_n_buf[0] = val;
+		}
+		sp_check_opbuf_usage(7 + sp_write_n_bytes);
+		if (sp_write_n_bytes >= sp_max_write_n)
+			sp_pass_writen();
+	} else {
+		/* We will have to do single writeb ops. */
+		unsigned char writeb_parm[4];
+		sp_check_opbuf_usage(6);
+		writeb_parm[0] = (addr >> 0) & 0xFF;
+		writeb_parm[1] = (addr >> 8) & 0xFF;
+		writeb_parm[2] = (addr >> 16) & 0xFF;
+		writeb_parm[3] = val;
+		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm);
+		sp_opbuf_usage += 5;
+	}
+}
+
+uint8_t serprog_chip_readb(const chipaddr addr)
+{
+	unsigned char c;
+	unsigned char buf[3];
+	/* Will stream the read operation - eg. add it to the stream buffer, *
+	 * then flush the buffer, then read the read answer.		     */
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf_noflush();
+	buf[0] = ((addr >> 0) & 0xFF);
+	buf[1] = ((addr >> 8) & 0xFF);
+	buf[2] = ((addr >> 16) & 0xFF);
+	sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf);
+	sp_flush_stream();
+	if (read(sp_fd, &c, 1) != 1)
+		sp_die("readb byteread");
+	msg_pspew("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c);
+	return c;
+}
+
+/* Local version that really does the job, doesn't care of max_read_n. */
+static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len)
+{
+	int rd_bytes = 0;
+	unsigned char sbuf[6];
+	msg_pspew("%s: addr=0x%lx len=%lu\n", __func__, addr, (unsigned long)len);
+	/* Stream the read-n -- as above. */
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf_noflush();
+	sbuf[0] = ((addr >> 0) & 0xFF);
+	sbuf[1] = ((addr >> 8) & 0xFF);
+	sbuf[2] = ((addr >> 16) & 0xFF);
+	sbuf[3] = ((len >> 0) & 0xFF);
+	sbuf[4] = ((len >> 8) & 0xFF);
+	sbuf[5] = ((len >> 16) & 0xFF);
+	sp_stream_buffer_op(S_CMD_R_NBYTES, 6, sbuf);
+	sp_flush_stream();
+	do {
+		int r = read(sp_fd, buf + rd_bytes, len - rd_bytes);
+		if (r <= 0)
+			sp_die("Error: cannot read read-n data");
+		rd_bytes += r;
+	} while (rd_bytes != len);
+	return;
+}
+
+/* The externally called version that makes sure that max_read_n is obeyed. */
+void serprog_chip_readn(uint8_t * buf, const chipaddr addr, size_t len)
+{
+	size_t lenm = len;
+	chipaddr addrm = addr;
+	while ((sp_max_read_n)&&(lenm > sp_max_read_n)) {
+		sp_do_read_n(&(buf[addrm-addr]),addrm,sp_max_read_n);
+		addrm += sp_max_read_n;
+		lenm -= sp_max_read_n;
+	}
+	if (lenm) sp_do_read_n(&(buf[addrm-addr]),addrm,lenm);
+}
+
+void serprog_delay(int delay)
+{
+	unsigned char buf[4];
+	msg_pspew("%s\n", __func__);
+	if ((sp_max_write_n) && (sp_write_n_bytes))
+		sp_pass_writen();
+	sp_check_opbuf_usage(5);
+	buf[0] = ((delay >> 0) & 0xFF);
+	buf[1] = ((delay >> 8) & 0xFF);
+	buf[2] = ((delay >> 16) & 0xFF);
+	buf[3] = ((delay >> 24) & 0xFF);
+	sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
+	sp_opbuf_usage += 5;
+	sp_prev_was_write = 0;
+}
+
+static int serprog_shutdown(void *data)
+{
+	msg_pspew("%s\n", __func__);
+	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
+		sp_execute_opbuf();
+	close(sp_fd);
+	if (sp_max_write_n)
+		free(sp_write_n_buf);
+	return 0;
+}
+
 int serprog_init(void)
 {
 	uint16_t iface;
@@ -373,6 +557,9 @@ 
 		return 1;
 	}
 
+	if (register_shutdown(serprog_shutdown, NULL))
+		return 1;
+
 	msg_pdbg(MSGHEADER "connected - attempting to synchronize\n");
 
 	sp_check_avail_automatic = 0;
@@ -494,189 +681,7 @@ 
 	sp_streamed_transmit_ops = 0;
 	sp_streamed_transmit_bytes = 0;
 	sp_opbuf_usage = 0;
-	return 0;
-}
 
-/* Move an in flashrom buffer existing write-n operation to	*
- * the on-device operation buffer.				*/
-static void sp_pass_writen(void)
-{
-	unsigned char header[7];
-	msg_pspew(MSGHEADER "Passing write-n bytes=%d addr=0x%x\n",
-		     sp_write_n_bytes, sp_write_n_addr);
-	if (sp_streamed_transmit_bytes >=
-	    (7 + sp_write_n_bytes + sp_device_serbuf_size))
-		sp_flush_stream();
-	/* In case it's just a single byte send it as a single write. */
-	if (sp_write_n_bytes == 1) {
-		sp_write_n_bytes = 0;
-		header[0] = (sp_write_n_addr >> 0) & 0xFF;
-		header[1] = (sp_write_n_addr >> 8) & 0xFF;
-		header[2] = (sp_write_n_addr >> 16) & 0xFF;
-		header[3] = sp_write_n_buf[0];
-		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, header);
-		sp_opbuf_usage += 5;
-		return;
-	}
-	header[0] = S_CMD_O_WRITEN;
-	header[1] = (sp_write_n_bytes >> 0) & 0xFF;
-	header[2] = (sp_write_n_bytes >> 8) & 0xFF;
-	header[3] = (sp_write_n_bytes >> 16) & 0xFF;
-	header[4] = (sp_write_n_addr >> 0) & 0xFF;
-	header[5] = (sp_write_n_addr >> 8) & 0xFF;
-	header[6] = (sp_write_n_addr >> 16) & 0xFF;
-	if (write(sp_fd, header, 7) != 7)
-		sp_die("Error: cannot write write-n command\n");
-	if (write(sp_fd, sp_write_n_buf, sp_write_n_bytes) !=
-	    sp_write_n_bytes)
-		sp_die("Error: cannot write write-n data");
-	sp_streamed_transmit_bytes += 7 + sp_write_n_bytes;
-	sp_streamed_transmit_ops += 1;
-	sp_opbuf_usage += 7 + sp_write_n_bytes;
-	sp_write_n_bytes = 0;
-	sp_prev_was_write = 0;
-}
-
-static void sp_execute_opbuf_noflush(void)
-{
-	if ((sp_max_write_n) && (sp_write_n_bytes))
-		sp_pass_writen();
-	sp_stream_buffer_op(S_CMD_O_EXEC, 0, NULL);
-	msg_pspew(MSGHEADER "Executed operation buffer of %d bytes\n",
-		     sp_opbuf_usage);
-	sp_opbuf_usage = 0;
-	sp_prev_was_write = 0;
-	return;
-}
-
-static void sp_execute_opbuf(void)
-{
-	sp_execute_opbuf_noflush();
-	sp_flush_stream();
-}
-
-int serprog_shutdown(void)
-{
-	msg_pspew("%s\n", __func__);
-	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
-		sp_execute_opbuf();
-	close(sp_fd);
-	if (sp_max_write_n)
-		free(sp_write_n_buf);
 	return 0;
 }
 
-static void sp_check_opbuf_usage(int bytes_to_be_added)
-{
-	if (sp_device_opbuf_size <= (sp_opbuf_usage + bytes_to_be_added)) {
-		sp_execute_opbuf();
-		/* If this happens in the mid of an page load the page load *
-		 * will probably fail.					    */
-		msg_pdbg(MSGHEADER "Warning: executed operation buffer due to size reasons\n");
-	}
-}
-
-void serprog_chip_writeb(uint8_t val, chipaddr addr)
-{
-	msg_pspew("%s\n", __func__);
-	if (sp_max_write_n) {
-		if ((sp_prev_was_write)
-		    && (addr == (sp_write_n_addr + sp_write_n_bytes))) {
-			sp_write_n_buf[sp_write_n_bytes++] = val;
-		} else {
-			if ((sp_prev_was_write) && (sp_write_n_bytes))
-				sp_pass_writen();
-			sp_prev_was_write = 1;
-			sp_write_n_addr = addr;
-			sp_write_n_bytes = 1;
-			sp_write_n_buf[0] = val;
-		}
-		sp_check_opbuf_usage(7 + sp_write_n_bytes);
-		if (sp_write_n_bytes >= sp_max_write_n)
-			sp_pass_writen();
-	} else {
-		/* We will have to do single writeb ops. */
-		unsigned char writeb_parm[4];
-		sp_check_opbuf_usage(6);
-		writeb_parm[0] = (addr >> 0) & 0xFF;
-		writeb_parm[1] = (addr >> 8) & 0xFF;
-		writeb_parm[2] = (addr >> 16) & 0xFF;
-		writeb_parm[3] = val;
-		sp_stream_buffer_op(S_CMD_O_WRITEB, 4, writeb_parm);
-		sp_opbuf_usage += 5;
-	}
-}
-
-uint8_t serprog_chip_readb(const chipaddr addr)
-{
-	unsigned char c;
-	unsigned char buf[3];
-	/* Will stream the read operation - eg. add it to the stream buffer, *
-	 * then flush the buffer, then read the read answer.		     */
-	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
-		sp_execute_opbuf_noflush();
-	buf[0] = ((addr >> 0) & 0xFF);
-	buf[1] = ((addr >> 8) & 0xFF);
-	buf[2] = ((addr >> 16) & 0xFF);
-	sp_stream_buffer_op(S_CMD_R_BYTE, 3, buf);
-	sp_flush_stream();
-	if (read(sp_fd, &c, 1) != 1)
-		sp_die("readb byteread");
-	msg_pspew("%s addr=0x%lx returning 0x%02X\n", __func__, addr, c);
-	return c;
-}
-
-/* Local version that really does the job, doesn't care of max_read_n. */
-static void sp_do_read_n(uint8_t * buf, const chipaddr addr, size_t len)
-{
-	int rd_bytes = 0;
-	unsigned char sbuf[6];
-	msg_pspew("%s: addr=0x%lx len=%lu\n", __func__, addr, (unsigned long)len);
-	/* Stream the read-n -- as above. */
-	if ((sp_opbuf_usage) || (sp_max_write_n && sp_write_n_bytes))
-		sp_execute_opbuf_noflush();
-	sbuf[0] = ((addr >> 0) & 0xFF);
-	sbuf[1] = ((addr >> 8) & 0xFF);
-	sbuf[2] = ((addr >> 16) & 0xFF);
-	sbuf[3] = ((len >> 0) & 0xFF);
-	sbuf[4] = ((len >> 8) & 0xFF);
-	sbuf[5] = ((len >> 16) & 0xFF);
-	sp_stream_buffer_op(S_CMD_R_NBYTES, 6, sbuf);
-	sp_flush_stream();
-	do {
-		int r = read(sp_fd, buf + rd_bytes, len - rd_bytes);
-		if (r <= 0)
-			sp_die("Error: cannot read read-n data");
-		rd_bytes += r;
-	} while (rd_bytes != len);
-	return;
-}
-
-/* The externally called version that makes sure that max_read_n is obeyed. */
-void serprog_chip_readn(uint8_t * buf, const chipaddr addr, size_t len)
-{
-	size_t lenm = len;
-	chipaddr addrm = addr;
-	while ((sp_max_read_n)&&(lenm > sp_max_read_n)) {
-		sp_do_read_n(&(buf[addrm-addr]),addrm,sp_max_read_n);
-		addrm += sp_max_read_n;
-		lenm -= sp_max_read_n;
-	}
-	if (lenm) sp_do_read_n(&(buf[addrm-addr]),addrm,lenm);
-}
-
-void serprog_delay(int delay)
-{
-	unsigned char buf[4];
-	msg_pspew("%s\n", __func__);
-	if ((sp_max_write_n) && (sp_write_n_bytes))
-		sp_pass_writen();
-	sp_check_opbuf_usage(5);
-	buf[0] = ((delay >> 0) & 0xFF);
-	buf[1] = ((delay >> 8) & 0xFF);
-	buf[2] = ((delay >> 16) & 0xFF);
-	buf[3] = ((delay >> 24) & 0xFF);
-	sp_stream_buffer_op(S_CMD_O_DELAY, 4, buf);
-	sp_opbuf_usage += 5;
-	sp_prev_was_write = 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: 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,12 +139,34 @@ 
 	.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;
 
 	get_io_perms();
 
+	if (register_shutdown(nicintel_spi_shutdown, NULL))
+		return 1;
+
 	io_base_addr = pcidev_init(PCI_BASE_ADDRESS_0, nics_intel_spi);
 
 	nicintel_spibar = physmap("Intel Gigabit NIC w/ SPI flash",
@@ -165,22 +187,3 @@ 
 
 	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)
+{
+	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 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)
@@ -40,6 +40,13 @@ 
 	{},
 };
 
+static int satasii_shutdown(void *data)
+{
+	pci_cleanup(pacc);
+	release_io_perms();
+	return 0;
+}
+
 int satasii_init(void)
 {
 	uint32_t addr;
@@ -67,16 +74,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);