Menulis Linux Device Driver – File Operation

Melanjutkan diskusi sebelumnya, sekarang saatnya kita berinteraksi dengan user. Tentunya kernel module tanpa antarmuka dengan user adalah tidak berguna (meskipun banyak yang tidak berinteraksi secara langsung seperti apa yang akan kita praktekkan di akhir diskusi ini). Sebagai catatan – menghindari kenaifan- bentuk interaksi yang paling sederhana dari sebuah file adalah fasilitas untuk dibuka (open(2)), tutup (close(2)), baca/tulis (read/write), dan I/O control.

Fasilitas tersebut didefinisikan di bagian file operation dari sebuah kernel module. Kita akan membuat satu character device paling sederhana dengan beberapa file operations di dalamnya untuk lebih memahami di mana file operation tersebut diletakkan dan bagaimana menggunakannya dari aplikasi.

static struct file_operations foo_fops = {
        .owner = THIS_MODULE,
        .ioctl = foo_ioctl,
        .write = foo_write,
        .read = foo_read,
        .open = foo_open,
        .release = foo_close,
};

Pada intinya, definisi file operations pada sebuah kernel module adalah fasilitas yang dapat digunakan oleh user atau system administrator. Secara teknis, file operation adalah sebuah struct yang terdiri dari pointer to function pada method yang nantinya akan dipanggil ketika file operation digunakan oleh user.

Kode di atas dapat dengan mudah kita pahami, setiap member dari struct file_operations foo_fops diisi dengan method dengan nama yang hampir mirip (ini bukan aturan, tentunya). Mari kita tengok lagi konsep tentang file di sistem Linux, segala sesuatu dianggap sebagai file begitu pula sebuah device. Dalam diskusi ini kita pakai sebuah character device yang di dalam filesystem direpresentasikan dengan sebuah file yang terletak di direktori /dev .

Sama seperti file reguler, character device ini juga dapat dibuka, ditutup, dibaca, ditulis. Dengan pengertian tersebut, maka foo_open adalah method yang akan dipanggil ketika si character device ini dibuka, foo_read dipanggil ketika ada perintah read(2) atas file handler si character device, dan seterusnya.

Open

Ketika aplikasi dari user membuka file dengan perintah open atau fopen, maka file operation open ini yang akan dipanggil kemudian mengembalikan error code, kemudian sistem mengembalikan file handler ke aplikasi untuk dipakai di kebutuhan selanjutnya. Bentuk umum dari open sebagai berikut:

static int foo_open(struct inode *inode, struct file *filp)

Close/Release

Sudah selesai? Kita perlu menutup file untuk membebaskan resource yang dipakai oleh aplikasi, salah satunya adalah file handler hasil dari perintah open di atas. Bentuk dari method close adalah sebagai berikut.

static int foo_close(struct inode *inode, struct file *filp)

Read

Banyak driver yang langsung menyediakan data ke user space. Sebuah aplikasi cukup membuka, lalu membaca data dari file handler untuk digunakan lebih lanjut. Untuk menyediakan fasilitas tersebut file operations memiliki elemen .read.

static ssize_t foo_read(struct file *filp,
                          char __user * buf,
                          size_t count,
                          loff_t * f_pos)

Write

Sama seperti namanya, write adalah fasilitas bagi aplikasi user untuk menuliskan “sesuatu” ke si device driver.

static ssize_t foo_write(struct file *filp,
                            const char *buff,
                            size_t len,
                            loff_t * off)

Baik, kita telah memiliki cukup bekal untuk menulis sebuah driver dengan file operation seperti di atas. Akan tetapi masih ada satu pertanyaan, bagaimana membuat sebuah driver yang dapat diakses dari user? Kita akan gunakan API dari kernel untuk mendefinisikan character driver di /dev.

Mendeklarasikan kemudian mendaftarkan sebuah character device perlu beberapa langkah, yang pertama dari kode kernel module dan yang kedua dari file system. Dari kernel module, kita perlu untuk melakukan registrasi nama dan MAJOR/MINOR number dari device beserta file operation yang tersedia.

ret = register_chrdev(MAJOR_NUM, DEVICE_NAME, &foo_fops);
 |            |            |                     |
return    registrasi  MAJOR number                    file operation

Dari user, kita perlu melakukan perintah berikut:

mknod /dev/DEVICE_NAME c MAJOR_NUM MINOR

Sesuaikan dengan nilai yang diisikan pada kode kernel module sebelumnya. Setelah melakukan perintah di atas, satu character device terbentuk di /dev/DEVICE_NAME. Sesuaikan DEVICE_NAME dengan kode di bawah (DEV_NAME), maka kita jumpai /dev/foo.

/* foo.c - the real foo device driver */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>

#define RET_OK    0
#define MAJOR     100
#define DEV_NAME  "foo"

static ssize_t foo_write(struct file *filp, const char *buff,
                                           size_t len, loff_t * off)
{
        printk(KERN_INFO "foo ditulisi! n");
        return RET_OK;
}

static ssize_t foo_read(struct file *filp, char __user * buf,
                                       size_t count, loff_t * f_pos)
{
        printk(KERN_INFO "foo sedang dibaca! n");
        return RET_OK;
}

static int foo_open(struct inode *inode, struct file *filp)
{
        static int rv;
        printk(KERN_INFO "foo buka toko! n");
        if (rv = try_module_get(THIS_MODULE)) {
                return RET_OK;
        } else {
                printk(KERN_ALERT "Error load/unload module!n");
                return rv;
        }
}

static int foo_close(struct inode *inode, struct file *filp)
{
        printk(KERN_INFO "foo tutup toko! n");
        module_put(THIS_MODULE);
        return RET_OK;
}

static struct file_operations foo_fops = {
        .owner = THIS_MODULE,
        .write = foo_write,
        .read = foo_read,
        .open = foo_open,
        .release = foo_close,
};

static int __init foo_init(void)
{
        int ret;
        ret = register_chrdev(MAJOR, DEV_NAME, &foo_fops);
        if (ret < RET_OK) {
                 printk(" %s %d", DEV_NAME, ret);
                 return ret;
        }
        printk(KERN_INFO "Init Foo dipanggil! n");
        return RET_OK;
}
module_init(foo_init);

static void __exit foo_exit(void)
{
        unregister_chrdev(MAJOR, DEV_NAME);
        printk(KERN_INFO "Exit Foo dipanggil!n");
}
module_exit(foo_exit);

MODULE_AUTHOR("Lutung Kode");
MODULE_DESCRIPTION("Foo Kite Flyer");
MODULE_LICENSE("GPL");

Pada kode di atas, ada baris try_module_get() pada device open, dan module_put() pada device release. Keduanya adalah antarmuka untuk kernel reference count yang berguna untuk menghindari module unloading ketika module sedang dipakai. Ketika aplikasi membuka device/open, maka reference count bertambah dan berkurang ketika menutup/release.

Hendaknya, return value dari try_module_get selalu dicek, Ketika gagal maka nilai balikan dari try_module_get adalah 0 dan selain itu berarti sukses (asalkan bernilai positif).

Bagaimana mengetes device driver yang baru saja kita buat? Caranya sangat mudah yaitu dengan sebuah aplikasi sederhana untuk membuka/tutup/read/write ke device node yang telah kita buat (/dev/foo). Berikut contoh kode untuk mengetes driver di atas:

/* fooTester.c - the flyer shooter tester */
#include <stdio.h>
#include <sys/fcntl.h>

int main(int argc, char *argv[])
{
        int fd;
        unsigned char tmp[10];

        /* Test .open fops */
        fd = open("/dev/foo", O_WRONLY);
        if (fd < 0) {
                perror("/dev/foo");
                return -1;
        }

        /* Test .read fops */
        read(fd, &tmp, sizeof(tmp));

        /* Test .write fops */
        write(fd, &tmp, sizeof(tmp));

        /* Test .release fops */
        close(fd);
        return 0;
}

Jangan lupa buat Makefile untuk mengompile kode di atas😉

Kita belajar beberapa hal kali ini:

  1. Character device (register/unregister)
  2. File operation
  3. Aplikasi user untuk buka/tutup/baca/tulis
  4. perintah mknod
  5. Kernel reference counter (put/get)
Pos ini dipublikasikan di Linux dan tag , , , . Tandai permalink.

Tinggalkan Balasan

Isikan data di bawah atau klik salah satu ikon untuk log in:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s