30 Aralık 2017 Cumartesi

LINUX KERNEL ILE RASPBERRY PI 3'DEN TCP PROTOLÜ ILE SENSÖR VERILERI ALINMASI

     
     Bu yazımızda Raspberry Pi 3 ile bağlanan DHT11 Sıcaklık Nem sensörü ve Water Sensor 'den alınan verileri TCP protoölü ile kernel modda loga yazılmasından bahsedeçeğiz.

     TCP/IP Nedir ?
     (Transmission Control Protocol/Internet Protocol), bilgisayarlar ile veri iletme/alma birimleri arasında organizasyonu sağlayan, böylece bir yerden diğerine veri iletişimini olanaklı kılan pek çok veri iletişim protokolüne verilen genel addır. (Yani, TCP/IP protokolleri bilgisayarlar arası veri iletişiminin kurallarını koyar). Bu protokollere örnek olarak, dosya alma/gönderme protokolü FTP (File Transfer Protocol), Elektronik posta iletişim protokolü SMTP (Simple Mail Transfer Protocol), TELNET protokolü(Internet üzerindeki başka bir bilgisayarda etkileşimli çalışma için geliştirilen *login* protokolü) verilebilir. Adını sıkça duyduğumuz WWW ortamında birbirine link objelerin iletilmesini sağlayan protokol ise Hyper Text Transfer Protocol (HTTP) olarak adlandırılmaktadır. TCP/IP protokolü aynı zamanda, diğer iletişim ağlarında da kullanılabilir. Özellikle pek çok farklı tipte bilgisayarı veya iş istasyonlarını birbirine bağlayan yerel ağlarda (LAN)kullanımı yaygındır.
     
     Raspberry Pi 3 Nedir ?
     Raspberry Pi kredi kartı boyutunda bir bilgisayardır. Televizyonunuza bağlayıp görüntü alabilir, bir klavye bağlayabilirsiniz. Yetenekli küçük bir bilgisayar diye adlandırdığımız Raspberry Pi ile normal masaüstü bilgisayarlarda yaptığınız işleri örneğin, sözcük işlemciler ve hesap programları (Word, Excel) ile çalışabilir çeşitli oyunlar oynayabilirsiniz. Ayrıca yüksek çözünürlüklü HD videolar oynatabilirsiniz. Ayrıca tüm dünyada çocukların alıp kullanabileceği, basit programlama yapabilecekleri hatta deneylerinde kullanabileceği uygun fiyatlı bir bilgisayar gibi düşünebilirsiniz.

     Gerekli donanımlar ve yazılımlar :
     Raspberry Pi 3 işletim sistemi kurulması
     İndirdiğimiz imaj dosyasını zip içerisinden çıkarıyoruz. Ardından win32diskImager( https://www.gezginler.net/indir/win32-disk-imager.html ) programını açıyoruz. İmaj dosyamızı belirtilen yerden seçiyoruz. Sd kartınızın bilgisayara takılı olduğundan emin olduktan sonra Devic ekısmında görebilirsiniz. Ardından Write butonuna tıklayıp yazma işlemini başlatıyoruz. Yazma işlemi yaklaşık 2-3 dk sürmektedir. Yazma işleminin bitmesini yeni açılan pencerede "Write Succesful." yazısını görene kadar bekleyiniz.
     SD Kartı Formatlamak için Disk Formatter programını kullanıyoruz.

     İşletim Sistemi kurulan Raspberry Pi ile gerekli bağlantıları yapıyoruz.

     Şimdi sıra bir Raspberry Pi’ye hayat vermeye geldi. Önce SD Kartı bilgisayardan çıkarıp Pi 3’e takıyoruz. Raspberry Pi’yi direk olarak HDMI bir ekrana bağlıyoruz. Elimizde HDMI girişi olan bir ekran yoksa VGA girişli bir ekran da kullanabiliriz. Bu durumda HDMI to VGA dönüştürücü kullanmamız gerekecek. Son olarak adaptör ile cihazımıza enerji veriyoruz. İlk olarak sizi şu şekilde bir ekran karşılayacak;


     VNC SERVER ile Bağlantı kurmak (isteğe bağlı)

Raspberry Pi üzerindeki soketleri kullanmadan bilgisayarımızdaki klavye, mouse gibi aygıtları kullanmak için VNC Viewer Kullanabiliriz. RealVNC'in VNC Viewer programını indirip bilgisayarınıza kurun ( https://www.realvnc.com/en/connect/download/viewer/ ) ve RPI konsoluna şu komutları sırayla yazın ; 

  • sudo apt-get update 
  • sudo apt-get upgrade
  • sudo apt-get install tightvncserver 

VNC Serverımız Raspberry Pi üzerinde kuruldu. Artık tek bir komutla VNC Server'ı başlatabiliriz. 
  • vncserver :1 
Bilgisayarımıza indirdiğimiz VNC Viewer programını başlatalım ve sol üst köşedeki File sekmesinden yani bir bağlantı oluşturalım. Açılan pencerede VNC Server yazan yere Angry Ip Scanner'dan öğrendiğimiz özel ip adresini (192.168.137.xxx) girelim ayrıntı unutulmamalıdır, ip adresinin sonuna portumuzu (1) eklemeliyiz. Yani VNC Server kısmına girilecek adres şuna benzemelidir ; 
  • 192.168.137.12:1 
Bağlantıyı başlatınca Raspberry Pi’nin masaüstü ekranınıza ulaşacaksınız. Şimdi projeye devam edersek, konsol ekranına ulaştığınızda şu komutları girerek RPI'nin güncel olmasını sağlayalım ;
  • sudo apt-get update 
  • sudo apt-get upgrade 
Ardında programlamamızı C dilinde yapacağımız için C derleyicisi (gcc) gerekli bunun için; 
  • sudo apt-get install gcc 
Ve sensörü bağlamak için wiringPi yazılımı gerekli. 
  • sudo apt-get install 
  • git-core cd git clone git ://git.drogon.net/wiringPi 
Ardından sensörü Raspberry Pi'ye bağlayıp programı yazmaya başlayabiliriz.

     Sensorlerin Kurulması

Sensörler üzerinde bulunan;
  • + pini : 3v/5v Güç
  • - pini : Topraklama
  • D/S pini : Data Pinidir
DHT11 Bağlantı Pini (7. pin soketine bağlandı)
Water Sensor Pini (7. pin soketine bağlandı)

     Raspberry Pi 3 Gerekli C kodlarının yazılması

Eğer ki terminal üzerinden ;
  • vim / vi shell komutları ile
yazmak istemiyorsanız shell üzerinden gedit text editörünü tavsiye ederiz.

  • sudo apt-get install gedit

 Water Sensor İçin Raspberry Pi kodları (source.c)(TCP Server):
//Rasp. Pi kütüphanesi
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//TCP Kütüphaneleri
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h> 
 
//Bağlanılan Pin numarası
#define PIN_NO             7
int main( void )
{
    //TCP bağlantı kodları
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        puts("Could not create socket");
         exit(0);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    //TCP port numarası
    server.sin_port = htons( 8888 );
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
 
        puts("bind failed. Error");
         exit(0);
    }    
    listen(socket_desc , 3);
    c = sizeof(struct sockaddr_in);    
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
        puts("accept failed");
        exit(0);
    }
         if ( wiringPiSetup() == -1 )
         {
                 exit( 1 );
         }
 
         //Sensor kurulumu için 
         pinMode( PIN_NO, OUTPUT );
         digitalWrite( PIN_NO, LOW );
         digitalWrite( PIN_NO, HIGH );
         pinMode( PIN_NO, INPUT );
         delay( 900 );
         //Sensorden gelen verileri kontrol etmek için
         while ( 1 )
         {
                 if(digitalRead( PIN_NO ) == HIGH )
                 {
                          printf("Su Alarm : Aktif\n");
                          char *msg="Su Alarm : Aktif";                          
                          write(client_sock , msg , strlen(msg));    
                 }
                 else
                 {
                          printf("Su Alarm : Pasif\n");
                          char *msg="Su Alarm : Pasif";
                          write(client_sock , msg , strlen(msg)); 
                 }
                 delay( 2000 );
         }
         
         return(0);
}
 DHT11 Sıcaklık Nem İçin Raspberry Pi kodları (source.c)(TCP Server):
//Rasp. Pi kütüphanesi
#include <wiringPi.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

//TCP Kütüphaneleri
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h> 
 
//Bağlanılan Pin numarası
#define PIN_NO             7
int main( void )
{
    //TCP bağlantı kodları
    int socket_desc , client_sock , c , read_size;
    struct sockaddr_in server , client;
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        puts("Could not create socket");
         exit(0);
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    //TCP port numarası
    server.sin_port = htons( 8888 );
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
 
        puts("bind failed. Error");
         exit(0);
    }    
    listen(socket_desc , 3);
    c = sizeof(struct sockaddr_in);    
    client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (client_sock < 0)
    {
        puts("accept failed");
        exit(0);
    }
    //Sensor'den verilerin okunması
    while(1){
    uint8_t laststate = HIGH;
 uint8_t counter  = 0;
 uint8_t j  = 0, i;
 float f; /* fahrenheit */

 dht11_dat[0] = dht11_dat[1] = dht11_dat[2] = dht11_dat[3] = dht11_dat[4] = 0;
 pinMode( DHTPIN, OUTPUT );
 digitalWrite( DHTPIN, LOW );
 delay( 18 );
 digitalWrite( DHTPIN, HIGH );
 delayMicroseconds( 40 );
 pinMode( DHTPIN, INPUT );

 for ( i = 0; i < MAXTIMINGS; i++ )
 {
  counter = 0;
  while ( digitalRead( DHTPIN ) == laststate )
  {
   counter++;
   delayMicroseconds( 1 );
   if ( counter == 255 )
   {
    break;
   }
  }
  laststate = digitalRead( DHTPIN );

  if ( counter == 255 )
   break;

  
  if ( (i >= 4) && (i % 2 == 0) )
  {
   dht11_dat[j / 8] <<= 1;
   if ( counter > 16 )
    dht11_dat[j / 8] |= 1;
   j++;
  }
 }


 if ( (j >= 40) && (dht11_dat[4] == ( (dht11_dat[0] + dht11_dat[1] + dht11_dat[2] + dht11_dat[3]) & 0xFF) ) )
 {
  char msg[100];
  sprintf(msg,"Sıcaklık = %d.%d *C | Nem = %d.%d  ",dht11_dat[2], dht11_dat[3], dht11_dat[0], dht11_dat[1]);
  write(client_sock , msg , strlen(msg));
 }
 else
 {
  char *msg="Veriler hatalı..";
  write(client_sock , msg , strlen(msg)); 
 }
 delay(1000);
 }
}
Derlemek için gerekli kütüphanlerin indiirlmesi
  • sudo apt-get update
  • sudo apt-get upgrade
  • sudo apt-get install git-core
  • git clone  git ://git.drogon.net/wiringPi
  • sudo apt-get install gcc
C Kodunun derlenmesi ve Çalıştırıması

  • gcc -o serverTCP source.c -lwiringPi -lwiringPiDev
  • sudo ./serverTCP 
     
      Ethernet Kablosu ile bağlanılan bilgisayar için(TCP Client)

Öncellikle bu yazımızda bahsetiğimiz kernel kurulumunu yapıyoruz ve daha sonra bu yazımızda bahsedinilen kernel modül programlama adımlarını yapıp chachter device yapısını aşağıdaki kodu yazıyoruz


#include <linux/module.h>

#include <linux/device.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <asm/uaccess.h>

#include <linux/init.h>

//TCP Socket Kütüphaneleri

#include <linux/net.h>

#include <net/sock.h>

#include <linux/tcp.h>

#include <linux/in.h>

#include <linux/socket.h>

#include <linux/slab.h>



MODULE_LICENSE("GPL");

MODULE_AUTHOR("NativeCore");


//port numarası
#define PORT 8888  

#define DEVICE_NAME "NativeCore"

#define CLASS_NAME "CoreClass"

#define MAX_SIZE 1024

static int    majorNumber; 

static struct class*  coreClass  = NULL;

static struct device* coreDevice = NULL;

struct socket *conn_socket = NULL;

static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset);

static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset);

u32 CreateIP(u8 *ip);

int ClientReceive(struct socket *sock, char *str,unsigned long flags);

int ClientConnect(void);

static char DeviceBuffer[MAX_SIZE];

static int BufferSize;

static struct file_operations fo =

{

 .owner = THIS_MODULE,

 .read = DeviceRead,

 .write = DeviceWrite

};

static int onStart(void)

{

        printk("NativeCore | Communucation to Raspberry Pi Water & Tempature Sensor  \n");

 majorNumber = register_chrdev(0, DEVICE_NAME, &fo);

 if (majorNumber<0)

 {

  printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber);

  return majorNumber;

 }

 coreClass = class_create(THIS_MODULE, CLASS_NAME);

 if (IS_ERR(coreClass))

 {

  unregister_chrdev(majorNumber, DEVICE_NAME);

  printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber);

  return PTR_ERR(coreClass);

 }

 coreDevice = device_create(coreClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME);

 if (IS_ERR(coreDevice)){

  class_destroy(coreClass);

  unregister_chrdev(majorNumber, DEVICE_NAME);

  printk(KERN_ALERT "Device oluşturulmadı\n");

  return PTR_ERR(coreClass);

 }

 printk(KERN_ALERT"Device oluşturuldu %d : %s.\n",majorNumber,DEVICE_NAME);

 return 0;

}

static void onExit(void){

 device_destroy(coreClass, MKDEV(majorNumber, 0));

 class_unregister(coreClass);

 class_destroy(coreClass);

 unregister_chrdev(majorNumber, DEVICE_NAME);

 printk(KERN_ALERT "Device silindi %d : %s.\n",majorNumber,DEVICE_NAME);

        DECLARE_WAIT_QUEUE_HEAD(exit_wait);

        if(conn_socket != NULL)

        {

                sock_release(conn_socket);

        }

        printk("Connection Canceled. \n");



}

static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset)

{

 ssize_t sst = 1;

 return simple_read_from_buffer(buffer,len,offset,DeviceBuffer,sst);

}

static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset){

 printk(KERN_ALERT "DeviceWrite : çalıştırıldı.\n");

 BufferSize = len;

 if (BufferSize > MAX_SIZE) 

 {

  BufferSize = MAX_SIZE;

 }

 if (copy_from_user(DeviceBuffer, buffer, BufferSize)) 

 {

  printk(KERN_ALERT "DeviceWrite : Device dosyası kopyalanmadı\n");

  return -EFAULT;

 }

 ClientConnect();

 return BufferSize;

}

u32 CreateIP(u8 *ip)

{

 u32 addr = 0;

 int i;

        for(i=0; i<4; i++)

        {

  addr += ip[i];

                if(i==3)

  {

  break;

  }

                addr <<= 8;

        }

        return addr;

}

int ClientReceive(struct socket *sock, char *str,\

                        unsigned long flags)

{

        struct msghdr msg;

        struct kvec vec;

        int len;

        int max_size = 50;

        msg.msg_name    = 0;

        msg.msg_namelen = 0;

        msg.msg_control = NULL;

        msg.msg_controllen = 0;

        msg.msg_flags   = flags;

        vec.iov_len = max_size;

        vec.iov_base = str;

read_again:

        len = kernel_recvmsg(sock, &msg, &vec, max_size, max_size, flags);



        if(len == -EAGAIN || len == -ERESTARTSYS)

        {

                printk("Error %d : A Problem occured while receive data from TCP/IP\n", len);

                goto read_again;

        }

 printk("%s\n",str);

        return len;

}



int ClientConnect(void)

{

        struct sockaddr_in saddr;
        //Ethernet kablosu ile bağlantı kurulan IP adresi
        unsigned char destip[5] = {192,168,0,21,'\0'};

        int len = 49;

        char response[len+1];

        int ret = -1;

        DECLARE_WAIT_QUEUE_HEAD(recv_wait);       

        ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &conn_socket);

        if(ret < 0)

        {

                printk("Error %d : Server or port does not connection\n", ret);

                goto err;

        }



        memset(&saddr, 0, sizeof(saddr));

        saddr.sin_family = AF_INET;

        saddr.sin_port = htons(PORT);

        saddr.sin_addr.s_addr = htonl(CreateIP(destip));



        ret = conn_socket->ops->connect(conn_socket, (struct sockaddr *)&saddr\

                        , sizeof(saddr), O_RDWR);

        

 if(ret && (ret != -EINPROGRESS))

        {

                printk("Error: %d : setup_connection \n", ret);

                goto err;

        }

read_again:

        wait_event_timeout(recv_wait,!skb_queue_empty(&conn_socket->sk->sk_receive_queue),2*HZ);

                  

 if(!skb_queue_empty(&conn_socket->sk->sk_receive_queue))

        {

          memset(&response, 0, len+1);

         ClientReceive(conn_socket, response, MSG_DONTWAIT);

  goto read_again;

        }

err:

        return -1;

}

module_init(onStart);

module_exit(onExit);
C Kodunun derlenmesi ve Çalıştırıması

  • gcc -o clientTCP source.c 
  • sudo ./clientTCP 
      Sensorlerin ve Modülün Çalıştırılması

Kodlar yazıldıktan sonra Server kurulumu olan Raspberry Pi ile kodlarımızı çalıştırmaya başlıyoruz ve Serverimizi başlatıyoruz.

  • sudo ./serverTCP
Water Sensor İçin Raspberry PI (Server)

Server başladıktan sonra ağa bağlanan bilgisayar ile TCP üzerinden kernel modda verilerimizi alıyoruz.

  • insmod source.ko
  • echo "GET">/dev/NativeCore


Water Sensor İçin Ağa Bağlanan Bilgisayar (Client)

DHT11 İçin Ağa Bağlanan Bilgisayar (Client)

Proje Mimarisi ve Genel Adımları :
  • Raspberry Pi’ye İşletim Sistemi Yükleme
  • Sensörleri Gerekli GPIO Pinine Bağlamak
  • Eğer Harici Ekipmanlar Kullanmak İstemiyosak PUTTY Ve VNCServer’i İle Raspberry Pi’yi Bilgisayara Bağlamak
  • Raspberry Pi Üzerine Öncelikle Seçtiğimiz Sensörden Verileri Okuyacak C Kodunu Yazmak 
  • Ek Olarak TCP/IP Protokoli İle Bu Okunan Değerleri Ağa Bağlı Olan Diğer Bilgisayarlara Mesaj Göndermek
  • Raspberry Pi Üzerinden Kodumuzu Derlemek Ve Çalıştırmak
  • Ağdaki Diğer Bilgisayardan Kaynak Kod İle Kurduğumuz Kernel Üzerine Bir Modül Oluşturmak 
  • Bu Modüle Gerekli Character Device Kodları Yazma 
  • Ek Olarak Kernel Mod İçin Socket Data Send Receive İçin C Kodlarını Fonksiyon Olarak Yazma 
  • Ağ Üzerinde Bağlı Olan Raspberry Pi’nin Ağ İp’sini Bulma 
  • TCP Protokolü İçin IP Ve Port Değerlerini Yazma 
  • Make Komutu İle Modülümüzü Derlemek Ve Character Device’yi Oluşturmak 
  • ins Mod xxx  Modülümü Devreye Sokma
  • Raspberry Pi Üzerinden sudo ./xxx ile Raspberry Pi Üzerinden Kodumuzu Çalıştırmak 
  • echo ’get’>/dev/nativecore ile Character Device Right Methodunu Etkinleştirmek
  • Character Device Dosyasına Yazılan Raspberry Pi Üzerinden TCP Protokolü İle Alının Verileri Kernel Satırına Yazdırmak 
  • dmesg İle Verileri Görmek RMMOD xxx İle Modülden Çıkış Yapmak 



TCP/IP protokolü Raspberry Pi sensöründen alınan verileri Kernel Modda Kernel loga yazmayı anlattık Bu yazımızın videolu sunumu ;



8 Aralık 2017 Cuma

LINUX CHARACTER DRIVERS ÜZERINDE KLAVYENIN LEDLERINI YAKMA



Linux Character Drivers Üzerinde  Klavyenin Ledlerini Yakma      

     Character special files veya character devices , donanım cihazına arabelleksiz, doğrudan erişim sağlar. Programların aynı anda tek bir karakter okumasına veya yazmasına izin vermezler. Örneğin bir sabit disk için kullanılan karakter aygıtı, normalde tüm okuma ve yazmaların sınırları engellemek için hizalanmasını ve kesinlikle tek bir baytın okunmasına izin vermeyeceğini gerektirir. Karakter aygıtları, bazen bir blok tabanlı donanım için bir karakter aygıtının hizalanmış blokları okumak ve yazmak için programlar gerektirdiği gerçeğini çevreleyen karışıklığı önlemek için ham aygıtlar olarak bilinir.
     Kullanıcı alanı programları, aygıt özel dosyaları olarak da adlandırılan aygıt düğümleri aracılığıyla karakterlere ve engelleme aygıtlarına erişir. Bir aygıt düğümü oluşturulduğunda, bir major ve minor numarayla ilişkilendirilir. Bu yapıyı görsellemek istersek;
      Klavye ledlerini yakmak için oluşturduğumuz   modülümüzde metholar ile /dev alt dosyalama yapısı oluşturabiliriz.

     Son iki yazımızda bulunan koda benzer şekilde, bir init () işlevi ve bir exit () işlevi vardır. Bununla birlikte, karakter aygıtı için gereken ek file_operations işlevleri vardır:


dev_open(): Cihaz kullanıcı alanından her açıldığında çağrılır.
dev_read(): Veriler cihazdan kullanıcı alanına gönderildiğinde çağrılır.
dev_write(): Veri kullanıcı alanından aygıta gönderildiğinde çağrılır.
dev_release (): Cihaz kullanıcı alanında kapatıldığında çağrılır
   1: Kernel Modülünün Oluşturulması

#cd  /home/demet/Downloads/LKernel
#mkdir  DriverModules
#cd DriverModules
#gedit Keyboard.c
   2: C Modulünün Yazılması
     
     Bu methodu ister /dev alt dosyasının Read methodunu çalışırken aktif edebiliriz ister Write methodu çalışırken aktif edebiliriz. İki yöntem arasında pek fark yoktur.Sürücülerin sınıf adı ve cihaz adı var.Sınıf adı olarak CoreClass  ve aygıt adı olarakNativeCore kullandık. Bu, dosya sisteminde /sys/class/CoreClass/NativeCore 'da görünen bir aygıt oluşturulmasına neden olur.

Kaynak Kodu indirmek için (Tıklayınız)

//Kütüphanelerin tanımlanması
#include <linux/module.h>
#include <linux/device.h> #include <linux/kernel.h> #include <linux/fs.h> #include <asm/uaccess.h> #include <linux/tty.h> #include <linux/kd.h> #include <linux/vt.h> #include <linux/console_struct.h> #include <linux/init.h>
//Modül bilgilerinin tanımlanması MODULE_LICENSE("GPL"); MODULE_AUTHOR("NativeCore");
//Char. Device bilgilerinin ve Keyboard LED lerinin adreslerinin tanımlanması #define DEVICE_NAME "NativeCore" #define CLASS_NAME "CoreClass" #define MAX_SIZE 3 #define ZERO 0x00 #define cc 0x04 #define ss 0x01 #define nn 0x02 //NumLock
//Char. Device Major Numarasının tanımlanması
static int majorNumber;
//Class ve Device struct yapılarının tanımlanması static struct class* coreClass = NULL; static struct device* coreDevice = NULL;
//Keyboard LED driverının tanımlanması static struct tty_driver *ttyDriver; extern int fg_console;
//File Operationsların tanımlanması static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset); static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset);
//Özel methodların tanımlanması
static void SetLEDs(char *);
static int Validate(char *,int);
//Değişkenlerin tanımlanması static char DeviceBuffer[MAX_SIZE]; static int BufferSize;
//File Operations struct yapısının tanımlanması static struct file_operations fo = { .owner = THIS_MODULE, .read = DeviceRead, .write = DeviceWrite };
//init methodu static int onStart(void) {
//Keyboard Driver ın oluşturulması int k = 0; printk(KERN_ALERT "NativeCore\n"); for (k = 0; k < MAX_NR_CONSOLES; k++) { if (!vc_cons[k].d) break; } ttyDriver = vc_cons[fg_console].d->port.tty->driver;
//Major numarsının kaydının alınması majorNumber = register_chrdev(0, DEVICE_NAME, &fo); if (majorNumber<0) { printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber); return majorNumber; }
//Class yapısının oluşturulması coreClass = class_create(THIS_MODULE, CLASS_NAME); if (IS_ERR(coreClass)) { unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Device kayıt edilmedi.Major Numarası : %d\n",majorNumber); return PTR_ERR(coreClass); }
//Device yapısının oluşturulması coreDevice = device_create(coreClass, NULL, MKDEV(majorNumber, 0), NULL, DEVICE_NAME); if (IS_ERR(coreDevice)){ class_destroy(coreClass); unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Device oluşturulmadı\n"); return PTR_ERR(coreClass); } printk(KERN_ALERT "Device oluşturuldu %d : %s.\n",majorNumber,DEVICE_NAME); return 0; }
//exit methodu static void onExit(void){
//Çıkış sonrası gerekli methodların kapatılması device_destroy(coreClass, MKDEV(majorNumber, 0)); class_unregister(coreClass); class_destroy(coreClass); unregister_chrdev(majorNumber, DEVICE_NAME); printk(KERN_ALERT "Device silindi %d : %s.\n",majorNumber,DEVICE_NAME); ((ttyDriver->ops)->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED,0xFF); }
//File Operations Read Methodu static ssize_t DeviceRead(struct file *filep, char *buffer, size_t __user len, loff_t *offset) { ssize_t sst = 1; return simple_read_from_buffer(buffer,len,offset,DeviceBuffer,sst); }
//File Operations Write Methodu static ssize_t DeviceWrite(struct file *filep, const char __user *buffer, size_t len, loff_t *offset){ printk(KERN_ALERT "DeviceWrite : çalıştırıldı.\n"); BufferSize = len; if (BufferSize > MAX_SIZE) { BufferSize = MAX_SIZE; } if (copy_from_user(DeviceBuffer, buffer, BufferSize)) { printk(KERN_ALERT "DeviceWrite : Device dosyası kopyalanmadı\n"); return -EFAULT; } if(BufferSize == MAX_SIZE) { if(Validate(DeviceBuffer,len) == 1) { SetLEDs(DeviceBuffer); } else { printk(KERN_ALERT "Geçersiz Karakter girildi.\n"); } } return BufferSize; }
//Girilen Karakter string in doğruluğunu kontrol edilmesi static int Validate(char *s,int l) { int i = 0; for(i=0;i<l-1;i++) { if(s[i] != '0' && s[i] != '1') { return 0; } } return 1; }
//Girilen 3 bitlik ([101] vb.) yapıya göre LED ışıklarının yanmasını sağlayan yapı static void SetLEDs(char *v) { //[***] //[0] CapsLock //[1] NumLock //[2] ScrollLock //int c = 0x04 * v[0]; //int n = 0x02 * v[1]; //int s = 0x01 * v[2]; printk(KERN_ALERT "----%d-%x----\n",((v[0] == '0') ? ZERO : cc )+((v[1] == '0') ? ZERO : nn)+((v[2] == '0') ? ZERO : ss),((v[0] == '0') ? ZERO : cc )+((v[1] == '0') ? ZERO : nn)+((v[2] == '0') ? ZERO : ss)); if(v[0] == '0' && v[1] == '0' && v[2] == '0') { ((ttyDriver->ops)->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED,0xFF); } else { ((ttyDriver->ops)->ioctl) (vc_cons[fg_console].d->port.tty, KDSETLED, ((v[0] == '0') ? ZERO : cc )+((v[1] == '0') ? ZERO : nn)+((v[2] == '0') ? ZERO : ss)); } } module_init(onStart); module_exit(onExit);
Klavye ledlerini yakmak için yazdığımız c kodunda LED durumu bir baytın 3 ilk bitinde aynı sırada saklanması yoluyla gerçekleştirdik.
  * Klavyedeki LED'ler şu şekildedir:
* Bit 0: CapsLock LED'i
  * Bit 1: NumLock LED'i
* Bit 2: ScrollLock LED'i
     
     3: Makefile Yazılması

obj-m = Keyboard.o
all:
 make -C /lib/modules/$(shell uname -r)/build/ M=$(PWD) modules
clean:
 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
   
     4: Modülün derlenmesi ve aktif olması


# make (derleme)
# insmod Keyboard.ko (aktif etme)
# rmmod Keyboard(inaktif etme)


     5: Dosyamıza veri yazılması ya da okunması
       
       Modül üzerinde ki Write methodu ile dosyaya veri yazmak için "echo"komutu kullanılır.

#### Dosyamıza veri yazmak için ####
# echo "ledleri yakmak içintercih ettiğiniz bit kombinasyonu" > /dev/DevName
echo "111">/dev/NativeCore
dmesg

    Örneğin echo “111”>/dev/NativeCore çalıştırdığımızda CapsLock LED'i
NumLock LED'i ve  ScrollLock LED'inin tümü yanacaktır.

   

   dmesg komutunu yazarak  çalışan DeviceWrite durumunu gözlemleyebiliriz;


     
     5: Dosyamıza veri yazılması ya da okunması

#rmmod Keyboard
Olası Hatalar ve Çözümleri

1: "Makefile:3: *** missing separator. Stop".
Makefile dosyanızda boşluk(Space) karakterleri olabilir,bu karakterler yerine TAB karakteri koymalısınız.
2: "make not found"
Bu hata Kernel Kaynak kodlarının bozulması sonucunda meydana gelir, güncel Kerneli indirip tekrar derlenip kurulması gerekir.
3"Bad Adrress" or "Is not proccess"
Bu hata genellikle Write Read methodları içinde veri okurken copy_to_user yada copy_from_user fonksiyonlarının karıştırılması sonucu oluşur 
4:"Could not save file"
Bu hata Kernel Dosyalarını değiştirmek için yönetici izninizin  olmadığını belirtir.

Yönetici şifrenizi oluşturmadıysanız 
# sudo passwd root
Yönetici olarak terminalde işlem yapmak için 
# su
C dosyasının derlerken alınan hatalar için komut satırı incelenmeli ve hataların ayıklanması gerekmektedir. En çok karşılaşan hatalar /proc dosyası için tanımlanan write/read methodlarının geri dönüş tipi yada parametlerin hatalı olmasından kaynaklamakdatır.

Olası hata kodlarının anlamları
/usr/include/asm-generic/errno-base.h
#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define EPERM        1  /* Operation not permitted */
#define ENOENT       2  /* No such file or directory */
#define ESRCH        3  /* No such process */
#define EINTR        4  /* Interrupted system call */
#define EIO      5  /* I/O error */
#define ENXIO        6  /* No such device or address */
#define E2BIG        7  /* Argument list too long */
#define ENOEXEC      8  /* Exec format error */
#define EBADF        9  /* Bad file number */
#define ECHILD      10  /* No child processes */
#define EAGAIN      11  /* Try again */
#define ENOMEM      12  /* Out of memory */
#define EACCES      13  /* Permission denied */
#define EFAULT      14  /* Bad address */
#define ENOTBLK     15  /* Block device required */
#define EBUSY       16  /* Device or resource busy */
#define EEXIST      17  /* File exists */
#define EXDEV       18  /* Cross-device link */
#define ENODEV      19  /* No such device */
#define ENOTDIR     20  /* Not a directory */
#define EISDIR      21  /* Is a directory */
#define EINVAL      22  /* Invalid argument */
#define ENFILE      23  /* File table overflow */
#define EMFILE      24  /* Too many open files */
#define ENOTTY      25  /* Not a typewriter */
#define ETXTBSY     26  /* Text file busy */
#define EFBIG       27  /* File too large */
#define ENOSPC      28  /* No space left on device */
#define ESPIPE      29  /* Illegal seek */
#define EROFS       30  /* Read-only file system */
#define EMLINK      31  /* Too many links */
#define EPIPE       32  /* Broken pipe */
#define EDOM        33  /* Math argument out of domain of func */
#define ERANGE      34  /* Math result not representable */

#endif
/usr/include/asm-generic/errno.h
#ifndef _ASM_GENERIC_ERRNO_H
#define _ASM_GENERIC_ERRNO_H

#include <asm-generic/errno-base.h>

#define EDEADLK     35  /* Resource deadlock would occur */
#define ENAMETOOLONG    36  /* File name too long */
#define ENOLCK      37  /* No record locks available */
#define ENOSYS      38  /* Function not implemented */
#define ENOTEMPTY   39  /* Directory not empty */
#define ELOOP       40  /* Too many symbolic links encountered */
#define EWOULDBLOCK EAGAIN  /* Operation would block */
#define ENOMSG      42  /* No message of desired type */
#define EIDRM       43  /* Identifier removed */
#define ECHRNG      44  /* Channel number out of range */
#define EL2NSYNC    45  /* Level 2 not synchronized */
#define EL3HLT      46  /* Level 3 halted */
#define EL3RST      47  /* Level 3 reset */
#define ELNRNG      48  /* Link number out of range */
#define EUNATCH     49  /* Protocol driver not attached */
#define ENOCSI      50  /* No CSI structure available */
#define EL2HLT      51  /* Level 2 halted */
#define EBADE       52  /* Invalid exchange */
#define EBADR       53  /* Invalid request descriptor */
#define EXFULL      54  /* Exchange full */
#define ENOANO      55  /* No anode */
#define EBADRQC     56  /* Invalid request code */
#define EBADSLT     57  /* Invalid slot */

#define EDEADLOCK   EDEADLK

#define EBFONT      59  /* Bad font file format */
#define ENOSTR      60  /* Device not a stream */
#define ENODATA     61  /* No data available */
#define ETIME       62  /* Timer expired */
#define ENOSR       63  /* Out of streams resources */
#define ENONET      64  /* Machine is not on the network */
#define ENOPKG      65  /* Package not installed */
#define EREMOTE     66  /* Object is remote */
#define ENOLINK     67  /* Link has been severed */
#define EADV        68  /* Advertise error */
#define ESRMNT      69  /* Srmount error */
#define ECOMM       70  /* Communication error on send */
#define EPROTO      71  /* Protocol error */
#define EMULTIHOP   72  /* Multihop attempted */
#define EDOTDOT     73  /* RFS specific error */
#define EBADMSG     74  /* Not a data message */
#define EOVERFLOW   75  /* Value too large for defined data type */
#define ENOTUNIQ    76  /* Name not unique on network */
#define EBADFD      77  /* File descriptor in bad state */
#define EREMCHG     78  /* Remote address changed */
#define ELIBACC     79  /* Can not access a needed shared library */
#define ELIBBAD     80  /* Accessing a corrupted shared library */
#define ELIBSCN     81  /* .lib section in a.out corrupted */
#define ELIBMAX     82  /* Attempting to link in too many shared libraries */
#define ELIBEXEC    83  /* Cannot exec a shared library directly */
#define EILSEQ      84  /* Illegal byte sequence */
#define ERESTART    85  /* Interrupted system call should be restarted */
#define ESTRPIPE    86  /* Streams pipe error */
#define EUSERS      87  /* Too many users */
#define ENOTSOCK    88  /* Socket operation on non-socket */
#define EDESTADDRREQ    89  /* Destination address required */
#define EMSGSIZE    90  /* Message too long */
#define EPROTOTYPE  91  /* Protocol wrong type for socket */
#define ENOPROTOOPT 92  /* Protocol not available */
#define EPROTONOSUPPORT 93  /* Protocol not supported */
#define ESOCKTNOSUPPORT 94  /* Socket type not supported */
#define EOPNOTSUPP  95  /* Operation not supported on transport endpoint */
#define EPFNOSUPPORT    96  /* Protocol family not supported */
#define EAFNOSUPPORT    97  /* Address family not supported by protocol */
#define EADDRINUSE  98  /* Address already in use */
#define EADDRNOTAVAIL   99  /* Cannot assign requested address */
#define ENETDOWN    100 /* Network is down */
#define ENETUNREACH 101 /* Network is unreachable */
#define ENETRESET   102 /* Network dropped connection because of reset */
#define ECONNABORTED    103 /* Software caused connection abort */
#define ECONNRESET  104 /* Connection reset by peer */
#define ENOBUFS     105 /* No buffer space available */
#define EISCONN     106 /* Transport endpoint is already connected */
#define ENOTCONN    107 /* Transport endpoint is not connected */
#define ESHUTDOWN   108 /* Cannot send after transport endpoint shutdown */
#define ETOOMANYREFS    109 /* Too many references: cannot splice */
#define ETIMEDOUT   110 /* Connection timed out */
#define ECONNREFUSED    111 /* Connection refused */
#define EHOSTDOWN   112 /* Host is down */
#define EHOSTUNREACH    113 /* No route to host */
#define EALREADY    114 /* Operation already in progress */
#define EINPROGRESS 115 /* Operation now in progress */
#define ESTALE      116 /* Stale NFS file handle */
#define EUCLEAN     117 /* Structure needs cleaning */
#define ENOTNAM     118 /* Not a XENIX named type file */
#define ENAVAIL     119 /* No XENIX semaphores available */
#define EISNAM      120 /* Is a named type file */
#define EREMOTEIO   121 /* Remote I/O error */
#define EDQUOT      122 /* Quota exceeded */

#define ENOMEDIUM   123 /* No medium found */
#define EMEDIUMTYPE 124 /* Wrong medium type */
#define ECANCELED   125 /* Operation Canceled */
#define ENOKEY      126 /* Required key not available */
#define EKEYEXPIRED 127 /* Key has expired */
#define EKEYREVOKED 128 /* Key has been revoked */
#define EKEYREJECTED    129 /* Key was rejected by service */

/* for robust mutexes */
#define EOWNERDEAD  130 /* Owner died */
#define ENOTRECOVERABLE 131 /* State not recoverable */

#endif

Linux Terminal Komutları
1: "cd" istenilen klasöre girilmesi
2: "ls" klasör içeriğinin görülmesi
3: "mkdir" klasör oluşturulması
4: "gedit" dosya açılması yada düzenlenmesi
5: "su" yönetici olarak girilmesi
6: "dmesg" kernel/debug mesajlarının görülmesi
7: "make" kernele ait komut olup derlenme işlemini yapar
8: "insmod" kernel modülünün aktif olması
9: "rmmod" kernel modülünün inaktif olması
10: "echo" veri yazılması
11: "cat" veri okunması
12: "lsmod" modüllerin listelenmesi

*tty_driver kaynak kodu (Tıklayınız)
Grup Üyeleri
DEMET AKYOL (Takım Lideri)