Reading GPIO internal registers using sysfs - embedded-linux

I am using a PCA9544 GPIO expander in embedded Linux. The driver is installed and controlling GPIO as expected. However, I would like to read the values of the INTn lines through the control register using sysfs. Is there a file associated with the control register?

If you export single GPIOs, you can wait for its interrupt on per GPIO basis. Take a look at libsoc project. It handles sysfs calls for you. Look at libsoc_gpio_wait_interrupt and libsoc_gpio_callback_interrupt routines. All you have to do it to activate required GPIOs, configure them as input and specify trigger edge (see options below), after this you can use interrupt routines. I've also an i2c based GPIO expander and it works.
typedef enum {
EDGE_ERROR = -1,
RISING = 0,
FALLING = 1,
NONE = 2,
BOTH = 3,
} gpio_edge;

Related

How to define a relay in a device tree

I have a board, SoC running Linux 5+, with a electrical relay. The relay is triggered by a GPIO. I am looking for a good way to define a relay in a device tree file.
I define LEDs as
led {
compatible = "gpio-leds";
debug {
label = "debug";
gpios = ...
default-state = "off";
};
};
This results in
# ls /sys/class/leds/
debug
I would like to have the relay be something similar such as
# ls /sys/class/{relays,outputs,gpios}/
relay1
What is a good way to achieve this?
Since relay behaves as simply as GPIO output (or more precisely GPO), what you need is just name the corresponding line. It can be done by assigning gpio-line-names property of the GPIO controller in the ACPI or Device Tree. With a use of libgpiod tools (such as gpiofind, gpioinfo), that access GPIO controller via character device node, you may find your line and do the operations on it. Note, GPIO sysfs interface is deprecated and it will be removed from the kernel on a horizon of ~5 years or so.

How to use kernel GPIO descriptor interface

I'm trying to develop a simple Linux kernel module that manages a bunch of sensors/actuators pinned on the GPIO of a Raspberry Pi.
The GPIO functionalities I need are quite simple: get/set pin values, receive IRQs, ...
In my code, I have a misc_device which implements the usual open, read, write and open operations. In my read operation, for instance, I'd like to get the value (high/low) of a specific GPIO pin.
Luckily, the kernel provides an interface for such GPIO operations. Actually, there are two interfaces, according to the official GPIO doc: the legacy one, which is extremely simple yet deprecated, and the new descriptor-based one.
I'd like to use the latter for my project, and I understand how to implement all I need except for one thing: the device tree stuff.
With reference to board.txt, before I can call gpiod_get_index() and later gpiod_get_value(), first I need to setup the device tree somehow like this:
foo_device {
compatible = "acme,foo";
...
led-gpios = <&gpio 15 GPIO_ACTIVE_HIGH>, /* red */
<&gpio 16 GPIO_ACTIVE_HIGH>, /* green */
<&gpio 17 GPIO_ACTIVE_HIGH>; /* blue */
power-gpios = <&gpio 1 GPIO_ACTIVE_LOW>;
};
However, I've absolutely no clue where to put that chunk of code, nor if I really need it. Mind that I have a misc device which looks like this, where aaa_fops contains the read operation:
static struct miscdevice aaa = {
MISC_DYNAMIC_MINOR, "aaa", &aaa_fops
};
Using the old deprecated interface, my problem would be solved because it doesn't require to mess with the device tree, but I'd still like to use the new one if not too complex.
I've read a bunch of documentation, both official and unofficial, but couldn't find a straight and simple answer to my issue. I tried to find an answer in the kernel source code, especially in the drivers section, but only got lost in a valley of complex and messy stuff.
The lack of working, minimal examples (WME) about kernel is significantly slowing down my learning process, just my opinion about it.
Could you please give me a WME of a simple device (preferably a misc) whose read() operation gets the value of a pin, using the new GPIO interface?
If you need more details about my code, just ask. Thanks in advance!
Note 1: I'm aware that most of my work could be done in userspace rather than kernelspace; my project is for educational purposes only, to learn the kernel.
Note 2: I choose a misc device because it's simple, but I can switch to a char device if needed.
... first I need to setup the device tree somehow like this:
...
However, I've absolutely no clue where to put that chunk of code
Device Tree nodes and properties should not be called "code".
Most devices are connected to a peripheral bus, so device nodes typically are child nodes of the peripheral bus node.
Could you please give me a WME of a simple device
You can find numerous examples of descriptor-based GPIO usage in the kernel source.
Since the documentation specifies the GPIO descriptor as a property named
<function>-gpios, a grep of the directory arch/arm/boot/dts for the string "\-gpios" reports many possible examples.
In particular there's
./bcm2835-rpi-b.dts: hpd-gpios = <&gpio 46 GPIO_ACTIVE_HIGH>;
This hpd-gpios property belongs to the hdmi base-node defined in bcm283x.dtsi, and is used by the gpu/drm/vc4/vc4_hdmi.c driver.
/* General HDMI hardware state. */
struct vc4_hdmi {
...
int hpd_gpio;
...
};
static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
{
...
/* Only use the GPIO HPD pin if present in the DT, otherwise
* we'll use the HDMI core's register.
*/
if (of_find_property(dev->of_node, "hpd-gpios", &value)) {
...
hdmi->hpd_gpio = of_get_named_gpio_flags(dev->of_node,
"hpd-gpios", 0,
&hpd_gpio_flags);
if (hdmi->hpd_gpio < 0) {
ret = hdmi->hpd_gpio;
goto err_unprepare_hsm;
}
...
}
If the hpd-gpios property is defined/found and successfully retrieved from the board's DeviceTree, then the driver's structure member hpd_gpio holds the GPIO pin number.
Since this driver does not call devm_gpio_request(), the framework apparently allocates the GPIO pin for the driver.
The driver can then access the GPIO pin.
static enum drm_connector_status
vc4_hdmi_connector_detect(struct drm_connector *connector, bool force)
{
...
if (vc4->hdmi->hpd_gpio) {
if (gpio_get_value_cansleep(vc4->hdmi->hpd_gpio) ^
vc4->hdmi->hpd_active_low)
return connector_status_connected;
else
return connector_status_disconnected;
}

Interpretation of gpio: in fixed-regulator device tree entry?

I'm trying to control (on/off) a voltage regulator that is mapped to a GPIO pin and powers an external device.
The device tree for the regulator has the following entry:
reg_usb1_vbus: usb1_vbus {
compatible = "regulator-fixed";
regulator-name = "usb1_vbus";
regulator-min-microvolt = <5000000>;
regulator-max-microvolt = <5000000>;
gpio = <&gpio3 28 0>;
enable-active-high;
};
As I read the documentation i got confused for it states:
Optional properties:
gpio: gpio to use for enable control
However, I can't export the sysfs interface of that GPIO and use it to control the power supply (just on/off) for the external device. In addition if I comment out the gpio = <&gpio3 28 0>; from the device tree, the external device gets no power (when it isn't commented the device is always powered).
The regulator has a sysfs interface exported:
80090000.usb-vbus power suspend_standby_state
device state type
microvolts subsystem uevent
name suspend_disk_state
num_users suspend_mem_state
however I can't write to any of the files.
What is the correct way to interpret the gpio: entry?
gpio to use for enable control
In which case I'm missing a mapping between a pin on which I want to have the regulator voltage.
gpio which will have the voltage from the regulator to power some external unit
In which case I'm missing a way to turn it on and off
I'm trying to control (on/off) a voltage regulator that is mapped to a GPIO pin and powers an external device.
...
What is the correct way to interpret the gpio: entry?
Seems like you're asking an XY question.
First the Y part regarding the GPIO.
The gpio DT entry you refer to would be for an enable/disable control by the regulator framework. It is intended for exclusive use by the regulator driver to control the (external?) regulator hardware. It is not intended for software control of the regulator outside the framework by the user (as you are trying to do).
This GPIO is defined as an output in drivers/regulator/core.c:
static int regulator_ena_gpio_request(struct regulator_dev *rdev,
const struct regulator_config *config)
{
...
ret = gpio_request_one(config->ena_gpio,
GPIOF_DIR_OUT | config->ena_gpio_flags,
rdev_get_name(rdev));
...
}
The GPIO pin is not read for "enable control", but has its value set in
regulator_ena_gpio_ctrl() in order to actively enable or disable the (external) regulator.
The inablity to export the same GPIO pin using sysfs when that pin is also declared in the Device Tree is easily explained. Once the driver acquires the specified GPIO for its use (through the DT), it is no longer unused, and you cannot export that GPIO through sysfs anymore.
GPIOs are a managed resource, and need to be allocated and freed (by a driver or sysfs) just like any other resource such as memory. If you were able to export this GPIO that was also used by the driver, then you would be able to put the GPIO into a state that was inconsistent with what the driver was doing. That in turn would lead to an unstable or misbehaving code.
In which case I'm missing a mapping between a pin on which I want to have the regulator voltage.
The GPIO pin specified in the Device Tree is a logic (i.e. digital) output. It is not the regulator output, which would be an analog output.
You should consult the schematic for your board to confirm that this GPIO is connected to a control input of a regulator.
As to the X part regarding enabling/disabling the regulator:
Software control of the regulator's output is documented in Documentation/power/regulator/consumer.txt
A consumer driver can get access to its supply regulator by calling :-
regulator = regulator_get(dev, "Vcc");
A consumer can enable its power supply by calling:-
int regulator_enable(regulator);
A consumer can disable its supply when no longer needed by calling :-
int regulator_disable(regulator);
The 'consumer" is an electronic device that is supplied power by a regulator.
Apparently the intended framework is have the "consumer driver" own and control its regulator, and not allow an external interface (e.g. sysfs) to interfere with this "consumer driver". If you insist on having userland control, then you could implement an ioctl() or sysfs interface to the "consumer driver" (to avoid conflict/contention with the regulator driver).
In which case I'm missing a way to turn it on and off
What you're really looking for seems to be (upper-layer) power management, and that has its own framework, of which regulators is a lower layer (which is normally not accessible for user control). You should study Documentation/driver-api/pm/devices.txt.
I am not extremely familiar with the regulator core in the kernel, but it seems to me that the regulator interface needs to give you access to the GPIO in a different way than the standad export GPIO method.
I have not looked into this, but it is possible the the regulator interface opens up a character device to userspace for control over the regulator. (Don't hold me to that)
I do see in the documentation and in the driver source code drivers/regulator/fixed.c that the GPIO is not a required DT attribute. You might be able to leave it out of the DT in which case the Driver will never acquire your GPIO, then you can manually control it through the standard export GPIO interface.

What's the relationship between GPIO and SPI?

I found GPIO driver in the kernel leave /sys/class/gpio to control gpio, but I found GPIO can be controlled by /dev/mem as well, I found this mapping may be done in the spi-bcm2708 (which call the __ioremap as a platform driver), but I don't understand the relationship between spi and GPIO,how they work together in the linux?
As I understand you are talking about this driver (which is used, for example, in Raspberry Pi). First of all, take a look at BCM2835 datasheet. Review next sections:
1.1 Overview
6.0 General Purpose I/O (GPIO)
6.2 Alternative Functions Assignments (see Table 6-31)
From driver code (see bcm2708_init_pinmode() function) and datasheet (table 6-31), we can see that SPI pins are actually GPIO7..11 pins. Those pins can be actually connected to different hardware modules (either SPI or SD, in this case).
Such a selection is done using pin muxing. So basically you need to connect GPIO7..GPIO11 pins to SPI module. To do so you need to select ALT0 function for each of GPIO7..GPIO11 pins. This can be done by writing corresponding values to GPFSEL0 and GPFSEL1 registers (see tables 6-1..6-3 in datasheet):
And this is how the driver is actually doing this:
/*
* This function sets the ALT mode on the SPI pins so that we can use them with
* the SPI hardware.
*
* FIXME: This is a hack. Use pinmux / pinctrl.
*/
static void bcm2708_init_pinmode(void)
{
#define INP_GPIO(g) *(gpio+((g)/10)) &= ~(7<<(((g)%10)*3))
#define SET_GPIO_ALT(g, a) *(gpio+(((g)/10))) |= (((a) <= 3 ? (a)+4 : (a) == 4 ? 3 : 2)<<(((g)%10)*3))
int pin;
u32 *gpio = ioremap(GPIO_BASE, SZ_16K);
/* SPI is on GPIO 7..11 */
for (pin = 7; pin <= 11; pin++) {
INP_GPIO(pin); /* set mode to GPIO input first */
SET_GPIO_ALT(pin, 0); /* set mode to ALT 0 */
}
iounmap(gpio);
#undef INP_GPIO
#undef SET_GPIO_ALT
}
which looks like quick hack to me, and they actually mentioned it: the correct way would be to use kernel mechanism called pinctrl.
Conclusion: BCM2708 driver doesn't actually trigger any GPIO pins, it's just doing pin muxing in order to connect GPIO7..GPIO11 pins to SPI module. And to do so this driver writes to GPFSELn registers, which happen to be GPIO registers. This is pretty much all relationship between SPI and GPIO in this driver.
P.S.: if you are curious about possible relationship between SPI and GPIO, read about bit banging. See for example spi-bitbang.c driver in Linux kernel.

Replace HW interrupt in flat memory mode with DOS32/A

I have a question about how to replace HW interrupt in flat memory mode...
about my application...
created by combining Watcom C and DOS32/A.
written for running on DOS mode( not on OS mode )
with DOS32/A now I can access >1M memory and allocate large memory to use...(running in flat memory mode !!!)
current issue...
I want to write an ISR(interrupt service routine) for one PCI card. Thus I need to "replace" the HW interrupt.
Ex. the PCI card's interrupt line = 0xE in DOS. That means this device will issue interrupt via 8259's IRQ 14.
But I did not how to achieve my goal to replace this interrupt in flat mode ?
# resource I found...
- in watcom C's library, there is one sample using _dos_getvect, _dos_setvect, and _chain_intr to hook INT 0x1C...
I tested this code and found OK. But when I apply it to my case: INT76 ( where IRQ 14 is "INT 0x76" <- (14-8) + 0x70 ) then nothing happened...
I checked HW interrupt is generated but my own ISR did not invoked...
Do I lose something ? or are there any functions I can use to achieve my goal ?
===============================================================
[20120809]
I tried to use DPMI calls 0x204 and 0x205 and found MyISR() is still not invoked. I described what I did as below and maybe you all can give me some suggestions !
1) Use inline assembly to implement DPMI calls 0x204 and 0x205 and test OK...
Ex. Use DPMI 0x204 to show the interrupt vectors of 16 IRQs and I get(selector:offset) following results: 8:1540(INT8),8:1544(INT9),.....,8:1560(INT70),8:1564(INT71),...,8:157C(INT77)
Ex. Use DPMI 0x205 to set the interrupt vector for IRQ14(INT76) and returned CF=0, indicating successful
2) Create my own ISR MyISR() as follows:
volatile int tick=0; // global and volatile...
void MyISR(void)
{
tick = 5; // simple code to change the value of tick...
}
3) Set new interrupt vector by DPMI call 0x205:
selector = FP_SEG(MyISR); // selector = 0x838 here
offset = FP_OFF(MyISR); // offset = 0x30100963 here
sts = DPMI_SetIntVector(0x76, selector, offset, &out_ax);
Then sts = 0(CF=0) indicating successful !
One strange thing here is:my app runs in flat memory model and I think the selector should be 0 for MyISR()... But if selector = 0 for DPMI call 0x205 then I got CF=1 and AX = 0x8022, indicating "invalid selector" !
4) Let HW interrupt be generated and the evidences are:
PCI device config register 0x5 bit2(Interrupt Disabled) = 0
PCI device config register 0x6 bit3(Interrupt status) = 1
PCI device config register 0x3C/0x3D (Interrupt line) = 0xE/0x2
In DOS the interrupt mode is PIC mode(8259 mode) and Pin-based(MSIE=0)
5) Display the value of tick and found it is still "0"...
Thus I think MyISR() is not invoked correctly...
Try using DPMI Function 0204h and 0205h instead of '_dos_getvect' and '_dos_setvect', respectively.
The runtime environment of your program is DOS32A or a DPMI Server/host. So use the api they provided instead of using DOS int21h facilities. But DOS32A does intercepts int21h interrupts, so your code should work fine, as far as real mode is concerned.
Actually what you did is you install only real mode interrupt handler for IRQ14 by using '_dos_getvect' and '_dos_setvect' functions.
By using the DPMI functions instead, you install protected mode interrupt handler for IRQ14, and DOS32a will autopassup IRQ14 interrupt to this protected mode handler.
Recall: A dos extender/DPMI server can be in protected mode or real mode while an IRQ is asserted.
This is becoz your application uses some DOS or BIOS API, so extender needs to switch to real mode to execute them and the return back to protected mode to transfer control to you protected mode application.
DOS32a does this by allocating a real-mode callback (at least for hardware interrupts) which calls your protected mode handler if IRQ14 is asserted while the Extender is in real-mode.
If the extender is in protected mode, while IRQ14 is asserted, it will automatically transfer control to your IRQ14 handler.
But if you didn't install protected mode handler for your IRQ, then DOS32a, will not allocate any real-mode callback, and your real-mode irq handler may not get control.
But it should recieve control AFAIK.
Anyway give the above two functions a try. And do chain to the previous int76h interrupt handler as Sean said.
In short:
In case of DOS32a, you need not use '_dos_getvect' and '_dos_setvect' functions. Instead use the DPMI functions 0204h and 0205h for installing your protected mode IRQ handler.
An advise : In your interrupt handler the first step should be to check whether your device actually generated interrupt or it is some other device sharing this irq(IRQ14 in your case). You can do this by checking a 'interrupt pending bit' in your device, if it is set, service your device and chain to next handler. If it is not set to 1, simply chain to next handler.
EDITED:
Use the latest version of DOS32a, instead of one that comes with OW.
Update on 2012-08-14:
Yes, you can use FP_SEG and FP_OFF macros for obtaining selector and offset respectively, just like you would use these macros in real modes to get segment and offset.
You can also use MK_FP macro to create far pointers from selector and offset. eg.
MK_FP(selector, offset).
You should declare your interrupt handler with ' __interrupt ', keyword when writing handlers in C.
Here is a snippet:
#include <i86.h> /* for FP_OFF, FP_SEG, and MK_FP in OW */
/* C Prototype for your IRQ handler */
void __interrupt __far irqHandler(void);
.
.
.
irq_selector = (unsigned short)FP_SEG( &irqHandler );
irq_offset = (unsigned long)FP_OFF( &irqHandler );
__dpmi_SetVect( intNum, irq_selector, irq_offset );
.
.
.
or, try this:
extern void sendEOItoMaster(void);
# pragma aux sendEOItoMaster = \
"mov al, 0x20" \
"out 0x20, al" \
modify [eax] ;
extern void sendEOItoSlave(void);
# pragma aux sendEOItoSlave = \
"mov al, 0x20" \
"out 0xA0, al" \
modify [eax] ;
unsigned int old76_selector, new76_selector;
unsigned long old76_offset, new76_offset;
volatile int chain = 1; /* Chain to the old handler */
volatile int tick=0; // global and volatile...
void (__interrupt __far *old76Handler)(void) = NULL; // function pointer declaration
void __interrupt __far new76Handler(void) {
tick = 5; // simple code to change the value of tick...
.
.
.
if( chain ){
// disable irqs if enabled above.
_chain_intr( old76Handler ); // 'jumping' to the old handler
// ( *old76Handler )(); // 'calling' the old handler
}else{
sendEOItoMaster();
sendEOItoSlave();
}
}
__dpmi_GetVect( 0x76, &old76_selector, &old76_offset );
old76Handler = ( void (__interrupt __far *)(void) ) MK_FP (old76_selector, old76_offset)
new76_selector = (unsigned int)FP_SEG( &new76Handler );
new76_offset = (unsigned long)FP_OFF( &new76Handler );
__dpmi_SetVect( 0x76, new76_selector, new76_offset );
.
.
NOTE:
You should first double check that the IRQ# you are hooking is really assigned/mapped to the interrupt pin of your concerned PCI device. IOWs, first read 'Interrupt Line register' (NOT Interrupt Pin register) from PCI configuration space, and hook only that irq#. The valid values for this register, in your case are: 0x00 through 0x0F inclusive, with 0x00 means IRQ0 and 0x01 means IRQ1 and so on.
POST/BIOS code writes a value in 'Interrupt Line register', while booting, and you MUST NOT modify this register at any cost.(of course, unless you are dealing with interrupt routing issues which an OS writer will deal with)
You should also get and save the selector and offset of the old handler by using DPMI call 0204h, in case you are chaining to old handler. If not, don't forget to send EOI(End-of-interrupt) to BOTH master and slave PICs in case you hooked an IRQ belonging to slave PIC(ie INT 70h through 77h, including INT 0Ah), and ONLY to master PIC in case you hooked an IRQ belonging to master PIC.
In flat model, the BASE address is 0 and Limit is 0xFFFFF, with G bit(ie Granularity bit) = 1.
The base and limit(along with attribute bits(e.g G bit) of a segment) reside in the descriptor corresponding to a particular segment. The descriptor itself, sits in the descriptor table.
Descriptor tables are an array with each entry being 8bytes.
The selector is merely a pointer(or an index) to the 8-byte descriptor entry, in the Descriptor table(either GDT or LDT). So a selector CAN'T be 0.
Note that lowest 3 bits of 16-bit selector have special meaning, and only the upper 13-bits are used to index a descriptor entry from a descriptor table.
GDT = Global Descriptor Table
LDT = Local Descriptor Table
A system can have only one GDT, but many LDTs.
As entry number 0 in GDT, is reserved and can't be used. AFAIK, DOS32A, does not create any LDT for its applications, instead it simply allocate and initalize descriptor entries corresponding to the application, in GDT itself.
Selector MUST not be 0, as x86 architecture regards 0 selector as invalid, when you try to access memory with that selector; though you can successfully place 0 in any segment register, it is only when you try to access(read/write/execute) that segment, the cpu generates an exception.
In case of interrupt handlers, the base address need not be 0, even in case of flat mode.
The DPMI environment must have valid reasons for doing this so.
After all, you still need to tackle segmentation at some level in x86 architecture.
PCI device config register 0x5 bit2(Interrupt Disabled) = 0
PCI device config register 0x6 bit3(Interrupt status) = 1
I think, you mean Bus master command and status registers respectively. They actually reside in either I/O space or memory space, but NOT in PCI configuration space.
So you can read/write them directly via IN/OUT or MOV, instructions.
For reading/writing, PCI configuration registers you must use configuration red/write methods or PCI BIOS routines.
NOTE:
Many PCI disk controllers, have a bit called 'Interrupt enable/disable' bit. The register
that contains this bit is usually in the PCI configuration space, and can be found from the datasheet.
Actually, this setting is for "forwarding" the interrupt generated by the device attached to the PCI controller, to the PCI bus.
If, interrupts are disabled via this bit, then even if your device(attached to PCI controller) is generating the interrupt, the interrupt will NOT be forwarded to the PCI bus(and hence cpu will never know if interrupt occurred), but the interrupt bit(This bit is different from 'Interrupt enable/disable' bit) in PCI controller is still set to notify that the device(attached to PCI controller, eg a hard disk) generated an interrupt, so that the program can read this bit and take appropriate actions. It is similar to polling, from programming perspective.
This usually apply only for non-bus master transfers.
But, it seems that you are using bus master transfers(ie DMA), so it should not apply in your case.
But anyway, I would suggest you do read the datasheet of the PCI controller carefully, especially looking for bits/registers related to interrupt handling
EDITED:
Well, as far as application level programming is concerned, you need not encounter/use _far pointers, as your program will not access anything outside to your code.
But this is not completely true, when you go to system-level programming, you need to access memory mapped device registers, external ROM, or implementing interrupt handlers, etc.
The story changes here. The creation of a segment ie allocating descriptor and getting its associated selector, ensures that even if there is a bug in code, it will not annoyingly change anything external to that particular segment from which current code is executing. If it tries to do so, cpu will generate a fault. So when accessing external devices(especially memory mapped device's registers), or accessing some rom data, eg BIOS etc., it is a good idea to have allocate a descriptor and set the base and segment limits according to the area you need to execute/read/write and proceed. But you are not bound to do so.
Some external code residing for eg in rom, assume that they will be invoked with a far call.
As I said earlier, in x86 architecture, at some level(the farther below you go) you need to deal with segmentation as there is no way to disable it completely.
But in flat model, segmentation is present as an aid to programmer, as I said above, when accessing external(wrt to your program) things. But you need not use if you don't desire to do so.
When an interrupt handler is invoked, it doesn't know the base and limits of program that was interrupted. It doesn't know the segment attributes, limits etc. of the interrupted program, we say except CS and EIP all registers are in undefined state wrt interrupt handler. So it is needed to be declared as far function to indicate that it resides somewhere external to currently executing program.
it's been a while since I fiddled with interrupts, but the table is a pointer to set where the processor should go to to process an interrupt. I can give you the process, but not code, as I only ever used 8086 code.
Pseudo code:
Initialize:
Get current vector - store value
Set vector to point to the entry point of your routine
next:
Process Interrupt:
Your code decides what to do with data
If it's your data:
process it, and return
If not:
jump to the stored vector that we got during initialize,
and let the chain of interrupts continue as they normally would
finally:
Program End:
check to see if interrupt still points to your code
if yes, set vector back to the saved value
if no, set beginning of your code to long jump to vector address you saved,
or set a flag that lets your program not process anything

Resources