«   2025/09   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
Archives
관리 메뉴

printf("ho_tari\n");

9일차 본문

2025.06.23

 

ctags

$ sudo apt-get install universal-ctags
$ sudo apt-get install cscope

#linux 폴더에서 실행

ctags -R
cscope -R
$ vi ~/.vimrc   로 .vimrc 파일을 연다.
set hlsearch
set autoindent
set cindent
set nu
set tabstop=4 
set shiftwidth=4
set title
if has("syntax")
    syntax on
endif


if filereadable("./tags")
    set tags=./tags
elseif filereadable("../tags")
    set tags=../tags
elseif filereadable("../../tags")
    set tags=../../tags
elseif filereadable("../../../tags")
    set tags=../../../tags
elseif filereadable("../../../../tags")
    set tags=../../../../tags
else
    set tags=/home/ubuntu/project/linux/tags
endif

set tags=/home/ubuntu/project/linux/tags

set csprg=/usr/bin/cscope
"set csto=0
"set cst
set nocsverb
if filereadable("./cscope.out")
    cs add ./cscope.out
elseif filereadable("../cscope.out")
    cs add ../cscope.out
elseif filereadable("../../cscope.out")
    cs add ../../cscope.out
elseif filereadable("../../../cscope.out")
    cs add ../../../cscope.out
elseif filereadable("../../../../cscope.out")
    cs add ../../../../cscope.out
else
    cs add /home/pi/project/linux/cscope.out
endif
set csverb

 

정의로 가기

Ctrl-] 

Ctrl-t

gd => go definition

Ctrl+o to go back; Ctrl+i to go forward

 

search 현재 커서있는 단어

g + *

n : next 찾기
shift + n : previous
Ctrl+o to go back; Ctrl+i to go forward

 

line number로 이동

50 + shift + g

 

word로 이동

7 + w

 

  • [cscope 사용법]
    1. 소스 디렉토리에서 ctags 생성
    분석하려는 소스들의 루트 디렉토리에서 생성
    $ ctags -R
    
    • -R 이라는 옵션이 하위디렉토리까지 생각해서 만든다는거다.
    • 만약에 ctags * 라고 명령어를 실행하면, 현재 디렉토리 기준으로만 ctag가 생성된다.
    1. 소스 디렉토리에서 cscope.out 과 cscope.files 를 생성
    즉, 내가 분석하려는 소스들의 루트 디렉토리에서 생성
    $ cscope -R
    
    ctrl + d 눌러서 빠져 나온다.
  • ex) 만약에 test/ 즉, test라는 폴더 안의 소스를 전부 분석하고 싶으면 cd test 로 test 폴더 들어가서 만들면 되겠지.
  • ex) 만약에 test/ 즉, test라는 폴더 안의 소스를 전부 분석하고 싶으면 cd test 로 test 폴더 들어가서 만들면 되겠지.

 

핸들러 함수 VS 콜백 함수

구분 핸들러 함수 (Handler Function) 콜백 함수 (Callback Function)
정의 특정 이벤트나 신호, 인터럽트 등이 발생했을 때 이를 처리하기 위해 등록된 함수 다른 함수에 인자로 전달되어, 특정 조건이나 이벤트 발생 시 호출되는 함수
주요 용도 주로 이벤트, 인터럽트, 신호, 사용자 입력 등을 처리하는 데 사용 비동기 처리, 이벤트 처리, 작업 완료 시 추가 동작 등 다양한 상황에서 사용
등록 방식 시스템이나 프레임워크에 핸들러로 명시적으로 등록됨 (예: 인터럽트 핸들러, 이벤트 핸들러) 함수의 인자로 직접 전달되거나, 내부적으로 등록되어 호출됨
실행 시점 해당 이벤트나 신호가 발생할 때 시스템이 자동으로 호출 해당 함수(예: 비동기 함수)의 작업이 끝나거나, 특정 조건이 만족될 때 호출
예시 인터럽트 핸들러, 이벤트 핸들러, 신호 핸들러 setTimeout, 비동기 파일 읽기, Promise.then, 이벤트 리스너 등

 

 

추가 설명

  • 핸들러 함수는 “어떤 이벤트나 신호가 발생했을 때 그에 대응해 처리하는 역할”을 하며, 운영체제나 프레임워크에 명시적으로 등록되어 동작합니다. 예를 들어, 인터럽트 핸들러는 하드웨어 인터럽트가 발생할 때 운영체제가 호출하는 함수입니다
  • 콜백 함수는 “다른 함수에 인자로 전달되어, 그 함수의 작업이 끝난 후에 호출되는 함수”입니다. 콜백은 비동기 처리, 이벤트 처리 등에서 널리 사용되며, 함수의 제어 흐름을 유연하게 만들 수 있습니다
  • 관계: 콜백 함수가 핸들러로 등록되어 사용될 수도 있습니다. 예를 들어, 이벤트 핸들러를 콜백 함수로 등록하는 경우가 많습니다

요약

  • 핸들러 함수: 이벤트나 신호가 발생할 때 시스템이 자동으로 호출하도록 등록된 함수
  • 콜백 함수: 다른 함수에 인자로 전달되어, 특정 조건이나 이벤트가 발생할 때 호출되는 함수

둘은 비슷해 보이지만, 핸들러는 “등록”과 “자동 호출”에 초점이 있고, 콜백은 “함수 전달”과 “유연한 실행”에 초점이 있습니다.

 

1. list_add() / list_add_tail()

void list_add(struct list_head *new, struct list_head *head);

void list_add_tail(struct list_head *new, struct list_head *head);

• 첫 번째 인자: new → 새로 리스트에 넣을 노드

• 두 번째 인자: head → 어디 근처에 넣을지를 결정하는 기준

즉, 이 함수는 new를 어디에 “넣을지” 결정하기 때문에, new가 주체입니다.

2. list_del() / list_move()

void list_del(struct list_head *entry);

void list_move(struct list_head *entry, struct list_head *head);

• 첫 번째 인자: entry → 이미 리스트 안에 존재하는 노드 (작업의 대상)

• list_del(entry) → entry를 삭제

• list_move(entry, head) → entry를 head 바로 뒤로 이동

이 함수들은 리스트에 이미 들어 있는 노드를 조작하는 것이기 때문에, 그 노드가 첫 번째 인자로 오는 것이 자연스럽습니다.

 

요약 표

함수 이름 첫 번째 인자 의미 동작 주체

list_add() new 새로 추가할 노드 추가 대상 노드

list_add_tail() new 새로 추가할 노드 추가 대상 노드

list_del() entry 삭제할 노드 조작 대상 노드

list_move() entry 이동시킬 노드 조작 대상 노드

list_move_tail() entry 리스트의 끝으로 이동시킬 노드 조작 대상 노드

비유로 이해하기

• list_add(new, head)는 “new야, 너 head 뒤에 가 있어” → new가 중심

• list_del(entry)는 “entry야, 너 삭제당해” → entry가 중심

보통 이런 설계는 왜 하나요?

• 일관성 유지와 가독성 때문입니다.

• 보통 어떤 함수가 무엇을 조작할지에 따라 그 “주체”를 첫 번째 인자로 넣는 것이 일반적입니다.

 

bh1750.c

/* bh1750_chrdrv.c */

#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#define BH1750_ADDR      0x23
#define CMD_POWER_ON     0x01
#define CMD_RESET        0x07
#define CMD_CONT_HIGHRES 0x10

#define DEVICE_NAME      "bh1750"
#define CLASS_NAME       "sensor"

struct bh1750_data {
    struct i2c_client *client;
    struct cdev cdev;
};

static dev_t dev_number;
static struct class *bh1750_class;

static int bh1750_read_lux(struct i2c_client *client, char *buf)
{
    u8 data[2];
    int ret;
    u16 raw;
    u32 lux_x100;

    /* 전원 ON, 리셋, 연속 고해상도 모드 */
    i2c_smbus_write_byte(client, CMD_POWER_ON);
    i2c_smbus_write_byte(client, CMD_RESET);
    i2c_smbus_write_byte(client, CMD_CONT_HIGHRES);
    msleep(180);

    ret = i2c_smbus_read_i2c_block_data(client,
                                        CMD_CONT_HIGHRES,
                                        2, data);
    if (ret < 0)
        return ret;

    raw = (data[0] << 8) | data[1];
    lux_x100 = (raw * 1000U + 6) / 12;

    return sprintf(buf, "%u.%02u\n",
                   lux_x100 / 100,
                   lux_x100 % 100);
}

static ssize_t bh1750_char_read(struct file *filp,
                                char __user *user_buf,
                                size_t count,
                                loff_t *ppos)
{
    struct bh1750_data *d = filp->private_data;
    char tmp[16];
    int len;

    if (*ppos > 0)
        return 0; /* EOF */

    len = bh1750_read_lux(d->client, tmp);
    if (len < 0)
        return len;

    if (copy_to_user(user_buf, tmp, len))
        return -EFAULT;

    *ppos += len;
    return len;
}

static int bh1750_char_open(struct inode *inode, struct file *filp)
{
    struct bh1750_data *d = container_of(inode->i_cdev,
                                         struct bh1750_data, cdev);
    filp->private_data = d;
    return 0;
}

static const struct file_operations bh1750_fops = {
    .owner   = THIS_MODULE,
    .open    = bh1750_char_open,
    .read    = bh1750_char_read,
};

static int bh1750_probe(struct i2c_client *client)
{
    struct bh1750_data *d;
    int ret;

    /* 드라이버 데이터 할당 */
    d = devm_kzalloc(&client->dev, sizeof(*d), GFP_KERNEL);
    if (!d)
        return -ENOMEM;
    d->client = client;

    /* 캐릭터 디바이스 번호 및 클래스 생성 */
    if (!bh1750_class) {
        ret = alloc_chrdev_region(&dev_number, 0, 1, DEVICE_NAME);
        if (ret < 0)
            return ret;

        bh1750_class = class_create(CLASS_NAME);
        if (IS_ERR(bh1750_class)) {
            unregister_chrdev_region(dev_number, 1);
            return PTR_ERR(bh1750_class);
        }
    }

    /* cdev 초기화 및 등록 */
    cdev_init(&d->cdev, &bh1750_fops);
    d->cdev.owner = THIS_MODULE;
    ret = cdev_add(&d->cdev, dev_number, 1);
    if (ret) {
        class_destroy(bh1750_class);
        unregister_chrdev_region(dev_number, 1);
        return ret;
    }

    /* /dev 노드 생성 */
    device_create(bh1750_class, NULL, dev_number, NULL, DEVICE_NAME);

    i2c_set_clientdata(client, d);
    dev_info(&client->dev, "%s: registered char device /dev/%s\n",
             DEVICE_NAME, DEVICE_NAME);
    return 0;
}

static void bh1750_remove(struct i2c_client *client)
{
    struct bh1750_data *d = i2c_get_clientdata(client);

    device_destroy(bh1750_class, dev_number);
    cdev_del(&d->cdev);
    class_destroy(bh1750_class);
    unregister_chrdev_region(dev_number, 1);
}

static const struct i2c_device_id bh1750_id[] = {
    { "bh1750", 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, bh1750_id);

static struct i2c_driver bh1750_driver = {
    .driver   = { .name = "bh1750" },
    .probe    = bh1750_probe,
    .remove   = bh1750_remove,
    .id_table = bh1750_id,
};

module_i2c_driver(bh1750_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("BH1750 I2C Light Sensor with /dev Interface");
MODULE_LICENSE("GPL");

bh1750_read.c

// bh1750_read.c (using sysfs interface, continuous measurement)
// 유저스페이스에서 sysfs로 BH1750 조도 센서 값을 계속 읽어 출력

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

int main(void) {
    const char *sysfs_path = "/dev/bh1750";
    char buf[32];

    while (1) {
        FILE *fp = fopen(sysfs_path, "r");
        if (!fp) {
            perror("fopen sysfs lux");
            return EXIT_FAILURE;
        }

        if (fgets(buf, sizeof(buf), fp) != NULL) {
            size_t len = strlen(buf);
            if (len > 0 && buf[len-1] == '\n')
                buf[len-1] = '\0';
            printf("Ambient Light: %s lux\n", buf);
        } else {
            fprintf(stderr, "Failed to read lux value from %s\n", sysfs_path);
            fclose(fp);
            return EXIT_FAILURE;
        }

        fclose(fp);
        sleep(1); // 1초 간격으로 측정
    }

    return EXIT_SUCCESS;
}

1. 모듈 등록 및 메타 정보

module_i2c_driver(bh1750_driver);

MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("BH1750 I2C Light Sensor with /dev Interface");
MODULE_LICENSE("GPL");
  • 일반 원리
    • module_init/module_exit 대신 버스 전용 매크로(module_i2c_driver) 사용
    • MODULE_LICENSE("GPL") 등으로 라이선스·저자·설명 기입
  • bh1750_chrdrv.c
    • I²C 버스에 붙는 bh1750_driver를 등록
    • 모듈이 로드될 때 bh1750_probe 호출, 언로드될 때 자동으로 bh1750_remove

2. I²C 드라이버 구조체

static const struct i2c_device_id bh1750_id[] = {
    { "bh1750", 0 },
    { }
};

static struct i2c_driver bh1750_driver = {
    .driver   = { .name = "bh1750" },
    .probe    = bh1750_probe,
    .remove   = bh1750_remove,
    .id_table = bh1750_id,
};
  • 일반 원리
    • .name / .id_table로 디바이스 매칭
    • .probe() / .remove() 콜백 등록
  • 적용 예
    • 디바이스 트리나 i2c_board_info에서 "bh1750"가 있으면 bh1750_probe 실행

3. 드라이버 데이터 저장

struct bh1750_data {
    struct i2c_client *client;
    struct cdev       cdev;
};
  • 일반 원리
    • 디바이스별 컨텍스트(struct)에 상태·핸들 저장
    • devm_kzalloc() / i2c_set_clientdata() 로 관리
  • 코드 내역
    • client 포인터 → I²C 전송에 사용
    • cdev 구조체 → 캐릭터 디바이스 등록에 사용

4. 캐릭터 디바이스 등록

/* 1) dev 번호 할당 & class 생성 */
ret = alloc_chrdev_region(&dev_number, 0, 1, DEVICE_NAME);
bh1750_class = class_create(CLASS_NAME);

/* 2) cdev 초기화 & 등록 */
cdev_init(&d->cdev, &bh1750_fops);
cdev_add(&d->cdev, dev_number, 1);

/* 3) /dev 노드 생성 */
device_create(bh1750_class, NULL, dev_number, NULL, DEVICE_NAME);
  • 일반 원리
    1. alloc_chrdev_region()
    2. cdev_init() + cdev_add()
    3. class_create() + device_create() → /dev/... 자동 생성
  • 주의 사항
    • class_create() 시점에 THIS_MODULE 인자는 최신 API에서 제거됨
    • 성공 시 major/minor 번호가 dev_number에 채워짐

5. 파일 연산자(File Operations)

static const struct file_operations bh1750_fops = {
    .owner = THIS_MODULE,
    .open  = bh1750_char_open,
    .read  = bh1750_char_read,
};
  • 일반 원리
    • open, read, write, ioctl 등 함수 포인터 테이블
    • 사용자 공간의 open("/dev/bh1750") → bh1750_char_open 호출
  • bh1750_chrdrv.c
    • open에서 private_data에 bh1750_data 구조체 연결
    • read에서 I²C 센서값을 읽어 버퍼에 복사

6. I²C 통신: SMBus API 사용

i2c_smbus_write_byte(client, CMD_POWER_ON);
i2c_smbus_write_byte(client, CMD_RESET);
i2c_smbus_write_byte(client, CMD_CONT_HIGHRES);
msleep(180);
i2c_smbus_read_i2c_block_data(client, CMD_CONT_HIGHRES, 2, data);
  • 일반 원리
    • SMBus API로 간단한 바이트 송수신 또는 블록 전송
  • 포인트
    • 센서에 명령 전송 → 측정 모드 설정
    • msleep() 사용으로 커널 스케줄러에 제어 양보

7. Probe & Remove

static int bh1750_probe(struct i2c_client *client) { … }
static void bh1750_remove(struct i2c_client *client) { … }
  • probe() 주요 작업
    1. devm_kzalloc()으로 driver data 할당
    2. char device 등록 (위 4번)
    3. i2c_set_clientdata() 연결
  • remove() 주요 작업
    1. device_destroy(), cdev_del()
    2. class_destroy(), unregister_chrdev_region()

8. 데이터 흐름 요약

  1. 모듈 로드 → I²C driver 등록
  2. 디바이스 바인딩 → bh1750_probe()
  3. 유저 공간: open("/dev/bh1750") → read()
  4. 커널: SMBus 통해 센서 읽고, 포맷팅 후 user buffer에 복사
  5. 모듈 언로드 → bh1750_remove()

9. 확장 학습 포인트

  • 동기 vs 비동기 I/O: read()가 블로킹/논블로킹 지원해 보기
  • IOCTL 추가: 측정 모드 변경, 타이밍 조절 기능 구현
  • Device Tree: .of_match_table 추가하여 DT 바인딩
  • 전력 관리: pm_runtime_* API로 센서 전원 관리

 

I²C 주소 스캔 결과에서

  • 23: 0x23 주소의 디바이스가 응답했다는 의미입니다.
    • i2cdetect가 “ping” 방식으로 해당 주소에 읽기 시도를 해서 ACK(응답)를 받으면 16진수 주소를 표시합니다.
  • UU: 그 주소(예: 0x23)에 이미 커널 드라이버가 바인딩(bind)되어 있어서, 유저 공간에서 직접 probing(읽기/쓰기)하지 않는다는 뜻입니다.
    • 이미 /sys/bus/i2c/devices/…/driver 아래에 해당 디바이스가 등록되어 있으면, 안전을 위해 i2cdetect는 “사용 중(Used)” 표시로 바꿔버립니다.

요약

23 장치가 응답함 아직 커널 드라이버 미등록 상태
UU 드라이버가 바인딩됨 드라이버에서 이미 사용 중이므로 스캔하지 않음

 

'(Telechips) AI 시스템 반도체 SW 개발자 교육 > SoC 시스템 반도체를 위한 임베디드 리눅스' 카테고리의 다른 글

2일차  (0) 2025.06.27
10일차  (0) 2025.06.24
8일차  (0) 2025.06.23
7일차  (0) 2025.06.20
6일차  (0) 2025.06.18