보통은 major number로 다루지만, tty 처럼 minor number 기반으로 처리해야 할 상황들이 있을 때 사용된다.
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/fs.h>
#include <asm/uaccess.h> //copy_to_user
#include <asm/io.h> //outb, inb
#include <linux/delay.h> //mdelay, udelay
#define FND 0xf1000000
#define FND_CS0 (*((volatile unsigned short *)(FND)))
#define FND_CS1 (*((volatile unsigned short *)(FND + 0x00100000)))
#define FND_CS2 (*((volatile unsigned short *)(FND + 0x00200000)))
#define FND_CS3 (*((volatile unsigned short *)(FND + 0x00300000)))
#define ADDR_FND_CS0 ((volatile unsigned short *)FND)
#define ADDR_FND_CS1 ((volatile unsigned short *)(FND + 0x00100000))
#define ADDR_FND_CS2 ((volatile unsigned short *)(FND + 0x00200000))
#define ADDR_FND_CS3 ((volatile unsigned short *)(FND + 0x00300000))
static short fnd_val[] = {0x3f3f,0x0606,0x5b5b,0x4f4f,0x6666,0x6d6d,0x7c7c,0x2727,0x7f7f,0x6767};
static int FND_MAJOR = 0;
//int fnd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg);
int fnd0_open (struct inode *inode, struct file *filp)
{
printk("fnd0_open\n");
MOD_INC_USE_COUNT;
return 0; /* success */
}
int fnd0_release (struct inode *inode, struct file *filp)
{
printk("fnd0_close\n");
MOD_DEC_USE_COUNT;
return 0;
}
ssize_t fnd0_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;
data = FND_CS0;
// equals
//data = inw(ADDR_FND_CS0);
printk("read data => %04x\n",data);
copy_to_user(buf, &data, count);
return(count);
}
ssize_t fnd0_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
int data;
copy_from_user(&data, buf, count);
printk("write data => %08x\n",data);
FND_CS0 = fnd_val[data];
// equals
//outw(fnd_val[data], ADDR_FND_CS0);
return(count);
}
int fnd0_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case 10:
printk("\n");
printk("cmd = 1\n");
FND_CS0 = 0x0000;
break;
default:
break;
}
return 0;
}
int fnd1_open (struct inode *inode, struct file *filp)
{
printk("fnd1_open\n");
MOD_INC_USE_COUNT;
return 0; /* success */
}
int fnd1_release (struct inode *inode, struct file *filp)
{
printk("fnd1_close\n");
MOD_DEC_USE_COUNT;
return 0;
}
ssize_t fnd1_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;
data = FND_CS1;
// equals
//data = inw(ADDR_FND_CS0);
printk("read data => %04x\n",data);
copy_to_user(buf, &data, count);
return(count);
}
ssize_t fnd1_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
int data;
copy_from_user(&data, buf, count);
printk("write data => %08x\n",data);
FND_CS1 = fnd_val[data];
// equals
//outw(fnd_val[data], ADDR_FND_CS1);
return(count);
}
int fnd1_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case 10:
printk("\n");
printk("cmd = 1\n");
FND_CS1 = 0x0000;
break;
default:
break;
}
return 0;
}
int fnd2_open (struct inode *inode, struct file *filp)
{
printk("fnd2_open\n");
MOD_INC_USE_COUNT;
return 0; /* success */
}
int fnd2_release (struct inode *inode, struct file *filp)
{
printk("fnd2_close\n");
MOD_DEC_USE_COUNT;
return 0;
}
ssize_t fnd2_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;
data = FND_CS2;
// equals
//data = inw(ADDR_FND_CS2);
printk("read data => %04x\n",data);
copy_to_user(buf, &data, count);
return(count);
}
ssize_t fnd2_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
int data;
copy_from_user(&data, buf, count);
printk("write data => %08x\n",data);
FND_CS2 = fnd_val[data];
// equals
//outw(fnd_val[data], ADDR_FND_CS2);
return(count);
}
int fnd2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case 10:
printk("\n");
printk("cmd = 1\n");
FND_CS2 = 0x0000;
break;
default:
break;
}
return 0;
}
int fnd3_open (struct inode *inode, struct file *filp)
{
printk("fnd3_open\n");
MOD_INC_USE_COUNT;
return 0; /* success */
}
int fnd3_release (struct inode *inode, struct file *filp)
{
printk("fnd3_close\n");
MOD_DEC_USE_COUNT;
return 0;
}
ssize_t fnd3_read (struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
short data;
data = FND_CS3;
// equals
//data = inw(ADDR_FND_CS3);
printk("read data => %04x\n",data);
copy_to_user(buf, &data, count);
return(count);
}
ssize_t fnd3_write (struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
int data;
copy_from_user(&data, buf, count);
printk("write data => %08x\n",data);
FND_CS3 = fnd_val[data];
// equals
//outw(fnd_val[data], ADDR_FND_CS0);
return(count);
}
int fnd3_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
switch(cmd)
{
case 10:
printk("\n");
printk("cmd = 1\n");
FND_CS3 = 0x0000;
break;
default:
break;
}
return 0;
}
void fnd_clear(void)
{
FND_CS0 = 0x0000;
FND_CS1 = 0x0000;
FND_CS2 = 0x0000;
FND_CS3 = 0x0000;
//mdelay(500);
}
/*
struct file_operations fnd_fops = {
read: fnd_read,
write: fnd_write,
open: fnd_open,
ioctl: fnd_ioctl,
release: fnd_release,
};
*/
struct file_operations minor0_fops =
{
read : fnd0_read,
write : fnd0_write,
open : fnd0_open,
release : fnd0_release,
ioctl : fnd0_ioctl,
};
struct file_operations minor1_fops =
{
read : fnd1_read,
write : fnd1_write,
open : fnd1_open,
release : fnd1_release,
ioctl : fnd1_ioctl,
};
struct file_operations minor2_fops =
{
read : fnd2_read,
write : fnd2_write,
open : fnd2_open,
release : fnd2_release,
ioctl : fnd2_ioctl,
};
struct file_operations minor3_fops =
{
read : fnd3_read,
write : fnd3_write,
open : fnd3_open,
release : fnd3_release,
ioctl : fnd3_ioctl,
};
int minor_open (struct inode *inode, struct file *filp)
{
printk( "call minor_open\n" );
printk("MINOR(inode->i_rdev) : %d\n", MINOR(inode->i_rdev));
switch (MINOR(inode->i_rdev))
{
case 0: filp->f_op = &minor0_fops; break;
case 1: filp->f_op = &minor1_fops; break;
case 2: filp->f_op = &minor2_fops; break;
case 3: filp->f_op = &minor3_fops; break;
default : return -ENXIO;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode,filp);
return -2;
}
struct file_operations minor_fops =
{
open : minor_open,
};
static int __init fnd_init(void)
{
int result;
printk("FND module is up...\n");
fnd_clear(); // H/W Initialization
//result = register_chrdev(FND_MAJOR, "FND", &fnd_fops);
result = register_chrdev(FND_MAJOR, "FND", &minor_fops);
if (result < 0) {
printk(KERN_WARNING " can't get major \n");
return result;
}
if(FND_MAJOR == 0)
FND_MAJOR = result;
printk("FND_MAJOR = %d\n", FND_MAJOR);
return 0;
}
static void __exit fnd_exit(void)
{
unregister_chrdev(FND_MAJOR, "FND");
printk("FND module is down...\n");
}
module_init(fnd_init);
module_exit(fnd_exit);
테스트 app
/***************************************
* Filename: fnd_app.c
* Title: fnd Device Application
* Desc: Implementation of system call
***************************************/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
int main(void)
{
int retn;
int fd;
int flag = 0;
int fnd_no, menu;
char dev_name[100];
while(1)
{
printf("\n\n===== FND Test Program ====\n");
printf(" 0. FND0 Test\n");
printf(" 1. FND1 Test\n");
printf(" 2. FND2 Test\n");
printf(" 3. FND3 Test\n");
printf(" 4. Program Quit\n");
do {
printf(" Select Menu => ");
scanf("%d", &fnd_no);
} while(fnd_no<0 || fnd_no>4);
if(fnd_no == 4) break;
while(1)
{
printf("\nFND%d Test...\n", fnd_no);
do {
printf("Select(0.0_print,1.1_print, ...,9.9_print,10.clear,11.Quit) => ");
scanf("%d", &menu);
} while(menu<0 || menu>11);
if(menu == 11) break;
sprintf(dev_name, "/dev/FND%d", fnd_no);
fd = open(dev_name, O_RDWR);
if (fd<0) {
perror(dev_name);
exit(-1);
}
else printf("/dev/FND%d detected(fd:%d)...\n", fnd_no, fd);
if (menu == 10) {
ioctl(fd, menu, flag); // clear
}
else { // menu is 4byte(int)
write(fd, &menu, sizeof(menu));
}
close(fd);
}
}
return 0;
}
Makefile
CC = arm-linux-gcc
INCLUDEDIR = /root/linux/include
#CFLAGS += -D__KERNEL__ -DMODULE -I$(INCLUDEDIR) -Wall -O
CFLAGS += -D__KERNEL__ -DMODULE -I$(INCLUDEDIR) -Wall -march=armv4 -DMODULE
#CFLAGS += -D__KERNEL__ -I$(INCLUDEDIR) -Wall -Wstrict-prototypes \
# -Wno-trigraphs -Os -mapcs -fno-strict-aliasing -fno-common \
# -fno-common -pipe -mapcs-32 -march=armv4 -Wa, -mtune=strongarm \
# -mshort-load-bytes -msoft-float -DMODULE
TARGET = fnd
OBJS = $(TARGET).o
All: $(TARGET).o $(TARGET)_app
$(TARGET).o: $(TARGET).c
$(CC) $(CFLAGS) -c -o $(OBJS) $(TARGET).c
$(TARGET)_app : $(TARGET)_app.c
$(CC) -o $(TARGET)_app $(TARGET)_app.c
clean :
rm -rf *.o $(TARGET)_app
보드에서 커널 모듈 등록
실행코드
와우 겁나 잼있다..
ioctl의 cmd 부분은 실제로는 다르게 사용되고 있다.
cmd의 구성은 구분번호, 매직번호, 데이터 크기. 읽기 쓰기 구분으로 나눠서 argument를 전달 할 수 있다.
'c or linux' 카테고리의 다른 글
[공부중] Device 관점에 본 /proc와 /sys 디렉토리 (0) | 2011.04.20 |
---|---|
[공부중] 커널 타이머 (0) | 2011.04.20 |
[공부중] memory mapped / io mapped (0) | 2011.04.19 |
device 목록 보고 (0) | 2011.04.19 |
tar를 풀면서(extract) 특정 디렉토리로 이동하기 (0) | 2011.04.19 |