Tvůrce webu je i pro tebe! Postav třeba web. Bez grafika. Bez kodéra. Hned.
wz

Obsah / Ovladače / INT / Funkce správce přerušení

Zdrojový kód: DRIVERS\INT.ASM


Funkce správce přerušení


; -----------------------------------------------------------------------------
;                       Lock/unlock interrupt service lock
; -----------------------------------------------------------------------------
; NOTES:	Use macro INTLOCK to lock, INTUNLOCK to unlock.
; -----------------------------------------------------------------------------

; ------------- Macro - lock interrupt service

%macro		INTLOCK 0
		LOCK_Lock IntLock	; lock interrupt service
%endmacro

; ------------- Macro - unlock interrupt service

%macro		INTUNLOCK 0
		LOCK_Unlock IntLock	; unlock interrupt service
%endmacro

; ------------- Interrupt service lock

IntLock:	SPINLOCK		; interrupt service lock

; ------------- Interrupt service default jump table

IntJumpTab:

%assign	IRQN	0
%rep	INT_NUM
		dd	IRQ %+ IRQN
%assign	IRQN	IRQN+1
%endrep

; ------------- Interrupt descriptor table

IntTab:		resb	INT_NUM*INTDESC_size

Makro INTLOCK uzamkne zámek správce přerušení IntLock a umožní tak modifikaci jeho datových struktur. Před uzamknutím zámku je nutné zakázat přerušení. Makro INTUNLOCK zámek opět odemkne.

IntJumpTab je tabulka skoků na obsluhy jednotlivých přerušení.

IntTab je tabulka popisovačů přerušení.


; -----------------------------------------------------------------------------
;   Verify interrupt handler if it can be installed into specific descriptor
; -----------------------------------------------------------------------------
; INPUT:	EBX = installed interrupt handler INTHAND
;		EDX = interrupt descriptor INTDESC
; OUTPUT:	CY = handler cannot be installed
; NOTES:	This is local function for IntInstall.
; -----------------------------------------------------------------------------

; ------------- Push registers

IntInstTest:	push	eax		; push EAX

; ------------- Check if interrupt descriptor is valid

		test	byte [edx+INTDESC_Flags],INTDESC_VALID ; valid?
		jz	IntInstTest8	; interrupt descriptor is not valid

; ------------- Check handler mask of usable IRQs

		movzx	eax,byte [edx+INTDESC_IRQ] ; EAX <- IRQ number
		bt	[ebx+INTHAND_IRQMask],eax ; check IRQ mask
		jnc	IntInstTest8	; this IRQ is not enabled

; ------------- "No handlers" is suitable for all the time

		cmp	byte [edx+INTDESC_HandNum],0 ; any handlers?
		je	IntInstTest9	; no handlers, it satisfies

; ------------- Check if installed handler can be shared

		test	byte [ebx+INTHAND_Flags],INT_SHARE ; shared handler?
		jz	IntInstTest8	; cannot share

; ------------- Check if last handler can be shared

		mov	eax,[edx+LIST_Prev] ; EAX <- last handler
		test	byte [eax+INTHAND_Flags],INT_SHARE ; shared handler?
		jnz	IntInstTest9	; can be shared

; ------------- Error, handler cannot be installed

IntInstTest8:	stc			; set error flag

; ------------- Pop registers

IntInstTest9:	pop	eax		; pop EAX
		ret

Funkce IntInstTest je pomocná funkce pro funkci instalace obsluhy přerušení IntInstall. Slouží k provedení testu, zda ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX, lze nainstalovat do popisovače přerušení INTDESC, jehož adresa je funkci předána v registru EDX. Pokud ovladač nelze nainstalovat, funkce navrátí příznak chyby CY.

Funkce ověří platnost popisovače přerušení (musí mít nastaven příznak platnosti) a ověří, zda je dané přerušení povolené maskou přerušení ovladače. Není-li pro toto přerušení žádný ovladač dosud nainstalován, je instalace povolena. V případě existujícího jiného ovladače je instalace povolena pouze tehdy, má-li jak instalovaný tak i stávající ovladač povolen příznak sdílení.


; -----------------------------------------------------------------------------
;                        Verify and install IRQ handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = installed interrupt handler INTHAND
;		EDX = interrupt descriptor INTDESC
; OUTPUT:	CY = handler cannot be installed
; NOTES:	This is local function for IntInstall.
; -----------------------------------------------------------------------------

; ------------- Check if interrupt handler can be installed

IntInstInst:	call	IntInstTest	; check if handler can be installed
		jc	short IntInstInst9 ; handler cannot be installed

; ------------- Push registers

		push	eax		; push EAX

; ------------- Install handler at the end of interrupt handler chain

		mov	eax,edx		; EAX <- interrupt descriptor
		xchg	eax,ebx		; EAX <- handler, EBX <- descriptor
		call	ListLast	; install at end of handler chain
		xchg	eax,ebx		; EBX <- interrupt handler

; ------------- Count IRQ handlers

		inc	byte [edx+INTDESC_HandNum] ; inc. number of handlers

; ------------- Initialize handler

		mov	[ebx+INTHAND_IntDesc],edx ; set pointer to int. descr.
		movzx	eax,byte [edx+INTDESC_IRQ] ; EAX <- IRQ number
		mov	[ebx+INTHAND_IRQ],al ; current IRQ number

; ------------- Install private interrupt vector (here is EAX=IRQ number)

		test	byte [ebx+INTHAND_Flags],INT_PRIVATE ; private handler?
		jz	IntInstInst6	; not private handler
		push	ebx		; push EBX
		push	edx		; push EDX
		add	eax,INT_FIRST	; EAX <- interrupt number
		mov	edx,[ebx+INTHAND_Int] ; EDX <- interrupt handler
		xchg	eax,ebx		; EBX <- interrupt number
		call	SetIntGate	; set interrupt gate
		pop	edx		; pop EDX
		pop	ebx		; pop EBX

; ------------- Pop registers

IntInstInst6:	pop	eax		; pop EAX

; ------------- Activate IRQ

		btr	dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
		jnc	IntInstInst9	; should not be active
		call	IntIntAct	; activate IRQ
		clc			; flag, installation OK
IntInstInst9:	ret

Funkce IntInstInst je pomocná funkce pro funkci instalace obsluhy přerušení IntInstall. Funkce otestuje, zda je instalace ovladače přerušení povolena a pokud ano, provede jeho instalaci. Instalovaný ovladač přerušení INTHAND je funkci předán v registru EBX. Popisovač přerušení INTDESC je funkci předán v registru EDX. Pokud ovladač nelze nainstalovat, funkce navrátí příznak chyby CY.

Na začátku funkce je pomocí funkce IntInstTest ověřeno, zda je ovladač možné nainstalovat. Pokud ano, připojí se ovladač do seznamu nainstalovaných ovladačů pro dané přerušení. Inkrementuje se čítač počtu ovladačů přerušení a inicializují se položky ovladače. Jedná-li se o privátní ovladač přerušení, nainstaluje se jeho funkce jako vektor přerušení (namísto standardního vektoru přerušení). Má-li ovladač přerušení nastaven příznak aktivního přerušení, je přerušení aktivováno.


; -----------------------------------------------------------------------------
;                        Install interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler INTHAND with filled up entries
; OUTPUT:	EAX = IRQ number (or -1 on error)
;		CY = cannot install interrupt handler (no free IRQ line)
; NOTES:	It activates interrupt handler if it has active flag set.
; -----------------------------------------------------------------------------

; ------------- Push registers

IntInstall:	push	edx		; push EDX

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock interrupt service

		INTLOCK			; lock interrupt service

; ------------- Try to install into recommended IRQ

		movzx	edx,byte [ebx+INTHAND_Best] ; EDX <- best IRQ number
		cmp	dl,INT_NUM	; check valid IRQ number
		jae	IntInstall2	; IRQ number is not valid
		imul	edx,edx,INTDESC_size ; EDX <- descriptor offset
		add	edx,IntTab	; EDX <- descriptor
		call	IntInstInst	; try to install handler
		jnc	IntInstall9	; handler installed OK

; ------------- Try to install into descriptor with minimal number of handles

IntInstall2:	xor	eax,eax		; EAX <- 0, number of handlers to find
IntInstall3:	mov	edx,IntTab	; EDX <- IRQ descriptor table
IntInstall4:	cmp	al,[edx+INTDESC_HandNum] ; check number of handlers
		jne	IntInstall5	; not suitable number of handlers
		call	IntInstInst	; try to install handler
		jnc	IntInstall9	; handler installed OK
IntInstall5:	add	edx,byte INTDESC_size ; EDX <- next IRQ descriptor
                cmp	edx,IntTab+INT_NUM*INTDESC_size ; end of table?
                jb	IntInstall4	; get next entry
		test	byte [ebx+INTHAND_Flags],INT_SHARE ; shared handler?
		jz	IntInstall8	; cannot share
		inc	al		; increase number of handlers
		jnz	IntInstall3	; try next number of handlers

; ------------- Installation ERROR: unlock interrupt service

IntInstall8:	INTUNLOCK		; unlock interrupt service

; ------------- Pop registers

		popf			; pop flags
		pop	edx		; pop EDX
		xor	eax,eax		; EAX <- 0
		dec	eax		; EAX <- -1
		stc			; flag, installation ERROR
		ret

; ------------- Installation OK: unlock interrupt service

IntInstall9:	INTUNLOCK		; unlock interrupt service

; ------------- Pop registers

		popf			; pop flags
		pop	edx		; pop EDX
		movzx	eax,byte [ebx+INTHAND_IRQ] ; EAX <- IRQ number
		clc			; flag, installation OK
		ret

Funkce IntInstall nainstaluje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX, do obsluh přerušení. V případě úspěchu funkce navrací příznak NC a v registru EAX číslo přerušení, které bylo ovladači přiděleno. Pokud nebylo nalezeno vyhovující přerušení, funkce navrátí příznak chyby CY a v registru EAX navrátí -1.

Funkce se nejdříve pokusí nainstalovat ovladač přerušení pro číslo přerušení, které je udáno v popisovači přerušení jako doporučené přerušení. U sdíleného přerušení může být toto přerušení použito i v případě, že jsou pro ně již nainstalovány jiné ovladače.

Nelze-li ovladač nainstalovat pro doporučené přerušení, provede se instalace pro některé jiné přerušení, které je povoleno položkou masky povolených přerušení. Je přitom přednostně použito přerušení s nejmenším počtem nainstalovaných ovladačů.

Je-li před instalací nastaven příznak aktivity přerušení, je přerušení pro instalovaný ovladač aktivováno. Jedná-li se o privátní ovladač přerušení, je adresa jeho obslužné funkce nainstalována do tabulky vektorů přerušení. Funkce uzamyká zámek správce přerušení a proto nesmí být volána z obsluhy přerušení.


; -----------------------------------------------------------------------------
;                          Uninstall interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler
; NOTES:	It deactivates interrupt handler if it was active and then
;		   saves active bit into handler again (for later activation).
;		It locks interrupt service (cannot be called from handler).
; -----------------------------------------------------------------------------

; ------------- Push registers

IntUninstall:	push	eax		; push EAX

; ------------- Disable interrupts

		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock interrupt service

		INTLOCK			; lock interrupt service

; ------------- Deactivate interrupt handler

		mov	al,[ebx+INTHAND_Flags] ; AL <- flags
		and	al,INT_ACTIVE	; AL <- active bit
		call	IntIntDeact	; deactivate interrupt handler
		or	[ebx+INTHAND_Flags],al ; return ACTIVE flag

; ------------- Uninstall private interrupt vector

		test	byte [ebx+INTHAND_Flags],INT_PRIVATE ; private handler?
		jz	IntUninstall2	; not private handler
		push	ebx		; push EBX
		push	edx		; push EDX
		movzx	ebx,byte [ebx+INTHAND_IRQ] ; EBX <- current IRQ number
		mov	edx,[IntJumpTab+ebx*4] ; EDX <- address
		add	ebx,INT_FIRST	; EBX <- interrupt number
		call	SetIntGate	; set interrupt gate
		pop	edx		; pop EDX
		pop	ebx		; pop EBX

; ------------- Decrease number of handlers

IntUninstall2:	mov	eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
		dec	byte [eax+INTDESC_HandNum]; decrease number of handlers

; ------------- Detach interrupt handler from the list

		call	ListDelEBX	; free IRQ handler

; ------------- Unlock interrupt service

		INTUNLOCK		; unlock interrupt service

; ------------- Enable interrupts

		popf			; pop flags

; ------------- Pop registers

		pop	eax		; pop EAX
		ret

Funkce IntUninstall odinstaluje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX. Pokud je ovladač před odinstalováním aktivní, je deaktivován a přitom je uchován jeho příznak aktivity - to umožňuje pozdějí automatickou reaktivaci při nové instalaci. Jedná-li se o privátní ovladač, je obnoven vektor přerušení na standardní adresu, která používá standardní obsluhu správce přerušení. Po snížení čítače počtu nainstalovaných ovladačů je ovladač odpojen ze seznamu nainstalovaných ovladačů tohoto přerušení.


; -----------------------------------------------------------------------------
;    Activate interrupt handler, can be called only from interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler
; NOTES:	It does NOT lock interrupt service.
; -----------------------------------------------------------------------------

; ------------- Activate interrupt handler

IntIntAct:	bts	dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
		jc	IntIntAct9	; handler is already active

; ------------- Push registers

		push	eax		; push EAX

; ------------- Count number of active handlers

		mov	eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
		inc	byte [eax+INTDESC_Active] ; increase active handlers
		cmp	byte [eax+INTDESC_Active],1 ; is it first handler?
		jne	IntIntAct8	; it is not first handler

; ------------- Enable IRQ

		movzx	eax,byte [eax+INTDESC_IRQ] ; EAX <- IRQ number
		call	IRQEnable	; enable IRQ		

; ------------- Pop registers

IntIntAct8:	pop	eax		; pop EAX
IntIntAct9:	ret

Funkce IntIntAct aktivuje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX. Je-li ovladač již aktivní, nic se neprovede. Funkce inkrementuje čítač aktivních ovladačů daného přerušení a jedná-li se o první aktivovaný ovladač, aktivuje se obsluha přerušení pomocí funkce IRQEnable. Tato varianta funkce neuzamyká zámek správce přerušení a proto může být volána pouze z obsluhy přerušení.


; -----------------------------------------------------------------------------
;    Activate interrupt handler, must NOT be called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler
; NOTES:	It DOES lock interrupt service.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

IntAct:		pushf			; push flags
		cli			; disable interrupts

; ------------- Lock interrupt service

		INTLOCK			; lock interrupt service

; ------------- Activate interrupt handler

		call	IntIntAct	; activate interrupt handler

; ------------- Unlock interrupt service

		INTUNLOCK		; unlock interrupt service

; ------------- Enable interrupts

		popf			; pop flags
		ret

Funkce IntAct aktivuje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX. Je-li ovladač již aktivní, nic se neprovede. Voláním funkce IntIntAct se inkrementuje čítač aktivních ovladačů daného přerušení a jedná-li se o první aktivovaný ovladač, aktivuje se obsluha přerušení pomocí funkce IRQEnable. Tato varianta funkce uzamyká zámek správce přerušení a proto nesmí být volána z obsluhy přerušení.


; -----------------------------------------------------------------------------
;    Deactivate interrupt handler, can be called only from interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler
; NOTES:	It does NOT lock interrupt service.
; -----------------------------------------------------------------------------

; ------------- Deactivate interrupt handler

IntIntDeact:	btr	dword [ebx+INTHAND_Flags],INT_ACTIVE_BIT ; active?
		jnc	IntIntDeact9	; handler is already inactive

; ------------- Push registers

		push	eax		; push EAX

; ------------- Count number of active handlers

		mov	eax,[ebx+INTHAND_IntDesc] ; EAX <- interrupt descriptor
		dec	byte [eax+INTDESC_Active] ; decrease active handlers
		jnz	IntIntDeact8	; another handler remains active

; ------------- Disable IRQ

		movzx	eax,byte [eax+INTDESC_IRQ] ; EAX <- IRQ number
		call	IRQDisable	; disable IRQ		
		call	IRQAck		; acknowledge interrupt

; ------------- Pop registers

IntIntDeact8:	pop	eax		; pop EAX
IntIntDeact9:	ret

Funkce IntIntDeact deaktivuje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX. Je-li ovladač již neaktivní, nic se neprovede. Funkce dekrementuje čítač aktivních ovladačů daného přerušení a pokud se jednalo o poslední aktivní ovladač, deaktivuje se obsluha přerušení pomocí funkce IRQDisable. Po deaktivaci přerušení jsou potvrzena případná čekající přerušení funkcí IRQAck. Tato varianta funkce neuzamyká zámek správce přerušení a proto může být volána pouze z obsluhy přerušení.


; -----------------------------------------------------------------------------
;   Deactivate interrupt handler, must NOT be called from interrupt handler
; -----------------------------------------------------------------------------
; INPUT:	EBX = interrupt handler
; NOTES:	It DOES lock interrupt service.
; -----------------------------------------------------------------------------

; ------------- Disable interrupts

IntDeact:	pushf			; push flags
		cli			; disable interrupts

; ------------- Lock interrupt service

		INTLOCK			; lock interrupt service

; ------------- Deactivate interrupt handler

		call	IntIntDeact	; deactivate interrupt handler

; ------------- Unlock interrupt service

		INTUNLOCK		; unlock interrupt service

; ------------- Enable interrupts

		popf			; pop flags
		ret

Funkce IntDeact deaktivuje ovladač přerušení INTHAND, jehož adresa je funkci předána v registru EBX. Je-li ovladač již neaktivní, nic se neprovede. Voláním funkce IntIntDeact se dekrementuje čítač aktivních ovladačů daného přerušení a pokud se jednalo o poslední aktivní ovladač, deaktivuje se obsluha přerušení pomocí funkce IRQDisable. Po deaktivaci přerušení jsou potvrzena případná čekající přerušení funkcí IRQAck. Tato varianta funkce uzamyká zámek správce přerušení a proto nesmí být volána z obsluhy přerušení.


; -----------------------------------------------------------------------------
;               Interrupt service (IRQ 0 to 31, i.e. INT 32 to 63)
; -----------------------------------------------------------------------------

; ------------- IRQ head (push registers and prepare IRQ number)

%assign	IRQN	0
%rep	INT_NUM-1-6
IRQ %+ IRQN:	push	eax		; push EAX
		mov	al,IRQN		; AL <- IRQ number
		jmp	short IRQInt	; IRQ common service
%assign	IRQN	IRQN+1
%endrep

IRQ %+ IRQN:	push	eax		; push EAX
		mov	al,IRQN		; AL <- IRQ number

; ------------- Push other registers (here AL = IRQ number)

IRQInt:		push	ebx		; push EBX
		push	ecx		; push ECX
		push	edx		; push EDX
		push	esi		; push ESI
		push	ds		; push DS
		push	es		; push ES
		cld			; direction up

; ------------- Initialize kernel segments

		mov	ebx,SYSTEM_DS	; EBX <- system data segment
		mov	ds,ebx		; DS <- system data segment
		mov	es,ebx		; ES <- system data segment

; ------------- Lock interrupt service

		INTLOCK			; lock interrupt service

; ------------- Interrupt descriptor (-> ECX)

		movzx	eax,al		; EAX <- IRQ number
		imul	ecx,eax,INTDESC_size ; ECX <- descriptor offset
		add	ecx,IntTab	; ECX <- descriptor

; ------------- Walk through the list of handlers

		mov	edx,ecx		; EDX <- list header
IRQInt2:	mov	ecx,[ecx+LIST_Next] ; ECX <- next handler
		cmp	ecx,edx		; end of list?
		je	IRQInt4		; end of list

; ------------- Check if interrupt handler is active

		test	byte [ecx+INTHAND_Flags],INT_ACTIVE ; active?
		jz	IRQInt2		; handler is not active

; ------------- Call interrupt handler

		mov	esi,[ecx+INTHAND_Int] ; ESI <- interrupt handler
		mov	ebx,[ecx+INTHAND_Data] ; EBX <- user data
		call	esi		; call interrupt handler
		jmp	short IRQInt2	; service next handler

; ------------- Acknowledge interrupt (EAX = interrupt number)

IRQInt4:	call	IRQAck		; acknowledge interrupt

; ------------- Unlock interrupt service

		INTUNLOCK		; unlock interrupt service

; ------------- Pop registers

		pop	es		; pop ES
		pop	ds		; pop DS
		pop	esi		; pop ESI
		pop	edx		; pop EDX
		pop	ecx		; pop ECX
		pop	ebx		; pop EBX
		pop	eax		; pop EAX
		iret

%rep	6
%assign	IRQN	IRQN+1
IRQ %+ IRQN:	push	eax		; push EAX
		mov	al,IRQN		; AL <- IRQ number
		jmp	short IRQInt	; IRQ common service
%endrep

Standardní obsluha přerušení správce přerušení začíná návěštími IRQ0IRQ31. Na začátku každého přerušení je do registru AL připraveno číslo přerušení. Po inicializaci segmentových registrů a uzamknutí správce přerušení se připraví příslušný popisovač přerušení. Postupně se prochází všechny ovladače přerušení a u těch ovladačů, které jsou nastavené jako aktivní, se zavolá obslužná funkce ovladače přerušení. Po obsluze všech ovladačů přerušení se přerušení potvrdí funkcí IRQAck a funkce odemkne zámek správce přerušení.


; -----------------------------------------------------------------------------
;                        Initialize interrupt service
; -----------------------------------------------------------------------------
; NOTES:	Interrupt must be disabled at this point.
; -----------------------------------------------------------------------------

; ------------- Initialize IRQ descriptor table

IntInit:	mov	ebx,IntTab	; EBX <- IRQ 
		xor	eax,eax		; EAX <- 0, IRQ number
IntInit2:	call	ListInit	; initialize list of IRQ handlers
		mov	[ebx+INTDESC_IRQ],al ; set IRQ number
		call	IRQInfo		; check if IRQ channel is valid
		jc	IntInit3	; IRQ channel is not valid
		mov	byte [ebx+INTDESC_Flags],INTDESC_VALID ; valid IRQ line
IntInit3:	inc	eax		; increase IRQ number
		add	ebx,byte INTDESC_size ; next entry
		cmp	al,INT_NUM	; next entry?
		jb	IntInit2	; process next entry

; ------------- Initialize IRQ interrupt vectors

		xor	ebx,ebx		; EBX <- 0
		mov	bl,INT_FIRST	; BL <- index of first IRQ interrupt
IntInit4:	mov	edx,[IntJumpTab+ebx*4-INT_FIRST*4] ; EDX<-address
		call	SetIntGate	; set interrupt gate (it increases EBX)
		cmp	bl,INT_FIRST+INT_NUM ; all interrupts?
		jb	IntInit4	; next interupt
		ret

Funkce IntInit provede inicializaci správce přerušení. Funkce musí být volána po instalaci ovladače řadiče přerušení IRQ, ale před instalací ostatních ovladačů. Přerušení musí být během provádění funkce zakázáno.

Funkce prochází seznam popisovačů přerušení, inicializuje jejich záhlaví seznamu ovladačů a po ověření platnosti přerušení pomocí funkce IRQInfo nastavuje jejich příznak platnosti.

Po inicializaci popisovačů přerušení funkce nainstaluje standardní vektory obsluhy přerušení pomocí tabulky IntJumpTab a funkce SetIntGate.


Obsah / Ovladače / INT / Funkce správce přerušení