I/O layer refactoring

Jef Driesen jef at libdivecomputer.org
Sat Jun 3 00:22:54 PDT 2017


On 2017-03-16 17:45, Jef Driesen wrote:
> I'm planning some major non-backwards compatible changes for the next
> release. The first one is a refactoring of the I/O layer to support
> bluetooth communication (and more). The remainder of this email
> contains a description of the problem and a proposal for how I would
> like to address this.
> 
> [...]

I've implemented a first version of the new I/O layer api. See the 
attached patches for details. To see what needs to change on the 
application side, patch #0007 is the most interesting one.

Note that there are several areas that will need some more polish, in 
particular:

1. Device discovery built-in filtering

Because the device discovery is now moved to the application, also some 
of this logic was moved to the application. For example the Uwatec 
discovery detects Uwatec dive computer based on a set of well known IrDA 
device names. But ideally only libdivecomputer should need to know those 
IrDA names.

This could be achieved by providing some built-in filtering based on the 
device descriptor. Thus if the application enumerates the IrDA devices, 
and passes a Uwatec device descriptor, the discovery would only return 
those devices that matches Uwatec names.

This can easily be extended for the other built-in transport protocols 
like USB HID (filtering on VID/PID), bluetooth (filtering on device 
name), etc.

So that means the discovery api will still change significantly.

2. Multiple transport types per device descriptor

At the moment each device descriptor can support only one transport 
type. But there are devices that support multiple transport protocols. 
For example all classic bluetooth (rfcomm) devices also support serial 
communication. This could be supported by changing the transport type 
into a bitfield. This will also require some changes on the application 
side.

3. Api to query the built-in I/O implementations

At the moment there is no mechanism to detect whether a particular I/O 
implementation is available. The functions are always available, and if 
not supported, they will fail with DC_STATUS_UNSUPPORTED when trying to 
use them. But it will certainly be more user friendly if an application 
can query libdivecomputer in advance and hide stuff that is not 
supported.

An alternative could be to not report a transport type if the 
corresponding built-in I/O implementation isn't available. But that 
might be less ideal for applications using a custom I/O implementation. 
If an application chooses to use a custom bluetooth implementation if 
the built-in one isn't available, then it probably still wants to know 
the device supports bluetooth communication, right?

I'm not sure yet what would be the best way to deal with this. Ideas is 
welcome.

4. No custom I/O layer yet

I haven't added the support for a custom I/O implementation yet. That's 
still on my todo list.

5. USB communication

The Atomic Aquatics Cobalt has not been ported to the new I/O layer, 
because USB communication doesn't really map nicely onto the api. As an 
exception to the rule, the application will need to pass a NULL pointer. 
The transport type is probably best changed to DC_TRANSPORT_NONE to 
reflect this. I don't really have another solution for this case.

Jef
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Add-a-new-abstract-I-O-interface.patch
Type: text/x-diff
Size: 21006 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0007.patch>
-------------- next part --------------
From 39451eacf47d4cc4acfcce1b351ae2848b7dc8de Mon Sep 17 00:00:00 2001
From: Jef Driesen <jefdriesen at users.sourceforge.net>
Date: Fri, 24 Mar 2017 22:11:21 +0100
Subject: [PATCH 2/9] Integrate the new I/O interface in the entire codebase

---
 examples/dctool_download.c       |  3 +-
 examples/dctool_dump.c           |  3 +-
 examples/dctool_fwupdate.c       |  3 +-
 examples/dctool_read.c           |  3 +-
 examples/dctool_write.c          |  3 +-
 include/libdivecomputer/device.h |  3 +-
 src/citizen_aqualand.c           | 62 ++++++++-------------------
 src/citizen_aqualand.h           |  3 +-
 src/cochran_commander.c          | 67 +++++++++---------------------
 src/cochran_commander.h          |  3 +-
 src/cressi_edy.c                 | 60 ++++++++++-----------------
 src/cressi_edy.h                 |  3 +-
 src/cressi_leonardo.c            | 79 ++++++++++++-----------------------
 src/cressi_leonardo.h            |  3 +-
 src/device.c                     | 66 ++++++++++++++---------------
 src/diverite_nitekq.c            | 46 +++++++-------------
 src/diverite_nitekq.h            |  3 +-
 src/divesystem_idive.c           | 58 +++++++-------------------
 src/divesystem_idive.h           |  3 +-
 src/hw_frog.c                    | 48 +++++++--------------
 src/hw_frog.h                    |  3 +-
 src/hw_ostc.c                    | 84 +++++++++++++------------------------
 src/hw_ostc.h                    |  3 +-
 src/hw_ostc3.c                   | 56 +++++++++----------------
 src/hw_ostc3.h                   |  3 +-
 src/mares_common.c               | 16 +++----
 src/mares_common.h               |  7 ++--
 src/mares_darwin.c               | 52 ++++++-----------------
 src/mares_darwin.h               |  3 +-
 src/mares_iconhd.c               | 66 +++++++++--------------------
 src/mares_iconhd.h               |  3 +-
 src/mares_nemo.c                 | 62 ++++++++-------------------
 src/mares_nemo.h                 |  3 +-
 src/mares_puck.c                 | 54 ++++++------------------
 src/mares_puck.h                 |  3 +-
 src/oceanic_atom2.c              | 52 ++++++++---------------
 src/oceanic_atom2.h              |  3 +-
 src/oceanic_veo250.c             | 64 +++++++++++-----------------
 src/oceanic_veo250.h             |  3 +-
 src/oceanic_vtpro.c              | 64 +++++++++++-----------------
 src/oceanic_vtpro.h              |  3 +-
 src/reefnet_sensus.c             | 44 +++++++-------------
 src/reefnet_sensus.h             |  3 +-
 src/reefnet_sensuspro.c          | 62 ++++++++-------------------
 src/reefnet_sensuspro.h          |  3 +-
 src/reefnet_sensusultra.c        | 58 +++++++-------------------
 src/reefnet_sensusultra.h        |  3 +-
 src/shearwater_common.c          | 43 ++++++-------------
 src/shearwater_common.h          | 10 ++---
 src/shearwater_petrel.c          | 10 +----
 src/shearwater_petrel.h          |  3 +-
 src/shearwater_predator.c        | 16 ++-----
 src/shearwater_predator.h        |  3 +-
 src/suunto_d9.c                  | 66 +++++++++--------------------
 src/suunto_d9.h                  |  3 +-
 src/suunto_eon.c                 | 58 +++++++-------------------
 src/suunto_eon.h                 |  3 +-
 src/suunto_eonsteel.c            | 39 +++++------------
 src/suunto_eonsteel.h            |  3 +-
 src/suunto_solution.c            | 74 ++++++++++-----------------------
 src/suunto_solution.h            |  3 +-
 src/suunto_vyper.c               | 70 ++++++++++---------------------
 src/suunto_vyper.h               |  3 +-
 src/suunto_vyper2.c              | 66 +++++++++--------------------
 src/suunto_vyper2.h              |  3 +-
 src/uwatec_aladin.c              | 56 +++++++------------------
 src/uwatec_aladin.h              |  3 +-
 src/uwatec_memomouse.c           | 90 ++++++++++++++--------------------------
 src/uwatec_memomouse.h           |  3 +-
 src/uwatec_meridian.c            | 64 ++++++++--------------------
 src/uwatec_meridian.h            |  3 +-
 src/uwatec_smart.c               | 90 ++++------------------------------------
 src/uwatec_smart.h               |  3 +-
 src/zeagle_n2ition3.c            | 50 +++++-----------------
 src/zeagle_n2ition3.h            |  3 +-
 75 files changed, 686 insertions(+), 1457 deletions(-)

diff --git a/examples/dctool_download.c b/examples/dctool_download.c
index 2c8f43d..56f36b1 100644
--- a/examples/dctool_download.c
+++ b/examples/dctool_download.c
@@ -155,6 +155,7 @@ static dc_status_t
 download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *cachedir, dc_buffer_t *fingerprint, dctool_output_t *output)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_iostream_t *iostream = NULL;
 	dc_device_t *device = NULL;
 	dc_buffer_t *ofingerprint = NULL;
 
@@ -163,7 +164,7 @@ download (dc_context_t *context, dc_descriptor_t *descriptor, const char *devnam
 		dc_descriptor_get_vendor (descriptor),
 		dc_descriptor_get_product (descriptor),
 		devname ? devname : "null");
-	rc = dc_device_open (&device, context, descriptor, devname);
+	rc = dc_device_open (&device, context, descriptor, iostream);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR ("Error opening the device.");
 		goto cleanup;
diff --git a/examples/dctool_dump.c b/examples/dctool_dump.c
index ce59899..26450f4 100644
--- a/examples/dctool_dump.c
+++ b/examples/dctool_dump.c
@@ -43,6 +43,7 @@ static dc_status_t
 dump (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, dc_buffer_t *fingerprint, dc_buffer_t *buffer)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_iostream_t *iostream = NULL;
 	dc_device_t *device = NULL;
 
 	// Open the device.
@@ -50,7 +51,7 @@ dump (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, d
 		dc_descriptor_get_vendor (descriptor),
 		dc_descriptor_get_product (descriptor),
 		devname ? devname : "null");
-	rc = dc_device_open (&device, context, descriptor, devname);
+	rc = dc_device_open (&device, context, descriptor, iostream);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR ("Error opening the device.");
 		goto cleanup;
diff --git a/examples/dctool_fwupdate.c b/examples/dctool_fwupdate.c
index 13b4fe3..022724e 100644
--- a/examples/dctool_fwupdate.c
+++ b/examples/dctool_fwupdate.c
@@ -44,6 +44,7 @@ static dc_status_t
 fwupdate (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, const char *hexfile)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_iostream_t *iostream = NULL;
 	dc_device_t *device = NULL;
 
 	// Open the device.
@@ -51,7 +52,7 @@ fwupdate (dc_context_t *context, dc_descriptor_t *descriptor, const char *devnam
 		dc_descriptor_get_vendor (descriptor),
 		dc_descriptor_get_product (descriptor),
 		devname ? devname : "null");
-	rc = dc_device_open (&device, context, descriptor, devname);
+	rc = dc_device_open (&device, context, descriptor, iostream);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR ("Error opening the device.");
 		goto cleanup;
diff --git a/examples/dctool_read.c b/examples/dctool_read.c
index 85bd9de..4e71660 100644
--- a/examples/dctool_read.c
+++ b/examples/dctool_read.c
@@ -42,6 +42,7 @@ static dc_status_t
 doread (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, unsigned int address, dc_buffer_t *buffer)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_iostream_t *iostream = NULL;
 	dc_device_t *device = NULL;
 
 	// Open the device.
@@ -49,7 +50,7 @@ doread (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname,
 		dc_descriptor_get_vendor (descriptor),
 		dc_descriptor_get_product (descriptor),
 		devname ? devname : "null");
-	rc = dc_device_open (&device, context, descriptor, devname);
+	rc = dc_device_open (&device, context, descriptor, iostream);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR ("Error opening the device.");
 		goto cleanup;
diff --git a/examples/dctool_write.c b/examples/dctool_write.c
index 4bf193b..7b6e050 100644
--- a/examples/dctool_write.c
+++ b/examples/dctool_write.c
@@ -42,6 +42,7 @@ static dc_status_t
 dowrite (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname, unsigned int address, dc_buffer_t *buffer)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
+	dc_iostream_t *iostream = NULL;
 	dc_device_t *device = NULL;
 
 	// Open the device.
@@ -49,7 +50,7 @@ dowrite (dc_context_t *context, dc_descriptor_t *descriptor, const char *devname
 		dc_descriptor_get_vendor (descriptor),
 		dc_descriptor_get_product (descriptor),
 		devname ? devname : "null");
-	rc = dc_device_open (&device, context, descriptor, devname);
+	rc = dc_device_open (&device, context, descriptor, iostream);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR ("Error opening the device.");
 		goto cleanup;
diff --git a/include/libdivecomputer/device.h b/include/libdivecomputer/device.h
index 7ba4bd6..97b3bbc 100644
--- a/include/libdivecomputer/device.h
+++ b/include/libdivecomputer/device.h
@@ -25,6 +25,7 @@
 #include "common.h"
 #include "context.h"
 #include "descriptor.h"
+#include "iostream.h"
 #include "buffer.h"
 #include "datetime.h"
 
@@ -70,7 +71,7 @@ typedef void (*dc_event_callback_t) (dc_device_t *device, dc_event_type_t event,
 typedef int (*dc_dive_callback_t) (const unsigned char *data, unsigned int size, const unsigned char *fingerprint, unsigned int fsize, void *userdata);
 
 dc_status_t
-dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const char *name);
+dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_iostream_t *iostream);
 
 dc_family_t
 dc_device_get_type (dc_device_t *device);
diff --git a/src/citizen_aqualand.c b/src/citizen_aqualand.c
index 5e111da..5382b68 100644
--- a/src/citizen_aqualand.c
+++ b/src/citizen_aqualand.c
@@ -25,7 +25,6 @@
 #include "citizen_aqualand.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "ringbuffer.h"
 #include "array.h"
@@ -34,14 +33,13 @@
 
 typedef struct citizen_aqualand_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[8];
 } citizen_aqualand_device_t;
 
 static dc_status_t citizen_aqualand_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t citizen_aqualand_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t citizen_aqualand_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t citizen_aqualand_device_vtable = {
 	sizeof(citizen_aqualand_device_t),
@@ -51,12 +49,12 @@ static const dc_device_vtable_t citizen_aqualand_device_vtable = {
 	NULL, /* write */
 	citizen_aqualand_device_dump, /* dump */
 	citizen_aqualand_device_foreach, /* foreach */
-	citizen_aqualand_device_close /* close */
+	NULL /* close */
 };
 
 
 dc_status_t
-citizen_aqualand_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+citizen_aqualand_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	citizen_aqualand_device_t *device = NULL;
@@ -72,40 +70,31 @@ citizen_aqualand_device_open (dc_device_t **out, dc_context_t *context, const ch
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (4800 8N1).
-	status = dc_serial_configure (device->port, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 300);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 300);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -113,23 +102,6 @@ error_free:
 
 
 static dc_status_t
-citizen_aqualand_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	citizen_aqualand_device_t *device = (citizen_aqualand_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 citizen_aqualand_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	citizen_aqualand_device_t *device = (citizen_aqualand_device_t *) abstract;
@@ -158,21 +130,21 @@ citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		return DC_STATUS_NOMEMORY;
 	}
 
-	dc_serial_set_dtr (device->port, 1);
+	dc_iostream_set_dtr (device->iostream, 1);
 
 	// Send the init byte.
 	const unsigned char init[] = {0x7F};
-	status = dc_serial_write (device->port, init, sizeof (init), NULL);
+	status = dc_iostream_write (device->iostream, init, sizeof (init), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
-	dc_serial_sleep(device->port, 1200);
+	dc_iostream_sleep(device->iostream, 1200);
 
 	// Send the command.
 	const unsigned char command[] = {0xFF};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -181,7 +153,7 @@ citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	while (1) {
 		// Receive the response packet.
 		unsigned char answer[32] = {0};
-		status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+		status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -190,7 +162,7 @@ citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		dc_buffer_append(buffer, answer, sizeof (answer));
 
 		// Send the command.
-		status = dc_serial_write (device->port, command, sizeof (command), NULL);
+		status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
 			return status;
@@ -200,7 +172,7 @@ citizen_aqualand_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 			break;
 	}
 
-	dc_serial_set_dtr (device->port, 0);
+	dc_iostream_set_dtr (device->iostream, 0);
 
 	return DC_STATUS_SUCCESS;
 }
diff --git a/src/citizen_aqualand.h b/src/citizen_aqualand.h
index 6391660..ac47cb6 100644
--- a/src/citizen_aqualand.h
+++ b/src/citizen_aqualand.h
@@ -23,6 +23,7 @@
 #define CITIZEN_AQUALAND_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-citizen_aqualand_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+citizen_aqualand_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 citizen_aqualand_parser_create (dc_parser_t **parser, dc_context_t *context);
diff --git a/src/cochran_commander.c b/src/cochran_commander.c
index d2c47b0..269f075 100644
--- a/src/cochran_commander.c
+++ b/src/cochran_commander.c
@@ -26,7 +26,6 @@
 #include "cochran_commander.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "array.h"
 
 #define C_ARRAY_SIZE(array) (sizeof (array) / sizeof *(array))
@@ -86,7 +85,7 @@ typedef struct cochran_device_layout_t {
 
 typedef struct cochran_commander_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	const cochran_device_layout_t *layout;
 	unsigned char id[67];
 	unsigned char fingerprint[6];
@@ -96,7 +95,6 @@ static dc_status_t cochran_commander_device_set_fingerprint (dc_device_t *device
 static dc_status_t cochran_commander_device_read (dc_device_t *device, unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t cochran_commander_device_dump (dc_device_t *device, dc_buffer_t *data);
 static dc_status_t cochran_commander_device_foreach (dc_device_t *device, dc_dive_callback_t callback, void *userdata);
-static dc_status_t cochran_commander_device_close (dc_device_t *device);
 
 static const dc_device_vtable_t cochran_commander_device_vtable = {
 	sizeof (cochran_commander_device_t),
@@ -106,7 +104,7 @@ static const dc_device_vtable_t cochran_commander_device_vtable = {
 	NULL, /* write */
 	cochran_commander_device_dump, /* dump */
 	cochran_commander_device_foreach, /* foreach */
-	cochran_commander_device_close /* close */
+	NULL /* close */
 };
 
 // Cochran Commander Nitrox
@@ -223,30 +221,30 @@ cochran_commander_serial_setup (cochran_commander_device_t *device)
 	dc_status_t status = DC_STATUS_SUCCESS;
 
 	// Set the serial communication protocol (9600 8N2, no FC).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (device->base.context, "Failed to set the terminal attributes.");
 		return status;
 	}
 
 	// Set the timeout for receiving data (5000 ms).
-	status = dc_serial_set_timeout (device->port, 5000);
+	status = dc_iostream_set_timeout (device->iostream, 5000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (device->base.context, "Failed to set the timeout.");
 		return status;
 	}
 
 	// Wake up DC and trigger heartbeat
-	dc_serial_set_break(device->port, 1);
-	dc_serial_sleep(device->port, 16);
-	dc_serial_set_break(device->port, 0);
+	dc_iostream_set_break(device->iostream, 1);
+	dc_iostream_sleep(device->iostream, 16);
+	dc_iostream_set_break(device->iostream, 0);
 
 	// Clear old heartbeats
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Wait for heartbeat byte before send
 	unsigned char answer = 0;
-	status = dc_serial_read(device->port, &answer, 1, NULL);
+	status = dc_iostream_read(device->iostream, &answer, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (device->base.context, "Failed to receive device heartbeat.");
 		return status;
@@ -277,9 +275,9 @@ cochran_commander_packet (cochran_commander_device_t *device, dc_event_progress_
 	// has no buffering.
 	for (unsigned int i = 0; i < csize; i++) {
 		// Give the DC time to read the character.
-		if (i) dc_serial_sleep(device->port, 16); // 16 ms
+		if (i) dc_iostream_sleep(device->iostream, 16); // 16 ms
 
-		status = dc_serial_write(device->port, command + i, 1, NULL);
+		status = dc_iostream_write(device->iostream, command + i, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
 			return status;
@@ -288,10 +286,10 @@ cochran_commander_packet (cochran_commander_device_t *device, dc_event_progress_
 
 	if (high_speed) {
 		// Give the DC time to process the command.
-		dc_serial_sleep(device->port, 45);
+		dc_iostream_sleep(device->iostream, 45);
 
 		// Rates are odd, like 806400 for the EMC, 115200 for commander
-		status = dc_serial_configure(device->port, device->layout->baudrate, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
+		status = dc_iostream_configure(device->iostream, device->layout->baudrate, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to set the high baud rate.");
 			return status;
@@ -306,7 +304,7 @@ cochran_commander_packet (cochran_commander_device_t *device, dc_event_progress_
 		if (len > 1024)
 			len = 1024;
 
-		status = dc_serial_read (device->port, answer + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, answer + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive data.");
 			return status;
@@ -414,7 +412,7 @@ cochran_commander_read (cochran_commander_device_t *device, dc_event_progress_t
 		return DC_STATUS_UNSUPPORTED;
 	}
 
-	dc_serial_sleep(device->port, 800);
+	dc_iostream_sleep(device->iostream, 800);
 
 	// set back to 9600 baud
 	rc = cochran_commander_serial_setup(device);
@@ -607,7 +605,7 @@ cochran_commander_read_all (cochran_commander_device_t *device, cochran_data_t *
 }
 
 dc_status_t
-cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+cochran_commander_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	cochran_commander_device_t *device = NULL;
@@ -623,26 +621,19 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	cochran_commander_device_set_fingerprint((dc_device_t *) device, NULL, 0);
 
-	// Open the device.
-	status = dc_serial_open (&device->port, device->base.context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (device->base.context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	status = cochran_commander_serial_setup(device);
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Read ID from the device
 	status = cochran_commander_read_id (device, device->id, sizeof(device->id));
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Device not responding.");
-		goto error_close;
+		goto error_free;
 	}
 
 	unsigned int model = cochran_commander_get_model(device);
@@ -662,37 +653,19 @@ cochran_commander_device_open (dc_device_t **out, dc_context_t *context, const c
 	default:
 		ERROR (context, "Unknown model");
 		status = DC_STATUS_UNSUPPORTED;
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
 }
 
 static dc_status_t
-cochran_commander_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-static dc_status_t
 cochran_commander_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	cochran_commander_device_t *device = (cochran_commander_device_t *) abstract;
diff --git a/src/cochran_commander.h b/src/cochran_commander.h
index e475b5f..84a521e 100644
--- a/src/cochran_commander.h
+++ b/src/cochran_commander.h
@@ -23,6 +23,7 @@
 #define COCHRAN_COMMANDER_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-cochran_commander_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+cochran_commander_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 cochran_commander_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/cressi_edy.c b/src/cressi_edy.c
index 226f32f..149955d 100644
--- a/src/cressi_edy.c
+++ b/src/cressi_edy.c
@@ -26,7 +26,6 @@
 #include "cressi_edy.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 #include "ringbuffer.h"
@@ -55,7 +54,7 @@ typedef struct cressi_edy_layout_t {
 
 typedef struct cressi_edy_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	const cressi_edy_layout_t *layout;
 	unsigned char fingerprint[SZ_PAGE / 2];
 	unsigned int model;
@@ -111,7 +110,7 @@ cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], u
 
 	for (unsigned int i = 0; i < csize; ++i) {
 		// Send the command to the device.
-		status = dc_serial_write (device->port, command + i, 1, NULL);
+		status = dc_iostream_write (device->iostream, command + i, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
 			return status;
@@ -119,7 +118,7 @@ cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], u
 
 		// Receive the echo.
 		unsigned char echo = 0;
-		status = dc_serial_read (device->port, &echo, 1, NULL);
+		status = dc_iostream_read (device->iostream, &echo, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the echo.");
 			return status;
@@ -134,7 +133,7 @@ cressi_edy_packet (cressi_edy_device_t *device, const unsigned char command[], u
 
 	if (asize) {
 		// Receive the answer of the device.
-		status = dc_serial_read (device->port, answer, asize, NULL);
+		status = dc_iostream_read (device->iostream, answer, asize, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -164,8 +163,8 @@ cressi_edy_transfer (cressi_edy_device_t *device, const unsigned char command[],
 			return rc;
 
 		// Delay the next attempt.
-		dc_serial_sleep (device->port, 300);
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_sleep (device->iostream, 300);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 	}
 
 	return DC_STATUS_SUCCESS;
@@ -217,7 +216,7 @@ cressi_edy_quit (cressi_edy_device_t *device)
 
 
 dc_status_t
-cressi_edy_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+cressi_edy_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	cressi_edy_device_t *device = NULL;
@@ -233,49 +232,42 @@ cressi_edy_device_open (dc_device_t **out, dc_context_t *context, const char *na
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->layout = NULL;
 	device->model = 0;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (1200 8N1).
-	status = dc_serial_configure (device->port, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep(device->port, 300);
-	dc_serial_purge(device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep(device->iostream, 300);
+	dc_iostream_purge(device->iostream, DC_DIRECTION_ALL);
 
 	// Send the init commands.
 	cressi_edy_init1 (device);
@@ -289,22 +281,20 @@ cressi_edy_device_open (dc_device_t **out, dc_context_t *context, const char *na
 	}
 
 	// Set the serial communication protocol (4800 8N1).
-	status = dc_serial_configure (device->port, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep(device->port, 300);
-	dc_serial_purge(device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep(device->iostream, 300);
+	dc_iostream_purge(device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -324,12 +314,6 @@ cressi_edy_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/cressi_edy.h b/src/cressi_edy.h
index d89f02f..7aa5a34 100644
--- a/src/cressi_edy.h
+++ b/src/cressi_edy.h
@@ -23,6 +23,7 @@
 #define CRESSI_EDY_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-cressi_edy_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+cressi_edy_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 cressi_edy_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/cressi_leonardo.c b/src/cressi_leonardo.c
index 5c1dd29..4f41b20 100644
--- a/src/cressi_leonardo.c
+++ b/src/cressi_leonardo.c
@@ -26,7 +26,6 @@
 #include "cressi_leonardo.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 #include "ringbuffer.h"
@@ -49,7 +48,7 @@
 
 typedef struct cressi_leonardo_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[5];
 } cressi_leonardo_device_t;
 
@@ -57,7 +56,6 @@ static dc_status_t cressi_leonardo_device_set_fingerprint (dc_device_t *abstract
 static dc_status_t cressi_leonardo_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t cressi_leonardo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t cressi_leonardo_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t cressi_leonardo_device_vtable = {
 	sizeof(cressi_leonardo_device_t),
@@ -67,7 +65,7 @@ static const dc_device_vtable_t cressi_leonardo_device_vtable = {
 	NULL, /* write */
 	cressi_leonardo_device_dump, /* dump */
 	cressi_leonardo_device_foreach, /* foreach */
-	cressi_leonardo_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
@@ -105,14 +103,14 @@ cressi_leonardo_packet (cressi_leonardo_device_t *device, const unsigned char co
 		return DC_STATUS_CANCELLED;
 
 	// Send the command to the device.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
 	// Receive the answer of the device.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -155,15 +153,15 @@ cressi_leonardo_transfer (cressi_leonardo_device_t *device, const unsigned char
 			return rc;
 
 		// Discard any garbage bytes.
-		dc_serial_sleep (device->port, 100);
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_sleep (device->iostream, 100);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 	}
 
 	return rc;
 }
 
 dc_status_t
-cressi_leonardo_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+cressi_leonardo_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	cressi_leonardo_device_t *device = NULL;
@@ -179,84 +177,59 @@ cressi_leonardo_device_open (dc_device_t **out, dc_context_t *context, const cha
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the RTS line.
-	status = dc_serial_set_rts (device->port, 1);
+	status = dc_iostream_set_rts (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
-	dc_serial_sleep (device->port, 200);
+	dc_iostream_sleep (device->iostream, 200);
 
 	// Clear the DTR line.
-	status = dc_serial_set_dtr (device->port, 0);
+	status = dc_iostream_set_dtr (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
-	dc_serial_sleep (device->port, 100);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 100);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
 }
 
 static dc_status_t
-cressi_leonardo_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-static dc_status_t
 cressi_leonardo_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	cressi_leonardo_device_t *device = (cressi_leonardo_device_t *) abstract;
@@ -333,7 +306,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send the command header to the dive computer.
 	const unsigned char command[] = {0x7B, 0x31, 0x32, 0x33, 0x44, 0x42, 0x41, 0x7d};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -341,7 +314,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Receive the header packet.
 	unsigned char header[7] = {0};
-	status = dc_serial_read (device->port, header, sizeof (header), NULL);
+	status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -363,7 +336,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Increase the packet size if more data is immediately available.
 		size_t available = 0;
-		status = dc_serial_get_available (device->port, &available);
+		status = dc_iostream_get_available (device->iostream, &available);
 		if (status == DC_STATUS_SUCCESS && available > len)
 			len = available;
 
@@ -372,7 +345,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 			len = SZ_MEMORY - nbytes;
 
 		// Read the packet.
-		status = dc_serial_read (device->port, data + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, data + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -387,7 +360,7 @@ cressi_leonardo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Receive the trailer packet.
 	unsigned char trailer[4] = {0};
-	status = dc_serial_read (device->port, trailer, sizeof (trailer), NULL);
+	status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
diff --git a/src/cressi_leonardo.h b/src/cressi_leonardo.h
index c17b41b..982bb01 100644
--- a/src/cressi_leonardo.h
+++ b/src/cressi_leonardo.h
@@ -23,6 +23,7 @@
 #define CRESSI_LEONARDO_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-cressi_leonardo_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+cressi_leonardo_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 cressi_leonardo_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/device.c b/src/device.c
index 2f3e1af..0adc263 100644
--- a/src/device.c
+++ b/src/device.c
@@ -99,7 +99,7 @@ dc_device_deallocate (dc_device_t *device)
 }
 
 dc_status_t
-dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, const char *name)
+dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descriptor, dc_iostream_t *iostream)
 {
 	dc_status_t rc = DC_STATUS_SUCCESS;
 	dc_device_t *device = NULL;
@@ -109,103 +109,103 @@ dc_device_open (dc_device_t **out, dc_context_t *context, dc_descriptor_t *descr
 
 	switch (dc_descriptor_get_type (descriptor)) {
 	case DC_FAMILY_SUUNTO_SOLUTION:
-		rc = suunto_solution_device_open (&device, context, name);
+		rc = suunto_solution_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_SUUNTO_EON:
-		rc = suunto_eon_device_open (&device, context, name);
+		rc = suunto_eon_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_SUUNTO_VYPER:
-		rc = suunto_vyper_device_open (&device, context, name);
+		rc = suunto_vyper_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_SUUNTO_VYPER2:
-		rc = suunto_vyper2_device_open (&device, context, name);
+		rc = suunto_vyper2_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_SUUNTO_D9:
-		rc = suunto_d9_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
+		rc = suunto_d9_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
 		break;
 	case DC_FAMILY_SUUNTO_EONSTEEL:
-		rc = suunto_eonsteel_device_open (&device, context);
+		rc = suunto_eonsteel_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_UWATEC_ALADIN:
-		rc = uwatec_aladin_device_open (&device, context, name);
+		rc = uwatec_aladin_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_UWATEC_MEMOMOUSE:
-		rc = uwatec_memomouse_device_open (&device, context, name);
+		rc = uwatec_memomouse_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_UWATEC_SMART:
-		rc = uwatec_smart_device_open (&device, context);
+		rc = uwatec_smart_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_UWATEC_MERIDIAN:
-		rc = uwatec_meridian_device_open (&device, context, name);
+		rc = uwatec_meridian_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_REEFNET_SENSUS:
-		rc = reefnet_sensus_device_open (&device, context, name);
+		rc = reefnet_sensus_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_REEFNET_SENSUSPRO:
-		rc = reefnet_sensuspro_device_open (&device, context, name);
+		rc = reefnet_sensuspro_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_REEFNET_SENSUSULTRA:
-		rc = reefnet_sensusultra_device_open (&device, context, name);
+		rc = reefnet_sensusultra_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_OCEANIC_VTPRO:
-		rc = oceanic_vtpro_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
+		rc = oceanic_vtpro_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
 		break;
 	case DC_FAMILY_OCEANIC_VEO250:
-		rc = oceanic_veo250_device_open (&device, context, name);
+		rc = oceanic_veo250_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_OCEANIC_ATOM2:
-		rc = oceanic_atom2_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
+		rc = oceanic_atom2_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
 		break;
 	case DC_FAMILY_MARES_NEMO:
-		rc = mares_nemo_device_open (&device, context, name);
+		rc = mares_nemo_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_MARES_PUCK:
-		rc = mares_puck_device_open (&device, context, name);
+		rc = mares_puck_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_MARES_DARWIN:
-		rc = mares_darwin_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
+		rc = mares_darwin_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
 		break;
 	case DC_FAMILY_MARES_ICONHD:
-		rc = mares_iconhd_device_open (&device, context, name);
+		rc = mares_iconhd_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_HW_OSTC:
-		rc = hw_ostc_device_open (&device, context, name);
+		rc = hw_ostc_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_HW_FROG:
-		rc = hw_frog_device_open (&device, context, name);
+		rc = hw_frog_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_HW_OSTC3:
-		rc = hw_ostc3_device_open (&device, context, name);
+		rc = hw_ostc3_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_CRESSI_EDY:
-		rc = cressi_edy_device_open (&device, context, name);
+		rc = cressi_edy_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_CRESSI_LEONARDO:
-		rc = cressi_leonardo_device_open (&device, context, name);
+		rc = cressi_leonardo_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_ZEAGLE_N2ITION3:
-		rc = zeagle_n2ition3_device_open (&device, context, name);
+		rc = zeagle_n2ition3_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_ATOMICS_COBALT:
 		rc = atomics_cobalt_device_open (&device, context);
 		break;
 	case DC_FAMILY_SHEARWATER_PREDATOR:
-		rc = shearwater_predator_device_open (&device, context, name);
+		rc = shearwater_predator_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_SHEARWATER_PETREL:
-		rc = shearwater_petrel_device_open (&device, context, name);
+		rc = shearwater_petrel_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_DIVERITE_NITEKQ:
-		rc = diverite_nitekq_device_open (&device, context, name);
+		rc = diverite_nitekq_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_CITIZEN_AQUALAND:
-		rc = citizen_aqualand_device_open (&device, context, name);
+		rc = citizen_aqualand_device_open (&device, context, iostream);
 		break;
 	case DC_FAMILY_DIVESYSTEM_IDIVE:
-		rc = divesystem_idive_device_open (&device, context, name, dc_descriptor_get_model (descriptor));
+		rc = divesystem_idive_device_open (&device, context, iostream, dc_descriptor_get_model (descriptor));
 		break;
 	case DC_FAMILY_COCHRAN_COMMANDER:
-		rc = cochran_commander_device_open (&device, context, name);
+		rc = cochran_commander_device_open (&device, context, iostream);
 		break;
 	default:
 		return DC_STATUS_INVALIDARGS;
diff --git a/src/diverite_nitekq.c b/src/diverite_nitekq.c
index 205115a..74e82ab 100644
--- a/src/diverite_nitekq.c
+++ b/src/diverite_nitekq.c
@@ -27,7 +27,6 @@
 #include "context-private.h"
 #include "device-private.h"
 #include "checksum.h"
-#include "serial.h"
 #include "array.h"
 
 #define ISINSTANCE(device) dc_device_isinstance((device), &diverite_nitekq_device_vtable)
@@ -51,7 +50,7 @@
 
 typedef struct diverite_nitekq_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char version[32];
 	unsigned char fingerprint[SZ_LOGBOOK];
 } diverite_nitekq_device_t;
@@ -86,7 +85,7 @@ diverite_nitekq_send (diverite_nitekq_device_t *device, unsigned char cmd)
 
 	// Send the command.
 	unsigned char command[] = {cmd};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -103,7 +102,7 @@ diverite_nitekq_receive (diverite_nitekq_device_t *device, unsigned char data[],
 	dc_device_t *abstract = (dc_device_t *) device;
 
 	// Read the answer.
-	status = dc_serial_read (device->port, data, size, NULL);
+	status = dc_iostream_read (device->iostream, data, size, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -111,7 +110,7 @@ diverite_nitekq_receive (diverite_nitekq_device_t *device, unsigned char data[],
 
 	// Read the checksum.
 	unsigned char checksum[2] = {0};
-	status = dc_serial_read (device->port, checksum, sizeof (checksum), NULL);
+	status = dc_iostream_read (device->iostream, checksum, sizeof (checksum), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the checksum.");
 		return status;
@@ -129,14 +128,14 @@ diverite_nitekq_handshake (diverite_nitekq_device_t *device)
 
 	// Send the command.
 	unsigned char command[] = {HANDSHAKE};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
 	// Read the answer.
-	status = dc_serial_read (device->port, device->version, sizeof (device->version), NULL);
+	status = dc_iostream_read (device->iostream, device->version, sizeof (device->version), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -147,7 +146,7 @@ diverite_nitekq_handshake (diverite_nitekq_device_t *device)
 
 
 dc_status_t
-diverite_nitekq_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+diverite_nitekq_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	diverite_nitekq_device_t *device = NULL;
@@ -163,47 +162,38 @@ diverite_nitekq_device_open (dc_device_t **out, dc_context_t *context, const cha
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 100);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 100);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Perform the handshaking.
 	status = diverite_nitekq_handshake (device);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to handshake.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -223,12 +213,6 @@ diverite_nitekq_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/diverite_nitekq.h b/src/diverite_nitekq.h
index c8987e2..e016cab 100644
--- a/src/diverite_nitekq.h
+++ b/src/diverite_nitekq.h
@@ -23,6 +23,7 @@
 #define DIVERITE_NITEKQ_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-diverite_nitekq_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+diverite_nitekq_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 diverite_nitekq_parser_create (dc_parser_t **parser, dc_context_t *context);
diff --git a/src/divesystem_idive.c b/src/divesystem_idive.c
index e2a5cda..5b90059 100644
--- a/src/divesystem_idive.c
+++ b/src/divesystem_idive.c
@@ -25,7 +25,6 @@
 #include "divesystem_idive.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -62,14 +61,13 @@ typedef struct divesystem_idive_commands_t {
 
 typedef struct divesystem_idive_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[4];
 	unsigned int model;
 } divesystem_idive_device_t;
 
 static dc_status_t divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t divesystem_idive_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t divesystem_idive_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t divesystem_idive_device_vtable = {
 	sizeof(divesystem_idive_device_t),
@@ -79,7 +77,7 @@ static const dc_device_vtable_t divesystem_idive_device_vtable = {
 	NULL, /* write */
 	NULL, /* dump */
 	divesystem_idive_device_foreach, /* foreach */
-	divesystem_idive_device_close /* close */
+	NULL /* close */
 };
 
 static const divesystem_idive_commands_t idive = {
@@ -107,7 +105,7 @@ static const divesystem_idive_commands_t ix3m_apos4 = {
 };
 
 dc_status_t
-divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
+divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	divesystem_idive_device_t *device = NULL;
@@ -123,41 +121,32 @@ divesystem_idive_device_open (dc_device_t **out, dc_context_t *context, const ch
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 	device->model = model;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 300);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 300);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -165,23 +154,6 @@ error_free:
 
 
 static dc_status_t
-divesystem_idive_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	divesystem_idive_device_t *device = (divesystem_idive_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 divesystem_idive_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	divesystem_idive_device_t *device = (divesystem_idive_device_t *) abstract;
@@ -221,7 +193,7 @@ divesystem_idive_send (divesystem_idive_device_t *device, const unsigned char co
 	packet[csize + 3] = (crc     ) & 0xFF;
 
 	// Send the data packet.
-	status = dc_serial_write (device->port, packet, csize + 4, NULL);
+	status = dc_iostream_write (device->iostream, packet, csize + 4, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -245,7 +217,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
 
 	// Read the packet start byte.
 	while (1) {
-		status = dc_serial_read (device->port, packet + 0, 1, NULL);
+		status = dc_iostream_read (device->iostream, packet + 0, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the packet start byte.");
 			return status;
@@ -256,7 +228,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
 	}
 
 	// Read the packet length.
-	status = dc_serial_read (device->port, packet + 1, 1, NULL);
+	status = dc_iostream_read (device->iostream, packet + 1, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the packet length.");
 		return status;
@@ -269,7 +241,7 @@ divesystem_idive_receive (divesystem_idive_device_t *device, unsigned char answe
 	}
 
 	// Read the packet payload and checksum.
-	status = dc_serial_read (device->port, packet + 2, len + 2, NULL);
+	status = dc_iostream_read (device->iostream, packet + 2, len + 2, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the packet payload and checksum.");
 		return status;
@@ -345,7 +317,7 @@ divesystem_idive_transfer (divesystem_idive_device_t *device, const unsigned cha
 			return DC_STATUS_PROTOCOL;
 
 		// Delay the next attempt.
-		dc_serial_sleep(device->port, 100);
+		dc_iostream_sleep(device->iostream, 100);
 	}
 
 	// Verify the length of the packet.
diff --git a/src/divesystem_idive.h b/src/divesystem_idive.h
index 20ed729..8c79866 100644
--- a/src/divesystem_idive.h
+++ b/src/divesystem_idive.h
@@ -23,6 +23,7 @@
 #define DIVESYSTEM_IDIVE_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-divesystem_idive_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
+divesystem_idive_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
 
 dc_status_t
 divesystem_idive_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/hw_frog.c b/src/hw_frog.c
index d8b89ca..2002649 100644
--- a/src/hw_frog.c
+++ b/src/hw_frog.c
@@ -25,7 +25,6 @@
 #include "hw_frog.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "ringbuffer.h"
 #include "array.h"
@@ -55,7 +54,7 @@
 
 typedef struct hw_frog_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[5];
 } hw_frog_device_t;
 
@@ -112,7 +111,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 
 	// Send the command.
 	unsigned char command[1] = {cmd};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -121,7 +120,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 	if (cmd != INIT && cmd != HEADER) {
 		// Read the echo.
 		unsigned char answer[1] = {0};
-		status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+		status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the echo.");
 			return status;
@@ -136,7 +135,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 
 	if (input) {
 		// Send the input data packet.
-		status = dc_serial_write (device->port, input, isize, NULL);
+		status = dc_iostream_write (device->iostream, input, isize, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the data packet.");
 			return status;
@@ -151,7 +150,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 
 			// Increase the packet size if more data is immediately available.
 			size_t available = 0;
-			status = dc_serial_get_available (device->port, &available);
+			status = dc_iostream_get_available (device->iostream, &available);
 			if (status == DC_STATUS_SUCCESS && available > len)
 				len = available;
 
@@ -160,7 +159,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 				len = osize - nbytes;
 
 			// Read the packet.
-			status = dc_serial_read (device->port, output + nbytes, len, NULL);
+			status = dc_iostream_read (device->iostream, output + nbytes, len, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				ERROR (abstract->context, "Failed to receive the answer.");
 				return status;
@@ -179,7 +178,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 	if (cmd != EXIT) {
 		// Read the ready byte.
 		unsigned char answer[1] = {0};
-		status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+		status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 		if (status != sizeof (answer)) {
 			ERROR (abstract->context, "Failed to receive the ready byte.");
 			return status;
@@ -197,7 +196,7 @@ hw_frog_transfer (hw_frog_device_t *device,
 
 
 dc_status_t
-hw_frog_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+hw_frog_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	hw_frog_device_t *device = NULL;
@@ -213,47 +212,38 @@ hw_frog_device_open (dc_device_t **out, dc_context_t *context, const char *name)
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 300);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 300);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Send the init command.
 	status = hw_frog_transfer (device, NULL, INIT, NULL, 0, NULL, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to send the command.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t *) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -274,12 +264,6 @@ hw_frog_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/hw_frog.h b/src/hw_frog.h
index 260e80d..74cf5af 100644
--- a/src/hw_frog.h
+++ b/src/hw_frog.h
@@ -23,6 +23,7 @@
 #define HW_FROG_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/hw_frog.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-hw_frog_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+hw_frog_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
diff --git a/src/hw_ostc.c b/src/hw_ostc.c
index 3f46603..e05fb35 100644
--- a/src/hw_ostc.c
+++ b/src/hw_ostc.c
@@ -25,7 +25,6 @@
 #include "hw_ostc.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 #include "ihex.h"
@@ -58,7 +57,7 @@
 
 typedef struct hw_ostc_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[5];
 } hw_ostc_device_t;
 
@@ -70,7 +69,6 @@ typedef struct hw_ostc_firmware_t {
 static dc_status_t hw_ostc_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t hw_ostc_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t hw_ostc_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t hw_ostc_device_vtable = {
 	sizeof(hw_ostc_device_t),
@@ -80,7 +78,7 @@ static const dc_device_vtable_t hw_ostc_device_vtable = {
 	NULL, /* write */
 	hw_ostc_device_dump, /* dump */
 	hw_ostc_device_foreach, /* foreach */
-	hw_ostc_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
@@ -94,7 +92,7 @@ hw_ostc_send (hw_ostc_device_t *device, unsigned char cmd, unsigned int echo)
 
 	// Send the command.
 	unsigned char command[1] = {cmd};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -103,7 +101,7 @@ hw_ostc_send (hw_ostc_device_t *device, unsigned char cmd, unsigned int echo)
 	if (echo) {
 		// Read the echo.
 		unsigned char answer[1] = {0};
-		status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+		status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the echo.");
 			return status;
@@ -121,7 +119,7 @@ hw_ostc_send (hw_ostc_device_t *device, unsigned char cmd, unsigned int echo)
 
 
 dc_status_t
-hw_ostc_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+hw_ostc_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	hw_ostc_device_t *device = NULL;
@@ -137,40 +135,31 @@ hw_ostc_device_open (dc_device_t **out, dc_context_t *context, const char *name)
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data.
-	status = dc_serial_set_timeout (device->port, 4000);
+	status = dc_iostream_set_timeout (device->iostream, 4000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 100);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 100);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -178,23 +167,6 @@ error_free:
 
 
 static dc_status_t
-hw_ostc_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	hw_ostc_device_t *device = (hw_ostc_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 hw_ostc_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	hw_ostc_device_t *device = (hw_ostc_device_t *) abstract;
@@ -230,7 +202,7 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send the command.
 	unsigned char command[1] = {'a'};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -238,7 +210,7 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Read the header.
 	unsigned char header[SZ_HEADER] = {0};
-	status = dc_serial_read (device->port, header, sizeof (header), NULL);
+	status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the header.");
 		return status;
@@ -284,7 +256,7 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Increase the packet size if more data is immediately available.
 		size_t available = 0;
-		status = dc_serial_get_available (device->port, &available);
+		status = dc_iostream_get_available (device->iostream, &available);
 		if (status == DC_STATUS_SUCCESS && available > len)
 			len = available;
 
@@ -293,7 +265,7 @@ hw_ostc_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 			len = size - nbytes;
 
 		// Read the packet.
-		status = dc_serial_read (device->port, data + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, data + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -367,7 +339,7 @@ hw_ostc_device_md2hash (dc_device_t *abstract, unsigned char data[], unsigned in
 		return rc;
 
 	// Read the answer.
-	status = dc_serial_read (device->port, data, SZ_MD2HASH, NULL);
+	status = dc_iostream_read (device->iostream, data, SZ_MD2HASH, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -400,7 +372,7 @@ hw_ostc_device_clock (dc_device_t *abstract, const dc_datetime_t *datetime)
 	unsigned char packet[6] = {
 		datetime->hour, datetime->minute, datetime->second,
 		datetime->month, datetime->day, datetime->year - 2000};
-	status = dc_serial_write (device->port, packet, sizeof (packet), NULL);
+	status = dc_iostream_write (device->iostream, packet, sizeof (packet), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the data packet.");
 		return status;
@@ -436,7 +408,7 @@ hw_ostc_device_eeprom_read (dc_device_t *abstract, unsigned int bank, unsigned c
 		return rc;
 
 	// Read the answer.
-	status = dc_serial_read (device->port, data, SZ_EEPROM, NULL);
+	status = dc_iostream_read (device->iostream, data, SZ_EEPROM, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -557,7 +529,7 @@ hw_ostc_device_screenshot (dc_device_t *abstract, dc_buffer_t *buffer, hw_ostc_f
 	unsigned int npixels = 0;
 	while (npixels < WIDTH * HEIGHT) {
 		unsigned char raw[3] = {0};
-		status = dc_serial_read (device->port, raw, 1, NULL);
+		status = dc_iostream_read (device->iostream, raw, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the packet.");
 			return status;
@@ -575,7 +547,7 @@ hw_ostc_device_screenshot (dc_device_t *abstract, dc_buffer_t *buffer, hw_ostc_f
 			count &= 0x3F;
 		} else {
 			// Color pixel.
-			status = dc_serial_read (device->port, raw + 1, 2, NULL);
+			status = dc_iostream_read (device->iostream, raw + 1, 2, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				ERROR (abstract->context, "Failed to receive the packet.");
 				return status;
@@ -775,7 +747,7 @@ hw_ostc_firmware_setup_internal (hw_ostc_device_t *device)
 
 	// Send the command.
 	unsigned char command[1] = {0xC1};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -783,7 +755,7 @@ hw_ostc_firmware_setup_internal (hw_ostc_device_t *device)
 
 	// Read the response.
 	unsigned char answer[2] = {0};
-	status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+	status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the response.");
 		return status;
@@ -826,7 +798,7 @@ hw_ostc_firmware_write_internal (hw_ostc_device_t *device, unsigned char *data,
 	dc_device_t *abstract = (dc_device_t *) device;
 
 	// Send the packet.
-	status = dc_serial_write (device->port, data, size, NULL);
+	status = dc_iostream_write (device->iostream, data, size, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the packet.");
 		return status;
@@ -834,7 +806,7 @@ hw_ostc_firmware_write_internal (hw_ostc_device_t *device, unsigned char *data,
 
 	// Read the response.
 	unsigned char answer[1] = {0};
-	status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+	status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the response.");
 		return status;
@@ -906,13 +878,13 @@ hw_ostc_device_fwupdate (dc_device_t *abstract, const char *filename)
 	// bootloader needs to be send repeatedly, until the response packet is
 	// received. Thus the time between each two attempts is directly controlled
 	// by the timeout value.
-	dc_serial_set_timeout (device->port, 300);
+	dc_iostream_set_timeout (device->iostream, 300);
 
 	// Setup the bootloader.
 	const unsigned int baudrates[] = {19200, 115200};
 	for (unsigned int i = 0; i < C_ARRAY_SIZE(baudrates); ++i) {
 		// Adjust the baudrate.
-		rc = dc_serial_configure (device->port, baudrates[i], 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+		rc = dc_iostream_configure (device->iostream, baudrates[i], 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 		if (rc != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to set the terminal attributes.");
 			free (firmware);
@@ -932,7 +904,7 @@ hw_ostc_device_fwupdate (dc_device_t *abstract, const char *filename)
 	}
 
 	// Increase the timeout again.
-	dc_serial_set_timeout (device->port, 1000);
+	dc_iostream_set_timeout (device->iostream, 1000);
 
 	// Enable progress notifications.
 	dc_event_progress_t progress = EVENT_PROGRESS_INITIALIZER;
diff --git a/src/hw_ostc.h b/src/hw_ostc.h
index 3f54a7b..a0ce743 100644
--- a/src/hw_ostc.h
+++ b/src/hw_ostc.h
@@ -23,6 +23,7 @@
 #define HW_OSTC_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/hw_ostc.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-hw_ostc_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+hw_ostc_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 hw_ostc_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int hwos);
diff --git a/src/hw_ostc3.c b/src/hw_ostc3.c
index e7b7d9e..c7b881c 100644
--- a/src/hw_ostc3.c
+++ b/src/hw_ostc3.c
@@ -27,7 +27,6 @@
 #include "hw_ostc3.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "array.h"
 #include "aes.h"
 
@@ -94,7 +93,7 @@ typedef enum hw_ostc3_state_t {
 
 typedef struct hw_ostc3_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int hardware;
 	unsigned int feature;
 	unsigned int model;
@@ -198,7 +197,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 	// Send the command.
 	unsigned char command[1] = {cmd};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -206,7 +205,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 	// Read the echo.
 	unsigned char echo[1] = {0};
-	status = dc_serial_read (device->port, echo, sizeof (echo), NULL);
+	status = dc_iostream_read (device->iostream, echo, sizeof (echo), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the echo.");
 		return status;
@@ -235,7 +234,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 				len = isize - nbytes;
 
 			// Write the packet.
-			status = dc_serial_write (device->port, input + nbytes, len, NULL);
+			status = dc_iostream_write (device->iostream, input + nbytes, len, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				ERROR (abstract->context, "Failed to send the data packet.");
 				return status;
@@ -259,7 +258,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 			// Increase the packet size if more data is immediately available.
 			size_t available = 0;
-			status = dc_serial_get_available (device->port, &available);
+			status = dc_iostream_get_available (device->iostream, &available);
 			if (status == DC_STATUS_SUCCESS && available > len)
 				len = available;
 
@@ -268,7 +267,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 				len = osize - nbytes;
 
 			// Read the packet.
-			status = dc_serial_read (device->port, output + nbytes, len, NULL);
+			status = dc_iostream_read (device->iostream, output + nbytes, len, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				ERROR (abstract->context, "Failed to receive the answer.");
 				return status;
@@ -288,18 +287,18 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 		unsigned int count = delay / 100;
 		for (unsigned int i = 0; i < count; ++i) {
 			size_t available = 0;
-			status = dc_serial_get_available (device->port, &available);
+			status = dc_iostream_get_available (device->iostream, &available);
 			if (status == DC_STATUS_SUCCESS && available > 0)
 				break;
 
-			dc_serial_sleep (device->port, 100);
+			dc_iostream_sleep (device->iostream, 100);
 		}
 	}
 
 	if (cmd != EXIT) {
 		// Read the ready byte.
 		unsigned char answer[1] = {0};
-		status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+		status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the ready byte.");
 			return status;
@@ -317,7 +316,7 @@ hw_ostc3_transfer (hw_ostc3_device_t *device,
 
 
 dc_status_t
-hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	hw_ostc3_device_t *device = NULL;
@@ -333,36 +332,29 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->hardware = INVALID;
 	device->feature = 0;
 	device->model = 0;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 300);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 300);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	device->state = OPEN;
 
@@ -370,8 +362,6 @@ hw_ostc3_device_open (dc_device_t **out, dc_context_t *context, const char *name
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -435,17 +425,17 @@ hw_ostc3_device_init_service (hw_ostc3_device_t *device)
 	unsigned char output[5];
 
 	// We cant use hw_ostc3_transfer here, due to the different echos
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to send the command.");
 		return status;
 	}
 
 	// Give the device some time to enter service mode
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Read the response
-	status = dc_serial_read (device->port, output, sizeof (output), NULL);
+	status = dc_iostream_read (device->iostream, output, sizeof (output), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to receive the echo.");
 		return status;
@@ -532,12 +522,6 @@ hw_ostc3_device_close (dc_device_t *abstract)
 		}
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/hw_ostc3.h b/src/hw_ostc3.h
index eec35b7..92cf277 100644
--- a/src/hw_ostc3.h
+++ b/src/hw_ostc3.h
@@ -23,6 +23,7 @@
 #define HW_OSTC3_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/hw_ostc3.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+hw_ostc3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 hw_ostc3_parser_create (dc_parser_t **out, dc_context_t *context, unsigned int model);
diff --git a/src/mares_common.c b/src/mares_common.c
index b157dc2..a890701 100644
--- a/src/mares_common.c
+++ b/src/mares_common.c
@@ -47,12 +47,12 @@
 #define GAUGE    3
 
 void
-mares_common_device_init (mares_common_device_t *device)
+mares_common_device_init (mares_common_device_t *device, dc_iostream_t *iostream)
 {
 	assert (device != NULL);
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->echo = 0;
 	device->delay = 0;
 }
@@ -88,11 +88,11 @@ mares_common_packet (mares_common_device_t *device, const unsigned char command[
 		return DC_STATUS_CANCELLED;
 
 	if (device->delay) {
-		dc_serial_sleep (device->port, device->delay);
+		dc_iostream_sleep (device->iostream, device->delay);
 	}
 
 	// Send the command to the device.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -101,7 +101,7 @@ mares_common_packet (mares_common_device_t *device, const unsigned char command[
 	if (device->echo) {
 		// Receive the echo of the command.
 		unsigned char echo[PACKETSIZE] = {0};
-		status = dc_serial_read (device->port, echo, csize, NULL);
+		status = dc_iostream_read (device->iostream, echo, csize, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the echo.");
 			return status;
@@ -114,7 +114,7 @@ mares_common_packet (mares_common_device_t *device, const unsigned char command[
 	}
 
 	// Receive the answer of the device.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -155,8 +155,8 @@ mares_common_transfer (mares_common_device_t *device, const unsigned char comman
 			return rc;
 
 		// Discard any garbage bytes.
-		dc_serial_sleep (device->port, 100);
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_sleep (device->iostream, 100);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 	}
 
 	return rc;
diff --git a/src/mares_common.h b/src/mares_common.h
index 4652d06..000ea28 100644
--- a/src/mares_common.h
+++ b/src/mares_common.h
@@ -22,8 +22,9 @@
 #ifndef MARES_COMMON_H
 #define MARES_COMMON_H
 
+#include <libdivecomputer/iostream.h>
+
 #include "device-private.h"
-#include "serial.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -41,13 +42,13 @@ typedef struct mares_common_layout_t {
 
 typedef struct mares_common_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int echo;
 	unsigned int delay;
 } mares_common_device_t;
 
 void
-mares_common_device_init (mares_common_device_t *device);
+mares_common_device_init (mares_common_device_t *device, dc_iostream_t *iostream);
 
 dc_status_t
 mares_common_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
diff --git a/src/mares_darwin.c b/src/mares_darwin.c
index 9ac56ba..eb79974 100644
--- a/src/mares_darwin.c
+++ b/src/mares_darwin.c
@@ -60,7 +60,6 @@ typedef struct mares_darwin_device_t {
 static dc_status_t mares_darwin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t mares_darwin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t mares_darwin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t mares_darwin_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t mares_darwin_device_vtable = {
 	sizeof(mares_darwin_device_t),
@@ -70,7 +69,7 @@ static const dc_device_vtable_t mares_darwin_device_vtable = {
 	NULL, /* write */
 	mares_darwin_device_dump, /* dump */
 	mares_darwin_device_foreach, /* foreach */
-	mares_darwin_device_close /* close */
+	NULL /* close */
 };
 
 static const mares_darwin_layout_t mares_darwin_layout = {
@@ -97,7 +96,7 @@ static dc_status_t
 mares_darwin_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-mares_darwin_device_open (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
+mares_darwin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	mares_darwin_device_t *device = NULL;
@@ -113,7 +112,7 @@ mares_darwin_device_open (dc_device_t **out, dc_context_t *context, const char *
 	}
 
 	// Initialize the base class.
-	mares_common_device_init (&device->base);
+	mares_common_device_init (&device->base, iostream);
 
 	// Set the default values.
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
@@ -123,44 +122,37 @@ mares_darwin_device_open (dc_device_t **out, dc_context_t *context, const char *
 	else
 		device->layout = &mares_darwin_layout;
 
-	// Open the device.
-	status = dc_serial_open (&device->base.port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->base.port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->base.iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->base.port, 1000);
+	status = dc_iostream_set_timeout (device->base.iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->base.port, 1);
+	status = dc_iostream_set_dtr (device->base.iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the RTS line.
-	status = dc_serial_set_rts (device->base.port, 1);
+	status = dc_iostream_set_rts (device->base.iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->base.port, 100);
-	dc_serial_purge (device->base.port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->base.iostream, 100);
+	dc_iostream_purge (device->base.iostream, DC_DIRECTION_ALL);
 
 	// Override the base class values.
 	device->base.echo = 1;
@@ -170,29 +162,11 @@ mares_darwin_device_open (dc_device_t **out, dc_context_t *context, const char *
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->base.port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
 }
 
-static dc_status_t
-mares_darwin_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	mares_darwin_device_t *device = (mares_darwin_device_t *) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->base.port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
 
 static dc_status_t
 mares_darwin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
diff --git a/src/mares_darwin.h b/src/mares_darwin.h
index e7767ec..a902014 100644
--- a/src/mares_darwin.h
+++ b/src/mares_darwin.h
@@ -23,6 +23,7 @@
 #define MARES_DARWIN_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-mares_darwin_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
+mares_darwin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
 
 dc_status_t
 mares_darwin_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/mares_iconhd.c b/src/mares_iconhd.c
index 3a73968..d9f4ab1 100644
--- a/src/mares_iconhd.c
+++ b/src/mares_iconhd.c
@@ -26,7 +26,6 @@
 #include "mares_iconhd.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "array.h"
 #include "rbstream.h"
 
@@ -65,7 +64,7 @@ typedef struct mares_iconhd_model_t {
 
 typedef struct mares_iconhd_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	const mares_iconhd_layout_t *layout;
 	unsigned char fingerprint[10];
 	unsigned char version[140];
@@ -77,7 +76,6 @@ static dc_status_t mares_iconhd_device_set_fingerprint (dc_device_t *abstract, c
 static dc_status_t mares_iconhd_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t mares_iconhd_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t mares_iconhd_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t mares_iconhd_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t mares_iconhd_device_vtable = {
 	sizeof(mares_iconhd_device_t),
@@ -87,7 +85,7 @@ static const dc_device_vtable_t mares_iconhd_device_vtable = {
 	NULL, /* write */
 	mares_iconhd_device_dump, /* dump */
 	mares_iconhd_device_foreach, /* foreach */
-	mares_iconhd_device_close /* close */
+	NULL /* close */
 };
 
 static const mares_iconhd_layout_t mares_iconhd_layout = {
@@ -156,7 +154,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 		return DC_STATUS_CANCELLED;
 
 	// Send the command header to the dive computer.
-	status = dc_serial_write (device->port, command, 2, NULL);
+	status = dc_iostream_write (device->iostream, command, 2, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -164,7 +162,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 
 	// Receive the header byte.
 	unsigned char header[1] = {0};
-	status = dc_serial_read (device->port, header, sizeof (header), NULL);
+	status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -178,7 +176,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 
 	// Send the command payload to the dive computer.
 	if (csize > 2) {
-		status = dc_serial_write (device->port, command + 2, csize - 2, NULL);
+		status = dc_iostream_write (device->iostream, command + 2, csize - 2, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
 			return status;
@@ -186,7 +184,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 	}
 
 	// Read the packet.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -194,7 +192,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 
 	// Receive the trailer byte.
 	unsigned char trailer[1] = {0};
-	status = dc_serial_read (device->port, trailer, sizeof (trailer), NULL);
+	status = dc_iostream_read (device->iostream, trailer, sizeof (trailer), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -211,7 +209,7 @@ mares_iconhd_transfer (mares_iconhd_device_t *device,
 
 
 dc_status_t
-mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	mares_iconhd_device_t *device = NULL;
@@ -227,57 +225,50 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, const char *
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->layout = NULL;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 	memset (device->version, 0, sizeof (device->version));
 	device->model = 0;
 	device->packetsize = 0;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8E1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_EVEN, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the DTR line.
-	status = dc_serial_set_dtr (device->port, 0);
+	status = dc_iostream_set_dtr (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Send the version command.
 	unsigned char command[] = {0xC2, 0x67};
 	status = mares_iconhd_transfer (device, command, sizeof (command),
 		device->version, sizeof (device->version));
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Autodetect the model using the version packet.
@@ -313,8 +304,6 @@ mares_iconhd_device_open (dc_device_t **out, dc_context_t *context, const char *
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -322,23 +311,6 @@ error_free:
 
 
 static dc_status_t
-mares_iconhd_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	mares_iconhd_device_t *device = (mares_iconhd_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 mares_iconhd_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	mares_iconhd_device_t *device = (mares_iconhd_device_t *) abstract;
diff --git a/src/mares_iconhd.h b/src/mares_iconhd.h
index b762843..55a50d0 100644
--- a/src/mares_iconhd.h
+++ b/src/mares_iconhd.h
@@ -23,6 +23,7 @@
 #define MARES_ICONHD_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+mares_iconhd_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 mares_iconhd_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/mares_nemo.c b/src/mares_nemo.c
index 2ec3588..92bcd18 100644
--- a/src/mares_nemo.c
+++ b/src/mares_nemo.c
@@ -26,7 +26,6 @@
 #include "mares_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -45,14 +44,13 @@
 
 typedef struct mares_nemo_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[5];
 } mares_nemo_device_t;
 
 static dc_status_t mares_nemo_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t mares_nemo_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t mares_nemo_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t mares_nemo_device_vtable = {
 	sizeof(mares_nemo_device_t),
@@ -62,7 +60,7 @@ static const dc_device_vtable_t mares_nemo_device_vtable = {
 	NULL, /* write */
 	mares_nemo_device_dump, /* dump */
 	mares_nemo_device_foreach, /* foreach */
-	mares_nemo_device_close /* close */
+	NULL /* close */
 };
 
 static const mares_common_layout_t mares_nemo_layout = {
@@ -83,7 +81,7 @@ static const mares_common_layout_t mares_nemo_apneist_layout = {
 
 
 dc_status_t
-mares_nemo_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+mares_nemo_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	mares_nemo_device_t *device = NULL;
@@ -99,53 +97,44 @@ mares_nemo_device_open (dc_device_t **out, dc_context_t *context, const char *na
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the RTS line.
-	status = dc_serial_set_rts (device->port, 1);
+	status = dc_iostream_set_rts (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -153,23 +142,6 @@ error_free:
 
 
 static dc_status_t
-mares_nemo_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	mares_nemo_device_t *device = (mares_nemo_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 mares_nemo_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	mares_nemo_device_t *device = (mares_nemo_device_t *) abstract;
@@ -206,18 +178,18 @@ mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Wait until some data arrives.
 	size_t available = 0;
-	while (dc_serial_get_available (device->port, &available) == DC_STATUS_SUCCESS && available == 0) {
+	while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) {
 		if (device_is_cancelled (abstract))
 			return DC_STATUS_CANCELLED;
 
 		device_event_emit (abstract, DC_EVENT_WAITING, NULL);
-		dc_serial_sleep (device->port, 100);
+		dc_iostream_sleep (device->iostream, 100);
 	}
 
 	// Receive the header of the package.
 	unsigned char header = 0x00;
 	for (unsigned int i = 0; i < 20;) {
-		status = dc_serial_read (device->port, &header, 1, NULL);
+		status = dc_iostream_read (device->iostream, &header, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the header.");
 			return status;
@@ -237,7 +209,7 @@ mares_nemo_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	while (nbytes < MEMORYSIZE) {
 		// Read the packet.
 		unsigned char packet[(PACKETSIZE + 1) * 2] = {0};
-		status = dc_serial_read (device->port, packet, sizeof (packet), NULL);
+		status = dc_iostream_read (device->iostream, packet, sizeof (packet), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
diff --git a/src/mares_nemo.h b/src/mares_nemo.h
index e25489b..c7a162b 100644
--- a/src/mares_nemo.h
+++ b/src/mares_nemo.h
@@ -23,6 +23,7 @@
 #define MARES_NEMO_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-mares_nemo_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+mares_nemo_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 mares_nemo_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/mares_puck.c b/src/mares_puck.c
index fc8e00d..a42c330 100644
--- a/src/mares_puck.c
+++ b/src/mares_puck.c
@@ -27,7 +27,6 @@
 #include "mares_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -47,7 +46,6 @@ typedef struct mares_puck_device_t {
 static dc_status_t mares_puck_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t mares_puck_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t mares_puck_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t mares_puck_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t mares_puck_device_vtable = {
 	sizeof(mares_puck_device_t),
@@ -57,7 +55,7 @@ static const dc_device_vtable_t mares_puck_device_vtable = {
 	NULL, /* write */
 	mares_puck_device_dump, /* dump */
 	mares_puck_device_foreach, /* foreach */
-	mares_puck_device_close /* close */
+	NULL /* close */
 };
 
 static const mares_common_layout_t mares_puck_layout = {
@@ -86,7 +84,7 @@ static const mares_common_layout_t mares_nemowide_layout = {
 
 
 dc_status_t
-mares_puck_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+mares_puck_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	mares_puck_device_t *device = NULL;
@@ -102,55 +100,48 @@ mares_puck_device_open (dc_device_t **out, dc_context_t *context, const char *na
 	}
 
 	// Initialize the base class.
-	mares_common_device_init (&device->base);
+	mares_common_device_init (&device->base, iostream);
 
 	// Set the default values.
 	device->layout = NULL;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->base.port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (38400 8N1).
-	status = dc_serial_configure (device->base.port, 38400, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->base.iostream, 38400, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->base.port, 1000);
+	status = dc_iostream_set_timeout (device->base.iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the DTR line.
-	status = dc_serial_set_dtr (device->base.port, 0);
+	status = dc_iostream_set_dtr (device->base.iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->base.port, 0);
+	status = dc_iostream_set_rts (device->base.iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->base.port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->base.iostream, DC_DIRECTION_ALL);
 
 	// Identify the model number.
 	unsigned char header[PACKETSIZE] = {0};
 	status = mares_common_device_read ((dc_device_t *) device, 0, header, sizeof (header));
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -174,8 +165,6 @@ mares_puck_device_open (dc_device_t **out, dc_context_t *context, const char *na
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->base.port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -183,23 +172,6 @@ error_free:
 
 
 static dc_status_t
-mares_puck_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	mares_puck_device_t *device = (mares_puck_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->base.port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 mares_puck_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	mares_puck_device_t *device = (mares_puck_device_t *) abstract;
diff --git a/src/mares_puck.h b/src/mares_puck.h
index 01447d1..421fc28 100644
--- a/src/mares_puck.h
+++ b/src/mares_puck.h
@@ -23,6 +23,7 @@
 #define MARES_PUCK_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-mares_puck_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+mares_puck_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
diff --git a/src/oceanic_atom2.c b/src/oceanic_atom2.c
index da19e7f..34f6914 100644
--- a/src/oceanic_atom2.c
+++ b/src/oceanic_atom2.c
@@ -26,7 +26,6 @@
 #include "oceanic_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "array.h"
 #include "ringbuffer.h"
 #include "checksum.h"
@@ -54,7 +53,7 @@
 
 typedef struct oceanic_atom2_device_t {
 	oceanic_common_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int delay;
 	unsigned int bigpage;
 	unsigned char cache[256];
@@ -469,11 +468,11 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman
 		return DC_STATUS_CANCELLED;
 
 	if (device->delay) {
-		dc_serial_sleep (device->port, device->delay);
+		dc_iostream_sleep (device->iostream, device->delay);
 	}
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -487,7 +486,7 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman
 
 	// Receive the response (ACK/NAK) of the dive computer.
 	unsigned char response = 0;
-	status = dc_serial_read (device->port, &response, 1, NULL);
+	status = dc_iostream_read (device->iostream, &response, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -501,7 +500,7 @@ oceanic_atom2_packet (oceanic_atom2_device_t *device, const unsigned char comman
 
 	if (asize) {
 		// Receive the answer of the dive computer.
-		status = dc_serial_read (device->port, answer, asize, NULL);
+		status = dc_iostream_read (device->iostream, answer, asize, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -550,8 +549,8 @@ oceanic_atom2_transfer (oceanic_atom2_device_t *device, const unsigned char comm
 			device->delay++;
 
 		// Delay the next attempt.
-		dc_serial_sleep (device->port, 100);
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_sleep (device->iostream, 100);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 	}
 
 	return DC_STATUS_SUCCESS;
@@ -572,7 +571,7 @@ oceanic_atom2_quit (oceanic_atom2_device_t *device)
 
 
 dc_status_t
-oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
+oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	oceanic_atom2_device_t *device = NULL;
@@ -591,19 +590,12 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 	oceanic_common_device_init (&device->base);
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->delay = 0;
 	device->bigpage = 1; // no big pages
 	device->cached = INVALID;
 	memset(device->cache, 0, sizeof(device->cache));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Get the correct baudrate.
 	unsigned int baudrate = 38400;
 	if (model == VTX || model == I750TC) {
@@ -611,35 +603,35 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 	}
 
 	// Set the serial communication protocol (38400 8N1).
-	status = dc_serial_configure (device->port, baudrate, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, baudrate, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Set the DTR/RTS lines.
-	dc_serial_set_dtr(device->port, 1);
-	dc_serial_set_rts(device->port, 1);
+	dc_iostream_set_dtr(device->iostream, 1);
+	dc_iostream_set_rts(device->iostream, 1);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Switch the device from surface mode into download mode. Before sending
 	// this command, the device needs to be in PC mode (automatically activated
 	// by connecting the device), or already in download mode.
 	status = oceanic_atom2_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version));
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -709,8 +701,6 @@ oceanic_atom2_device_open (dc_device_t **out, dc_context_t *context, const char
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -730,12 +720,6 @@ oceanic_atom2_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/oceanic_atom2.h b/src/oceanic_atom2.h
index cee2306..25da953 100644
--- a/src/oceanic_atom2.h
+++ b/src/oceanic_atom2.h
@@ -23,6 +23,7 @@
 #define OCEANIC_ATOM2_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -33,7 +34,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-oceanic_atom2_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
+oceanic_atom2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
 
 dc_status_t
 oceanic_atom2_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/oceanic_veo250.c b/src/oceanic_veo250.c
index d47c532..8016d8a 100644
--- a/src/oceanic_veo250.c
+++ b/src/oceanic_veo250.c
@@ -26,7 +26,6 @@
 #include "oceanic_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "ringbuffer.h"
 #include "checksum.h"
 
@@ -40,7 +39,7 @@
 
 typedef struct oceanic_veo250_device_t {
 	oceanic_common_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int last;
 } oceanic_veo250_device_t;
 
@@ -98,10 +97,10 @@ oceanic_veo250_send (oceanic_veo250_device_t *device, const unsigned char comman
 		return DC_STATUS_CANCELLED;
 
 	// Discard garbage bytes.
-	dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -109,7 +108,7 @@ oceanic_veo250_send (oceanic_veo250_device_t *device, const unsigned char comman
 
 	// Receive the response (ACK/NAK) of the dive computer.
 	unsigned char response = NAK;
-	status = dc_serial_read (device->port, &response, 1, NULL);
+	status = dc_iostream_read (device->iostream, &response, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -148,11 +147,11 @@ oceanic_veo250_transfer (oceanic_veo250_device_t *device, const unsigned char co
 			return rc;
 
 		// Delay the next attempt.
-		dc_serial_sleep (device->port, 100);
+		dc_iostream_sleep (device->iostream, 100);
 	}
 
 	// Receive the answer of the dive computer.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -176,7 +175,7 @@ oceanic_veo250_init (oceanic_veo250_device_t *device)
 
 	// Send the command to the dive computer.
 	unsigned char command[2] = {0x55, 0x00};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -185,7 +184,7 @@ oceanic_veo250_init (oceanic_veo250_device_t *device)
 	// Receive the answer of the dive computer.
 	size_t n = 0;
 	unsigned char answer[13] = {0};
-	status = dc_serial_read (device->port, answer, sizeof (answer), &n);
+	status = dc_iostream_read (device->iostream, answer, sizeof (answer), &n);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		if (n == 0)
@@ -214,7 +213,7 @@ oceanic_veo250_quit (oceanic_veo250_device_t *device)
 
 	// Send the command to the dive computer.
 	unsigned char command[2] = {0x98, 0x00};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -225,7 +224,7 @@ oceanic_veo250_quit (oceanic_veo250_device_t *device)
 
 
 dc_status_t
-oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	oceanic_veo250_device_t *device = NULL;
@@ -247,65 +246,58 @@ oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, const char
 	device->base.multipage = MULTIPAGE;
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->last = 0;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000 ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the RTS line.
-	status = dc_serial_set_rts (device->port, 1);
+	status = dc_iostream_set_rts (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Initialize the data cable (PPS mode).
 	status = oceanic_veo250_init (device);
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Delay the sending of the version command.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Switch the device from surface mode into download mode. Before sending
 	// this command, the device needs to be in PC mode (manually activated by
 	// the user), or already in download mode.
 	status = oceanic_veo250_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version));
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -320,8 +312,6 @@ oceanic_veo250_device_open (dc_device_t **out, dc_context_t *context, const char
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -341,12 +331,6 @@ oceanic_veo250_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/oceanic_veo250.h b/src/oceanic_veo250.h
index aefd20e..fc79375 100644
--- a/src/oceanic_veo250.h
+++ b/src/oceanic_veo250.h
@@ -23,6 +23,7 @@
 #define OCEANIC_VEO250_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -33,7 +34,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-oceanic_veo250_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+oceanic_veo250_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 oceanic_veo250_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/oceanic_vtpro.c b/src/oceanic_vtpro.c
index 2e86f5f..88466e2 100644
--- a/src/oceanic_vtpro.c
+++ b/src/oceanic_vtpro.c
@@ -27,7 +27,6 @@
 #include "oceanic_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "ringbuffer.h"
 #include "checksum.h"
 #include "array.h"
@@ -50,7 +49,7 @@ typedef enum oceanic_vtpro_protocol_t {
 
 typedef struct oceanic_vtpro_device_t {
 	oceanic_common_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int model;
 	oceanic_vtpro_protocol_t protocol;
 } oceanic_vtpro_device_t;
@@ -139,7 +138,7 @@ oceanic_vtpro_send (oceanic_vtpro_device_t *device, const unsigned char command[
 		return DC_STATUS_CANCELLED;
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -147,7 +146,7 @@ oceanic_vtpro_send (oceanic_vtpro_device_t *device, const unsigned char command[
 
 	// Receive the response (ACK/NAK) of the dive computer.
 	unsigned char response = NAK;
-	status = dc_serial_read (device->port, &response, 1, NULL);
+	status = dc_iostream_read (device->iostream, &response, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -188,7 +187,7 @@ oceanic_vtpro_transfer (oceanic_vtpro_device_t *device, const unsigned char comm
 
 	if (asize) {
 		// Receive the answer of the dive computer.
-		status = dc_serial_read (device->port, answer, asize, NULL);
+		status = dc_iostream_read (device->iostream, answer, asize, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -209,7 +208,7 @@ oceanic_vtpro_init (oceanic_vtpro_device_t *device)
 	unsigned char command[2][2] = {
 		{0xAA, 0x00},
 		{0x20, 0x00}};
-	status = dc_serial_write (device->port, command[device->protocol], sizeof (command[device->protocol]), NULL);
+	status = dc_iostream_write (device->iostream, command[device->protocol], sizeof (command[device->protocol]), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -217,7 +216,7 @@ oceanic_vtpro_init (oceanic_vtpro_device_t *device)
 
 	// Receive the answer of the dive computer.
 	unsigned char answer[13] = {0};
-	status = dc_serial_read (device->port, answer, sizeof (answer), NULL);
+	status = dc_iostream_read (device->iostream, answer, sizeof (answer), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -268,9 +267,9 @@ oceanic_vtpro_calibrate (oceanic_vtpro_device_t *device)
 	// device needs approximately 6 seconds to respond.
 	unsigned char answer[2] = {0};
 	unsigned char command[2] = {0x18, 0x00};
-	dc_serial_set_timeout (device->port, 9000);
+	dc_iostream_set_timeout (device->iostream, 9000);
 	dc_status_t rc = oceanic_vtpro_transfer (device, command, sizeof (command), answer, sizeof (answer));
-	dc_serial_set_timeout (device->port, 3000);
+	dc_iostream_set_timeout (device->iostream, 3000);
 	if (rc != DC_STATUS_SUCCESS)
 		return rc;
 
@@ -336,7 +335,7 @@ oceanic_aeris500ai_device_logbook (dc_device_t *abstract, dc_event_progress_t *p
 	for (unsigned int i = 0; i < last + 1; ++i) {
 		// Receive the answer of the dive computer.
 		unsigned char answer[PAGESIZE / 2 + 1] = {0};
-		rc = dc_serial_read (device->port, answer, sizeof(answer), NULL);
+		rc = dc_iostream_read (device->iostream, answer, sizeof(answer), NULL);
 		if (rc != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return rc;
@@ -384,7 +383,7 @@ oceanic_vtpro_device_logbook (dc_device_t *abstract, dc_event_progress_t *progre
 }
 
 dc_status_t
-oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
+oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	oceanic_vtpro_device_t *device = NULL;
@@ -406,7 +405,7 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char
 	device->base.multipage = MULTIPAGE;
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->model = model;
 	if (model == AERIS500AI) {
 		device->protocol = INTR;
@@ -414,51 +413,44 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char
 		device->protocol = MOD;
 	}
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000 ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the RTS line.
-	status = dc_serial_set_rts (device->port, 1);
+	status = dc_iostream_set_rts (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, device->protocol == MOD ? 100 : 1000);
+	dc_iostream_sleep (device->iostream, device->protocol == MOD ? 100 : 1000);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Initialize the data cable (MOD mode).
 	status = oceanic_vtpro_init (device);
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Switch the device from surface mode into download mode. Before sending
@@ -466,7 +458,7 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char
 	// the user), or already in download mode.
 	status = oceanic_vtpro_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version));
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Calibrate the device. Although calibration is optional, it's highly
@@ -474,7 +466,7 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char
 	// when processing the command itself is quite slow.
 	status = oceanic_vtpro_calibrate (device);
 	if (status != DC_STATUS_SUCCESS) {
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -493,8 +485,6 @@ oceanic_vtpro_device_open (dc_device_t **out, dc_context_t *context, const char
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -514,12 +504,6 @@ oceanic_vtpro_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/oceanic_vtpro.h b/src/oceanic_vtpro.h
index 9689818..6a3c2cd 100644
--- a/src/oceanic_vtpro.h
+++ b/src/oceanic_vtpro.h
@@ -23,6 +23,7 @@
 #define OCEANIC_VTPRO_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -33,7 +34,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-oceanic_vtpro_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
+oceanic_vtpro_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
 
 dc_status_t
 oceanic_vtpro_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/reefnet_sensus.c b/src/reefnet_sensus.c
index 94d7a7e..3003fc4 100644
--- a/src/reefnet_sensus.c
+++ b/src/reefnet_sensus.c
@@ -25,7 +25,6 @@
 #include "reefnet_sensus.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -36,7 +35,7 @@
 
 typedef struct reefnet_sensus_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char handshake[SZ_HANDSHAKE];
 	unsigned int waiting;
 	unsigned int timestamp;
@@ -71,7 +70,7 @@ reefnet_sensus_cancel (reefnet_sensus_device_t *device)
 
 	// Send the command to the device.
 	unsigned char command = 0x00;
-	status = dc_serial_write (device->port, &command, 1, NULL);
+	status = dc_iostream_write (device->iostream, &command, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -85,7 +84,7 @@ reefnet_sensus_cancel (reefnet_sensus_device_t *device)
 
 
 dc_status_t
-reefnet_sensus_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+reefnet_sensus_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	reefnet_sensus_device_t *device = NULL;
@@ -101,43 +100,34 @@ reefnet_sensus_device_open (dc_device_t **out, dc_context_t *context, const char
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->waiting = 0;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 	memset (device->handshake, 0, sizeof (device->handshake));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (19200 8N1).
-	status = dc_serial_configure (device->port, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000 ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -160,12 +150,6 @@ reefnet_sensus_device_close (dc_device_t *abstract)
 		}
 	}
 
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
@@ -214,7 +198,7 @@ reefnet_sensus_handshake (reefnet_sensus_device_t *device)
 
 	// Send the command to the device.
 	unsigned char command = 0x0A;
-	status = dc_serial_write (device->port, &command, 1, NULL);
+	status = dc_iostream_write (device->iostream, &command, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -222,7 +206,7 @@ reefnet_sensus_handshake (reefnet_sensus_device_t *device)
 
 	// Receive the answer from the device.
 	unsigned char handshake[SZ_HANDSHAKE + 2] = {0};
-	status = dc_serial_read (device->port, handshake, sizeof (handshake), NULL);
+	status = dc_iostream_read (device->iostream, handshake, sizeof (handshake), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the handshake.");
 		return status;
@@ -266,7 +250,7 @@ reefnet_sensus_handshake (reefnet_sensus_device_t *device)
 	// Wait at least 10 ms to ensures the data line is
 	// clear before transmission from the host begins.
 
-	dc_serial_sleep (device->port, 10);
+	dc_iostream_sleep (device->iostream, 10);
 
 	return DC_STATUS_SUCCESS;
 }
@@ -297,7 +281,7 @@ reefnet_sensus_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send the command to the device.
 	unsigned char command = 0x40;
-	status = dc_serial_write (device->port, &command, 1, NULL);
+	status = dc_iostream_write (device->iostream, &command, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -314,7 +298,7 @@ reefnet_sensus_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		if (len > 128)
 			len = 128;
 
-		status = dc_serial_read (device->port, answer + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, answer + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
diff --git a/src/reefnet_sensus.h b/src/reefnet_sensus.h
index 2ca6d43..a021b86 100644
--- a/src/reefnet_sensus.h
+++ b/src/reefnet_sensus.h
@@ -23,6 +23,7 @@
 #define REEFNET_SENSUS_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/reefnet_sensus.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-reefnet_sensus_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+reefnet_sensus_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 reefnet_sensus_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int devtime, dc_ticks_t systime);
diff --git a/src/reefnet_sensuspro.c b/src/reefnet_sensuspro.c
index bf88801..2dbb7a4 100644
--- a/src/reefnet_sensuspro.c
+++ b/src/reefnet_sensuspro.c
@@ -25,7 +25,6 @@
 #include "reefnet_sensuspro.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -36,7 +35,7 @@
 
 typedef struct reefnet_sensuspro_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char handshake[SZ_HANDSHAKE];
 	unsigned int timestamp;
 	unsigned int devtime;
@@ -46,7 +45,6 @@ typedef struct reefnet_sensuspro_device_t {
 static dc_status_t reefnet_sensuspro_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t reefnet_sensuspro_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t reefnet_sensuspro_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t reefnet_sensuspro_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t reefnet_sensuspro_device_vtable = {
 	sizeof(reefnet_sensuspro_device_t),
@@ -56,14 +54,14 @@ static const dc_device_vtable_t reefnet_sensuspro_device_vtable = {
 	NULL, /* write */
 	reefnet_sensuspro_device_dump, /* dump */
 	reefnet_sensuspro_device_foreach, /* foreach */
-	reefnet_sensuspro_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 reefnet_sensuspro_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-reefnet_sensuspro_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+reefnet_sensuspro_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	reefnet_sensuspro_device_t *device = NULL;
@@ -79,65 +77,39 @@ reefnet_sensuspro_device_open (dc_device_t **out, dc_context_t *context, const c
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 	memset (device->handshake, 0, sizeof (device->handshake));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (19200 8N1).
-	status = dc_serial_configure (device->port, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
 }
 
 
-static dc_status_t
-reefnet_sensuspro_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	reefnet_sensuspro_device_t *device = (reefnet_sensuspro_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
 dc_status_t
 reefnet_sensuspro_device_get_handshake (dc_device_t *abstract, unsigned char data[], unsigned int size)
 {
@@ -181,18 +153,18 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device)
 	dc_device_t *abstract = (dc_device_t *) device;
 
 	// Assert a break condition.
-	dc_serial_set_break (device->port, 1);
+	dc_iostream_set_break (device->iostream, 1);
 
 	// Receive the handshake from the dive computer.
 	unsigned char handshake[SZ_HANDSHAKE + 2] = {0};
-	status = dc_serial_read (device->port, handshake, sizeof (handshake), NULL);
+	status = dc_iostream_read (device->iostream, handshake, sizeof (handshake), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the handshake.");
 		return status;
 	}
 
 	// Clear the break condition again.
-	dc_serial_set_break (device->port, 0);
+	dc_iostream_set_break (device->iostream, 0);
 
 	// Verify the checksum of the handshake packet.
 	unsigned short crc = array_uint16_le (handshake + SZ_HANDSHAKE);
@@ -228,7 +200,7 @@ reefnet_sensuspro_handshake (reefnet_sensuspro_device_t *device)
 	vendor.size = sizeof (device->handshake);
 	device_event_emit (abstract, DC_EVENT_VENDOR, &vendor);
 
-	dc_serial_sleep (device->port, 10);
+	dc_iostream_sleep (device->iostream, 10);
 
 	return DC_STATUS_SUCCESS;
 }
@@ -246,7 +218,7 @@ reefnet_sensuspro_send (reefnet_sensuspro_device_t *device, unsigned char comman
 		return rc;
 
 	// Send the instruction code to the device.
-	status = dc_serial_write (device->port, &command, 1, NULL);
+	status = dc_iostream_write (device->iostream, &command, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -286,7 +258,7 @@ reefnet_sensuspro_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		if (len > 256)
 			len = 256;
 
-		status = dc_serial_read (device->port, answer + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, answer + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -351,9 +323,9 @@ reefnet_sensuspro_device_write_interval (dc_device_t *abstract, unsigned char in
 	if (rc != DC_STATUS_SUCCESS)
 		return rc;
 
-	dc_serial_sleep (device->port, 10);
+	dc_iostream_sleep (device->iostream, 10);
 
-	status = dc_serial_write (device->port, &interval, 1, NULL);
+	status = dc_iostream_write (device->iostream, &interval, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the data packet.");
 		return status;
diff --git a/src/reefnet_sensuspro.h b/src/reefnet_sensuspro.h
index 000959a..9932515 100644
--- a/src/reefnet_sensuspro.h
+++ b/src/reefnet_sensuspro.h
@@ -23,6 +23,7 @@
 #define REEFNET_SENSUSPRO_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/reefnet_sensuspro.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-reefnet_sensuspro_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+reefnet_sensuspro_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 reefnet_sensuspro_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int devtime, dc_ticks_t systime);
diff --git a/src/reefnet_sensusultra.c b/src/reefnet_sensusultra.c
index f28dbb2..f440ec7 100644
--- a/src/reefnet_sensusultra.c
+++ b/src/reefnet_sensusultra.c
@@ -26,7 +26,6 @@
 #include "reefnet_sensusultra.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -45,7 +44,7 @@
 
 typedef struct reefnet_sensusultra_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char handshake[SZ_HANDSHAKE];
 	unsigned int timestamp;
 	unsigned int devtime;
@@ -55,7 +54,6 @@ typedef struct reefnet_sensusultra_device_t {
 static dc_status_t reefnet_sensusultra_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t reefnet_sensusultra_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t reefnet_sensusultra_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t reefnet_sensusultra_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t reefnet_sensusultra_device_vtable = {
 	sizeof(reefnet_sensusultra_device_t),
@@ -65,12 +63,12 @@ static const dc_device_vtable_t reefnet_sensusultra_device_vtable = {
 	NULL, /* write */
 	reefnet_sensusultra_device_dump, /* dump */
 	reefnet_sensusultra_device_foreach, /* foreach */
-	reefnet_sensusultra_device_close /* close */
+	NULL /* close */
 };
 
 
 dc_status_t
-reefnet_sensusultra_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+reefnet_sensusultra_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	reefnet_sensusultra_device_t *device = NULL;
@@ -86,65 +84,39 @@ reefnet_sensusultra_device_open (dc_device_t **out, dc_context_t *context, const
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 	memset (device->handshake, 0, sizeof (device->handshake));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
 }
 
 
-static dc_status_t
-reefnet_sensusultra_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	reefnet_sensusultra_device_t *device = (reefnet_sensusultra_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
 dc_status_t
 reefnet_sensusultra_device_get_handshake (dc_device_t *abstract, unsigned char data[], unsigned int size)
 {
@@ -189,7 +161,7 @@ reefnet_sensusultra_send_uchar (reefnet_sensusultra_device_t *device, unsigned c
 
 	// Wait for the prompt byte.
 	unsigned char prompt = 0;
-	status = dc_serial_read (device->port, &prompt, 1, NULL);
+	status = dc_iostream_read (device->iostream, &prompt, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the prompt byte");
 		return status;
@@ -202,7 +174,7 @@ reefnet_sensusultra_send_uchar (reefnet_sensusultra_device_t *device, unsigned c
 	}
 
 	// Send the value to the device.
-	status = dc_serial_write (device->port, &value, 1, NULL);
+	status = dc_iostream_write (device->iostream, &value, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the value.");
 		return status;
@@ -243,7 +215,7 @@ reefnet_sensusultra_packet (reefnet_sensusultra_device_t *device, unsigned char
 		return DC_STATUS_CANCELLED;
 
 	// Receive the data packet.
-	status = dc_serial_read (device->port, data, size, NULL);
+	status = dc_iostream_read (device->iostream, data, size, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the packet.");
 		return status;
@@ -345,7 +317,7 @@ static dc_status_t
 reefnet_sensusultra_send (reefnet_sensusultra_device_t *device, unsigned short command)
 {
 	// Flush the input and output buffers.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Wake-up the device and send the instruction code.
 	unsigned int nretries = 0;
@@ -365,8 +337,8 @@ reefnet_sensusultra_send (reefnet_sensusultra_device_t *device, unsigned short c
 		// not accidentally buffered by the host and (mis)interpreted as part
 		// of the next packet.
 
-		dc_serial_sleep (device->port, 250);
-		dc_serial_purge (device->port, DC_DIRECTION_ALL);
+		dc_iostream_sleep (device->iostream, 250);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 	}
 
 	return DC_STATUS_SUCCESS;
diff --git a/src/reefnet_sensusultra.h b/src/reefnet_sensusultra.h
index 84fcaea..af73c8a 100644
--- a/src/reefnet_sensusultra.h
+++ b/src/reefnet_sensusultra.h
@@ -23,6 +23,7 @@
 #define REEFNET_SENSUSULTRA_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/reefnet_sensusultra.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-reefnet_sensusultra_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+reefnet_sensusultra_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 reefnet_sensusultra_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int devtime, dc_ticks_t systime);
diff --git a/src/shearwater_common.c b/src/shearwater_common.c
index c6dafbb..d6f8d78 100644
--- a/src/shearwater_common.c
+++ b/src/shearwater_common.c
@@ -36,48 +36,31 @@
 #define ESC_ESC   0xDD
 
 dc_status_t
-shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name)
+shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		return status;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (115200 8N1).
-	status = dc_serial_configure (device->port, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 115200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		return status;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		return status;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_sleep (device->port, 300);
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_sleep (device->iostream, 300);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	return DC_STATUS_SUCCESS;
-
-error_close:
-	dc_serial_close (device->port);
-	return status;
-}
-
-
-dc_status_t
-shearwater_common_close (shearwater_common_device_t *device)
-{
-	// Close the device.
-	return dc_serial_close (device->port);
 }
 
 
@@ -153,7 +136,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 #if 0
 	// Send an initial END character to flush out any data that may have
 	// accumulated in the receiver due to line noise.
-	status = dc_serial_write (device->port, end, sizeof (end), NULL);
+	status = dc_iostream_write (device->iostream, end, sizeof (end), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		return status;
 	}
@@ -182,7 +165,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 
 		// Flush the buffer if necessary.
 		if (nbytes + len + sizeof(end) > sizeof(buffer)) {
-			status = dc_serial_write (device->port, buffer, nbytes, NULL);
+			status = dc_iostream_write (device->iostream, buffer, nbytes, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				return status;
 			}
@@ -200,7 +183,7 @@ shearwater_common_slip_write (shearwater_common_device_t *device, const unsigned
 	nbytes += sizeof(end);
 
 	// Flush the buffer.
-	status = dc_serial_write (device->port, buffer, nbytes, NULL);
+	status = dc_iostream_write (device->iostream, buffer, nbytes, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		return status;
 	}
@@ -223,7 +206,7 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d
 		unsigned char c = 0;
 
 		// Get a single character to process.
-		status = dc_serial_read (device->port, &c, 1, NULL);
+		status = dc_iostream_read (device->iostream, &c, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			return status;
 		}
@@ -242,7 +225,7 @@ shearwater_common_slip_read (shearwater_common_device_t *device, unsigned char d
 		case ESC:
 			// If it's an ESC character, get another character and then
 			// figure out what to store in the packet based on that.
-			status = dc_serial_read (device->port, &c, 1, NULL);
+			status = dc_iostream_read (device->iostream, &c, 1, NULL);
 			if (status != DC_STATUS_SUCCESS) {
 				return status;
 			}
diff --git a/src/shearwater_common.h b/src/shearwater_common.h
index b93f973..b149035 100644
--- a/src/shearwater_common.h
+++ b/src/shearwater_common.h
@@ -22,8 +22,9 @@
 #ifndef SHEARWATER_COMMON_H
 #define SHEARWATER_COMMON_H
 
+#include <libdivecomputer/iostream.h>
+
 #include "device-private.h"
-#include "serial.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -34,14 +35,11 @@ extern "C" {
 
 typedef struct shearwater_common_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } shearwater_common_device_t;
 
 dc_status_t
-shearwater_common_open (shearwater_common_device_t *device, dc_context_t *context, const char *name);
-
-dc_status_t
-shearwater_common_close (shearwater_common_device_t *device);
+shearwater_common_setup (shearwater_common_device_t *device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 shearwater_common_transfer (shearwater_common_device_t *device, const unsigned char input[], unsigned int isize, unsigned char output[], unsigned int osize, unsigned int *actual);
diff --git a/src/shearwater_petrel.c b/src/shearwater_petrel.c
index 4966d14..7895c3f 100644
--- a/src/shearwater_petrel.c
+++ b/src/shearwater_petrel.c
@@ -76,7 +76,7 @@ str2num (unsigned char data[], unsigned int size, unsigned int offset)
 
 
 dc_status_t
-shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	shearwater_petrel_device_t *device = NULL;
@@ -95,7 +95,7 @@ shearwater_petrel_device_open (dc_device_t **out, dc_context_t *context, const c
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
 	// Open the device.
-	status = shearwater_common_open (&device->base, context, name);
+	status = shearwater_common_setup (&device->base, context, iostream);
 	if (status != DC_STATUS_SUCCESS) {
 		goto error_free;
 	}
@@ -124,12 +124,6 @@ shearwater_petrel_device_close (dc_device_t *abstract)
 		dc_status_set_error(&status, rc);
 	}
 
-	// Close the device.
-	rc = shearwater_common_close (device);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
 	return status;
 }
 
diff --git a/src/shearwater_petrel.h b/src/shearwater_petrel.h
index c23bb74..0a4e756 100644
--- a/src/shearwater_petrel.h
+++ b/src/shearwater_petrel.h
@@ -23,6 +23,7 @@
 #define SHEARWATER_PETREL_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+shearwater_petrel_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 shearwater_petrel_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/shearwater_predator.c b/src/shearwater_predator.c
index 0e7a0d3..f7e07e7 100644
--- a/src/shearwater_predator.c
+++ b/src/shearwater_predator.c
@@ -47,7 +47,6 @@ typedef struct shearwater_predator_device_t {
 static dc_status_t shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t shearwater_predator_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t shearwater_predator_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t shearwater_predator_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t shearwater_predator_device_vtable = {
 	sizeof(shearwater_predator_device_t),
@@ -57,14 +56,14 @@ static const dc_device_vtable_t shearwater_predator_device_vtable = {
 	NULL, /* write */
 	shearwater_predator_device_dump, /* dump */
 	shearwater_predator_device_foreach, /* foreach */
-	shearwater_predator_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 shearwater_predator_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	shearwater_predator_device_t *device = NULL;
@@ -83,7 +82,7 @@ shearwater_predator_device_open (dc_device_t **out, dc_context_t *context, const
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
 	// Open the device.
-	status = shearwater_common_open (&device->base, context, name);
+	status = shearwater_common_setup (&device->base, context, iostream);
 	if (status != DC_STATUS_SUCCESS) {
 		goto error_free;
 	}
@@ -99,15 +98,6 @@ error_free:
 
 
 static dc_status_t
-shearwater_predator_device_close (dc_device_t *abstract)
-{
-	shearwater_common_device_t *device = (shearwater_common_device_t *) abstract;
-
-	return shearwater_common_close (device);
-}
-
-
-static dc_status_t
 shearwater_predator_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	shearwater_predator_device_t *device = (shearwater_predator_device_t *) abstract;
diff --git a/src/shearwater_predator.h b/src/shearwater_predator.h
index 4665f80..5d1fccb 100644
--- a/src/shearwater_predator.h
+++ b/src/shearwater_predator.h
@@ -23,6 +23,7 @@
 #define SHEARWATER_PREDATOR_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+shearwater_predator_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 shearwater_predator_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/suunto_d9.c b/src/suunto_d9.c
index 2908792..dc9ecc3 100644
--- a/src/suunto_d9.c
+++ b/src/suunto_d9.c
@@ -26,7 +26,6 @@
 #include "suunto_d9.h"
 #include "suunto_common2.h"
 #include "context-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -43,11 +42,10 @@
 
 typedef struct suunto_d9_device_t {
 	suunto_common2_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } suunto_d9_device_t;
 
 static dc_status_t suunto_d9_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size);
-static dc_status_t suunto_d9_device_close (dc_device_t *abstract);
 
 static const suunto_common2_device_vtable_t suunto_d9_device_vtable = {
 	{
@@ -58,7 +56,7 @@ static const suunto_common2_device_vtable_t suunto_d9_device_vtable = {
 		suunto_common2_device_write, /* write */
 		suunto_common2_device_dump, /* dump */
 		suunto_common2_device_foreach, /* foreach */
-		suunto_d9_device_close /* close */
+		NULL /* close */
 	},
 	suunto_d9_device_packet
 };
@@ -108,7 +106,7 @@ suunto_d9_device_autodetect (suunto_d9_device_t *device, unsigned int model)
 		unsigned int idx = (hint + i) % C_ARRAY_SIZE(baudrates);
 
 		// Adjust the baudrate.
-		status = dc_serial_configure (device->port, baudrates[idx], 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+		status = dc_iostream_configure (device->iostream, baudrates[idx], 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to set the terminal attributes.");
 			return status;
@@ -125,7 +123,7 @@ suunto_d9_device_autodetect (suunto_d9_device_t *device, unsigned int model)
 
 
 dc_status_t
-suunto_d9_device_open (dc_device_t **out, dc_context_t *context, const char *name, unsigned int model)
+suunto_d9_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream, unsigned int model)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_d9_device_t *device = NULL;
@@ -144,47 +142,40 @@ suunto_d9_device_open (dc_device_t **out, dc_context_t *context, const char *nam
 	suunto_common2_device_init (&device->base);
 
 	// Set the default values.
-	device->port = NULL;
-
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000 ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line (power supply for the interface).
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Try to autodetect the protocol variant.
 	status = suunto_d9_device_autodetect (device, model);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to identify the protocol variant.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -201,8 +192,6 @@ suunto_d9_device_open (dc_device_t **out, dc_context_t *context, const char *nam
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -210,23 +199,6 @@ error_free:
 
 
 static dc_status_t
-suunto_d9_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	suunto_d9_device_t *device = (suunto_d9_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 suunto_d9_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
@@ -236,10 +208,10 @@ suunto_d9_device_packet (dc_device_t *abstract, const unsigned char command[], u
 		return DC_STATUS_CANCELLED;
 
 	// Clear RTS to send the command.
-	dc_serial_set_rts (device->port, 0);
+	dc_iostream_set_rts (device->iostream, 0);
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -248,7 +220,7 @@ suunto_d9_device_packet (dc_device_t *abstract, const unsigned char command[], u
 	// Receive the echo.
 	unsigned char echo[128] = {0};
 	assert (sizeof (echo) >= csize);
-	status = dc_serial_read (device->port, echo, csize, NULL);
+	status = dc_iostream_read (device->iostream, echo, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the echo.");
 		return status;
@@ -261,10 +233,10 @@ suunto_d9_device_packet (dc_device_t *abstract, const unsigned char command[], u
 	}
 
 	// Set RTS to receive the reply.
-	dc_serial_set_rts (device->port, 1);
+	dc_iostream_set_rts (device->iostream, 1);
 
 	// Receive the answer of the dive computer.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
diff --git a/src/suunto_d9.h b/src/suunto_d9.h
index 91fe8b2..94a782f 100644
--- a/src/suunto_d9.h
+++ b/src/suunto_d9.h
@@ -23,6 +23,7 @@
 #define SUUNTO_D9_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/suunto_d9.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-suunto_d9_device_open (dc_device_t **device, dc_context_t *context, const char *name, unsigned int model);
+suunto_d9_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream, unsigned int model);
 
 dc_status_t
 suunto_d9_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/suunto_eon.c b/src/suunto_eon.c
index ce9ce83..e08839f 100644
--- a/src/suunto_eon.c
+++ b/src/suunto_eon.c
@@ -26,7 +26,6 @@
 #include "suunto_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -36,12 +35,11 @@
 
 typedef struct suunto_eon_device_t {
 	suunto_common_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } suunto_eon_device_t;
 
 static dc_status_t suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t suunto_eon_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t suunto_eon_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t suunto_eon_device_vtable = {
 	sizeof(suunto_eon_device_t),
@@ -51,7 +49,7 @@ static const dc_device_vtable_t suunto_eon_device_vtable = {
 	NULL, /* write */
 	suunto_eon_device_dump, /* dump */
 	suunto_eon_device_foreach, /* foreach */
-	suunto_eon_device_close /* close */
+	NULL /* close */
 };
 
 static const suunto_common_layout_t suunto_eon_layout = {
@@ -64,7 +62,7 @@ static const suunto_common_layout_t suunto_eon_layout = {
 
 
 dc_status_t
-suunto_eon_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+suunto_eon_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_eon_device_t *device = NULL;
@@ -83,42 +81,33 @@ suunto_eon_device_open (dc_device_t **out, dc_context_t *context, const char *na
 	suunto_common_device_init (&device->base);
 
 	// Set the default values.
-	device->port = NULL;
-
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (1200 8N2).
-	status = dc_serial_configure (device->port, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR/RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -126,23 +115,6 @@ error_free:
 
 
 static dc_status_t
-suunto_eon_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	suunto_eon_device_t *device = (suunto_eon_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
@@ -162,7 +134,7 @@ suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send the command.
 	unsigned char command[1] = {'P'};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -177,7 +149,7 @@ suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Increase the packet size if more data is immediately available.
 		size_t available = 0;
-		status = dc_serial_get_available (device->port, &available);
+		status = dc_iostream_get_available (device->iostream, &available);
 		if (status == DC_STATUS_SUCCESS && available > len)
 			len = available;
 
@@ -186,7 +158,7 @@ suunto_eon_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 			len = sizeof(answer) - nbytes;
 
 		// Read the packet.
-		status = dc_serial_read (device->port, answer + nbytes, len, NULL);
+		status = dc_iostream_read (device->iostream, answer + nbytes, len, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -263,7 +235,7 @@ suunto_eon_device_write_name (dc_device_t *abstract, unsigned char data[], unsig
 	// Send the command.
 	unsigned char command[21] = {'N'};
 	memcpy (command + 1, data, size);
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -284,7 +256,7 @@ suunto_eon_device_write_interval (dc_device_t *abstract, unsigned char interval)
 
 	// Send the command.
 	unsigned char command[2] = {'T', interval};
-	status = dc_serial_write (device->port, command, sizeof (command), NULL);
+	status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
diff --git a/src/suunto_eon.h b/src/suunto_eon.h
index 2adce91..5e99a69 100644
--- a/src/suunto_eon.h
+++ b/src/suunto_eon.h
@@ -23,6 +23,7 @@
 #define SUUNTO_EON_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 #include <libdivecomputer/suunto_eon.h>
@@ -32,7 +33,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-suunto_eon_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+suunto_eon_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 suunto_eon_parser_create (dc_parser_t **parser, dc_context_t *context, int spyder);
diff --git a/src/suunto_eonsteel.c b/src/suunto_eonsteel.c
index 615f5c7..39ae3c2 100644
--- a/src/suunto_eonsteel.c
+++ b/src/suunto_eonsteel.c
@@ -27,7 +27,6 @@
 #include "context-private.h"
 #include "device-private.h"
 #include "array.h"
-#include "usbhid.h"
 
 #ifdef _MSC_VER
 #define snprintf _snprintf
@@ -35,7 +34,7 @@
 
 typedef struct suunto_eonsteel_device_t {
 	dc_device_t base;
-	dc_usbhid_t *usbhid;
+	dc_iostream_t *iostream;
 	unsigned int magic;
 	unsigned short seq;
 	unsigned char version[0x30];
@@ -71,7 +70,6 @@ struct directory_entry {
 
 static dc_status_t suunto_eonsteel_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t suunto_eonsteel_device_close(dc_device_t *abstract);
 
 static const dc_device_vtable_t suunto_eonsteel_device_vtable = {
 	sizeof(suunto_eonsteel_device_t),
@@ -81,7 +79,7 @@ static const dc_device_vtable_t suunto_eonsteel_device_vtable = {
 	NULL, /* write */
 	NULL, /* dump */
 	suunto_eonsteel_device_foreach, /* foreach */
-	suunto_eonsteel_device_close /* close */
+	NULL /* close */
 };
 
 static const char dive_directory[] = "0:/dives";
@@ -133,7 +131,7 @@ static int receive_packet(suunto_eonsteel_device_t *eon, unsigned char *buffer,
 	size_t transferred = 0;
 	int len;
 
-	rc = dc_usbhid_read(eon->usbhid, buf, PACKET_SIZE, &transferred);
+	rc = dc_iostream_read(eon->iostream, buf, PACKET_SIZE, &transferred);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR(eon->base.context, "read interrupt transfer failed");
 		return -1;
@@ -199,7 +197,7 @@ static int send_cmd(suunto_eonsteel_device_t *eon,
 		memcpy(buf+14, buffer, len);
 	}
 
-	rc = dc_usbhid_write(eon->usbhid, buf, sizeof(buf), &transferred);
+	rc = dc_iostream_write(eon->iostream, buf, sizeof(buf), &transferred);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR(eon->base.context, "write interrupt transfer failed");
 		return -1;
@@ -515,20 +513,20 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
 	unsigned char buf[64];
 	struct eon_hdr hdr;
 
-	dc_usbhid_set_timeout(eon->usbhid, 10);
+	dc_iostream_set_timeout(eon->iostream, 10);
 
 	/* Get rid of any pending stale input first */
 	for (;;) {
 		size_t transferred = 0;
 
-		dc_status_t rc = dc_usbhid_read(eon->usbhid, buf, sizeof(buf), &transferred);
+		dc_status_t rc = dc_iostream_read(eon->iostream, buf, sizeof(buf), &transferred);
 		if (rc != DC_STATUS_SUCCESS)
 			break;
 		if (!transferred)
 			break;
 	}
 
-	dc_usbhid_set_timeout(eon->usbhid, 5000);
+	dc_iostream_set_timeout(eon->iostream, 5000);
 
 	if (send_cmd(eon, INIT_CMD, sizeof(init), init)) {
 		ERROR(eon->base.context, "Failed to send initialization command");
@@ -547,7 +545,7 @@ static int initialize_eonsteel(suunto_eonsteel_device_t *eon)
 }
 
 dc_status_t
-suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context)
+suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_eonsteel_device_t *eon = NULL;
@@ -560,29 +558,22 @@ suunto_eonsteel_device_open(dc_device_t **out, dc_context_t *context)
 		return DC_STATUS_NOMEMORY;
 
 	// Set up the magic handshake fields
+	eon->iostream = iostream;
 	eon->magic = INIT_MAGIC;
 	eon->seq = INIT_SEQ;
 	memset (eon->version, 0, sizeof (eon->version));
 	memset (eon->fingerprint, 0, sizeof (eon->fingerprint));
 
-	status = dc_usbhid_open(&eon->usbhid, context, 0x1493, 0x0030);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR(context, "unable to open device");
-		goto error_free;
-	}
-
 	if (initialize_eonsteel(eon) < 0) {
 		ERROR(context, "unable to initialize device");
 		status = DC_STATUS_IO;
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t *) eon;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_usbhid_close(eon->usbhid);
 error_free:
 	free(eon);
 	return status;
@@ -700,13 +691,3 @@ suunto_eonsteel_device_foreach(dc_device_t *abstract, dc_dive_callback_t callbac
 
 	return device_is_cancelled(abstract) ? DC_STATUS_CANCELLED : DC_STATUS_SUCCESS;
 }
-
-static dc_status_t
-suunto_eonsteel_device_close(dc_device_t *abstract)
-{
-	suunto_eonsteel_device_t *eon = (suunto_eonsteel_device_t *) abstract;
-
-	dc_usbhid_close(eon->usbhid);
-
-	return DC_STATUS_SUCCESS;
-}
diff --git a/src/suunto_eonsteel.h b/src/suunto_eonsteel.h
index 98763c9..e84ab00 100644
--- a/src/suunto_eonsteel.h
+++ b/src/suunto_eonsteel.h
@@ -23,6 +23,7 @@
 #define SUUNTO_EONSTEEL_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context);
+suunto_eonsteel_device_open(dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 suunto_eonsteel_parser_create(dc_parser_t **parser, dc_context_t *context, unsigned int model);
diff --git a/src/suunto_solution.c b/src/suunto_solution.c
index c708114..483b0d1 100644
--- a/src/suunto_solution.c
+++ b/src/suunto_solution.c
@@ -27,7 +27,6 @@
 #include "context-private.h"
 #include "device-private.h"
 #include "ringbuffer.h"
-#include "serial.h"
 #include "array.h"
 
 #define ISINSTANCE(device) dc_device_isinstance((device), &suunto_solution_device_vtable)
@@ -39,12 +38,11 @@
 
 typedef struct suunto_solution_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } suunto_solution_device_t;
 
 static dc_status_t suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t suunto_solution_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t suunto_solution_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t suunto_solution_device_vtable = {
 	sizeof(suunto_solution_device_t),
@@ -54,14 +52,14 @@ static const dc_device_vtable_t suunto_solution_device_vtable = {
 	NULL, /* write */
 	suunto_solution_device_dump, /* dump */
 	suunto_solution_device_foreach, /* foreach */
-	suunto_solution_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 suunto_solution_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-suunto_solution_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+suunto_solution_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_solution_device_t *device = NULL;
@@ -77,42 +75,33 @@ suunto_solution_device_open (dc_device_t **out, dc_context_t *context, const cha
 	}
 
 	// Set the default values.
-	device->port = NULL;
-
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (1200 8N2).
-	status = dc_serial_configure (device->port, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 1200, 8, DC_PARITY_NONE, DC_STOPBITS_TWO, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR/RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -120,23 +109,6 @@ error_free:
 
 
 static dc_status_t
-suunto_solution_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	suunto_solution_device_t *device = (suunto_solution_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
@@ -160,14 +132,14 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	unsigned char answer[3] = {0};
 
 	// Assert DTR
-	dc_serial_set_dtr (device->port, 1);
+	dc_iostream_set_dtr (device->iostream, 1);
 
 	// Send: 0xFF
 	command[0] = 0xFF;
-	dc_serial_write (device->port, command, 1, NULL);
+	dc_iostream_write (device->iostream, command, 1, NULL);
 
 	// Receive: 0x3F
-	status = dc_serial_read (device->port, answer, 1, NULL);
+	status = dc_iostream_read (device->iostream, answer, 1, NULL);
 	if (status != DC_STATUS_SUCCESS)
 		return status;
 	if (answer[0] != 0x3F)
@@ -177,7 +149,7 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	command[0] = 0x4D;
 	command[1] = 0x01;
 	command[2] = 0x01;
-	dc_serial_write (device->port, command, 3, NULL);
+	dc_iostream_write (device->iostream, command, 3, NULL);
 
 	// Update and emit a progress event.
 	progress.current += 1;
@@ -186,7 +158,7 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	data[0] = 0x00;
 	for (unsigned int i = 1; i < SZ_MEMORY; ++i) {
 		// Receive: 0x01, i, data[i]
-		status = dc_serial_read (device->port, answer, 3, NULL);
+		status = dc_iostream_read (device->iostream, answer, 3, NULL);
 		if (status != DC_STATUS_SUCCESS)
 			return status;
 		if (answer[0] != 0x01 || answer[1] != i)
@@ -194,10 +166,10 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Send: i
 		command[0] = i;
-		dc_serial_write (device->port, command, 1, NULL);
+		dc_iostream_write (device->iostream, command, 1, NULL);
 
 		// Receive: data[i]
-		status = dc_serial_read (device->port, data + i, 1, NULL);
+		status = dc_iostream_read (device->iostream, data + i, 1, NULL);
 		if (status != DC_STATUS_SUCCESS)
 			return status;
 		if (data[i] != answer[2])
@@ -205,7 +177,7 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Send: 0x0D
 		command[0] = 0x0D;
-		dc_serial_write (device->port, command, 1, NULL);
+		dc_iostream_write (device->iostream, command, 1, NULL);
 
 		// Update and emit a progress event.
 		progress.current += 1;
@@ -213,7 +185,7 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	}
 
 	// Receive: 0x02, 0x00, 0x80
-	status = dc_serial_read (device->port, answer, 3, NULL);
+	status = dc_iostream_read (device->iostream, answer, 3, NULL);
 	if (status != DC_STATUS_SUCCESS)
 		return status;
 	if (answer[0] != 0x02 || answer[1] != 0x00 || answer[2] != 0x80)
@@ -221,10 +193,10 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send: 0x80
 	command[0] = 0x80;
-	dc_serial_write (device->port, command, 1, NULL);
+	dc_iostream_write (device->iostream, command, 1, NULL);
 
 	// Receive: 0x80
-	status = dc_serial_read (device->port, answer, 1, NULL);
+	status = dc_iostream_read (device->iostream, answer, 1, NULL);
 	if (status != DC_STATUS_SUCCESS)
 		return status;
 	if (answer[0] != 0x80)
@@ -232,10 +204,10 @@ suunto_solution_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Send: 0x20
 	command[0] = 0x20;
-	dc_serial_write (device->port, command, 1, NULL);
+	dc_iostream_write (device->iostream, command, 1, NULL);
 
 	// Receive: 0x3F
-	status = dc_serial_read (device->port, answer, 1, NULL);
+	status = dc_iostream_read (device->iostream, answer, 1, NULL);
 	if (status != DC_STATUS_SUCCESS)
 		return status;
 	if (answer[0] != 0x3F)
diff --git a/src/suunto_solution.h b/src/suunto_solution.h
index e322583..9d9e632 100644
--- a/src/suunto_solution.h
+++ b/src/suunto_solution.h
@@ -23,6 +23,7 @@
 #define SUUNTO_SOLUTION_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-suunto_solution_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+suunto_solution_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 suunto_solution_parser_create (dc_parser_t **parser, dc_context_t *context);
diff --git a/src/suunto_vyper.c b/src/suunto_vyper.c
index ce44e05..25cdb1a 100644
--- a/src/suunto_vyper.c
+++ b/src/suunto_vyper.c
@@ -27,7 +27,6 @@
 #include "suunto_common.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -46,14 +45,13 @@
 
 typedef struct suunto_vyper_device_t {
 	suunto_common_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } suunto_vyper_device_t;
 
 static dc_status_t suunto_vyper_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t suunto_vyper_device_write (dc_device_t *abstract, unsigned int address, const unsigned char data[], unsigned int size);
 static dc_status_t suunto_vyper_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t suunto_vyper_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t suunto_vyper_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t suunto_vyper_device_vtable = {
 	sizeof(suunto_vyper_device_t),
@@ -63,7 +61,7 @@ static const dc_device_vtable_t suunto_vyper_device_vtable = {
 	suunto_vyper_device_write, /* write */
 	suunto_vyper_device_dump, /* dump */
 	suunto_vyper_device_foreach, /* foreach */
-	suunto_vyper_device_close /* close */
+	NULL /* close */
 };
 
 static const suunto_common_layout_t suunto_vyper_layout = {
@@ -84,7 +82,7 @@ static const suunto_common_layout_t suunto_spyder_layout = {
 
 
 dc_status_t
-suunto_vyper_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+suunto_vyper_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_vyper_device_t *device = NULL;
@@ -103,48 +101,39 @@ suunto_vyper_device_open (dc_device_t **out, dc_context_t *context, const char *
 	suunto_common_device_init (&device->base);
 
 	// Set the default values.
-	device->port = NULL;
-
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (2400 8O1).
-	status = dc_serial_configure (device->port, 2400, 8, DC_PARITY_ODD, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 2400, 8, DC_PARITY_ODD, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line (power supply for the interface).
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -152,35 +141,18 @@ error_free:
 
 
 static dc_status_t
-suunto_vyper_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	suunto_vyper_device_t *device = (suunto_vyper_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 suunto_vyper_send (suunto_vyper_device_t *device, const unsigned char command[], unsigned int csize)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	dc_device_t *abstract = (dc_device_t *) device;
 
-	dc_serial_sleep (device->port, 500);
+	dc_iostream_sleep (device->iostream, 500);
 
 	// Set RTS to send the command.
-	dc_serial_set_rts (device->port, 1);
+	dc_iostream_set_rts (device->iostream, 1);
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -197,11 +169,11 @@ suunto_vyper_send (suunto_vyper_device_t *device, const unsigned char command[],
 	// receive the reply before RTS is cleared. We have to wait some time
 	// before clearing RTS (around 30ms). But if we wait too long (> 500ms),
 	// the reply disappears again.
-	dc_serial_sleep (device->port, 200);
-	dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+	dc_iostream_sleep (device->iostream, 200);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 
 	// Clear RTS to receive the reply.
-	dc_serial_set_rts (device->port, 0);
+	dc_iostream_set_rts (device->iostream, 0);
 
 	return DC_STATUS_SUCCESS;
 }
@@ -226,7 +198,7 @@ suunto_vyper_transfer (suunto_vyper_device_t *device, const unsigned char comman
 	}
 
 	// Receive the answer of the dive computer.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -351,7 +323,7 @@ suunto_vyper_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc
 		// Receive the header of the package.
 		size_t n = 0;
 		unsigned char answer[SZ_PACKET + 3] = {0};
-		status = dc_serial_read (device->port, answer, 2, &n);
+		status = dc_iostream_read (device->iostream, answer, 2, &n);
 		if (status != DC_STATUS_SUCCESS) {
 			// If no data is received because a timeout occured, we assume
 			// the last package was already received and the transmission
@@ -376,7 +348,7 @@ suunto_vyper_read_dive (dc_device_t *abstract, dc_buffer_t *buffer, int init, dc
 
 		// Receive the remaining part of the package.
 		unsigned char len = answer[1];
-		status = dc_serial_read (device->port, answer + 2, len + 1, NULL);
+		status = dc_iostream_read (device->iostream, answer + 2, len + 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
diff --git a/src/suunto_vyper.h b/src/suunto_vyper.h
index 95f1a4a..858520f 100644
--- a/src/suunto_vyper.h
+++ b/src/suunto_vyper.h
@@ -23,6 +23,7 @@
 #define SUUNTO_VYPER_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-suunto_vyper_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+suunto_vyper_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 suunto_vyper_parser_create (dc_parser_t **parser, dc_context_t *context);
diff --git a/src/suunto_vyper2.c b/src/suunto_vyper2.c
index 8ab521e..2c902c0 100644
--- a/src/suunto_vyper2.c
+++ b/src/suunto_vyper2.c
@@ -25,7 +25,6 @@
 #include "suunto_vyper2.h"
 #include "suunto_common2.h"
 #include "context-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -35,11 +34,10 @@
 
 typedef struct suunto_vyper2_device_t {
 	suunto_common2_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 } suunto_vyper2_device_t;
 
 static dc_status_t suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size);
-static dc_status_t suunto_vyper2_device_close (dc_device_t *abstract);
 
 static const suunto_common2_device_vtable_t suunto_vyper2_device_vtable = {
 	{
@@ -50,7 +48,7 @@ static const suunto_common2_device_vtable_t suunto_vyper2_device_vtable = {
 		suunto_common2_device_write, /* write */
 		suunto_common2_device_dump, /* dump */
 		suunto_common2_device_foreach, /* foreach */
-		suunto_vyper2_device_close /* close */
+		NULL /* close */
 	},
 	suunto_vyper2_device_packet
 };
@@ -73,7 +71,7 @@ static const suunto_common2_layout_t suunto_helo2_layout = {
 
 
 dc_status_t
-suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	suunto_vyper2_device_t *device = NULL;
@@ -92,50 +90,43 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char
 	suunto_common2_device_init (&device->base);
 
 	// Set the default values.
-	device->port = NULL;
-
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
+	device->iostream = iostream;
 
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000 ms).
-	status = dc_serial_set_timeout (device->port, 3000);
+	status = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line (power supply for the interface).
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Give the interface 100 ms to settle and draw power up.
-	dc_serial_sleep (device->port, 100);
+	dc_iostream_sleep (device->iostream, 100);
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Enable half-duplex emulation.
-	dc_serial_set_halfduplex (device->port, 1);
+	dc_iostream_set_halfduplex (device->iostream, 1);
 
 	// Read the version info.
 	status = suunto_common2_device_version ((dc_device_t *) device, device->base.version, sizeof (device->base.version));
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to read the version info.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Override the base class values.
@@ -149,8 +140,6 @@ suunto_vyper2_device_open (dc_device_t **out, dc_context_t *context, const char
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -158,23 +147,6 @@ error_free:
 
 
 static dc_status_t
-suunto_vyper2_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	suunto_vyper2_device_t *device = (suunto_vyper2_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize, unsigned int size)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
@@ -183,23 +155,23 @@ suunto_vyper2_device_packet (dc_device_t *abstract, const unsigned char command[
 	if (device_is_cancelled (abstract))
 		return DC_STATUS_CANCELLED;
 
-	dc_serial_sleep (device->port, 600);
+	dc_iostream_sleep (device->iostream, 600);
 
 	// Set RTS to send the command.
-	dc_serial_set_rts (device->port, 1);
+	dc_iostream_set_rts (device->iostream, 1);
 
 	// Send the command to the dive computer.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
 	// Clear RTS to receive the reply.
-	dc_serial_set_rts (device->port, 0);
+	dc_iostream_set_rts (device->iostream, 0);
 
 	// Receive the answer of the dive computer.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
diff --git a/src/suunto_vyper2.h b/src/suunto_vyper2.h
index b6486d9..0fc7fc8 100644
--- a/src/suunto_vyper2.h
+++ b/src/suunto_vyper2.h
@@ -27,11 +27,12 @@ extern "C" {
 #endif /* __cplusplus */
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/suunto_vyper2.h>
 
 dc_status_t
-suunto_vyper2_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+suunto_vyper2_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
diff --git a/src/uwatec_aladin.c b/src/uwatec_aladin.c
index d70369a..9e75940 100644
--- a/src/uwatec_aladin.c
+++ b/src/uwatec_aladin.c
@@ -25,7 +25,6 @@
 #include "uwatec_aladin.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "ringbuffer.h"
 #include "checksum.h"
 #include "array.h"
@@ -43,7 +42,7 @@
 
 typedef struct uwatec_aladin_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int timestamp;
 	unsigned int devtime;
 	dc_ticks_t systime;
@@ -52,7 +51,6 @@ typedef struct uwatec_aladin_device_t {
 static dc_status_t uwatec_aladin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size);
 static dc_status_t uwatec_aladin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t uwatec_aladin_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t uwatec_aladin_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t uwatec_aladin_device_vtable = {
 	sizeof(uwatec_aladin_device_t),
@@ -62,14 +60,14 @@ static const dc_device_vtable_t uwatec_aladin_device_vtable = {
 	NULL, /* write */
 	uwatec_aladin_device_dump, /* dump */
 	uwatec_aladin_device_foreach, /* foreach */
-	uwatec_aladin_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 uwatec_aladin_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-uwatec_aladin_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+uwatec_aladin_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	uwatec_aladin_device_t *device = NULL;
@@ -85,52 +83,43 @@ uwatec_aladin_device_open (dc_device_t **out, dc_context_t *context, const char
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (19200 8N1).
-	status = dc_serial_configure (device->port, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 19200, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (INFINITE).
-	status = dc_serial_set_timeout (device->port, -1);
+	status = dc_iostream_set_timeout (device->iostream, -1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the DTR line.
-	status = dc_serial_set_dtr (device->port, 1);
+	status = dc_iostream_set_dtr (device->iostream, 1);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -138,23 +127,6 @@ error_free:
 
 
 static dc_status_t
-uwatec_aladin_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	uwatec_aladin_device_t *device = (uwatec_aladin_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 uwatec_aladin_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	uwatec_aladin_device_t *device = (uwatec_aladin_device_t*) abstract;
@@ -196,7 +168,7 @@ uwatec_aladin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		if (device_is_cancelled (abstract))
 			return DC_STATUS_CANCELLED;
 
-		status = dc_serial_read (device->port, answer + i, 1, NULL);
+		status = dc_iostream_read (device->iostream, answer + i, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -217,7 +189,7 @@ uwatec_aladin_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	device_event_emit (abstract, DC_EVENT_PROGRESS, &progress);
 
 	// Receive the remaining part of the package.
-	status = dc_serial_read (device->port, answer + 4, sizeof (answer) - 4, NULL);
+	status = dc_iostream_read (device->iostream, answer + 4, sizeof (answer) - 4, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Unexpected EOF in answer.");
 		return status;
diff --git a/src/uwatec_aladin.h b/src/uwatec_aladin.h
index 41484f6..7579871 100644
--- a/src/uwatec_aladin.h
+++ b/src/uwatec_aladin.h
@@ -27,10 +27,11 @@ extern "C" {
 #endif /* __cplusplus */
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 
 dc_status_t
-uwatec_aladin_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+uwatec_aladin_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
diff --git a/src/uwatec_memomouse.c b/src/uwatec_memomouse.c
index b23c6b2..3a26823 100644
--- a/src/uwatec_memomouse.c
+++ b/src/uwatec_memomouse.c
@@ -26,7 +26,6 @@
 #include "uwatec_memomouse.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 
@@ -39,7 +38,7 @@
 
 typedef struct uwatec_memomouse_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int timestamp;
 	unsigned int devtime;
 	dc_ticks_t systime;
@@ -48,7 +47,6 @@ typedef struct uwatec_memomouse_device_t {
 static dc_status_t uwatec_memomouse_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
 static dc_status_t uwatec_memomouse_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t uwatec_memomouse_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t uwatec_memomouse_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t uwatec_memomouse_device_vtable = {
 	sizeof(uwatec_memomouse_device_t),
@@ -58,14 +56,14 @@ static const dc_device_vtable_t uwatec_memomouse_device_vtable = {
 	NULL, /* write */
 	uwatec_memomouse_device_dump, /* dump */
 	uwatec_memomouse_device_foreach, /* foreach */
-	uwatec_memomouse_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 uwatec_memomouse_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
 dc_status_t
-uwatec_memomouse_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+uwatec_memomouse_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	uwatec_memomouse_device_t *device = NULL;
@@ -81,55 +79,46 @@ uwatec_memomouse_device_open (dc_device_t **out, dc_context_t *context, const ch
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (9600 8N1).
-	status = dc_serial_configure (device->port, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 9600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the DTR line.
-	status = dc_serial_set_dtr (device->port, 0);
+	status = dc_iostream_set_dtr (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the DTR line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Clear the RTS line.
-	status = dc_serial_set_rts (device->port, 0);
+	status = dc_iostream_set_rts (device->iostream, 0);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to clear the RTS line.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -137,23 +126,6 @@ error_free:
 
 
 static dc_status_t
-uwatec_memomouse_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	uwatec_memomouse_device_t *device = (uwatec_memomouse_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 uwatec_memomouse_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	uwatec_memomouse_device_t *device = (uwatec_memomouse_device_t*) abstract;
@@ -179,7 +151,7 @@ uwatec_memomouse_read_packet (uwatec_memomouse_device_t *device, unsigned char d
 	assert (result != NULL);
 
 	// Receive the header of the package.
-	status = dc_serial_read (device->port, data, 1, NULL);
+	status = dc_iostream_read (device->iostream, data, 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -196,7 +168,7 @@ uwatec_memomouse_read_packet (uwatec_memomouse_device_t *device, unsigned char d
 	}
 
 	// Receive the remaining part of the package.
-	status = dc_serial_read (device->port, data + 1, len + 1, NULL);
+	status = dc_iostream_read (device->iostream, data + 1, len + 1, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -233,11 +205,11 @@ uwatec_memomouse_read_packet_outer (uwatec_memomouse_device_t *device, unsigned
 			return rc;
 
 		// Flush the input buffer.
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 
 		// Reject the packet.
 		unsigned char value = NAK;
-		status = dc_serial_write (device->port, &value, 1, NULL);
+		status = dc_iostream_write (device->iostream, &value, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to reject the packet.");
 			return status;
@@ -276,7 +248,7 @@ uwatec_memomouse_read_packet_inner (uwatec_memomouse_device_t *device, dc_buffer
 
 		// Accept the packet.
 		unsigned char value = ACK;
-		status = dc_serial_write (device->port, &value, 1, NULL);
+		status = dc_iostream_write (device->iostream, &value, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to accept the packet.");
 			return status;
@@ -343,22 +315,22 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t *
 	device_event_emit (&device->base, DC_EVENT_PROGRESS, &progress);
 
 	// Waiting for greeting message.
-	while (dc_serial_get_available (device->port, &available) == DC_STATUS_SUCCESS && available == 0) {
+	while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) {
 		if (device_is_cancelled (abstract))
 			return DC_STATUS_CANCELLED;
 
 		// Flush the input buffer.
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 
 		// Reject the packet.
 		unsigned char value = NAK;
-		status = dc_serial_write (device->port, &value, 1, NULL);
+		status = dc_iostream_write (device->iostream, &value, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to reject the packet.");
 			return status;
 		}
 
-		dc_serial_sleep (device->port, 300);
+		dc_iostream_sleep (device->iostream, 300);
 	}
 
 	// Read the ID string.
@@ -381,24 +353,24 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t *
 
 	// Wait a small amount of time before sending the command.
 	// Without this delay, the transfer will fail most of the time.
-	dc_serial_sleep (device->port, 50);
+	dc_iostream_sleep (device->iostream, 50);
 
 	// Keep send the command to the device,
 	// until the ACK answer is received.
 	unsigned char answer = NAK;
 	while (answer == NAK) {
 		// Flush the input buffer.
-		dc_serial_purge (device->port, DC_DIRECTION_INPUT);
+		dc_iostream_purge (device->iostream, DC_DIRECTION_INPUT);
 
 		// Send the command to the device.
-		status = dc_serial_write (device->port, command, sizeof (command), NULL);
+		status = dc_iostream_write (device->iostream, command, sizeof (command), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to send the command.");
 			return status;
 		}
 
 		// Wait for the answer (ACK).
-		status = dc_serial_read (device->port, &answer, 1, NULL);
+		status = dc_iostream_read (device->iostream, &answer, 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return status;
@@ -412,12 +384,12 @@ uwatec_memomouse_dump_internal (uwatec_memomouse_device_t *device, dc_buffer_t *
 	}
 
 	// Wait for the data packet.
-	while (dc_serial_get_available (device->port, &available) == DC_STATUS_SUCCESS && available == 0) {
+	while (dc_iostream_get_available (device->iostream, &available) == DC_STATUS_SUCCESS && available == 0) {
 		if (device_is_cancelled (abstract))
 			return DC_STATUS_CANCELLED;
 
 		device_event_emit (&device->base, DC_EVENT_WAITING, NULL);
-		dc_serial_sleep (device->port, 100);
+		dc_iostream_sleep (device->iostream, 100);
 	}
 
 	// Fetch the current system time.
@@ -457,10 +429,10 @@ uwatec_memomouse_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 	// Give the interface some time to notice the DTR
 	// line change from a previous transfer (if any).
-	dc_serial_sleep (device->port, 500);
+	dc_iostream_sleep (device->iostream, 500);
 
 	// Set the DTR line.
-	rc = dc_serial_set_dtr (device->port, 1);
+	rc = dc_iostream_set_dtr (device->iostream, 1);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to set the RTS line.");
 		return rc;
@@ -470,7 +442,7 @@ uwatec_memomouse_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 	status = uwatec_memomouse_dump_internal (device, buffer);
 
 	// Clear the DTR line again.
-	rc = dc_serial_set_dtr (device->port, 0);
+	rc = dc_iostream_set_dtr (device->iostream, 0);
 	if (rc != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to set the RTS line.");
 		return rc;
diff --git a/src/uwatec_memomouse.h b/src/uwatec_memomouse.h
index 8d37dc6..49ecf9e 100644
--- a/src/uwatec_memomouse.h
+++ b/src/uwatec_memomouse.h
@@ -23,6 +23,7 @@
 #define UWATEC_MEMOMOUSE_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-uwatec_memomouse_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+uwatec_memomouse_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 uwatec_memomouse_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int devtime, dc_ticks_t systime);
diff --git a/src/uwatec_meridian.c b/src/uwatec_meridian.c
index d59b855..87334de 100644
--- a/src/uwatec_meridian.c
+++ b/src/uwatec_meridian.c
@@ -27,7 +27,6 @@
 #include "context-private.h"
 #include "device-private.h"
 #include "checksum.h"
-#include "serial.h"
 #include "array.h"
 
 #define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_meridian_device_vtable)
@@ -37,7 +36,7 @@
 
 typedef struct uwatec_meridian_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned int timestamp;
 	unsigned int devtime;
 	dc_ticks_t systime;
@@ -46,7 +45,6 @@ typedef struct uwatec_meridian_device_t {
 static dc_status_t uwatec_meridian_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
 static dc_status_t uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t uwatec_meridian_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t uwatec_meridian_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t uwatec_meridian_device_vtable = {
 	sizeof(uwatec_meridian_device_t),
@@ -56,7 +54,7 @@ static const dc_device_vtable_t uwatec_meridian_device_vtable = {
 	NULL, /* write */
 	uwatec_meridian_device_dump, /* dump */
 	uwatec_meridian_device_foreach, /* foreach */
-	uwatec_meridian_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
@@ -82,7 +80,7 @@ uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char
 	packet[11 + csize] = checksum_xor_uint8 (packet + 7, csize + 4, 0x00);
 
 	// Send the packet.
-	status = dc_serial_write (device->port, packet, csize + 12, NULL);
+	status = dc_iostream_write (device->iostream, packet, csize + 12, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
@@ -90,7 +88,7 @@ uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char
 
 	// Read the echo.
 	unsigned char echo[sizeof(packet)];
-	status = dc_serial_read (device->port, echo, csize + 12, NULL);
+	status = dc_iostream_read (device->iostream, echo, csize + 12, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the echo.");
 		return status;
@@ -104,7 +102,7 @@ uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char
 
 	// Read the header.
 	unsigned char header[6];
-	status = dc_serial_read (device->port, header, sizeof (header), NULL);
+	status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the header.");
 		return status;
@@ -117,7 +115,7 @@ uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char
 	}
 
 	// Read the packet.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the packet.");
 		return status;
@@ -125,7 +123,7 @@ uwatec_meridian_transfer (uwatec_meridian_device_t *device, const unsigned char
 
 	// Read the checksum.
 	unsigned char csum = 0x00;
-	status = dc_serial_read (device->port, &csum, sizeof (csum), NULL);
+	status = dc_iostream_read (device->iostream, &csum, sizeof (csum), NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the checksum.");
 		return status;
@@ -182,7 +180,7 @@ uwatec_meridian_handshake (uwatec_meridian_device_t *device)
 
 
 dc_status_t
-uwatec_meridian_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+uwatec_meridian_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	uwatec_meridian_device_t *device = NULL;
@@ -198,48 +196,39 @@ uwatec_meridian_device_open (dc_device_t **out, dc_context_t *context, const cha
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (57600 8N1).
-	status = dc_serial_configure (device->port, 57600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 57600, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (3000ms).
-	status  = dc_serial_set_timeout (device->port, 3000);
+	status  = dc_iostream_set_timeout (device->iostream, 3000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Perform the handshaking.
 	status = uwatec_meridian_handshake (device);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to handshake with the device.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -247,23 +236,6 @@ error_free:
 
 
 static dc_status_t
-uwatec_meridian_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 uwatec_meridian_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	uwatec_meridian_device_t *device = (uwatec_meridian_device_t*) abstract;
@@ -397,7 +369,7 @@ uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Read the header.
 		unsigned char header[5];
-		status = dc_serial_read (device->port, header, sizeof (header), NULL);
+		status = dc_iostream_read (device->iostream, header, sizeof (header), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the header.");
 			return status;
@@ -411,7 +383,7 @@ uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		}
 
 		// Read the packet data.
-		status = dc_serial_read (device->port, data + nbytes, packetsize - 1, NULL);
+		status = dc_iostream_read (device->iostream, data + nbytes, packetsize - 1, NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the packet.");
 			return status;
@@ -419,7 +391,7 @@ uwatec_meridian_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Read the checksum.
 		unsigned char csum = 0x00;
-		status = dc_serial_read (device->port, &csum, sizeof (csum), NULL);
+		status = dc_iostream_read (device->iostream, &csum, sizeof (csum), NULL);
 		if (status != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the checksum.");
 			return status;
diff --git a/src/uwatec_meridian.h b/src/uwatec_meridian.h
index 9c6918c..4ac80d3 100644
--- a/src/uwatec_meridian.h
+++ b/src/uwatec_meridian.h
@@ -23,6 +23,7 @@
 #define UWATEC_MERIDIAN_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-uwatec_meridian_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+uwatec_meridian_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
diff --git a/src/uwatec_smart.c b/src/uwatec_smart.c
index 425e76e..03b8739 100644
--- a/src/uwatec_smart.c
+++ b/src/uwatec_smart.c
@@ -25,15 +25,13 @@
 #include "uwatec_smart.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "irda.h"
 #include "array.h"
 
 #define ISINSTANCE(device) dc_device_isinstance((device), &uwatec_smart_device_vtable)
 
 typedef struct uwatec_smart_device_t {
 	dc_device_t base;
-	dc_irda_t *socket;
-	unsigned int address;
+	dc_iostream_t *iostream;
 	unsigned int timestamp;
 	unsigned int devtime;
 	dc_ticks_t systime;
@@ -42,7 +40,6 @@ typedef struct uwatec_smart_device_t {
 static dc_status_t uwatec_smart_device_set_fingerprint (dc_device_t *device, const unsigned char data[], unsigned int size);
 static dc_status_t uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t uwatec_smart_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t uwatec_smart_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t uwatec_smart_device_vtable = {
 	sizeof(uwatec_smart_device_t),
@@ -52,34 +49,12 @@ static const dc_device_vtable_t uwatec_smart_device_vtable = {
 	NULL, /* write */
 	uwatec_smart_device_dump, /* dump */
 	uwatec_smart_device_foreach, /* foreach */
-	uwatec_smart_device_close /* close */
+	NULL /* close */
 };
 
 static dc_status_t
 uwatec_smart_extract_dives (dc_device_t *device, const unsigned char data[], unsigned int size, dc_dive_callback_t callback, void *userdata);
 
-static void
-uwatec_smart_discovery (unsigned int address, const char *name, unsigned int charset, unsigned int hints, void *userdata)
-{
-	uwatec_smart_device_t *device = (uwatec_smart_device_t*) userdata;
-	if (device == NULL)
-		return;
-
-	if (strncmp (name, "UWATEC Galileo Sol", 18) == 0 ||
-		strncmp (name, "Uwatec Smart", 12) == 0 ||
-		strstr (name, "Uwatec") != NULL ||
-		strstr (name, "UWATEC") != NULL ||
-		strstr (name, "Aladin") != NULL ||
-		strstr (name, "ALADIN") != NULL ||
-		strstr (name, "Smart") != NULL ||
-		strstr (name, "SMART") != NULL ||
-		strstr (name, "Galileo") != NULL ||
-		strstr (name, "GALILEO") != NULL)
-	{
-		device->address = address;
-	}
-}
-
 
 static dc_status_t
 uwatec_smart_transfer (uwatec_smart_device_t *device, const unsigned char command[], unsigned int csize, unsigned char answer[], unsigned int asize)
@@ -87,13 +62,13 @@ uwatec_smart_transfer (uwatec_smart_device_t *device, const unsigned char comman
 	dc_status_t status = DC_STATUS_SUCCESS;
 	dc_device_t *abstract = (dc_device_t *) device;
 
-	status = dc_irda_write (device->socket, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
-	status = dc_irda_read (device->socket, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -141,7 +116,7 @@ uwatec_smart_handshake (uwatec_smart_device_t *device)
 
 
 dc_status_t
-uwatec_smart_device_open (dc_device_t **out, dc_context_t *context)
+uwatec_smart_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	uwatec_smart_device_t *device = NULL;
@@ -157,52 +132,22 @@ uwatec_smart_device_open (dc_device_t **out, dc_context_t *context)
 	}
 
 	// Set the default values.
-	device->socket = NULL;
-	device->address = 0;
+	device->iostream = iostream;
 	device->timestamp = 0;
 	device->systime = (dc_ticks_t) -1;
 	device->devtime = 0;
 
-	// Open the irda socket.
-	status = dc_irda_open (&device->socket, context);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the irda socket.");
-		goto error_free;
-	}
-
-	// Discover the device.
-	status = dc_irda_discover (device->socket, uwatec_smart_discovery, device);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to discover the device.");
-		goto error_close;
-	}
-
-	if (device->address == 0) {
-		ERROR (context, "No dive computer found.");
-		status = DC_STATUS_IO;
-		goto error_close;
-	}
-
-	// Connect the device.
-	status = dc_irda_connect_lsap (device->socket, device->address, 1);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to connect the device.");
-		goto error_close;
-	}
-
 	// Perform the handshaking.
 	status = uwatec_smart_handshake (device);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to handshake with the device.");
-		goto error_close;
+		goto error_free;
 	}
 
 	*out = (dc_device_t*) device;
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_irda_close (device->socket);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -210,23 +155,6 @@ error_free:
 
 
 static dc_status_t
-uwatec_smart_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	uwatec_smart_device_t *device = (uwatec_smart_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_irda_close (device->socket);
-	if (status != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 uwatec_smart_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	uwatec_smart_device_t *device = (uwatec_smart_device_t*) abstract;
@@ -361,7 +289,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 
 		// Increase the packet size if more data is immediately available.
 		size_t available = 0;
-		rc = dc_irda_get_available (device->socket, &available);
+		rc = dc_iostream_get_available (device->iostream, &available);
 		if (rc == DC_STATUS_SUCCESS && available > len)
 			len = available;
 
@@ -369,7 +297,7 @@ uwatec_smart_device_dump (dc_device_t *abstract, dc_buffer_t *buffer)
 		if (nbytes + len > length)
 			len = length - nbytes;
 
-		rc = dc_irda_read (device->socket, data + nbytes, len, NULL);
+		rc = dc_iostream_read (device->iostream, data + nbytes, len, NULL);
 		if (rc != DC_STATUS_SUCCESS) {
 			ERROR (abstract->context, "Failed to receive the answer.");
 			return rc;
diff --git a/src/uwatec_smart.h b/src/uwatec_smart.h
index 5304c73..2f7f2b9 100644
--- a/src/uwatec_smart.h
+++ b/src/uwatec_smart.h
@@ -23,6 +23,7 @@
 #define UWATEC_SMART_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 #include <libdivecomputer/parser.h>
 
@@ -31,7 +32,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-uwatec_smart_device_open (dc_device_t **device, dc_context_t *context);
+uwatec_smart_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 dc_status_t
 uwatec_smart_parser_create (dc_parser_t **parser, dc_context_t *context, unsigned int model, unsigned int devtime, dc_ticks_t systime);
diff --git a/src/zeagle_n2ition3.c b/src/zeagle_n2ition3.c
index fed6ae2..d90fe6f 100644
--- a/src/zeagle_n2ition3.c
+++ b/src/zeagle_n2ition3.c
@@ -26,7 +26,6 @@
 #include "zeagle_n2ition3.h"
 #include "context-private.h"
 #include "device-private.h"
-#include "serial.h"
 #include "checksum.h"
 #include "array.h"
 #include "ringbuffer.h"
@@ -46,7 +45,7 @@
 
 typedef struct zeagle_n2ition3_device_t {
 	dc_device_t base;
-	dc_serial_t *port;
+	dc_iostream_t *iostream;
 	unsigned char fingerprint[16];
 } zeagle_n2ition3_device_t;
 
@@ -54,7 +53,6 @@ static dc_status_t zeagle_n2ition3_device_set_fingerprint (dc_device_t *abstract
 static dc_status_t zeagle_n2ition3_device_read (dc_device_t *abstract, unsigned int address, unsigned char data[], unsigned int size);
 static dc_status_t zeagle_n2ition3_device_dump (dc_device_t *abstract, dc_buffer_t *buffer);
 static dc_status_t zeagle_n2ition3_device_foreach (dc_device_t *abstract, dc_dive_callback_t callback, void *userdata);
-static dc_status_t zeagle_n2ition3_device_close (dc_device_t *abstract);
 
 static const dc_device_vtable_t zeagle_n2ition3_device_vtable = {
 	sizeof(zeagle_n2ition3_device_t),
@@ -64,7 +62,7 @@ static const dc_device_vtable_t zeagle_n2ition3_device_vtable = {
 	NULL, /* write */
 	zeagle_n2ition3_device_dump, /* dump */
 	zeagle_n2ition3_device_foreach, /* foreach */
-	zeagle_n2ition3_device_close /* close */
+	NULL /* close */
 };
 
 
@@ -80,14 +78,14 @@ zeagle_n2ition3_packet (zeagle_n2ition3_device_t *device, const unsigned char co
 		return DC_STATUS_CANCELLED;
 
 	// Send the command to the device.
-	status = dc_serial_write (device->port, command, csize, NULL);
+	status = dc_iostream_write (device->iostream, command, csize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to send the command.");
 		return status;
 	}
 
 	// Receive the answer of the device.
-	status = dc_serial_read (device->port, answer, asize, NULL);
+	status = dc_iostream_read (device->iostream, answer, asize, NULL);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (abstract->context, "Failed to receive the answer.");
 		return status;
@@ -132,7 +130,7 @@ zeagle_n2ition3_init (zeagle_n2ition3_device_t *device)
 }
 
 dc_status_t
-zeagle_n2ition3_device_open (dc_device_t **out, dc_context_t *context, const char *name)
+zeagle_n2ition3_device_open (dc_device_t **out, dc_context_t *context, dc_iostream_t *iostream)
 {
 	dc_status_t status = DC_STATUS_SUCCESS;
 	zeagle_n2ition3_device_t *device = NULL;
@@ -148,32 +146,25 @@ zeagle_n2ition3_device_open (dc_device_t **out, dc_context_t *context, const cha
 	}
 
 	// Set the default values.
-	device->port = NULL;
+	device->iostream = iostream;
 	memset (device->fingerprint, 0, sizeof (device->fingerprint));
 
-	// Open the device.
-	status = dc_serial_open (&device->port, context, name);
-	if (status != DC_STATUS_SUCCESS) {
-		ERROR (context, "Failed to open the serial port.");
-		goto error_free;
-	}
-
 	// Set the serial communication protocol (4800 8N1).
-	status = dc_serial_configure (device->port, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
+	status = dc_iostream_configure (device->iostream, 4800, 8, DC_PARITY_NONE, DC_STOPBITS_ONE, DC_FLOWCONTROL_NONE);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the terminal attributes.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Set the timeout for receiving data (1000 ms).
-	status = dc_serial_set_timeout (device->port, 1000);
+	status = dc_iostream_set_timeout (device->iostream, 1000);
 	if (status != DC_STATUS_SUCCESS) {
 		ERROR (context, "Failed to set the timeout.");
-		goto error_close;
+		goto error_free;
 	}
 
 	// Make sure everything is in a sane state.
-	dc_serial_purge (device->port, DC_DIRECTION_ALL);
+	dc_iostream_purge (device->iostream, DC_DIRECTION_ALL);
 
 	// Send the init commands.
 	zeagle_n2ition3_init (device);
@@ -182,8 +173,6 @@ zeagle_n2ition3_device_open (dc_device_t **out, dc_context_t *context, const cha
 
 	return DC_STATUS_SUCCESS;
 
-error_close:
-	dc_serial_close (device->port);
 error_free:
 	dc_device_deallocate ((dc_device_t *) device);
 	return status;
@@ -191,23 +180,6 @@ error_free:
 
 
 static dc_status_t
-zeagle_n2ition3_device_close (dc_device_t *abstract)
-{
-	dc_status_t status = DC_STATUS_SUCCESS;
-	zeagle_n2ition3_device_t *device = (zeagle_n2ition3_device_t*) abstract;
-	dc_status_t rc = DC_STATUS_SUCCESS;
-
-	// Close the device.
-	rc = dc_serial_close (device->port);
-	if (rc != DC_STATUS_SUCCESS) {
-		dc_status_set_error(&status, rc);
-	}
-
-	return status;
-}
-
-
-static dc_status_t
 zeagle_n2ition3_device_set_fingerprint (dc_device_t *abstract, const unsigned char data[], unsigned int size)
 {
 	zeagle_n2ition3_device_t *device = (zeagle_n2ition3_device_t *) abstract;
diff --git a/src/zeagle_n2ition3.h b/src/zeagle_n2ition3.h
index d10daa7..2a0d683 100644
--- a/src/zeagle_n2ition3.h
+++ b/src/zeagle_n2ition3.h
@@ -23,6 +23,7 @@
 #define ZEAGLE_N2ITION3_H
 
 #include <libdivecomputer/context.h>
+#include <libdivecomputer/iostream.h>
 #include <libdivecomputer/device.h>
 
 #ifdef __cplusplus
@@ -30,7 +31,7 @@ extern "C" {
 #endif /* __cplusplus */
 
 dc_status_t
-zeagle_n2ition3_device_open (dc_device_t **device, dc_context_t *context, const char *name);
+zeagle_n2ition3_device_open (dc_device_t **device, dc_context_t *context, dc_iostream_t *iostream);
 
 #ifdef __cplusplus
 }
-- 
2.11.0

-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0003-Port-the-serial-code-to-the-new-I-O-interface.patch
Type: text/x-diff
Size: 50599 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0008.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0004-Port-the-IrDA-code-to-the-new-I-O-interface.patch
Type: text/x-diff
Size: 22680 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0009.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0005-Port-the-USB-HID-code-to-the-new-I-O-interface.patch
Type: text/x-diff
Size: 15163 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0010.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0006-Port-the-bluetooth-code-to-the-new-I-O-interface.patch
Type: text/x-diff
Size: 24256 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0011.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0007-Update-the-example-application.patch
Type: text/x-diff
Size: 11771 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0012.patch>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0008-Add-support-for-the-scan-command.patch
Type: text/x-diff
Size: 6395 bytes
Desc: not available
URL: <http://libdivecomputer.org/pipermail/devel/attachments/20170603/c9c608a9/attachment-0013.patch>


More information about the devel mailing list