Index: uspace/drv/ns8250/ns8250.c
===================================================================
--- uspace/drv/ns8250/ns8250.c	(revision 8b1e15ac9100f7b9da56e372d2f441ba44db6fcc)
+++ uspace/drv/ns8250/ns8250.c	(revision 68414f4a1f1b548a47a6b4525bd18dfbed67df1d)
@@ -1,4 +1,5 @@
-/*                        
+/*
  * Copyright (c) 2010 Lenka Trochtova
+ * Copyright (c) 2011 Jiri Svoboda
  * All rights reserved.
  *
@@ -68,4 +69,10 @@
 #define DLAB_MASK (1 << 7)
 
+/** Obtain soft-state structure from function node */
+#define NS8250(fnode) ((ns8250_t *) ((fnode)->dev->driver_data))
+
+/** Obtain soft-state structure from device node */
+#define NS8250_FROM_DEV(dnode) ((ns8250_t *) ((dnode)->driver_data))
+
 /** The number of bits of one data unit send by the serial port. */
 typedef enum {
@@ -85,5 +92,9 @@
 
 /** The driver data for the serial port devices. */
-typedef struct ns8250_dev_data {
+typedef struct ns8250 {
+	/** DDF device node */
+	device_t *dev;
+	/** DDF function node */
+	function_t *fun;
 	/** Is there any client conntected to the device? */
 	bool client_connected;
@@ -98,30 +109,29 @@
 	/** The fibril mutex for synchronizing the access to the device. */
 	fibril_mutex_t mutex;
-} ns8250_dev_data_t;
-
-/** Create driver data for a device.
- *
- * @return		The driver data.
- */
-static ns8250_dev_data_t *create_ns8250_dev_data(void)
-{
-	ns8250_dev_data_t *data;
-	
-	data = (ns8250_dev_data_t *) malloc(sizeof(ns8250_dev_data_t));
-	if (NULL != data) {
-		memset(data, 0, sizeof(ns8250_dev_data_t));
-		fibril_mutex_initialize(&data->mutex);
-	}
-	return data;
-}
-
-/** Delete driver data.
- *
- * @param data		The driver data structure.
- */
-static void delete_ns8250_dev_data(ns8250_dev_data_t *data)
-{
-	if (data != NULL)
-		free(data);
+} ns8250_t;
+
+/** Create per-device soft-state structure.
+ *
+ * @return	Pointer to soft-state structure.
+ */
+static ns8250_t *ns8250_new(void)
+{
+	ns8250_t *ns;
+	
+	ns = (ns8250_t *) calloc(1, sizeof(ns8250_t));
+	if (ns == NULL)
+		return NULL;
+	
+	fibril_mutex_initialize(&ns->mutex);
+	return ns;
+}
+
+/** Delete soft-state structure.
+ *
+ * @param ns	The driver data structure.
+ */
+static void ns8250_delete(ns8250_t *ns)
+{
+	free(ns);
 }
 
@@ -171,5 +181,5 @@
 /** Read data from the serial port device.
  *
- * @param dev		The serial port device.
+ * @param fun		The serial port function
  * @param buf		The ouput buffer for read data.
  * @param count		The number of bytes to be read.
@@ -180,13 +190,13 @@
 static int ns8250_read(function_t *fun, char *buf, size_t count)
 {
+	ns8250_t *ns = NS8250(fun);
 	int ret = EOK;
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) fun->dev->driver_data;
-	
-	fibril_mutex_lock(&data->mutex);
-	while (!buf_is_empty(&data->input_buffer) && (size_t)ret < count) {
-		buf[ret] = (char)buf_pop_front(&data->input_buffer);
+	
+	fibril_mutex_lock(&ns->mutex);
+	while (!buf_is_empty(&ns->input_buffer) && (size_t)ret < count) {
+		buf[ret] = (char)buf_pop_front(&ns->input_buffer);
 		ret++;
 	}
-	fibril_mutex_unlock(&data->mutex);
+	fibril_mutex_unlock(&ns->mutex);
 	
 	return ret;
@@ -195,28 +205,28 @@
 /** Write a character to the serial port.
  *
- * @param data		The serial port device's driver data.
- * @param c		The character to be written.
- */
-static inline void ns8250_putchar(ns8250_dev_data_t *data, uint8_t c)
-{
-	fibril_mutex_lock(&data->mutex);
-	ns8250_write_8(data->port, c);
-	fibril_mutex_unlock(&data->mutex);
+ * @param ns		Serial port device
+ * @param c		The character to be written
+ */
+static inline void ns8250_putchar(ns8250_t *ns, uint8_t c)
+{
+	fibril_mutex_lock(&ns->mutex);
+	ns8250_write_8(ns->port, c);
+	fibril_mutex_unlock(&ns->mutex);
 }
 
 /** Write data to the serial port.
  *
- * @param dev		The serial port device.
- * @param buf		The data to be written.
- * @param count		The number of bytes to be written.
- * @return		Zero on success.
+ * @param fun		The serial port function
+ * @param buf		The data to be written
+ * @param count		The number of bytes to be written
+ * @return		Zero on success
  */
 static int ns8250_write(function_t *fun, char *buf, size_t count)
 {
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) fun->dev->driver_data;
+	ns8250_t *ns = NS8250(fun);
 	size_t idx;
 	
 	for (idx = 0; idx < count; idx++)
-		ns8250_putchar(data, (uint8_t) buf[idx]);
+		ns8250_putchar(ns, (uint8_t) buf[idx]);
 	
 	return 0;
@@ -244,18 +254,13 @@
 };
 
-/** Clean up the serial port device structure.
- *
- * @param dev		The device structure.
- */
-static void ns8250_dev_cleanup(device_t *dev)
-{
-	if (dev->driver_data != NULL) {
-		delete_ns8250_dev_data((ns8250_dev_data_t*) dev->driver_data);
-		dev->driver_data = NULL;
-	}
-	
-	if (dev->parent_phone > 0) {
-		async_hangup(dev->parent_phone);
-		dev->parent_phone = 0;
+/** Clean up the serial port soft-state
+ *
+ * @param ns		Serial port device
+ */
+static void ns8250_dev_cleanup(ns8250_t *ns)
+{
+	if (ns->dev->parent_phone > 0) {
+		async_hangup(ns->dev->parent_phone);
+		ns->dev->parent_phone = 0;
 	}
 }
@@ -263,18 +268,16 @@
 /** Enable the i/o ports of the device.
  *
- * @param dev		The serial port device.
- * @return		True on success, false otherwise.
- */
-static bool ns8250_pio_enable(device_t *dev)
-{
-	printf(NAME ": ns8250_pio_enable %s\n", dev->name);
-	
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
+ * @param ns		Serial port device
+ * @return		True on success, false otherwise
+ */
+static bool ns8250_pio_enable(ns8250_t *ns)
+{
+	printf(NAME ": ns8250_pio_enable %s\n", ns->dev->name);
 	
 	/* Gain control over port's registers. */
-	if (pio_enable((void *)(uintptr_t) data->io_addr, REG_COUNT,
-	    (void **) &data->port)) {
+	if (pio_enable((void *)(uintptr_t) ns->io_addr, REG_COUNT,
+	    (void **) &ns->port)) {
 		printf(NAME ": error - cannot gain the port %#" PRIx32 " for device "
-		    "%s.\n", data->io_addr, dev->name);
+		    "%s.\n", ns->io_addr, ns->dev->name);
 		return false;
 	}
@@ -285,13 +288,12 @@
 /** Probe the serial port device for its presence.
  *
- * @param dev		The serial port device.
- * @return		True if the device is present, false otherwise.
- */
-static bool ns8250_dev_probe(device_t *dev)
-{
-	printf(NAME ": ns8250_dev_probe %s\n", dev->name);
-	
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
-	ioport8_t *port_addr = data->port;
+ * @param ns		Serial port device
+ * @return		True if the device is present, false otherwise
+ */
+static bool ns8250_dev_probe(ns8250_t *ns)
+{
+	printf(NAME ": ns8250_dev_probe %s\n", ns->dev->name);
+	
+	ioport8_t *port_addr = ns->port;
 	bool res = true;
 	uint8_t olddata;
@@ -310,5 +312,5 @@
 	
 	if (!res)
-		printf(NAME ": device %s is not present.\n", dev->name);
+		printf(NAME ": device %s is not present.\n", ns->dev->name);
 	
 	return res;
@@ -317,10 +319,10 @@
 /** Initialize serial port device.
  *
- * @param dev		The serial port device.
- * @return		Zero on success, negative error number otherwise.
- */
-static int ns8250_dev_initialize(device_t *dev)
-{
-	printf(NAME ": ns8250_dev_initialize %s\n", dev->name);
+ * @param ns		Serial port device
+ * @return		Zero on success, negative error number otherwise
+ */
+static int ns8250_dev_initialize(ns8250_t *ns)
+{
+	printf(NAME ": ns8250_dev_initialize %s\n", ns->dev->name);
 	
 	int ret = EOK;
@@ -329,25 +331,19 @@
 	memset(&hw_resources, 0, sizeof(hw_resource_list_t));
 	
-	/* Allocate driver data for the device. */
-	ns8250_dev_data_t *data = create_ns8250_dev_data();
-	if (data == NULL)
-		return ENOMEM;
-	dev->driver_data = data;
-	
 	/* Connect to the parent's driver. */
-	dev->parent_phone = devman_parent_device_connect(dev->handle,
+	ns->dev->parent_phone = devman_parent_device_connect(ns->dev->handle,
 	    IPC_FLAG_BLOCKING);
-	if (dev->parent_phone < 0) {
+	if (ns->dev->parent_phone < 0) {
 		printf(NAME ": failed to connect to the parent driver of the "
-		    "device %s.\n", dev->name);
-		ret = dev->parent_phone;
+		    "device %s.\n", ns->dev->name);
+		ret = ns->dev->parent_phone;
 		goto failed;
 	}
 	
 	/* Get hw resources. */
-	ret = hw_res_get_resource_list(dev->parent_phone, &hw_resources);
+	ret = hw_res_get_resource_list(ns->dev->parent_phone, &hw_resources);
 	if (ret != EOK) {
 		printf(NAME ": failed to get hw resources for the device "
-		    "%s.\n", dev->name);
+		    "%s.\n", ns->dev->name);
 		goto failed;
 	}
@@ -362,15 +358,15 @@
 		switch (res->type) {
 		case INTERRUPT:
-			data->irq = res->res.interrupt.irq;
+			ns->irq = res->res.interrupt.irq;
 			irq = true;
 			printf(NAME ": the %s device was asigned irq = 0x%x.\n",
-			    dev->name, data->irq);
+			    ns->dev->name, ns->irq);
 			break;
 			
 		case IO_RANGE:
-			data->io_addr = res->res.io_range.address;
+			ns->io_addr = res->res.io_range.address;
 			if (res->res.io_range.size < REG_COUNT) {
 				printf(NAME ": i/o range assigned to the device "
-				    "%s is too small.\n", dev->name);
+				    "%s is too small.\n", ns->dev->name);
 				ret = ELIMIT;
 				goto failed;
@@ -378,5 +374,5 @@
 			ioport = true;
 			printf(NAME ": the %s device was asigned i/o address = "
-			    "0x%x.\n", dev->name, data->io_addr);
+			    "0x%x.\n", ns->dev->name, ns->io_addr);
 			break;
 			
@@ -388,5 +384,5 @@
 	if (!irq || !ioport) {
 		printf(NAME ": missing hw resource(s) for the device %s.\n",
-		    dev->name);
+		    ns->dev->name);
 		ret = ENOENT;
 		goto failed;
@@ -397,5 +393,5 @@
 	
 failed:
-	ns8250_dev_cleanup(dev);
+	ns8250_dev_cleanup(ns);
 	hw_res_clean_resource_list(&hw_resources);
 	return ret;
@@ -404,5 +400,5 @@
 /** Enable interrupts on the serial port device.
  *
- * Interrupt when data is received.
+ * Interrupt when data is received
  *
  * @param port		The base address of the serial port device's ports.
@@ -416,5 +412,5 @@
 /** Disable interrupts on the serial port device.
  *
- * @param port		The base address of the serial port device's ports.
+ * @param port		The base address of the serial port device's ports
  */
 static inline void ns8250_port_interrupts_disable(ioport8_t *port)
@@ -425,13 +421,11 @@
 /** Enable interrupts for the serial port device.
  *
- * @param dev		The device.
- * @return		Zero on success, negative error number otherwise.
- */
-static int ns8250_interrupt_enable(device_t *dev)
-{
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
-	
+ * @param ns		Serial port device
+ * @return		Zero on success, negative error number otherwise
+ */
+static int ns8250_interrupt_enable(ns8250_t *ns)
+{
 	/* Enable interrupt on the serial port. */
-	ns8250_port_interrupts_enable(data->port);
+	ns8250_port_interrupts_enable(ns->port);
 	
 	return EOK;
@@ -618,10 +612,9 @@
  * Set the default parameters of the serial communication.
  *
- * @param dev		The serial port device.
- */
-static void ns8250_initialize_port(device_t *dev)
-{
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *)dev->driver_data;
-	ioport8_t *port = data->port;
+ * @param ns		Serial port device
+ */
+static void ns8250_initialize_port(ns8250_t *ns)
+{
+	ioport8_t *port = ns->port;
 	
 	/* Disable interrupts. */
@@ -643,14 +636,13 @@
  * buffer.
  *
- * @param dev		The serial port device.
- */
-static void ns8250_read_from_device(device_t *dev)
-{
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
-	ioport8_t *port = data->port;
+ * @param ns		Serial port device
+ */
+static void ns8250_read_from_device(ns8250_t *ns)
+{
+	ioport8_t *port = ns->port;
 	bool cont = true;
 	
 	while (cont) {
-		fibril_mutex_lock(&data->mutex);
+		fibril_mutex_lock(&ns->mutex);
 		
 		cont = ns8250_received(port);
@@ -658,17 +650,17 @@
 			uint8_t val = ns8250_read_8(port);
 			
-			if (data->client_connected) {
-				if (!buf_push_back(&data->input_buffer, val)) {
+			if (ns->client_connected) {
+				if (!buf_push_back(&ns->input_buffer, val)) {
 					printf(NAME ": buffer overflow on "
-					    "%s.\n", dev->name);
+					    "%s.\n", ns->dev->name);
 				} else {
 					printf(NAME ": the character %c saved "
 					    "to the buffer of %s.\n",
-					    val, dev->name);
+					    val, ns->dev->name);
 				}
 			}
 		}
 		
-		fibril_mutex_unlock(&data->mutex);
+		fibril_mutex_unlock(&ns->mutex);
 		fibril_yield();
 	}
@@ -685,78 +677,87 @@
     ipc_call_t *icall)
 {
-	ns8250_read_from_device(dev);
+	ns8250_read_from_device(NS8250_FROM_DEV(dev));
 }
 
 /** Register the interrupt handler for the device.
  *
+ * @param ns		Serial port device
+ */
+static inline int ns8250_register_interrupt_handler(ns8250_t *ns)
+{
+	return register_interrupt_handler(ns->dev, ns->irq,
+	    ns8250_interrupt_handler, NULL);
+}
+
+/** Unregister the interrupt handler for the device.
+ *
+ * @param ns		Serial port device
+ */
+static inline int ns8250_unregister_interrupt_handler(ns8250_t *ns)
+{
+	return unregister_interrupt_handler(ns->dev, ns->irq);
+}
+
+/** The add_device callback method of the serial port driver.
+ *
+ * Probe and initialize the newly added device.
+ *
  * @param dev		The serial port device.
  */
-static inline int ns8250_register_interrupt_handler(device_t *dev)
-{
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
-	
-	return register_interrupt_handler(dev, data->irq,
-	    ns8250_interrupt_handler, NULL);
-}
-
-/** Unregister the interrupt handler for the device.
- *
- * @param dev		The serial port device.
- */
-static inline int ns8250_unregister_interrupt_handler(device_t *dev)
-{
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
-	
-	return unregister_interrupt_handler(dev, data->irq);
-}
-
-/** The add_device callback method of the serial port driver.
- *
- * Probe and initialize the newly added device.
- *
- * @param dev		The serial port device.
- */
 static int ns8250_add_device(device_t *dev)
 {
-	function_t *fun;
-
+	ns8250_t *ns = NULL;
+	function_t *fun = NULL;
+	bool need_cleanup = false;
+	int rc;
+	
 	printf(NAME ": ns8250_add_device %s (handle = %d)\n",
 	    dev->name, (int) dev->handle);
 	
-	int res = ns8250_dev_initialize(dev);
-	if (res != EOK)
-		return res;
-	
-	if (!ns8250_pio_enable(dev)) {
-		ns8250_dev_cleanup(dev);
-		return EADDRNOTAVAIL;
+	/* Allocate soft-state for the device */
+	ns = ns8250_new();
+	if (ns == NULL) {
+		rc = ENOMEM;
+		goto fail;
+	}
+	
+	ns->dev = dev;
+	dev->driver_data = ns;
+	
+	rc = ns8250_dev_initialize(ns);
+	if (rc != EOK)
+		goto fail;
+	
+	need_cleanup = true;
+	
+	if (!ns8250_pio_enable(ns)) {
+		rc = EADDRNOTAVAIL;
+		goto fail;
 	}
 	
 	/* Find out whether the device is present. */
-	if (!ns8250_dev_probe(dev)) {
-		ns8250_dev_cleanup(dev);
-		return ENOENT;
+	if (!ns8250_dev_probe(ns)) {
+		rc = ENOENT;
+		goto fail;
 	}
 	
 	/* Serial port initialization (baud rate etc.). */
-	ns8250_initialize_port(dev);
+	ns8250_initialize_port(ns);
 	
 	/* Register interrupt handler. */
-	if (ns8250_register_interrupt_handler(dev) != EOK) {
+	if (ns8250_register_interrupt_handler(ns) != EOK) {
 		printf(NAME ": failed to register interrupt handler.\n");
-		ns8250_dev_cleanup(dev);
-		return res;
+		rc = EADDRNOTAVAIL;
+		goto fail;
 	}
 	
 	/* Enable interrupt. */
-	res = ns8250_interrupt_enable(dev);
-	if (res != EOK) {
+	rc = ns8250_interrupt_enable(ns);
+	if (rc != EOK) {
 		printf(NAME ": failed to enable the interrupt. Error code = "
-		    "%d.\n", res);
-		ns8250_dev_cleanup(dev);
-		ns8250_unregister_interrupt_handler(dev);
-		return res;
-	}
-
+		    "%d.\n", rc);
+		goto fail;
+	}
+	
 	fun = create_function();
 	fun->ftype = fun_exposed;
@@ -766,4 +767,5 @@
 	fun->ops = &ns8250_dev_ops;
 	register_function(fun, dev);
+	ns->fun = fun;
 	
 	add_function_to_class(fun, "serial");
@@ -773,4 +775,10 @@
 	
 	return EOK;
+fail:
+	if (need_cleanup)
+		ns8250_dev_cleanup(ns);
+	if (ns != NULL)
+		ns8250_delete(ns);
+	return rc;
 }
 
@@ -784,5 +792,5 @@
 static int ns8250_open(function_t *fun)
 {
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) fun->dev->driver_data;
+	ns8250_t *data = (ns8250_t *) fun->dev->driver_data;
 	int res;
 	
@@ -808,5 +816,5 @@
 static void ns8250_close(function_t *fun)
 {
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) fun->dev->driver_data;
+	ns8250_t *data = (ns8250_t *) fun->dev->driver_data;
 	
 	fibril_mutex_lock(&data->mutex);
@@ -833,5 +841,5 @@
     unsigned int *word_length, unsigned int* stop_bits)
 {
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
+	ns8250_t *data = (ns8250_t *) dev->driver_data;
 	ioport8_t *port = data->port;
 	
@@ -864,5 +872,5 @@
 	    stop_bits);
 	
-	ns8250_dev_data_t *data = (ns8250_dev_data_t *) dev->driver_data;
+	ns8250_t *data = (ns8250_t *) dev->driver_data;
 	ioport8_t *port = data->port;
 	int ret;
