sayfa başı

14 Mart 2015 Cumartesi

Adım Adım Linux Driver Yazıyoruz -3-

     Yolu buraya düşen herkse ilk olarak Merhaba. Bugün yazdığımız modülü derleyip çalıştıracağız. Bir önceki yazımda makefile oluşturacağımzı bahsetmiştim. Bu yazımda da Makefile hakkında kısa bir yazı yazmıştım. Bu yazılardan sonra kaldığımız yerden devam edelim.
    İlk olarak modülümüz derlememize yardımcı olacak Makefile kodunu aşağıda bulunmakta. Makefile dosyası ile .c uzantılı modül kodumuzu aynı dizine koyalım ve konsoldan o dizine gidelim. Derleme için tek yapmamız gereken konsola make yazıp entere basmak. Hepsi bu. Belki KERNEL_SOURCE := /usr/src/linux-headers-3.13.0-32-generic bağlı olarak hata alabilirsiniz. Onu da kendi PC bulunan ayara göre düzenleyerek çözebilirsiniz. Zaten /usr/src/ dizinine giderseniz orada linux-...... diye birşey görürsünüz. Gerisi kopyala yapıştır.

# Makefile – makefile of our first driver

# if KERNELRELEASE is not defined, we've been called directly from the command line.
# Invoke the kernel build system.
ifeq (${KERNELRELEASE},)
 KERNEL_SOURCE := /usr/src/linux-headers-3.13.0-32-generic
 PWD := $(shell pwd)
default:
 ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules

clean:
 ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} clean

# Otherwise KERNELRELEASE is defined; we've been invoked from the
# kernel build system and can use its language.
else
 obj-m := zafer.o //derlenecek .c dosyasının ismi verilmeli
endif
    Derleme işleminden sonra elimizde .ko uzantılı bir dosya oluştu. Bundan sonra yapmamız gereken ilk iş ya seriport üzerinden ya da tftp ile bu dosyayı cihaza aktarmamız olacak. Bu adımı da başarılı bir şekilde yaptığımızı varsayarak sıradaki işlemlere geçelim.

    Cihazımıza .ko uzantılı kernel objesini  yükledik. Peki bu dosyayı nasıl çalıştıracağız. Tabiki normal çalıştırılabilir dosyalar gibi ./isim.ko şeklinde çalıştıramayız. Modül dosyalarını çalıştırmak ya da durdurmak için tanımlanmış komutlar vardır. Özetle onlara bir göz atalım.
  • lsmod: yüklenmiş modül dosyalarını listeler
  • insmod: modülü Kernel yükler yani çalışmasını sağlar
  • rmmod: Kernel yüklenen modülü geri alır. Çalışmasını durdurur.
    Şu bilgiyi hatırlayalım: yüklenmiş modüller major numarası ve isimleri ile /proc/devices/ dizini altında görülür. Yazdığımız modülün Kernel yüklenmesi yani çalışır duruma gelmesi için insmod komutunu koşalım $insmod modül_ismi koşalımŞimdi modülümüz Kernel yüklenmiş oldu. Teyit etmek için cat /proc/devices/ komutunu kullanalım. Verdiğimiz isim ve major numarası ile cihazımız tam karşımızda olması lazım. Major numarasını bir yere not edelim. Kısa bir süre bu numara bizim için önemli olacak.
    Madem insmod ile modülü yükledim, o zaman /dev/ altında normal cihazlar gibi görüp kullanmam gerekir diyenlere bunun şimdilik imkansız olduğunu söylemek gerekir. Cihaz dosyasını /dev/ altında oluşturma işini şimdilik elle yapacağız. 
             sudo mknod /dev/isim c 250 0
   Buradaki isim bağımsız olarak verebileceğimz bir isim, istediğiniz isimi verebilirsiniz. c char device file. 250 hani kaydettiğimiz major numarası vardı ya işte o değer, 0 ise cihaza atayacağımız minor numarası. Bu adımdan sonra ls -l /dev/ yaptığıızda /dev/ altında yeni oluşan dosyayı görebiliriz.


Otomatik Cihaz dosyasını(Device File) Oluşturma

    Bir önceki adımda cihaz dosyasını(Device File) mknod komutu yardımıyla elle oluşturduk. Şimdiki adımımız bu işlemi otomatik olarak yapma.
    2.4 Kernel versiyonna kadar device file oluşturma işi devfs ait uygun API ile Kernel tarafından otomatik olarak yapılırdı. Kernel' ın gelişmesiyle geliştiriciler cihaz dosyasının kullanıcı katmanına ait bir olduğunu düşünerek bunun kullanıcılar tarafından yapılmasına karar verdiler.
    <linux/device.h> altındaki fonksiyonlara bakarak bu işlemi gerçekleştirelim.
struct class *cl;

if (IS_ERR(cl = class_create(THIS_MODULE, MY_DRIVER_CLASS_NAME)))
{
 rv = PTR_ERR(cl);
 goto err_class_create;
}

if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, MY_DRIVER_DEVICE_FILE_NAME)))
{
 rv = PTR_ERR(dev_ret);
 goto err_device_create;
}
    Bu kod parçasını init fonksiyomuz içine koyarak cihaz dosyasını otomatik olarak oluşturmuş oluruz. IS_ERR makrosu ile fonksiyonların geri dönüş değerleri test edildi. Her ne kadar C kullananlar goto kullanmayı sevmese de burada goto iyi bir tercih. “Linux Device Drivers” kitabinda sayfa 33' de goto için yazılanlar aynen size aktarıyorum. "We normally hate to use goto, but in our opinion, this is one situation where it is useful."  Bu cümlenin ardından verdikleri örnek de şu şekilde:
int __init my_init_function(void)
{
   int err;

   /* registration takes a pointer and a name */
   err = register_this(ptr1, "skull");
   if (err) goto fail_this;
   err = register_that(ptr2, "skull");
   if (err) goto fail_that;
   err = register_those(ptr3, "skull");
   if (err) goto fail_those;
   return 0; /* success */
   fail_those: unregister_that(ptr2, "skull");
   fail_that: unregister_this(ptr1, "skull");
   fail_this: return err; /* propagate the error */
}
    Peki IS_ERR hata olduğunu bildirir ve goto nun gösterdiği yere gidersek burada ne yapacağız. 
err_device_create:
 class_destroy(cl);
err_class_create:
 cdev_del(&c_dev);
    Burada bir şeye dikkat çekmek isterim. İlk sorguya ait hata işleme yeri ikinci sorguya ait hata işleme yerinden daha sonra yazılmış yani en son adım hata oluşursa kendinden önceki tüm adımlarda yapılan işlemler hatalıymış gibi onlar da geri alınmalı. Hataları ele alma yeri init fonksiyonun sonunda return hemen önce olmalı. Ayrıca exit fonksiyonumuzda da bu durumları ele almalıyız. Bu kadar sözel açıklamadan sonra kodun son halini görerek anlatılanların resmine bakalım.
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>

static dev_t dev;          //dev device number
static struct cdev c_dev;   //character device structure
static struct class *cl;   //device class


static int __init myModule_init(void) /* Constructor */
{
 int retVal = -EACCES;
 struct device *dev_ret;

 if ((retVal = alloc_chrdev_region(&dev, 0, 1, "zafer")) < 0)
 {
  goto err_alloc_dev_region;
 }

 if (IS_ERR(cl = class_create(THIS_MODULE, "chardriver")))
 {
  retVal = PTR_ERR(cl);
  goto err_class_create;
 }

        /*Burada verdigimiz isim /dev/ altinda gorulur "myDevice"*/
 if (IS_ERR(dev_ret = device_create(cl, NULL, dev, NULL, "myDevice"))) 
 {
  retVal = PTR_ERR(dev_ret);
  goto err_device_create;
 }
 return 0;

err_device_create:
 class_destroy(cl);
err_class_create:
 cdev_del(&c_dev);
err_cdev_add:
 unregister_chrdev_region(dev, 1);
err_alloc_dev_region:
 return retVal;
}

static void __exit myModule_exit(void) /* Destructor */
{
 cdev_del(&c_dev);
 device_destroy(cl, dev);
 class_destroy(cl);
 unregister_chrdev_region(dev, 1);
 printk(KERN_INFO "myModule unregistered");
}

module_init(myModule_init);
module_exit(myModule_exit);

MODULE_AUTHOR("Zafer Satilmis");
MODULE_DESCRIPTION("First Character Driver");
    Artık modülümüz hazır. /dev/ altında da cihaz dosyamızı görür durumdayız. Bundan sonraki adım kullanıcı katmanında bu cihaz dosyası ile cihazı kontrol etmek. Bunun için son adım olan dosya operasyonları kodunu yazacağız. Dosya operasyonları ile cihazımız üzerinde okuma, yazma işlemleri yapacağız. ioctl(input/output control) ile cihaza kendimizin tanımladığı komutları vereceğiz. Bu işlemler sonucunda tam kullanışlı bir iş yapmış olacağız. 

Faydalanacağını düşündüğünüz kişilere iletin, eksik ya da yanlış olduğunu düşündüğünüz yerleri belirtin.


Şimdi Yazmaya Devam ....




Hiç yorum yok:

Yorum Gönder

Son Ütücü