Your driver could provide a character device, which provides a stream of event descriptors, one for each event. Personally, I'd use a binary structure similar to various interchange formats, i.e. something like
Code:
struct event {
uint32_t identifier;
uint32_t length;
/* Optional data, if length > 0 */
};
If none of your interrupts have any associated data, and you know you'll need much less than 257 of them ever, then you could just make it a byte stream. An extensible event structure lets you extend the events without confusing existing clients.
When a client application is interested in those interrupts, it opens the device, and starts reading. Using the standard file semantics, O_NONBLOCK flag indicates that a reader wants to receive EWOULDBLOCK/EAGAIN when there is no data; without the flag the reader is blocked. O_ASYNC flag semantics are also useful to implement: whenever an event occurs, the reader is sent a signal (SIGIO by default, settable to eg. a realtime signal using fcntl()).
Note that my viewpoint here is from the userspace application; what is best for userspace applications and userspace programmers, not what is easiest to implement in a kernel module. Still, all of this is described in various tutorials, and is bog-standard stuff anyway; used by practically all sane char device drivers you'll ever see in Linux.
For example, see
Linux Device Drivers, 3rd edition.
Chapter 3 describes the basics of a char driver,
Chapter 6 describes how to block and make the userspace application sleep (O_NONBLOCK) or signal asynchronous readers (O_ASYNC). Signal details are explained near the end of
chapter 5.