Index: uspace/app/test_serial/test_serial.c
===================================================================
--- uspace/app/test_serial/test_serial.c	(revision ba95e8f41817124410b7cfa7c805434f83686dff)
+++ uspace/app/test_serial/test_serial.c	(revision ca97cad5803ad9a8511c331f8c9583385867b64d)
@@ -45,4 +45,5 @@
 #include <devman.h>
 #include <device/char.h>
+#include <string.h>
 
 #define NAME 		"test serial"
@@ -104,9 +105,14 @@
 		if (read > 0) {			
 			buf[read] = 0;
-			printf(buf);		
+			printf(buf);	
+			// write data back to the device to test the opposite direction of data transfer
+			write_dev(phone, buf, read);
 		} else {	
 			usleep(100000);			
 		}	
 	}
+	
+	char *the_end = "\n---------\nTHE END\n---------\n";
+	write_dev(phone, the_end, str_size(the_end));
 	
 	devman_hangup_phone(DEVMAN_CLIENT);
Index: uspace/lib/libc/generic/device/char.c
===================================================================
--- uspace/lib/libc/generic/device/char.c	(revision ba95e8f41817124410b7cfa7c805434f83686dff)
+++ uspace/lib/libc/generic/device/char.c	(revision ca97cad5803ad9a8511c331f8c9583385867b64d)
@@ -40,14 +40,33 @@
 #include <stdio.h>
 
-
-int read_dev(int dev_phone, void *buf, size_t len)
-{	
+/** Read to or write from the device using its character interface.
+ * 
+ * Helper function.
+ * 
+ * @param dev_phone phone to the device.
+ * @param buf the buffer for the data read from or written to the device.
+ * @param len the maximum length of the data to be read or written.
+ * @param read read from the device if true, write to it otherwise.
+ * 
+ * @return non-negative number of bytes actually read from or written to the device on success,
+ * negative error number otherwise.
+ * 
+ */
+static int rw_dev(int dev_phone, void *buf, size_t len, bool read) 
+{
 	ipc_call_t answer;
 	
 	async_serialize_start();
 	
-	aid_t req = async_send_1(dev_phone, DEV_IFACE_ID(CHAR_DEV_IFACE), CHAR_READ_DEV, &answer);
+	aid_t req;
+	int rc;
 	
-	int rc = async_data_read_start(dev_phone, buf, len);
+	if (read) {
+		req = async_send_1(dev_phone, DEV_IFACE_ID(CHAR_DEV_IFACE), CHAR_READ_DEV, &answer);
+		rc = async_data_read_start(dev_phone, buf, len);		
+	} else {
+		req = async_send_1(dev_phone, DEV_IFACE_ID(CHAR_DEV_IFACE), CHAR_WRITE_DEV, &answer);
+		rc = async_data_write_start(dev_phone, buf, len);
+	}
 	
 	if (rc != EOK) {
@@ -70,11 +89,31 @@
 	}
 	
-	return IPC_GET_ARG1(answer);
+	return IPC_GET_ARG1(answer);	
 }
 
+/** Read from the device using its character interface.
+ * 
+ * @param dev_phone phone to the device.
+ * @param buf the output buffer for the data read from the device.
+ * @param len the maximum length of the data to be read.
+ * 
+ * @return non-negative number of bytes actually read from the device on success, negative error number otherwise.
+ */
+int read_dev(int dev_phone, void *buf, size_t len)
+{	
+	return rw_dev(dev_phone, buf, len, true);
+}
+
+/** Write to the device using its character interface.
+ * 
+ * @param dev_phone phone to the device.
+ * @param buf the input buffer containg the data to be written to the device.
+ * @param len the maximum length of the data to be written.
+ * 
+ * @return non-negative number of bytes actually written to the device on success, negative error number otherwise.
+ */
 int write_dev(int dev_phone, void *buf, size_t len)
 {
-	// TODO
-	return 0;
+	return rw_dev(dev_phone, buf, len, false);
 }
 
Index: uspace/lib/libdrv/generic/remote_char.c
===================================================================
--- uspace/lib/libdrv/generic/remote_char.c	(revision ba95e8f41817124410b7cfa7c805434f83686dff)
+++ uspace/lib/libdrv/generic/remote_char.c	(revision ca97cad5803ad9a8511c331f8c9583385867b64d)
@@ -45,4 +45,6 @@
 static void remote_char_write(device_t *dev, void *iface, ipc_callid_t callid, ipc_call_t *call);
 
+/** Remote character interface operations. 
+ */
 static remote_iface_func_ptr_t remote_char_iface_ops [] = {
 	&remote_char_read,
@@ -50,4 +52,7 @@
 }; 
  
+/** Remote character interface structure. 
+ * Interface for processing request from remote clients addressed to the character interface.
+ */
 remote_iface_t remote_char_iface = {
 	.method_count = sizeof(remote_char_iface_ops) / sizeof(remote_iface_func_ptr_t),
@@ -55,4 +60,12 @@
 };
 
+/** Process the read request from the remote client.
+ * 
+ * Receive the read request's parameters from the remote client and pass them to the local interface.
+ * Return the result of the operation processed by the local interface to the remote client.
+ * 
+ * @param dev the device from which the data are read.
+ * @param iface the local interface structure. 
+ */
 static void remote_char_read(device_t *dev, void *iface, ipc_callid_t callid, ipc_call_t *call)
 {	
@@ -86,21 +99,34 @@
 	}
 	
+	// the operation was successful, return the number of data read
 	async_data_read_finalize(cid, buf, ret);
 	ipc_answer_1(callid, EOK, ret);	
 }
 
+/** Process the write request from the remote client.
+ * 
+ * Receive the write request's parameters from the remote client and pass them to the local interface.
+ * Return the result of the operation processed by the local interface to the remote client.
+ * 
+ * @param dev the device to which the data are written.
+ * @param iface the local interface structure. 
+ */
 static void remote_char_write(device_t *dev, void *iface, ipc_callid_t callid, ipc_call_t *call)
 {
 	char_iface_t *char_iface = (char_iface_t *)iface;
+	ipc_callid_t cid;
+	size_t len;
+	
+	if (!async_data_write_receive(&cid, &len)) {
+		// TODO handle protocol error
+		ipc_answer_0(callid, EINVAL);
+		return;
+    }
+	
 	if (!char_iface->write) {
+		async_data_write_finalize(cid, NULL, 0);
 		ipc_answer_0(callid, ENOTSUP);
 		return;
 	}	
-	
-	size_t len;
-	if (!async_data_write_receive(&callid, &len)) {
-		// TODO handle protocol error
-		return;
-    }
 	
 	if (len > MAX_CHAR_RW_COUNT) {
@@ -110,5 +136,5 @@
 	char buf[MAX_CHAR_RW_COUNT];
 	
-	async_data_write_finalize(callid, buf, len);
+	async_data_write_finalize(cid, buf, len);
 	
 	int ret = (*char_iface->write)(dev, buf, len);
@@ -116,4 +142,5 @@
 		ipc_answer_0(callid, ret);
 	} else {
+		// the operation was successful, return the number of data written
 		ipc_answer_1(callid, EOK, ret);
 	}
Index: uspace/srv/drivers/serial/serial.c
===================================================================
--- uspace/srv/drivers/serial/serial.c	(revision ba95e8f41817124410b7cfa7c805434f83686dff)
+++ uspace/srv/drivers/serial/serial.c	(revision ca97cad5803ad9a8511c331f8c9583385867b64d)
@@ -92,4 +92,27 @@
 }
 
+static bool serial_received(ioport8_t *port) 
+{
+   return (pio_read_8(port + 5) & 1) != 0;
+}
+
+static uint8_t serial_read_8(ioport8_t *port) 
+{
+	return pio_read_8(port);
+}
+
+static bool is_transmit_empty(ioport8_t *port) 
+{
+   return (pio_read_8(port + 5) & 0x20) != 0;
+}
+
+static void serial_write_8(ioport8_t *port, uint8_t c) 
+{
+	while (!is_transmit_empty(port)) 
+		;
+	
+	pio_write_8(port, c);
+}
+
 static int serial_read(device_t *dev, char *buf, size_t count) 
 {
@@ -111,7 +134,20 @@
 }
 
+static inline void serial_putchar(serial_dev_data_t *data, uint8_t c)
+{	
+	fibril_mutex_lock(&data->mutex);
+	serial_write_8(data->port, c);	
+	fibril_mutex_unlock(&data->mutex);
+}
+
 static int serial_write(device_t *dev, char *buf, size_t count) 
 {
-	// TODO
+	serial_dev_data_t *data = (serial_dev_data_t *)dev->driver_data;
+	
+	size_t idx;
+	for (idx = 0; idx < count; idx++) {
+		serial_putchar(data, (uint8_t)buf[idx]);
+	}
+	
 	return 0;
 }
@@ -150,27 +186,4 @@
 		dev->parent_phone = 0;
 	}	
-}
-
-static bool serial_received(ioport8_t *port) 
-{
-   return (pio_read_8(port + 5) & 1) != 0;
-}
-
-static uint8_t serial_read_8(ioport8_t *port) 
-{
-	return pio_read_8(port);
-}
-
-static bool is_transmit_empty(ioport8_t *port) 
-{
-   return (pio_read_8(port + 5) & 0x20) != 0;
-}
-
-static void serial_write_8(ioport8_t *port, uint8_t c) 
-{
-	while (!is_transmit_empty(port)) 
-		;
-	
-	pio_write_8(port, c);
 }
 
@@ -333,9 +346,11 @@
 	
 	while (cont) {	
+		fibril_mutex_lock(&data->mutex);
+		
 		if (cont = serial_received(port)) {
 			uint8_t val = serial_read_8(port);
 			printf(NAME ": character %c read from %s.\n", val, dev->name);
 			
-			fibril_mutex_lock(&data->mutex);
+			
 			if (data->client_connected) {
 				if (!buf_push_back(&(data->input_buffer), val)) {
@@ -346,8 +361,10 @@
 			} else {
 				printf(NAME ": no client is connected to %s, discarding the character which was read.\n", dev->name);
-			}
-			fibril_mutex_unlock(&data->mutex);
+			}			
 		}
 		
+		fibril_mutex_unlock(&data->mutex);	
+		
+		fibril_yield();		
 	}	
 }
