Lowlevel
Lowlevel => Lowlevel-Coding => Thema gestartet von: middle_endian am 25. July 2007, 15:25
-
hi,
ich versuch mich gerade an einem tastaturtreiber. dazu hab ich die irqs des master/slave-pics auf die interrupts 20h-27h und 28h-2fh gemappt. soweit ich das richtig verstanden hab, wird bei einer tastatureingabe der irq1 aufgerufen (wobei danach auf port 0x60 der scancode abgelesen werden kann). dem irq1 ist bei mir die isr 21h zugeordnet. allerdings wird diese isr nicht aufgerufen, wenn ich eine taste drücke.
jetzt frag ich mich, obs an der remapping routine liegt, oder an was anderem. hier der code den ich zum remappen verwende:
;; PIC ports
%define PIC_MASTER_CMD 20h
%define PIC_MASTER_DATA 21h
%define PIC_SLAVE_CMD 0a0h
%define PIC_SLAVE_DATA 0a1h
;; -----------------------------------------------
;; remapping PICs IRQs to not-reserved interrupts
;; -----------------------------------------------
remap_pic:
;; ICW1
mov al, 10001b ; bit1=1: forth ICW, bit2=0: pic cascading
out PIC_MASTER_CMD, al
out PIC_SLAVE_CMD, al
;; now starting to send the 3 inizialisation words on the data port
;; ICW2: its vector offset
;; ICW3: tell it how it is wired to master/slaves
;; ICW4: gives additionnal infos about the environment
;; ICW2
mov al, 20h
out PIC_MASTER_DATA, al ; map master irqs to int 20h-27h
mov al, 28h
out PIC_SLAVE_DATA, al ; map slave ircq to 28h-2fh
;; ICW3
mov al, 4 ; 4 == bit3=1 -> irq2@master wired with slave ...
out PIC_MASTER_DATA, al
shr al, 2 ; number of master irq necessary -> also irq2
out PIC_SLAVE_DATA, al
;; ICW4
;; before isr finishes, the pic needs to be notified,
;; so he knows when to restart work
mov al, 1
out PIC_MASTER_DATA, al
out PIC_SLAVE_DATA, al
;; end of interrupt(eoi) signal to both pics
mov al, 20h
out PIC_MASTER_CMD, al
out PIC_SLAVE_CMD, al
ret
wenn ich den irq1 per software-interrupt aufrufe, wird die entsprechende isr auch ausgeführt, was mich vermuten lässt, dass es nichts mit einer möglicherweise falschen initialisierung der idt zu tun.
meine allgemeine ablaufreihenfolge sieht so aus:
;; interrupts disabled
;; ...
pmode:
;; ...
;; init idt
;; ...
call remap_pic
call enable_irqs
sti
;; kernel main
;; ...
kann mir jemand sagen, was da schief läuft?
-
Lass mal das Senden des EOIs in remap_pic weg. Das hat es bei mir nicht gebraucht.
-
ändert leider auch nix :(
hab auch versucht kurz delays hinter die out instruktionen zu setzen. bringt auch nix.
-
Wie bluecode schon sagte, der EOI muss raus der Rest ist richtig. Aber zeigt der IRQ0 bei dir auf einen Handler? Denn dieser wird automatisch aufgerufen. Du solltest allen nicht benutzten IRQs einen Handler angeben, der min. einen EOI (bei master nur an 20h und bei Slave an 20h und A0h) sendet.
bitmaster
-
Du kannst auch alle IRQs ausser dem Tastatur-IRQ demaskieren etwa so:
mov al, 11111101b
out 0x21, al
Gruss
Nooooooooooos
-
Stimmt, dir kommt wahrscheinlich ein IRQ0 ohne EOI in die Quere und dann ist der PIC beleidigt und hört einfach auf zu tun was man gerne hätte. 8-)
-
@bluecode: Ja ja ja, mache dich ruhig über mich lustig. Das ist mir egal. Außerdem hat der FC Bayern gerade gegen den VFB gewonnen. Juhuuuuuuuuuuuuuuuuuuuu
bitmaster
-
wtf? :-o Das war jetzt ehrlich nicht meine Intention. Ich hab ehrlich gesagt das mit dem IRQ0 in deinem Post überlesen und dachte das fügste jetzt noch hinzu...
-
ok, hab jetzt den eoi code am ende der ersten beiden irq-handler(0 und 1) eingefügt. in meinen interrupt routinen geb ich wie gesagt aus, welcher interrupt gerade aufgerufen wurde.
jetzt bekomm ich natürlich ständig die irq0-meldung(timer) ausgegeben. das initialisieren der idt mit den handlern scheint also zu funktionieren. wenn ich allerdings eine taste drück, passiert mit irq1 immer noch nichts. erst nach 9-maligem drücken bekomm ich von bochs die meldung
[kbd ] internal keyboard buffer full. ignoring scancode(xy)
aber warum wird der handler nicht aufgerufen?
EDIT: habs hingekriegt. es war ein einfacher logischer fehler. danke für die hilfe :)
-
hi,
da ich keinen neuen thread deswegen aufmachen wollte, stell ich die frage gleich hier. es geht um das prinzipielle design des keyboard treibers.
herzstück des treibers ist bei mir die handle_keystroke routine, die beim auftreten des irq1 aufgerufen wird. diese routine schreibt das übertragene zeichen in einen eigens angelegten puffer-bereich. die "user-level" routinen des treibers arbeiten nur noch mit diesem tastatur puffer.
getch sieht bei mir so aus:
;; ---------------------------------------------
;; read one character from the keyboard(buffer)
;; ---------------------------------------------
;; return value:
;; al = keyboard input character
;; temp. register = ebx
getch:
;; initialization stuff
;; ...
;; wait until data is available
.wait:
mov al, [key_buf_pos]
cmp [key_buf_beg], al ; wait for keyboard data
jne .ready
jmp .wait
.ready:
;; read data from keyboard buffer
;; ...
ret
funktionieren tut das ganze, nur stört mich diese schleife ein wenig, die kontinuierlich die cpu belastet und checkt, ob im puffer daten zur verfügung stehn. prinzipiell wäre das (im moment) kein problem, da die cpu zum zeitpunkt des abarbeitens von getch sowieso nichts anderes zu tun hat. auch im multi-process betrieb seh ich kein problem, da die getch routine in einem eigenen prozess aufgerufen wird, und dieser eben neben anderen prozessen vor sich herläuft.
trotzdem meine frage: gibts eine elegantere (effizientere) variante? mir fällt mit den zur verfügung stehenden mitteln nichts ein(mein kernel besitzt im moment lediglich funktionen zur textausgabe und eben den tastaturtreiber).
-
Dann solltest du dir so langsam gedanken über gescheite IPC machen. zB könntest du bei message passing deinem scheduler sagen, er soll den thread erst wieder aufwecken, wenn der Thread eine message vom Typ MSG_KEYSTROKE (oder was auch immer) bekommt. Der Tastaturtreiber verschickt dann bei Tastendruck so eine Nachricht. Wenn du als IPC RPC wählst, dann kannst du eine funktion namens keystroke_callback implementieren, die der keyboard treiber dann indirekt aufruft. Natürlich sollte man da auch dem Scheduler sagen können, dass ein Thread grad nichts tun möchte und erst beim nächsten RPC aufgeweckt werden soll.
btw. was du machst nennt man busy-waiting und es ist ein PITA, wenn es mal mehr als einen Thread gibt. :wink:
-
ok ja, das macht sinn. aber im jetzigen zustand meines kernels lässt sich wenig am design der routine ändern bzw sollte diese vorgehensweise doch sogar die "vernünftigste" sein!?
-
Wenn das alles im Kernel ist, dann kannst du auch einfach deine Shell im keyboard isr aufrufen und dort den Tastendruck verarbeiten:
size_t index = 0;
char buf[512];
void shell_callback(char c)
{
if (c == '\n')
{
// execute the command
if (strcmp(buf, "ls"))
show_file_listing();
index = 0;
}
else
{
// append to the buffer
buf[index++] = c;
}
// Null-terminate
buf[index] = '\0';
}