Получение информации о типе и функциональных возможностях драйвера
driver_info AH == 1, AL == 255 (код запроса)
public
_driver_info
_driver_info
proc near
mov AX, 1FFH
; ah=1, al=255
call int_pkt
; обращение к драйверу
jnc lv
mov AX, seg _PARAM.ER_CODE
mov DS, AX
mov _PARAM.ER_CODE, 272
; Устанавливаем код "Нет инф. о драйвере"
lv:
ret
_driver_info
endp
int_pkt:
; Подпрограмма обращения к драйверу
push ds
push es
pushf
cli
call _param.Handler
; адрес _param.Handler должен быть определен раньше
pop es
pop ds
ret
Целочисленный указатель (handle) должен быть занесен в регистр BX (для старых драйверов). В случае ошибки устанавливается флаг carry, а код ошибки заносится в регистр DH. Сообщение BAD_HANDLE (неверный указатель) возможно только для старых драйверов. При благополучном исполнении флаг carry равен нулю, а в регистры будет занесены следующие параметры:
BX
версия;
CH
класс;
CL
номер;
DX
тип;
DS:SI
указывают на строку имени драйвера;
AL
функциональные возможности.
AL = 1
гарантируется выполнение базовых функций;
= 2
обеспечено выполнение базового и расширенного набора функций;
= 5
выполняется базовый и экстра-набор функций;
= 6
выполним полный набор функций;
= 255
драйвер не установлен.
Ниже приведен пример программы, реализующей некоторые из описанных запросов.
.MODEL
small
PUBLIC
_INFACE
VERSION
EQU
1
EXTRN
_PARAM:BYTE
EXTRN
_Q:BYTE
.DATA
INCLUDE
DEF.ASM
; Определения некоторых констант
P_LIST
STRUC
LINTN
DB
32 dup(0)
; Список активных номеров прерываний
HANDLES
DW
?
HANDLEP
DW
?
ER_CODE
DW
?
ERNUM
DW
?
; Код ошибки
HANDLER
DD
?
MODE
DW
?
; Текущий режим приема пакетов
MLIST
DB
0,0,0,0,0,0
; Список допустимых режимов; 1 => имеется
PKT_IN
DW
?,?
; Диагностический массив
pkt_out
DW
?,?
byte_in
DW
?,?
byt_out
DW
?,?
err_in
DW
?,?
err_out
DW
?,?
pk_drop
DW
?,?
L1
DW
0
; Версия драйвера
L2
DW
0
; класс/номер
L3
DW
0
; Тип
L4
DW
0
; Функция
_NAME
DB
0,0,0,0,0,0,0,0,0,0
; Имя интерфейса
ETHER_ADR
DB
ADDR_LEN dup(-1)
; Ethernet-адрес
S_ADR
DB
EADDR_LEN+5 dup(-1)
; Ethernet-адрес получателя
D_ADR
DB
EADDR_LEN+5 dup(-1)
; Ethernet-адрес отправителя
P_LIST
ENDS
QUEUE
STRUC
Leng
DW
15000,?
; Длина очереди
Tail
DW
?
; Смещение последнего элемента очереди
Head
DW
?
; Смещение первого элемента очереди
_end
DW
?
; Указатель на конец очереди
p_len
DW
?
; Длина пакета
P_start
DW
?
; Указатель на текущий пакет = Q_head - Q_begin +2
NEW
DB
0
; Флаг нового пакета
Line
DB
?
; Строка экрана
Npacks
DD
0
; Счетчик принятых пакетов
B
DW
?
; смещение Q_beg
Point
DW
380 dup(?)
Beg
DB
31000 dup(?)
; Пакетный буфер
QUEUE
ENDS
ether_bdcst
DB
EADDR_LEN dup(-1)
; Широковещательный адрес Ethernet, заполненный -1.
ether_addr
DB
EADDR_LEN dup(-1)
bogus_type
DB
0,0;
signature
DB
'PKT DRVR',0
; Сигнатура пакетного драйвера
signature_len
equ
$-signature
SAFE
DW
?
DFLAG
DB
0
.CODE
PUBLIC
_INFACE
_INFACE
PROC
NEAR
CLD
MOV DFLAG, 0
; Очистка флага драйвера
MOV _PARAM.ER_CODE, 0
; Очистка флага ошибки
PUSH BP
; Спасение регистров
MOV BP, SP
PUSH SI
PUSH DI
PUSH ES
PUSH DS
MOV CX, 32
MOV AL, 60H
; Установка начального номера прерывания
LEA SI, _PARAM.LINTN
; Формирование указателя на список номеров прерывания
CHECK:
PUSH AX
PUSH CX
PUSH SI
CALL CHK_INT
POP SI
POP CX
MOV byte ptr [SI], 0 ;
JNE NO_SIGNATURE
INC DFLAG
; Установка флага <Это драйвер>
MOV BYTE PTR [SI], 1
; Установка флага наличия
NO_SIGNATURE:
POP AX
INC AL
; Следующий номер прерывания
INC SI
; Актуализация указателя
LOOP CHECK
CMP DFLAG, 0
; Драйвер присутствует?
JNE HAVE_SIGNATURE
MOV _PARAM.ER_CODE, 271
; Установка флага <No signature>
JMP OKAY
INT_PKT:
PUSH ES
pushf
cli
call _PARAM.HANDLER
POP ES
RET
CHK_INT:
PUSH ES
; AL = номер прерывания
PUSH DI
MOV AH, 35H
; Получение вектора прерывания
INT 21H
; ES:BX=seg:offs драйвера
MOV _PARAM.HANDLER.OFFS,BX
; Записываем адрес драйвера
MOV _PARAM.HANDLER.SEGM, ES
LEA DI, 3[BX]
; Устанавливаем смещение сигнатуры драйвера
MOV SI, OFFSET SIGNATURE
; Проверка сигнатуры драйвера
MOV CX, SIGNATURE_LEN
; Присутствует ли здесь драйвер?
REPE CMPSB ; DS:[SI] - ES:[DI]
POP DI
POP ES
RET
<
HAVE_SIGNATURE:
MOV CX, 32
; Установка начального значения счетчика
LEA SI, _PARAM.LINTN
; Устанавливаем указатель списка
MOV AL, 60H
; Задаем начальный номер прерывания
CHOICE:
CMP BYTE PTR [SI], 0
JNE SETDRV
INC AL
LOOP CHOICE
SETDRV:
MOV AH, 35H
INT 21H
MOV _PARAM.HANDLER.OFFS,BX
; Определяем адрес драйвера
MOV _PARAM.HANDLER.SEGM, ES
PUSH DS
POP ES
MOV CX, EADDR_LEN
MOV SI, OFFSET ETHER_ADDR
MOV DI, OFFSET ETHER_BDCST
REPE CMPSB
JE GET_MODE
; Адрес не определен
MOV AH, 25
; Записываем ethernet-адрес
MOV DI, offset ETHER_ADDR
MOV CX, EADDR_LEN
call int_pkt
MOV _PARAM.ER_CODE, DX
; Устанавливаем код ошибки
JMP OKAY
GET_MODE:
MOV SAFE, DS
; Спасаем DS
PUSH DS
MOV AH, 2
; Открываем доступ пакетам
MOV AL, 1
; Класс интерфейса
MOV BX, -1
; Тип интерфейса
MOV DL, 0
; Номер интерфейса
MOV CX, 2
; Используем длину type = 2
MOV SI, OFFSET BOGUS_TYPE
PUSH CS
; ES:DI -> Receiver.
POP ES
MOV DI, OFFSET RECEIVER
call INT_PKT
JNC $_$
MOV _PARAM.ER_CODE, DX
; Устанавливаем код ошибки
$_$:
MOV _PARAM.HANDLES, AX
; Записываем указатель-Handle
MOV AH, 6
; Определяем ethernet-адрес интерфейса
PUSH DS
POP ES
MOV DI, offset _PARAM.ETHER_ADR
MOV CX, EADDR_LEN
MOV BX, _PARAM.HANDLES
call int_pkt
JNC NOBAD
MOV _PARAM.ER_CODE, 273
; Ошибка при определении Ethernet-адреса
POP DS
JMP OKAY
NOBAD:
MOV AX, 1FFH
; Запрашиваем информацию о драйвере
MOV BX, _PARAM.HANDLES
; Устанавливаем указатель
call INT_PKT
JNC N_BAD
MOV _PARAM.ER_CODE, 272
; Ошибка при получении информации о драйвере
POP DS
JMP OKAY
N_BAD:
PUSH DS
PUSH SS
&nsp;
POP DS
MOV ES, SAFE
MOV _PARAM.L1, BX
; Версия драйвера
MOV _PARAM.L2, CX
; номер/класс
MOV _PARAM.L3, DX
; Тип
MOV _PARAM.L4, AX
; Функциональность
LEA BX, _PARAM._NAME
POP DS
MOV CX, 8
ZFIND:
CMP byte ptr [SI], 0
MOV AL, byte ptr [SI]
MOV byte ptr ES:[BX], AL
JE ZERO_
INC SI
INC BX
LOOP ZFIND
ZERO_:
POP DS
MOV AH, 21
; Запрашиваем код режима приема пакетов
MOV BX, _PARAM.HANDLES
call INT_PKT
MOV _PARAM.MODE, AX
; Записываем код режима
.........................
OKAY:
POP DS
POP ES
POP DI
POP SI
MOV SP, BP
POP BP
RET
RECEIVER:
; Подпрограмма RECEIVER, вызываемая при получении пакета
OR AX, AX
; Первый или второй вызов?
JNE RECV
MOV AX, seg _Q.beg
; Указатель буфера ES:DI
MOV ES, AX
MOV DI, offset _Q.beg
RECV:
RETF
2. Организация доступа для пакетов данного типа access_type(if_class, if_type, if_number, type, typelen, receiver) AH ==2 (код запроса)
Запрос access_type инициализирует доступ для пакетов определенного типа (type). Аргумент typelen - длина спецификации типа в байтах, для PC/TCP равна 5 (наименьшее значение - 2, для IP и ARP). Аргумент receiver является указателем на подпрограмму, которая вызывается при приеме пакета. Получая пакет, драйвер дважды обращается к этой программе. Первый раз (при AX==0) это делается с целью получения адреса буфера, куда должен быть положен пакет. Прикладная программа в этом случае должна выдать указатель буфера в регистры ES:DI. Если прикладной процесс не имеет свободного буфера,то возвращается значение 0:0. Пакет выбрасывается и повторное обращение к программе receiver отменяется. Форма реализации запроса аналогична приведенному для driver_info:
; установка класса; здесь предполагается, что содержимое регистров соответствует тому, что получено в результате обращения к driver_info
mov bx, dx
; устанавливаем параметр type
mov dl, cl
; устанавливаем параметр number, при одном интерфейсе number=0
xor cx, cx
; длина type равна нулю
push cs
; устанавливаем сегментный регистр receiver
pop es
mov di, offset RECEIVER
; вызов подпрограммы receiver
call int_pkt
; обращение к пакетному драйверу
В случае ошибки флаг carry=1, а в регистр DH заносится код ошибки.
Возможные ошибки:
2
NO_CLASS не найдено интерфейса указанного класса;
3
NO_TYPE не найдено интерфейса указанного типа;
4
NO_NUMBER не найдено интерфейса с указанным номером;
5
BAD_TYPE специфицирован неправильный тип пакета;
9
NO_SPACE недостаточно места в памяти;
10
TYPE_INUSE было обращение к данному типу и он пока занят.
При успешном выполнении запроса флаг carry=0, а в регистр AX занесен указатель (handle).
Обращение к приемнику (receiver):
(*receiver)(handle, flag, len [, buffer])
int handle;
BX
; указатель
int flag;
AX
; флаг вызова(0/1)
unsigned len;
CX
; целое без знака - длина пакета
if AX == 1,
char far *buffer;
DS:SI
; адрес буфера
Если параметр typelen равен нулю, прикладной процесс готов получать все пакеты. Очень важно, чтобы при первом обращении к receiver (AX==0) CX (длина пакета) была указана правильно, что позволит выделить нужное место в памяти. CX должна включать в себя длину MAC-заголовка и размер самого сообщения без контрольной суммы (CRC). Повторный вызов (AX==1) программы receiver указывает на то,что пакет записан в буфер и прикладная программа может с ним работать. Адрес буфера будет указан в регистрах DS:SI.