diff --git a/kernel/branches/Kolibri-acpi/const.inc b/kernel/branches/Kolibri-acpi/const.inc index 4fda41ed53..05cd449b1e 100644 --- a/kernel/branches/Kolibri-acpi/const.inc +++ b/kernel/branches/Kolibri-acpi/const.inc @@ -645,7 +645,7 @@ virtual at 0 end virtual struc MEM_STATE -{ .mutex rd 1 +{ .mutex MUTEX .smallmap rd 1 .treemap rd 1 .topsize rd 1 @@ -664,7 +664,7 @@ struc PG_DATA .kernel_pages dd ? .kernel_tables dd ? .sys_page_dir dd ? - .pg_mutex dd ? + .mutex MUTEX } ;struc LIB diff --git a/kernel/branches/Kolibri-acpi/core/apic.inc b/kernel/branches/Kolibri-acpi/core/apic.inc index bc2ef4bb0c..70c4aaf195 100644 --- a/kernel/branches/Kolibri-acpi/core/apic.inc +++ b/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 @@ APIC_init: 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 @@ APIC_init: ;init handlers table - mov ecx, IRQ_RESERVE + mov ecx, IRQ_RESERVED mov edi, irqh_tab @@: mov eax, edi diff --git a/kernel/branches/Kolibri-acpi/core/exports.inc b/kernel/branches/Kolibri-acpi/core/exports.inc index 278e36565b..e41b73b9d8 100644 --- a/kernel/branches/Kolibri-acpi/core/exports.inc +++ b/kernel/branches/Kolibri-acpi/core/exports.inc @@ -85,6 +85,13 @@ iglobal 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: diff --git a/kernel/branches/Kolibri-acpi/core/heap.inc b/kernel/branches/Kolibri-acpi/core/heap.inc index 9f7021dc57..2d6aca2c0c 100644 --- a/kernel/branches/Kolibri-acpi/core/heap.inc +++ b/kernel/branches/Kolibri-acpi/core/heap.inc @@ -151,7 +151,8 @@ proc init_kernel_heap 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 @@ proc alloc_kernel_space stdcall, size:dword 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 @@ proc alloc_kernel_space stdcall, size:dword 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 @@ proc alloc_kernel_space stdcall, size:dword 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,8 +404,9 @@ proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword 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 @@ proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword @@: 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 @@ proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword @@: 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 @@ align 4 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 @@ proc kernel_free stdcall, base:dword 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 diff --git a/kernel/branches/Kolibri-acpi/core/irq.inc b/kernel/branches/Kolibri-acpi/core/irq.inc index 29b426c69b..1353ba2cd0 100644 --- a/kernel/branches/Kolibri-acpi/core/irq.inc +++ b/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 @@ macro list_add_tail new, head 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 @@ proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword .irqh dd ? endl - xchg bx, bx - and [.irqh], 0 push ebx @@ -54,7 +56,7 @@ proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword test ebx, ebx jz .err - cmp ebx, IRQ_RESERVE + cmp ebx, IRQ_RESERVED jae .err mov edx, [handler] @@ -72,17 +74,17 @@ proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword 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 lea edx, [irqh_tab+ebx*8] list_add_tail ecx, edx ;clobber eax - - stdcall enable_irq, [irq] + stdcall enable_irq, ebx .fail: popfd @@ -116,7 +118,6 @@ proc detach_int_handler endp - macro irq_serv_h [num] { forward align 4 @@ -142,8 +143,6 @@ align 16 .main: save_ring3_context - xchg bx, bx - mov ebp, [esp + 32] mov bx, app_data ;os_data mov ds, bx @@ -212,4 +211,15 @@ align 16 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 diff --git a/kernel/branches/Kolibri-acpi/core/malloc.inc b/kernel/branches/Kolibri-acpi/core/malloc.inc index edf3ee53eb..27c44e186d 100644 --- a/kernel/branches/Kolibri-acpi/core/malloc.inc +++ b/kernel/branches/Kolibri-acpi/core/malloc.inc @@ -20,7 +20,7 @@ $Revision$ ; esi= nb ; ebx= idx ; -align 16 +align 4 malloc: push esi @@ -31,8 +31,8 @@ malloc: 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 @@ malloc: 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 @@ malloc: 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 @@ malloc: 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 @@ malloc: 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 @@ free: 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 @@ free: 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 @@ insert_chunk: 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 @@ init_malloc: cmp eax, mst.smallbins+512 jb @B + mov ecx, mst.mutex + call mutex_init + ret diff --git a/kernel/branches/Kolibri-acpi/core/memory.inc b/kernel/branches/Kolibri-acpi/core/memory.inc index 79dc7406f8..01f9aedd4c 100644 --- a/kernel/branches/Kolibri-acpi/core/memory.inc +++ b/kernel/branches/Kolibri-acpi/core/memory.inc @@ -214,30 +214,32 @@ endp align 4 commit_pages: - push edi test ecx, ecx jz .fail - mov edi, ebx - mov ebx, pg_data.pg_mutex - call wait_mutex ;ebx + push edi + push eax + push ecx + mov ecx, pg_data.mutex + call mutex_lock + pop ecx + pop eax - mov edx, 0x1000 - mov ebx, edi - shr ebx, 12 + mov edi, ebx + shr edi, 12 + lea edi, [page_tabs+edi*4] @@: - 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 -.fail: + stosd + invlpg [ebx] + add eax, 0x1000 + add ebx, 0x1000 + loop @B + pop edi + + mov ecx, pg_data.mutex + call mutex_unlock +.fail: ret @@ -248,15 +250,21 @@ commit_pages: 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] @@ -264,9 +272,7 @@ release_pages: @@: xor eax, eax xchg eax, [esi] - push eax invlpg [edi] - pop eax test eax, 1 jz .next @@ -285,11 +291,16 @@ release_pages: .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 @@ endp 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 @@ proc new_mem_resize stdcall, new_size:dword 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 @@ proc new_mem_resize stdcall, new_size:dword 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 diff --git a/kernel/branches/Kolibri-acpi/core/sys32.inc b/kernel/branches/Kolibri-acpi/core/sys32.inc index b2c51550ec..341bcca448 100644 --- a/kernel/branches/Kolibri-acpi/core/sys32.inc +++ b/kernel/branches/Kolibri-acpi/core/sys32.inc @@ -54,7 +54,7 @@ iglobal 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 @@ -245,18 +245,6 @@ show_error_parameters: restore reg_edi -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 @@ term9: 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 diff --git a/kernel/branches/Kolibri-acpi/core/taskman.inc b/kernel/branches/Kolibri-acpi/core/taskman.inc index 49ca4631f3..643424f1c3 100644 --- a/kernel/branches/Kolibri-acpi/core/taskman.inc +++ b/kernel/branches/Kolibri-acpi/core/taskman.inc @@ -360,8 +360,8 @@ proc create_app_space stdcall, app_size:dword,img_base:dword,img_size:dword 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 @@ -387,11 +387,11 @@ proc create_app_space stdcall, app_size:dword,img_base:dword,img_size:dword shr ecx, 12 mov [img_pages], ecx - if GREEDY_KERNEL +if GREEDY_KERNEL lea eax, [ecx+ebx+2] ;only image size - else +else lea eax, [eax+ebx+2] ;all requested memory - end if +end if cmp eax, [pg_data.pages_free] ja .fail @@ -480,11 +480,13 @@ end if .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 @@ proc destroy_app_space stdcall, pg_dir:dword, dlls_list:dword 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 @@ proc destroy_app_space stdcall, pg_dir:dword, dlls_list:dword .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,24 +959,6 @@ proc new_sys_threads 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: diff --git a/kernel/branches/Kolibri-acpi/core/timers.inc b/kernel/branches/Kolibri-acpi/core/timers.inc new file mode 100644 index 0000000000..43a2ea944d --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/core/v86.inc b/kernel/branches/Kolibri-acpi/core/v86.inc index 75c376af8c..04ab2c15cb 100644 --- a/kernel/branches/Kolibri-acpi/core/v86.inc +++ b/kernel/branches/Kolibri-acpi/core/v86.inc @@ -328,7 +328,7 @@ v86_start: 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 @@ end if ; mov byte [BOOT_VAR + 48Eh], 0FFh ; ret +align 4 v86_irq: ; push irq/pushad/jmp v86_irq ; eax = irq diff --git a/kernel/branches/Kolibri-acpi/data32.inc b/kernel/branches/Kolibri-acpi/data32.inc index 44c50583ab..429ff8d337 100644 --- a/kernel/branches/Kolibri-acpi/data32.inc +++ b/kernel/branches/Kolibri-acpi/data32.inc @@ -47,8 +47,34 @@ keymap_alt: db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - boot_memdetect db 'Determining amount of memory',0 + +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_tss db 'Setting TSSs',0 boot_cpuid db 'Reading CPUIDs',0 boot_devices db 'Detecting devices',0 @@ -63,12 +89,14 @@ keymap_alt: 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 @@ -280,7 +308,7 @@ mem_block_arr rd 1 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 diff --git a/kernel/branches/Kolibri-acpi/docs/drivers_api.txt b/kernel/branches/Kolibri-acpi/docs/drivers_api.txt new file mode 100644 index 0000000000..cdfe47a880 --- /dev/null +++ b/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. diff --git a/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm b/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm index 4996aae182..eb77ac3bce 100644 --- a/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm +++ b/kernel/branches/Kolibri-acpi/drivers/com_mouse.asm @@ -332,7 +332,7 @@ irq_handler: mov [esi+COM_MOUSE_DATA.MouseByteNumber],0 .EndMouseInterrupt: - + mov al, 1 ret ;all initialized data place here diff --git a/kernel/branches/Kolibri-acpi/drivers/sb16/sb16.asm b/kernel/branches/Kolibri-acpi/drivers/sb16/sb16.asm index 424c7f9583..70af1896bb 100644 --- a/kernel/branches/Kolibri-acpi/drivers/sb16/sb16.asm +++ b/kernel/branches/Kolibri-acpi/drivers/sb16/sb16.asm @@ -239,7 +239,8 @@ else if sb_buffer_size eq full_buffer stdcall [callback],SB16Buffer0 ;for 64k buffer stdcall [callback],SB16Buffer1 ;for 64k buffer end if - xor eax,eax + xor eax, eax + not eax ret .fill_second_half: @@ -249,7 +250,8 @@ else if sb_buffer_size eq full_buffer stdcall [callback],SB16Buffer2 ;for 64k buffer stdcall [callback],SB16Buffer3 ;for 64k buffer end if - xor eax,eax + xor eax, eax + not eax ret endp ;------------------------------------------------------------------------------- diff --git a/kernel/branches/Kolibri-acpi/drivers/vidintel.asm b/kernel/branches/Kolibri-acpi/drivers/vidintel.asm new file mode 100644 index 0000000000..70e898cddd --- /dev/null +++ b/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 diff --git a/kernel/branches/Kolibri-acpi/drivers/vt823x.asm b/kernel/branches/Kolibri-acpi/drivers/vt823x.asm new file mode 100644 index 0000000000..51de417386 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/kernel/branches/Kolibri-acpi/fs/disk.inc b/kernel/branches/Kolibri-acpi/fs/disk.inc new file mode 100644 index 0000000000..3dce6e3dc4 --- /dev/null +++ b/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 / 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 diff --git a/kernel/branches/Kolibri-acpi/fs/fat32.inc b/kernel/branches/Kolibri-acpi/fs/fat32.inc index b3e49f472d..c56d788f8f 100644 --- a/kernel/branches/Kolibri-acpi/fs/fat32.inc +++ b/kernel/branches/Kolibri-acpi/fs/fat32.inc @@ -60,6 +60,7 @@ ERROR_MEMORY_POINTER = 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] diff --git a/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc b/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc index 578422b65e..6142e9754d 100644 --- a/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc +++ b/kernel/branches/Kolibri-acpi/fs/fs_lfn.inc @@ -85,6 +85,7 @@ virtual_root_query: 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 @@ file_system_lfn: .notfounda: cmp edi, esp jnz .notfound - add esp, 8 + call dword [edi+4] + add esp, 16 jmp .notfound .found1: @@ -850,6 +852,8 @@ biosdisk_handler: 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 @@ fs_BdNext: 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 diff --git a/kernel/branches/Kolibri-acpi/init.inc b/kernel/branches/Kolibri-acpi/init.inc index 54ba0d6e40..6bf6bf8708 100644 --- a/kernel/branches/Kolibri-acpi/init.inc +++ b/kernel/branches/Kolibri-acpi/init.inc @@ -266,7 +266,6 @@ proc init_page_map add ebx, [pg_data.pagemap_size-OS_BASE] mov [page_end-OS_BASE], ebx - mov [pg_data.pg_mutex-OS_BASE], 0 ret endp diff --git a/kernel/branches/Kolibri-acpi/kernel.asm b/kernel/branches/Kolibri-acpi/kernel.asm index d1e59230dc..26e1a67cc8 100644 --- a/kernel/branches/Kolibri-acpi/kernel.asm +++ b/kernel/branches/Kolibri-acpi/kernel.asm @@ -321,6 +321,12 @@ high_code: 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 @@ end if 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 @@ end if 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 @@ window_minimize db 0 sound_flag db 0 endg -iglobal -version_inf: - db 0,7,7,0 ; version 0.7.7.0 - db UID_KOLIBRI - dd __REV__ -version_end: -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 0 + dd __REV__ +version_end: +endg + sys_cachetodiskette: cmp ebx, 1 jne .no_floppy_a_save diff --git a/kernel/branches/Kolibri-acpi/kernel32.inc b/kernel/branches/Kolibri-acpi/kernel32.inc index 873c2e3bc6..cbb12ad27c 100644 --- a/kernel/branches/Kolibri-acpi/kernel32.inc +++ b/kernel/branches/Kolibri-acpi/kernel32.inc @@ -223,6 +223,7 @@ include "core/string.inc" 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 @@ include "gui/button.inc" ; 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 diff --git a/kernel/branches/Kolibri-acpi/macros.inc b/kernel/branches/Kolibri-acpi/macros.inc index e99a17ec97..1a90f44f6c 100644 --- a/kernel/branches/Kolibri-acpi/macros.inc +++ b/kernel/branches/Kolibri-acpi/macros.inc @@ -20,13 +20,16 @@ macro struct name, [arg] struc name arg { } +macro declare_sizeof xname,value +{ sizeof.#xname = value } + macro struct_helper name { match xname,name \{ virtual at 0 xname xname - sizeof.#xname = $ - xname + declare_sizeof xname, $ - xname name equ sizeof.#xname end virtual \} diff --git a/kernel/branches/Kolibri-acpi/network/socket.inc b/kernel/branches/Kolibri-acpi/network/socket.inc index 37e2b8b532..9c4860e6ce 100644 --- a/kernel/branches/Kolibri-acpi/network/socket.inc +++ b/kernel/branches/Kolibri-acpi/network/socket.inc @@ -54,7 +54,7 @@ struct SOCKET .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 @@ proc net_socket_alloc stdcall uses ebx ecx edx edi 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 @@ proc socket_read stdcall 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 @@ proc socket_read stdcall 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 @@ proc socket_read_packet stdcall 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 ; if count of bytes is zero.. jz .exit ; exit function (eax will be zero) @@ -789,7 +798,9 @@ proc socket_read_packet stdcall 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 @@ proc socket_read_packet stdcall 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: diff --git a/kernel/branches/Kolibri-acpi/network/tcp.inc b/kernel/branches/Kolibri-acpi/network/tcp.inc index ae01e2ba42..862928116e 100644 --- a/kernel/branches/Kolibri-acpi/network/tcp.inc +++ b/kernel/branches/Kolibri-acpi/network/tcp.inc @@ -963,12 +963,10 @@ proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD 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,7 +984,9 @@ proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD 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 @@ -1031,8 +1031,9 @@ proc stateTCB_ESTABLISHED stdcall, sockAddr:DWORD .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