티스토리 뷰
- 장치들을 파일로 관리(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;
**************************************************************/