Index: uspace/lib/c/generic/io/chardev.c
===================================================================
--- uspace/lib/c/generic/io/chardev.c	(revision c657bd7f48e0cda0c4e65bc45f24367c51318513)
+++ uspace/lib/c/generic/io/chardev.c	(revision 19ea61d4d9f3c1ab64d2df09da1ad3f2772d554f)
@@ -96,28 +96,41 @@
 int chardev_read(chardev_t *chardev, void *buf, size_t size, size_t *nread)
 {
-	if (size > 4 * sizeof(sysarg_t))
-		return ELIMIT;
-
 	async_exch_t *exch = async_exchange_begin(chardev->sess);
-	sysarg_t message[4] = { 0 };
-	const ssize_t ret = async_req_1_4(exch, CHARDEV_READ, size,
-	    &message[0], &message[1], &message[2], &message[3]);
+
+	if (size > DATA_XFER_LIMIT) {
+		/* This should not hurt anything. */
+		size = DATA_XFER_LIMIT;
+	}
+
+	ipc_call_t answer;
+	aid_t req = async_send_0(exch, CHARDEV_READ, &answer);
+	int rc = async_data_read_start(exch, buf, size);
 	async_exchange_end(exch);
-	if (ret > 0 && (size_t)ret <= size)
-		memcpy(buf, message, size);
-
-	if (ret < 0) {
+
+	if (rc != EOK) {
+		async_forget(req);
 		*nread = 0;
-		return ret;
-	}
-
-	*nread = ret;
-	return EOK;
-}
-
-/** Write to character device.
- *
- * Write @a size bytes from @a data to character device. On success EOK
- * is returned, all bytes were written and @a *nwritten is set to @a size.
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+
+	if (retval != EOK) {
+		*nread = 0;
+		return retval;
+	}
+
+	*nread = IPC_GET_ARG2(answer);
+	/* In case of partial success, ARG1 contains the error code */
+	return IPC_GET_ARG1(answer);
+
+}
+
+/** Write up to DATA_XFER_LIMIT bytes to character device.
+ *
+ * Write up to @a size or DATA_XFER_LIMIT bytes from @a data to character
+ * device. On success EOK is returned, bytes were written and @a *nwritten
+ * is set to min(@a size, DATA_XFER_LIMIT)
  *
  * On error a non-zero error code is returned and @a *nwritten is filled with
@@ -131,25 +144,74 @@
  * @return EOK on success or non-zero error code
  */
+static int chardev_write_once(chardev_t *chardev, const void *data,
+    size_t size, size_t *nwritten)
+{
+	async_exch_t *exch = async_exchange_begin(chardev->sess);
+	ipc_call_t answer;
+	aid_t req;
+	int rc;
+
+	/* Break down large transfers */
+	if (size > DATA_XFER_LIMIT)
+		size = DATA_XFER_LIMIT;
+
+	req = async_send_0(exch, CHARDEV_WRITE, &answer);
+	rc = async_data_write_start(exch, data, size);
+	async_exchange_end(exch);
+
+	if (rc != EOK) {
+		async_forget(req);
+		*nwritten = 0;
+		return rc;
+	}
+
+	sysarg_t retval;
+	async_wait_for(req, &retval);
+	if (retval != EOK) {
+		*nwritten = 0;
+		return retval;
+	}
+
+	*nwritten = IPC_GET_ARG2(answer);
+	/* In case of partial success, ARG1 contains the error code */
+	return IPC_GET_ARG1(answer);
+}
+
+/** Write to character device.
+ *
+ * Write @a size bytes from @a data to character device. On success EOK
+ * is returned, all bytes were written and @a *nwritten is set to @a size.
+ *
+ * On error a non-zero error code is returned and @a *nwritten is filled with
+ * the number of bytes that were successfully transferred.
+ *
+ * @param chardev Character device
+ * @param buf Destination buffer
+ * @param size Maximum number of bytes to read
+ * @param nwritten Place to store actual number of bytes written
+ *
+ * @return EOK on success or non-zero error code
+ */
 int chardev_write(chardev_t *chardev, const void *data, size_t size,
     size_t *nwritten)
 {
-	int ret;
-
-	if (size > 3 * sizeof(sysarg_t))
-		return ELIMIT;
-
-	async_exch_t *exch = async_exchange_begin(chardev->sess);
-	sysarg_t message[3] = { 0 };
-	memcpy(message, data, size);
-	ret = async_req_4_0(exch, CHARDEV_WRITE, size,
-	    message[0], message[1], message[2]);
-	async_exchange_end(exch);
-
-	if (ret < 0) {
-		*nwritten = 0;
-		return ret;
-	}
-
-	*nwritten = ret;
+	size_t nw;
+	size_t p;
+	int rc;
+
+	p = 0;
+	while (p < size) {
+		rc = chardev_write_once(chardev, data + p, size - p, &nw);
+		/* nw is always valid, we can have partial success */
+		p += nw;
+
+		if (rc != EOK) {
+			/* We can return partial success */
+			*nwritten = p;
+			return rc;
+		}
+	}
+
+	*nwritten = p;
 	return EOK;
 }
Index: uspace/lib/c/generic/io/chardev_srv.c
===================================================================
--- uspace/lib/c/generic/io/chardev_srv.c	(revision c657bd7f48e0cda0c4e65bc45f24367c51318513)
+++ uspace/lib/c/generic/io/chardev_srv.c	(revision 19ea61d4d9f3c1ab64d2df09da1ad3f2772d554f)
@@ -46,21 +46,40 @@
     ipc_call_t *call)
 {
-	size_t size = IPC_GET_ARG1(*call);
+	void *buf;
+	size_t size;
 	int rc;
+	ipc_callid_t rcallid;
 
-	if (srv->srvs->ops->read == NULL) {
-		async_answer_0(callid, ENOTSUP);
+	if (!async_data_read_receive(&rcallid, &size)) {
+		async_answer_0(callid, EINVAL);
 		return;
 	}
 
-	if (size <= 4 * sizeof(sysarg_t)) {
-		sysarg_t message[4] = {};
+	buf = malloc(size);
+	if (buf == NULL) {
+		async_answer_0(rcallid, ENOMEM);
+		async_answer_0(callid, ENOMEM);
+		return;
+	}
 
-		rc = srv->srvs->ops->read(srv, (char *)message, size);
-		async_answer_4(callid, rc, message[0], message[1],
-		    message[2], message[3]);
-	} else {
-		async_answer_0(callid, ELIMIT);
+	if (srv->srvs->ops->read == NULL) {
+		async_answer_0(rcallid, ENOTSUP);
+		async_answer_0(callid, ENOTSUP);
+		free(buf);
+		return;
 	}
+
+	rc = srv->srvs->ops->read(srv, buf, size);
+	if (rc < 0) {
+		async_answer_0(rcallid, rc);
+		async_answer_0(callid, rc);
+		free(buf);
+		return;
+	}
+
+	async_data_read_finalize(rcallid, buf, size);
+
+	free(buf);
+	async_answer_2(callid, EOK, EOK, rc /* nread */);
 }
 
@@ -68,6 +87,13 @@
     ipc_call_t *call)
 {
-	size_t size = IPC_GET_ARG1(*call);
+	void *data;
+	size_t size;
 	int rc;
+
+	rc = async_data_write_accept(&data, false, 0, 0, 0, &size);
+	if (rc != EOK) {
+		async_answer_0(callid, rc);
+		return;
+	}
 
 	if (srv->srvs->ops->write == NULL) {
@@ -76,16 +102,10 @@
 	}
 
-	if (size <= 3 * sizeof(sysarg_t)) {
-		const sysarg_t message[3] = {
-			IPC_GET_ARG2(*call),
-			IPC_GET_ARG3(*call),
-			IPC_GET_ARG4(*call)
-		};
+	rc = srv->srvs->ops->write(srv, data, size);
+	free(data);
+	if (rc < 0)
+		async_answer_0(callid, rc);
 
-		rc = srv->srvs->ops->write(srv, (char *)message, size);
-		async_answer_0(callid, rc);
-	} else {
-		async_answer_0(callid, ELIMIT);
-	}
+	async_answer_2(callid, EOK, EOK, rc /* nwritten */);
 }
 
