/* * $Header: /home/steve/uio48/RCS/uio48.c,v 1.1 2005/04/22 20:10:58 steve Exp $ * $Id: uio48.c,v 1.1 2005/04/22 20:10:58 steve Exp $ * * Filename : $RCSfile: uio48.c,v $ * * $Log: uio48.c,v $ * Revision 1.1 2005/04/22 20:10:58 steve * Initial revision * * * * WinSystems UIO48 Linux Device Driver */ static char *RCSInfo = "$Id: uio48.c,v 1.1 2005/04/22 20:10:58 steve Exp $"; /* Portions of original code Copyright (C) 1998-99 by Ori Pomerantz */ /* Standard in kernel modules */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include #include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); //#define DEBUG 1 /* Our own ioctl numbers */ #include "uio48.h" #define SUCCESS 0 /* Function prototypes for local functions */ void init_io(int chip_number, unsigned io_address); int read_bit(int chip_number, int bit_number); void write_bit(int chip_number, int bit_number, int val); void UIO48_set_bit(int chip_num, int bit_num); void clr_bit(int chip_num, int bit_num); void enab_int(int chip_number, int bit_number, int polarity); void disab_int(int chip_number, int bit_number); void clr_int(int chip_number, int bit_number); int get_int(int chip_number); int get_buffered_int(int chip_number); /* Interrupt handlers */ irqreturn_t handler_1(int, void *, struct pt_regs *); irqreturn_t handler_2(int, void *, struct pt_regs *); irqreturn_t handler_3(int, void *, struct pt_regs *); irqreturn_t handler_4(int, void *, struct pt_regs *); irqreturn_t handler_5(int, void *, struct pt_regs *); irqreturn_t handler_6(int, void *, struct pt_regs *); void common_handler(int); /* ******************* Device Declarations ***************************** */ #define MAX_INTS 1024 /* This holds the base addresses of the IO chips */ unsigned base_port[MAX_CHIPS] = {0,0,0,0,0,0}; /* The name for our device, as it will appear in * /proc/devices */ #define MAJOR_NUM 110 #define DEVICE_NAME "uio48" /* The maximum length of the message for the device */ #define BUF_LEN 20 /* The message the device will give when asked */ static char Message[BUF_LEN]; /* A separate pointer for each device */ int Message_Ptr[MAX_CHIPS] = {0,0,0,0,0,0}; /* Our insmod command line arguments */ MODULE_PARM(io,"1-6i"); MODULE_PARM(irq,"1-6i"); static unsigned io[MAX_CHIPS] = {0,0,0,0,0,0}; static unsigned irq[MAX_CHIPS] = {0,0,0,0,0,0}; unsigned int_count[MAX_CHIPS] = {0,0,0,0,0,0}; /* We will buffer up the transition interrupts and will pass them on to waiting applications */ unsigned char int_buffer[MAX_CHIPS][MAX_INTS]; int inptr[MAX_CHIPS] = {0,0,0,0,0,0}; int outptr[MAX_CHIPS] = {0,0,0,0,0,0}; /* These declarations create the wait queues. One for each supported device. */ DECLARE_WAIT_QUEUE_HEAD(wq_1); DECLARE_WAIT_QUEUE_HEAD(wq_2); DECLARE_WAIT_QUEUE_HEAD(wq_3); DECLARE_WAIT_QUEUE_HEAD(wq_4); DECLARE_WAIT_QUEUE_HEAD(wq_5); DECLARE_WAIT_QUEUE_HEAD(wq_6); /* This is the common interrupt handler. It is called by the chip specific handlers with the device number as an argument. */ void common_handler(int device) { int my_interrupt; int x; int c; int count; my_interrupt = irq[device]; while(1) { count = 0; for(x = 0; x < MAX_CHIPS; x++) { if(irq[x] == my_interrupt) { c = get_int(x + 1); if(c != 0) { clr_int(x+1,c); count++; #ifdef DEBUG printk("<1> Interrupt on chip %d, bit %d\n",x,c); #endif int_buffer[x][inptr[x]++] = c; if(inptr[x] == MAX_INTS) inptr[x] = 0; switch(x) { case 0: wake_up_interruptible(&wq_1); break; case 1: wake_up_interruptible(&wq_2); break; case 2: wake_up_interruptible(&wq_3); break; case 3: wake_up_interruptible(&wq_4); break; case 4: wake_up_interruptible(&wq_5); break; case 5: wake_up_interruptible(&wq_6); break; } } } } if(count) continue; for(x = 0; x < MAX_CHIPS; x++) { if(irq[x] == my_interrupt) { c = get_int(x + 1); if(c != 0) { clr_int(x+1,c); count++; #ifdef DEBUG printk("<1> Interrupt on chip %d, bit %d\n",x,c); #endif int_buffer[x][inptr[x]++] = c; if(inptr[x] == MAX_INTS) inptr[x] = 0; switch(x) { case 0: wake_up_interruptible(&wq_1); break; case 1: wake_up_interruptible(&wq_2); break; case 2: wake_up_interruptible(&wq_3); break; case 3: wake_up_interruptible(&wq_4); break; case 4: wake_up_interruptible(&wq_5); break; case 5: wake_up_interruptible(&wq_6); break; } } } } if(count) continue; break; } } /* We have 6 separate interrupt handlers. One for each supported device */ /* ============================= Handler 1 ============================ */ irqreturn_t handler_1(int pirq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received Chip 1\n"); #endif common_handler(0); return IRQ_HANDLED; } //===========================Handler 2 ================================ irqreturn_t handler_2(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received Chip 2\n"); #endif common_handler(1); return IRQ_HANDLED; } //========================== Handler 3 ====================== irqreturn_t handler_3(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received chip 3\n"); #endif common_handler(2); return IRQ_HANDLED; } //===========================Handler 4 ============================ irqreturn_t handler_4(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received Chip 4\n"); #endif common_handler(3); return IRQ_HANDLED; } //===========================Handler 5 ============================ irqreturn_t handler_5(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received Chip 5\n"); #endif common_handler(4); return IRQ_HANDLED; } //===========================Handler 6 ============================ irqreturn_t handler_6(int irq, void *dev_id, struct pt_regs *regs) { #ifdef DEBUG printk("<1>UIO48 Interrupt received Chip 6\n"); #endif common_handler(5); return IRQ_HANDLED; } /*********************************************************************** * * * DEVICE OPEN * * ************************************************************************ */ /* This function is called whenever a process attempts * to open the device file */ static int device_open(struct inode *inode, struct file *file) { unsigned int minor = MINOR(inode->i_rdev); if(base_port[minor] == 0) { printk("<1>**** OPEN ATTEMPT on uninitialized port *****\n"); return -1; } #ifdef DEBUG printk ("device_open(%p)\n", file); #endif /* Rewind the message pointer for this device on a new open. This could cause problems for applications that use file I/O for talking to the device as we do not limit the number of applications that can open the device. The solution is to use IOCTL functions to control the device rather than file I/O calls. */ Message_Ptr[minor] = 0; return SUCCESS; } /************************************************************************* * * * DEVICE CLOSE * * ************************************************************************** */ int device_release(struct inode *inode, struct file *file) { #ifdef DEBUG printk ("device_release(%p,%p)\n", inode, file); #endif return 0; } /**************************************************************************** * * DEVICE READ * * * ***************************************************************************** */ ssize_t device_read(struct file *file,char *buffer,size_t length,loff_t *offset) { unsigned port; int bytes_read; int x; unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); /* We only allow reading of the 10 valid ports in the device. Beyond that we return EOF. */ if(Message_Ptr[minor] == 10) return 0; /* EOF Condition */ port = base_port[minor]; // Do a fresh read of the ports */ for(x=0; x<10; x++) Message[x] = inb(port+x); /* Number of bytes actually written to the buffer */ bytes_read = 0; #ifdef DEBUG printk("device_read(Minor %d,%p,%p,%d)\n", minor,file, buffer, length); #endif /* Actually put the data into the buffer */ while (length && (Message_Ptr[minor] < 10)) { /* Because the buffer is in the user data segment, * not the kernel data segment, assignment wouldn't * work. Instead, we have to use put_user which * copies data from the kernel data segment to the * user data segment. */ put_user((Message[Message_Ptr[minor]++]), buffer++); length --; bytes_read ++; } #ifdef DEBUG printk ("Read %d bytes, %d left\n", bytes_read, length); #endif /* Read functions are supposed to return the number * of bytes actually inserted into the buffer */ /* By updating file->f_pos we can allow seeking of our Messge_Ptr which will allow random access to the ports using file I/O */ file->f_pos = (loff_t)Message_Ptr[minor]; return bytes_read; } /**************************************************************************** * * * DEVICE WRITE * ***************************************************************************** */ ssize_t device_write(struct file *file,const char *buffer,size_t length,loff_t *offset) { int i; unsigned port; unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); port = base_port[minor]; #ifdef DEBUG printk ("device_write( Minor %d,%p,%s,%d)", minor,file, buffer, length); #endif /* Read in the bytes to write, one at a time */ for(i=0; if_pos = (loff_t) Message_Ptr[minor]; /* Again, return the number of input characters used */ return i; } /**************************************************************************** * * * * DEVICE IOCTL * * ***************************************************************************** */ int device_ioctl(struct inode *inode,struct file *file, unsigned int ioctl_num, unsigned long ioctl_param) { int i; unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); int device, port, ret_val; #ifdef DEBUG printk("UIO48 - IOCTL call minor %d,IOCTL CODE %04X\n",minor,ioctl_num); #endif /* Switch according to the ioctl called */ switch (ioctl_num) { case IOCTL_READ_PORT: device = minor; port = (ioctl_param & 0xff); ret_val = inb(base_port[device]+port); return ret_val; break; case IOCTL_WRITE_PORT: device = minor; port = (ioctl_param >> 8) & 0xff; ret_val = ioctl_param & 0xff; outb(ret_val, base_port[device]+port); #ifdef DEBUG printk("Writing to I/O Port %04x with%02x\n", base_port[device]+port,ret_val); #endif return SUCCESS; break; case IOCTL_READ_BIT: device = minor + 1; ret_val = read_bit(device,ioctl_param & 0xff); return ret_val; break; case IOCTL_WRITE_BIT: device = minor + 1; write_bit(device,(ioctl_param >> 8) & 0xff, ioctl_param & 0xff); return SUCCESS; break; case IOCTL_SET_BIT: device = minor + 1; UIO48_set_bit(device, ioctl_param & 0xff); return SUCCESS; break; case IOCTL_CLR_BIT: device = minor + 1; clr_bit(device, ioctl_param & 0xff); return SUCCESS; break; case IOCTL_ENAB_INT: device = minor + 1; #ifdef DEBUG printk("<1>UIO48 - IOCTL Set_int Device %d Bit %d polarity %d\n", device,(int)(ioctl_param>>8),(int)(ioctl_param & 0xff)); #endif enab_int(device,(int)(ioctl_param >> 8),(int)(ioctl_param&0xff)); return SUCCESS; break; case IOCTL_DISAB_INT: device = minor +1; #ifdef DEBUG printk("<1>UIO48 - IOCTL Disab_Int Device %d Bit %d\n", device,(int)(ioctl_param & 0xff)); #endif disab_int(device, (int)(ioctl_param & 0xff)); return SUCCESS; break; case IOCTL_CLR_INT: device = minor +1; #ifdef DEBUG printk("<1>UIO48 - IOCTL Clr_int Device %d Bit %d\n", device,(int)(ioctl_param & 0xff)); printk("<1>UIO48 - Int_count = %d\n",int_count[minor]); #endif clr_int(device,(int)(ioctl_param & 0xff)); return SUCCESS; break; case IOCTL_GET_INT: device = minor + 1; #ifdef DEBUG printk("<1>UIO48 - IOCTL get_int device %d\n",device); #endif i = get_buffered_int(device); return i; break; case IOCTL_WAIT_INT: device = minor + 1; #ifdef DEBUG printk("<1>UIO48 - IOCTL wait_int device %d\n",device); #endif if((i = get_buffered_int(minor + 1))) return i; // Code to put current process to sleep awaiting interrupt #ifdef DEBUG printk("<1> UIO48 : current process %i (%s) going to sleep\n", current->pid,current->comm); #endif switch(minor) { case 0: interruptible_sleep_on(&wq_1); break; case 1: interruptible_sleep_on(&wq_2); break; case 2: interruptible_sleep_on(&wq_3); break; case 3: interruptible_sleep_on(&wq_4); break; case 4: interruptible_sleep_on(&wq_5); break; case 5: interruptible_sleep_on(&wq_6); break; } #ifdef DEBUG printk("<1> UIO48 : awoken %i (%s)\n",current->pid,current->comm); #endif /* Getting here does not guarantee that there's an in interrupt available we may have been awakened by some other signal. In any case We'll return whatever's available in the interrupt queue even if it's empty */ i = get_buffered_int(minor + 1); return i; break; } return SUCCESS; } /***************************** DEVICE_LSEEK ***************************/ loff_t device_lseek(struct file *filp, loff_t off, int whence) { unsigned offset; unsigned int minor = MINOR(filp->f_dentry->d_inode->i_rdev); /* Convert the offset value to a more familiar size. After all our device is a fixed size of 10 bytes. */ offset = (unsigned)off; #ifdef DEBUG printk("<1>UIO48:lseek - device %d offset %d mode %d\n",minor,offset,whence); #endif switch(whence) { case 0: /* Seek set */ Message_Ptr[minor] = offset; break; case 1: /* Seek from Current */ Message_Ptr[minor] += offset; break; default: /* Seek from End */ return -EINVAL; break; } if(Message_Ptr[minor] < 0) { Message_Ptr[minor] = 0; filp->f_pos = (loff_t)Message_Ptr[minor]; return -EINVAL; } if(Message_Ptr[minor] > 9) { Message_Ptr[minor] = 10; filp->f_pos = (loff_t)Message_Ptr[minor]; return -EINVAL; } filp->f_pos = (loff_t)Message_Ptr[minor]; return (loff_t)Message_Ptr[minor]; } /* ******************* Module Declarations *************************** */ /* This structure will hold the functions to be called * when a process does something to the our device */ struct file_operations Fops = { owner: THIS_MODULE, llseek: device_lseek, read: device_read, write: device_write, ioctl: device_ioctl, open: device_open, release: device_release, }; /**************************************************************************** * * INIT_MODULE * * ***************************************************************************** */ /* Initialize the module - Register the character device */ int init_module() { int ret_val; int x; /* Sign-on */ printk("<1>\nWinSystems UIO48 Driver. Copyright 2002-2005. All rights reserved\n"); printk("<1>%s\n",RCSInfo); /* Check and Map our I/O region requests */ for(x = 0; x < MAX_CHIPS; x++) { if(io[x] != 0) { if(request_region(io[x],0x10,"UIO48") == NULL) { printk("<1>Unable to use I/O Address %04X\n",io[x]); io[x] = 0; continue; } } } /* Register the character device */ ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops); /* Negative values signify an error */ if (ret_val < 0) { printk ("%s failed with %d\n", "Sorry, registering the character device ", ret_val); return ret_val; } for(x=0; x < MAX_CHIPS; x++) { if(io[x] != 0) { printk ("\nThe Base I/O Address = %04X\n",io[x]); init_io(x+1,io[x]); } if((io[x] != 0) && (irq[x] != 0)) { switch(x) { case 0: if(request_irq(irq[x],handler_1,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 1\n",irq[x]); break; case 1: if(request_irq(irq[x],handler_2,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 2\n",irq[x]); break; case 2: if(request_irq(irq[x],handler_3,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 3\n",irq[x]); break; case 3: if(request_irq(irq[x],handler_4,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 4\n",irq[x]); break; case 4: if(request_irq(irq[x],handler_5,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 5\n",irq[x]); break; case 5: if(request_irq(irq[x],handler_6,SA_SHIRQ,"UIO48",RCSInfo)) printk("<1>UIO48 - Unable to register IRQ %d\n",irq[x]); else printk("<1>UIO48 - IRQ %d registered ok Chip 6\n",irq[x]); break; } } } return 0; } /*************************************************************************** * * CLEANUP_MODULE * **************************************************************************** */ /* Cleanup - unregister the appropriate file from /proc */ void cleanup_module() { int x; /* Unregister I/O Port usage */ for(x=0; x < MAX_CHIPS; x++) { if(irq[x]) { free_irq(irq[x],RCSInfo); } if(io[x]) { release_region(io[x],0x10); } } /* Unregister the device */ unregister_chrdev(MAJOR_NUM, DEVICE_NAME); } /* start of device subroutines */ /* This array holds the image values of the last write to each I/O port. This allows bit manipulation routines to work without having to actually do a read-modify-write to the I/O port. */ unsigned char port_images[MAX_CHIPS][6]; void init_io(int chip_number, unsigned io_address) { int x; /* Zero adjust chip number */ --chip_number; /* save the address for later use */ base_port[chip_number] = io_address; /* Clear all of the I/O ports. This also makes them inputs */ for(x=0; x < 7; x++) outb(0,base_port[chip_number]+x); /* Clear the image values as well */ for(x=0; x < 6; x++) port_images[chip_number][x] = 0; /* Set page 2 access, for interrupt enables */ outb(0x80,base_port[chip_number]+7); /* Clear all interrupt enables */ outb(0,base_port[chip_number]+8); outb(0,base_port[chip_number]+9); outb(0,base_port[chip_number]+0x0a); /* Restore page 0 register access */ outb(0,base_port[chip_number]+7); } int read_bit(int chip_number, int bit_number) { unsigned port; int val; /* Adjust chip number to zero based addressing */ --chip_number; /* Adjust bit_number to 0 to 47 numbering */ --bit_number; /* Calculate the I/O port address based on the updated bit_number */ port = (bit_number /8) + base_port[chip_number]; /* Get the current contents of the port */ val = inb(port); /* Get just the bit we specified */ val = val & (1 << (bit_number % 8)); if(val) return 1; return 0; } void write_bit(int chip_number, int bit_number, int val) { unsigned port; unsigned temp; unsigned mask; /* Adjust the chip number for 0 based numbering */ --chip_number; /* Adjust bit number for 0 based numbering */ --bit_number; /* Calculate the I/O address of the port based on the bit number */ port = (bit_number / 8) + base_port[chip_number]; /* Use the image value to avoid having to read the port first */ temp = port_images[chip_number][bit_number / 8]; /* Calculate a bit mask for the specified bit */ mask = (1 << (bit_number % 8)); /* check whether the request was to set or clear and mask accordingly */ if(val) /* If the bit needs to be set */ temp = temp | mask; else temp = temp & ~mask; /* Update the image value with the value we're about to write */ port_images[chip_number][bit_number /8] = temp; /* Now actally update the port. Only the specified bit is affected */ outb(temp,port); } void UIO48_set_bit(int chip_num, int bit_num) { write_bit(chip_num, bit_num, 1); } void clr_bit(int chip_num, int bit_num) { write_bit(chip_num, bit_num, 0); } void enab_int(int chip_number, int bit_number, int polarity) { unsigned port; unsigned temp; unsigned mask; /* Adjust for 0 based numbering */ --chip_number; /* Also adjust bit number */ --bit_number; /* Calculate the I/O address based upon bit number */ port = (bit_number / 8) + base_port[chip_number] + 8; /* Calculate a bit mask based upon the specified bit number */ mask = (1 << (bit_number % 8)); /* Turn on page 2 access */ outb(0x80,base_port[chip_number]+7); /* Get the current state of the interrupt enable register */ temp = inb(port); /* Set the enable bit for our bit number */ temp = temp | mask; /* Now update the interrupt enable register */ outb(temp,port); /* Turn on access to page 1 for polarity control */ outb(0x40,base_port[chip_number]+7); /* Get the current state of the polarity register */ temp = inb(port); /* Set the polarity according to the argument in the image value*/ if(polarity) temp = temp | mask; else temp = temp & ~mask; /* Write out the new polarity value */ outb(temp,port); /* Set access back to page 0 */ outb(0x00,base_port[chip_number]+7); } void disab_int(int chip_number, int bit_number) { unsigned port; unsigned temp; unsigned mask; /* Adjust for 0 based numbering */ --chip_number; /* Also adjust bit number */ --bit_number; /* Calculate the I/O address based upon bit number */ port = (bit_number / 8) + base_port[chip_number] + 8; /* Calculate a bit mask based upon the specified bit number */ mask = (1 << (bit_number % 8)); /* Turn on page 2 access */ outb(0x80,base_port[chip_number]+7); /* Get the current state of the interrupt enable register */ temp = inb(port); /* clear the enable bit for our bit number */ temp = temp & ~mask; /* Now update the interrupt enable register */ outb(temp,port); /* Set access back to page 0 */ outb(0x00,base_port[chip_number]+7); } void clr_int(int chip_number, int bit_number) { unsigned port; unsigned temp; unsigned mask; /* Adjust for 0 based numbering */ --chip_number; /* Also adjust bit number */ --bit_number; /* Calculate the I/O address based upon bit number */ port = (bit_number / 8) + base_port[chip_number] + 8; /* Calculate a bit mask based upon the specified bit number */ mask = (1 << (bit_number % 8)); /* Turn on page 2 access */ outb(0x80,base_port[chip_number]+7); /* Get the current state of the interrupt enable register */ temp = inb(port); /* Temporarily clear only our enable. This clears the interrupt */ temp = temp & ~mask; /* Clear the enable for this bit */ /* Now update the interrupt enable register */ outb(temp,port); /* Re-enable our interrupt bit */ temp = temp | mask; outb(temp,port); /* Set access back to page 0 */ outb(0x00,base_port[chip_number]+7); } int get_int(int chip_number) { int temp; int x; /* Adjust the chip number for 0 based numbering */ --chip_number; /* Read the master interrupt pending register, mask off undefined bits */ temp = inb(base_port[chip_number]+6) & 0x07; /* If there are no pending interrupts, return 0 */ if((temp & 7) == 0) return 0; /* There is something pending, now we need to identify it */ /* Set access to page 3 for interrupt id register */ outb(0xc0, base_port[chip_number] + 7); /* Read the interrupt ID register for port 0 */ temp = inb(base_port[chip_number]+8); /* See if any bit set, if so return the bit number */ if(temp != 0) { for(x=0; x<=7; x++) { if(temp & (1 << x)) { outb(0,base_port[chip_number]+7); return(x+1); } } } /* None in port 0, read port 1 interrupt ID register */ temp = inb(base_port[chip_number]+9); /* See if any bit set, if so return the bit number */ if(temp != 0) { for(x=0; x<=7; x++) { if(temp & (1 << x)) { outb(0,base_port[chip_number]+7); return(x+9); } } } /* Lastly, read the statur of port 2 interrupt ID register */ temp = inb(base_port[chip_number]+0x0a); /* If any pending, return the appropriate bit number */ if(temp != 0) { for(x=0; x<=7; x++) { if(temp & (1 << x)) { outb(0,base_port[chip_number]+7); return(x+17); } } } /* We should never get here unless the hardware is seriously misbehaving, but just to be sure, we'll turn the page access back to 0 and return a 0 for no interrupt found */ outb(0,base_port[chip_number]+7); return 0; } int get_buffered_int(int chip_number) { int temp; --chip_number; if(irq[chip_number] == 0) { temp = get_int(chip_number+1); if(temp) clr_int(chip_number+1,temp); return temp; } if(outptr[chip_number] != inptr[chip_number]) { temp = int_buffer[chip_number][outptr[chip_number]++]; if(outptr[chip_number] == MAX_INTS) outptr[chip_number] = 0; return temp; } return 0; }