poniedziałek, 2 września 2013

Programowanie sterowników pod Linux cz.5 - odczyt i zapis

Przyjrzyjmy się deklaracjom funkcji:

  • ssize_t read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
  • ssize_t write(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)

Argumenty:

  • struct file *filp - wskaźnik na plik, nie mylić z FILE który występuje w standardowej bibliotece C a nigdy w kodzie kernela
  • char __user *buff - wskaźnik na bufor przechowujący dane, należy pamiętać, że jest to wskaźnik z poziomu user-space, z tego powodu nie możemy go zwyczajnie wyłuskać w naszym module, który wykonywany jest w kernel-space
  • size_t count - rozmiar danych do transferu
  • loff_t *f_pos - aktualna pozycja w pliku

Jak zostało wspomniane wyżej, wskaźnika buff nie możemy wyłuskać. Tutaj z pomocą przychodzą nam funkcje zdefinowane w nagłówku <linux/uaccess.h>

  • unsigned long copy_to_user(void __user *to, const void *from, unsigned long count) - kopiuje dane z kernel-space do user-space
  • unsigned long copy_from_user(void *to, const void __user *from, unsigned long count) - kopiuje dane z user-space do kernel-space

Argumenty:

  • to - wskaźnik na adres docelowy
  • from - wskaźnik na adres źródłowy
  • count - rozmiar  danych do transferu w bajtach

Obie funkcje zwracają ilość bajtów, których nie udało się skopiować. W przypadku pełnego powodzenia zwracana jest wartość 0. Zaimplementujmy więc mechanizm zapisu i odczytu.

static ssize_t char_read(struct file *filp, char __user *buff, size_t count, loff_t *f_pos)
{
   static const ssize_t size = sizeof(device.array);
   printk("char_read()\n");

   if ((*f_pos) >= size)
      return 0;

   if (copy_to_user(buff,&device.array,size) != 0)
      return -EFAULT;

   (*f_pos) = size;
   return size;
}

Zmienna ssize_t size będzie przechowywać rozmiar tablicy z naszego wirtualnego urządzenia. Na początku sprawdzamy czy pozycja w pliku nie wykroczyła poza zakres. Jeśli tak, to kończymy odczytywanie. Następnie kopiujemy zawartość naszej tablicy do bufora znajdującego się w user-space. Ustawiamy odpowiednią pozycję pliku i zwracamy ilość skopiowanych bajtów.

static ssize_t char_write(struct file *filp, const char __user *buff, size_t count, loff_t *f_pos)
{
   printk("char_write()\n");

   if (copy_from_user(&device.array,buff,count) != 0)
       return -EFAULT;

   printk("Zapisano %s\n",device.array);
   return count;
}

Podczas zapisu korzystamy z funkcji copy_from_user, ponieważ kopiujemy dane z przestrzeni użytkownika do kernela. Zwracamy ilość zapisanych bajtów.

Z tak zaimplementowanymi funkcjami polecam sprawdzać działanie za pomocą prostego skryptu, np.




Po uruchomieniu skryptu:



2 komentarze:

  1. Jak przystosować funkcję read do dwóch lub wiecej urzadzeń? Dostać się jakoś przez file?

    OdpowiedzUsuń
    Odpowiedzi
    1. ok, to zwraca pointer do urzadzenia : file->private_data;
      Ale mimo wszystko laduje mi zawsze do jednego z urzadzen..

      Usuń