배우고픈 공돌이
예제. 본문
1. 다중화 입출력을 사용해서 디바이스를 제어한다.
2. 타이머 인터럽트로 5초마다 한 번씩 화면에 나타낸다. read 장치 1
3. 장치에 타이머 시간을 준다. write 장치 1
4. 지정 시간 후, 커널의 정보를 화면에 띄우며 종료한다. read 장치 2
#ifndef _SKELETON_H_
#define _SKELETON_H_
#define TIME_STEP (10 * HZ / 10)
typedef struct
{
struct timer_list timer;
unsigned long work;
}__attribute((packed))KERNEL_TIMER_MANAGER;
typedef struct
{
int pid;
int next_fd;
int f_flags;
unsigned long start_code;
unsigned long end_code;
unsigned long i_ino;
}__attribute((packed))PUSH_DATA;
void kerneltimer_timeover(unsigned long);
void kerneltimer_register(KERNEL_TIMER_MANAGER *, unsigned long);
void call_workqueue(struct work_struct *);
#endif
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/fdtable.h>
#include <linux/timer.h>
#include "skeleton.h"
#include <linux/interrupt.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/poll.h>
#define SK_IRQ 7
#define SK_MAJOR 240
static DECLARE_WAIT_QUEUE_HEAD(wait_read1);
static DECLARE_WAIT_QUEUE_HEAD(wait_read2);
static DECLARE_WAIT_QUEUE_HEAD(wait_write);
static KERNEL_TIMER_MANAGER *ptrmng = NULL;
static int sec;
static int time_limit;
static int input_time[3];
int read1_condition = 0;
int read2_condition = 0;
int write_condition = 0;
/* 장치 인터럽트 */
static irqreturn_t read1_interrupt(int irq, void *dev_id)
{
sec+=5;
read1_condition++;
wake_up_interruptible(&wait_read1);
return IRQ_HANDLED;
}
static irqreturn_t read2_interrupt(int irq, void *dev_id)
{
time_limit++;
if(time_limit == input_time[2])
{
read2_condition++;
wake_up_interruptible(&wait_read2);
}
return IRQ_HANDLED;
}
static irqreturn_t write_interrupt(int irq, void *dev_id)
{
write_condition++;
wake_up_interruptible(&wait_write);
return IRQ_HANDLED;
}
// ===========================
/* 타이머 셋팅 */
void kerneltimer_register(KERNEL_TIMER_MANAGER *ptr, unsigned long timeover)
{
init_timer(&(ptr->timer));
ptr->timer.expires = get_jiffies_64() + timeover;
ptr->timer.data = (unsigned long)ptr;
ptr->timer.function = kerneltimer_timeover;
add_timer(&(ptr->timer));
}
void kerneltimer_timeover(unsigned long arg)
{
KERNEL_TIMER_MANAGER *ptr = NULL;
if(arg)
{
ptr = (KERNEL_TIMER_MANAGER *)arg;
ptr->work++;
if(ptr->work % 5 == 0)
{
read1_interrupt(SK_IRQ, NULL);
write_interrupt(SK_IRQ, NULL);
}
read2_interrupt(SK_IRQ, NULL);
}
kerneltimer_register(ptr, TIME_STEP);
}
// ===========================
/* Minor 1 */
static int minor1_open(struct inode *inode, struct file *filp)
{
printk("minor1 open success!\n");
sec=0;
return 0;
}
static int minor1_release(struct inode *inode, struct file *filp)
{
printk("minor1 release success!\n");
sec=0;
return 0;
}
static ssize_t minor1_read(struct file *filp, char * buf,size_t count,loff_t * f_pos)
{
copy_to_user(buf, &sec, count);
read1_condition = 0;
return count;
}
static unsigned int minor1_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(filp, &wait_read1, wait);
if(read1_condition > 0)
mask |= POLLIN | POLLRDNORM;
return mask;
}
// ===========================
/* Minor 2 */
static int minor2_open(struct inode *inode, struct file *filp)
{
printk("minor2 open success!\n");
return 0;
}
static int minor2_release(struct inode *inode, struct file *filp)
{
printk("minor2 release success!\n");
return 0;
}
static ssize_t minor2_write(struct file *filp, const char * buf, size_t count, loff_t * f_pos)
{
copy_from_user(input_time, buf, count);
printk("current TIME write >>> %d\n", input_time[0]);
printk("expire TIME write >>> %d\n", input_time[1]);
input_time[2] = time_limit+input_time[1]-input_time[0];
sec = 0;
write_condition=0;
return count;
}
static unsigned int minor2_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(filp, &wait_write, wait);
if(write_condition > 0)
mask |= POLLOUT | POLLWRNORM;
return mask;
}
// ===========================
/* Minor 3 */
static int minor3_open(struct inode *inode, struct file *filp)
{
printk("minor3 open success!\n");
return 0;
}
static int minor3_release(struct inode *inode, struct file *filp)
{
printk("minor3 release success!\n");
return 0;
}
static ssize_t minor3_read(struct file *filp, char * buf,size_t count,loff_t * f_pos)
{
PUSH_DATA stream;
stream.pid = current->pid;
stream.start_code = current->mm->start_code;
stream.end_code = current->mm->end_code;
stream.next_fd = current->files->next_fd;
stream.f_secs = filp->f_secs;
stream.i_ino = filp->f_path.dentry->d_inode->i_ino;
copy_to_user((unsigned long *)buf,&stream,sizeof(stream));
read2_condition=0;
return count;
}
static unsigned int minor3_poll(struct file *filp, poll_table *wait)
{
unsigned int mask = 0;
poll_wait(filp, &wait_read2, wait);
if(read2_condition > 0)
mask |= POLLIN | POLLRDNORM;
return mask;
}
// ===========================
struct file_operations minor1_fops =
{
.owner = THIS_MODULE,
.open = minor1_open,
.release = minor1_release,
.read = minor1_read,
.poll = minor1_poll,
};
struct file_operations minor2_fops =
{
.owner = THIS_MODULE,
.open = minor2_open,
.release = minor2_release,
.write = minor2_write,
.poll = minor2_poll,
};
struct file_operations minor3_fops =
{
.owner = THIS_MODULE,
.open = minor3_open,
.release = minor3_release,
.read = minor3_read,
.poll = minor3_poll,
};
/* 부번호 처리 루틴 */
static int master_open(struct inode *inode, struct file *filp)
{
printk("master open success\n");
switch(MINOR(inode->i_rdev))
{
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 0;
}
struct file_operations master_fops =
{
.owner = THIS_MODULE,
.open = master_open,
};
// ===========================
static int __init skeleton_init(void)
{
int ret;
printk("skeleton init success\n");
ret = register_chrdev(SK_MAJOR, "skeleton", &master_fops);
if(ret < 0)
return ret;
request_irq(SK_IRQ, read1_interrupt, IRQF_SHARED, "skirq_read", NULL);
request_irq(SK_IRQ, read2_interrupt, IRQF_SHARED, "skirq_read", NULL);
request_irq(SK_IRQ, write_interrupt, IRQF_SHARED, "skirq_write", NULL);
ptrmng = kmalloc(sizeof(KERNEL_TIMER_MANAGER), GFP_KERNEL);
if(ptrmng == NULL)
return -ENOMEM;
memset(ptrmng, 0, sizeof(KERNEL_TIMER_MANAGER));
kerneltimer_register(ptrmng, TIME_STEP);
return 0;
}
static void __exit skeleton_exit(void)
{
printk("skeleton exit success\n");
unregister_chrdev(SK_MAJOR, "skeleton");
if(ptrmng != NULL)
{
del_timer(&(ptrmng->timer));
kfree(ptrmng);
}
}
module_init(skeleton_init);
module_exit(skeleton_exit);
MODULE_LICENSE("GPL");
read 1와 write 1 의 인터럽트는 5초마다 발생한다.
read 2의 인터럽트는 매초마다 발생한다.
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
typedef struct
{
int pid;
int next_fd;
int f_times;
unsigned long start_code;
unsigned long end_code;
unsigned long i_ino;
}__attribute((packed))PUSH_DATA;
int main_menu(void);
void open_dev(void);
void read_dev(void);
void write_dev(void);
void close_dev(void);
PUSH_DATA data;
int rfd1, wfd, rfd2, ret;
int pid, next_fd, f_times;
int time, flag,read_exit;
unsigned long i_ino;
unsigned long address_code[2] = {0};
int buff[2];
char key;
struct pollfd Events[2];
int retval,retval2;
int main(void)
{
open_dev();
write_dev();
while(1)
{
read_dev();
if(flag)
break;
}
close_dev();
return 0;
}
/* device open */
void open_dev(void)
{
printf("read open\n");
rfd1=open("/dev/sk1", O_RDWR);
if(rfd1 < 0)
{
perror("/dev/sk1");
exit(-1);
}
printf("rfd1 number is %d\n", rfd1);
printf("write open\n");
wfd=open("/dev/sk2", O_RDWR);
if(wfd < 0)
{
perror("/dev/sk2");
exit(-1);
}
printf("wfd number is %d\n", wfd);
printf("ioctl open\n");
rfd2=open("/dev/sk3", O_RDWR);
if(rfd2 < 0)
{
perror("/dev/sk3");
exit(-1);
}
printf("rfd2 number is %d\n", rfd2);
}
/* read device */
void read_dev(void)
{
memset(Events,0, sizeof(Events));
Events[0].fd = rfd1;
Events[0].events = POLLIN;
Events[1].fd = rfd2;
Events[1].events = POLLIN;
retval = poll((struct pollfd *)&Events, 2, 20000);
//poll(이벤트 배열 주소,장치 갯수, 응답 대기 시간)
//예외처리
if(retval < 0)
{
perror("poll error\n");
exit(EXIT_FAILURE);
}
if(retval == 0)
{
printf("Time over\n");
}
if(Events[0].revents & POLLERR)
{
printf("Device Error\n");
exit(EXIT_FAILURE);
}
if(Events[0].revents & POLLIN)
{
read(rfd1, &time, 4);
printf("%dsec... \n",time);
printf("press any key for stop\n");
}
if(Events[1].revents & POLLERR)
{
printf("Device Error\n");
exit(EXIT_FAILURE);
}
if(Events[1].revents & POLLIN)
{
read(rfd2, &data, sizeof(data));
printf("pid : %d\n",data.pid);
printf("next_fd : %d\n",data.next_fd);
printf("f_times : %d\n",data.f_times);
printf("Start_code : %ld\n",data.start_code);
printf("end_code : %ld\n",data.end_code);
printf("i_ino : %ld\n",data.i_ino);
flag=1;
}
}
/* write device */
void write_dev(void)
{
printf("input start time>>");
scanf("%d",&buff[0]);
printf("input end time>>");
scanf("%d",&buff[1]);
write(wfd, buff,sizeof(buff));
}
/* device close */
void close_dev(void)
{
printf("close dev\n");
close(rfd1);
close(wfd);
close(rfd2);
}