* デバイスドライバってどうやって作るの?
-(by [[K]], 2008.03.21)
-発展課題のページ(全部読む前にここを読むのは混乱するのでおすすめじゃないです)
--このページは子ページです。親ページはこちら → [[advance/driver]]
----
-このページではデバイスドライバを使えるようにするための、OS本体の改造個所をまとめてあります。
*** naskfunc.nas
-asm_hrb_dpiを作成します。終了処理のないasm_hrb_apiだと思えばいいと思います。
_asm_hrb_dpi:
; STI ; 勝手にIF=1にしてはいけないかもしれないのでこれはつけない
PUSH DS
PUSH ES
PUSHAD ; 保存のためのPUSH
PUSHAD ; hrb_dpiにわたすためのPUSH
MOV AX,SS
MOV DS,AX ; OS用のセグメントをDSとESにも入れる
MOV ES,AX
CALL _hrb_dpi
ADD ESP,32
POPAD
POP ES
POP DS
IRETD
-asm_hrb_api:の最初のSTIも外してください。割り込み処理中はSTIしてはいけないからです。STIが残っていると割り込み処理中はAPIが呼べないことになってしまいます。
-でも単にSTIを消すだけだと、今度はDPI/APIの呼び出し中は常にIF=0になって割り込み禁止になってしまいます。これはいけません。ということで、dsctbl.cを改造します。
*** dsctbl.c
-init_gdtidt()をこんな風にします。
#define AR_TRPGATE32 0x8f
(中略)
set_gatedesc(idt + 0x40, (int) asm_hrb_api, 2 * 8, AR_TRPGATE32 + 0x60);
set_gatedesc(idt + 0x41, (int) asm_hrb_dpi, 2 * 8, AR_TRPGATE32 + 0x60);
(中略)
*** console.c
-cmd_app()を少しだけ書き換えます。これでHrDvも実行できるようになります。
if (appsiz >= 36 && (strncmp(p + 4, "Hari", 4) == 0 || strncmp(p + 4, "HrDv", 4) == 0) && *p == 0x00) {
segsiz = *((int *) (p + 0x0000));
(中略)
for (i = 0; i < datsiz; i++) {
q[esp + i] = p[dathrb + i];
}
if (strncmp(p + 4, "HrDv", 4) == 0) {
io_store_eflags(0x00003202); /* IOPL=3 */
}
start_app(0x1b, 0 * 8 + 4, esp, 1 * 8 + 4, &(task->tss.esp0));
io_store_eflags(0x00000202); /* IOPL=0 */
shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
-本当は拡張子とシグネチャが一致しているかどうかもチェックするべきです。ここではめんどくさいので省略。
-この改造だけだとコマンドラインからドライバを実行する(=組み込む)ときに拡張子の.hdvを省略できません。アプリみたいに拡張子を省略してもいいようにしたい人はそういう風に改造するといいでしょう。もしくは、stdrvコマンドとかを作って、"stdrv ドライバ名"って入力しないと起動しないようにしてもいいかもしれません。
*** console.c
-hrd_dpi()を作ります。多くの部分がhrb_api()によく似ています。
void hrb_dpi(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
struct TASK *task = task_now();
int ds_base = task->ds_base;
struct CONSOLE *cons = task->cons;
struct FIFO32 *sys_fifo = (struct FIFO32 *) *((int *) 0x0fec);
int *reg = &eax + 1; /* eaxの次の番地 */
/* 保存のためのPUSHADを強引に書き換える */
/* reg[0] : EDI, reg[1] : ESI, reg[2] : EBP, reg[3] : ESP */
/* reg[4] : EBX, reg[5] : EDX, reg[6] : ECX, reg[7] : EAX */
int i;
struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
/* ここでHrDvからじゃなければエラーにする処理を書くべき だけど今は手抜き */
if (edx == 1) {
dpi_irq_table[ecx].func = eax;
dpi_irq_table[ecx].esp = ebx;
dpi_irq_table[ecx].task = task;
/* 重複登録になっていないか確認したほうがいい */
} else if (edx == 2) {
i = io_load_eflags();
io_cli();
reg[7] = fifo32_put(ebx, eax + 256);
reg[7] = fifo32_put(ecx, eax + 256);
io_store_eflags(i);
} else if (edx == 3) {
reg[7] = (int) &task->fifo;
} else if (edx == 4) {
i = io_load_eflags();
io_cli();
reg[7] = fifo32_put(sys_fifo, eax + 256);
io_store_eflags(i);
} else if (edx == 5) {
めんどくさくなってきたので省略
}
return;
}
-こうするとdpi_irq_table[]関係の処理と、キー入力関係の処理を書き直す必要が出てきます。キー入力については、キーコードから文字コードに変換する処理をドライバにやらせることにしたいので、task_aを以下のように改造します。
*** bootpack.c
-HariMain()の一部を書き換えます。
if (256 <= i && i <= 511) { /* キーボードデータ */
if (256 <= i && i <= 256 + 0xef && key_win != 0) {
fifo32_put(&key_win->task->fifo, i);
} else if (256 + 0xf0 <= i && i <= 256 + 0xf7) { /* Lock */
key_leds = i & 7;
fifo32_put(&keycmd, KEYCMD_LED);
fifo32_put(&keycmd, key_leds);
} else if (i == 256 + 0xf8) { /* Tab */
if (key_win != 0) {
keywin_off(key_win);
j = key_win->height - 1;
if (j == 0) {
j = shtctl->top - 1;
}
key_win = shtctl->sheets[j];
keywin_on(key_win);
}
} else if (i == 256 + 0xf9) { /* Shift+F1 */
if (key_win != 0) {
task = key_win->task;
if (task != 0 && task->tss.ss0 != 0) {
cons_putstr0(task->cons, "\nBreak(key) :\n");
io_cli(); /* 強制終了処理中にタスクが変わると困るから */
task->tss.eax = (int) &(task->tss.esp0);
task->tss.eip = (int) asm_end_app;
io_sti();
task_run(task, -1, 0); /* 終了処理を確実にやらせるために、寝ていたら起こす */
}
}
} else if (i == 256 + 0xfa) { /* Shift+F2 */
/* 新しく作ったコンソールを入力選択状態にする(そのほうが親切だよね?) */
if (key_win != 0) {
keywin_off(key_win);
}
key_win = open_console(shtctl, memtotal);
sheet_slide(key_win, 32, 4);
sheet_updown(key_win, shtctl->top);
keywin_on(key_win);
} else if (i == 256 + 0xfb) { /* F11 */
sheet_updown(shtctl->sheets[1], shtctl->top - 1);
} else if (i == 256 + 0xfc) { /* キーボードがデータを無事に受け取った */
keycmd_wait = -1;
} else if (i == 256 + 0xfd) { /* キーボードがデータを無事に受け取れなかった */
wait_KBC_sendready();
io_out8(PORT_KEYDAT, keycmd_wait);
}
} else if (512 <= i && i <= 767) { /* マウスデータ */
*** bootpack.h
-さて残るは、dpi_irq_table[]関係です。まずはbootpack.hで構造体宣言を書くことにします。
struct STR_DPI_IRQ_TABLE {
struct TASK *task;
int func, esp;
};
extern struct STR_DPI_IRQ_TABLE dpi_irq_table[16];
*** int.c
-そしてkeyboard.cのinthandler21()をint.cに引越しさせて、少々改造します。
struct STR_DPI_IRQ_TABLE dpi_irq_table[16];
void call_irq_driver(struct STR_DPI_IRQ_TABLE *table)
/* IF=0で呼ぶこと */
{
int old_esp0, old_ss0, old_ldtr;
struct TSS32 *tss = &(table->task.tss);
old_esp0 = tss->esp0;
old_ss0 = tss->ss0;
old_ldtr = load_ldtr();
store_ldtr(tss->ldtr);
start_app(table->func, 4, table->esp, 12, &(tss->esp0));
load_ldtr(old_ldtr);
tss->esp0 = old_esp0;
tss->ss0 = old_ss0;
return;
}
void inthandler21(int *esp)
{
if (dpi_irq_table[0x01].task != 0) {
call_irq_driver(&dpi_irq_table[0x01]);
} else {
/* 処理先がないのでとりあえず空読みでもしておく */
io_out8(PIC0_OCW2, 0x61); /* IRQ-01受付完了をPICに通知 */
io_in8(PORT_KEYDAT);
}
return;
}
*** naskfunc.nas
-int.cでLDTRの読み書き関数が必要になったので準備します。
_load_ldtr: ; int load_ldtr(void);
MOV EAX,0
SLDT AX
RET
_store_ldtr: ; void store_cr0(int ldtr);
LLDT [ESP+4]
RET
-これでOS側の改造は終わりです。
* こめんと欄
-コメントは親ページへ → [[advance/driver]]