def add_interrupt_callback(self, gpio_id, callback, edge='both',
            pull_up_down=_GPIO.PUD_OFF, threaded_callback=False,
            debounce_timeout_ms=None):
        """
        Add a callback to be executed when the value on 'gpio_id' changes to
        the edge specified via the 'edge' parameter (default='both').

        `pull_up_down` can be set to `RPIO.PUD_UP`, `RPIO.PUD_DOWN`, and
        `RPIO.PUD_OFF`.

        If `threaded_callback` is True, the callback will be started
        inside a Thread.
        """
        gpio_id = _GPIO.channel_to_gpio(gpio_id)
        debug("Adding callback for GPIO %s" % gpio_id)
        if not edge in ["falling", "rising", "both", "none"]:
            raise AttributeError("'%s' is not a valid edge." % edge)

        if not pull_up_down in [_GPIO.PUD_UP, _GPIO.PUD_DOWN, _GPIO.PUD_OFF]:
            raise AttributeError("'%s' is not a valid pull_up_down." % edge)

        # Make sure the gpio_id is valid
        if not gpio_id in set(chain(RPIO.GPIO_LIST_R1, RPIO.GPIO_LIST_R2, \
                              RPIO.GPIO_LIST_R3)):
            raise AttributeError("GPIO %s is not a valid gpio-id." % gpio_id)

        # Require INPUT pin setup; and set the correct PULL_UPDN
        if RPIO.gpio_function(int(gpio_id)) == RPIO.IN:
            RPIO.set_pullupdn(gpio_id, pull_up_down)
        else:
            debug("- changing gpio function from %s to INPUT" % \
                    (GPIO_FUNCTIONS[RPIO.gpio_function(int(gpio_id))]))
            RPIO.setup(gpio_id, RPIO.IN, pull_up_down)

        # Prepare the callback (wrap in Thread if needed)
        cb = callback if not threaded_callback else \
                partial(_threaded_callback, callback)

        # Prepare the /sys/class path of this gpio
        path_gpio = "%sgpio%s/" % (_SYS_GPIO_ROOT, gpio_id)

        # If initial callback for this GPIO then set everything up. Else make
        # sure the edge detection is the same.
        if gpio_id in self._map_gpioid_to_callbacks:
            with open(path_gpio + "edge", "r") as f:
                e = f.read().strip()
                if e != edge:
                    raise AttributeError(("Cannot add callback for gpio %s:"
                            " edge detection '%s' not compatible with existing"
                            " edge detection '%s'.") % (gpio_id, edge, e))

            # Check whether edge is the same, else throw Exception
            debug("- kernel interface already setup for GPIO %s" % gpio_id)
            self._map_gpioid_to_callbacks[gpio_id].append(cb)

        else:
            # If kernel interface already exists unexport first for clean setup
            if os.path.exists(path_gpio):
                if self._show_warnings:
                    warn("Kernel interface for GPIO %s already exists." % \
                            gpio_id)
                debug("- unexporting kernel interface for GPIO %s" % gpio_id)
                with open(_SYS_GPIO_ROOT + "unexport", "w") as f:
                    f.write("%s" % gpio_id)
                time.sleep(0.1)

            # Export kernel interface /sys/class/gpio/gpioN
            with open(_SYS_GPIO_ROOT + "export", "w") as f:
                f.write("%s" % gpio_id)
            self._gpio_kernel_interfaces_created.append(gpio_id)
            debug("- kernel interface exported for GPIO %s" % gpio_id)

            # Configure gpio as input
            with open(path_gpio + "direction", "w") as f:
                f.write("in")

            # Configure gpio edge detection
            with open(path_gpio + "edge", "w") as f:
                f.write(edge)

            debug(("- kernel interface configured for GPIO %s "
                    "(edge='%s', pullupdn=%s)") % (gpio_id, edge, \
                    _PULL_UPDN[pull_up_down]))

            # Open the gpio value stream and read the initial value
            f = open(path_gpio + "value", 'r')
            val_initial = f.read().strip()
            debug("- inital gpio value: %s" % val_initial)
            f.seek(0)

            # Add callback info to the mapping dictionaries
            self._map_fileno_to_file[f.fileno()] = f
            self._map_fileno_to_gpioid[f.fileno()] = gpio_id
            self._map_fileno_to_options[f.fileno()] = {
                    "debounce_timeout_s": debounce_timeout_ms / 1000.0 if \
                            debounce_timeout_ms else 0,
                    "interrupt_last": 0,
                    "edge": edge
                    }
            self._map_gpioid_to_fileno[gpio_id] = f.fileno()
            self._map_gpioid_to_callbacks[gpio_id] = [cb]

            # Add to epoll
            self._epoll.register(f.fileno(), select.EPOLLPRI | select.EPOLLERR)