/kernel/branches/Kolibri-acpi/const.inc |
---|
645,7 → 645,7 |
end virtual |
struc MEM_STATE |
{ .mutex rd 1 |
{ .mutex MUTEX |
.smallmap rd 1 |
.treemap rd 1 |
.topsize rd 1 |
664,7 → 664,7 |
.kernel_pages dd ? |
.kernel_tables dd ? |
.sys_page_dir dd ? |
.pg_mutex dd ? |
.mutex MUTEX |
} |
;struc LIB |
/kernel/branches/Kolibri-acpi/core/apic.inc |
---|
5,7 → 5,7 |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
IRQ_RESERVE = 24 ; 16 or 24 |
IRQ_RESERVED = 24 ; 16 or 24 |
iglobal |
IRQ_COUNT dd 24 |
62,10 → 62,11 |
shr eax, 16 |
inc al |
movzx eax, al |
cmp al, IRQ_RESERVE |
cmp al, IRQ_RESERVED |
jbe @f |
mov al, IRQ_RESERVE |
@@: mov [IRQ_COUNT], eax |
mov al, IRQ_RESERVED |
@@: |
mov [IRQ_COUNT], eax |
; Reroute IOAPIC & mask all interrupts |
xor ecx, ecx |
106,7 → 107,7 |
;init handlers table |
mov ecx, IRQ_RESERVE |
mov ecx, IRQ_RESERVED |
mov edi, irqh_tab |
@@: |
mov eax, edi |
/kernel/branches/Kolibri-acpi/core/exports.inc |
---|
85,7 → 85,14 |
szStrchr db 'strchr',0 |
szStrrchr db 'strrchr',0 |
szDiskAdd db 'DiskAdd',0 |
szDiskDel db 'DiskDel',0 |
szDiskMediaChanged db 'DiskMediaChanged',0 |
szTimerHS db 'TimerHS',0 |
szCancelTimerHS db 'CancelTimerHS',0 |
align 16 |
kernel_export: |
dd szRegService , reg_service |
/kernel/branches/Kolibri-acpi/core/heap.inc |
---|
151,7 → 151,8 |
mov [mem_block_list+63*4], ebx |
mov byte [mem_block_map], 0xFC |
and [heap_mutex], 0 |
mov ecx, heap_mutex |
call mutex_init |
mov [heap_blocks], 4095 |
mov [free_blocks], 4094 |
ret |
272,14 → 273,14 |
push esi |
push edi |
mov ecx, heap_mutex |
call mutex_lock |
mov eax, [size] |
add eax, 4095 |
and eax, not 4095 |
mov [size], eax |
mov ebx, heap_mutex |
call wait_mutex ;ebx |
cmp eax, [heap_free] |
ja .error |
355,10 → 356,11 |
mov [edx+list_bk], esi |
mov [esi+block_flags], USED_BLOCK |
mov eax, [esi+block_base] |
mov ebx, [size] |
sub [heap_free], ebx |
and [heap_mutex], 0 |
mov ecx, heap_mutex |
call mutex_unlock |
mov eax, [esi+block_base] |
pop edi |
pop esi |
pop ebx |
378,17 → 380,19 |
mov [edx+list_bk], edi |
mov [edi+block_flags], USED_BLOCK |
mov eax, [edi+block_base] |
mov ebx, [size] |
sub [heap_free], ebx |
and [heap_mutex], 0 |
mov ecx, heap_mutex |
call mutex_unlock |
mov eax, [edi+block_base] |
pop edi |
pop esi |
pop ebx |
ret |
.error: |
mov ecx, heap_mutex |
call mutex_unlock |
xor eax, eax |
mov [heap_mutex], eax |
pop edi |
pop esi |
pop ebx |
400,9 → 404,10 |
push ebx |
push esi |
push edi |
mov ebx, heap_mutex |
call wait_mutex ;ebx |
mov ecx, heap_mutex |
call mutex_lock |
mov eax, [base] |
mov esi, [mem_used.fd] |
@@: |
491,9 → 496,10 |
@@: |
bts [mem_block_mask], eax |
.m_eq: |
mov ecx, heap_mutex |
call mutex_unlock |
xor eax, eax |
mov [heap_mutex], eax |
dec eax |
not eax |
pop edi |
pop esi |
pop ebx |
513,16 → 519,18 |
@@: |
bts [mem_block_mask], eax |
mov [esi+block_flags],FREE_BLOCK |
mov ecx, heap_mutex |
call mutex_unlock |
xor eax, eax |
mov [heap_mutex], eax |
dec eax |
not eax |
pop edi |
pop esi |
pop ebx |
ret |
.fail: |
mov ecx, heap_mutex |
call mutex_unlock |
xor eax, eax |
mov [heap_mutex], eax |
pop edi |
pop esi |
pop ebx |
607,8 → 615,8 |
proc kernel_free stdcall, base:dword |
push ebx esi |
mov ebx, heap_mutex |
call wait_mutex ;ebx |
mov ecx, heap_mutex |
call mutex_lock |
mov eax, [base] |
mov esi, [mem_used.fd] |
624,19 → 632,17 |
cmp [esi+block_flags], USED_BLOCK |
jne .fail |
and [heap_mutex], 0 |
call mutex_unlock |
push ecx |
mov ecx, [esi+block_size]; |
shr ecx, 12 |
call release_pages ;eax, ecx |
pop ecx |
stdcall free_kernel_space, [base] |
pop esi ebx |
ret |
.fail: |
call mutex_unlock |
xor eax, eax |
mov [heap_mutex], eax |
pop esi ebx |
ret |
endp |
/kernel/branches/Kolibri-acpi/core/irq.inc |
---|
5,6 → 5,10 |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
IRQ_POOL_SIZE equ 48 |
macro __list_add new, prev, next |
{ |
mov [next+LHEAD.prev], new |
28,13 → 32,13 |
uglobal |
align 16 |
irqh_tab rd LHEAD.sizeof * IRQ_RESERVE / 4 |
irqh_tab rd LHEAD.sizeof * IRQ_RESERVED / 4 |
irqh_pool rd IRQH.sizeof *48 /4 |
irqh_pool rd IRQH.sizeof * IRQ_POOL_SIZE /4 |
next_irqh rd 1 |
irq_active_set rd 1 |
irq_failed rd IRQ_RESERVE |
irq_failed rd IRQ_RESERVED |
endg |
44,8 → 48,6 |
.irqh dd ? |
endl |
xchg bx, bx |
and [.irqh], 0 |
push ebx |
54,7 → 56,7 |
test ebx, ebx |
jz .err |
cmp ebx, IRQ_RESERVE |
cmp ebx, IRQ_RESERVED |
jae .err |
mov edx, [handler] |
72,9 → 74,10 |
mov eax, [ecx] |
mov [next_irqh], eax |
mov [.irqh], ecx |
mov [irq_failed+ebx*4], 0 ;clear counter |
mov eax, [user_data] |
mov [ecx+IRQH.handler], edx |
mov [ecx+IRQH.data], eax |
81,9 → 84,8 |
lea edx, [irqh_tab+ebx*8] |
list_add_tail ecx, edx ;clobber eax |
stdcall enable_irq, ebx |
stdcall enable_irq, [irq] |
.fail: |
popfd |
.err: |
116,7 → 118,6 |
endp |
macro irq_serv_h [num] { |
forward |
align 4 |
142,8 → 143,6 |
.main: |
save_ring3_context |
xchg bx, bx |
mov ebp, [esp + 32] |
mov bx, app_data ;os_data |
mov ds, bx |
212,4 → 211,15 |
add esp, 4 |
iret |
align 4 |
irqD: |
push eax |
push ecx |
xor eax,eax |
out 0xf0,al |
mov eax, 13 |
call IRQ_EOI |
pop ecx |
pop eax |
iret |
/kernel/branches/Kolibri-acpi/core/malloc.inc |
---|
20,7 → 20,7 |
; esi= nb |
; ebx= idx |
; |
align 16 |
align 4 |
malloc: |
push esi |
31,8 → 31,8 |
and esi, -8 |
add esi, 8 |
mov ebx, mst.mutex |
call wait_mutex ;ebx |
mov ecx, mst.mutex |
call mutex_lock |
cmp esi, 256 |
jae .large |
92,9 → 92,13 |
pop edi |
pop ebp |
.done: |
mov esi, eax |
mov ecx, mst.mutex |
call mutex_unlock |
mov eax, esi |
pop esi |
mov [mst.mutex], 0 |
ret |
.split: |
lea ebx, [edx+8] ;ebx=mem |
133,10 → 137,10 |
mov [edx+12], eax ; F->bk = r; |
mov [eax+8], edx ; r->fd = F; |
mov [eax+12], ecx ; r->bk = B; |
mov eax, ebx |
pop esi |
mov [mst.mutex], 0 |
ret |
jmp .done |
.small: |
; if (ms.treemap != 0 && (mem = malloc_small(nb)) != 0) |
150,9 → 154,8 |
call malloc_small |
test eax, eax |
jz .from_top |
pop esi |
and [mst.mutex], 0 |
ret |
jmp .done |
.large: |
; if (ms.treemap != 0 && (mem = malloc_large(nb)) != 0) |
189,18 → 192,15 |
mov [edx+4], eax |
mov [ecx+4], esi |
lea eax, [ecx+8] |
pop esi |
and [mst.mutex], 0 |
ret |
jmp .done |
.fail: |
xor eax, eax |
pop esi |
and [mst.mutex], 0 |
ret |
jmp .done |
; param |
; eax= mem |
align 4 |
free: |
push edi |
mov edi, eax |
211,8 → 211,8 |
test byte [edi+4], 2 |
je .fail |
mov ebx, mst.mutex |
call wait_mutex ;ebx |
mov ecx, mst.mutex |
call mutex_lock |
; psize = p->head & (~3); |
289,7 → 289,10 |
mov [mst.top], edi |
mov [edi+4], eax |
.fail2: |
and [mst.mutex], 0 |
mov esi, eax |
mov ecx, mst.mutex |
call mutex_unlock |
mov eax, esi |
pop esi |
.fail: |
pop edi |
410,13 → 413,15 |
mov [esi+8], edx ;P->fd = F |
mov [esi+12], eax ;P->bk = B |
pop esi |
and [mst.mutex], 0 |
mov ecx, mst.mutex |
call mutex_unlock |
ret |
.large: |
mov ebx, eax |
call insert_large_chunk |
pop esi |
and [mst.mutex], 0 |
mov ecx, mst.mutex |
call mutex_unlock |
ret |
1025,5 → 1030,8 |
cmp eax, mst.smallbins+512 |
jb @B |
mov ecx, mst.mutex |
call mutex_init |
ret |
/kernel/branches/Kolibri-acpi/core/memory.inc |
---|
214,30 → 214,32 |
align 4 |
commit_pages: |
push edi |
test ecx, ecx |
jz .fail |
push edi |
push eax |
push ecx |
mov ecx, pg_data.mutex |
call mutex_lock |
pop ecx |
pop eax |
mov edi, ebx |
mov ebx, pg_data.pg_mutex |
call wait_mutex ;ebx |
shr edi, 12 |
lea edi, [page_tabs+edi*4] |
@@: |
stosd |
invlpg [ebx] |
add eax, 0x1000 |
add ebx, 0x1000 |
loop @B |
mov edx, 0x1000 |
mov ebx, edi |
shr ebx, 12 |
@@: |
mov [page_tabs+ebx*4], eax |
; push eax |
invlpg [edi] |
; pop eax |
add edi, edx |
add eax, edx |
inc ebx |
dec ecx |
jnz @B |
mov [pg_data.pg_mutex],ecx |
pop edi |
mov ecx, pg_data.mutex |
call mutex_unlock |
.fail: |
pop edi |
ret |
248,16 → 250,22 |
align 4 |
release_pages: |
pushad |
mov ebx, pg_data.pg_mutex |
call wait_mutex ;ebx |
push ebp |
push esi |
push edi |
push ebx |
mov esi, eax |
mov edi, eax |
shr esi, 10 |
add esi, page_tabs |
shr esi, 12 |
lea esi, [page_tabs+esi*4] |
push ecx |
mov ecx, pg_data.mutex |
call mutex_lock |
pop ecx |
mov ebp, [pg_data.pages_free] |
mov ebx, [page_start] |
mov edx, sys_pgmap |
264,9 → 272,7 |
@@: |
xor eax, eax |
xchg eax, [esi] |
push eax |
invlpg [edi] |
pop eax |
test eax, 1 |
jz .next |
285,11 → 291,16 |
.next: |
add edi, 0x1000 |
add esi, 4 |
dec ecx |
jnz @B |
loop @B |
mov [pg_data.pages_free], ebp |
and [pg_data.pg_mutex],0 |
popad |
mov ecx, pg_data.mutex |
call mutex_unlock |
pop ebx |
pop edi |
pop esi |
pop ebp |
ret |
; param |
423,8 → 434,8 |
align 4 |
proc new_mem_resize stdcall, new_size:dword |
mov ebx, pg_data.pg_mutex |
call wait_mutex ;ebx |
mov ecx, pg_data.mutex |
call mutex_lock |
mov edi, [new_size] |
add edi,4095 |
464,8 → 475,10 |
mov ebx, [new_size] |
call update_mem_size |
mov ecx, pg_data.mutex |
call mutex_unlock |
xor eax, eax |
dec [pg_data.pg_mutex] |
ret |
.expand: |
539,9 → 552,11 |
pop edi |
pop esi |
.exit: |
mov ecx, pg_data.mutex |
call mutex_unlock |
xor eax, eax |
inc eax |
dec [pg_data.pg_mutex] |
ret |
endp |
/kernel/branches/Kolibri-acpi/core/sys32.inc |
---|
54,7 → 54,7 |
dd irq_serv.irq_22 |
dd irq_serv.irq_23 |
times 32 - IRQ_RESERVE dd unknown_interrupt |
times 32 - IRQ_RESERVED dd unknown_interrupt |
;int_0x40 gate trap (for directly copied) |
dw i40 and 0xFFFF, os_code, 11101111b shl 8, i40 shr 16 |
246,18 → 246,6 |
align 4 |
irqD: |
push eax |
xor eax,eax |
out 0xf0,al |
mov al,0x20 |
out 0xa0,al |
out 0x20,al |
pop eax |
iret |
align 4 |
set_application_table_status: |
push eax |
682,8 → 670,13 |
restore .slot |
iglobal |
if lang eq ru |
boot_sched_1 db '®§¤ ¨¥ GDT TSS 㪠§ ⥫ï',0 |
boot_sched_2 db '®§¤ ¨¥ IDT â ¡«¨æë',0 |
else |
boot_sched_1 db 'Building gdt tss pointer',0 |
boot_sched_2 db 'Building IDT table',0 |
end if |
endg |
/kernel/branches/Kolibri-acpi/core/taskman.inc |
---|
360,8 → 360,8 |
app_tabs dd ? |
endl |
mov ebx, pg_data.pg_mutex |
call wait_mutex ;ebx |
mov ecx, pg_data.mutex |
call mutex_lock |
xor eax, eax |
mov [dir_addr], eax |
480,11 → 480,13 |
.done: |
stdcall map_page,[tmp_task_pdir],dword 0,dword PG_UNMAP |
dec [pg_data.pg_mutex] |
mov ecx, pg_data.mutex |
call mutex_unlock |
mov eax, [dir_addr] |
ret |
.fail: |
dec [pg_data.pg_mutex] |
mov ecx, pg_data.mutex |
call mutex_unlock |
cmp [dir_addr], 0 |
je @f |
stdcall destroy_app_space, [dir_addr], 0 |
554,10 → 556,10 |
jg .ret |
;if there isn't threads then clear memory. |
mov esi, [dlls_list] |
call destroy_all_hdlls |
call destroy_all_hdlls ;ecx=APPDATA |
mov ebx, pg_data.pg_mutex |
call wait_mutex ;ebx |
mov ecx, pg_data.mutex |
call mutex_lock |
mov eax, [pg_dir] |
and eax, not 0xFFF |
583,7 → 585,8 |
.exit: |
stdcall map_page,[tmp_task_ptab],0,PG_UNMAP |
stdcall map_page,[tmp_task_pdir],0,PG_UNMAP |
dec [pg_data.pg_mutex] |
mov ecx, pg_data.mutex |
call mutex_unlock |
.ret: |
ret |
endp |
956,25 → 959,7 |
ret |
endp |
; param |
; ebx=mutex |
align 4 |
wait_mutex: |
;;Maxis use atomic bts for mutex 4.4.2009 |
push eax |
push ebx |
.do_wait: |
bts dword [ebx],0 |
jnc .locked |
call change_task |
jmp .do_wait |
.locked: |
pop ebx |
pop eax |
ret |
align 4 |
tls_app_entry: |
call init_heap |
/kernel/branches/Kolibri-acpi/core/timers.inc |
---|
0,0 → 1,205 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2011. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
$Revision: 2122 $ |
; Simple implementation of timers. All timers are organized in a double-linked |
; list, and the OS loop after every timer tick processes the list. |
; This structure describes a timer for the kernel. |
struct TIMER |
.Next dd ? |
.Prev dd ? |
; These fields organize a double-linked list of all timers. |
.TimerFunc dd ? |
; Function to be called when the timer is activated. |
.UserData dd ? |
; The value that is passed as is to .TimerFunc. |
.Time dd ? |
; Time at which the timer should be activated. |
.Interval dd ? |
; Interval between activations of the timer, in 0.01s. |
ends |
iglobal |
align 4 |
; The head of timer list. |
timer_list: |
dd timer_list |
dd timer_list |
endg |
uglobal |
; These two variables are used to synchronize access to the global list. |
; Logically, they form an recursive mutex. Physically, the first variable holds |
; the slot number of the current owner or 0, the second variable holds the |
; recursion count. |
; The mutex should be recursive to allow a timer function to add/delete other |
; timers or itself. |
timer_list_owner dd 0 |
timer_list_numlocks dd 0 |
; A timer function can delete any timer, including itself and the next timer in |
; the chain. To handle such situation correctly, we keep the next timer in a |
; global variable, so the removing operation can update it. |
timer_next dd 0 |
endg |
; This internal function acquires the lock for the global list. |
lock_timer_list: |
mov edx, [CURRENT_TASK] |
@@: |
xor eax, eax |
lock cmpxchg [timer_list_owner], edx |
jz @f |
cmp eax, edx |
jz @f |
call change_task |
jmp @b |
@@: |
inc [timer_list_numlocks] |
ret |
; This internal function releases the lock for the global list. |
unlock_timer_list: |
dec [timer_list_numlocks] |
jnz .nothing |
mov [timer_list_owner], 0 |
.nothing: |
ret |
; This function adds a timer. |
; If deltaStart is nonzero, the timer is activated after deltaStart hundredths |
; of seconds starting from the current time. If interval is nonzero, the timer |
; is activated every deltaWork hundredths of seconds starting from the first |
; activation. The activated timer calls timerFunc as stdcall function with one |
; argument userData. |
; Return value is NULL if something has failed or some value which is opaque |
; for the caller. Later this value can be used for cancel_timer_hs. |
proc timer_hs stdcall uses ebx, deltaStart:dword, interval:dword, \ |
timerFunc:dword, userData:dword |
; 1. Allocate memory for the TIMER structure. |
; 1a. Call the allocator. |
push sizeof.TIMER |
pop eax |
call malloc |
; 1b. If allocation failed, return (go to 5) with eax = 0. |
test eax, eax |
jz .nothing |
; 2. Setup the TIMER structure. |
xchg ebx, eax |
; 2a. Copy values from the arguments. |
mov ecx, [interval] |
mov [ebx+TIMER.Interval], ecx |
mov ecx, [timerFunc] |
mov [ebx+TIMER.TimerFunc], ecx |
mov ecx, [userData] |
mov [ebx+TIMER.UserData], ecx |
; 2b. Get time of the next activation. |
mov ecx, [deltaStart] |
test ecx, ecx |
jnz @f |
mov ecx, [interval] |
@@: |
add ecx, [timer_ticks] |
mov [ebx+TIMER.Time], ecx |
; 3. Insert the TIMER structure to the global list. |
; 3a. Acquire the lock. |
call lock_timer_list |
; 3b. Insert an item at ebx to the tail of the timer_list. |
mov eax, timer_list |
mov ecx, [eax+TIMER.Prev] |
mov [ebx+TIMER.Next], eax |
mov [ebx+TIMER.Prev], ecx |
mov [eax+TIMER.Prev], ebx |
mov [ecx+TIMER.Next], ebx |
; 3c. Release the lock. |
call unlock_timer_list |
; 4. Return with eax = pointer to TIMER structure. |
xchg ebx, eax |
.nothing: |
; 5. Returning. |
ret |
endp |
; This function removes a timer. |
; The only argument is [esp+4] = the value which was returned from timer_hs. |
cancel_timer_hs: |
push ebx ; save used register to be stdcall |
; 1. Remove the TIMER structure from the global list. |
; 1a. Acquire the lock. |
call lock_timer_list |
mov ebx, [esp+4+4] |
; 1b. Delete an item at ebx from the double-linked list. |
mov eax, [ebx+TIMER.Next] |
mov ecx, [ebx+TIMER.Prev] |
mov [eax+TIMER.Prev], ecx |
mov [ecx+TIMER.Next], eax |
; 1c. If we are removing the next timer in currently processing chain, |
; the next timer for this timer becomes new next timer. |
cmp ebx, [timer_next] |
jnz @f |
mov [timer_next], eax |
@@: |
; 1d. Release the lock. |
call unlock_timer_list |
; 2. Free the TIMER structure. |
xchg eax, ebx |
call free |
; 3. Return. |
pop ebx ; restore used register to be stdcall |
ret 4 ; purge one dword argument to be stdcall |
; This function is regularly called from osloop. It processes the global list |
; and activates the corresponding timers. |
check_timers: |
; 1. Acquire the lock. |
call lock_timer_list |
; 2. Loop over all registered timers, checking time. |
; 2a. Get the first item. |
mov eax, [timer_list+TIMER.Next] |
mov [timer_next], eax |
.loop: |
; 2b. Check for end of list. |
cmp eax, timer_list |
jz .done |
; 2c. Get and store the next timer. |
mov edx, [eax+TIMER.Next] |
mov [timer_next], edx |
; 2d. Check time for timer activation. |
; We can't just compare [timer_ticks] and [TIMER.Time], since overflows are |
; possible: if the current time is 0FFFFFFFFh ticks and timer should be |
; activated in 3 ticks, the simple comparison will produce incorrect result. |
; So we calculate the difference [timer_ticks] - [TIMER.Time]; if it is |
; non-negative, the time is over; if it is negative, then either the time is |
; not over or we have not processed this timer for 2^31 ticks, what is very |
; unlikely. |
mov edx, [timer_ticks] |
sub edx, [eax+TIMER.Time] |
js .next |
; The timer should be activated now. |
; 2e. Store the timer data in the stack. This is required since 2f can delete |
; the timer, invalidating the content. |
push [eax+TIMER.UserData] ; parameter for TimerFunc |
push [eax+TIMER.TimerFunc] ; to be restored in 2g |
; 2f. Calculate time of next activation or delete the timer if it is one-shot. |
mov ecx, [eax+TIMER.Interval] |
add [eax+TIMER.Time], ecx |
test ecx, ecx |
jnz .nodelete |
stdcall cancel_timer_hs, eax |
.nodelete: |
; 2g. Activate timer, using data from the stack. |
pop eax |
call eax |
.next: |
; 2h. Advance to the next timer and continue the loop. |
mov eax, [timer_next] |
jmp .loop |
.done: |
; 3. Release the lock. |
call unlock_timer_list |
; 4. Return. |
ret |
/kernel/branches/Kolibri-acpi/core/v86.inc |
---|
328,7 → 328,7 |
cmp edx, -1 |
jz .noirqhook |
uglobal |
v86_irqhooks rd IRQ_RESERVE * 2 |
v86_irqhooks rd IRQ_RESERVED * 2 |
endg |
cmp [v86_irqhooks+edx*8], 0 |
jz @f |
839,6 → 839,7 |
; mov byte [BOOT_VAR + 48Eh], 0FFh |
; ret |
align 4 |
v86_irq: |
; push irq/pushad/jmp v86_irq |
; eax = irq |
/kernel/branches/Kolibri-acpi/data32.inc |
---|
47,8 → 47,34 |
db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' |
if lang eq ru |
boot_fonts db 'à¨äâë § £à㦥ë',0 |
boot_memdetect db '®«¨ç¥á⢮ ®¯¥à ⨢®© ¯ ¬ïâ¨',' ',' ¡',0 |
boot_tss db 'áâ ®¢ª TSSs',0 |
boot_cpuid db '⥨¥ CPUIDs',0 |
boot_devices db '®¨áª ãáâனáâ¢',0 |
boot_timer db 'áâ ®¢ª â ©¬¥à ',0 |
boot_irqs db '¥à¥®¯à¥¤¥«¥¨¥ IRQ',0 |
boot_setmouse db 'áâ ®¢ª ¬ëè¨',0 |
boot_windefs db 'áâ ®¢ª áâ஥ª ®ª® ¯® 㬮«ç ¨î',0 |
boot_bgr db 'áâ ®¢ª ä® ',0 |
boot_resirqports db '¥§¥à¢¨à®¢ ¨¥ IRQ ¨ ¯®à⮢',0 |
boot_setrports db 'áâ ®¢ª ¤à¥á®¢ IRQ',0 |
boot_setostask db '®§¤ ¨¥ ¯à®æ¥áá ï¤à ',0 |
boot_allirqs db 'âªàë⨥ ¢á¥å IRQ',0 |
boot_tsc db '⥨¥ TSC',0 |
boot_cpufreq db ' áâ®â ¯à®æ¥áá®à ',' ',' æ',0 |
boot_pal_ega db 'áâ ®¢ª EGA/CGA 320x200 ¯ «¨âàë',0 |
boot_pal_vga db 'áâ ®¢ª VGA 640x480 ¯ «¨âàë',0 |
boot_failed db ' £à㧪 ¯¥à¢®£® ¯à¨«®¦¥¨ï ¥ 㤠« áì',0 |
boot_mtrr db 'áâ ®¢ª MTRR',0 |
if preboot_blogesc |
boot_tasking db 'ᥠ£®â®¢® ¤«ï § ¯ã᪠, ¦¬¨âॠESC ¤«ï áâ àâ ',0 |
end if |
else |
boot_fonts db 'Fonts loaded',0 |
boot_memdetect db 'Determining amount of memory',0 |
boot_fonts db 'Fonts loaded',0 |
boot_tss db 'Setting TSSs',0 |
boot_cpuid db 'Reading CPUIDs',0 |
boot_devices db 'Detecting devices',0 |
63,13 → 89,15 |
boot_pal_ega db 'Setting EGA/CGA 320x200 palette',0 |
boot_pal_vga db 'Setting VGA 640x480 palette',0 |
boot_failed db 'Failed to start first app',0 |
boot_APIC_found db 'APIC enabled', 0 |
boot_APIC_nfound db 'APIC not found', 0 |
boot_mtrr db 'Setting MTRR',0 |
if preboot_blogesc |
boot_tasking db 'All set - press ESC to start',0 |
end if |
end if |
boot_APIC_found db 'APIC enabled', 0 |
boot_APIC_nfound db 'APIC not found', 0 |
;new_process_loading db 'K : New Process - loading',13,10,0 |
;new_process_running db 'K : New Process - done',13,10,0 |
start_not_enough_memory db 'K : New Process - not enough memory',13,10,0 |
280,7 → 308,7 |
mem_block_start rd 1 |
mem_block_end rd 1 |
heap_mutex rd 1 |
heap_mutex MUTEX |
heap_size rd 1 |
heap_free rd 1 |
heap_blocks rd 1 |
/kernel/branches/Kolibri-acpi/docs/drivers_api.txt |
---|
0,0 → 1,94 |
All functions are stdcall unless mentioned otherwise. |
=== Disk === |
The kernel exports the functions 'DiskAdd', 'DiskMediaChanged', 'DiskDel' for |
drivers. They must be called in the following order: DiskAdd, then zero or |
more DiskMediaChanged, then optionally DiskDel. The driver must not call |
two functions in parallel, including two calls to DiskMediaChanged. |
void* DiskAdd(DISKFUNC* functions, const char* name, void* userdata, int flags); |
; The pointer 'functions' must be valid at least until the disk will be deleted |
; (until DISKFUNC.close is called). |
; The pointer 'name' can be invalid after this function returns. |
; It should point to ASCIIZ-string without leading '/' in latin lowercase and |
; digits, like 'usbhd0'. |
; The value 'userdata' is any pointer-sized data, passed as is to all |
; callbacks. |
DISK_NO_INSERT_NOTIFICATION = 1 |
; The bitfield 'flags' has currently only one bit defined. If it is set, the |
; driver will never call DiskMediaChanged(hDisk, true), so the kernel must scan |
; for media insertion when the operation is requested. |
struc DISKFUNC |
{ |
.strucsize dd ? |
.close dd ? |
; void close(void* userdata); |
; Optional. |
; The last function that is called for the given disk. The kernel calls it when |
; the kernel has finished all operations with the disk and it is safe to free |
; all driver-specific data identified by 'userdata'. |
.closemedia dd ? |
; void closemedia(void* userdata); |
; Optional. |
; The kernel calls this function when it finished all processing with the |
; current media. If media is removed, the driver should decline all requests |
; to that media with DISK_STATUS_NO_MEDIA, even if new media is inserted, |
; until this function is called. If media is removed, a new call to |
; DiskMediaChanged(hDisk, true) is not allowed until this function is called. |
.querymedia dd ? |
; int querymedia(void* userdata, DISKMEDIAINFO* info); |
; return value: 0 = success, otherwise = error |
.read dd ? |
; int read(void* userdata, void* buffer, __int64 startsector, |
; int* numsectors); |
; return value: 0 = success, otherwise = error |
.write dd ? |
; int write(void* userdata, const void* buffer, __int64 startsector, |
; int* numsectors); |
; Optional. |
; return value: 0 = success, otherwise = error |
.flush dd ? |
; int flush(void* userdata); |
; Optional. |
; Flushes the hardware cache, if it exists. Note that a driver should not |
; implement a software cache for read/write, since they are called from the |
; kernel cache manager. |
} |
struc DISKMEDIAINFO |
{ |
.flags dd ? |
DISK_MEDIA_READONLY = 1 |
.sectorsize dd ? |
.capacity dq ? |
} |
void DiskDel(void* hDisk); |
; This function informs the kernel that the disk should be deleted from the |
; system. This function removes the disk from the global file system; however, |
; it is possible that active operations with the disk are still running. When |
; the disk is actually removed, the kernel calls the 'close' function, which |
; can free all device-related resources. |
void DiskMediaChanged(void* hDisk, int newstate); |
; This function informs the kernel that a media has been inserted, removed or |
; changed. 'newstate' should be zero if currently there is no media inserted |
; and nonzero in the other case. This function must not be called with nonzero |
; 'newstate' from any of callbacks. This function must not be called if another |
; call to this function is active. |
=== Timers === |
Timers allow to schedule a function call to some time in the future, once |
or periodically. A timer function can do anything, including adding/removing |
other timers and itself, but it should not run time-consuming tasks, since that |
would block the processing thread for a long time; for such tasks it is |
recommended to create new thread. |
void* TimerHS(unsigned int deltaStart, unsigned int interval, |
void* timerFunc, void* userData); |
; Registers a timer which is activated in (deltaStart == 0 ? deltaStart : |
; interval) 1/100ths of second starting from the current time. If interval |
; is zero, this timer is automatically deleted when activated. Otherwise, |
; this timer will be activated every (interval) 1/100ths of second from the |
; first activation. Activated timer calls timerFunc(userData) as stdcall. |
; Returned value: NULL = failed, otherwise = timer handle which can be passed |
; to CancelTimerHS. |
void CancelTimerHS(void* hTimer); |
; Cancels previously registered timer. |
/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm |
---|
332,7 → 332,7 |
mov [esi+COM_MOUSE_DATA.MouseByteNumber],0 |
.EndMouseInterrupt: |
mov al, 1 |
ret |
;all initialized data place here |
/kernel/branches/Kolibri-acpi/drivers/sb16/sb16.asm |
---|
240,6 → 240,7 |
stdcall [callback],SB16Buffer1 ;for 64k buffer |
end if |
xor eax,eax |
not eax |
ret |
.fill_second_half: |
250,6 → 251,7 |
stdcall [callback],SB16Buffer3 ;for 64k buffer |
end if |
xor eax,eax |
not eax |
ret |
endp |
;------------------------------------------------------------------------------- |
/kernel/branches/Kolibri-acpi/drivers/vidintel.asm |
---|
0,0 → 1,465 |
; Stub of videodriver for Intel videocards. |
; (c) CleverMouse |
; When the start procedure gots control, |
; it tries to detect preferred resolution, |
; sets the detected resolution assuming 32-bpp VESA mode and exits |
; (without registering a service). |
; Detection can be overloaded with compile-time settings |
; use_predefined_mode/predefined_width/predefined_height. |
; set predefined resolution here |
use_predefined_mode = 0;1 |
predefined_width = 0;1366 |
predefined_height = 0;768 |
; standard driver stuff |
format MS COFF |
DEBUG = 1 |
include 'proc32.inc' |
include 'imports.inc' |
public START |
public version |
section '.flat' code readable align 16 |
; the start procedure (see the description above) |
START: |
; 1. Detect device. Abort if not found. |
push esi |
call DetectDevice |
test esi, esi |
jz .return0 |
; 2. Detect optimal mode unless the mode is given explicitly. Abort if failed. |
if use_predefined_mode = 0 |
call DetectMode |
end if |
cmp [width], 0 |
jz .return0_cleanup |
; 3. Set the detected mode. |
call SetMode |
; 4. Cleanup and return. |
.return0_cleanup: |
stdcall FreeKernelSpace, esi |
.return0: |
pop esi |
xor eax, eax |
ret 4 |
; check that there is Intel videocard |
; if so, map MMIO registers and set internal variables |
; esi points to MMIO block; NULL means no device |
DetectDevice: |
; 1. Sanity check: check that we are dealing with Intel videocard. |
; Integrated video device for Intel is always at PCI:0:2:0. |
xor esi, esi ; initialize return value to NULL |
; 1a. Get PCI VendorID and DeviceID. |
push esi |
push 10h |
push esi |
call PciRead32 |
; 1b. loword(eax) = ax = VendorID, hiword(eax) = DeviceID. |
; Test whether we have Intel chipset. |
cmp ax, 8086h |
jnz .return |
; 1c. Say hi including DeviceID. |
shr eax, 10h |
push edi |
pusha |
mov edi, pciid_text |
call WriteWord |
mov esi, hellomsg |
call SysMsgBoardStr |
popa |
; 1d. Test whether we know this DeviceID. |
; If this is the case, remember the position of the device in line of Intel cards; |
; this knowledge will be useful later. |
; Tested on devices with id: 8086:0046, partially 8086:2A02. |
mov ecx, pciids_num |
mov edi, pciids |
repnz scasw |
pop edi |
jnz .return_unknown_pciid |
sub ecx, pciids_num - 1 |
neg ecx |
mov [deviceType], ecx |
; 1e. Continue saying hi with positive intonation. |
pusha |
mov esi, knownmsg |
call SysMsgBoardStr |
popa |
; 2. Prepare MMIO region to control the card. |
; 2a. Read MMIO physical address from PCI config space. |
push 10h |
cmp ecx, i9xx_start |
jae @f |
mov byte [esp], 14h |
@@: |
push 10h |
push esi |
call PciRead32 |
; 2b. Mask out PCI region type, lower 4 bits. |
and al, not 0xF |
; 2c. Create virtual mapping of the physical memory. |
push 1Bh |
push 100000h |
push eax |
call MapIoMem |
; 3. Return. |
xchg esi, eax |
.return: |
ret |
; 1f. If we do not know DeviceID, continue saying hi with negative intonation. |
.return_unknown_pciid: |
pusha |
mov esi, unknownmsg |
call SysMsgBoardStr |
popa |
ret |
; Convert word in ax to hexadecimal text in edi, advance edi. |
WriteWord: |
; 1. Convert high byte. |
push eax |
mov al, ah |
call WriteByte |
pop eax |
; 2. Convert low byte. |
; Fall through to WriteByte; ret from WriteByte is ret from WriteWord too. |
; Convert byte in al to hexadecimal text in edi, advance edi. |
WriteByte: |
; 1. Convert high nibble. |
push eax |
shr al, 4 |
call WriteNibble |
pop eax |
; 2. Convert low nibble. |
and al, 0xF |
; Fall through to WriteNibble; ret from WriteNibble is ret from WriteByte too. |
; Convert nibble in al to hexadecimal text in edi, advance edi. |
WriteNibble: |
; Obvious, isn't it? |
cmp al, 10 |
sbb al, 69h |
das |
stosb |
ret |
if use_predefined_mode = 0 |
; detect resolution of the flat panel |
DetectMode: |
push esi edi |
; 1. Get the location of block of GMBUS* registers. |
; Starting with Ironlake, GMBUS* registers were moved. |
add esi, 5100h |
cmp [deviceType], ironlake_start |
jb @f |
add esi, 0xC0000 |
@@: |
; 2. Initialize GMBUS engine. |
mov edi, edid |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 80h |
loopnz @b |
jnz .fail |
mov dword [esi], 3 |
test byte [esi+8+1], 4 |
jz .noreset |
call ResetGMBus |
jnz .fail |
.noreset: |
; 3. Send read command. |
and dword [esi+20h], 0 |
mov dword [esi+4], 4E8000A1h |
; 4. Wait for data, writing to the buffer as data arrive. |
.getdata: |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 8 |
loopz @b |
test byte [esi+8+1], 4 |
jz .dataok |
call ResetGMBus |
jmp .fail |
.dataok: |
mov eax, [esi+0Ch] |
stosd |
cmp edi, edid+80h |
jb .getdata |
; 5. Wait for bus idle. |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 2 |
loopnz @b |
; 6. We got EDID; dump it if DEBUG. |
if DEBUG |
pusha |
xor ecx, ecx |
mov esi, edid |
mov edi, edid_text |
.dumploop: |
lodsb |
call WriteByte |
mov al, ' ' |
stosb |
inc cl |
test cl, 15 |
jnz @f |
mov byte [edi-1], 13 |
mov al, 10 |
stosb |
@@: |
test cl, cl |
jns .dumploop |
mov esi, edidmsg |
call SysMsgBoardStr |
popa |
end if |
; 7. Test whether EDID is good. |
; 7a. Signature: 00 FF FF FF FF FF FF 00. |
mov esi, edid |
cmp dword [esi], 0xFFFFFF00 |
jnz .fail |
cmp dword [esi+4], 0x00FFFFFF |
jnz .fail |
; 7b. Checksum must be zero. |
xor edx, edx |
mov ecx, 80h |
@@: |
lodsb |
add dl, al |
loop @b |
jnz .fail |
; 8. Get width and height from EDID. |
xor eax, eax |
mov ah, [esi-80h+3Ah] |
shr ah, 4 |
mov al, [esi-80h+38h] |
mov [width], eax |
mov ah, [esi-80h+3Dh] |
shr ah, 4 |
mov al, [esi-80h+3Bh] |
mov [height], eax |
; 9. Return. |
.fail: |
pop edi esi |
ret |
; reset bus, clear all errors |
ResetGMBus: |
; look into the PRM |
mov dword [esi+4], 80000000h |
mov dword [esi+4], 0 |
mov ecx, 0x10000 |
@@: |
test byte [esi+8+1], 2 |
loopnz @b |
ret |
end if |
; set resolution [width]*[height] |
SetMode: |
; 1. Program the registers of videocard. |
; look into the PRM |
cli |
; or byte [esi+7000Ah], 0Ch ; PIPEACONF: disable Display+Cursor Planes |
; or byte [esi+7100Ah], 0Ch ; PIPEBCONF: disable Display+Cursor Planes |
xor eax, eax |
xor edx, edx |
cmp [deviceType], i965_start |
jb @f |
mov dl, 9Ch - 84h |
@@: |
; or byte [esi+71403h], 80h ; VGACNTRL: VGA Display Disable |
and byte [esi+70080h], not 27h ; CURACNTR: disable cursor A |
mov dword [esi+70084h], eax ; CURABASE: force write to CURA* regs |
and byte [esi+700C0h], not 27h ; CURBCNTR: disable cursor B |
mov dword [esi+700C4h], eax ; CURBBASE: force write to CURB* regs |
and byte [esi+70183h], not 80h ; DSPACNTR: disable Primary A Plane |
mov dword [esi+edx+70184h], eax ; DSPALINOFF/DSPASURF: force write to DSPA* regs |
and byte [esi+71183h], not 80h ; DSPBCNTR: disable Primary B Plane |
mov dword [esi+edx+71184h], eax ; DSPBLINOFF/DSPBSURF: force write to DSPB* regs |
if 1 |
cmp [deviceType], ironlake_start |
jae .disable_pipes |
mov edx, 10000h |
or byte [esi+70024h], 2 ; PIPEASTAT: clear VBLANK status |
or byte [esi+71024h], 2 ; PIPEBSTAT: clear VBLANK status |
.wait_vblank_preironlake1: |
mov ecx, 1000h |
loop $ |
test byte [esi+7000Bh], 80h ; PIPEACONF: pipe A active? |
jz @f |
test byte [esi+70024h], 2 ; PIPEASTAT: got VBLANK? |
jz .wait_vblank_preironlake2 |
@@: |
test byte [esi+7100Bh], 80h ; PIPEBCONF: pipe B active? |
jz .disable_pipes |
test byte [esi+71024h], 2 ; PIPEBSTAT: got VBLANK? |
jnz .disable_pipes |
.wait_vblank_preironlake2: |
dec edx |
jnz .wait_vblank_preironlake1 |
jmp .not_disabled |
.disable_pipes: |
end if |
and byte [esi+7000Bh], not 80h ; PIPEACONF: disable pipe |
and byte [esi+7100Bh], not 80h ; PIPEBCONF: disable pipe |
if 1 |
mov edx, 10000h |
@@: |
mov ecx, 1000h |
loop $ |
test byte [esi+7000Bh], 40h ; PIPEACONF: wait until pipe disabled |
jz @f |
dec edx |
jnz @b |
.not_disabled: |
sti |
jmp .return |
@@: |
test byte [esi+7100Bh], 40h ; PIPEBCONF: wait until pipe disabled |
jz @f |
mov ecx, 1000h |
loop $ |
dec edx |
jnz @b |
jmp .not_disabled |
@@: |
else |
; alternative way of waiting for pipe stop, works too |
mov edx, 1000h |
.dis1: |
push dword [esi+71000h] |
push dword [esi+70000h] |
mov ecx, 10000h |
loop $ |
pop eax |
xor eax, [esi+70000h] |
and eax, 1FFFh |
pop eax |
jnz .notdis1 |
xor eax, [esi+71000h] |
and eax, 1FFFh |
jz .disabled |
.notdis1: |
dec edx |
jnz .dis1 |
.not_disabled: |
sti |
jmp .return |
.disabled: |
end if |
lea eax, [esi+61183h] |
cmp [deviceType], ironlake_start |
jb @f |
add eax, 0xE0000 - 0x60000 |
@@: |
lea edx, [esi+60000h] |
test byte [eax], 40h |
jz @f |
add edx, 1000h |
@@: |
mov eax, [width] |
dec eax |
shl eax, 16 |
mov ax, word [height] |
dec eax |
mov dword [edx+1Ch], eax ; PIPEASRC: set source image size |
ror eax, 16 |
mov dword [edx+10190h], eax ; for old cards |
mov ecx, [width] |
add ecx, 15 |
and ecx, not 15 |
shl ecx, 2 |
mov dword [edx+10188h], ecx ; DSPASTRIDE: set scanline length |
and byte [esi+61233h], not 80h ; PFIT_CONTROL: disable panel fitting |
or byte [edx+1000Bh], 80h ; PIPEACONF: enable pipe |
; and byte [edx+1000Ah], not 0Ch ; PIPEACONF: enable Display+Cursor Planes |
or byte [edx+10183h], 80h ; DSPACNTR: enable Display Plane A |
sti |
; 2. Notify the kernel that resolution has changed. |
call GetDisplay |
mov edx, [width] |
mov dword [eax+8], edx |
mov edx, [height] |
mov dword [eax+0Ch], edx |
mov [eax+18h], ecx |
mov eax, [width] |
dec eax |
dec edx |
call SetScreen |
.return: |
ret |
align 4 |
hellomsg db 'Intel videocard detected, PciId=8086:' |
pciid_text db '0000' |
db ', which is ', 0 |
knownmsg db 'known',13,10,0 |
unknownmsg db 'unknown',13,10,0 |
if DEBUG |
edidmsg db 'EDID successfully read:',13,10 |
edid_text rb 8*(16*3+1) |
db 0 |
end if |
version: |
dd 0x50005 |
width dd predefined_width |
height dd predefined_height |
pciids: |
dw 0x3577 ; i830m |
dw 0x2562 ; 845g |
dw 0x3582 ; i855gm |
i865_start = ($ - pciids) / 2 |
dw 0x2572 ; i865g |
i9xx_start = ($ - pciids) / 2 |
dw 0x2582 ; i915g |
dw 0x258a ; e7221g (i915g) |
dw 0x2592 ; i915gm |
dw 0x2772 ; i945g |
dw 0x27a2 ; i945gm |
dw 0x27ae ; i945gme |
i965_start = ($ - pciids) / 2 |
dw 0x2972 ; i946qz (i965g) |
dw 0x2982 ; g35g (i965g) |
dw 0x2992 ; i965q (i965g) |
dw 0x29a2 ; i965g |
dw 0x29b2 ; q35g |
dw 0x29c2 ; g33g |
dw 0x29d2 ; q33g |
dw 0x2a02 ; i965gm |
dw 0x2a12 ; i965gm |
dw 0x2a42 ; gm45 |
dw 0x2e02 ; g45 |
dw 0x2e12 ; g45 |
dw 0x2e22 ; g45 |
dw 0x2e32 ; g45 |
dw 0x2e42 ; g45 |
dw 0x2e92 ; g45 |
dw 0xa001 ; pineview |
dw 0xa011 ; pineview |
ironlake_start = ($ - pciids) / 2 |
dw 0x0042 ; ironlake_d |
dw 0x0046 ; ironlake_m |
dw 0x0102 ; sandybridge_d |
dw 0x0112 ; sandybridge_d |
dw 0x0122 ; sandybridge_d |
dw 0x0106 ; sandybridge_m |
dw 0x0116 ; sandybridge_m |
dw 0x0126 ; sandybridge_m |
dw 0x010A ; sandybridge_d |
pciids_num = ($ - pciids) / 2 |
align 4 |
deviceType dd ? |
edid rb 0x80 |
/kernel/branches/Kolibri-acpi/drivers/vt823x.asm |
---|
0,0 → 1,1281 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2004-2007. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
format MS COFF |
DEBUG equ 1 |
include 'proc32.inc' |
include 'imports.inc' |
API_VERSION equ 0x01000100 |
USE_COM_IRQ equ 0 ;make irq 3 and irq 4 available for PCI devices |
IRQ_REMAP equ 0 |
IRQ_LINE equ 0 |
;irq 0,1,2,8,12,13 ¥¤®áâã¯ë |
; FEDCBA9876543210 |
VALID_IRQ equ 1100111011111000b |
ATTCH_IRQ equ 0000111010100000b |
if USE_COM_IRQ |
ATTCH_IRQ equ 0000111010111000b |
end if |
CPU_FREQ equ 2600d |
BIT0 EQU 0x00000001 |
BIT1 EQU 0x00000002 |
BIT5 EQU 0x00000020 |
BIT10 EQU 0x00000400 |
VID_VIA equ 0x1106 |
CTRL_VT82C686 equ 0x3058 |
CTRL_VT8233_5 equ 0x3059 |
CODEC_MASTER_VOL_REG equ 0x02 |
CODEC_AUX_VOL equ 0x04 ; |
CODEC_PCM_OUT_REG equ 0x18 ; PCM output volume |
CODEC_EXT_AUDIO_REG equ 0x28 ; extended audio |
CODEC_EXT_AUDIO_CTRL_REG equ 0x2a ; extended audio control |
CODEC_PCM_FRONT_DACRATE_REG equ 0x2c ; PCM out sample rate |
CODEC_PCM_SURND_DACRATE_REG equ 0x2e ; surround sound sample rate |
CODEC_PCM_LFE_DACRATE_REG equ 0x30 ; LFE sample rate |
;VIA host controller registers set |
;; common offsets |
VIA_REG_OFFSET_STATUS equ 0x00 ;; byte - channel status |
VIA_REG_STAT_ACTIVE equ 0x80 ;; RO |
VIA_REG_STAT_PAUSED equ 0x40 ;; RO |
VIA_REG_STAT_TRIGGER_QUEUED equ 0x08 ;; RO |
VIA_REG_STAT_STOPPED equ 0x04 ;; RWC |
VIA_REG_STAT_EOL equ 0x02 ;; RWC |
VIA_REG_STAT_FLAG equ 0x01 ;; RWC |
VIA_REG_OFFSET_CONTROL equ 0x01 ;; byte - channel control |
VIA_REG_CTRL_START equ 0x80 ;; WO |
VIA_REG_CTRL_TERMINATE equ 0x40 ;; WO |
VIA_REG_CTRL_AUTOSTART equ 0x20 |
VIA_REG_CTRL_PAUSE equ 0x08 ;; RW |
VIA_REG_CTRL_INT_STOP equ 0x04 |
VIA_REG_CTRL_INT_EOL equ 0x02 |
VIA_REG_CTRL_INT_FLAG equ 0x01 |
VIA_REG_CTRL_RESET equ 0x01 ;; RW - probably reset? undocumented |
VIA_REG_CTRL_INT equ (VIA_REG_CTRL_INT_FLAG or \ |
VIA_REG_CTRL_INT_EOL or \ |
VIA_REG_CTRL_AUTOSTART) |
VIA_REG_OFFSET_TYPE equ 0x02 ;; byte - channel type (686 only) |
VIA_REG_TYPE_AUTOSTART equ 0x80 ;; RW - autostart at EOL |
VIA_REG_TYPE_16BIT equ 0x20 ;; RW |
VIA_REG_TYPE_STEREO equ 0x10 ;; RW |
VIA_REG_TYPE_INT_LLINE equ 0x00 |
VIA_REG_TYPE_INT_LSAMPLE equ 0x04 |
VIA_REG_TYPE_INT_LESSONE equ 0x08 |
VIA_REG_TYPE_INT_MASK equ 0x0c |
VIA_REG_TYPE_INT_EOL equ 0x02 |
VIA_REG_TYPE_INT_FLAG equ 0x01 |
VIA_REG_OFFSET_TABLE_PTR equ 0x04 ;; dword - channel table pointer |
VIA_REG_OFFSET_CURR_PTR equ 0x04 ;; dword - channel current pointer |
VIA_REG_OFFSET_STOP_IDX equ 0x08 ;; dword - stop index, channel type, sample rate |
VIA8233_REG_TYPE_16BIT equ 0x00200000 ;; RW |
VIA8233_REG_TYPE_STEREO equ 0x00100000 ;; RW |
VIA_REG_OFFSET_CURR_COUNT equ 0x0c ;; dword - channel current count (24 bit) |
VIA_REG_OFFSET_CURR_INDEX equ 0x0f ;; byte - channel current index (for via8233 only) |
VIADEV_PLAYBACK equ 0x00 |
VIADEV_CAPTURE equ 0x10 |
VIADEV_FM equ 0x20 |
;; AC'97 ;; |
VIA_REG_AC97 equ 0x80 ; dword |
VIA_REG_AC97_CODEC_ID_MASK equ 0xC0000000 ;(3<<30) |
VIA_REG_AC97_CODEC_ID_SHIFT equ 30 |
VIA_REG_AC97_CODEC_ID_PRIMARY equ 0x00 |
VIA_REG_AC97_CODEC_ID_SECONDARY equ 0x01 |
VIA_REG_AC97_SECONDARY_VALID equ 0x08000000 ;(1<<27) |
VIA_REG_AC97_PRIMARY_VALID equ 0x02000000 ;(1<<25) |
VIA_REG_AC97_BUSY equ 0x01000000 ;(1<<24) |
VIA_REG_AC97_READ equ 0x00800000 ;(1<<23) |
VIA_REG_AC97_CMD_SHIFT equ 16 |
VIA_REG_AC97_CMD_MASK equ 0x7E |
VIA_REG_AC97_DATA_SHIFT equ 0 |
VIA_REG_AC97_DATA_MASK equ 0xFFFF |
VIA_REG_SGD_SHADOW equ 0x84 ; dword |
;; via8233-specific registers ;; |
VIA_REG_OFS_PLAYBACK_VOLUME_L equ 0x02 ;; byte |
VIA_REG_OFS_PLAYBACK_VOLUME_R equ 0x03 ;; byte |
VIA_REG_OFS_MULTPLAY_FORMAT equ 0x02 ;; byte - format and channels |
VIA_REG_MULTPLAY_FMT_8BIT equ 0x00 |
VIA_REG_MULTPLAY_FMT_16BIT equ 0x80 |
VIA_REG_MULTPLAY_FMT_CH_MASK equ 0x70 ;; # channels << 4 (valid = 1,2,4,6) |
VIA_REG_OFS_CAPTURE_FIFO equ 0x02 ;; byte - bit 6 = fifo enable |
VIA_REG_CAPTURE_FIFO_ENABLE equ 0x40 |
VIA_DXS_MAX_VOLUME equ 31 ;; max. volume (attenuation) of reg 0x32/33 |
VIA_TBL_BIT_FLAG equ 0x40000000 |
VIA_TBL_BIT_EOL equ 0x80000000 |
;; pci space ;; |
VIA_ACLINK_STAT equ 0x40 |
;... |
VIA_ACLINK_C00_READY equ 0x01 ; primary codec ready |
VIA_ACLINK_CTRL equ 0x41 |
VIA_ACLINK_CTRL_ENABLE equ 0x80 ; 0: disable, 1: enable |
VIA_ACLINK_CTRL_RESET equ 0x40 ; 0: assert, 1: de-assert |
VIA_ACLINK_CTRL_SYNC equ 0x20 ; 0: release SYNC, 1: force SYNC hi |
VIA_ACLINK_CTRL_SDO equ 0x10 ; 0: release SDO, 1: force SDO hi |
VIA_ACLINK_CTRL_VRA equ 0x08 ; 0: disable VRA, 1: enable VRA |
VIA_ACLINK_CTRL_PCM equ 0x04 ; 0: disable PCM, 1: enable PCM |
VIA_ACLINK_CTRL_FM equ 0x02 ; via686 only |
VIA_ACLINK_CTRL_SB equ 0x01 ; via686 only |
VIA_ACLINK_CTRL_INIT equ (VIA_ACLINK_CTRL_ENABLE or \ |
VIA_ACLINK_CTRL_RESET or \ |
VIA_ACLINK_CTRL_PCM or \ |
VIA_ACLINK_CTRL_VRA) |
VIA_FUNC_ENABLE equ 0x42 |
VIA_FUNC_MIDI_PNP equ 0x80 ; FIXME: it's 0x40 in the datasheet! |
VIA_FUNC_MIDI_IRQMASK equ 0x40 ; FIXME: not documented! |
VIA_FUNC_RX2C_WRITE equ 0x20 |
VIA_FUNC_SB_FIFO_EMPTY equ 0x10 |
VIA_FUNC_ENABLE_GAME equ 0x08 |
VIA_FUNC_ENABLE_FM equ 0x04 |
VIA_FUNC_ENABLE_MIDI equ 0x02 |
VIA_FUNC_ENABLE_SB equ 0x01 |
VIA_PNP_CONTROL equ 0x43 |
VIA_FM_NMI_CTRL equ 0x48 |
VIA8233_VOLCHG_CTRL equ 0x48 |
VIA8233_SPDIF_CTRL equ 0x49 |
VIA8233_SPDIF_DX3 equ 0x08 |
VIA8233_SPDIF_SLOT_MASK equ 0x03 |
VIA8233_SPDIF_SLOT_1011 equ 0x00 |
VIA8233_SPDIF_SLOT_34 equ 0x01 |
VIA8233_SPDIF_SLOT_78 equ 0x02 |
VIA8233_SPDIF_SLOT_69 equ 0x03 |
;] Asper |
SRV_GETVERSION equ 0 |
DEV_PLAY equ 1 |
DEV_STOP equ 2 |
DEV_CALLBACK equ 3 |
DEV_SET_BUFF equ 4 |
DEV_NOTIFY equ 5 |
DEV_SET_MASTERVOL equ 6 |
DEV_GET_MASTERVOL equ 7 |
DEV_GET_INFO equ 8 |
struc AC_CNTRL ;AC controller base class |
{ .bus dd ? |
.devfn dd ? |
.vendor dd ? |
.dev_id dd ? |
.pci_cmd dd ? |
.pci_stat dd ? |
.codec_io_base dd ? |
.codec_mem_base dd ? |
.ctrl_io_base dd ? |
.ctrl_mem_base dd ? |
.cfg_reg dd ? |
.int_line dd ? |
.vendor_ids dd ? ;vendor id string |
.ctrl_ids dd ? ;hub id string |
.buffer dd ? |
.notify_pos dd ? |
.notify_task dd ? |
.lvi_reg dd ? |
.ctrl_setup dd ? |
.user_callback dd ? |
.codec_read16 dd ? |
.codec_write16 dd ? |
.ctrl_read8 dd ? |
.ctrl_read16 dd ? |
.ctrl_read32 dd ? |
.ctrl_write8 dd ? |
.ctrl_write16 dd ? |
.ctrl_write32 dd ? |
} |
struc CODEC ;Audio Chip base class |
{ |
.chip_id dd ? |
.flags dd ? |
.status dd ? |
.ac_vendor_ids dd ? ;ac vendor id string |
.chip_ids dd ? ;chip model string |
.shadow_flag dd ? |
dd ? |
.regs dw ? ; codec registers |
.reg_master_vol dw ? ;0x02 |
.reg_aux_out_vol dw ? ;0x04 |
.reg_mone_vol dw ? ;0x06 |
.reg_master_tone dw ? ;0x08 |
.reg_beep_vol dw ? ;0x0A |
.reg_phone_vol dw ? ;0x0C |
.reg_mic_vol dw ? ;0x0E |
.reg_line_in_vol dw ? ;0x10 |
.reg_cd_vol dw ? ;0x12 |
.reg_video_vol dw ? ;0x14 |
.reg_aux_in_vol dw ? ;0x16 |
.reg_pcm_out_vol dw ? ;0x18 |
.reg_rec_select dw ? ;0x1A |
.reg_rec_gain dw ? ;0x1C |
.reg_rec_gain_mic dw ? ;0x1E |
.reg_gen dw ? ;0x20 |
.reg_3d_ctrl dw ? ;0X22 |
.reg_page dw ? ;0X24 |
.reg_powerdown dw ? ;0x26 |
.reg_ext_audio dw ? ;0x28 |
.reg_ext_st dw ? ;0x2a |
.reg_pcm_front_rate dw ? ;0x2c |
.reg_pcm_surr_rate dw ? ;0x2e |
.reg_lfe_rate dw ? ;0x30 |
.reg_pcm_in_rate dw ? ;0x32 |
dw ? ;0x34 |
.reg_cent_lfe_vol dw ? ;0x36 |
.reg_surr_vol dw ? ;0x38 |
.reg_spdif_ctrl dw ? ;0x3A |
dw ? ;0x3C |
dw ? ;0x3E |
dw ? ;0x40 |
dw ? ;0x42 |
dw ? ;0x44 |
dw ? ;0x46 |
dw ? ;0x48 |
dw ? ;0x4A |
dw ? ;0x4C |
dw ? ;0x4E |
dw ? ;0x50 |
dw ? ;0x52 |
dw ? ;0x54 |
dw ? ;0x56 |
dw ? ;0x58 |
dw ? ;0x5A |
dw ? ;0x5C |
dw ? ;0x5E |
.reg_page_0 dw ? ;0x60 |
.reg_page_1 dw ? ;0x62 |
.reg_page_2 dw ? ;0x64 |
.reg_page_3 dw ? ;0x66 |
.reg_page_4 dw ? ;0x68 |
.reg_page_5 dw ? ;0x6A |
.reg_page_6 dw ? ;0x6C |
.reg_page_7 dw ? ;0x6E |
dw ? ;0x70 |
dw ? ;0x72 |
dw ? ;0x74 |
dw ? ;0x76 |
dw ? ;0x78 |
dw ? ;0x7A |
.reg_vendor_id_1 dw ? ;0x7C |
.reg_vendor_id_2 dw ? ;0x7E |
.reset dd ? ;virual |
.set_master_vol dd ? |
} |
struc CTRL_INFO |
{ .pci_cmd dd ? |
.irq dd ? |
.glob_cntrl dd ? |
.glob_sta dd ? |
.codec_io_base dd ? |
.ctrl_io_base dd ? |
.codec_mem_base dd ? |
.ctrl_mem_base dd ? |
.codec_id dd ? |
} |
struc IOCTL |
{ .handle dd ? |
.io_code dd ? |
.input dd ? |
.inp_size dd ? |
.output dd ? |
.out_size dd ? |
} |
virtual at 0 |
IOCTL IOCTL |
end virtual |
EVENT_NOTIFY equ 0x00000200 |
public START |
public service_proc |
public version |
section '.flat' code readable align 16 |
proc START stdcall, state:dword |
cmp [state], 1 |
jne .stop |
if DEBUG |
mov esi, msgInit |
call SysMsgBoardStr |
end if |
call detect_controller |
test eax, eax |
jz .fail |
if DEBUG |
mov esi,[ctrl.vendor_ids] |
call SysMsgBoardStr |
mov esi, [ctrl.ctrl_ids] |
call SysMsgBoardStr |
end if |
call init_controller |
test eax, eax |
jz .fail |
call init_codec |
test eax, eax |
jz .fail |
call setup_codec |
mov esi, msgPrimBuff |
call SysMsgBoardStr |
call create_primary_buff |
mov esi, msgDone |
call SysMsgBoardStr |
if IRQ_REMAP |
pushf |
cli |
mov ebx, [ctrl.int_line] |
in al, 0xA1 |
mov ah, al |
in al, 0x21 |
test ebx, ebx |
jz .skip |
bts ax, bx ;mask old line |
.skip: |
bts ax, IRQ_LINE ;mask new ine |
out 0x21, al |
mov al, ah |
out 0xA1, al |
stdcall PciWrite8, 0, 0xF8, 0x61, IRQ_LINE ;remap IRQ |
mov dx, 0x4d0 ;8259 ELCR1 |
in al, dx |
bts ax, IRQ_LINE |
out dx, al ;set level-triggered mode |
mov [ctrl.int_line], IRQ_LINE |
popf |
mov esi, msgRemap |
call SysMsgBoardStr |
end if |
mov eax, VALID_IRQ |
mov ebx, [ctrl.int_line] |
mov esi, msgInvIRQ |
bt eax, ebx |
jnc .fail_msg |
mov eax, ATTCH_IRQ |
mov esi, msgAttchIRQ |
bt eax, ebx |
jnc .fail_msg |
stdcall AttachIntHandler, ebx, ac97_irq_VIA, dword 0 |
.reg: |
stdcall RegService, sz_sound_srv, service_proc |
ret |
.fail: |
if DEBUG |
mov esi, msgFail |
call SysMsgBoardStr |
end if |
xor eax, eax |
ret |
.fail_msg: |
call SysMsgBoardStr |
xor eax, eax |
ret |
.stop: |
call stop |
xor eax, eax |
ret |
endp |
handle equ IOCTL.handle |
io_code equ IOCTL.io_code |
input equ IOCTL.input |
inp_size equ IOCTL.inp_size |
output equ IOCTL.output |
out_size equ IOCTL.out_size |
align 4 |
proc service_proc stdcall, ioctl:dword |
mov edi, [ioctl] |
mov eax, [edi+io_code] |
cmp eax, SRV_GETVERSION |
jne @F |
mov eax, [edi+output] |
cmp [edi+out_size], 4 |
jne .fail |
mov [eax], dword API_VERSION |
xor eax, eax |
ret |
@@: |
cmp eax, DEV_PLAY |
jne @F |
if DEBUG |
mov esi, msgPlay |
call SysMsgBoardStr |
end if |
call play |
ret |
@@: |
cmp eax, DEV_STOP |
jne @F |
if DEBUG |
mov esi, msgStop |
call SysMsgBoardStr |
end if |
call stop |
ret |
@@: |
cmp eax, DEV_CALLBACK |
jne @F |
mov ebx, [edi+input] |
stdcall set_callback, [ebx] |
ret |
@@: |
cmp eax, DEV_SET_MASTERVOL |
jne @F |
mov eax, [edi+input] |
mov eax, [eax] |
call set_master_vol ;eax= vol |
ret |
@@: |
cmp eax, DEV_GET_MASTERVOL |
jne @F |
mov ebx, [edi+output] |
stdcall get_master_vol, ebx |
ret |
@@: |
cmp eax, DEV_GET_INFO |
jne @F |
mov ebx, [edi+output] |
stdcall get_dev_info, ebx |
ret |
@@: |
.fail: |
or eax, -1 |
ret |
endp |
restore handle |
restore io_code |
restore input |
restore inp_size |
restore output |
restore out_size |
align 4 |
proc ac97_irq_VIA |
locals |
status db 0 |
endl |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STATUS |
call [ctrl.ctrl_read8] |
test al, VIA_REG_STAT_ACTIVE |
jz @f |
and al, VIA_REG_STAT_EOL or VIA_REG_STAT_FLAG or VIA_REG_STAT_STOPPED |
mov byte [status], al |
mov ebx, dword [buff_list] |
cmp [ctrl.user_callback], 0 |
je @f |
stdcall [ctrl.user_callback], ebx |
@@: |
mov al, byte [status] ;; ack ;; |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STATUS |
call [ctrl.ctrl_write8] |
ret |
endp |
align 4 |
proc create_primary_buff |
stdcall KernelAlloc, 0x10000 |
mov [ctrl.buffer], eax |
mov edi, eax |
mov ecx, 0x10000/4 |
xor eax, eax |
cld |
rep stosd |
mov eax, [ctrl.buffer] |
call GetPgAddr |
mov edi, pcmout_bdl |
stosd |
mov eax, 0x80004000 |
stosd |
mov edi, buff_list |
mov eax, [ctrl.buffer] |
mov ecx, 4 |
@@: |
mov [edi], eax |
mov [edi+16], eax |
mov [edi+32], eax |
mov [edi+48], eax |
mov [edi+64], eax |
mov [edi+80], eax |
mov [edi+96], eax |
mov [edi+112], eax |
;add eax, 0x4000 |
add edi, 4 |
loop @B |
stdcall channel_reset, VIADEV_PLAYBACK |
stdcall codec_check_ready |
mov eax, pcmout_bdl |
mov ebx, eax |
call GetPgAddr |
and ebx, 0xFFF |
add eax, ebx |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_TABLE_PTR |
call [ctrl.ctrl_write32] |
stdcall codec_check_ready |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFS_PLAYBACK_VOLUME_L |
mov eax, 7;31 |
call [ctrl.ctrl_write8] |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFS_PLAYBACK_VOLUME_R |
mov eax, 7;31 |
call [ctrl.ctrl_write8] |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STOP_IDX |
mov eax, VIA8233_REG_TYPE_16BIT or VIA8233_REG_TYPE_STEREO or 0xfffff or 0xff000000 |
mov [ctrl.lvi_reg], 16;0xF;eax |
call [ctrl.ctrl_write32] |
stdcall codec_check_ready |
ret |
endp |
proc channel_reset channel:dword |
mov esi, dword [channel] |
mov edx, esi |
add edx, VIA_REG_OFFSET_CONTROL |
mov eax, VIA_REG_CTRL_PAUSE or VIA_REG_CTRL_TERMINATE or VIA_REG_CTRL_RESET |
call [ctrl.ctrl_write8] |
mov edx, esi |
add edx, VIA_REG_OFFSET_CONTROL |
call [ctrl.ctrl_read8] |
mov eax, 50000 ; wait 50 ms |
call StallExec |
; disable interrupts |
mov edx, esi |
add edx, VIA_REG_OFFSET_CONTROL |
xor eax, eax |
call [ctrl.ctrl_write8] |
; clear interrupts |
mov edx, esi |
add edx, VIA_REG_OFFSET_STATUS |
mov eax, 0x03 |
call [ctrl.ctrl_write8] |
;outb(0x00, VIADEV_REG(viadev, OFFSET_TYPE)); /* for via686 */ |
; mov edx, esi ;; for via686 |
; add edx, VIA_REG_OFFSET_TYPE |
; mov eax, 0x03 |
; call [ctrl.ctrl_write8] |
;; outl(0, VIADEV_REG(viadev, OFFSET_CURR_PTR)); |
;mov edx, esi |
;add edx, VIA_REG_OFFSET_CURR_PTR |
;xor eax, eax |
;call [ctrl.ctrl_write8] |
ret |
endp |
align 4 |
proc detect_controller |
locals |
last_bus dd ? |
bus dd ? |
devfn dd ? |
endl |
xor eax, eax |
mov [bus], eax |
inc eax |
call PciApi |
cmp eax, -1 |
je .err |
mov [last_bus], eax |
.next_bus: |
and [devfn], 0 |
.next_dev: |
stdcall PciRead32, [bus], [devfn], dword 0 |
test eax, eax |
jz .next |
cmp eax, -1 |
je .next |
mov edi, devices |
@@: |
mov ebx, [edi] |
test ebx, ebx |
jz .next |
cmp eax, ebx |
je .found |
add edi, 12 |
jmp @B |
.next: |
inc [devfn] |
cmp [devfn], 256 |
jb .next_dev |
mov eax, [bus] |
inc eax |
mov [bus], eax |
cmp eax, [last_bus] |
jna .next_bus |
xor eax, eax |
ret |
.found: |
mov ebx, [bus] |
mov [ctrl.bus], ebx |
mov ecx, [devfn] |
mov [ctrl.devfn], ecx |
mov edx, eax |
and edx, 0xFFFF |
mov [ctrl.vendor], edx |
shr eax, 16 |
mov [ctrl.dev_id], eax |
mov ebx, [edi+4] |
mov [ctrl.ctrl_ids], ebx |
mov esi, [edi+8] |
mov [ctrl.ctrl_setup], esi |
cmp edx, VID_VIA |
jne @F |
mov [ctrl.vendor_ids], msg_VIA |
ret |
@@: |
.err: |
xor eax, eax |
mov [ctrl.vendor_ids], eax ;something wrong ? |
ret |
endp |
align 4 |
proc init_controller |
stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 4 |
mov ebx, eax |
and eax, 0xFFFF |
mov [ctrl.pci_cmd], eax |
shr ebx, 16 |
mov [ctrl.pci_stat], ebx |
mov esi, msgPciCmd |
call SysMsgBoardStr |
call dword2str |
call SysMsgBoardStr |
mov esi, msgPciStat |
call SysMsgBoardStr |
mov eax, [ctrl.pci_stat] |
call dword2str |
call SysMsgBoardStr |
mov esi, msgCtrlIsaIo |
call SysMsgBoardStr |
stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x10 |
call dword2str |
call SysMsgBoardStr |
and eax, 0xFFC0 |
mov [ctrl.ctrl_io_base], eax |
.default: |
stdcall PciRead32, [ctrl.bus], [ctrl.devfn], dword 0x3C |
and eax, 0xFF |
@@: |
mov [ctrl.int_line], eax |
;stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_FUNC_ENABLE ;0x42 |
;mov byte [old_legacy], al |
;stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_PNP_CONTROL ;0x43 |
;mov byte [old_legacy_cfg], al |
;mov al, VIA_FUNC_ENABLE_SB or VIA_FUNC_ENABLE_FM |
;xor al, 0xFF |
;and al, byte [old_legacy] |
;and eax, 0xFF |
;stdcall PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_FUNC_ENABLE, eax ;0x42 |
;mov byte [old_legacy], al |
call [ctrl.ctrl_setup] |
xor eax, eax |
inc eax |
ret |
endp |
align 4 |
proc set_VIA |
mov [ctrl.codec_read16], codec_io_r16 ;virtual |
mov [ctrl.codec_write16], codec_io_w16 ;virtual |
mov [ctrl.ctrl_read8 ], ctrl_io_r8 ;virtual |
mov [ctrl.ctrl_read16], ctrl_io_r16 ;virtual |
mov [ctrl.ctrl_read32], ctrl_io_r32 ;virtual |
mov [ctrl.ctrl_write8 ], ctrl_io_w8 ;virtual |
mov [ctrl.ctrl_write16], ctrl_io_w16 ;virtual |
mov [ctrl.ctrl_write32], ctrl_io_w32 ;virtual |
ret |
endp |
align 4 |
proc init_codec |
locals |
counter dd ? |
endl |
mov esi, msgControl |
call SysMsgBoardStr |
stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL |
and eax, 0xFF |
call dword2str |
call SysMsgBoardStr |
mov esi, msgStatus |
call SysMsgBoardStr |
stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT |
and eax, 0xFF |
push eax |
call dword2str |
call SysMsgBoardStr |
pop eax |
test eax, VIA_ACLINK_C00_READY |
jz .ready |
call reset_codec |
test eax, eax |
jz .err |
.ready: |
xor edx, edx ; ac_reg_0 |
call [ctrl.codec_write16] |
jmp .done |
.err: |
xor eax, eax ; timeout error |
ret |
.done: |
call detect_codec |
xor eax, eax |
inc eax |
ret |
endp |
align 4 |
proc reset_codec |
stdcall PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, \ |
VIA_ACLINK_CTRL_ENABLE or VIA_ACLINK_CTRL_RESET or VIA_ACLINK_CTRL_SYNC |
mov eax, 100000 ; wait 100 ms |
call StallExec |
.cold: |
call cold_reset |
jnc .ok |
if DEBUG |
mov esi, msgCFail |
call SysMsgBoardStr |
end if |
xor eax, eax ; timeout error |
ret |
.ok: |
if DEBUG |
mov esi, msgResetOk |
call SysMsgBoardStr |
end if |
xor eax, eax |
inc eax |
ret |
endp |
align 4 |
proc cold_reset |
locals |
counter dd ? |
endl |
stdcall PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, dword 0 |
if DEBUG |
mov esi, msgCold |
call SysMsgBoardStr |
end if |
mov eax, 100000 ; wait 100 ms ;400000 ; wait 400 ms |
call StallExec |
;; ACLink on, deassert ACLink reset, VSR, SGD data out |
;; note - FM data out has trouble with non VRA codecs !! |
stdcall PciWrite8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL, dword VIA_ACLINK_CTRL_INIT |
mov [counter], 16 ; total 20*100 ms = 2s |
.wait: |
stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT |
test eax, VIA_ACLINK_C00_READY |
jnz .ok |
mov eax, 100000 ; wait 100 ms |
call StallExec |
dec [counter] |
jnz .wait |
if DEBUG |
mov esi, msgCRFail |
call SysMsgBoardStr |
end if |
.fail: |
stc |
ret |
.ok: |
mov esi, msgControl |
call SysMsgBoardStr |
stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_CTRL |
call dword2str |
call SysMsgBoardStr |
mov esi, msgStatus |
call SysMsgBoardStr |
stdcall PciRead8, [ctrl.bus], [ctrl.devfn], dword VIA_ACLINK_STAT |
and eax, 0xFF |
push eax |
call dword2str |
call SysMsgBoardStr |
pop eax |
test eax, VIA_ACLINK_C00_READY ;CTRL_ST_CREADY |
jz .fail |
clc |
ret |
endp |
align 4 |
play: |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STOP_IDX |
mov eax, VIA8233_REG_TYPE_16BIT or VIA8233_REG_TYPE_STEREO or 0xfffff or 0xff000000 |
mov [ctrl.lvi_reg], 16 |
call [ctrl.ctrl_write32] |
mov eax, VIA_REG_CTRL_INT |
or eax, VIA_REG_CTRL_START |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_CONTROL |
call [ctrl.ctrl_write8] |
xor eax, eax |
ret |
align 4 |
stop: |
mov eax, VIA_REG_CTRL_INT |
or eax, VIA_REG_CTRL_TERMINATE |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_CONTROL |
call [ctrl.ctrl_write8] |
stdcall channel_reset, VIADEV_PLAYBACK |
xor eax, eax |
ret |
align 4 |
proc get_dev_info stdcall, p_info:dword |
virtual at esi |
CTRL_INFO CTRL_INFO |
end virtual |
mov esi, [p_info] |
mov eax, [ctrl.int_line] |
mov ecx, [ctrl.ctrl_io_base] |
mov [CTRL_INFO.irq], eax |
mov [CTRL_INFO.ctrl_io_base], ecx |
xor eax, eax |
;mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_TABLE_PTR |
;call [ctrl.ctrl_read32] |
mov [CTRL_INFO.codec_io_base], eax |
;mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STOP_IDX |
;call [ctrl.ctrl_read32] |
mov [CTRL_INFO.codec_mem_base], eax |
;mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_CURR_COUNT |
;call [ctrl.ctrl_read32] |
mov [CTRL_INFO.ctrl_mem_base], eax |
mov eax, [codec.chip_id] |
mov [CTRL_INFO.codec_id], eax |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_CONTROL |
call [ctrl.ctrl_read8] |
and eax, 0xFF |
mov [CTRL_INFO.glob_cntrl], eax |
mov edx, VIADEV_PLAYBACK +VIA_REG_OFFSET_STATUS |
call [ctrl.ctrl_read8] |
and eax, 0xFF |
mov [CTRL_INFO.glob_sta], eax |
mov ebx, [ctrl.pci_cmd] |
mov [CTRL_INFO.pci_cmd], ebx |
ret |
endp |
align 4 |
proc set_callback stdcall, handler:dword |
mov eax, [handler] |
mov [ctrl.user_callback], eax |
ret |
endp |
align 4 |
proc codec_check_ready stdcall |
locals |
counter dd ? |
endl |
mov [counter], 1000 ; total 1000*1 ms = 1s |
.wait: |
call [ctrl.codec_read16] |
test eax, VIA_REG_AC97_BUSY |
jz .ok |
mov eax, 1000 ; wait 1 ms |
call StallExec |
sub [counter] , 1 |
jnz .wait |
.err: |
mov eax, -1 |
ret |
.ok: |
and eax, 0xFFFF |
ret |
endp |
align 4 |
proc codec_valid stdcall |
stdcall codec_check_ready |
ret |
endp |
align 4 |
proc codec_read stdcall, ac_reg:dword ; reg = edx, reval = eax |
locals |
counter dd ? |
endl |
;Use only primary codec. |
mov eax, [ac_reg] |
and eax, 0x7F |
shl eax, VIA_REG_AC97_CMD_SHIFT |
or eax, VIA_REG_AC97_PRIMARY_VALID or VIA_REG_AC97_READ |
mov [counter], 3 ; total 3*20 ms = 60ms |
.wait: |
push eax |
call [ctrl.codec_write16] |
mov eax, 20000 ; wait 20 ms |
call StallExec |
stdcall codec_valid, |
cmp eax, 0 |
pop eax |
jge .ok |
sub [counter] , 1 |
jnz .wait |
jmp .err |
.ok: |
mov eax, 25000 ; wait 25 ms |
call StallExec |
call [ctrl.codec_read16] ;change edx !!! |
and eax, 0xFFFF |
ret |
.err: |
if DEBUG |
mov esi, msgCInvalid |
call SysMsgBoardStr |
end if |
mov eax, -1 ; invalid codec error |
ret |
endp |
align 4 |
proc codec_write stdcall, ac_reg:dword |
;Use only primary codec. |
mov esi, [ac_reg] |
mov edx, esi |
shl edx, VIA_REG_AC97_CMD_SHIFT |
shl eax, VIA_REG_AC97_DATA_SHIFT |
or edx, eax |
mov eax, VIA_REG_AC97_CODEC_ID_PRIMARY ;not VIA_REG_AC97_CODEC_ID_PRIMARY |
shl eax, VIA_REG_AC97_CODEC_ID_SHIFT |
or edx, eax |
mov eax, edx |
mov edx, esi |
call [ctrl.codec_write16] |
mov [codec.regs+esi], ax |
stdcall codec_check_ready |
cmp eax, 0 |
jl .err |
.ok: |
ret |
.err: |
if DEBUG |
mov esi, msgCFail |
call SysMsgBoardStr |
end if |
;mov eax, -1 ; codec not ready error |
ret |
endp |
align 4 |
proc StallExec |
push ecx |
push edx |
push ebx |
push eax |
mov ecx, CPU_FREQ |
mul ecx |
mov ebx, eax ;low |
mov ecx, edx ;high |
rdtsc |
add ebx, eax |
adc ecx, edx |
@@: |
rdtsc |
sub eax, ebx |
sbb edx, ecx |
js @B |
pop eax |
pop ebx |
pop edx |
pop ecx |
ret |
endp |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; CONTROLLER IO functions |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
align 4 |
proc codec_io_r16 ;r32 |
mov edx, [ctrl.ctrl_io_base] |
add edx, VIA_REG_AC97 |
in eax, dx |
ret |
endp |
align 4 |
proc codec_io_w16 ;w32 |
mov edx, [ctrl.ctrl_io_base] |
add edx, VIA_REG_AC97 |
out dx, eax |
ret |
endp |
align 4 |
proc ctrl_io_r8 |
add edx, [ctrl.ctrl_io_base] |
in al, dx |
ret |
endp |
align 4 |
proc ctrl_io_r16 |
add edx, [ctrl.ctrl_io_base] |
in ax, dx |
ret |
endp |
align 4 |
proc ctrl_io_r32 |
add edx, [ctrl.ctrl_io_base] |
in eax, dx |
ret |
endp |
align 4 |
proc ctrl_io_w8 |
add edx, [ctrl.ctrl_io_base] |
out dx, al |
ret |
endp |
align 4 |
proc ctrl_io_w16 |
add edx, [ctrl.ctrl_io_base] |
out dx, ax |
ret |
endp |
align 4 |
proc ctrl_io_w32 |
add edx, [ctrl.ctrl_io_base] |
out dx, eax |
ret |
endp |
align 4 |
dword2str: |
push eax ebx ecx |
mov esi, hex_buff |
mov ecx, -8 |
@@: |
rol eax, 4 |
mov ebx, eax |
and ebx, 0x0F |
mov bl, [ebx+hexletters] |
mov [8+esi+ecx], bl |
inc ecx |
jnz @B |
pop ecx ebx eax |
ret |
hexletters db '0123456789ABCDEF' |
hex_buff db 8 dup(0),13,10,0 |
include "codec.inc" |
align 4 |
devices dd (CTRL_VT82C686 shl 16)+VID_VIA,msg_VT82C686,set_VIA |
dd (CTRL_VT8233_5 shl 16)+VID_VIA,msg_VT8233,set_VIA |
dd 0 ;terminator |
version dd (5 shl 16) or (API_VERSION and 0xFFFF) |
msg_VT82C686 db 'VT82C686', 13,10, 0 |
msg_VT8233 db 'VT8233', 13,10, 0 |
msg_VIA db 'VIA' , 13,10, 0 |
szKernel db 'KERNEL', 0 |
sz_sound_srv db 'SOUND',0 |
msgInit db 'detect hardware...',13,10,0 |
msgFail db 'device not found',13,10,0 |
msgAttchIRQ db 'IRQ line not supported', 13,10, 0 |
msgInvIRQ db 'IRQ line not assigned or invalid', 13,10, 0 |
msgPlay db 'start play', 13,10,0 |
msgStop db 'stop play', 13,10,0 |
;msgIRQ db 'AC97 IRQ', 13,10,0 |
;msgInitCtrl db 'init controller',13,10,0 |
;msgInitCodec db 'init codec',13,10,0 |
msgPrimBuff db 'create primary buffer ...',0 |
msgDone db 'done',13,10,0 |
msgRemap db 'Remap IRQ',13,10,0 |
;msgReg db 'set service handler',13,10,0 |
;msgOk db 'service installed',13,10,0 |
msgCold db 'cold reset',13,10,0 |
;msgWarm db 'warm reset',13,10,0 |
;msgWRFail db 'warm reset failed',13,10,0 |
msgCRFail db 'cold reset failed',13,10,0 |
msgCFail db 'codec not ready',13,10,0 |
msgCInvalid db 'codec is not valid',13,10,0 ;Asper |
msgResetOk db 'reset complete',13,10,0 |
msgStatus db 'global status ',0 |
msgControl db 'global control ',0 |
msgPciCmd db 'PCI command ',0 |
msgPciStat db 'PCI status ',0 |
msgCtrlIsaIo db 'controller io base ',0 |
;msgMixIsaIo db 'codec io base ',0 |
;msgCtrlMMIo db 'controller mmio base ',0 |
;msgMixMMIo db 'codec mmio base ',0 |
;msgIrqMap db 'AC97 irq map as ',0 |
section '.data' data readable writable align 16 |
pcmout_bdl rq 32 |
buff_list rd 32 |
codec CODEC |
ctrl AC_CNTRL |
chip_type rb 1 |
/kernel/branches/Kolibri-acpi/fs/disk.inc |
---|
0,0 → 1,1216 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
;; ;; |
;; Copyright (C) KolibriOS team 2011. All rights reserved. ;; |
;; Distributed under terms of the GNU General Public License ;; |
;; ;; |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
; ============================================================================= |
; ================================= Constants ================================= |
; ============================================================================= |
; Error codes for callback functions. |
DISK_STATUS_OK = 0 ; success |
DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable |
DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters |
DISK_STATUS_NO_MEDIA = 2 ; no media present |
DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data |
; Driver flags. Represent bits in DISK.DriverFlags. |
DISK_NO_INSERT_NOTIFICATION = 1 |
; Media flags. Represent bits in DISKMEDIAINFO.Flags. |
DISK_MEDIA_READONLY = 1 |
; If we see too many partitions, probably there is some error on the disk. |
; 256 partitions should be enough for any reasonable use. |
; Also, the same number is limiting the number of MBRs to process; if we see |
; too many MBRs, probably there is a loop in the MBR structure. |
MAX_NUM_PARTITIONS = 256 |
; ============================================================================= |
; ================================ Structures ================================= |
; ============================================================================= |
; This structure defines all callback functions for working with the physical |
; device. They are implemented by a driver. Objects with this structure reside |
; in a driver. |
struct DISKFUNC |
.strucsize dd ? |
; Size of the structure. This field is intended for possible extensions of |
; this structure. If a new function is added to this structure and a driver |
; implements an old version, the caller can detect this by checking .strucsize, |
; so the driver remains compatible. |
.close dd ? |
; The pointer to the function which frees all driver-specific resources for |
; the disk. |
; Optional, may be NULL. |
; void close(void* userdata); |
.closemedia dd ? |
; The pointer to the function which informs the driver that the kernel has |
; finished all processing with the current media. If media is removed, the |
; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA, |
; even if new media is inserted, until this function is called. If media is |
; removed, a new call to 'disk_media_changed' is not allowed until this |
; function is called. |
; Optional, may be NULL (if media is not removable). |
; void closemedia(void* userdata); |
.querymedia dd ? |
; The pointer to the function which determines capabilities of the media. |
; int querymedia(void* userdata, DISKMEDIAINFO* info); |
; Return value: one of DISK_STATUS_* |
.read dd ? |
; The pointer to the function which reads data from the device. |
; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors); |
; input: *numsectors = number of sectors to read |
; output: *numsectors = number of sectors which were successfully read |
; Return value: one of DISK_STATUS_* |
.write dd ? |
; The pointer to the function which writes data to the device. |
; Optional, may be NULL. |
; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors); |
; input: *numsectors = number of sectors to write |
; output: *numsectors = number of sectors which were successfully written |
; Return value: one of DISK_STATUS_* |
.flush dd ? |
; The pointer to the function which flushes the internal device cache. |
; Optional, may be NULL. |
; int flush(void* userdata); |
; Return value: one of DISK_STATUS_* |
; Note that read/write are called by the cache manager, so a driver should not |
; create a software cache. This function is implemented for flushing a hardware |
; cache, if it exists. |
ends |
; This structure holds an information about a media. |
; Objects with this structure are allocated by the kernel as a part of DISK |
; structure and filled by a driver in the 'querymedia' callback. |
struct DISKMEDIAINFO |
.Flags dd ? |
; Combination of DISK_MEDIA_* bits. |
.SectorSize dd ? |
; Size of the sector. |
.Capacity dq ? |
; Size of the media in sectors. |
ends |
; This structure represents a disk device and its media for the kernel. |
; This structure is allocated by the kernel in the 'disk_add' function, |
; freed in the 'disk_dereference' function. |
struct DISK |
; Fields of disk object |
.Next dd ? |
.Prev dd ? |
; All disk devices are linked in one list with these two fields. |
; Head of the list is the 'disk_list' variable. |
.Functions dd ? |
; Pointer to the 'DISKFUNC' structure with driver functions. |
.Name dd ? |
; Pointer to the string used for accesses through the global filesystem. |
.UserData dd ? |
; This field is passed to all callback functions so a driver can decide which |
; physical device is addressed. |
.DriverFlags dd ? |
; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined. |
; If it is set, the driver will never issue 'disk_media_changed' notification |
; with argument set to true, so the kernel must try to detect media during |
; requests from the file system. |
.RefCount dd ? |
; Count of active references to this structure. One reference is kept during |
; the lifetime of the structure between 'disk_add' and 'disk_del'. |
; Another reference is taken during any filesystem operation for this disk. |
; One reference is added if media is inserted. |
; The structure is destroyed when the reference count decrements to zero: |
; this usually occurs in 'disk_del', but can be delayed to the end of last |
; filesystem operation, if one is active. |
.MediaLock MUTEX |
; Lock to protect the MEDIA structure. See the description after |
; 'disk_list_mutex' for the locking strategy. |
; Fields of media object |
.MediaInserted db ? |
; 0 if media is not inserted, nonzero otherwise. |
.MediaUsed db ? |
; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is |
; nonzero, this field is nonzero too; however, when .MediaRefCount goes |
; to zero, there is some time interval during which media object is still used. |
align 4 |
; The following fields are not valid unless either .MediaInserted is nonzero |
; or they are accessed from a code which has obtained the reference when |
; .MediaInserted was nonzero. |
.MediaRefCount dd ? |
; Count of active references to the media object. One reference is kept during |
; the lifetime of the media between two calls to 'disk_media_changed'. |
; Another reference is taken during any filesystem operation for this media. |
; The callback 'closemedia' is called when the reference count decrements to |
; zero: this usually occurs in 'disk_media_changed', but can be delayed to the |
; end of last filesystem operation, if one is active. |
.MediaInfo DISKMEDIAINFO |
; This field keeps an information about the current media. |
.NumPartitions dd ? |
; Number of partitions on this media. |
.Partitions dd ? |
; Pointer to array of .NumPartitions pointers to PARTITION structures. |
ends |
; This structure represents one partition for the kernel. This is a base |
; template, the actual contents after common fields is determined by the |
; file system code for this partition. |
struct PARTITION |
.FirstSector dq ? |
; First sector of the partition. |
.Length dq ? |
; Length of the partition in sectors. |
.FSUserFunctions dd ? |
; Handlers for the sysfunction 70h. This field is a pointer to the following |
; array. The first dword is a number of supported subfunctions, other dwords |
; point to handlers of corresponding subfunctions. |
; This field is 0 if file system is not recognized. |
; ...fs-specific data may follow... |
ends |
; This is an external structure, it represents an entry in the partition table. |
struct PARTITION_TABLE_ENTRY |
.Bootable db ? |
; 80h = bootable partition, 0 = non-bootable partition, other values = invalid |
.FirstHead db ? |
.FirstSector db ? |
.FirstTrack db ? |
; Coordinates of first sector in CHS. |
.Type db ? |
; Partition type, one of predefined constants. 0 = empty, several types denote |
; extended partition (see process_partition_table_entry), we are not interested |
; in other values. |
.LastHead db ? |
.LastSector db ? |
.LastTrack db ? |
; Coordinates of last sector in CHS. |
.FirstAbsSector dd ? |
; Coordinate of first sector in LBA. |
.Length dd ? |
; Length of the partition in sectors. |
ends |
; ============================================================================= |
; ================================ Global data ================================ |
; ============================================================================= |
iglobal |
; The pseudo-item for the list of all DISK structures. |
; Initialized to the empty list. |
disk_list: |
dd disk_list |
dd disk_list |
endg |
uglobal |
; This mutex guards all operations with the global list of DISK structures. |
disk_list_mutex MUTEX |
; * There are two dependent objects, a disk and a media. In the simplest case |
; disk and media are both non-removable. However, in the general case both |
; can be removed at any time, simultaneously or only media, this makes things |
; complicated. |
; * For efficiency, both disk and media objects are located in the one |
; structure named DISK. However, logically they are different. |
; * The following operations use data of disk object: adding (disk_add); |
; deleting (disk_del); filesystem (fs_lfn which eventually calls |
; dyndisk_handler or dyndisk_enum_root). |
; * The following operations use data of media object: adding/removing |
; (disk_media_changed); filesystem (fs_lfn which eventually calls |
; dyndisk_handler; dyndisk_enum_root doesn't work with media). |
; * Notifications disk_add, disk_media_changed, disk_del are synchronized |
; between themselves, this is a requirement for the driver. However, file |
; system operations are asynchronous, can be issued at any time by any |
; thread. |
; * We must prevent a situation when a filesystem operation thinks that the |
; object is still valid but in fact the notification has destroyed the |
; object. So we keep a reference counter for both disk and media and destroy |
; the object when this counter goes to zero. |
; * The driver must know when it is safe to free driver-allocated resources. |
; The object can be alive even after death notification has completed. |
; We use special callbacks to satisfy both assertions: 'close' for the disk |
; and 'closemedia' for the media. The destruction of the object includes |
; calling the corresponding callback. |
; * Each filesystem operation keeps one reference for the disk and one |
; reference for the media. Notification disk_del forces notification on the |
; media death, so the reference counter for the disk is always not less than |
; the reference counter for the media. |
; * Two operations "get the object" and "increment the reference counter" can |
; not be done simultaneously. We use a mutex to guard the consistency here. |
; It must be a part of the container for the object, so that this mutex can |
; be acquired as a part of getting the object from the container. The |
; container for disk object is the global list, and this list is guarded by |
; 'disk_list_mutex'. The container for media object is the disk object, and |
; the corresponding mutex is DISK.MediaLock. |
; * Notifications do not change the data of objects, they can only remove |
; objects. Thus we don't need another synchronization at this level. If two |
; filesystem operations are referencing the same filesystem data, this is |
; better resolved at the level of the filesystem. |
endg |
iglobal |
; The function 'disk_scan_partitions' needs two 512-byte buffers for |
; MBR and bootsectors data. It can not use the static buffers always, |
; since it can be called for two or more disks in parallel. However, this |
; case is not typical. We reserve two static 512-byte buffers and a flag |
; that these buffers are currently used. If 'disk_scan_partitions' detects that |
; the buffers are currently used, it allocates buffers from the heap. |
; The flag is implemented as a global dword variable. When the static buffers |
; are not used, the value is -1. When the static buffers are used, the value |
; is normally 0 and temporarily can become greater. The function increments |
; this value. If the resulting value is zero, it uses the buffers and |
; decrements the value when the job is done. Otherwise, it immediately |
; decrements the value and uses buffers from the heap, allocated in the |
; beginning and freed in the end. |
partition_buffer_users dd -1 |
endg |
uglobal |
; The static buffers for MBR and bootsectors data. |
align 16 |
mbr_buffer rb 512 |
bootsect_buffer rb 512 |
endg |
iglobal |
; This is the array of default implementations of driver callbacks. |
; Same as DRIVERFUNC structure except for the first field; all functions must |
; have the default implementations. |
align 4 |
disk_default_callbacks: |
dd disk_default_close |
dd disk_default_closemedia |
dd disk_default_querymedia |
dd disk_default_read |
dd disk_default_write |
dd disk_default_flush |
endg |
; ============================================================================= |
; ================================= Functions ================================= |
; ============================================================================= |
; This function registers a disk device. |
; This includes: |
; - allocating an internal structure describing this device; |
; - registering this structure in the global filesystem. |
; The function initializes the disk as if there is no media. If a media is |
; present, the function 'disk_media_changed' should be called after this |
; function succeeds. |
; Parameters: |
; [esp+4] = pointer to DISKFUNC structure with the callbacks |
; [esp+8] = pointer to name (ASCIIZ string) |
; [esp+12] = userdata to be passed to the callbacks as is. |
; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit |
; is defined. |
; Return value: |
; NULL = operation has failed |
; non-NULL = handle of the disk. This handle can be used |
; in the operations with other Disk* functions. |
; The handle is the pointer to the internal structure DISK. |
disk_add: |
push ebx esi ; save used registers to be stdcall |
; 1. Allocate the DISK structure. |
; 1a. Call the heap manager. |
push sizeof.DISK |
pop eax |
call malloc |
; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0. |
test eax, eax |
jz .nothing |
; 2. Copy disk name to the DISK structure. |
; 2a. Get length of the name, including the terminating zero. |
mov esi, [esp+8+8] ; esi = pointer to name |
push eax ; save allocated pointer to DISK |
xor eax, eax ; the argument of malloc() is in eax |
@@: |
inc eax |
cmp byte [esi+eax-1], 0 |
jnz @b |
; 2b. Call the heap manager. |
call malloc |
; 2c. Check the result. If allocation failed, go to 7. |
pop ebx ; restore allocated pointer to DISK |
test eax, eax |
jz .free |
; 2d. Store the allocated pointer to the DISK structure. |
mov [ebx+DISK.Name], eax |
; 2e. Copy the name. |
@@: |
mov dl, [esi] |
mov [eax], dl |
inc esi |
inc eax |
test dl, dl |
jnz @b |
; 3. Copy other arguments of the function to the DISK structure. |
mov eax, [esp+4+8] |
mov [ebx+DISK.Functions], eax |
mov eax, [esp+12+8] |
mov [ebx+DISK.UserData], eax |
mov eax, [esp+16+8] |
mov [ebx+DISK.DriverFlags], eax |
; 4. Initialize other fields of the DISK structure. |
; Media is not inserted, initialized state of mutex is zero, |
; reference counter is 1. |
lea ecx, [ebx+DISK.MediaLock] |
call mutex_init |
xor eax, eax |
mov dword [ebx+DISK.MediaInserted], eax |
inc eax |
mov [ebx+DISK.RefCount], eax |
; The DISK structure is initialized. |
; 5. Insert the new structure to the global list. |
; 5a. Acquire the mutex. |
mov ecx, disk_list_mutex |
call mutex_lock |
; 5b. Insert item to the tail of double-linked list. |
mov edx, disk_list |
list_add_tail ebx, edx ;ebx= new edx= list head |
; 5c. Release the mutex. |
call mutex_unlock |
; 6. Return with eax = pointer to DISK. |
xchg eax, ebx |
jmp .nothing |
.free: |
; Memory allocation for DISK structure succeeded, but for disk name failed. |
; 7. Free the DISK structure. |
xchg eax, ebx |
call free |
; 8. Return with eax = 0. |
xor eax, eax |
.nothing: |
; 9. Return. |
pop esi ebx ; restore used registers to be stdcall |
ret 16 ; purge 4 dword arguments to be stdcall |
; This function deletes a disk device from the global filesystem. |
; This includes: |
; - removing a media including all partitions; |
; - deleting this structure from the global filesystem; |
; - dereferencing the DISK structure and possibly destroying it. |
; Parameters: |
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. |
; Return value: none. |
disk_del: |
push esi ; save used registers to be stdcall |
; 1. Force media to be removed. If the media is already removed, the |
; call does nothing. |
mov esi, [esp+4+8] ; esi = handle of the disk |
stdcall disk_media_changed, esi, 0 |
; 2. Delete the structure from the global list. |
; 2a. Acquire the mutex. |
mov ecx, disk_list_mutex |
call mutex_lock |
; 2b. Delete item from double-linked list. |
mov eax, [esi+DISK.Next] |
mov edx, [esi+DISK.Prev] |
mov [eax+DISK.Prev], edx |
mov [edx+DISK.Next], eax |
; 2c. Release the mutex. |
call mutex_unlock |
; 3. The structure still has one reference created in disk_add. Remove this |
; reference. If there are no other references, disk_dereference will free the |
; structure. |
call disk_dereference |
; 4. Return. |
pop esi ; restore used registers to be stdcall |
ret 4 ; purge 1 dword argument to be stdcall |
; This is an internal function which removes a previously obtained reference |
; to the disk. If this is the last reference, this function lets the driver |
; finalize all associated data, and afterwards frees the DISK structure. |
; esi = pointer to DISK structure |
disk_dereference: |
; 1. Decrement reference counter. Use atomic operation to correctly handle |
; possible simultaneous calls. |
lock dec [esi+DISK.RefCount] |
; 2. If the result is nonzero, there are other references, so nothing to do. |
; In this case, return (go to 4). |
jnz .nothing |
; 3. If we are here, we just removed the last reference and must destroy the |
; disk object. |
; 3a. Call the driver. |
mov al, DISKFUNC.close |
stdcall disk_call_driver |
; 3b. Free the structure. |
xchg eax, esi |
call free |
; 4. Return. |
.nothing: |
ret |
; This is an internal function which removes a previously obtained reference |
; to the media. If this is the last reference, this function calls 'closemedia' |
; callback to signal the driver that the processing has finished and it is safe |
; to inform about a new media. |
; esi = pointer to DISK structure |
disk_media_dereference: |
; 1. Decrement reference counter. Use atomic operation to correctly handle |
; possible simultaneous calls. |
lock dec [esi+DISK.MediaRefCount] |
; 2. If the result is nonzero, there are other references, so nothing to do. |
; In this case, return (go to 4). |
jnz .nothing |
; 3. If we are here, we just removed the last reference and must destroy the |
; media object. |
; Note that the same place inside the DISK structure is reused for all media |
; objects, so we must guarantee that reusing does not happen while freeing. |
; Reusing is only possible when someone processes a new media. There are two |
; mutually exclusive variants: |
; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit |
; in DISK.DriverFlags is not set). In this case, we require from the driver |
; that such notification (except for the first one) can occur only after a |
; call to 'closemedia' callback. |
; * driver does not issue media insert notifications. In this case, the kernel |
; itself must sometimes check whether media is inserted. We have the flag |
; DISK.MediaUsed, visible to the kernel. This flag signals to the other parts |
; of kernel that the way is free. |
; In the first case other parts of the kernel do not use DISK.MediaUsed, so it |
; does not matter when this flag is cleared. In the second case this flag must |
; be cleared after all other actions, including call to 'closemedia'. |
; 3a. Free all partitions. |
push esi edi |
mov edi, [esi+DISK.NumPartitions] |
mov esi, [esi+DISK.Partitions] |
test edi, edi |
jz .nofree |
.freeloop: |
lodsd |
call free |
dec edi |
jnz .freeloop |
.nofree: |
pop edi esi |
; 3b. Call the driver. |
mov al, DISKFUNC.closemedia |
stdcall disk_call_driver |
; 3c. Clear the flag. |
mov [esi+DISK.MediaUsed], 0 |
.nothing: |
ret |
; This function is called by the driver and informs the kernel that the media |
; has changed. If the media is non-removable, it is called exactly once |
; immediately after 'disk_add' and once from 'disk_del'. |
; Parameters: |
; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. |
; [esp+8] = new status of the media: zero = no media, nonzero = media inserted. |
disk_media_changed: |
push ebx esi edi ; save used registers to be stdcall |
; 1. Remove the existing media, if it is present. |
mov esi, [esp+4+12] ; esi = pointer to DISK |
; 1a. Check whether it is present. Since DISK.MediaInserted is changed only |
; in this function and calls to this function are synchronized, no lock is |
; required for checking. |
cmp [esi+DISK.MediaInserted], 0 |
jz .noremove |
; We really need to remove the media. |
; 1b. Acquire mutex. |
lea ecx, [esi+DISK.MediaLock] |
call mutex_lock |
; 1c. Clear the flag. |
mov [esi+DISK.MediaInserted], 0 |
; 1d. Release mutex. |
call mutex_unlock |
; 1e. Remove the "lifetime" reference and possibly destroy the structure. |
call disk_media_dereference |
.noremove: |
; 2. Test whether there is new media. |
cmp dword [esp+8+12], 0 |
jz .noinsert |
; Yep, there is. |
; 3. Process the new media. We assume that all media fields are available to |
; use, see comments in 'disk_media_dereference' (this covers using by previous |
; media referencers) and note that calls to this function are synchronized |
; (this covers using by new media referencers). |
; 3a. Call the 'querymedia' callback. |
; .Flags are set to zero for possible future extensions. |
lea edx, [esi+DISK.MediaInfo] |
and [edx+DISKMEDIAINFO.Flags], 0 |
mov al, DISKFUNC.querymedia |
stdcall disk_call_driver, edx |
; 3b. Check the result of the callback. Abort if it failed. |
test eax, eax |
jnz .noinsert |
; 3c. Acquire the lifetime reference for the media object. |
inc [esi+DISK.MediaRefCount] |
; 3d. Scan for partitions. Ignore result; the list of partitions is valid even |
; on errors. |
call disk_scan_partitions |
; 3e. Media is inserted and available for use. |
inc [esi+DISK.MediaInserted] |
.noinsert: |
; 4. Return. |
pop edi esi ebx ; restore used registers to be stdcall |
ret 8 ; purge 2 dword arguments to be stdcall |
; This function is a thunk for all functions of a disk driver. |
; It checks whether the referenced function is implemented in the driver. |
; If so, this function jumps to the function in the driver. |
; Otherwise, it jumps to the default implementation. |
; al = offset of function in the DISKFUNC structure; |
; esi = pointer to the DISK structure; |
; stack is the same as for the corresponding function except that the |
; first parameter (void* userdata) is prepended automatically. |
disk_call_driver: |
movzx eax, al ; eax = offset of function in the DISKFUNC structure |
; 1. Prepend the first argument to the stack. |
pop ecx ; ecx = return address |
push [esi+DISK.UserData] ; add argument |
push ecx ; save return address |
; 2. Check that the required function is inside the table. If not, go to 5. |
mov ecx, [esi+DISK.Functions] |
cmp eax, [ecx+DISKFUNC.strucsize] |
jae .default |
; 3. Check that the required function is implemented. If not, go to 5. |
mov ecx, [ecx+eax] |
test ecx, ecx |
jz .default |
; 4. Jump to the required function. |
jmp ecx |
.default: |
; 5. Driver does not implement the required function; use default implementation. |
jmp dword [disk_default_callbacks+eax-4] |
; The default implementation of DISKFUNC.querymedia. |
disk_default_querymedia: |
push DISK_STATUS_INVALID_CALL |
pop eax |
ret 8 |
; The default implementation of DISKFUNC.read and DISKFUNC.write. |
disk_default_read: |
disk_default_write: |
push DISK_STATUS_INVALID_CALL |
pop eax |
ret 20 |
; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and |
; DISKFUNC.flush. |
disk_default_close: |
disk_default_closemedia: |
disk_default_flush: |
xor eax, eax |
ret 4 |
; This is an internal function called from 'disk_media_changed' when new media |
; is detected. It creates the list of partitions for the media. |
; If media is not partitioned, then the list consists of one partition which |
; covers all the media. |
; esi = pointer to the DISK structure. |
disk_scan_partitions: |
; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list. |
and [esi+DISK.NumPartitions], 0 |
and [esi+DISK.Partitions], 0 |
; 2. Currently we can work only with 512-bytes sectors. Check this restriction. |
; The only exception is 2048-bytes CD/DVD, but they are not supported yet by |
; this code. |
cmp [esi+DISK.MediaInfo.SectorSize], 512 |
jz .doscan |
DEBUGF 1,'K : sector size is %d, only 512 is supported\n',[esi+DISK.MediaInfo.SectorSize] |
ret |
.doscan: |
; 3. Acquire the buffer for MBR and bootsector tests. See the comment before |
; the 'partition_buffer_users' variable. |
mov ebx, mbr_buffer ; assume the global buffer is free |
lock inc [partition_buffer_users] |
jz .buffer_acquired ; yes, it is free |
lock dec [partition_buffer_users] ; no, we must allocate |
stdcall kernel_alloc, 1024 |
test eax, eax |
jz .nothing |
xchg eax, ebx |
.buffer_acquired: |
; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no |
; more than MAX_NUM_PARTITION times. |
; 4. Prepare things for the loop. |
; ebp will hold the sector number for current MBR/EBR. |
; [esp] will hold the sector number for current extended partition, if there |
; is one. |
; [esp+4] will hold the counter that prevents long loops. |
push ebp ; save ebp |
push MAX_NUM_PARTITIONS ; the counter of max MBRs to process |
xor ebp, ebp ; start from sector zero |
push ebp ; no extended partition yet |
.new_mbr: |
; 5. Read the current sector. |
; Note that 'read' callback operates with 64-bit sector numbers, so we must |
; push additional zero as a high dword of sector number. |
mov al, DISKFUNC.read |
push 1 |
stdcall disk_call_driver, ebx, ebp, 0, esp |
pop ecx |
; 6. If the read has failed, abort the loop. |
dec ecx |
jnz .mbr_failed |
; 7. Check the MBR/EBR signature. If it is wrong, abort the loop. |
; Soon we will access the partition table which starts at ebx+0x1BE, |
; so we can fill its address right now. If we do it now, then the addressing |
; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset. |
lea ecx, [ebx+0x1be] ; ecx -> partition table |
cmp word [ecx+0x40], 0xaa55 |
jnz .mbr_failed |
; 8. The MBR is treated differently from EBRs. For MBR we additionally need to |
; execute step 9 and possibly step 10. |
test ebp, ebp |
jnz .mbr |
; Partition table can be present or not present. In the first case, we just |
; read the MBR. In the second case, we just read the bootsector for some |
; filesystem. |
; We use the following algorithm to distinguish between these cases. |
; A. If at least one entry of the partition table is invalid, this is |
; a bootsector. See the description of 'is_partition_table_entry' for |
; definition of validity. |
; B. If all entries are empty (filesystem type field is zero) and the first |
; byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to |
; have zeros in the place of partition table. |
; C. Otherwise, this is a MBR. |
; 9. Test for MBR vs bootsector. |
; 9a. Check entries. If any is invalid, go to 10 (rule A). |
call is_partition_table_entry |
jc .notmbr |
add ecx, 10h |
call is_partition_table_entry |
jc .notmbr |
add ecx, 10h |
call is_partition_table_entry |
jc .notmbr |
add ecx, 10h |
call is_partition_table_entry |
jc .notmbr |
; 9b. Check types of the entries. If at least one is nonzero, go to 11 (rule C). |
mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type] |
or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type] |
or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type] |
or al, [ecx+PARTITION_TABLE_ENTRY.Type] |
jnz .mbr |
; 9c. Empty partition table or bootsector with many zeroes? (rule B) |
cmp byte [ebx], 0EBh |
jz .notmbr |
cmp byte [ebx], 0E9h |
jnz .mbr |
.notmbr: |
; 10. This is not MBR. The media is not partitioned. Create one partition |
; which covers all the media and abort the loop. |
stdcall disk_add_partition, 0, 0, \ |
dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4] |
jmp .done |
.mbr: |
; 11. Process all entries of the new MBR/EBR |
lea ecx, [ebx+0x1be] ; ecx -> partition table |
push 0 ; assume no extended partition |
call process_partition_table_entry |
add ecx, 10h |
call process_partition_table_entry |
add ecx, 10h |
call process_partition_table_entry |
add ecx, 10h |
call process_partition_table_entry |
pop ebp |
; 12. Test whether we found a new EBR and should continue the loop. |
; 12a. If there was no next EBR, return. |
test ebp, ebp |
jz .done |
; Ok, we have EBR. |
; 12b. EBRs addresses are relative to the start of extended partition. |
; For simplicity, just abort if an 32-bit overflow occurs; large disks |
; are most likely partitioned with GPT, not MBR scheme, since the precise |
; calculation here would increase limit just twice at the price of big |
; compatibility problems. |
pop eax ; load extended partition |
add ebp, eax |
; 12c. If extended partition has not yet started, start it. |
test eax, eax |
jnz @f |
mov eax, ebp |
@@: |
; 12c. If the limit is not exceeded, continue the loop. |
dec dword [esp] |
push eax ; store extended partition |
jnz .new_mbr |
.mbr_failed: |
.done: |
; 13. Cleanup after the loop. |
pop eax ; not important anymore |
pop eax ; not important anymore |
pop ebp ; restore ebp |
; 14. Release the buffer. |
; 14a. Test whether it is the global buffer or we have allocated it. |
cmp ebx, mbr_buffer |
jz .release_partition_buffer |
; 14b. If we have allocated it, free it. |
xchg eax, ebx |
call free |
jmp .nothing |
; 14c. Otherwise, release reference. |
.release_partition_buffer: |
lock dec [partition_buffer_users] |
.nothing: |
; 15. Return. |
ret |
; This is an internal function called from disk_scan_partitions. It checks |
; whether the entry pointed to by ecx is a valid entry of partition table. |
; The entry is valid if the first byte is 0 or 80h, the first sector plus the |
; length is less than twice the size of media. Multiplication by two is |
; required since the size mentioned in the partition table can be slightly |
; greater than the real size. |
is_partition_table_entry: |
; 1. Check .Bootable field. |
mov al, [ecx+PARTITION_TABLE_ENTRY.Bootable] |
and al, 7Fh |
jnz .invalid |
; 3. Calculate first sector + length. Note that .FirstAbsSector is relative |
; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length. |
mov eax, ebp |
xor edx, edx |
add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
adc edx, 0 |
add eax, [ecx+PARTITION_TABLE_ENTRY.Length] |
adc edx, 0 |
; 4. Divide by two. |
shr edx, 1 |
rcr eax, 1 |
; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not |
; overflow, this is bad. |
sub eax, dword [esi+DISK.MediaInfo.Capacity] |
sbb edx, dword [esi+DISK.MediaInfo.Capacity+4] |
jnc .invalid |
.valid: |
; 5. Return success: CF is cleared. |
clc |
ret |
.invalid: |
; 6. Return fail: CF is set. |
stc |
ret |
; This is an internal function called from disk_scan_partitions. It processes |
; the entry pointed to by ecx. |
; * If the entry is invalid, just ignore this entry. |
; * If the type is zero, just ignore this entry. |
; * If the type is one of types for extended partition, store the address |
; of this partition as the new MBR in [esp+4]. |
; * Otherwise, add the partition to the list of partitions for this disk. |
; We don't use the type from the entry to identify the file system; |
; fs-specific checks do this more reliably. |
process_partition_table_entry: |
; 1. Check for valid entry. If invalid, return (go to 5). |
call is_partition_table_entry |
jc .nothing |
; 2. Check for empty entry. If invalid, return (go to 5). |
mov al, [ecx+PARTITION_TABLE_ENTRY.Type] |
test al, al |
jz .nothing |
; 3. Check for extended partition. If extended, go to 6. |
irp type,\ |
0x05,\ ; DOS: extended partition |
0x0f,\ ; WIN95: extended partition, LBA-mapped |
0xc5,\ ; DRDOS/secured: extended partition |
0xd5 ; Old Multiuser DOS secured: extended partition |
{ |
cmp al, type |
jz .extended |
} |
; 4. If we are here, that is a normal partition. Add it to the list. |
; Note that the first sector is relative to MBR/EBR. |
mov eax, ebp |
xor edx, edx |
add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
adc edx, 0 |
push ecx |
stdcall disk_add_partition, eax, edx, \ |
[ecx+PARTITION_TABLE_ENTRY.Length], 0 |
pop ecx |
.nothing: |
; 5. Return. |
ret |
.extended: |
; 6. If we are here, that is an extended partition. Store the address. |
mov eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] |
mov [esp+4], eax |
ret |
; This is an internal function called from disk_scan_partitions and |
; process_partition_table_entry. It adds one partition to the list of |
; partitions for the media. |
proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword |
; 1. Check that this partition will not exceed the limit on total number. |
cmp [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS |
jae .nothing |
; 2. Check that this partition does not overlap with any already registered |
; partition. Since any file system assumes that the disk data will not change |
; outside of its control, such overlap could be destructive. |
; Since the number of partitions is usually very small and is guaranteed not |
; to be large, the simple linear search is sufficient. |
; 2a. Prepare the loop: edi will point to the current item of .Partitions |
; array, ecx will be the current item, ebx will hold number of items left. |
mov edi, [esi+DISK.Partitions] |
mov ebx, [esi+DISK.NumPartitions] |
test ebx, ebx |
jz .partitionok |
.scan_existing: |
; 2b. Get the next partition. |
mov ecx, [edi] |
add edi, 4 |
; The range [.FirstSector, .FirstSector+.Length) must be either entirely to |
; the left of [start, start+length) or entirely to the right. |
; 2c. Subtract .FirstSector - start. The possible overflow distinguish between |
; cases "to the left" (2?) and "to the right" (2d). |
mov eax, dword [ecx+PARTITION.FirstSector] |
mov edx, dword [ecx+PARTITION.FirstSector+4] |
sub eax, dword [start] |
sbb edx, dword [start+4] |
jb .less |
; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector |
; is greater than or equal to start+length; the subtraction |
; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is |
; good or to 2f in the other case. |
sub eax, dword [length] |
sbb edx, dword [length+4] |
jb .overlap |
jmp .next_existing |
.less: |
; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less |
; than or equal to start. If the addition (.FirstSector-start) + .Length does |
; not cause overflow, then .FirstSector + .Length is strictly less than start; |
; since the equality is also valid, use decrement preliminarily. Go to 2g or |
; 2f depending on the overflow. |
sub eax, 1 |
sbb edx, 0 |
add eax, dword [ecx+PARTITION.Length] |
adc edx, dword [ecx+PARTITION.Length+4] |
jnc .next_existing |
.overlap: |
; 2f. The partition overlaps with previously registered partition. Say warning |
; and return with nothing done. |
dbgstr 'two partitions overlap, ignoring the last one' |
jmp .nothing |
.next_existing: |
; 2g. The partition does not overlap with the current partition. Continue the |
; loop. |
dec ebx |
jnz .scan_existing |
.partitionok: |
; 3. The partition has passed tests. Reallocate the partitions array for a new |
; entry. |
; 3a. Call the allocator. |
mov eax, [esi+DISK.NumPartitions] |
inc eax ; one more entry |
shl eax, 2 ; each entry is dword |
call malloc |
; 3b. Test the result. If failed, return with nothing done. |
test eax, eax |
jz .nothing |
; 3c. Copy the old array to the new array. |
mov edi, eax |
push esi |
mov ecx, [esi+DISK.NumPartitions] |
mov esi, [esi+DISK.Partitions] |
rep movsd |
pop esi |
; 3d. Set the field in the DISK structure to the new array. |
xchg [esi+DISK.Partitions], eax |
; 3e. Free the old array. |
call free |
; 4. Recognize the file system. |
; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure |
; with possible filesystem-specific fields. |
call disk_detect_partition |
; 4b. Check return value. If zero, return with list not changed; so far only |
; the array was reallocated, this is ok for other code. |
test eax, eax |
jz .nothing |
; 5. Insert the new partition to the list. |
stosd |
inc [esi+DISK.NumPartitions] |
; 6. Return. |
.nothing: |
ret |
endp |
; This is an internal function called from disk_add_partition. |
; It tries to recognize the file system on the partition and allocates the |
; corresponding PARTITION structure with filesystem-specific fields. |
disk_detect_partition: |
; This function inherits the stack frame from disk_add_partition. In stdcall |
; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp |
; and [ebp+4]=return address. |
virtual at ebp+8 |
.start dq ? |
.length dq ? |
end virtual |
; Currently no file systems are supported, so just allocate the PARTITION |
; structure without extra fields. |
; 1. Allocate and check result. |
push sizeof.PARTITION |
pop eax |
call malloc |
test eax, eax |
jz .nothing |
; 2. Fill the common fields: copy .start and .length. |
mov edx, dword [.start] |
mov dword [eax+PARTITION.FirstSector], edx |
mov edx, dword [.start+4] |
mov dword [eax+PARTITION.FirstSector+4], edx |
mov edx, dword [.length] |
mov dword [eax+PARTITION.Length], edx |
mov edx, dword [.length+4] |
mov dword [eax+PARTITION.Length+4], edx |
.nothing: |
; 3. Return with eax = pointer to PARTITION or NULL. |
ret |
; This function is called from file_system_lfn. |
; This handler gets the control each time when fn 70 is called |
; with unknown item of root subdirectory. |
; in: esi -> name |
; ebp = 0 or rest of name relative to esi |
; out: if the handler processes path, it must not return in file_system_lfn, |
; but instead pop return address and return directly to the caller |
; otherwise simply return |
dyndisk_handler: |
push ebx edi ; save registers used in file_system_lfn |
; 1. Acquire the mutex. |
mov ecx, disk_list_mutex |
call mutex_lock |
; 2. Loop over the list of DISK structures. |
; 2a. Initialize. |
mov ebx, disk_list |
.scan: |
; 2b. Get the next item. |
mov ebx, [ebx+DISK.Next] |
; 2c. Check whether the list is done. If so, go to 3. |
cmp ebx, disk_list |
jz .notfound |
; 2d. Compare names. If names match, go to 5. |
mov edi, [ebx+DISK.Name] |
push esi |
@@: |
; esi points to the name from fs operation; it is terminated by zero or slash. |
lodsb |
test al, al |
jz .eoin_dec |
cmp al, '/' |
jz .eoin |
; edi points to the disk name. |
inc edi |
; edi points to lowercase name, this is a requirement for the driver. |
; Characters at esi can have any register. Lowercase the current character. |
; This lowercasing works for latin letters and digits; since the disk name |
; should not contain other symbols, this is ok. |
or al, 20h |
cmp al, [edi-1] |
jz @b |
.wrongname: |
; 2f. Names don't match. Continue the loop. |
pop esi |
jmp .scan |
.notfound: |
; The loop is done and no name matches. |
; 3. Release the mutex. |
call mutex_unlock |
; 4. Return normally. |
pop edi ebx ; restore registers used in file_system_lfn |
ret |
; part of 2d: the name matches partially, but we must check that this is full |
; equality. |
.eoin_dec: |
dec esi |
.eoin: |
cmp byte [edi], 0 |
jnz .wrongname |
; We found the addressed DISK structure. |
; 5. Reference the disk. |
lock inc [ebx+DISK.RefCount] |
; 6. Now we are sure that the DISK structure is not going to die at least |
; while we are working with it, so release the global mutex. |
call mutex_unlock |
; 7. Acquire the mutex for media object. |
pop edi ; restore edi |
lea ecx, [ebx+DISK.MediaLock] |
call mutex_lock |
; 8. Get the media object. If it is not NULL, reference it. |
xor edx, edx |
cmp [ebx+DISK.MediaInserted], dl |
jz @f |
mov edx, ebx |
inc [ebx+DISK.MediaRefCount] |
@@: |
; 9. Now we are sure that the media object, if it exists, is not going to die |
; at least while we are working with it, so release the mutex for media object. |
call mutex_unlock |
mov ecx, ebx |
pop ebx eax ; restore ebx, pop return address |
; 10. Check whether the fs operation wants to enumerate partitions (go to 11) |
; or work with some concrete partition (go to 12). |
cmp byte [esi], 0 |
jnz .haspartition |
; 11. The fs operation wants to enumerate partitions. |
; 11a. Only "list directory" operation is applicable to /<diskname> path. Check |
; the operation code. If wrong, go to 13. |
cmp dword [ebx], 1 |
jnz .access_denied |
; 11b. If the media is inserted, use 'fs_dyndisk_next' as an enumeration |
; procedure. Otherwise, use 'fs_dyndisk_next_nomedia'. |
mov esi, fs_dyndisk_next_nomedia |
test edx, edx |
jz @f |
mov esi, fs_dyndisk_next |
@@: |
; 11c. Let the procedure from fs_lfn.inc do the job. |
jmp file_system_lfn.maindir_noesi |
.haspartition: |
; 12. The fs operation has specified some partition. |
; 12a. Store parameters for callback functions. |
push edx |
push ecx |
; 12b. Store callback functions. |
push dyndisk_cleanup |
push fs_dyndisk |
mov edi, esp |
; 12c. Let the procedure from fs_lfn.inc do the job. |
jmp file_system_lfn.found2 |
.access_denied: |
; 13. Fail the operation with the appropriate code. |
mov dword [esp+32], ERROR_ACCESS_DENIED |
.cleanup: |
; 14. Cleanup. |
mov esi, ecx ; disk*dereference assume that esi points to DISK |
.cleanup_esi: |
test edx, edx ; if there are no media, we didn't reference it |
jz @f |
call disk_media_dereference |
@@: |
call disk_dereference |
; 15. Return. |
ret |
; This is a callback for cleaning up things called from file_system_lfn.found2. |
dyndisk_cleanup: |
mov esi, [edi+8] |
mov edx, [edi+12] |
jmp dyndisk_handler.cleanup_esi |
; This is a callback for enumerating partitions called from |
; file_system_lfn.maindir in the case of inserted media. |
; It just increments eax until DISK.NumPartitions reached and then |
; cleans up. |
fs_dyndisk_next: |
cmp eax, [ecx+DISK.NumPartitions] |
jae .nomore |
inc eax |
clc |
ret |
.nomore: |
pusha |
mov esi, ecx |
call disk_media_dereference |
call disk_dereference |
popa |
stc |
ret |
; This is a callback for enumerating partitions called from |
; file_system_lfn.maindir in the case of missing media. |
; In this case we create one pseudo-partition. |
fs_dyndisk_next_nomedia: |
cmp eax, 1 |
jae .nomore |
inc eax |
clc |
ret |
.nomore: |
pusha |
mov esi, ecx |
call disk_dereference |
popa |
stc |
ret |
; This is a callback for doing real work with selected partition. |
; Currently this is just placeholder, since no file systems are supported. |
; edi = esp -> {dd fs_dyndisk, dd dyndisk_cleanup, dd pointer to DISK, dd media object} |
; ecx = partition number, esi+ebp = ASCIIZ name |
fs_dyndisk: |
dec ecx ; convert to zero-based partition index |
pop edx edx edx eax ; edx = pointer to DISK, eax = NULL or edx |
test eax, eax |
jz .nomedia |
.main: |
cmp ecx, [edx+DISK.NumPartitions] |
jae .notfound |
mov dword [esp+32], ERROR_UNKNOWN_FS |
.cleanup: |
mov esi, edx |
call disk_media_dereference |
call disk_dereference |
ret |
.notfound: |
mov dword [esp+32], ERROR_FILE_NOT_FOUND |
jmp .cleanup |
.nomedia: |
test ecx, ecx |
jnz .notfound |
test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION |
jz .deverror |
; if the driver does not support insert notifications and we are the only fs |
; operation with this disk, issue the fake insert notification; if media is |
; still not inserted, 'disk_media_changed' will detect this and do nothing |
;;; push ebx |
lea ecx, [edx+DISK.MediaLock] |
call mutex_lock |
cmp [edx+DISK.MediaRefCount], 1 |
jnz .noluck |
call mutex_unlock |
push edx |
stdcall disk_media_changed, edx, 1 |
pop edx |
lea ecx, [edx+DISK.MediaLock] |
call mutex_lock |
cmp [edx+DISK.MediaInserted], 0 |
jz .noluck |
lock inc [edx+DISK.MediaRefCount] |
call mutex_unlock |
xor ecx, ecx |
jmp .main |
.noluck: |
call mutex_unlock |
.deverror: |
mov dword [esp+32], ERROR_DEVICE |
mov esi, edx |
call disk_dereference |
ret |
; This function is called from file_system_lfn. |
; This handler is called when virtual root is enumerated |
; and must return all items which can be handled by this. |
; It is called several times, first time with eax=0 |
; in: eax = 0 for first call, previously returned value for subsequent calls |
; out: eax = 0 => no more items |
; eax != 0 => buffer pointed to by edi contains name of item |
dyndisk_enum_root: |
push ebx ; save register used in file_system_lfn |
mov ecx, disk_list_mutex ; it will be useful |
; 1. If this is the first call, acquire the mutex and initialize. |
test eax, eax |
jnz .notfirst |
call mutex_lock |
mov eax, disk_list |
.notfirst: |
; 2. Get next item. |
mov eax, [eax+DISK.Next] |
; 3. If there are no more items, go to 6. |
cmp eax, disk_list |
jz .last |
; 4. Copy name from the DISK structure to edi. |
push eax esi |
mov esi, [eax+DISK.Name] |
@@: |
lodsb |
stosb |
test al, al |
jnz @b |
pop esi eax |
; 5. Return with eax = item. |
pop ebx ; restore register used in file_system_lfn |
ret |
.last: |
; 6. Release the mutex and return with eax = 0. |
call mutex_unlock |
xor eax, eax |
pop ebx ; restore register used in file_system_lfn |
ret |
/kernel/branches/Kolibri-acpi/fs/fat32.inc |
---|
60,6 → 60,7 |
ERROR_DISK_FULL = 8 |
ERROR_FAT_TABLE = 9 |
ERROR_ACCESS_DENIED = 10 |
ERROR_DEVICE = 11 |
PUSHAD_EAX equ [esp+28] |
PUSHAD_ECX equ [esp+24] |
/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc |
---|
85,6 → 85,7 |
fs_additional_handlers: |
dd biosdisk_handler, biosdisk_enum_root |
dd dyndisk_handler, dyndisk_enum_root |
; add new handlers here |
dd 0 |
383,7 → 384,8 |
.notfounda: |
cmp edi, esp |
jnz .notfound |
add esp, 8 |
call dword [edi+4] |
add esp, 16 |
jmp .notfound |
.found1: |
850,6 → 852,8 |
jmp file_system_lfn.maindir_noesi |
@@: |
push ecx |
push ecx |
push biosdisk_cleanup |
push fs_OnBd |
mov edi, esp |
jmp file_system_lfn.found2 |
858,10 → 862,11 |
cmp eax, [BiosDiskPartitions+ecx*4] |
inc eax |
cmc |
biosdisk_cleanup: |
ret |
fs_OnBd: |
pop edx edx |
pop edx edx edx edx |
; edx = disk number, ecx = partition number |
; esi+ebp = name |
call reserve_hd1 |
/kernel/branches/Kolibri-acpi/init.inc |
---|
266,7 → 266,6 |
add ebx, [pg_data.pagemap_size-OS_BASE] |
mov [page_end-OS_BASE], ebx |
mov [pg_data.pg_mutex-OS_BASE], 0 |
ret |
endp |
/kernel/branches/Kolibri-acpi/kernel.asm |
---|
321,6 → 321,12 |
mov eax, cr3 |
mov cr3, eax ; flush TLB |
mov ecx, pg_data.mutex |
call mutex_init |
mov ecx, disk_list_mutex |
call mutex_init |
; SAVE REAL MODE VARIABLES |
mov ax, [BOOT_VAR + 0x9031] |
mov [IDEContrRegsBaseAddr], ax |
713,7 → 719,11 |
call boot_log |
movzx ecx, word [boot_y] |
or ecx, (10+29*6) shl 16 ; "Determining amount of memory" |
if lang eq ru |
or ecx, (10+30*6) shl 16 |
else |
or ecx, (10+29*6) shl 16 |
end if |
sub ecx, 10 |
mov edx, 0xFFFFFF |
mov ebx, [MEM_AMOUNT] |
832,7 → 842,11 |
mov ebx, edx |
movzx ecx, word [boot_y] |
add ecx, (10+17*6) shl 16 - 10 ; 'CPU frequency is ' |
if lang eq ru |
add ecx, (10+19*6) shl 16 - 10; 'Determining amount of memory' |
else |
add ecx, (10+17*6) shl 16 - 10; 'Determining amount of memory' |
end if |
mov edx, 0xFFFFFF |
xor edi,edi |
mov eax, 0x00040000 |
2270,18 → 2284,18 |
sound_flag db 0 |
endg |
UID_NONE=0 |
UID_MENUETOS=1 ;official |
UID_KOLIBRI=2 ;russian |
iglobal |
version_inf: |
db 0,7,7,0 ; version 0.7.7.0 |
db UID_KOLIBRI |
db 0 |
dd __REV__ |
version_end: |
endg |
UID_NONE=0 |
UID_MENUETOS=1 ;official |
UID_KOLIBRI=2 ;russian |
sys_cachetodiskette: |
cmp ebx, 1 |
jne .no_floppy_a_save |
/kernel/branches/Kolibri-acpi/kernel32.inc |
---|
223,6 → 223,7 |
include "core/v86.inc" ; virtual-8086 manager |
include "core/apic.inc" ; Interrupt Controller functions |
include "core/irq.inc" ; irq handling functions |
include "core/timers.inc" |
; GUI stuff |
include "gui/window.inc" |
234,6 → 235,7 |
; file system |
include "fs/disk.inc" ; support for plug-n-play disks |
include "fs/fs.inc" ; syscall |
include "fs/fat32.inc" ; read / write for fat32 filesystem |
include "fs/ntfs.inc" ; read / write for ntfs filesystem |
/kernel/branches/Kolibri-acpi/macros.inc |
---|
20,6 → 20,9 |
struc name arg { |
} |
macro declare_sizeof xname,value |
{ sizeof.#xname = value } |
macro struct_helper name |
{ |
match xname,name |
26,7 → 29,7 |
\{ |
virtual at 0 |
xname xname |
sizeof.#xname = $ - xname |
declare_sizeof xname, $ - xname |
name equ sizeof.#xname |
end virtual |
\} |
/kernel/branches/Kolibri-acpi/network/socket.inc |
---|
54,7 → 54,7 |
.SEG_LEN dd ? ; segment length |
.SEG_WND dd ? ; segment window |
.wndsizeTimer dd ? ; window size timer |
.lock dd ? ; lock mutex |
.lock MUTEX ; lock mutex |
.rxData dd ? ; receive data buffer here |
ends |
99,6 → 99,11 |
rep stosd |
pop eax |
mov ebx, eax |
lea ecx, [eax+SOCKET.lock] |
call mutex_init |
mov eax, ebx |
; add socket to the list by changing pointers |
mov ebx, net_sockets |
push [ebx + SOCKET.NextPtr] |
703,10 → 708,10 |
or eax, eax |
jz .error |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
mov ebx, eax |
lea ecx, [eax + SOCKET.lock] |
call mutex_lock |
mov ebx, eax |
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes |
test eax, eax |
jz .error_release |
727,15 → 732,18 |
and ecx, 3 |
rep movsb |
mov [ebx + SOCKET.lock], 0 |
lea ecx, [ebx + SOCKET.lock] |
mov ebx, eax |
call mutex_unlock |
mov eax, ebx |
ret |
.error_release: |
mov [ebx + SOCKET.lock], 0 |
lea ecx, [ebx + SOCKET.lock] |
call mutex_unlock |
.error: |
xor ebx, ebx |
xor eax, eax |
ret |
endp |
756,10 → 764,11 |
or eax, eax |
jz .error |
lea ebx, [eax + SOCKET.lock] |
call wait_mutex |
mov ebx, eax |
mov ebx, eax |
lea ecx, [eax + SOCKET.lock] |
call mutex_lock |
mov eax, [ebx + SOCKET.rxDataCount] ; get count of bytes |
test eax, eax ; if count of bytes is zero.. |
jz .exit ; exit function (eax will be zero) |
789,7 → 798,9 |
rep movsb ; copy remaining bytes |
.exit: |
mov [ebx + SOCKET.lock], 0 |
lea ecx, [ebx + SOCKET.lock] |
call mutex_unlock |
mov eax, edx |
ret ; at last, exit |
.error: |
800,7 → 811,9 |
xor esi, esi |
mov [ebx + SOCKET.rxDataCount], esi ; store new count (zero) |
call .start_copy |
mov [ebx + SOCKET.lock], 0 |
lea ecx, [ebx + SOCKET.lock] |
call mutex_unlock |
mov eax, edx |
ret |
.start_copy: |
/kernel/branches/Kolibri-acpi/network/tcp.inc |
---|
963,12 → 963,10 |
jmp .exit |
.data: |
push ebx |
add ebx, SOCKET.lock |
call wait_mutex |
pop ebx |
push ecx |
lea ecx, [ebx+SOCKET.lock] |
call mutex_lock |
push ecx |
push ebx |
mov eax, [ebx + SOCKET.rxDataCount] |
add eax, ecx |
986,8 → 984,10 |
cld |
rep movsb ; copy the data across |
mov [ebx + SOCKET.lock], 0 ; release mutex |
lea ecx,[ebx + SOCKET.lock] |
call mutex_unlock |
; flag an event to the application |
pop ebx |
call signal_network_event |
1031,8 → 1031,9 |
.overflow: |
; no place in buffer |
; so simply restore stack and exit |
lea ecx, [ebx + SOCKET.lock] |
call mutex_unlock |
pop eax ecx |
mov [ebx + SOCKET.lock], 0 |
ret |
endp |