bottom half

EEEFFEE·2024년 1월 10일
0

임베디드 리눅스

목록 보기
11/14

24.01.10 최초 작성

예제

threaded IRQ

등록해제인터럽트 핸들러bottom half
k_module_init()k_module_exit()k_top_half_gpio_irq_signal_handler()k_bottom_half_gpio_irq_signal_handler()
  • k_module_init() : GPIO 16번에 신호가 들어오면 인터럽트 핸들러 실행되도록 설정

static int __init k_module_init(void)
{
	
    ...
    
    button_irq = gpio_to_irq(16);

	if (request_threaded_irq(button_irq, (void *)k_top_half_gpio_irq_signal_handler, 
			(void *)k_bottom_half_gpio_irq_signal_handler, IRQF_TRIGGER_RISING, "k_gpio_irq_signal", NULL) != 0) {
		pr_err("Error!\n Can not request interrupt nr: %d\n", button_irq);
		gpio_free(17);
		return -1;
	}
    
    ...
    
    
}

  • k_top_half_gpio_irq_signal_handler() : 메시지를 출력하는 인터럽트 핸들러

static irq_handler_t k_top_half_gpio_irq_signal_handler(unsigned int irq, void *dev_id)
{
	unsigned long diff = jiffies - pre_jiffie;

	if (in_interrupt()) {
		pr_info("k_top_half_gpio_irq_signal_handler: interrupt conext 입니다.!!!\n");
	} else {
		pr_info("k_top_half_gpio_irq_signal_handler: thread conext 입니다.!!!\n");
	}

	/* 200 msec 이내에 호출한 인터럽트는 무시 */
	if (diff < msecs_to_jiffies(200)) {
		return (irq_handler_t) IRQ_HANDLED;
	}

	pre_jiffie = jiffies;

	/*
	 * 만약 리턴 값이 IRQ_HANDLED 이면, k_bottom_half_gpio_irq_signal_handler 호출 안함.
     * IRQ_WAKE_THREAD이면 bottom half thread 호출함.
	*/
	return (irq_handler_t)IRQ_WAKE_THREAD;
}

  • k_bottom_half_gpio_irq_signal_handler() : 메시지를 출력하고 LED를 조작하는 bottom half 핸들러

static irq_handler_t k_bottom_half_gpio_irq_signal_handler(unsigned int irq, void *dev_id)
{
	if (in_interrupt()) {
		pr_info("k_top_half_gpio_irq_signal_handler: interrupt conext 입니다.!!!\n");
	} else {
		pr_info("k_top_half_gpio_irq_signal_handler: thread conext 입니다.!!!\n");
	}

	pr_info("k_gpio_irq_signal_handler: interrupt triggered!!!\n");

	led = (0x01 ^ led);
	gpio_set_value(K_GPIO_OUTPUT, led);
	pr_info("Interrupt(Threaded Handler) : K_GPIO_OUTPUT : %d ",gpio_get_value(K_GPIO_OUTPUT));

	return (irq_handler_t) IRQ_HANDLED;
}

workqueue

등록해제read인터럽트 핸들러bottom half
k_module_init()k_module_exit()k_driver_read()k_top_half_gpio_irq_signal_handler()workqueue_fn()
  • k_module_init() : 인터럽트 핸들러 등록 및 워크큐 생성

static int __init k_module_init(void)
{
	...
    
    button_irq = gpio_to_irq(16);

	if (request_threaded_irq(button_irq, (void *)k_top_half_gpio_irq_signal_handler, 
			(void *)k_bottom_half_gpio_irq_signal_handler, IRQF_TRIGGER_RISING, "k_gpio_irq_signal", NULL) != 0) {
		pr_err("Error!\n Can not request interrupt nr: %d\n", button_irq);
		gpio_free(17);
		return -1;
	}

	k_workqueue = create_workqueue("k_wq");
    
    ...
}

  • workqueue_fn() : k_top_half_gpio_irq_signal_handler()의 bottom half로 워크큐에 GPIO 입력을 저장

//	work를 통해 workqueue_fn 호출
static DECLARE_WORK(work, workqueue_fn);

//	매크로를 통해 linked list 생성
struct k_list {
	struct list_head list;
	int data;
};

//	매크로를 통해 동기화 lock 설정
static DEFINE_RWLOCK(k_rwlock);

LIST_HEAD(k_list_head);


volatile int k_value = 0;

static void workqueue_fn(struct work_struct *work)
{
	struct k_list *new_node = NULL;

	if (in_interrupt()) {
		pr_info("workqueue_fn: interrupt conext 입니다.!!!\n");
	} else {
		pr_info("workqueue_fn: thread conext 입니다.!!!\n");
	}
	new_node = kmalloc(sizeof(struct k_list), GFP_KERNEL);
	new_node->data = k_value++;
	INIT_LIST_HEAD(&new_node->list);
    
    //	쓰기 동기화
	write_lock(&k_rwlock);
	list_add_tail(&new_node->list, &k_list_head);
	write_unlock(&k_rwlock);
}

  • k_top_half_gpio_irq_signal_handler() : 인터럽트 핸들러

static irq_handler_t k_top_half_gpio_irq_signal_handler(unsigned int irq, void *dev_id)
{
	unsigned long diff = jiffies - pre_jiffie;

	if (in_interrupt()) {
		pr_info("k_top_half_gpio_irq_signal_handler: interrupt conext 입니다.!!!\n");
	} else {
		pr_info("k_top_half_gpio_irq_signal_handler: thread conext 입니다.!!!\n");
	}

	/* 200 msec 이내에 호출한 인터럽트는 무시 */
	if (diff < msecs_to_jiffies(200)) {
		return (irq_handler_t) IRQ_HANDLED;
	}

	pre_jiffie = jiffies;

	queue_work(k_workqueue, &work);
	/*
	 * 만약 리턴 값이 IRQ_HANDLED 이면, k_bottom_half_gpio_irq_signal_handler 호출 안함.
     * IRQ_WAKE_THREAD이면 bottom half thread 호출함.
	*/
	return (irq_handler_t)IRQ_WAKE_THREAD;
}

  • k_driver_read() : 워크큐에 저장된 모든 내용을 출력하고 워크큐에서 삭제

static ssize_t k_driver_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
{
	struct k_list *temp;
	int list_count = 0;

	read_lock(&k_rwlock);
	list_for_each_entry(temp, &k_list_head, list) {
		pr_info("list_count : %d, data : %d\n", list_count++, temp->data);
	}
	read_unlock(&k_rwlock);

	pr_info("Node count: %d\n", list_count);
	return BUF_SIZE;
}

  • k_module_exit() : 워크큐 삭제

static void __exit k_module_exit(void)
{
	...
    
    destroy_workqueue(k_workqueue);
    
    ...

}

0개의 댓글

관련 채용 정보