Klavye Ledlerini Yakıp Söndüren Character Device Driver Uygulaması

Nedir bu Character Device Driver ?

UNIX' de device' lar iki kategoriye ayrılır. Character device' lar ve Block Device' lar. Block device' lar bazı programlar karakterler halinde okuma/yazma yapabilseler de bu işlemi bloklar halinde yapabilirken; Character Device' lar klavyeler ve seri portlar gibi yalnızca karakterler halinde okuma/yazma yapabilirler. 

UNIX' in güzelliği bu aygıtların dosya olarak tutulmasıdır. Hem Character Device' lar hem de Block Device' lar, /dev dizininde ilgili dosyalar tarafından temsil edilir. Bu da open, close, read ve write gibi standart sistem çağrılarıyla bu dosyaları işleyerek cihazları okuyabileceğimiz ya da içerisine yazma işlemi gerçekleştirebileceğimiz anlamına gelir. 

Bu yazımızdaki örnekte de klavye sürücüsü içerisine yükleyeceğimiz bir modül sayesinde klavye ledlerini yakıp söndüreceğiz.

Peki Linux Hangi Dosya Hangi Cihazla İlişkili Bunu Nasıl Biliyor ?

Bunun için, her device' ın ve o device'ın dosyasının kendine özgün birer  Major ve Minor numarası vardır ve bununla ilişkilendirilmiştir. Hiçbir iki cihazın aynı Major numarası yoktur. Bir aygıt dosyası açıldığında, Linux Major numarasını inceler ve çağrıyı söz konusu aygıt için kayıtlı sürücüye iletir. Ardından gelen read/write/close çağrıları da aynı sürücü tarafından işlenir. Çekirdek söz konusu olduğunda yalnızca Major numarası önemlidir. Sürücü bir türün birden fazla aygıtını kontrol ediyorsa Minor numarası belirli aygıt örneğini tanımlamak için kullanılır.

Major ve Minor numaralarını öğrenmek için 

ls -l 

komutunu kullanıyoruz.


'c' ile başlaması bir character device dosyası olduğu anlamına gelir. 1 Major numarası ve 8 de Minor numarasıdır.

Linux driver' ı aslında çekirdeğe çalışır durumdayken bağlanabilen ve adreslenebilen bir Linux modülüdür. Driver Kernel Space' de çalışır ve bir kere yüklendiğinde kernel' ın bir parçası haline gelir. Daha sonra kernel tarafından verilen simgelere erişebilir.

Device driver modülü yüklendiğinde sürücü kendisini belirli bir Major numarasına sahip bir aygıt olarak kaydeder.

Klavye Işıklarını Yakıp Söndüren Character Device Driver

#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <linux/console_struct.h>
#include <linux/vt_kern.h>
#include <linux/slab.h>

static char msg[2];
static char bosluk[2] = {'\0' , '\0'};

struct tty_driver *tty;

static dev_t first;
static struct class *cl;
static struct cdev c_dev;

//Led states

#define S0 0xFF
#define S1 0x01
#define S2 0x02
#define S3 0x03
#define S4 0x04
#define S5 0x05
#define S6 0x06
#define S7 0x07

/*ledi yakmaya yarayan fonskiyon*/
static void settled(int state){
int set;
int i;
char *ret = (char*)kmalloc(sizeof(char)*30 , GFP_KERNEL);

for(i=0;i<30;i++){
ret[i] = '\0' ;
}

/* 
* Scr 1
* Num 2
* Caps 4 
*/

if(state == 0){
set = 50;
strcpy(ret, "Scr: Off Num: Off Caps:Off");
}
else if (state == 1){
set = S1;
strcpy(ret, "Scr: On Num: Off Caps:Off");
}
else if (state == 2){
set = S2;
strcpy(ret, "Scr: Off Num: On Caps:Off");
}
else if (state == 3){
set = S3;
strcpy(ret, "Scr: On Num: On Caps:Off");
}
else if (state == 4){
set = S4;
strcpy(ret, "Scr: Off Num: Off Caps:On");
}
else if (state == 5){
set = S5;
strcpy(ret, "Scr: On Num: Off Caps:On");
}
else if (state == 6){
set = S6;
strcpy(ret, "Scr: Off Num: On Caps:On");
}
else if (state == 7){
set = S7;
strcpy(ret, "Scr: On Num: On Caps:On");
}
else{
set = 50;
strcpy(ret, "Scr: Off Num: Off Caps:Off");
}

//ttv driverin ioctrl fonskiyonuna hangi ledin yakilacagini soyler

((tty->ops) -> ioctl) (vc_cons[fg_console].d->port.tty,KDSETLED,set);

printk(KERN_INFO "%s\n", ret);

for (i=0;i<30;i++){
ret[i] = '\0';
}

kfree(ret);
}

/*gecerli bir led statei oluo olmadigini kontrol eder eger gecerliyse stati dondurur degilse -1 dondurur */

int isValid(char c){
char nums[8] = {'0','1','2','3','4','5','6','7'};
int check = -1;
int i;

for(i = 0 ; i<8 ; i++){
if(nums[i] ==c)
check = i;
}

return check ;
}

/*Girilen inputu parse edip yakilacak ve gerekli fonskiyona parametre olarak yollar */

void isle(char *param){
char take;
int param_length;
int i;
int iparam;
int isFound = 0;
int ret;
param_length = strlen(param);

for(i = 0; i<param_length;i++){
take = param[i];
ret= isValid(take);
if(ret != -1){
iparam = ret;
isFound = 1;
break;
}
}
if(isFound){
settled(iparam);
}else{
return -EFAULT;
}
}

static int c_open(struct inode *i,struct file *f)
{
return 0;
}

static int c_release(struct inode *i,struct file *f){
return 0;
}

static ssize_t c_read(struct file *f, char __user *buf, size_t len, loff_t *off){
ssize_t ret;
size_t str_len = 1;

ret = simple_read_from_buffer(buf,len,off,msg,str_len);

return ret;
}

static ssize_t c_write(struct file *f,const char __user *buf, size_t len, loff_t *off){
ssize_t ret;

if(len > 3)
len = 3;
strcpy(msg,bosluk);
memcpy(msg,buf,len);
isle(msg);
ret = strlen(msg);

return len;
}

static struct file_operations fops ={
.owner = THIS_MODULE,
.open = c_open,
.read = c_read,
.release = c_release,
.write = c_write
};

static int __init firs_init(void) {
int i;

if(alloc_chrdev_region(&first,0,1,"DevelOp")<0){
return -1;
}

if((cl = class_create(THIS_MODULE, "dvlOp")) == NULL){
unregister_chrdev_region(first,1);
return -1;
}

if(device_create(cl,NULL,first,NULL,"DevelOpNull") == NULL){
class_destroy(cl);
unregister_chrdev_region(first,1);
return -1;
}

cdev_init(&c_dev,&fops);

if(cdev_add(&c_dev,first,1) == -1){
device_destroy(cl,first);
class_destroy(cl);
unregister_chrdev_region(first,1);
return -1;
}

for (i=0;i<MAX_NR_CONSOLES;i++){
if(!vc_cons[i].d)
break;
}

tty = vc_cons[fg_console].d->port.tty->driver;

printk(KERN_INFO "\nDevelOp registered\n");

return 0;
}

static void __exit first_exit(void){
//Device 1 kaldır
cdev_del(&c_dev);
device_destroy(cl,first);
class_destroy(cl);
unregister_chrdev_region(first,1);
printk(KERN_INFO "\nDevelOp unregister\n");
}

MODULE_LICENSE("GPL");

module_init(firs_init);

module_exit(first_exit);

kodunu .c uzantılı dosya içerisine yazıyoruz.

Makefile dosyası oluşturup içerisine

obj-m = keyboardLEDs.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

yazıyoruz. Kaydedip çıktıktan sonra 

make

komutuyla c dosyamızı derliyoruz.



İlgili dosyalarımızın oluştuğu bilgisi çıktıda göründüğüne göre kod başarıyla derlendi.

sudo insmod keyboardLEDs.ko

yazarak modülü kernel' a yüklüyoruz.


modül yüklendi mesajı loga düştü. "dmesg" komutuyla kontrol edebiliriz.

/dev dizinini de kontrol ederek character device driverın kernela başarıyla yüklendiğini görebiliriz.


Artık modülümüzü kontrol edebiliriz. Bunun için echo komutuyla device dosyasının içerisine parametreleri gireceğiz. "echo 0-7 > /dev/DevelOpNull" yazarak 0 dan 7 ye kadar bir sayı gireceğiz. Ardından Num Lock, Scroll Lock ve Caps Lock ledlerinin yanıp yanmadığını kontrol edebiliriz. Aynı şekilde "dmesg" komutuyla loglardan da hangi işlemin gerçekleştirdiği bilgisini kontrol edebiliriz.

Girilen parametrelerin anlamı şu şekildedir:

0: Hepsini Kapat
1: Scroll Lock Aç
2: Num Lock Aç
4: Caps Lock Aç
7: Hepsini Aç

eğer iki veya daha fazla ledi yakmak istiyorsak örneğin Caps Lock ile Num Lock ledlerini aynı anda yakmak için 4 + 2 yani 6 parametresini girmemiz yeterli olacaktır.

echo > 4 /dev/DevelOpNull





echo 6 > /dev/DevelOpNull


dmesg



İşlemimizi bitirdikten sonra

sudo rmmod keyboardLEDs.ko

komutuyla modülü kerneldan kaldırabiliriz.




  Grup Üyeleri:

Yorumlar

Bu blogdaki popüler yayınlar

Kernel Modülü Programlama

ITIL SÜREÇLERİ ve YAZILIM KALİTE METRİKLERİ

Linux Kernel Son Sürümünü Derleme

Raspberry Pi ile Konuşacak Sensörden Gelen Verileri Loga Yazdıran Character Device Driver Uygulaması

Linux Dört İşlem Yapan Proc Uygulaması