Получение информации о типе и функциональных возможностях драйвера
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] | |
<
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 | |
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:
| Int | if_class; AL | ; класс интерфейса |
| Int | if_type; BX | ; тип интерфейса |
| Int | if_number; DL | ; номер интерфейса |
| Char | far *type; DS:SI | |
| Unsigned | typelen; CX | |
| Int | (far *receiver); ES:DI | |
| access: | mov ah, 2 | |
| | style="font-family:arial;font-size:12pt"mov al, ch | ; установка класса; здесь предполагается, что содержимое регистров соответствует тому, что получено в результате обращения к 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.