Page 1 of 2
Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:10 pm
by eXeCuTeR
The situation is as follows:
I'm implementing 'cat' and every thing works splendidly GREAT! except that it handles a `cat request` (`cat file1` for instance) by calling fat_read which uses floppy_read which waits for an interrupt, while all this is done under the handling of the keyboard interrupt. (i've called it in such names you would understand what it does)
So what is actually needed is to wait for an interrupt (for floppy, since reading involves waiting for interrupts) inside an handling of an interrupt (the keyboard one!)
Sure, I could have played with iret and ret and manipulate with the EIP but that is does not look good and really annoying.
Here's some code snippets in case you haven't completely understood what happend (the code works just fine, it is just that an interrupt has to be called for inside the handling of another)
(No need to actually read the code because it is fine, just to make things more clear for those who have not fully understood)
(try to ignore the disturbing indention, it's just that I'm using KWrite so copying it to here makes it look bad)
cat.c:
Code: Select all
if((pos=find_entry(cwd, (const char *)name, (const char *)ext)))
{
if(!(pos->attributes & ATTR_SUB_DIRECTORY))
{
if(pos->file_size > LARGEST_FILE_FOR_SHOW) printf("cat error: file is too large to be shown.");
else
{
char *buffer = (char *)malloc(pos->file_size+1);
memset((void *)buffer, 0, pos->file_size+1);
fat_read(pos, buffer, pos->file_size, 0);
printf("%s", buffer);
kfree((void **)&buffer);
}
}
else
printf("cat error: requested file is a directory.\n");
}
else
printf("cat error: requested file is neither a file or a directory.\n");
keyboard.c:
Code: Select all
void keyboard_handler(registers_t *regs)
{
...some code
pos->cmd->exec(current_cmd->args); // calls cat
...some code
}
fat.c:
Code: Select all
int fat_read(fat_entry_t *file, const char *buffer, unsigned int size, unsigned int offset)
{
...some code
floppy_read_sector(cluster_to_sector(cluster));
...some code
}
floppy.c:
Code: Select all
void floppy_read_sector_chs(unsigned char cylinder, unsigned char head, unsigned char sector)
{
...some code
floppy_wait_int(); // or either floppy_sense_interrupt(&st0, &cyl); is there also
...some code
}
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:18 pm
by pcmattman
You need some method to nest interrupts safely - usually involves jumping to a new "nesting level" and loading a stack for that level. As you wind back (interrupts return) you can reduce the nesting level accordingly and free up the stacks. This is a super simplified explanation of how we go about nested IRQs and interrupts in Pedigree.
Really though, I can identify a bigger problem you're dealing with - you say all this is happening from your keyboard IRQ? That sounds to me like you've just hacked in a command prompt to the IRQ handler rather than doing it "properly"...
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:21 pm
by gerryg400
eXeCuTeR, pcmattman is correct. This is a very unusual and perhaps problematic approach. Are you asking for a different way to do it ?
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:22 pm
by eXeCuTeR
pcmattman wrote:You need some method to nest interrupts safely - usually involves jumping to a new "nesting level" and loading a stack for that level. As you wind back (interrupts return) you can reduce the nesting level accordingly and free up the stacks. This is a super simplified explanation of how we go about nested IRQs and interrupts in Pedigree.
Really though, I can identify a bigger problem you're dealing with - you say all this is happening from your keyboard IRQ? That sounds to me like you've just hacked in a command prompt to the IRQ handler rather than doing it "properly"...
Hmm, what's the alternative? Parsing a command and getting the arguments for it is done through the keyboard handler, how could I pass such information to higher levels rather than an IRQ?
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:31 pm
by pcmattman
Buffer the keys that are pressed in a FIFO or something (ring buffers work well) that applications access through a well-defined API. That way the actual reading of key presses happens outside of an IRQ.
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:34 pm
by gerryg400
There are a few ways, but mostly the answer is to convert the asynchronous interrupt to a synchronous event that the kernel or driver can handle. For a keyboard driver you might do this
Code: Select all
/* ISR - occurs every keypress - asynchronous */
1. Read key from keyboard hardware
2. Maybe handle ctrl, shift and alt status
3. translate key to char.
4. Put char in buffer in driver
5. Set flag to say key is available.
6. Do hardware things like ack keyboard and PIC.
7. IRET
/* Readkey system call - called from cat process */
1. disable interrupts
2. Checks flag to see whether there is a key available
3. Removes char and clears flag if buffer is empty
4. enables interrupts.
5. Return char or NULL if no key OR might block waiting for key to become available
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:36 pm
by eXeCuTeR
pcmattman wrote:Buffer the keys that are pressed in a FIFO or something (ring buffers work well) that applications access through a well-defined API. That way the actual reading of key presses happens outside of an IRQ.
Thought of this way, to be honest. Thanks, by implementing it this way, no harm will be done and problem will be solved.
But only one thing is unclear...
how can I define such an API that applications will EDITED:
not be able to just write to, say, (if this FIFO is at 0x1234 for instance) just write to 0x1234.
To make these pages that these addresses occupy only accessible on DPL0 (ring0) and every time an application is trying to reach it, it'll call a system call which will do the trick? Is this a good design enough?
EDIT: Thanks gerry, it is the basic idea indeed even though I think I'll just write to the buffer whenever I have a command parsed with its arguments and not every char - this doesn't really risk anything. when the command is ready (enter was pressed), i'll "pipe" it out to the buffer, set a key that will indicate that a command is ready to be triggered.
if you could also just refer to my new question (which I think I gave a fair answer, but just to make sure I don't make another design error).
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 9:46 pm
by gerryg400
You mean this question ?
how can I define such an API that applications will be able to just write to, say, (if this FIFO is at 0x1234 for instance) just write to 0x1234.
I think it's better not to do this. You should try to abstract away the memory reference. The API should be ReadKey() or getch() or something. The kernel can handle the index, permissions, blocking etc. That way you can modify your kernel later and all your APPS will still work. These are the reasons for developing an API.
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 10:20 pm
by eXeCuTeR
gerryg400 wrote:You mean this question ?
how can I define such an API that applications will be able to just write to, say, (if this FIFO is at 0x1234 for instance) just write to 0x1234.
I think it's better not to do this. You should try to abstract away the memory reference. The API should be ReadKey() or getch() or something. The kernel can handle the index, permissions, blocking etc. That way you can modify your kernel later and all your APPS will still work. These are the reasons for developing an API.
Hehe no, I actually meant the opposite (how to prevent such case): how could I make such thing
NOT to happen, I forgot to add a critical word (just read my next lines, you'll see). sorry about that. (been awake for way too long)
So, I'm thinking of making ReadKey() a system call, that will return the buffer when called if ready.
By doing it this way, I actually must move a character by a character, which is kind bad for me because all the parsing of the command is already done in the IRQ handler.
Any other way maybe? (to keep the buffer safe from applications and also create an easy to implement API)
Re: Calling an interrupt from the handling of another.
Posted: Thu Jul 22, 2010 10:36 pm
by gerryg400
By doing it this way, I actually must move a character by a character, which is kind bad for me because all the parsing of the command is already done in the IRQ handler.
Any other way maybe? (to keep the buffer safe from applications and also create an easy to implement API)
I was recommending that the ISR simply put the chars in the buffer. The parsing would be done in the APP. The APP calls ReadKey until it gets a '\n' and then parses the command.
Re: Calling an interrupt from the handling of another.
Posted: Fri Jul 23, 2010 6:06 am
by egos
gerryg400 is right. Just small remark: character translation could be done by application from (virtual) key scancodes, control key flags and keyboard indicator flags. Even more generalized way is to use event queue for foreground process. For keyboard I use KEYDOWN and KEYUP events with following parameters: event identifier (dword), virtual key scancode (dword), control key flags (dword: low byte - left, next byte - right), keyboard indicator flags (dword).
Re: Calling an interrupt from the handling of another.
Posted: Fri Jul 23, 2010 10:49 pm
by eXeCuTeR
Hmm, I'm thinking about it again and I'm not sure if a ring buffer is actually needed. I actually need a room from only 1 byte, since getch()/ReadKey() read only one and all the other functions just like getline(), rely on these. What do you guys think?
Re: Calling an interrupt from the handling of another.
Posted: Fri Jul 23, 2010 10:55 pm
by gerryg400
The ring buffer is for type-ahead. So the user can continue typing the next command while the APP (in this case cat) is still executing.
Re: Calling an interrupt from the handling of another.
Posted: Fri Jul 23, 2010 11:00 pm
by eXeCuTeR
gerryg400 wrote:The ring buffer is for type-ahead. So the user can continue typing the next command while the APP (in this case cat) is still executing.
But what happens when you call getch once, and the user keeps on typing.
Then you call getch one more time, what will be returned?
As I see it know, the keyboard ring buffer, whenever a character is pressed, it pushes at into the ring buffer.
So when calling getch() it actually just reads the next character in the FIFO? (while there could be alot of values)
Also kinda buggy in a point where the use types lots of characters, and also, our memory if finite, and the buffer is circular, so some data could be lost.
Re: Calling an interrupt from the handling of another.
Posted: Fri Jul 23, 2010 11:43 pm
by gerryg400
Generally for type-ahead you just need a few chars. The user might type a few, then realise nothing is happening yet so he waits. If the buffer is too big, the user will be annoyed that characters that he cannot remember typing, or didn't mean to type (e.g. leant on the keyboard) are still being processed. I just checked and my driver has a 32 key ring-buffer per terminal.