콘텐츠로 바로가기

now0930 일지

이런저런 생각

  • 홈
  • 비공개
  • 강좌
  • 잔여 작업 조회
  • 위치

character device를 tasklet으로 테스트

tasklet을 배우고 실습하다 보니, 캐릭터 디바이스를 활용한 예제를 찾았다. 하다보니 강제로 character device를 등록, 열고, 닫고, 읽고, 쓰는 방법을 배웠다. 대충 이해한 다음 아래 동작을 구성했다.

  • read: 앞쪽부터 읽음.
  • write: 데이터가 있다면 뒤로 채워 넣음.
  • 인터럽트: gpio를 시물레이션하여 앞에서 한 글자씩 지움.
  • 버퍼 용량은 char 256개 사용.

전에 사용한 파일을 수정했고, 모듈을 만들기 위한 makefile을 그대로 사용했다.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>     //GPIO


#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/fcntl.h>

#define GPIO_10_OUT (10)
#define DEVICE_DATA_MAX 256

unsigned int GPIO_irqNumber;

void my_tasklet_fn(unsigned long); 

/*\uc0ac\uc6a9\uc790 \ub370\uc774\ud130 \ubd80\ubd84*/
static struct my_device_data{
	struct cdev cdev;
	int index;
	char my_string[DEVICE_DATA_MAX];
}my_data_global;

void my_tasklet_fn(unsigned long data) 
{
	/*taskelet \ubc1c\uc0dd\ud558\uba74 \uc55e\uc5d0\uc11c\ubd80\ud130 1\uac1c \ubb38\uc790 \uc0ad\uc81c*/
	struct my_device_data *my_data;
	int i;
	/*address\ub85c data\uac00 \ub118\uc5b4\uc624\uae30 \ub54c\ubb38\uc5d0 \ud3ec\uc778\ud130\ub85c casting*/
	my_data = (struct my_device_data*)data;
	//\uc55e\uc5d0\uc11c\ubd80\ud130 \ud55c \ubb38\uc790 \uc0ad\uc81c
	for (i=0;i<DEVICE_DATA_MAX;i++)
	{
		my_data->my_string[i]=my_data->my_string[i+1];

	}
	my_data->my_string[(DEVICE_DATA_MAX-1)]=(char)0;
	pr_info("taskelet: deleted 1st character.\n");

}

/* Init the Tasklet by Static Method */
DECLARE_TASKLET(my_tasklet,my_tasklet_fn, (unsigned long)&my_data_global);


static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
{
	/*Scheduling Task to Tasklet*/
        tasklet_schedule(&my_tasklet); 
	//pr_info("interrupt occured\n");
	return IRQ_HANDLED;

}


//device driver \uc791\uc131 \ubd80\ubd84.
/*************\ub4dc\ub77c\uc774\ubc84 \ud568\uc218 ******************/
static int mydriver_open(struct inode *inode, struct file *file);
static int mydriver_release(struct inode *inode, struct file *file);
static ssize_t mydriver_read(struct file *flip,
		char *buf, size_t len, loff_t *off);
static ssize_t mydriver_write(struct file *flip,
		const char *buf, size_t len, loff_t *off);
/********************************************/



//file operation structure

static struct file_operations fops = 
{
	.owner = THIS_MODULE,
	.read = mydriver_read,
	.write = mydriver_write,
	.open = mydriver_open,
	.release = mydriver_release,
};

static int mydriver_open(struct inode *inode, struct file *file)
{
	pr_info("Deviced file was opend.\n");
	return 0;

}

static int mydriver_release(struct inode *inode, struct file *file)
{
	pr_info("Deviced file was closed.\n");
	return 0;
}


static int mydriver_read(struct file *file,
		char *buf, size_t len, loff_t *off)
{
	struct my_device_data *my_data;
	size_t datalen, read_len;
	my_data = (struct my_device_data*)&my_data_global;
	datalen = strlen(my_data->my_string);
	//\ucd5c\ub300\uac12\uc73c\ub85c \uac15\uc81c \uc124\uc815
	if(len > datalen)
	{
		len = datalen;
	}
	read_len = len - *off;
	if (read_len <= 0)
		return 0;
	pr_info("start %p, offset is %lld, read_len is %d\n", my_data->my_string, *off, read_len);
	if(copy_to_user(buf, my_data->my_string+*off, read_len))
		return -EFAULT;
	//read_len = datalen-*off;
	*off += read_len;
	pr_info("kernel has %d, read %d characters from kernel\n", datalen, read_len);
	//zero\ub97c \ubc18\ud658\ud560 \ub54c\uae4c\uc9c0 \ubc18\ubcf5.
	return read_len;
}

static int mydriver_write(struct file *flip,
		const char *buf, size_t len, loff_t *off)
{
	struct my_device_data *my_data;
	size_t datalen, write_len, start_pos;
	my_data = &my_data_global;
	start_pos = strlen(my_data->my_string);
	datalen = strlen(my_data->my_string);
	if(len > datalen)
	{
		len = DEVICE_DATA_MAX;
	}

	write_len = len - *off;

	if(copy_from_user(my_data->my_string + start_pos + *off, buf, write_len))
		return -EFAULT;
	*off += write_len;
	return write_len;
}

dev_t dev = 0;
static struct cdev my_cdev;
static struct class *dev_class;

static int __init init_hw(void)
{
	//\ub514\ubc14\uc774\uc2a4 \ub4f1\ub85d
	if(( alloc_chrdev_region(&dev, 0, 1, "test_device") < 0))
	{
		pr_err("[!]character device was not allocated\n");
		goto r_unreg;

	}
	pr_info("[=]%d-%d, was allocated\n", MAJOR(dev), MINOR(dev));


	//\ucd08\uae30\ud654
	cdev_init(&my_cdev, &fops);
	pr_info("[=]driver was initialized\n");


	//\uc2dc\uc2a4\ud15c\uc5d0 \ucd94\uac00
	if((cdev_add(&my_cdev, dev, 1)) < 0)
	{
		pr_err("[!]cannot add device to kernel\n");
		goto r_del;

	}


	//class \ub9cc\ub4e6.
	if((dev_class=class_create(THIS_MODULE, "my_class")) == NULL)
	{
		pr_err("[!]cannot add class\n");
		goto r_class;
	}


	if((device_create(dev_class, NULL, dev, NULL, "my_device")) == NULL)
	{

		pr_err("[!]cannot create device\n");
		goto r_device;
	}

	//gpio 10\ubc88\uc744 \uc0ac\uc6a9.
	//export\ud558\uc5ec \uac04\ub2e8\ud788 \uc0ac\uc6a9.
	//\uc785\ub825\uc740 \uac12\uc744 \uc368 \ub123\uc744 \uc218 \uc5c6\uc74c. \ucd9c\ub825\uc73c\ub85c \uc124\uc815.
	GPIO_irqNumber = gpio_to_irq(GPIO_10_OUT);
	pr_info("[=]irq %d was assinged\n",GPIO_irqNumber);

	//interrupt \ub4f1\ub85d \ud544\uc694
	if (request_irq(GPIO_irqNumber,
				(void*)gpio_irq_handler,
				IRQF_TRIGGER_RISING,
				"my_device",
				NULL))
	{
		pr_err("[!]my_device: cannot register IRQ\n");
		goto r_gpio;
	}
	pr_info("[=]module was installed\n");
	return 0;
r_gpio:
	gpio_free(GPIO_10_OUT);
r_device:
	device_destroy(dev_class,dev);

r_class:
	class_destroy(dev_class);
r_del:
	cdev_del(&my_cdev);
r_unreg:
	unregister_chrdev_region(dev,1);

	return -1;
}

static void __exit exit_hw(void) {
	free_irq(GPIO_irqNumber, NULL);
	gpio_free(GPIO_10_OUT);
	device_destroy(dev_class,dev);
	//class_unregister(dev_class);
	class_destroy(dev_class);
	cdev_del(&my_cdev);
	unregister_chrdev_region(dev,1);
	printk(KERN_INFO "module was removed\n");
}


module_init(init_hw);
module_exit(exit_hw);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eunseong Park (esp-ark.com)");
MODULE_DESCRIPTION("Hello, world!");

make 후 모듈을 로딩한다. 캐릭터 디바이스가 236, 0으로 등록되었다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo insmod my_driver.ko 
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ lsmod | grep my_driver
my_driver              16384  0
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -4
[42918.717112] [=]236-0, was allocated
[42918.717123] [=]driver was initialized
[42918.717332] [=]irq 54 was assinged
[42918.717363] [=]module was installed
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ ls /dev/my_device -l
crw------- 1 root root 236, 0  7\uc6d4 14 12:45 /dev/my_device

쓰기를 위해 권한을 부여했다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo chmod 666 /dev/my_device ;ls -l /dev/my_device 
crw-rw-rw- 1 root root 236, 0  7\uc6d4 14 12:45 /dev/my_device

로딩 후 읽어보면 아무런 데이터를 읽을 수 없다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c10 /dev/my_device
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -2
[43123.405028] Deviced file was opend.
[43123.405091] Deviced file was closed.

원하는 데이터를 디바이스에 기록한다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ echo "I need more space to delete characters" > /dev/my_device 
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ echo "I need more space to delete characters" > /dev/my_device

내용을 확인한다. 최대 길이를 넘기면 잘못 읽는데 어떻게 해결할 지 모르겠다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c100 /dev/my_device 
I need more space to delete characters
I need more space to delete characters
head: '/dev/my_device를 읽는 도중 오류 발생: 주소가 잘못됨
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -10
[43264.763161] Deviced file was closed.
[43309.683908] Deviced file was opend.
[43309.683961] start 18f123ee, offset is 0, read_len is 10
[43309.683972] kernel has 78, read 10 characters from kernel
[43309.684006] Deviced file was closed.
[43316.934115] Deviced file was opend.
[43316.934165] start 18f123ee, offset is 0, read_len is 78
[43316.934183] kernel has 78, read 78 characters from kernel
[43316.934260] start 18f123ee, offset is 78, read_len is -56
[43316.934813] Deviced file was closed.

인터럽트를 설정 한 다음 발생시킨다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ sudo ./config_gpio.sh 
1
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ ./make_gpio.sh 
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ cat config_gpio.sh 
#!/bin/bash
echo "10" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio10/direction 
gpio read 10
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ cat make_gpio.sh 
#!/bin/bash
#echo "10" > /sys/class/gpio/export
#sleep(1)
#echo "out" > /sys/class/gpio/gpio10/direction 
#sleep(1)
#gpio read 10
#sleep(1)
echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
sleep 1
echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
sleep 1
echo 0 > /sys/class/gpio/gpio10/value ; echo 1 > /sys/class/gpio/gpio10/value;
pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ dmesg | tail -10
[43309.683972] kernel has 78, read 10 characters from kernel
[43309.684006] Deviced file was closed.
[43316.934115] Deviced file was opend.
[43316.934165] start 18f123ee, offset is 0, read_len is 78
[43316.934183] kernel has 78, read 78 characters from kernel
[43316.934260] start 18f123ee, offset is 78, read_len is -56
[43316.934813] Deviced file was closed.
[43694.677107] taskelet: deleted 1st character.
[43695.686488] taskelet: deleted 1st character.
[43696.693794] taskelet: deleted 1st character.

다시 읽어본다. 앞 캐릭터 3개가 지워졌다.

pi@raspberrypi:~/RaspberryDebug/my_driver_tasklet $ head -c100 /dev/my_device 
eed more space to delete characters
I need more space to delete characters
head: '/dev/my_device를 읽는 도중 오류 발생: 주소가 잘못됨

https://olegkutkov.me/2018/03/14/simple-linux-character-device-driver/

https://linux-kernel-labs.github.io/refs/heads/master/labs/device_drivers.html

https://stackoverflow.com/questions/1330284/how-might-i-learn-to-write-char-device-drivers-for-linux

https://www.oreilly.com/library/view/linux-device-drivers/0596000081/ch03s04.html

https://pr0gr4m.tistory.com/entry/Linux-Kernel-5-Character-Device-Driver

http://derekmolloy.ie/writing-a-linux-kernel-module-part-2-a-character-device/

https://stackoverflow.com/questions/12124628/endlessly-looping-when-reading-from-character-device

이 글 공유하기:

  • Tweet
발행일 2021-07-14글쓴이 이대원
카테고리 생활코딩 태그 linux, raspberry, tasklet, 임베디드 리눅스

댓글 남기기응답 취소

이 사이트는 Akismet을 사용하여 스팸을 줄입니다. 댓글 데이터가 어떻게 처리되는지 알아보세요.

글 내비게이션

이전 글

linux driver, softIrq 실습

다음 글

workqueue 실습

2025 5월
일 월 화 수 목 금 토
 123
45678910
11121314151617
18192021222324
25262728293031
4월    

최신 글

  • common mode, differential mode 2025-05-11
  • signal conditioner, 신호 처리기 2025-05-10
  • strain gage 2025-05-09
  • 칼만 필터 2025-05-01
  • positioner(I/P) 2025-04-26

카테고리

  • 산업계측제어기술사
  • 삶 자국
    • 책과 영화
    • 투자
  • 생활코딩
    • LEGO
    • ROS
    • tensorflow
  • 전기기사
  • 피아노 악보

메타

  • 로그인
  • 엔트리 피드
  • 댓글 피드
  • WordPress.org

페이지

  • 소개
  • 잔여 작업 조회
    • 작업 추가
    • 작업의 사진 조회
    • 작업 수정 페이지
  • 사진
    • GPS 입력된 사진
    • 사진 조회
  • 위치
    • 하기 휴가 방문지
    • 해외 출장

태그

android bash c docker driver FSM gps java kernel LEGO linux mysql network program opcua open62541 plc programmers python raspberry reinforcementLearning ros state space system program tensorflow transfer function 경제 미국 민수 삼국지 세계사 실기 에너지 역사 유전자 일본 임베디드 리눅스 전기기사 조선 중국 채윤 코딩 테스트 통계 한국사 한국어

팔로우하세요

  • Facebook
now0930 일지
WordPress로 제작.