Urządzenie CasinoDev

Urządzenie

Urządzenie jest podłączane do komputera przez szynę PCI – identyfikator producenta to 0x0666, a identyfikator urządzenia to 0x777.

Urządzenie nie posiada własnej pamięci i operuje na buforach w głównej pamięci komputera przez bezpośredni dostęp do pamięci (DMA). Ponieważ główna pamięć komputera podlega fragmentacji, a urządzenie może używać całkiem dużo pamięci, urządzenie wykonuje dostęp do pamięci przez tablicę stron w swoim własnym formacie.

Urządzenie jest zaprojektowane tak, by można było je obsługiwać z minimalną ingerencją jądra w przesyłane polecenia — wszystkie polecenia są walidowane przez urządzenie i operują tylko tylko na adresach wirtualnych, tłumaczonych przez tabele stron — użytkownik przygotowujący bufor poleceń nie ma możliwości zawieszenia urządzenia ani dostępu do nie swojej pamięci. Co najwyżej, może spowodować zgłoszenie błędu przez urządzenie, który może być obsłużony przez jądro systemu.

Urządzeniem steruje się przez rejestry wejścia/wyjścia zmapowane do pamięci (MMIO). Ten obszar MMIO jest pierwszym i jedynym obszarem BAR używanym przez urządzenie (BAR0). Urządzenie wykorzystuje również jedną linię przerwania PCI.

Obszar MMIO ma wielkość 64kiB, ale tylko niektóre bajty z tego zakresu są używane na rejestry. Wszystkie udokumentowane rejestry są 32-bitowe w formacie little-endian i powinny być używane tylko przez wyrównane 32-bitowe odczyty i zapisy.

Bufory i blok stronicowania

Wszystkie dane i polecenia używane przez urządzenie są zawarte w stronicowanych buforach. Urządzenie może jednocześnie używać do 16 buforów — blok stronicowania ma 16 “slotów” (identyfikowane przez liczby 0..15), w które można wpiąć bufor za pomocą polecenia BIND_SLOT. Każdy slot ma następujące atrybuty, wybierane przy wpięciu bufora:

  • ENABLED: jeśli ustawiony, do slota wpięty jest bufor, ma tabelę stron, i może być użyty przez urządzenie; jeśli nie jest ustawiony, każda próba użycia tego slotu spowoduje błąd

  • TYPE: Określa typ talii wpiętej w bufor (52 lub 24 karty)

  • PT_ADDR: adres fizyczny tabeli stron

  • SEED: używany do losowego generowania kart

Urządzenie używa 40-bitowych adresów fizycznych (zarówno dla buforów jak i dla tabel stron), 22-bitowych adresów wirtualnych w ramach bufora, i stron o rozmiarze 4kiB. Tabele stron używane przez urządzenie są jednopoziomowe:

  • jądro podaje urządzeniu adres fizyczny katalogu stron przy podpinaniu bufora

  • bity 12-21 adresu wirtualnego wybierają wpis w tabeli stron (page table), który zawiera adres fizyczny strony

  • bity 0-11 adresu wirtualnego to offset wewnątrz strony

Tabele stron mają rozmiar 4kiB (taki jak strona) i składają się z 1024 wpisów, gdzie każdy wpis jest 32-bitowym słowem w formacie little-endian. Wpis w tabeli stron ma następujący format:

  • bit 0: PRESENT — jeśli ustawiony, dany wpis jest obecny i może być użyty. Jeśli wyzerowany, wpis jest nieobecny i użycie go spowoduje błąd braku strony (MEM_ERROR).

  • bity 4-31: PA — bity 12-39 adresu fizycznego strony (bity 0-11 adresu są zawsze równe 0 — strony muszą być wyrównane).

Katalog stron wybiera się, podając bity 12-39 jego adresu fizycznego (czyli adres przesunięty w prawo o 12 bitów).

Jeśli urządzenie napotka niepoprawny adres wirtualny (bez flagi PRESENT), zgłaszane jest przerwanie MEM_ERROR.

Wysyłanie poleceń

Urządzenia używa się przez wysyłanie mu poleceń. Polecenia składają się ze zmiennej liczby 32-bitowych słów w formacie little-endian. Istnieje 6 różnych typów poleceń, rozróżnianych po niskich 4 bitach pierwszego słowa.

Wszystkie nieużywane pola muszą być ustawione na 0 — w przeciwnym wypadku, zachowanie urządzenia nie jest zdefiniowane.

Urządzenia mogą być wysyłane do urządzenia ręcznie przez zapis kolejnych słów polecenia do rejestru MMIO urządzenia.

Ręczne wysyłanie poleceń

Polecenia można wysyłać do urządzenia bezpośrednio przez MMIO, używając następujących rejestrów:

BAR0 + 0x008c: CMD_MANUAL_FREE

Rejestr tylko do odczytu. Odczytana wartość jest liczbą wolnych miejsc w kolejce poleceń (tyle słów można natychmiast zapisać do CMD_MANUAL_FEED bez obaw o FEED_OVERFLOW). Kolejka ta ma maksymalną pojemność 255 słów. Można założyć, że ten rejestr będzie równy 255 bezpośrednio po resecie urządzenia bądź gdy zostaną przetworzone wszystkie wcześniej wysłane polecenia.

BAR0 + 0x008c: CMD_MANUAL_FEED

Rejestr tylko do zapisu. Każdy zapis do tego rejestru wysyła jedno słowo do kolejki poleceń. Jeśli kolejka jest już pełna (CMD_MANUAL_FREE == 0), zamiast tego wyzwalane jest przerwanie FEED_ERROR.

Polecenie NOP

Nic nie robi. Składa się z 1 słowa. Może być użyte do zapchania nieużywanych słów w buforze poleceń, jeśli czujemy taką potrzebę.

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x0)

Polecenie BIND_SLOT

Służy do wpięcia bufora do slotu. Składa się z 2 słów:

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x1)

    • bity 4-7: SLOT — numer slotu, który przepinamy

    • bity 12-27: SEED — inicjalny SEED bufora

    • bity 28-31: TYPE— typ talii

  • słowo 1: - bity 0-31: PA: pierwsze 32 bity pełnego fizycznego adresu tabeli stron

  • słowo 2: - bity 0-31: PA: ostatnie 32 bity pełnego fizycznego adresu tabeli stron

Polecenie GET_CARDS

Służy do pobrania zadanej liczby kart. Składa się z 1 słowa:

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x2)

    • bity 4-19: NUM — liczba kart które chcemy pobrać

    • bity 20-23: OUTPUT_TYPE — Typ pobrania kart: FAIR (karty są uczciwie losowane) lub GOOD / BAD aby celowo zwracać lepsze i gorsze karty.

    • bity 24-27: SLOT— Numer slotu z buforem w którym zwracamy karty, ustawiane przez sterownik.

Polecenie FENCE

Służy do powiadamiania sterownika o zakończeniu wykonywania wszystkich poprzednich poleceń. Składa się z 1 słowa.

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x3)

    • bity 4-31: VAL — parametr; nie ma zdefinowanej semantyki przez urządzenie, zalecamy użycie go jako numeru kolejnej serii poleceń (modulo 2**28).

To polecenie ma następujące efekty:

  1. Oczekuje na pełne zakończenie wykonania wszystkich wcześniejszych poleceń.

  2. Ustawia wartość rejestru CMD_FENCE_LAST na parametr VAL

  3. Jeśli parametr VAL jest równy rejestrowi CMD_FENCE_WAIT, wyzwala przerwanie FENCE_WAIT.

BAR0 + 0x0090: CMD_FENCE_LAST

28-bitowy rejestr do odczytu i zapisu, ustawiany na parametr VAL przy wykonaniu polecenia FENCE. Może być użyty do ustalenia, które polecenia już zostały wykonane. Zalecamy zapisanie go ze sterownika dokładnie raz, przed uruchomieniem urządzenia, aby nadać mu wartość początkową.

BAR0 + 0x0094: CMD_FENCE_WAIT

Rejestr do odczytu i zapisu, służy do wyzwolenia przerwania gdy podane polecenie FENCE zostanie wykonane.

  • bity 0-27: VAL, wartość na którą sterownik czeka. Gdy zostanie wykonane polecenie FENCE z równą wartością, zostaje wyzwolone przerwanie FENCE_WAIT.

Polecenie NEW_DECK

Służy do wygenerowania i przetasowania nowej talii. Składa się z 1 słowa:

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x4)

    • bity 4-7: SLOT— Numer slotu z buforem w którym zwracamy karty, ustawiane przez sterownik.

Polecenie UNBIND_SLOT

Służy do odpięcia nieużywanego slotu. Składa się z 1 słowa:

  • słowo 0: nagłówek

    • bity 0-3: typ polecenia (0x5)

    • bity 4-7: SLOT— Numer slotu który zwalniamy po stronie urządzenia.

Rejestry sterujące

Poza wcześniej opisanymi rejestrami urządzenie używa również:

BAR0 + 0x0008: ENABLE

Rejestr kontrolujący pracę pozostałych bloków, dostępny do odczytu i zapisu. Jeśli jego wartość to 0 to komendy nie są przetwarzane.

BAR0 + 0x000c: INCREMENT_SEED

Rejestr odpowiadający za funkcjonalność inkrementacji seedów buforów przy nowym losowaniu, dostępny do odczytu i zapisu. Domyślnie jest ustawiony na 0 (m.in. dla łatwiejszego testowania), ale praktyczne zastosowanie urządzenia zakłada jego przestawienia na 1.

BAR0 + 0x0010: PROC_STATE

Rejestr odpowiadający za stan obsługi wczytanych poleceń, dostępny do odczytu i zapisu.

Przerwania

Urządzenie wewnętrznie używa 5 przerwań (które są agregowane w jedno przerwanie PCI):

  • FENCE_WAIT — przerwanie używane do powiadomienia sterownika o wykonaniu wybranego polecenia FENCE przez blok CMD.

  • FEED_ERROR — użycie rejestru CMD_MANUAL_FEED, gdy nie ma miejsca w kolejce

  • CMD_ERROR — wyzwalane, gdy urządzenie otrzyma niepoprawne polecenie

  • MEM_ERROR — wyzwalane, gdy urządzenie otrzyma nieprawidłowy dostęp do pamięci

  • SLOT_ERROR — wyzwalane, gdy urządzenie otrzyma zapytanie używające nieaktywnego slotu

Każde z powyższych przerwań może być w danej chwili aktywne bądź nie. Przerwanie staje się aktywne, gdy zajdzie odpowiednie zdarzenie. Przerwanie staje się nieaktywne, gdy sterownik zapisze 1 do odpowiedniego bitu w rejestrze INTR. Aby sprawdzić, które przerwania są aktywne, należy przeczytać rejestr INTR.

Niezależnie, każde z powyższych przerwań może być w danej chwili włączone bądź nie. Sterownik może ustawić włączony podzbiór przerwań przez zapis odpowiedniej maski do rejestru INTR_ENABLE. Urządzenie zgłasza przerwanie na swojej linii przerwań PCI wtedy i tylko wtedy, gdy istnieje włączone i aktywne przerwanie.

BAR0 + 0x0000: INTR

Rejestr statusu przerwań. Ma 5 bitów, każdy odpowiadający jednemu rodzajowi przerwań:

  • bit 0: FENCE_WAIT,

  • bit 1: FEED_ERROR,

  • bit 2: CMD_ERROR,

  • bit 3: MEM_ERROR,

  • bit 4: SLOT_ERROR

Odczyt tego rejestru zwróci 1 dla aktywnych przerwań, 0 dla nieaktywnych. Zapis spowoduje wyzerowanie (ustawienie na nieaktywne) wszystkich przerwań, dla których został zapisany bit 1.

BAR0 + 0x0004: INTR_ENABLE

Rejestr włączania przerwań, dostępny do odczytu i zapisu. Ma takie same bity jak INTR. 1 oznacza przerwanie włączone, a 0 — przerwanie wyłączone. Przy resecie maszyny, rejestr zostaje ustawiony na 0, blokując możliwość zgłaszania przerwania PCI przez urządzenie do momentu załadowania sterownika.

Uruchomienie urządzenia

Poprawna procedura uruchamiania urządzenia jest następująca (użycie dowolnej innej procedury spowoduje utratę gwarancji i brak możliwości zwrotu urządzenia):

  • wyzerować INTR

  • włączyć używane przez nas przerwania w INTR_ENABLE,

  • włączyć wszystkie bloki urządzenia w ENABLE.

  • zainicjować CMD_FENCE_LAST i CMD_FENCE_WAIT, jeśli czujemy taką potrzebę.

Żeby wyłączyć urządzenie, wystarczy zapisać 0 do ENABLE oraz INTR_ENABLE.

W przypadku zgłoszenia błędu przez urządzenie, należy je zresetować w sposób analogiczny do uruchamiania urządzenia.