Niektórym programom nie wystarcza działanie na samym procesorze czy sprzęcie znajdującym się w komputerze. Czasem trzeba połączyć się z jakimś urządzeniem zewnętrznym, takim jak modem zewnętrzny czy drukarka. Celem tego artykułu jest właśnie pokazanie, jak to zrobić.
Tutaj sprawa jest dość prosta. Porty równoległe nie wymagają żadnych ustawień, ewentualnie
tylko tryb pracy, ustawiany w BIOSie.
Praca z portem równoległym pod Linuksem sprowadza się do czytania i zapisu do specjalnych
plików - /dev/parportN
(N - liczba), które reprezentują porty równoległe. O tym, jak
obsługiwać pliki, napisałem w kursie.
Porty szeregowe są trudniejsze w obsłudze. Czasem wystarczy, podobnie jak dla portów równoległych, po prostu czytać i zapisywać do specjalnych plików, ale to nie zawsze może wystarczyć. Jest tak, gdyż porty szeregowe mają swoje ustawienia:
Pierwszym krokiem jest otwarcie specjalnego pliku urządzenia, zazwyczaj /dev/ttySx
(x - liczba). O tym, jak otwierać pliki, zapisywać i odczytywać z nich dane, napisałem
w kursie.
Jeśli trzeba ustawić parametry portu, wykonuje się to funkcją systemową sys_ioctl (numer 54). Przyjmuje ona w tym przypadku 3 argumenty:
struc __kernel_termios .c_iflag: resd 1; flagi trybu wejścia .c_oflag: resd 1; flagi trybu wyjścia .c_cflag: resd 1; flagi trybu kontroli .c_lflag: resd 1; flagi trybu lokalnego .c_line: resb 1; obsługa linii .c_cc: resb 32; znaki kontrolne endstruc
Najpierw należy pobrać bieżące parametry portu, potem zmienić te, które chcemy i wysłać je do portu. Poniżej przedstawiam przykładowy kod w składni NASMa. Najpierw otwiera on plik portu, potem odczytuje bieżące argumenty, i ustawia nowe:
Wszystkie wykorzystane stałe można znaleźć w pliku /usr/include/bits/termios.h
.
Polecam zapoznanie się, gdyż można tam znaleźć wiele ciekawych opcji, na przykład automatyczne
tłumaczenie znaków CR na LF i na odwrót.
mov eax, 5 ; otwieranie pliku mov ebx, port ; nazwa mov ecx, 402o ; odczyt i zapis, nie terminal kontrolujący mov edx, 777o ; rwx dla wszystkich int 80h cmp eax, 0 jl koniec mov ebx, eax ; EBX = deskryptor ; pobieranie i ustawianie parametrów portu %define TCGETS 0x00005401 %define TCSETS 0x00005402 mov eax, 54 ; sys_ioctl ;ebx = deskryptor mov ecx, TCGETS ; pobierz parametry mov edx, termios int 80h cmp eax, 0 jl koniec %define B115200 0010002o %define CS8 0000060o %define CLOCAL 0004000o %define CREAD 0000200o mov dword [termios+__kernel_termios.c_cflag],B115200|CS8|CLOCAL|CREAD %define INPCK 0000020o mov dword [termios+__kernel_termios.c_iflag], INPCK mov dword [termios+__kernel_termios.c_oflag], 0 %define ICANON 0000002o mov dword [termios+__kernel_termios.c_lflag], ICANON %define VKILL 3 %define VMIN 6 mov byte [termios+__kernel_termios.c_cc+VKILL], 0 mov byte [termios+__kernel_termios.c_cc+VMIN], 1 mov eax, 54 ; sys_ioctl ;ebx = deskryptor mov ecx, TCSETS ; ustaw parametry mov edx, termios int 80h cmp eax, 0 jnl ioctl_set_ok ... section .data port db "/dev/ttyS0", 0 termios istruc __kernel_termios
Po wykonaniu tego kodu można normalnie czytać z urządzenia i zapisywać do niego jak do zwykłego pliku.
Na uwagę zasługuje wartość 402o wpisana do ECX przed otwarciem pliku. Mówi ona, że chcemy dostęp do odczytu i zapisu, ale dodatkowo włączona jest opcja O_NOCTTY (400o). Sprawi ona, że otwarte przez nas urządzenie znakowe NIE stanie się terminalem kontrolującym programu. W innym przypadku czytanie ze standardowego wejścia kończyłoby się czytaniem z wybranego portu szeregowego, a próba wyświetlenia napisu wysyłałaby bajty na ten port.
Polecam lekturę Serial Programming HOWTO.