티스토리 뷰

전자회로/임베디드교육

8주차

쥬브62 2020. 5. 25. 17:40

- 장치들을 파일로 관리(VFS)하여 입/출력 한다

 

- LDD(linux device driver)

 

- 어플리케이션이 파일을 통해 입/출력을 하면 LDD가 실질적으로 입/출력 한다

 

-

.bashrc에 arm gcc 경로 추가

 

- built-in.o  각 디렉토리의 obj를 합한 것

 

- 드라이버 정보 매크로 사용

__init macro : 초기화시 한번만 사용하고 메모리 free

__initdata : 초기변수에 대해 위와 유사

__exit : 모듈이 커널에 삽입시 생략

 

# 모듈만들고 컴파일해서 실행까지(8-49p)

hello_4.c

#include <linux/module.h>
#include <linux/moduleparam.h>  //모듈 매개변수 사용
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>

MODULE_LICENSE("GPL"); //모듈의 라이센스
MODULE_AUTHOR("Peter Jay Salzman"); //모듈 제작자

static int myint = 420;
static char *mystring = "blah";
static int myintArray[2] = { .1, .1 };
static int arr_argc = 0;

module_param(myint, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
MODULE_PARM_DESC(myint, "An integer");

module_param(mystring, charp, 0000); // cahr *
MODULE_PARM_DESC(mystring, "A character string");

module_param_array(myintArray, int, &arr_argc, 0000); //3번째 인자는 배열개수
MODULE_PARM_DESC(myintArray, "An array of integers");

static int __init hello_4_init(void)
{
	int i;

	printk(KERN_INFO "Hello, world 4\n=============\n");
	printk(KERN_INFO "myint is an integer: %d\n", myint);
	printk(KERN_INFO "mystring is a string: %s\n", mystring);

	for (i = 0; i < (sizeof myintArray / sizeof (int)); i++)
	{
		printk(KERN_INFO "myintArray[%d] = %d\n", i, myintArray[i]);
	}

	printk(KERN_INFO "got %d arguments for myintArray.\n", arr_argc);
	return 0;
}

static void __exit hello_4_exit(void)
{
	printk(KERN_INFO "Goodbye, world 4\n");
}

module_init(hello_4_init);
module_exit(hello_4_exit);

 

Makefile

 obj-m += hello_4.o

# KDIR	:= /lib/modules/$(shell uname -r)/build
 KDIR	:= /work/linux

all:
	make -C $(KDIR) SUBDIRS=$(PWD) modules

clean:
	make -C $(KDIR) SUBDIRS=$(PWD) clean

install:
	cp *.ko /nfsroot/root

 

#캐릭터 디바이스 등록 및 해제(8-58p)

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;

static struct file_operations sk_fops;
static struct cdev sk_cdev;

static int sk_register_cdev(void);

static int sk_init(void)
{
    printk("SK Module is up... \n");

	if((result = sk_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
}

static void sk_exit(void)
{
    printk("The module is down...\n");

	cdev_del(&sk_cdev); //등록된 캐릭터 디바이스 제거
	unregister_chrdev_region(sk_dev, 1); //사용중인 디바이스 번호 해제
}

static int sk_register_cdev(void)
{
	int error;

	/* allocation device number */
	if(sk_major) { //0이 아니면
		sk_dev = MKDEV(sk_major, sk_minor); //번호를 설정
		error = register_chrdev_region(sk_dev, 1, "sk"); //번호를 등록
	} else { //0이면
		error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk"); // 디바이스 sk가 sk_dev에 번호를 동적으로 할당받음
		sk_major = MAJOR(sk_dev); // 주번호 추출
	}

	if(error < 0) { // 번호 등록이나 번호 할당 실패시
		printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
		return result;
	}
    printk("major number=%d\n", sk_major);

	/* register chrdev */
	cdev_init(&sk_cdev, &sk_fops); // cdev구조체와 등록할 파일오퍼레이션 구조체포인터를 초기화
	sk_cdev.owner = THIS_MODULE; 
	sk_cdev.ops = &sk_fops;
	error = cdev_add(&sk_cdev, sk_dev, 1); // 초기화한 cdev구조체를 등록

	if(error)
		printk(KERN_NOTICE "sk Register Error %d\n", error);

	return 0;
}

module_init(sk_init); 
module_exit(sk_exit); 

 

- cat으로 조회하면 등록된거 확인 가능

 

 

# 장치 파일의 생성(8-63p)

- 파일은 위에 것으로 사용

 

# mknod /dev/SK c 251 0   //  /dev/SK 경로에 character형이고 major=251, minor=0 인 디바이스 이름을 생성

 

 

# open과 relase(8-73p)

@sk_app.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(void)
{
    int fd;
    
    fd = open("/dev/SK", O_RDWR); //저수준 입출력이라 fd를 int로 받음
    printf("fd = %d\n", fd);
    
    if (fd<0) {
        perror("/dev/SK error");
        exit(-1);
    }
    else
        printf("SK has been detected...\n");
    
    getchar(); // 키 입력 하나 받음
    close(fd);
    
    return 0;
}

- arm gcc로 컴파일하여 보드에 옮김

 

@sk.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h> //file_operations
#include <linux/cdev.h>

MODULE_LICENSE("GPL");

static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;

static struct cdev sk_cdev;
static int sk_register_cdev(void);

static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);

static int sk_open(struct inode *inode, struct file *filp)
{
    printk("Device has been opened...\n");
    
    return 0;
}

static int sk_release(struct inode *inode, struct file *filp)
{
    printk("Device has been closed...\n");
    
    return 0;
}

struct file_operations sk_fops = { 
    .open       = sk_open,  // open시 호출되는 함수
    .release    = sk_release, // close시 호출되는 함수
};

static int __init sk_init(void)
{
    printk("SK Module is up... \n");

	if((result = sk_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
}

static void __exit sk_exit(void)
{
    printk("The module is down...\n");
	cdev_del(&sk_cdev);
	unregister_chrdev_region(sk_dev, 1);
}

static int sk_register_cdev(void)
{
	int error;

	if(sk_major) {
		sk_dev = MKDEV(sk_major, sk_minor);
		error = register_chrdev_region(sk_dev, 1, "sk");
	} else {
		error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
		sk_major = MAJOR(sk_dev);
	}

	if(error < 0) {
		printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
		return result;
	}
	printk("major number=%d\n", sk_major);

	cdev_init(&sk_cdev, &sk_fops);
	sk_cdev.owner = THIS_MODULE;
	sk_cdev.ops = &sk_fops;
	error = cdev_add(&sk_cdev, sk_dev, 1);

	if(error)
		printk(KERN_NOTICE "sk Register Error %d\n", error);

	return 0;
}

module_init(sk_init); 
module_exit(sk_exit); 

- 모듈생성하여 옮김

 

 

# write (8-79p)

@sk_app.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(void)
{
    int retn;
    int fd;

    char buf[100] = "MDS Academy\n";
    
    fd = open("/dev/SK", O_RDWR);
    printf("fd = %d\n", fd);
    
    if (fd<0) {
        perror("/dev/SK error");
        exit(-1);
    }
    else
        printf("SK has been detected...\n");
  
    retn = write(fd, buf, 12); //fd가 가리키는 파일에 buf에 있는 12바이트를 씀
    
    printf("\nSize of written data : %d\n", retn);

    close(fd);
    
    return 0;
}

 

@sk.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;

static struct cdev sk_cdev;

static int sk_register_cdev(void);

static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);

static int sk_open(struct inode *inode, struct file *filp)
{
    printk("Device has been opened...\n");
    
    return 0;
}

static int sk_release(struct inode *inode, struct file *filp)
{
    printk("Device has been closed...\n");
    
    return 0;
}

static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	char data[13];

	copy_from_user(data, buf, count); // 사용자 메모리에서 커널메모리로 복사
	printk("\ndata >>>>> = %s\n", data);

	return count;
}

struct file_operations sk_fops = { 
    .open       = sk_open,
    .release    = sk_release,
	.write		= sk_write,  //write시 호출되는 함수
};

static int __init sk_init(void)
{
    printk("SK Module is up... \n");

	if((result = sk_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
}

static void __exit sk_exit(void)
{
    printk("The module is down...\n");
	cdev_del(&sk_cdev);
	unregister_chrdev_region(sk_dev, 1);
}

static int sk_register_cdev(void)
{
	int error;

	if(sk_major) {
		sk_dev = MKDEV(sk_major, sk_minor);
		error = register_chrdev_region(sk_dev, 1, "sk");
	} else {
		error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
		sk_major = MAJOR(sk_dev);
	}

	if(error < 0) {
		printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
		return result;
	}
	printk("major number=%d\n", sk_major);

	cdev_init(&sk_cdev, &sk_fops);
	sk_cdev.owner = THIS_MODULE;
	sk_cdev.ops = &sk_fops;
	error = cdev_add(&sk_cdev, sk_dev, 1);

	if(error)
		printk(KERN_NOTICE "sk Register Error %d\n", error);

	return 0;
}

module_init(sk_init); 
module_exit(sk_exit); 

 

- 우선순위 때문에 출력 순서에 차이가 생긴다

 

 

# read (8-82p)

@sk_app.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(void)
{
    int retn;
    int fd;

    char buf[100] = {0};
    
    fd = open("/dev/SK", O_RDWR);
    printf("fd = %d\n", fd);
    
    if (fd<0) {
        perror("/dev/SK error");
        exit(-1);
    }
    else
        printf("SK has been detected...\n");
    
    retn = read(fd, buf, 20); //fd가 가리키는 파일에 buf에서 20바이트를 읽음
    printf("\ndata : %s\n", buf);

    close(fd);
    
    return 0;
}

 

@sk.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;

static struct cdev sk_cdev;

static int sk_register_cdev(void);

static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);

static int sk_open(struct inode *inode, struct file *filp)
{
    printk("Device has been opened...\n");
    
    return 0;
}

static int sk_release(struct inode *inode, struct file *filp)
{
    printk("Device has been closed...\n");
    
    return 0;
}

static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	char data[11];

	copy_from_user(data, buf, count);
	printk("data >>>>> = %s\n", data);

	return count;
}

static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	char data[20] = "this is read func...";

	copy_to_user(buf, data, count); //커널메모리에서 사용자메모리로 복사

	return 0;
}

struct file_operations sk_fops = { 
    .open       = sk_open,
    .release    = sk_release,
	.write		= sk_write,
	.read		= sk_read, // read시 호출하는 함수
};

static int __init sk_init(void)
{
    printk("SK Module is up... \n");

	if((result = sk_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
}

static void __exit sk_exit(void)
{
    printk("The module is down...\n");
	cdev_del(&sk_cdev);
	unregister_chrdev_region(sk_dev, 1);
}

static int sk_register_cdev(void)
{
	int error;

	if(sk_major) {
		sk_dev = MKDEV(sk_major, sk_minor);
		error = register_chrdev_region(sk_dev, 1, "sk");
	} else {
		error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
		sk_major = MAJOR(sk_dev);
	}

	if(error < 0) {
		printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
		return result;
	}
	printk("major number=%d\n", sk_major);

	cdev_init(&sk_cdev, &sk_fops);
	sk_cdev.owner = THIS_MODULE;
	sk_cdev.ops = &sk_fops;
	error = cdev_add(&sk_cdev, sk_dev, 1);

	if(error)
		printk(KERN_NOTICE "sk Register Error %d\n", error);

	return 0;
}

module_init(sk_init); 
module_exit(sk_exit); 

 

 

 

# ioctl (8-86p)

@sk_app.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <termio.h>

#include "sk.h"

int main(void)
{
	int flag =0;
    int fd;
    fd = open("/dev/SK", O_RDWR);
    printf("fd = %d\n", fd);
    
    if (fd<0) {
        perror("/dev/SK error");
        exit(-1);
    }

    getchar();

	ioctl(fd, SK_LED_ON, flag); getchar(); //fd에 ON하라는 cmd 
    ioctl(fd, SK_LED_OFF, flag); getchar(); //fd에 OFF하라는 cmd 
    ioctl(fd, SK_LED_ON, flag); getchar();
    ioctl(fd, SK_LED_OFF, flag); getchar();
    ioctl(fd, SK_LED_ON, flag); getchar();
    ioctl(fd, SK_LED_OFF, flag); getchar();
    ioctl(fd, SK_LED_ON, flag); getchar();

	printf("LED must be ON \n");
	    
    close(fd);    

    return 0;
}

 

@sk.h

  #ifndef _SK_H_
  #define _SK_H_
 
  #define SK_MAGIC    'k'
  #define SK_MAXNR    6
  
  #define SK_LED_OFF      _IO(SK_MAGIC, 0) // _IO(매직번호, 구분번호)
  #define SK_LED_ON       _IO(SK_MAGIC, 1)	

  #endif /* _SK_H_ */

 

@sk.c

#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#include "sk.h"

MODULE_LICENSE("GPL");

static int sk_major = 0, sk_minor = 0;
static int result;
static dev_t sk_dev;

static struct cdev sk_cdev;

static int sk_register_cdev(void);

static int sk_open(struct inode *inode, struct file *filp);
static int sk_release(struct inode *inode, struct file *filp);
static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
static int sk_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);

static int sk_open(struct inode *inode, struct file *filp)
{
    printk("Device has been opened...\n");

   	s3c_gpio_cfgpin(S3C2410_GPG(4), S3C_GPIO_SFN(1));	// set GPG(4) OUTPUT, 초기화

    return 0;
}

static int sk_release(struct inode *inode, struct file *filp)
{
    printk("Device has been closed...\n");
    
    return 0;
}

static int sk_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	char data[11];

	copy_from_user(data, buf, count);
	printk("data >>>>> = %s\n", data);

	return count;
}

static int sk_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	char data[20] = "this is read func...";

	copy_to_user(buf, data, count);

	return 0;
}

int sk_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
      if(_IOC_TYPE(cmd) != SK_MAGIC) // _IOC_TYPE(명령) : 매직번호를 읽음
		  return -EINVAL;
      if(_IOC_NR(cmd) >= SK_MAXNR) // _IOC_NR(명령) : 구분번호를 읽음
		  return -EINVAL;

	  switch(cmd) {
		case SK_LED_OFF: {gpio_set_value(S3C2410_GPG(4), 1);break;}		// 1: OFF
		case SK_LED_ON : {gpio_set_value(S3C2410_GPG(4), 0);break;}		// 0: ON
	  }
}

struct file_operations sk_fops = { 
    .open       = sk_open,
    .release    = sk_release,
	.write		= sk_write,
	.read		= sk_read,
	.unlocked_ioctl		= sk_ioctl,  //ioctl사용시 호출되는 함수
};

static int __init sk_init(void)
{
    printk("SK Module is up... \n");

	if((result = sk_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
}

static void __exit sk_exit(void)
{
    printk("The module is down...\n");
	cdev_del(&sk_cdev);
	unregister_chrdev_region(sk_dev, 1);
}

static int sk_register_cdev(void)
{
	int error;

	if(sk_major) {
		sk_dev = MKDEV(sk_major, sk_minor);
		error = register_chrdev_region(sk_dev, 1, "sk");
	} else {
		error = alloc_chrdev_region(&sk_dev, sk_minor, 1, "sk");
		sk_major = MAJOR(sk_dev);
	}

	if(error < 0) {
		printk(KERN_WARNING "sk: can't get major %d\n", sk_major);
		return result;
	}
	printk("major number=%d\n", sk_major);

	cdev_init(&sk_cdev, &sk_fops);
	sk_cdev.owner = THIS_MODULE;
	sk_cdev.ops = &sk_fops;
	error = cdev_add(&sk_cdev, sk_dev, 1);

	if(error)
		printk(KERN_NOTICE "sk Register Error %d\n", error);

	return 0;
}

module_init(sk_init); 
module_exit(sk_exit); 

 

 

 

#동적 메모리 예제(8-101p)

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>

void kmalloc_test( void )
{
   
    char *buff;
 
    printk( "kmalloc test\n" );
    
    buff = kmalloc( 1204, GFP_KERNEL ); //1024바이트를 할당, GFP_KERNEL : 동적메모리 할당이 항상 성공하도록 요구, 메모리가 충분치 않을때는 기다림
    if( buff != NULL )
    {
        sprintf( buff, "1204B Memory by kmalloc is Ok\n" );
        printk( buff );
    
        kfree( buff );
    }    
    
    buff = kmalloc( 4 * 1024*1024 + 1 , GFP_KERNEL); // 4MB 넘겨서 출력되지 않음
    if( buff != NULL )
    {
        printk( "upto 4MB Memory by kmalloc is Ok\n" );
        kfree( buff );
    }
    
}

void vmalloc_test( void )
{

    char *buff;

    printk( "vmalloc test\n" );
    
    buff = vmalloc( 4 * 1024 * 1024 + 1 ); // 할당 가능
    if( buff != NULL )
    {
        printk("more than 4MB Memory by vmalloc is Ok\n");
    
        vfree( buff );
    }    

}

void get_free_pages_test( void )
{
    char *buff;
    int   order;

    printk( "get_free_pages test\n" );
    
    order = get_order(4096 *2*2*2*2); // order는 4
    buff  = (char *)(__get_free_pages( GFP_KERNEL, order ));
    if( buff != NULL) 
    {
        sprintf( buff, "__get_free_pages test ok [%d]\n", order );
        printk( buff );
        
        free_pages((unsigned long)buff, order); //할당해제
    }
}

int memtest_init(void)      
{ 
    char *data;
    
    printk("Module Memory Test\n" );
    
    kmalloc_test();
    vmalloc_test();
    get_free_pages_test();
    
    return 0; 
}

void memtest_exit(void)  
{ 
    printk("Module Memory Test End\n"); 
}

module_init(memtest_init);
module_exit(memtest_exit);

MODULE_LICENSE("Dual BSD/GPL");

 

 

#메모리 풀 예제(8-103p) - 코드 분석 해야함

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/slab.h>
#include <linux/mempool.h>

#define MIN_ELEMENT     10
#define TEST_ELEMENT    30 

typedef struct 
{
    int number;
	char string[1048576]; // 1MB쯤?
    //char string[128];
} TMemElement;


int elementcount = 0;

void *mempool_alloc_test(int gfp_mask, void *pool_data)
{
    TMemElement  *data;
    printk( "----> mempool_alloc_test\n" );        
    
    data = kmalloc( sizeof( TMemElement ), gfp_mask );
    if( data != NULL ) data->number = elementcount++;
    return data;
}

void mempool_free_test(void *element, void *pool_data)
{
    printk( "----> call mempool_free_test\n" );        
    if( element != NULL ) kfree( element );
}

int mempool_init(void)      
{ 
    mempool_t   *mp;    
    TMemElement *element[TEST_ELEMENT];
    int          lp;
    
    printk("Module MEMPOOL Test\n" );
    
    memset( element, 0, sizeof( element ) );
    
    printk( "call mempool_create\n" );
    mp = mempool_create( MIN_ELEMENT, mempool_alloc_test, mempool_free_test, NULL );
    
    printk( "mempool allocate\n" );
    for( lp=0; lp < TEST_ELEMENT; lp++ )
    {
        element[lp] = mempool_alloc(mp, GFP_KERNEL );
        if( element[lp] == NULL ) printk( "allocte fail\n" );
        else
        {
            sprintf( element[lp]->string, "alloc data %d\n", element[lp]->number );
            printk( element[lp]->string );
        }
    }
    
    printk( "mempool free\n" );
    for( lp=0; lp < TEST_ELEMENT; lp++ )
    {
        if( element[lp] != NULL ) mempool_free( element[lp], mp );
    }
    
    printk( "call mempool_destroy\n" );
    mempool_destroy( mp );
    
    return 0; 
}

void mempool_exit(void)  
{ 
    printk("Module MEMPOOL Test End\n"); 
}

module_init(mempool_init);
module_exit(mempool_exit);

MODULE_LICENSE("Dual BSD/GPL");

 

- 20번째 부터 사전에 할당해 둔것을 사용

 

 

# 인터럽트 (8 - 118p) 

@keyint.c

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#define DRV_NAME "keyint"

static irqreturn_t keyinterrupt_func(int irq, void *dev_id, struct pt_regs *regs)
{
	printk("\nkeypad was pressed \n");
	return IRQ_HANDLED;
}

static int __init keyint_init(void)
{
	int ret;
	// set Interrupt mode
	s3c_gpio_cfgpin(S3C2410_GPF(0), S3C_GPIO_SFN(2)); //GFP0을 인터럽트 0으로 사용
	if( request_irq(IRQ_EINT0, (void *)keyinterrupt_func,IRQF_DISABLED|IRQF_TRIGGER_FALLING, DRV_NAME, NULL) )
	{
		printk("failed to request external interrupt.\n");
		ret = -ENOENT;
		return ret;
	}
	printk(KERN_INFO "%s successfully loaded\npress sw14\n", DRV_NAME);
	return 0;
}

static void __exit keyint_exit(void)
{
	free_irq(IRQ_EINT0, NULL);
	printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}

module_init(keyint_init);
module_exit(keyint_exit);

MODULE_LICENSE("GPL");

 

@keyint_base.c   ??

//keyint_base.c
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>

#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/gpio.h>

#define DRV_NAME		"keyint"


struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
};

static struct rebis_key_detection rebis_gd = {
   IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static irqreturn_t
rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{

	printk("\nkeypad was pressed \n");


    return IRQ_HANDLED;

}

static int __init rebis_keyint_init(void)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");

    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1); 

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));  // upper line common key interrupt 


#if 1
	writel(readl(S3C2410_EXTINT0) & (~(0xf << 12)), S3C2410_EXTINT0);	
	writel(readl(S3C2410_EXTINT0) | (0x2 << 12), S3C2410_EXTINT0); // Falling Edge interrupt
	
#endif 


    if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {   
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
                return ret;
    }  
	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

    return 0;
    
}

static void __exit rebis_keyint_exit(void)
{
   gpio_free(S3C2410_GPF(2));
   gpio_free(S3C2410_GPF(3));
   gpio_free(S3C2410_GPF(4));
   gpio_free(S3C2410_GPF(5));
   gpio_free(S3C2410_GPF(6));

    free_irq(rebis_gd.irq, &rebis_gd);

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}


module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

 

@keyint_bottom.c  ??

//keyint_bottom.c
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>

#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/gpio.h>


#include <linux/workqueue.h>


#define DRV_NAME		"keyint"

#define EXAMPLE 100 
//======================================
// 100 : workqueue test
// 200 : tasklet test
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

struct my_dat {
	struct rebis_key_detection *data;
	struct work_struct gdetect;
};

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct my_dat my_data;

static struct rebis_key_detection rebis_gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};


/*
*	interrupt service routine
*/
static irqreturn_t rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("keypad was pressed \n");

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);  	// sample the current pin value
    gd->last_state = state;		// store it for later comparison

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if   (EXAMPLE == 100)
	schedule_work(&gd->gdetect);		// use workqueue
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);		// use tasklet
	#endif

	return IRQ_HANDLED;

}
#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
	// no special work at the moment by workqueue
	gprintk("workqueue callback call\n\n");
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(struct work_struct *w_arg)
{
	// no special work at the moment by tasklet
	gprintk("tasklet callback call\n\n");
}
#endif

static int __init rebis_keyint_init(void)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");

    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));

    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback);   // initialize workqeue as BH
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd));	// initialize tasklet as BH 
	#endif

	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

    if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	
	
    return 0;
    
}

static void __exit rebis_keyint_exit(void)
{
    free_irq(rebis_gd.irq, &rebis_gd);
	
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}


module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

 

@keyint_bottom_kscan.c  ??

//keyint_bottom_kscan.c
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <asm/io.h>
#include <asm/irq.h>

#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>


#include <linux/workqueue.h>


#define DRV_NAME		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 100 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// 
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int cur_key, old_key;

struct key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct key_detection gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0

};

static irqreturn_t keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct key_detection *gd = (struct key_detection *) dev_id;
    int             state;

	state = 1;
	gprintk(">> \n");

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);
    gd->last_state = state;
//  gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	gprintk("<< \n");	
	return IRQ_HANDLED;

}

static int scan_input(void) {

	// let's read GPF[7] pin.
	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
		// is it down to low(zero) ?
		if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;	// yes
	}
	return 0;	// no
}

static void keyint_callback(void *pgd)
{
	int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};
	int i;

	gprintk(">> \n");

	cur_key = 0;
	
	// configure GPF[7] as input
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));
	
	for(i=6; i>=2; i--)
	{
		// set GPFDAT[6:0] bits '1'
		writel(readl(S3C2410_GPFDAT) | (0x7f), S3C2410_GPFDAT);
		
		// clr i-th bit of GPFDAT[6:2]
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();

		// if scan_input returns 'yes', than sw of GPF[i-th] is pressed.
		if(cur_key)
		{
			cur_key = key_base[i-2];
			if(cur_key == old_key)
				goto Same_Value;	// ignore same consecutive keyinputs.
			else {
				old_key = cur_key;
				gprintk("cur_key = %d \n\n", cur_key);	// new key is pressed !
			}
			break;
		}
	}
Same_Value:

	old_key = 0;

	// configure GPF[6:2] as output
	gpio_direction_output(S3C2410_GPF(2), 1);
	gpio_direction_output(S3C2410_GPF(3), 1);
	gpio_direction_output(S3C2410_GPF(4), 1);
	gpio_direction_output(S3C2410_GPF(5), 1);
	gpio_direction_output(S3C2410_GPF(6), 1);
	
	// configure GPF[7] as EXTINT
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	gprintk("<< \n");
	
}

static int __init keyint_init(void)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));
	
	#if (EXAMPLE == 100)
	INIT_WORK(&gd.gdetect, keyint_callback);
	#elif (EXAMPLE == 200)
	tasklet_init(&gd.gdetect, keyint_callback, (unsigned long)(&gd)); 
	#endif 

	
	if( request_irq(IRQ_EINT7, (void *)keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }



	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

    return 0;
    
}

static void __exit keyint_exit(void)
{

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));

    free_irq(gd.irq, &gd);

	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&gd.gdetect);
	#endif

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}


module_init(keyint_init);
module_exit(keyint_exit);

MODULE_LICENSE("GPL");

 

@keyint_bottom_kscan_dd.c & app.c  ??

//keyint_bottom_kscan_dd.c
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/io.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 


#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>



#define DRV_NAME		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 100 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int key_major = 0, key_minor = 0;
static int result;
static dev_t key_dev;
struct cdev key_cdev;

static char NODE_NAME[] = "keyscan";
static int cur_key, old_key;
static int flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);		// this is WAIT queue ! not workqueue !

struct key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct key_detection gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static int scan_input(void);
static int key_register_cdev(void);

static irqreturn_t
keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct key_detection *gd = (struct key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",(unsigned int)gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	flag = 1;
	wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	// let's read GPF[7] pin.
	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
		// is it down to low(zero) ?
		if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;	// yes
	}
	return 0;	// no
}

static void keyint_callback(void *pgd)
{
	int i;

	cur_key = 0;
	
	// configure GPF[7] as input
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));
	
	for(i=6; i>=2; i--)
	{
		// set GPFDAT[6:0] bits '1'
		writel(readl(S3C2410_GPFDAT) | (0x7f), S3C2410_GPFDAT);
		
		// clr i-th bit of GPFDAT[6:2]
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();

		// if scan_input returns 'yes', than sw of GPF[i-th] is pressed.
	
		if(cur_key)
		{
			cur_key += (6-i);
			if(cur_key == old_key)
				goto SameValue;
			else {
				old_key = cur_key;
				gprintk("cur_key = %d \n\n", cur_key);	// new key is pressed !
			}
			break;
		}
	}
SameValue:
	old_key = 0;
	flag = 1;

	// configure GPF[6:2] as output
	gpio_direction_output(S3C2410_GPF(2), 1);
	gpio_direction_output(S3C2410_GPF(3), 1);
	gpio_direction_output(S3C2410_GPF(4), 1);
	gpio_direction_output(S3C2410_GPF(5), 1);
	gpio_direction_output(S3C2410_GPF(6), 1);
	
	// configure GPF[7] as EXTINT
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


	gprintk("callback call\n\n");

}

static ssize_t keyscan_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{

	if(!(filp->f_flags & O_NONBLOCK)){
		//	interruptible_sleep_on(&wq);
			wait_event_interruptible(wq, flag != 0);
		//	wait_event_interruptible_timeout(wq, flag != 0, 600);
	}

	put_user(cur_key,(char *)buff);

	flag = 0;

	return count;
}

static int keyscan_open(struct inode * inode, struct file * file)
{
	old_key = 0;

	printk(KERN_INFO "ready to scan key value\n");

	return 0;
}

static int keyscan_release(struct inode * inode, struct file * file)
{
	printk(KERN_INFO "end of the scanning\n");

	return 0;
}

static struct file_operations keyscan_fops = {
	.owner		= THIS_MODULE,
	.open		= keyscan_open,
	.release	= keyscan_release,
	.read		= keyscan_read,
};

static int __init keyint_init(void)
{
	int ret;


    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));
	
	#if (EXAMPLE == 100)
	INIT_WORK(&gd.gdetect, keyint_callback);
	#elif (EXAMPLE == 200)
	tasklet_init(&gd.gdetect, keyint_callback, (unsigned long)(&gd)); 
	#endif

	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

	if( request_irq(IRQ_EINT7, (void *)keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	
	
	if((result = key_register_cdev()) < 0)
	{
		return result;
	}

    return 0;
    
}

static void __exit keyint_exit(void)
{
    free_irq(gd.irq, &gd);

	cdev_del(&key_cdev);
	unregister_chrdev_region(key_dev, 1);

	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}

static int key_register_cdev(void)
{
    int error;
	
	/* allocation device number */
	if(key_major) {
		key_dev = MKDEV(key_major, key_minor);
		error = register_chrdev_region(key_dev, 1, NODE_NAME);
	} else {
		error = alloc_chrdev_region(&key_dev, key_minor, 1, NODE_NAME);
		key_major = MAJOR(key_dev);
	}
	
	if(error < 0) {
		printk(KERN_WARNING "keyscan: can't get major %d\n", key_major);
		return result;
	}
	printk("major number=%d\n", key_major);
	
	/* register chrdev */
	cdev_init(&key_cdev, &keyscan_fops);
	key_cdev.owner = THIS_MODULE;
	error = cdev_add(&key_cdev, key_dev, 1);
	
	if(error)
		printk(KERN_NOTICE "Keyscan Register Error %d\n", error);
	
	return 0;
}

module_init(keyint_init);
module_exit(keyint_exit);

MODULE_LICENSE("GPL");

 

 

//app.c
#include <stdio.h>
#include <fcntl.h>

char *key_string[5] = {"ENTER","UP","LEFT","DOWN","RIGHT"};	

int main(void)
{
	int fd;
	int len = 0;
	char key_value;

	printf("\nStarting keyscan test\n");

	fd = open("/dev/keyscan", O_RDONLY, 0);
	//fd = open("/dev/input/event0", O_RDONLY, 0);
	if(fd < 0) {
		printf("/dev/keyscan : device open error\n");
		exit(0);
	}

	while(1) {
		printf("press keypad: \n");

		while(1) {

			len = read(fd, &key_value, 1);

			if(len > 0)	break;
		}

		if(key_value != 0) {
			printf("%s(%d)\n", key_string[key_value-1], key_value);
	
		}

	}

	close(fd);
}

 

 

 

# 메모리 매핑

@mmapcall_app.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

#define	DEVICE_FILENAME	"/dev/mmapcall"

#define	MMAP_SIZE		0x1000			/* 4096 byte */

int main(void)
{
	int		dev;
	int		loop;
	char	*ptrdata;

	dev = open(DEVICE_FILENAME, O_RDWR|O_NDELAY);

	if(dev >= 0)
	{
		/* 메모리 영역 맵핑 시도 */
		ptrdata = (char *)mmap( (void*) 0x12345000,	/* Start Address */
		//ptrdata = (char *)mmap(0,	/* Start Address */
						MMAP_SIZE,
						PROT_READ|PROT_WRITE,
						MAP_SHARED,			/* 여러 프로세스와 공유 */
						dev,
						0x87654000);		/* Offset Address */

		/* 메모리 영역 해제 */
		munmap(ptrdata, MMAP_SIZE);

		close(dev);
	}

	return 0;
}

 

@ mmapcall_dev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/fcntl.h>

#include <linux/vmalloc.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <asm/page.h>
#include <linux/mm.h>

#define	MMAPCALL_DEV_NAME		"mmapcall"
#define	MMAPCALL_DEV_MAJOR		240

MODULE_LICENSE("Dual BSD/GPL");

static int mmapcall_open(struct inode *inode, struct file *filp)
{
	return 0;
}

static int mmapcall_release(struct inode *inode, struct file *filp)
{
	return 0;
}

static int mmapcall_mmap(struct file *filp, struct vm_area_struct *vma)
{
	printk("vm_pgoff	[%08X]\n", vma->vm_pgoff << PAGE_SHIFT);

	/* process에 할당될 메모리 주소 */
	printk("vm_start	[%08X]\n", vma->vm_start);
	/* process에 할당될 메모리 주소 끝 */
	printk("vm_end		[%08X]\n", vma->vm_end);
	/* 할당 요구한 크기 */
	printk("length		[%08X]\n", (vma->vm_end - vma->vm_start));
	/* 상태 플래그 */
	printk("vm_flags	[%08X]\n", vma->vm_flags);

	/* mmap이 실제로 구현되고 있지 않으므로 상태만 찍고 EAGAIN으로 반환 */
	return -EAGAIN;
}

struct file_operations mmapcall_fops =
{
	.owner		= THIS_MODULE,
	.open		= mmapcall_open,
	.release	= mmapcall_release,
	.mmap		= mmapcall_mmap,
};

static int mmapcall_init(void)
{
	int result;

	result = register_chrdev(MMAPCALL_DEV_MAJOR, MMAPCALL_DEV_NAME, &mmapcall_fops);
	if(result < 0)	return result;
	printk(KERN_INFO "Register Character Device Major Number: %d\n", result);

	return 0;
}

static void mmapcall_exit(void)
{
	unregister_chrdev(MMAPCALL_DEV_MAJOR, MMAPCALL_DEV_NAME);
}

module_init(mmapcall_init);
module_exit(mmapcall_exit);

- 모듈로 만들어서 올려두고 mmapcall_app 실행

 

 

@mmapled_app.c

//mmapled_app.c
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 

#include <sys/types.h> 
#include <sys/stat.h> 
#include <fcntl.h> 

#include <sys/mman.h> 

// gpn 15 led control
#define GPIO_BASE_ADDR		0x56000000 
#define GPIO_SIZE		0x00000100
#define OFFSET_GPGCON		0x60
#define OFFSET_GPGDAT		0x64
#define OFFSET_GPGPUD		0x68


void led_test (void *addr)
{
	int i=0; 
	
	unsigned int *pgpgcon;
	unsigned int *pgpgdat;
	
	pgpgcon = (unsigned int *)(addr + OFFSET_GPGCON);
	pgpgdat = (unsigned int *)(addr + OFFSET_GPGDAT);
	
	*pgpgcon = ((*pgpgcon & ~(0x03<<10))| (0x01<<10)); // outmode setting
	for( i=0; i<10; i++)
	{
		*pgpgdat = (*pgpgdat|(1<<5)); // led off
		sleep(1);
		*pgpgdat = (*pgpgdat & ~(1<<5)); // led on
		sleep(1);
	}
}

int main(void)
{ 
	int fd;
	void *addr;

	fd = open( "/dev/mem", O_RDWR|O_SYNC ); 
	if( fd < 0 ) 
	{ 
		perror( "/dev/mem open error" ); 
		exit(1); 
	} 

	addr = mmap( 0,
			GPIO_SIZE,
			PROT_READ|PROT_WRITE, MAP_SHARED,
			fd,
			GPIO_BASE_ADDR ); 

	if( addr == NULL )
	{
		printf("mmap fail ==> exit()");
		exit(1);
	}

	printf( "fd value %d\n", fd );
	printf( "IO ADDR %p\n", addr ); 
	
	led_test( addr );
	
	
	munmap(addr, GPIO_SIZE ); 
	close (fd);

	return 0; 
}

- led에 불켜짐

 

@mmapfb0_app.c

//mmapfb0_app.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>        // open/close
#include <fcntl.h>         // O_RDWR
#include <sys/ioctl.h>     // ioctl
#include <sys/mman.h>      // mmap PROT_
#include <linux/fb.h>

int   main( int argc, char **argv)
{
   int      screen_width;
   int      screen_height;
   int      bits_per_pixel;
   int      line_length;

   int      fb_fd;
   struct   fb_var_screeninfo  fbvar;
   struct   fb_fix_screeninfo  fbfix;
   void     *fb_mapped;
   int      mem_size;
   unsigned short *ptr;

   int      ndx;

   if ( access( "/dev/fb0", F_OK))
   {
      printf( "no frame buffer\n");
      return 0;
   }

   if ( 0 > ( fb_fd = open( "/dev/fb0", O_RDWR)))
   {
      printf( "no frame buffer\n");
      return 0;

   }

   if ( ioctl( fb_fd, FBIOGET_VSCREENINFO, &fbvar))
   {
      printf( "FBIOGET_VSCREENINFO\n");
      return 0;
   }
   if ( ioctl( fb_fd, FBIOGET_FSCREENINFO, &fbfix))
   {
      printf( "FBIOGET_FSCREENINFO\n");
      return 0;
   }

   screen_width   = fbvar.xres;
   screen_height  = fbvar.yres;
   bits_per_pixel = fbvar.bits_per_pixel;
   line_length    = fbfix.line_length;

   mem_size       = line_length * screen_height;

   fb_mapped      = ( void *)mmap( 0,
                                   mem_size,
                                   PROT_READ|PROT_WRITE,
                                   MAP_SHARED,
                                   fb_fd,
                                   0);

   printf( "screen width   = %dn", screen_width  );
   printf( "screen height  = %dn", screen_height );
   printf( "bits per pixel = %dn", bits_per_pixel);
   printf( "line length    = %dn", line_length   );

   for ( ndx = 0; ndx < 100; ndx++)
   {
      ptr   = ( unsigned short*)fb_mapped + screen_width * ndx + ndx;
     *ptr  = 0xffff;
   }

   for ( ndx = 0; ndx < 500; ndx++)
   {
     *ptr++  = 0xffff;
   }

   munmap( fb_mapped, mem_size);
   close( fb_fd);

   return 0;
}

 

 

# blocking (8-167p)

@blocking.c - 모듈

#include <linux/module.h>
#include <linux/init.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <asm/uaccess.h>

MODULE_LICENSE("GPL");

static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
static int result;
static char data[20] = {0,};

/* Define Prototype of functions */
static int sleepy_open(struct inode *inode, struct file *filp);
static int sleepy_release(struct inode *inode, struct file *filp);
static int sleepy_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int sleepy_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);

/* Implementation of functions */
static int sleepy_open(struct inode *inode, struct file *filp)
{
    printk("\ndevice) opened...\n");
    
    return 0;
}

static int sleepy_release(struct inode *inode, struct file *filp)
{
    printk("\ndevice) closed...\n");
  
    return 0;
}

static int sleepy_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	printk(KERN_INFO "process %i (%s) awakening the reader ...\n", current->pid, current->comm);
	copy_from_user(data, buf, count);
	flag = 1;
	wake_up_interruptible(&wq);	// wake up tasks in the wq.

    return count;
}

static int sleepy_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	printk(KERN_INFO "process %i (%s) going to sleep\n", current->pid, current->comm);
	wait_event_interruptible(wq, flag != 0);  // from now, reader get into sleep until being waken up
	flag = 0;
	printk(KERN_INFO "awaken %i (%s)\n", current->pid, current->comm);

	copy_to_user(buf, data, count);

    return 0;
}

struct file_operations sleepy_fops = { 
    .open       = sleepy_open,
    .release    = sleepy_release,
    .write      = sleepy_write,
    .read       = sleepy_read,

};

static int __init sleepy_init(void)
{
	result = register_chrdev(0, "SLEEPY", &sleepy_fops);
	if (result < 0 ) {
		printk("Couldn't get a major number...\n");
	}

 

@app.c

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>

int main(void)
{
	int retn;
	int fdp, fdc;
	int pid, i;

	char buf[100] = "i love you, mom.....\n";
    
	fdp = open("/dev/sleepy", O_RDWR);
	if (fdp < 0) {
        perror("/dev/sleepy open error...!!");
        exit(-1);
    }
    
	pid = fork();
	if(pid < 0) {
		perror("fork error!!!");
		exit(-1);
	}
	else if(pid == 0)	// son  
	{
		for(i=0;i<100000000;i++);
    	fdc = open("/dev/sleepy", O_RDWR);
    	if (fdc < 0) {
        	perror("/dev/sleepy open error...!!");
        	exit(-1);
    	}

		retn = write(fdc, buf, 20);
		close(fdc);
		exit(0); 	// son exit. no more exist.
	}
	else 
	{			// mom
    	retn = read(fdp, buf, 20);
    	printf("\nmom: received data from son is : %s\n", buf);
    	
		close(fdp);

		exit(0);
	}

}

 

 

#커널타이머

/***************************************
 * Filename: kerneltimer.c
 * Title: Kernel Timer Handler
 * Desc: Timer Handler Module
 ***************************************/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>

#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>

#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#include <linux/time.h>
#include <linux/timer.h>
#include <linux/gpio.h>

#define   TIME_STEP           (2*HZ/10)

typedef struct
{
    struct timer_list  timer;            
	unsigned long      led;
} __attribute__ ((packed)) KERNEL_TIMER_MANAGER;
static KERNEL_TIMER_MANAGER *ptrmng = NULL;

void kerneltimer_timeover(unsigned long arg );

void kerneltimer_registertimer( KERNEL_TIMER_MANAGER *pdata, unsigned long timeover )
{
     init_timer( &(pdata->timer) );
     pdata->timer.expires  = get_jiffies_64() + timeover;
     pdata->timer.data     = (unsigned long) pdata      ;
     pdata->timer.function = kerneltimer_timeover       ;
     add_timer( &(pdata->timer) );
}

void kerneltimer_timeover(unsigned long arg )
{
   KERNEL_TIMER_MANAGER *pdata = NULL;     
	
   
   if( arg )
   {
      pdata = ( KERNEL_TIMER_MANAGER * ) arg;

       gpio_direction_output(S3C2410_GPG(4), (pdata->led & 0xFF));
       gpio_direction_output(S3C2410_GPG(5), (pdata->led & 0xFF));
       gpio_direction_output(S3C2410_GPG(6), (pdata->led & 0xFF));
       gpio_direction_output(S3C2410_GPG(7), (pdata->led & 0xFF));
      

       pdata->led = ~(pdata->led);

      kerneltimer_registertimer( pdata , TIME_STEP );
 }
}

int kerneltimer_init(void)
{
    ptrmng = kmalloc( sizeof( KERNEL_TIMER_MANAGER ), GFP_KERNEL );
    if( ptrmng == NULL ) return -ENOMEM;
     
    memset( ptrmng, 0, sizeof( KERNEL_TIMER_MANAGER ) );
     
    ptrmng->led = 0;
    kerneltimer_registertimer( ptrmng, TIME_STEP );
	gpio_request(S3C2410_GPG(4), "led 1");
	gpio_request(S3C2410_GPG(5), "led 2");
	gpio_request(S3C2410_GPG(6), "led 3" );
	gpio_request(S3C2410_GPG(7), "led 4" );
   
    gpio_set_value(S3C2410_GPG(4), 1);
    gpio_set_value(S3C2410_GPG(5), 1);
    gpio_set_value(S3C2410_GPG(6), 1);
    gpio_set_value(S3C2410_GPG(7), 1);

    s3c_gpio_cfgpin(S3C2410_GPG(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPG(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPG(6), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPG(7), S3C_GPIO_SFN(1));
  
    return 0;
}

void kerneltimer_exit(void)
{
    if( ptrmng != NULL ) 
    {
        del_timer( &(ptrmng->timer) );
 kfree( ptrmng );
    }    

    gpio_set_value(S3C2410_GPG(4), 1);    
    gpio_set_value(S3C2410_GPG(5), 1); 
    gpio_set_value(S3C2410_GPG(6), 1); 
    gpio_set_value(S3C2410_GPG(7), 1); 

	gpio_free(S3C2410_GPG(4));
	gpio_free(S3C2410_GPG(5));
	gpio_free(S3C2410_GPG(6));
	gpio_free(S3C2410_GPG(7));
}

module_init(kerneltimer_init);
module_exit(kerneltimer_exit);

MODULE_LICENSE("Dual BSD/GPL");

 

 

# 버스, 디바이스, 드라이버, 클래스

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 

#include <linux/platform_device.h> //platform_driver_register()


#define DRV_NAME		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int key_major = 0, key_minor = 0;
static int result;
static dev_t key_dev = 0;
static struct cdev key_cdev;

static char dev_name2[] = "rebis_keyscan";
static int cur_key, old_key;
static int flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);


struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static int scan_input(void);
static int key_register_cdev(void);


static irqreturn_t rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	flag = 1;
	wake_up_interruptible(&wq);

	return IRQ_HANDLED;
}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
		if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *)pgd;
    
    int state = gd->last_state;

	gprintk("workqueue callback call\n\n");
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *)data;
    
    int state = gd->last_state;

	int i;

	gprintk("tasklet callback call\n");

}
#endif

static ssize_t rebis_keyscan_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	int i;
	int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};

	cur_key = 0;
	#if 0
	interruptible_sleep_on(&wq);
	#else
	wait_event_interruptible(wq, flag != 0);
	//wait_event_interruptible_timeout(wq, flag != 0, 600);
	#endif
#if	KEY_DEBUG
	printk("key input\n");
#endif

	// change input port
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		if(cur_key = scan_input())
		{
			cur_key = key_base[i-2];
			if(cur_key == old_key)
				return 0;
			old_key = cur_key;
			put_user(cur_key,(char *)buff);
			break;
		}
	}
	
	old_key = 0;	
	flag = 0;


    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


#if	KEY_DEBUG
	printk("Read Function\n");	
	printk("GPBDAT = 0x%08x\n", readl(S3C2410_GPBDAT));
	printk("GPFCON = 0x%08x\n", readl(S3C2410_GPFCON));
	printk("EXTINT0 = 0x%08x\n", readl(S3C2410_EXTINT0));
#endif

	return count;
}

static int rebis_keyscan_open(struct inode * inode, struct file * file)
{
	old_key = 0;

	printk(KERN_INFO "ready to scan key value\n");

	return 0;
}

static int rebis_keyscan_release(struct inode * inode, struct file * file)
{
	printk(KERN_INFO "end of the scanning\n");

	return 0;
}

static struct file_operations rebis_keyscan_fops = {
	.owner		= THIS_MODULE,
	.open		= rebis_keyscan_open,
	.release	= rebis_keyscan_release,
	.read		= rebis_keyscan_read,
};

static int s3c2410keypad_remove(struct platform_device *pdev)
{
	free_irq(rebis_gd.irq, &rebis_gd);
		
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));


	printk("this is s3c2410keypad_remove\n");
	return 0;
}

static int __init s3c2410keypad_probe(struct platform_device *pdev)
{
	int ret;
	
    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1); 
    gpio_direction_output(S3C2410_GPF(3), 1); 
    gpio_direction_output(S3C2410_GPF(4), 1); 
    gpio_direction_output(S3C2410_GPF(5), 1); 
    gpio_direction_output(S3C2410_GPF(6), 1); 
	
	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback, &rebis_gd);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

    printk("this is s3c2410keypad_probe\n");

	if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	
	
	return 0;
}

struct bus_type ldd_bus = { 
    .name = "s3c2450-bus",
};

static void ldd_device_release(struct device * dev){
    printk(KERN_DEBUG "s3c2450-bus release\n");
}

struct device ldd_device = {
    .bus = &ldd_bus,
    .init_name = "s3c2450-keypad-device",
    .release    = ldd_device_release,

};

static struct device_driver s3c2450keypad_driver = {
       .name    = "s3c2450-keypad-driver",
       .owner   = THIS_MODULE,
       .bus     = &ldd_bus,
       .probe   = s3c2410keypad_probe,
       .remove  = s3c2410keypad_remove,
};

static void keypad_class_release(struct class *dev)
{
    printk("key class release\n");
}

static struct class keypad_class = {
    .name   ="s3c2450-keypad-class",
    .class_release = keypad_class_release,
};


static int __init rebis_keyint_init(void)
{
	
	int ret;
	key_register_cdev();


    ret = bus_register(&ldd_bus);
    if(ret)
        return ret;

    class_register(&keypad_class);

    ret = driver_register(&s3c2450keypad_driver);
    if(!ret){
        printk("platform_driver initiated  = %d \n", ret);

        ret = device_register(&ldd_device);
        printk("1. platform_device_ret = %d \n", ret);

        printk("2. platform_device_ret = %d \n", ret);
        if(ret)
            driver_unregister(&s3c2450keypad_driver);
    }
	

	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

	return result;
    
}

static void __exit rebis_keyint_exit(void)
{
    
	cdev_del(&key_cdev);
	unregister_chrdev_region(key_dev, 1);


    device_unregister(&ldd_device);
    driver_unregister(&s3c2450keypad_driver);
    class_unregister(&keypad_class);
    bus_unregister(&ldd_bus);


    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}

#if 1
static int key_register_cdev(void)
{
    int error;
	
	/* allocation device number */
	if(key_major) {
		key_dev = MKDEV(key_major, key_minor);
		error = register_chrdev_region(key_dev, 1, dev_name2);
	} else {
		error = alloc_chrdev_region(&key_dev, key_minor, 1, dev_name2);
		key_major = MAJOR(key_dev);
	}
	
	if(error < 0) {
		printk(KERN_WARNING "keyscan: can't get major %d\n", key_major);
		return result;
	}
	printk("major number=%d\n", key_major);
	
	/* register chrdev */
	cdev_init(&key_cdev, &rebis_keyscan_fops);
	key_cdev.owner = THIS_MODULE;
	error = cdev_add(&key_cdev, key_dev, 1);
	
	if(error)
		printk(KERN_NOTICE "Keyscan Register Error %d\n", error);
	
	return 0;
}
#endif

module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);


MODULE_LICENSE("GPL");

 

 

# kobject

/*
 * Sample kobject implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a simple subdirectory in sysfs called
 * /sys/kernel/kobject-example  In that directory, 3 files are created:
 * "foo", "baz", and "bar".  If an integer is written to these files, it can be
 * later read out of it.
 */

static int foo;
static int baz;
static int bar;

/*
 * The "foo" file where a static variable is read from and written to.
 */
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf)
{
	return sprintf(buf, "%d\n", foo);
}

static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count)
{
	sscanf(buf, "%du", &foo);
	return count;
}

static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0666, foo_show, foo_store);

/*
 * More complex function where we determine which variable is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
		      char *buf)
{
	int var;

	if (strcmp(attr->attr.name, "baz") == 0)
		var = baz;
	else
		var = bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
		       const char *buf, size_t count)
{
	int var;

	sscanf(buf, "%du", &var);
	if (strcmp(attr->attr.name, "baz") == 0)
		baz = var;
	else
		bar = var;
	return count;
}

static struct kobj_attribute baz_attribute =
	__ATTR(baz, 0666, b_show, b_store);
static struct kobj_attribute bar_attribute =
	__ATTR(bar, 0666, b_show, b_store);


/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

/*
 * An unnamed attribute group will put all of the attributes directly in
 * the kobject directory.  If we specify a name, a subdirectory will be
 * created for the attributes with the directory being the name of the
 * attribute group.
 */
static struct attribute_group attr_group = {
	.attrs = attrs,
};

static struct kobject *example_kobj;

static int __init example_init(void)
{
	int retval;

	/*
	 * Create a simple kobject with the name of "kobject_example",
	 * located under /sys/kernel/
	 *
	 * As this is a simple directory, no uevent will be sent to
	 * userspace.  That is why this function should not be used for
	 * any type of dynamic kobjects, where the name and number are
	 * not known ahead of time.
	 */
	example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
	if (!example_kobj)
		return -ENOMEM;

	/* Create the files associated with this kobject */
	retval = sysfs_create_group(example_kobj, &attr_group);
	if (retval)
		kobject_put(example_kobj);

	return retval;
}

static void __exit example_exit(void)
{
	kobject_put(example_kobj);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");

 

 

#kset

/*
 * Sample kset and ktype implementation
 *
 * Copyright (C) 2004-2007 Greg Kroah-Hartman <greg@kroah.com>
 * Copyright (C) 2007 Novell Inc.
 *
 * Released under the GPL version 2 only.
 *
 */
#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/init.h>

/*
 * This module shows how to create a kset in sysfs called
 * /sys/kernel/kset-example
 * Then tree kobjects are created and assigned to this kset, "foo", "baz",
 * and "bar".  In those kobjects, attributes of the same name are also
 * created and if an integer is written to these files, it can be later
 * read out of it.
 */


/*
 * This is our "object" that we will create a few of and register them with
 * sysfs.
 */
struct foo_obj {
	struct kobject kobj;
	int foo;
	int baz;
	int bar;
};
#define to_foo_obj(x) container_of(x, struct foo_obj, kobj)

/* a custom attribute that works just for a struct foo_obj. */
struct foo_attribute {
	struct attribute attr;
	ssize_t (*show)(struct foo_obj *foo, struct foo_attribute *attr, char *buf);
	ssize_t (*store)(struct foo_obj *foo, struct foo_attribute *attr, const char *buf, size_t count);
};
#define to_foo_attr(x) container_of(x, struct foo_attribute, attr)

/*
 * The default show function that must be passed to sysfs.  This will be
 * called by sysfs for whenever a show function is called by the user on a
 * sysfs file associated with the kobjects we have registered.  We need to
 * transpose back from a "default" kobject to our custom struct foo_obj and
 * then call the show function for that specific object.
 */
static ssize_t foo_attr_show(struct kobject *kobj,
			     struct attribute *attr,
			     char *buf)
{
	struct foo_attribute *attribute;
	struct foo_obj *foo;

	attribute = to_foo_attr(attr);
	foo = to_foo_obj(kobj);

	if (!attribute->show)
		return -EIO;

	return attribute->show(foo, attribute, buf);
}

/*
 * Just like the default show function above, but this one is for when the
 * sysfs "store" is requested (when a value is written to a file.)
 */
static ssize_t foo_attr_store(struct kobject *kobj,
			      struct attribute *attr,
			      const char *buf, size_t len)
{
	struct foo_attribute *attribute;
	struct foo_obj *foo;

	attribute = to_foo_attr(attr);
	foo = to_foo_obj(kobj);

	if (!attribute->store)
		return -EIO;

	return attribute->store(foo, attribute, buf, len);
}

/* Our custom sysfs_ops that we will associate with our ktype later on */
static const struct sysfs_ops foo_sysfs_ops = {
	.show = foo_attr_show,
	.store = foo_attr_store,
};

/*
 * The release function for our object.  This is REQUIRED by the kernel to
 * have.  We free the memory held in our object here.
 *
 * NEVER try to get away with just a "blank" release function to try to be
 * smarter than the kernel.  Turns out, no one ever is...
 */
static void foo_release(struct kobject *kobj)
{
	struct foo_obj *foo;

	foo = to_foo_obj(kobj);
	kfree(foo);
}

/*
 * The "foo" file where the .foo variable is read from and written to.
 */
static ssize_t foo_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
			char *buf)
{
	return sprintf(buf, "%d\n", foo_obj->foo);
}

static ssize_t foo_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
			 const char *buf, size_t count)
{
	sscanf(buf, "%du", &foo_obj->foo);
	return count;
}

static struct foo_attribute foo_attribute =
	__ATTR(foo, 0666, foo_show, foo_store);

/*
 * More complex function where we determine which variable is being accessed by
 * looking at the attribute for the "baz" and "bar" files.
 */
static ssize_t b_show(struct foo_obj *foo_obj, struct foo_attribute *attr,
		      char *buf)
{
	int var;

	if (strcmp(attr->attr.name, "baz") == 0)
		var = foo_obj->baz;
	else
		var = foo_obj->bar;
	return sprintf(buf, "%d\n", var);
}

static ssize_t b_store(struct foo_obj *foo_obj, struct foo_attribute *attr,
		       const char *buf, size_t count)
{
	int var;

	sscanf(buf, "%du", &var);
	if (strcmp(attr->attr.name, "baz") == 0)
		foo_obj->baz = var;
	else
		foo_obj->bar = var;
	return count;
}

static struct foo_attribute baz_attribute =
	__ATTR(baz, 0666, b_show, b_store);
static struct foo_attribute bar_attribute =
	__ATTR(bar, 0666, b_show, b_store);

/*
 * Create a group of attributes so that we can create and destroy them all
 * at once.
 */
static struct attribute *foo_default_attrs[] = {
	&foo_attribute.attr,
	&baz_attribute.attr,
	&bar_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

/*
 * Our own ktype for our kobjects.  Here we specify our sysfs ops, the
 * release function, and the set of default attributes we want created
 * whenever a kobject of this type is registered with the kernel.
 */
static struct kobj_type foo_ktype = {
	.sysfs_ops = &foo_sysfs_ops,
	.release = foo_release,
	.default_attrs = foo_default_attrs,
};

static struct kset *example_kset;
static struct foo_obj *foo_obj;
static struct foo_obj *bar_obj;
static struct foo_obj *baz_obj;

static struct foo_obj *create_foo_obj(const char *name)
{
	struct foo_obj *foo;
	int retval;

	/* allocate the memory for the whole object */
	foo = kzalloc(sizeof(*foo), GFP_KERNEL);
	if (!foo)
		return NULL;

	/*
	 * As we have a kset for this kobject, we need to set it before calling
	 * the kobject core.
	 */
	foo->kobj.kset = example_kset;

	/*
	 * Initialize and add the kobject to the kernel.  All the default files
	 * will be created here.  As we have already specified a kset for this
	 * kobject, we don't have to set a parent for the kobject, the kobject
	 * will be placed beneath that kset automatically.
	 */
	retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);
	if (retval) {
		kobject_put(&foo->kobj);
		return NULL;
	}

	/*
	 * We are always responsible for sending the uevent that the kobject
	 * was added to the system.
	 */
	kobject_uevent(&foo->kobj, KOBJ_ADD);

	return foo;
}

static void destroy_foo_obj(struct foo_obj *foo)
{
	kobject_put(&foo->kobj);
}

static int __init example_init(void)
{
	/*
	 * Create a kset with the name of "kset_example",
	 * located under /sys/kernel/
	 */
	example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);
	if (!example_kset)
		return -ENOMEM;

	/*
	 * Create three objects and register them with our kset
	 */
	foo_obj = create_foo_obj("foo");
	if (!foo_obj)
		goto foo_error;

	bar_obj = create_foo_obj("bar");
	if (!bar_obj)
		goto bar_error;

	baz_obj = create_foo_obj("baz");
	if (!baz_obj)
		goto baz_error;

	return 0;

baz_error:
	destroy_foo_obj(bar_obj);
bar_error:
	destroy_foo_obj(foo_obj);
foo_error:
	return -EINVAL;
}

static void __exit example_exit(void)
{
	destroy_foo_obj(baz_obj);
	destroy_foo_obj(bar_obj);
	destroy_foo_obj(foo_obj);
	kset_unregister(example_kset);
}

module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");

 

 

# 플랫폼 드라이버

 

@  keyint_bottom_kscan_platform.c

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 

#include <linux/platform_device.h> //platform_driver_register()

#include <linux/input.h> //input_dev

#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#define DRV_NAME		"keyint"
#define S3C2410BUTVERSION	0x001


#define	KEY_MATRIX_BASE1	0
#define	KEY_MATRIX_BASE2	1
#define	KEY_MATRIX_BASE3	2
#define	KEY_MATRIX_BASE4	3
#define	KEY_MATRIX_BASE5	4

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int cur_key, old_key;

static DECLARE_WAIT_QUEUE_HEAD(wq);

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
   IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};



static int scan_input(void);
//static int key_register_cdev(void);

static irqreturn_t rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",(unsigned int)gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	//flag = 1;
	//wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
        if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)pgd    
    //int state = gd->last_state;
	int i;

	gprintk("workqueue callback call\n\n");

	//for key scan
	#if 1
	cur_key = 0;
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key = (i-2);
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			
			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


	#endif
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)data;    
    //int state = gd->last_state;

	int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};
	int i;

	gprintk("tasklet callback call\n");

	//for key scan
	#if 1
	cur_key = 0;
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key =key_base[i-2];
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			
			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


	#endif
}
#endif




static int s3c2410keypad_remove(struct platform_device *pdev)
{
	free_irq(rebis_gd.irq, &rebis_gd);
		
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));


	printk("this is s3c2410keypad_remove\n");
	return 0;
}

static int __init s3c2410keypad_probe(struct platform_device *pdev)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback, &rebis_gd);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

    printk("this is s3c2410keypad_probe\n");

	if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	
	
	return 0;
}

static void release_pdev(struct device * dev){
	dev->parent 	= NULL;
}

static struct platform_device pdev  =
{
	.name	= "s3c2410-keypad2",
	.id		= -1,
	.dev	= {
		.release	= release_pdev,
	},
};

static struct platform_driver s3c2410keypad_driver = {
       .driver         = {
	       .name   = "s3c2410-keypad2",
	       .owner  = THIS_MODULE,
       },
       .probe          = s3c2410keypad_probe,
       .remove         = s3c2410keypad_remove,
};

static int __init rebis_keyint_init(void)
{
	int result;
	result = platform_driver_register(&s3c2410keypad_driver);
	
	if(!result){
		printk("platform_driver initiated  = %d \n", result);
		result = platform_device_register(&pdev);
		printk("platform_device_result = %d \n", result);
		if(result)
			platform_driver_unregister(&s3c2410keypad_driver);
	}
	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

	return result;
    
}

static void __exit rebis_keyint_exit(void)
{
	platform_device_unregister(&pdev);
	platform_driver_unregister(&s3c2410keypad_driver);

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}

module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

/********************************************************
* platform device 
REBIS_BSP : arch/arm/mach-s3c2410/mach-rebis.c

static struct platform_device *rebis_devices[] __initdata = {
    &s3c_device_usb,
    &s3c_device_lcd,
    &s3c_device_wdt,
    &s3c_device_i2c,
    &s3c_device_iis,
    &s3c_device_nand,
    &s3c_device_sound, 
    &s3c_device_ts, 
    &s3c_device_sdi,
    &s3c_device_keypad, --> Ãß°¡
};

REBIS_BSP : arch/arm/mach-s3c2410/devs.c
struct platform_device s3c_device_keypad = {
    .name   = "s3c2410-keypad",
    .id     = -1,
};
EXPORT_SYMBOL(s3c_device_keypad);

REBIS_BSP : arch/arm/mach-s3c2410/devs.h
extern struct platform_device s3c_device_keypad; 

**************************************************************/

 

@  keyint_bottom_kscan_dd_platform.c  & app.c

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 

#include <linux/platform_device.h> //platform_driver_register()

#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#define DRV_NAME		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int key_major = 0, key_minor = 0;
static int result;
static dev_t key_dev;
static struct cdev key_cdev;

static char m2_dev_name[] = "rebis_keyscan";
static int cur_key, old_key;
static flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static int scan_input(void);
static int key_register_cdev(void);

static irqreturn_t rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	flag = 1;
	wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
		if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
	gprintk("workqueue callback call\n\n");
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
	gprintk("tasklet callback call\n");

}
#endif

static ssize_t rebis_keyscan_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	int i;
	int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};

	cur_key = 0;
	#if 0
	interruptible_sleep_on(&wq);
	#else
	wait_event_interruptible(wq, flag != 0);
	#endif

	// change input port
	s3c_gpio_cfgpin(S3C2410_GPF(7),  S3C_GPIO_SFN(0) );

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPBDAT) | (0x3f), S3C2410_GPBDAT);
		writel(readl(S3C2410_GPBDAT) & (~(0x1 << i)), S3C2410_GPBDAT);
		
		if(cur_key = scan_input())
		{
			cur_key = key_base[i-2];
			if(cur_key == old_key)
				return 0;
			old_key = cur_key;
			put_user(cur_key,(char *)buff);
			break;
		}
	}
	
	old_key = 0;	
	flag = 0;

	// set GPBDAT 0
	gpio_direction_output(S3C2410_GPF(2), 1);
	gpio_direction_output(S3C2410_GPF(3), 1);
	gpio_direction_output(S3C2410_GPF(4), 1);
	gpio_direction_output(S3C2410_GPF(5), 1);
	gpio_direction_output(S3C2410_GPF(6), 1);
	
	// change External Interrupts
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


#if	KEY_DEBUG
	printk("Read Function\n");	
	printk("GPBDAT = 0x%08x\n", readl(S3C2410_GPBDAT));
	printk("GPFCON = 0x%08x\n", readl(S3C2410_GPFCON));
	printk("EXTINT0 = 0x%08x\n", readl(S3C2410_EXTINT0));
#endif

	return count;
}

static int rebis_keyscan_open(struct inode * inode, struct file * file)
{
	old_key = 0;

	printk(KERN_INFO "ready to scan key value\n");

	return 0;
}

static int rebis_keyscan_release(struct inode * inode, struct file * file)
{
	printk(KERN_INFO "end of the scanning\n");

	return 0;
}

static struct file_operations rebis_keyscan_fops = {
	.owner		= THIS_MODULE,
	.open		= rebis_keyscan_open,
	.release	= rebis_keyscan_release,
	.read		= rebis_keyscan_read,
};

static int s3c2410keypad_remove(struct platform_device *pdev)
{
	free_irq(rebis_gd.irq, &rebis_gd);
		
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

	gpio_free(S3C2410_GPF(2));
	gpio_free(S3C2410_GPF(3));
	gpio_free(S3C2410_GPF(4));
	gpio_free(S3C2410_GPF(5));
	gpio_free(S3C2410_GPF(6));

	printk("this is s3c2410keypad_remove\n");
	return 0;
}

static int __init s3c2410keypad_probe(struct platform_device *pdev)
{
	int ret;
	
	gpio_request(S3C2410_GPF(2), "key 2");
	gpio_request(S3C2410_GPF(3), "key 3");
	gpio_request(S3C2410_GPF(4), "key 4");
	gpio_request(S3C2410_GPF(5), "key 5");
	gpio_request(S3C2410_GPF(6), "key 6");

	// set output mode
	s3c_gpio_cfgpin(S3C2410_GPF(2), S3C2410_GPIO_OUTPUT);
	s3c_gpio_cfgpin(S3C2410_GPF(3), S3C2410_GPIO_OUTPUT);
	s3c_gpio_cfgpin(S3C2410_GPF(4), S3C2410_GPIO_OUTPUT);
	s3c_gpio_cfgpin(S3C2410_GPF(5), S3C2410_GPIO_OUTPUT);
	s3c_gpio_cfgpin(S3C2410_GPF(6), S3C2410_GPIO_OUTPUT);
	// set data
	gpio_direction_output(S3C2410_GPF(2), 1);
	gpio_direction_output(S3C2410_GPF(3), 1);
	gpio_direction_output(S3C2410_GPF(4), 1);
	gpio_direction_output(S3C2410_GPF(5), 1);
	gpio_direction_output(S3C2410_GPF(6), 1);

	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback, &rebis_gd);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

    printk("this is s3c2410keypad_probe\n");

	if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }

	
	return 0;
}

static void release_pdev(struct device * dev){ 
    dev->parent     = NULL;
}

static struct platform_device pdev  =
{
    .name   = "s3c2410-keypad2",
    .id     = -1, 
    .dev    = { 
        .release    = release_pdev,
    },  
};



static struct platform_driver s3c2410keypad_driver = {
       .driver         = {
	       .name   = "s3c2410-keypad2",
	       .owner  = THIS_MODULE,
       },
       .probe          = s3c2410keypad_probe,
       .remove         = s3c2410keypad_remove,
};


static int key_class_release(void)
{
    printk("key class released\n");
}

static struct class key_class = {
    .name   ="s3c2410-key",
    .class_release = key_class_release,
};



static int __init rebis_keyint_init(void)
{

    key_register_cdev();

    class_register(&key_class);

	result = platform_driver_register(&s3c2410keypad_driver);

    if(!result){
        printk("platform_driver initiated  = %d \n", result);
        result = platform_device_register(&pdev);
        printk("platform_device_result = %d \n", result);
        if(result)
            platform_driver_unregister(&s3c2410keypad_driver);
    }



	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

	return result;
    
}

static void __exit rebis_keyint_exit(void)
{
    
	cdev_del(&key_cdev);
	unregister_chrdev_region(key_dev, 1);

#if 1
    platform_device_unregister(&pdev);
#endif

	platform_driver_unregister(&s3c2410keypad_driver);

    class_unregister(&key_class);
    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}
#if 1
static int key_register_cdev(void)
{
    int error;
	
	/* allocation device number */
	if(key_major) {
		key_dev = MKDEV(key_major, key_minor);
		error = register_chrdev_region(key_dev, 1, m2_dev_name);
	} else {
		error = alloc_chrdev_region(&key_dev, key_minor, 1, m2_dev_name);
		key_major = MAJOR(key_dev);
	}
	
	if(error < 0) {
		printk(KERN_WARNING "keyscan: can't get major %d\n", key_major);
		return result;
	}
	printk("major number=%d\n", key_major);
	
	/* register chrdev */
	cdev_init(&key_cdev, &rebis_keyscan_fops);
	key_cdev.owner = THIS_MODULE;
	error = cdev_add(&key_cdev, key_dev, 1);
	
	if(error)
		printk(KERN_NOTICE "Keyscan Register Error %d\n", error);
	
	return 0;
}
#endif

module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

/********************************************************
* platform device µî·Ï °úÁ¤ 
REBIS_BSP : arch/arm/mach-s3c2410/mach-rebis.c

static struct platform_device *rebis_devices[] __initdata = {
    &s3c_device_usb,
    &s3c_device_lcd,
    &s3c_device_wdt,
    &s3c_device_i2c,
    &s3c_device_iis,
    &s3c_device_nand,
    &s3c_device_sound, 
    &s3c_device_ts, 
    &s3c_device_sdi,
    &s3c_device_keypad, --> Ãß°¡
};

REBIS_BSP : arch/arm/mach-s3c2410/devs.c
struct platform_device s3c_device_keypad = {
    .name   = "s3c2410-keypad",
    .id     = -1,
};
EXPORT_SYMBOL(s3c_device_keypad);

REBIS_BSP : arch/arm/mach-s3c2410/devs.h
extern struct platform_device s3c_device_keypad; 

**************************************************************/

#include <stdio.h>
#include <fcntl.h>

char *key_string[5] = {"ENTER","UP","LEFT","DOWN","RIGHT"};	

int main(void)
{
	int fd;
	int len = 0;
	char key_value;

	printf("\nStarting keyscan test\n");

	fd = open("/dev/keyscan", O_RDONLY, 0);
	//fd = open("/dev/input/event0", O_RDONLY, 0);
	if(fd < 0) {
		printf("keyscan device open error\n");
		exit(0);
	}

	while(1) {
		printf("press keypad: \n");

		while(1) {

			len = read(fd, &key_value, 1);

			if(len > 0)	break;
		}

		if(key_value != 0) {
			printf("%s(%d)\n", key_string[key_value-2], key_value);
	
		}

	}

	close(fd);
}

 

 

@  keyint_bottom_kscan_dd_platform_sysfs.c 

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>
#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 

#include <linux/platform_device.h> //platform_driver_register()


#define DRV_NAME1		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

static int key_major = 0, key_minor = 0;
static int result;
static dev_t key_dev;
static struct cdev key_cdev;

static char dev_name1[] = "rebis_keyscan";
static int cur_key, old_key;
static flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
	IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static int scan_input(void);
static int key_register_cdev(void);

static irqreturn_t
rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	flag = 1;
	wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
		if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *)pgd;
    
    int state = gd->last_state;

	gprintk("workqueue callback call\n\n");
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *)data;
    
    int state = gd->last_state;

	int i;

	gprintk("tasklet callback call\n");

}
#endif

static ssize_t rebis_keyscan_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	int i;
	int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};

	cur_key = 0;
	#if 0
	interruptible_sleep_on(&wq);
	#else
	wait_event_interruptible(wq, flag != 0);
	//wait_event_interruptible_timeout(wq, flag != 0, 600);
	#endif
#if	KEY_DEBUG
	printk("key input\n");
#endif

	// change input port
	s3c2410_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		if(cur_key = scan_input())
		{
			cur_key = key_base[i-2];
			if(cur_key == old_key)
				return 0;
			old_key = cur_key;
			put_user(cur_key,(char *)buff);
			break;
		}
	}
	
	old_key = 0;	
	flag = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));


#if	KEY_DEBUG
	printk("Read Function\n");	
	printk("GPBDAT = 0x%08x\n", readl(S3C2410_GPBDAT));
	printk("GPFCON = 0x%08x\n", readl(S3C2410_GPFCON));
	printk("EXTINT0 = 0x%08x\n", readl(S3C2410_EXTINT0));
#endif

	return count;
}

static int rebis_keyscan_open(struct inode * inode, struct file * file)
{
	old_key = 0;

	printk(KERN_INFO "ready to scan key value\n");

	return 0;
}

static int rebis_keyscan_release(struct inode * inode, struct file * file)
{
	printk(KERN_INFO "end of the scanning\n");

	return 0;
}

static struct file_operations rebis_keyscan_fops = {
	.owner		= THIS_MODULE,
	.open		= rebis_keyscan_open,
	.release	= rebis_keyscan_release,
	.read		= rebis_keyscan_read,
};

static int s3c2410keypad_remove(struct platform_device *pdev)
{
	free_irq(rebis_gd.irq, &rebis_gd);
		
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));


	printk("this is s3c2410keypad_remove\n");
	return 0;
}

static int __init s3c2410keypad_probe(struct platform_device *pdev)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "led 1");
    gpio_request(S3C2410_GPF(3), "led 2");
    gpio_request(S3C2410_GPF(4), "led 3");
    gpio_request(S3C2410_GPF(5), "led 4");
    gpio_request(S3C2410_GPF(6), "led 5");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));
	
	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback, &rebis_gd);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

    printk("this is s3c2410keypad_probe\n");

	if( request_irq(IRQ_EINT7,(void *) rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME1, &rebis_gd) )     
    {
                printk("failed to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }

	
	return 0;
}

static void release_pdev(struct device * dev){
	dev->parent 	= NULL;
}

static struct platform_device pdev  =
{
	.name	= "s3c2410-keypad",
	.id		= -1,
	.dev	= {
		.release	= release_pdev,
	},
};

static struct platform_driver s3c2410keypad_driver = {
       .driver         = {
	       .name   = "s3c2410-keypad",
	       .owner  = THIS_MODULE,
       },
       .probe          = s3c2410keypad_probe,
       .remove         = s3c2410keypad_remove,
};


static struct class key_class = {
	.name	="key",
};
//static struct class_device cdevice_key;

static struct class_device key_cdevice;

static int __init rebis_keyint_init(void)
{
	//key_class = class_create(THIS_MODULE, "key");
	//if(IS_ERR(key_class))
	//	return PTR_ERR(key_class);
	
	key_register_cdev();

	//class_device_add(&cdevice_key);
	class_register(&key_class);

	
	result = platform_driver_register(&s3c2410keypad_driver);
	#if 1
	
	if(!result){
		printk("platform_driver initiated  = %d \n", result);
		result = platform_device_register(&pdev);
		printk("platform_device_result = %d \n", result);
		if(result)
			platform_driver_unregister(&s3c2410keypad_driver);
	}
	#endif

	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME1);

	return result;
    
}

static void __exit rebis_keyint_exit(void)
{
    
	cdev_del(&key_cdev);
	unregister_chrdev_region(key_dev, 1);

	#if 1
	platform_device_unregister(&pdev);
	#endif
	platform_driver_unregister(&s3c2410keypad_driver);
	
	class_unregister(&key_class);
    printk(KERN_INFO "%s successfully removed\n", DRV_NAME1);
}
#if 1
static int key_register_cdev(void)
{
    int error;
	
	/* allocation device number */
	if(key_major) {
		key_dev = MKDEV(key_major, key_minor);
		error = register_chrdev_region(key_dev, 1, dev_name1);
	} else {
		error = alloc_chrdev_region(&key_dev, key_minor, 1, dev_name1);
		key_major = MAJOR(key_dev);
	}
	
	if(error < 0) {
		printk(KERN_WARNING "keyscan: can't get major %d\n", key_major);
		return result;
	}
	printk("major number=%d\n", key_major);
	
	/* register chrdev */
	cdev_init(&key_cdev, &rebis_keyscan_fops);
	key_cdev.owner = THIS_MODULE;
	error = cdev_add(&key_cdev, key_dev, 1);
	
	if(error)
		printk(KERN_NOTICE "Keyscan Register Error %d\n", error);
	
	return 0;
}
#endif

module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

/********************************************************
* platform device µî·Ï °úÁ¤ 
REBIS_BSP : arch/arm/mach-s3c2410/mach-rebis.c

static struct platform_device *rebis_devices[] __initdata = {
    &s3c_device_usb,
    &s3c_device_lcd,
    &s3c_device_wdt,
    &s3c_device_i2c,
    &s3c_device_iis,
    &s3c_device_nand,
    &s3c_device_sound, 
    &s3c_device_ts, 
    &s3c_device_sdi,
    &s3c_device_keypad, --> Ãß°¡
};

REBIS_BSP : arch/arm/mach-s3c2410/devs.c
struct platform_device s3c_device_keypad = {
    .name   = "s3c2410-keypad",
    .id     = -1,
};
EXPORT_SYMBOL(s3c_device_keypad);

REBIS_BSP : arch/arm/mach-s3c2410/devs.h
extern struct platform_device s3c_device_keypad; 

**************************************************************/

 

 

 

# misc 드라이버

 

@ keyint_bottom_kscan_dd_misc.c & app.c

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h> //user access

#include <linux/workqueue.h> //work queue
#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <linux/fs.h> //file_operatios 
#include <linux/miscdevice.h> //misc device

#include <linux/gpio.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#define DRV_NAME		"keyint"

#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

//static char dev_name[] = "rebis_keyscan";
static int cur_key, old_key;
static int flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static int scan_input(void);
//static int key_register_cdev(void);

static irqreturn_t
rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",(unsigned int)gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	flag = 1;
	wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 3) & 0x1) != 0x1)
	{
		if(!gpio_get_value(S3C2410_GPF(3)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(struct work_struct *w_arg)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)pgd;    
    //int state = gd->last_state;
	int i;

	gprintk("workqueue callback call\n\n");

	//for key scan
	#if 1
	cur_key = 0;
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));
	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key += (i-2);//key_base[i];
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;
	flag = 0;

	// set GPBDAT 0
	gpio_direction_output(S3C2410_GPF(2), 1);
	gpio_direction_output(S3C2410_GPF(3), 1);
	gpio_direction_output(S3C2410_GPF(4), 1);
	gpio_direction_output(S3C2410_GPF(5), 1);
	gpio_direction_output(S3C2410_GPF(6), 1);
	
	// change External Interrupts
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));
	#endif
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)data;    
    //int state = gd->last_state;

	//int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};
	int i;

	gprintk("tasklet callback call\n");

	//for key scan
	#if 1
	cur_key = 0;
	s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));
	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x3f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key += (i-2);//key_base[i];
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;
	flag = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1); 
    gpio_direction_output(S3C2410_GPF(3), 1); 
    gpio_direction_output(S3C2410_GPF(4), 1); 
    gpio_direction_output(S3C2410_GPF(5), 1); 
    gpio_direction_output(S3C2410_GPF(6), 1); 

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#endif
}
#endif

static ssize_t rebis_keyscan_read(struct file *filp, char *buff, size_t count, loff_t *offp)
{
	//int i;
	//int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};

	//cur_key = 0;

	if(!(filp->f_flags & O_NONBLOCK)){
		#if 0
		interruptible_sleep_on(&wq);
		#else
		wait_event_interruptible(wq, flag != 0);
		//wait_event_interruptible_timeout(wq, flag != 0, 600);
		#endif
	}

	return count;
}

static int rebis_keyscan_open(struct inode * inode, struct file * file)
{
	old_key = 0;

	printk(KERN_INFO "ready to scan key value\n");

	return 0;
}

static int rebis_keyscan_release(struct inode * inode, struct file * file)
{
	printk(KERN_INFO "end of the scanning\n");

	return 0;
}

static struct file_operations rebis_keyscan_fops = {
	.owner		= THIS_MODULE,
	.open		= rebis_keyscan_open,
	.release	= rebis_keyscan_release,
	.read		= rebis_keyscan_read,
};


/*
*	misc device reserves major number 10. the following example register minor number, 251
*/

struct miscdevice keyint_miscdev = {
    251, "keyint_misc",&rebis_keyscan_fops
};

static int __init rebis_keyint_init(void)
{
	int ret;

    gpio_request(S3C2410_GPF(2), "key 2");
    gpio_request(S3C2410_GPF(3), "key 3");
    gpio_request(S3C2410_GPF(4), "key 4");
    gpio_request(S3C2410_GPF(5), "key 5");
    gpio_request(S3C2410_GPF(6), "key 6");
    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));
    // set data
    gpio_direction_output(S3C2410_GPF(2), 1); 
    gpio_direction_output(S3C2410_GPF(3), 1); 
    gpio_direction_output(S3C2410_GPF(4), 1); 
    gpio_direction_output(S3C2410_GPF(5), 1); 
    gpio_direction_output(S3C2410_GPF(6), 1); 
    
	#if (EXAMPLE == 100)
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

	if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {
                printk("faikey to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	

	#if 0
	if((result = key_register_cdev()) < 0)
	{
		return result;
	}
	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);
	#endif

	ret = misc_register(&keyint_miscdev);
	if( ret < 0 )
	{
		printk(KERN_ERR "keyint misc driver register error\n");
		return ret;
	}
	printk(KERN_INFO "%s successfully loaded with misc driver\n", DRV_NAME);;

    return 0;

    
}

static void __exit rebis_keyint_exit(void)
{
    free_irq(rebis_gd.irq, &rebis_gd);

	//cdev_del(&key_cdev);
	//unregister_chrdev_region(key_dev, 1);
	misc_deregister(&keyint_miscdev);

	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif

    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}


module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

#include <stdio.h>
#include <fcntl.h>

char *key_string[5] = {"ENTER","UP","LEFT","DOWN","RIGHT"};	

int main(void)
{
	int fd;
	int len = 0;
	char key_value;

	printf("\nStarting keyscan test\n");

	fd = open("/dev/keyint_misc", O_RDONLY, 0);

	if(fd < 0) {
		printf("no /dev/keyint_misc or driver is loaded \n");
		exit(0);
	}

	while(1) {
		printf("press keypad: \n");

		while(1) {

			len = read(fd, &key_value, 1);

			if(len > 0)	break;
		}

		if(key_value != 0) {
			printf("%s(%d)\n", key_string[key_value-1], key_value);
	
		}

	}

	close(fd);
}

 

 

# simple serial driver( 8-215p )

@ app.c

#include <stdio.h>
#include <fcntl.h>

int main(void)
{
	int fd, cnt;
	char buffer[1000];
	int i;
	
	fd = open("/dev/mydevice", O_RDWR);

	if (fd<0) {
		printf("cannot open device /dev/mydevice \n");
		exit(1);
	}
	printf("UART Test Program\nType 10 chars...\n");

	strcpy( buffer, "UART0 Read Write test" );
	write( fd, buffer, strlen( buffer ) );
	
	for(i=0; i<5; i++) {
		strcpy( buffer, "\r\nInput string: " );
		write( fd, buffer, strlen( buffer ) );
		
		cnt = read( fd, buffer, 10 );
		
		buffer[cnt] = 0;
		printf( "Input string count: %d\n", cnt );
		printf( "Input string      : %s\n", buffer );
	}
	
	close(fd);
}

 

@ simpleserial.c

#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/serial_core.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <plat/regs-serial.h>


#define portaddr(port, reg) ((port)->membase + (reg))
        
#define rd_regb(port, reg) (__raw_readb(portaddr(port, reg)))	
#define rd_regl(port, reg) (__raw_readl(portaddr(port, reg)))  

#define wr_regb(port, reg, val) \
  do { __raw_writeb(val, portaddr(port, reg)); } while(0)

#define wr_regl(port, reg, val) \
  do { __raw_writel(val, portaddr(port, reg)); } while(0)


#define SSER_MAJOR	251	

char devicename[20];
static struct cdev my_cdev;
int sser_major;

struct uart_port *port;		

#define MAX_BUF	32

int sser_open(struct inode *inode, struct file *filp)
{
	port = kmalloc(sizeof(struct uart_port), GFP_ATOMIC);

	port->membase = (void __iomem *)S3C24XX_VA_UART1;	

	return 0;
}

ssize_t sser_read(struct file *filp, char *buf, size_t count, loff_t *l)
{
	char *b2;
	int i;

	b2 = kmalloc(count, GFP_ATOMIC);

	for (i=0; i<count; i++) {
		while( !(rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_RXDR));
		b2[i] = rd_regb(port, S3C2410_URXH);
	}

	copy_to_user(buf, b2, count);

	kfree(b2);
	return count;
}

ssize_t sser_write(struct file *filp, const char *buf, size_t count, loff_t *l)
{
	char *b2;
	int i;

	b2 = kmalloc(count, GFP_ATOMIC);
	copy_from_user(b2, buf, count);

	for (i=0; i<count; i++) {
		while ( !(rd_regl(port, S3C2410_UTRSTAT) & S3C2410_UTRSTAT_TXFE));
		wr_regb(port, S3C2410_UTXH, b2[i]);
	}
	
	kfree(b2);
	return count;
}

int sser_release(struct inode *inode, struct file *file)
{
	kfree(port);
	printk("sser_release called\n");
	return 0;
}

static struct file_operations sser_fops = {
	.open =  sser_open,
	.read =  sser_read,
	.write = sser_write,
	.release = sser_release,
};

static int __init simple_serial_init (void)
{
	dev_t dev;
	sser_major = SSER_MAJOR;
	dev = MKDEV(sser_major, 0);

	printk("starting simple_serial_init()...\n");
	strcpy(devicename, "Simple serial device driver");
	register_chrdev_region(dev, 128, devicename);
	cdev_init(&my_cdev, &sser_fops);
	cdev_add(&my_cdev, dev, 128);
	
	return 0;
}

static void __exit simple_serial_exit(void)
{
	printk("Closing simple serial driver\n");
	cdev_del(&my_cdev);
	unregister_chrdev_region(MKDEV(SSER_MAJOR,0),128);
}

module_init(simple_serial_init);
module_exit(simple_serial_exit);

MODULE_LICENSE("GPL");

 

+

 

위에는 모듈을 만들어서 하는 것이고 아래는 커널에 삽입하는 방법

 

$ cd /work/linux/drivers/char

$ vi Makefile

    obj-$(CONFIG_SIMPLE_SERIAL) += simpleserial.o  //추가

$ vi Kconfig

    config SIMPLE_SERIAL

               bool "Simple Serial Device"            // 추가

$ cp /work/exercise/module_04/42_simpleserial/simpleserial.c /work/linux/drivers/char

$ cd /work/linux

$ make menuconfig

  device drivers -> charater devices -> simple serial device 선택

$ make

$ cp arch/arm/boot/zImage /tftpboot/

 

커널이미지 올리고 보드 재부팅 하고 # cat /proc/devices 하면 시리얼이 등록되어 있음

# mknod /dev/mydevice c 251 0

# ./app 

해주면 실행됨

 

 

 

# keypad 디바이스 드라이버

@ app.c

#include <stdio.h>
#include <fcntl.h>
#include <linux/input.h>

int main(void)
{
	int fd;
	int i;
	int len = 0;
	struct input_event event_buf[3];

	printf("\nStarting keyscan by input event test\n");

	/*
		as you insmod the input device driver, event2 node will appear.
		we open /dev/input/event2 as input source then.
	*/
	fd = open("/dev/input/event2", O_RDONLY, 0);

	printf("fd = %d  \n", fd);

	if(fd < 0) {
		printf("keyscan device open error\n");
		exit(0);
	}

	while(1) 
	{
		while(1) 
		{
			len = read(fd, event_buf, (sizeof(struct input_event)*3) );
			if(len > 0)	break;
		}
		
		for( i=0; i<(len/sizeof(struct input_event)); i++ )
        {
			//printf("type = %d \n", event_buf[i].type);
			switch( event_buf[i].type )
			{
				case EV_SYN:
					printf("---------------------------------------\n");
					break;
				case EV_KEY:
					printf("Button code %d", event_buf[i].code);
					switch (event_buf[i].value)
					{
						case 1:
							printf(": pressed\n");
							break;
                 	 	case 0:
							printf(": released\n");
							break;
						default:
							printf("Unknown: type %d, code %d, value %d", event_buf[i].type, event_buf[i].code, event_buf[i].value);
                            break;
					}
					break;
				default:
					printf("Unknown: type %d, code %d, value %d\n", event_buf[i].type, event_buf[i].code, event_buf[i].value);
            		break;
			} 
		} 
	}

	close(fd);
}


 

@key_touch_test.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h>

#define EVENT_BUF_NUM 64

int             event_fd = -1;    /* the file descriptor for the device */

int
main(int argc, char **argv)
{
    int             i;       
    size_t          read_bytes;        
    struct input_event event_buf[EVENT_BUF_NUM];
    char           *device;
    
    fd_set          fdset;
    int             max_fd;
    static struct timeval zero;
    
    

    if( argc != 2 )
    {
        printf("Usage: key_touch_test key\n");
		printf("or\n");        
	printf("       key_touch_test touch\n");        
        exit(1);
    }
    
    if( !strncmp("key", argv[1], 3) ) 		// key button
    {
    	device = "/dev/input/event2";
    }
    else if( !strncmp("touch", argv[1], 5) ) 	// touch screen
    {
    	device = "/dev/input/event0";
    }
    else
    {
    	printf("device argument error\n");
    	exit(1);
    }
    
    // input device open
    if ((event_fd = open(device, O_RDONLY)) < 0)
    {
        printf("%s: open error", device);
        exit(1);
    }
printf("eventfd = %d\n", event_fd);


    
    printf("max_fd = %d\n", max_fd);


    while (1)
    {

    FD_ZERO(&fdset);
    max_fd = 0;
    FD_SET(event_fd, &fdset);
    if( max_fd < event_fd )
    {
        max_fd = event_fd;
    }
    zero.tv_sec = 1;
    zero.tv_usec = 0;
    	
        if( select(max_fd + 1, &fdset, NULL, NULL, &zero) > 0 )
        {
            if( FD_ISSET(event_fd, &fdset) )
            {
            	printf("event.......keyboard\n");
                read_bytes = read(event_fd, event_buf, (sizeof(struct input_event)*EVENT_BUF_NUM) );
                printf("read_bytes = %d\n", read_bytes);
                if( read_bytes < sizeof(struct input_event) )
                {
                    printf("%s: read error", device);
                    exit(1);
                }
                
                for( i=0; i<(read_bytes/sizeof(struct input_event)); i++ )
                {
                    switch( event_buf[i].type )
                    {
                    case EV_SYN:
                        printf("---------------------------------------\n");
                        break;
                    	
                    case EV_KEY:
                        printf("Button code %d", event_buf[i].code);
                        switch (event_buf[i].value)
                        {
                        case 1:
                            printf(": pressed\n");
                            break;
                        case 0:
                            printf(": released\n");
                            break;
                        default:
                            printf("Unknown: type %d, code %d, value %d",
                                  event_buf[i].type, 
                                  event_buf[i].code, 
                                  event_buf[i].value);
                            break;
                        }
                        break;
                        
                    case EV_ABS:
                        switch (event_buf[i].code)
                        {
                        case ABS_X:
                            printf("X position: %d\n", event_buf[i].value);
                            break;
                        case ABS_Y:
                            printf("Y position: %d\n", event_buf[i].value);
                            break;
                        case ABS_PRESSURE:    
                        	printf("Pressure  : %s\n", (event_buf[i].value == 1)? "yes":"no" );
                        	break;
                        default:
                            printf("Touch Unknown: type %d, code %d, value %d\n",
                                  event_buf[i].type, 
                                  event_buf[i].code, 
                                  event_buf[i].value);
                            break;
                        }
						break;            
                    default:
                        printf("Unknown: type %d, code %d, value %d\n",
                               event_buf[i].type, 
                               event_buf[i].code, 
                               event_buf[i].value);
            
                        break;
                    } // switch
                } // for
            } // FD_ISSET
        } // select
        else
        	printf("timeout\n");
    } // while

    close(event_fd);

    exit(0);
}

 

@keyint_bottom_kscan_platform_input.c

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>

#include <linux/workqueue.h> //work queue

#include <linux/cdev.h> //cdev_init
#include <linux/wait.h> //wait_event_interruptible
#include <asm/uaccess.h> //user access
#include <linux/fs.h> //file_operatios 

#include <linux/platform_device.h> //platform_driver_register()


#include <linux/gpio.h>
#include <linux/input.h> //input_dev

#define DRV_NAME		"keyint"
#define S3C2410BUTVERSION	0x001


#define	KEY_MATRIX_BASE1	2
#define	KEY_MATRIX_BASE2	3
#define	KEY_MATRIX_BASE3	4
#define	KEY_MATRIX_BASE4	5
#define	KEY_MATRIX_BASE5	6

#define EXAMPLE 200 
//======================================
// 100 : workqueue test
// 200 : tasklet test
// others : non bottom-half
//======================================

#if 1
	#define gprintk(fmt, x... ) printk( "%s: " fmt, __FUNCTION__ , ## x)
#else
	#define gprintk(x...) do { } while (0)
#endif

//static int key_major = 0, key_minor = 0;
//static int result;
//static dev_t key_dev;
//static struct cdev key_cdev;

//static char dev_name[] = "rebis_keyscan";
static int cur_key, old_key;
//static int flag = 0;

static DECLARE_WAIT_QUEUE_HEAD(wq);

struct rebis_key_detection
{
    int             irq;
    int             pin;
    int             pin_setting;
    char            *name;
    int             last_state;
	#if (EXAMPLE == 100)
	struct work_struct gdetect;
	#elif (EXAMPLE == 200)
	struct tasklet_struct gdetect;
	#endif
};

static struct rebis_key_detection rebis_gd = {
    IRQ_EINT7, S3C2410_GPF(7), S3C2410_GPF7_EINT7, "key-detect", 0
};

static struct s3c2440_buttons_private
{
	struct input_dev *input;
	spinlock_t		lock;
	int 			count;
	int 			shift;
	char 			phys[32];
};

struct s3c2440_buttons_private * s3c2440_buttons_private;

static int scan_input(void);
//static int key_register_cdev(void);

static irqreturn_t
rebis_keyevent(int irq, void *dev_id, struct pt_regs *regs)
{
    struct rebis_key_detection *gd = (struct rebis_key_detection *) dev_id;
    int             state;

	state = 1;
	printk("gd= %x, keypad was pressed \n",(unsigned int)gd);

    if (!gd)
        return IRQ_HANDLED;

	#if 1
    state = gpio_get_value(gd->pin);

    gd->last_state = state;

    gprintk("%s gd %s\n\n", gd->name, state ? "high" : "low");
	#endif

	#if (EXAMPLE == 100)
	schedule_work(&gd->gdetect);
	#elif (EXAMPLE == 200)
	tasklet_schedule(&gd->gdetect);
	#endif 

	//flag = 1;
	//wake_up_interruptible(&wq);

	return IRQ_HANDLED;

}

static int scan_input(void) {

	if(((readl(S3C2410_GPFDAT) >> 7) & 0x1) != 0x1)
	{
        if(!gpio_get_value(S3C2410_GPF(7)))
			return 1;
	}
	return 0;
}

#if (EXAMPLE == 100)
static void rebis_keyint_callback(void *pgd)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)pgd;    
    //int state = gd->last_state;
	int i;
	gprintk("workqueue callback call\n\n");

	//for key scan
	#if 1
	cur_key = 0;
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x1f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key += (i-2);//key_base[i];
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			
			//add by june
			//printk("private->input = 0x%x, cur_key=%d \n", s3c2440_buttons_private->input, cur_key);
			input_report_key(s3c2440_buttons_private->input, cur_key, 1);
			//mdelay(100);
			input_sync(s3c2440_buttons_private->input);

			input_report_key(s3c2440_buttons_private->input, cur_key, 0);
			//mdelay(100);
			input_sync(s3c2440_buttons_private->input);



			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#endif
}
#elif (EXAMPLE == 200)
static void rebis_keyint_callback(ulong data)
{
    //struct rebis_key_detection *gd = (struct rebis_key_detection *)data;    
    //int state = gd->last_state;
	int i;
	//int key_base[5] = {KEY_MATRIX_BASE5, KEY_MATRIX_BASE4, KEY_MATRIX_BASE3, KEY_MATRIX_BASE2, KEY_MATRIX_BASE1};
	

	gprintk("tasklet callback call\n");


	//for key scan
	#if 1
	cur_key = 0;
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(0));

	for(i=6; i>=2; i--)
	{
		writel(readl(S3C2410_GPFDAT) | (0x1f), S3C2410_GPFDAT);
		writel(readl(S3C2410_GPFDAT) & (~(0x1 << i)), S3C2410_GPFDAT);
		
		cur_key = scan_input();
		if(cur_key)
		//cur_key = scan_input();
		{
			cur_key += (i-2);//key_base[i];
			if(cur_key == old_key)
				goto SameValue;
			old_key = cur_key;
			printk("cur_key = %d \n\n", cur_key);
			
			//add by june
			//printk("private->input = 0x%x, cur_key=%d \n", s3c2440_buttons_private->input, cur_key);
			input_report_key(s3c2440_buttons_private->input, cur_key, 1);
			//mdelay(100);
			input_sync(s3c2440_buttons_private->input);

			input_report_key(s3c2440_buttons_private->input, cur_key, 0);
			//mdelay(100);
			input_sync(s3c2440_buttons_private->input);



			//put_user(cur_key,(char *)buff);
			break;
		}
	}
SameValue:
	old_key = 0;

    // set GPBDAT 0
    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    // change External Interrupts
    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));

	#endif
}
#endif




static int s3c2410keypad_remove(struct platform_device *pdev)
{
	free_irq(rebis_gd.irq, &rebis_gd);
		
	#if (EXAMPLE == 100)
	#elif (EXAMPLE == 200)
	tasklet_kill(&rebis_gd.gdetect);
	#endif
	struct s3c2440_buttons_private *s3c2440_buttons_private_temp = platform_get_drvdata(pdev);
	input_unregister_device(s3c2440_buttons_private_temp->input);
	kfree(s3c2440_buttons_private_temp);



    gpio_free(S3C2410_GPF(2));
    gpio_free(S3C2410_GPF(3));
    gpio_free(S3C2410_GPF(4));
    gpio_free(S3C2410_GPF(5));
    gpio_free(S3C2410_GPF(6));


	printk("this is s3c2410keypad_remove\n");
	return 0;
}

static int __init s3c2410keypad_probe(struct platform_device *pdev)
{
	int i;
	int ret;
	int error;
	struct input_dev 	*input_dev;

    gpio_request(S3C2410_GPF(2), "Key 1");
    gpio_request(S3C2410_GPF(3), "Key 2");
    gpio_request(S3C2410_GPF(4), "Key 3");
    gpio_request(S3C2410_GPF(5), "Key 4");
    gpio_request(S3C2410_GPF(6), "Key 5");


    // set output mode
    s3c_gpio_cfgpin(S3C2410_GPF(2), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(3), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(4), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(5), S3C_GPIO_SFN(1));
    s3c_gpio_cfgpin(S3C2410_GPF(6), S3C_GPIO_SFN(1));

    gpio_direction_output(S3C2410_GPF(2), 1);
    gpio_direction_output(S3C2410_GPF(3), 1);
    gpio_direction_output(S3C2410_GPF(4), 1);
    gpio_direction_output(S3C2410_GPF(5), 1);
    gpio_direction_output(S3C2410_GPF(6), 1);

    s3c_gpio_cfgpin(S3C2410_GPF(7), S3C_GPIO_SFN(2));



	//input device register
	s3c2440_buttons_private = kzalloc(sizeof(struct s3c2440_buttons_private), GFP_KERNEL);	
	input_dev = input_allocate_device();

	if(!s3c2440_buttons_private || !input_dev){
		printk("memory alloc error --------------------------\n");
		error = -ENOMEM;
		goto fail;
	}
	
	platform_set_drvdata(pdev, s3c2440_buttons_private);
	s3c2440_buttons_private->input = input_dev;
	input_dev->evbit[0] = BIT(EV_KEY);
//	input_dev->private	= s3c2440_buttons_private;

	input_dev->name 	= DRV_NAME;
	input_dev->id.bustype	=	BUS_HOST;
	input_dev->id.vendor 	= 	0xDEAD;
	input_dev->id.product	= 	0xBEEF;
	input_dev->id.version 	= 	S3C2410BUTVERSION;

	for(i=5;i>0;i--){
		set_bit(i,input_dev->keybit);
	}

	#if (EXAMPLE == 100)
	//INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback, &rebis_gd);
	INIT_WORK(&rebis_gd.gdetect, rebis_keyint_callback);
	#elif (EXAMPLE == 200)
	tasklet_init(&rebis_gd.gdetect, rebis_keyint_callback, (unsigned long)(&rebis_gd)); 
	#endif

	if( request_irq(IRQ_EINT7, (void *)rebis_keyevent, IRQF_DISABLED | IRQF_TRIGGER_RISING, DRV_NAME, &rebis_gd) )     
    {
                printk("failed to request external interrupt.\n");
                ret = -ENOENT;
               return ret;
    }
	
	
	input_register_device(input_dev);
    printk("this is s3c2410keypad_probe\n");

	
	return 0;

fail:	kfree(s3c2440_buttons_private);
	input_free_device(input_dev);
	return error;
}

static void release_pdev(struct device * dev){
	dev->parent 	= NULL;
}

static struct platform_device pdev  =
{
	.name	= "s3c2410-keypad2",
	.id		= -1,
	.dev	= {
		.release	= release_pdev,
	},
};

static struct platform_driver s3c2410keypad_driver = {
       .driver         = {
	       .name   = "s3c2410-keypad2",
	       .owner  = THIS_MODULE,
       },
       .probe          = s3c2410keypad_probe,
       .remove         = s3c2410keypad_remove,
};

static int __init rebis_keyint_init(void)
{
	int result;
	result = platform_driver_register(&s3c2410keypad_driver);
	
	if(!result){
		printk("platform_driver initiated  = %d \n", result);
		result = platform_device_register(&pdev);
		printk("platform_device_result = %d \n", result);
		if(result)
			platform_driver_unregister(&s3c2410keypad_driver);
	}
	printk(KERN_INFO "%s successfully loaded\n", DRV_NAME);

	return result;
    
}

static void __exit rebis_keyint_exit(void)
{
	platform_device_unregister(&pdev);
	platform_driver_unregister(&s3c2410keypad_driver);

    printk(KERN_INFO "%s successfully removed\n", DRV_NAME);
}

module_init(rebis_keyint_init);
module_exit(rebis_keyint_exit);

MODULE_LICENSE("GPL");

/********************************************************
* platform device µî·Ï °úÁ¤ 
REBIS_BSP : arch/arm/mach-s3c2410/mach-rebis.c

static struct platform_device *rebis_devices[] __initdata = {
    &s3c_device_usb,
    &s3c_device_lcd,
    &s3c_device_wdt,
    &s3c_device_i2c,
    &s3c_device_iis,
    &s3c_device_nand,
    &s3c_device_sound, 
    &s3c_device_ts, 
    &s3c_device_sdi,
    &s3c_device_keypad, --> Ãß°¡
};

REBIS_BSP : arch/arm/mach-s3c2410/devs.c
struct platform_device s3c_device_keypad = {
    .name   = "s3c2410-keypad",
    .id     = -1,
};
EXPORT_SYMBOL(s3c_device_keypad);

REBIS_BSP : arch/arm/mach-s3c2410/devs.h
extern struct platform_device s3c_device_keypad; 

**************************************************************/

 

 

 

 

'전자회로 > 임베디드교육' 카테고리의 다른 글

7주차  (0) 2020.05.18
6  (0) 2020.05.11
5  (0) 2020.05.04
4  (0) 2020.04.27
MMU(memory management unit) -수정중  (0) 2020.04.21
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함