   #Start Contents English version

                        Porty szeregowe i rownolegle

   Niektorym programom nie wystarcza dzialanie na samym procesorze czy
   sprzecie znajdujacym sie w komputerze. Czasem trzeba polaczyc sie z
   jakims urzadzeniem zewnetrznym, takim jak modem zewnetrzny czy
   drukarka. Celem tego artykulu jest wlasnie pokazanie, jak to zrobic.
     _________________________________________________________________

Porty rownolegle

   (przeskocz porty rownolegle)

   Tutaj sprawa jest dosc prosta. Porty rownolegle nie wymagaja zadnych
   ustawien, ewentualnie tylko tryb pracy, ustawiany w BIOSie. Praca z
   portem rownoleglym pod Linuksem sprowadza sie do czytania i zapisu do
   specjalnych plikow - /dev/parportN (N - liczba), ktore reprezentuja
   porty rownolegle. O tym, jak obslugiwac pliki, napisalem w kursie.
     _________________________________________________________________

Porty szeregowe

   Porty szeregowe sa trudniejsze w obsludze. Czasem wystarczy, podobnie
   jak dla portow rownoleglych, po prostu czytac i zapisywac do
   specjalnych plikow, ale to nie zawsze moze wystarczyc. Jest tak, gdyz
   porty szeregowe maja swoje ustawienia:
     * szybkosc transmisji (w bodach = bitach na sekunde): od 75 do nawet
       4 milionow
     * liczba bitow danych - od 5 do 8
     * kontrola parzystosci - brak, parzysta, nieparzysta, mark
       (znacznik) i space
     * kontrola przeplywu - brak, programowa (XON/XOFF) i sprzetowa
       (RTS/CTS)
     * bity stopu - 1, poltora lub 2

   Pierwszym krokiem jest otwarcie specjalnego pliku urzadzenia,
   zazwyczaj /dev/ttySx (x - liczba). O tym, jak otwierac pliki,
   zapisywac i odczytywac z nich dane, napisalem w kursie.

   Jesli trzeba ustawic parametry portu, wykonuje sie to funkcja
   systemowa sys_ioctl (numer 54). Przyjmuje ona w tym przypadku 3
   argumenty:
     * EBX = deskryptor portu, otrzymany z otwarcia pliku urzadzenia
     * ECX = komenda (TCGETS=0x00005401 dla pobrania parametrow portu,
       TCSETS=0x00005402 dla ustawienia)
     * EDX = adres struktury termios, ktora otrzyma dane lub zawiera
       parametry do ustawienia. Struktura ta wyglada tak:
        struc __kernel_termios
            .c_iflag:   resd 1;         flagi trybu wejscia
            .c_oflag:   resd 1;         flagi trybu wyjscia
            .c_cflag:   resd 1;         flagi trybu kontroli
            .c_lflag:   resd 1;         flagi trybu lokalnego
            .c_line:    resb 1;         obsluga linii
            .c_cc:      resb 32;        znaki kontrolne
        endstruc

   Najpierw nalezy pobrac biezace parametry portu, potem zmienic te,
   ktore chcemy i wyslac je do portu. Ponizej przedstawiam przykladowy
   kod w skladni NASMa. Najpierw otwiera on plik portu, potem odczytuje
   biezace argumenty, i ustawia nowe:
     * we flagach kontrolnych - szybkosc 115200 bps (B115200), 8 bitow
       danych (CS8), polaczenie lokalne (CLOCAL), mozliwosc odczytywania
       (CREAD)
     * we flagach wejsciowych - sprawdzanie parzystosci (INPCK)
     * we flagach wyjsciowych nic nie jest potrzebne
     * we flagach obslugi linii - tryb kanoniczny (ICANON): dane sa
       przesylane linijkami (dopiero znak Entera powoduje wyslanie
       danych)
     * w znakach kontrolnych - znak VKILL ma byc ignorowany, a minimalna
       liczba znakow (VMIN) wynosi 1

   Wszystkie wykorzystane stale mozna znalezc w pliku
   /usr/include/bits/termios.h. Polecam zapoznanie sie, gdyz mozna tam
   znalezc wiele ciekawych opcji, na przyklad automatyczne tlumaczenie
   znakow CR na LF i na odwrot.
        mov     eax, 5          ; otwieranie pliku
        mov     ebx, port       ; nazwa
        mov     ecx, 402o       ; odczyt i zapis, nie terminal kontrolujacy
        mov     edx, 777o       ; rwx dla wszystkich
        int     80h

        cmp     eax, 0
        jl      koniec
        mov     ebx, eax        ; EBX = deskryptor

        ; pobieranie i ustawianie parametrow 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 mozna normalnie czytac z urzadzenia i zapisywac
   do niego jak do zwyklego pliku.

   Na uwage zasluguje wartosc 402o wpisana do ECX przed otwarciem pliku.
   Mowi ona, ze chcemy dostep do odczytu i zapisu, ale dodatkowo wlaczona
   jest opcja O_NOCTTY (400o). Sprawi ona, ze otwarte przez nas
   urzadzenie znakowe NIE stanie sie terminalem kontrolujacym programu. W
   innym przypadku czytanie ze standardowego wejscia konczyloby sie
   czytaniem z wybranego portu szeregowego, a proba wyswietlenia napisu
   wysylalaby bajty na ten port.

   Polecam lekture Serial Programming HOWTO.

   Spis tresci off-line (klawisz dostepu 1)
   Spis tresci on-line (klawisz dostepu 2)
   Ulatwienia dla niepelnosprawnych (klawisz dostepu 0)
