'문제'에 해당되는 글 2건

  1. 2009.05.30 Serial Device Driver 작성시 주의사항?
  2. 2007.01.15 멀티 도스에서의 문제점 기술 2



너무나 돌아온 느낌이다. 시리얼 통신으로 터치스크린 이벤트 좌표값을 가져오기 위해서 구현하는 것인데... 원래는 USB로 할 예정이지만, 그 전에 시리얼로 한번 해보는 것이다. 특별한 설정 없이도 되는 줄 알고.. 하지만, 시리얼 디바이스 드라이버 구현보다는, 연결에 어려움이 꽤 있었다.

우선 kernel의 driver/input/touchscreen/mtouch.c 파일의 내용을 수정해서 새로운 uv_mtd 장치에 대한 시리얼 디바이스 드라이버를 작성하였다.

static int __init mtouch_init(void)
{
        return serio_register_driver(&mtouch_drv);
}

static void __exit mtouch_exit(void)
{
        serio_unregister_driver(&mtouch_drv);
}

module_init(mtouch_init);
module_exit(mtouch_exit);




위 소스는 모듈의 등록과 해제를 담당하는 루틴으로, 보면 insmod 해당모듈.ko를 하게 되면, 그냥 serio_register_driver이 호출되고, 드라이버가 새로이 등록되버린 상태로 가만히 있게 된다.

static irqreturn_t mtouch_interrupt(struct serio *serio,
                unsigned char data, unsigned int flags)
{
        struct mtouch* mtouch = serio_get_drvdata(serio);

        mtouch->data[mtouch->idx] = data;

        if (MTOUCH_FORMAT_TABLET_STATUS_BIT & mtouch->data[0])
                mtouch_process_format_tablet(mtouch);
        else if (MTOUCH_RESPONSE_BEGIN_BYTE == mtouch->data[0])
                mtouch_process_response(mtouch);
        else
                printk(KERN_DEBUG "mtouch.c: unknown/unsynchronized data from device, byte %x\n",mtouch->data[0]);

        return IRQ_HANDLED;
}



위 루틴은 해당 드라이버의 인터럽트 처리 부분이다. 즉, 시리얼 포트(ttyS0)를 통해 데이터가 들어오면, 위 루틴에서 처리를 담당하는 것이다. 이 말은, 데이터가 들어오게 되면 무조건 인터럽트가 걸리게 된다는 것으로, 이 함수에 들어와야 정상이다.. 하지만, 단순히 모듈을 적재하는 것으로 인터럽트를 연결하진 못했다. 이 전에 인터럽트 자체가 등록이 되지 않은 상황이었다. ㅡ.ㅜ

static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
{
        struct mtouch *mtouch;
        struct input_dev *input_dev;
        int err;

        mtouch = kzalloc(sizeof(struct mtouch), GFP_KERNEL);
        input_dev = input_allocate_device();
        if (!mtouch || !input_dev) {
                err = -ENOMEM;
                goto fail1;
        }

        mtouch->serio = serio;
        mtouch->dev = input_dev;
        snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);

        input_dev->name = "MicroTouch Serial TouchScreen";
        input_dev->phys = mtouch->phys;
        input_dev->id.bustype = BUS_RS232;
        input_dev->id.vendor = SERIO_MICROTOUCH;
        input_dev->id.product = 0;
        input_dev->id.version = 0x0100;
        input_dev->dev.parent = &serio->dev;
        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
        input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
        input_set_abs_params(mtouch->dev, ABS_X, MTOUCH_MIN_XC, MTOUCH_MAX_XC, 0, 0);
        input_set_abs_params(mtouch->dev, ABS_Y, MTOUCH_MIN_YC, MTOUCH_MAX_YC, 0, 0);

        serio_set_drvdata(serio, mtouch);

        err = serio_open(serio, drv);
        if (err)
                goto fail2;

        err = input_register_device(mtouch->dev);
        if (err)
                goto fail3;

        return 0;

 fail3: serio_close(serio);
 fail2: serio_set_drvdata(serio, NULL);
 fail1: input_free_device(input_dev);
        kfree(mtouch);
        return err;
}



우선 인터럽트를 등록하는 위 connect 함수가 호출되어야 한다. 하지만, 이 함수 역시 모듈 적재만으론 되지 않았다. 솔직히 이 문제를 해결하기 위해 약 3일간 밤낮없이 고생한 것 같다. 구글에 검색을 해봐도 이 문제에 봉착한 경우만 있지, 해결 방법에 대해선 별다른 말이 없었기 때문이다. 그 과정에서 알게 된 것이 setserial과 inputattach 프로그램 뿐이었고, 이것에 대한 자세한 설명도 없는 상황이었다.

/*
 * The serio driver structure.
 */

static struct serio_device_id mtouch_serio_ids[] = {
        {
                .type   = SERIO_RS232,
                .proto  = SERIO_MICROTOUCH,
                .id     = SERIO_ANY,
                .extra  = SERIO_ANY,
        },
        { 0 }
};

MODULE_DEVICE_TABLE(serio, mtouch_serio_ids);

static struct serio_driver mtouch_drv = {
        .driver         = {
                .name   = "mtouch",
        },
        .description    = DRIVER_DESC,
        .id_table       = mtouch_serio_ids,
        .interrupt      = mtouch_interrupt,
        .connect        = mtouch_connect,
        .disconnect     = mtouch_disconnect,
};



위 소스와 같이 mtouch_drv는 mtouch_init 함수에 의해서 시리얼 디바이스 드라이버로 등록이 되지만, 실제로 .connect로 정의된 mtouch_connect 함수는 호출이 되지 않는 문제...

결국은, 구글 검색의 힘(해커들의 질의응답... 검색어는 "lagacy serial driver .connect")으로 해결할 수 있었다.

- 참고사이트1 : http://kerneltrap.org/mailarchive/linux-kernel-mentors/2007/12/5/477437
- 참고사이트2 : http://lkml.org/lkml/2007/6/19/254 - Writing a driver for a legacy serial device
- 참고사이트3 : http://lkml.indiana.edu/hypermail/linux/kernel/0706.2/1755.html

The problem is that taos_connect is never called. I suppose that I need
different values for .type, .proto or .id, except that I just don't
know what to put there. I tried a few random values without success.
What's the trick?


참고로 위 질문에서 나의 문제와 똑같다는 것을 느끼게 되었다. 이 never called라는 메시지가 정말 반가웠다 ㅡ.ㅜ
해당 글의 쓰레드를 조금 추적해서 들어가면 inputattach.c 소스를 구할 수 있고(http://linuxconsole.cvs.sourceforge.net/viewvc/*checkout*/linuxconsole/ruby/utils/inputattach.c), 이 프로그램에 새로운 시리얼 디바이스의 속성을 지정하고, 실행하면 해당 시리얼 디바이스 드라이버와 연결된다는 것을 알게 되었다.
inputattach를 실제 실행하면, 키보드 입력을 받게 되어 있어서 어떻게 사용하는지 조금 난해했었다.

kernel/Documentation/input/joystick.txt 문서에서 inputattach -xxx /dev/tts/X & 를 보고, 이렇게 하니 잘 되었다;;

May 30 09:55:29 mola_server uv_mtd_init
May 30 09:55:59 mola_server serio: Serial port ttyS0
May 30 09:55:59 mola_server uv_mtd_connect
May 30 09:55:59 mola_server uv_mtd ttyS0/serio0/input0
May 30 09:55:59 mola_server input: UV Serial Multi-TouchScreen as /class/input/input13
May 30 09:56:12 mola_server uv_mtd_interrupt
May 30 09:56:12 mola_server uv_mtd unknown/unsynchronized data from device, byte 53
May 30 09:56:12 mola_server uv_mtd_interrupt
May 30 09:56:12 mola_server uv_mtd unknown/unsynchronized data from device, byte 63
May 30 09:56:12 mola_server uv_mtd_interrupt
May 30 09:56:12 mola_server uv_mtd unknown/unsynchronized data from device, byte 72



위 메시지는 커널의 메시지이며, insmod uv_mtd.ko를 하고 ./inputattach --uv_mtd /dev/ttyS0 & 를 한 후, 시리얼을 통해 데이터를 보낸 상태이다. 아직 인터럽트에서 제대로 처리를 하지 않아 위 메시지가 나왔지만, 인터럽트 내에 제대로 진입한 것을 볼 수 있다.;;

p.s.#1 이거 안되면 드라이버단에서 sys_open( /dev/ttyS0 )을 해서 처리하려고 했었다..
         장치에서 값이 들어오든 안들어오든 무조건 blocking된 상태에서 말이다.;;; 그래도 다행이다. 드라이버 단에서 되서~
p.s.#2 앞으로는 이벤트 드라이버와 연결해서 시리얼에서 온 데이터를 이벤트화 하면 된다.
         (이벤트 드라이버는 이미 테스트 종료 상태 ^^;;)

Posted by wbhacker :

내가 만드는 프로그램에 문제점이 있다.
이 프로그램의 목적은 도스에서도 여러개의 화면 전환이 이루어지게끔 하는 것이다. 프로그램이 수행되지 않는 상황에서 화면 전환은 쉽게 이루어진다. 하지만, 프로그램이 이미 수행중에 있는 상태에서 화면 전환을 시도하면 어떻게 되는가?
간단히 계속 Blocking 되고 있는 것이다. 입력을 해도, 이전에 수행되고 있는 프로그램의 입력으로 쏙 들어가 버린다. 이것은 리눅스의 기본 Console이나 screen 명령에서 지원되는 수준에 훨씬 못 미치는 수준으로 밖에 안보인다. 스크립트 수준...

그래서, 이 프로그램은 태스크 전환을 시도하며, 그것을 위해서 두개의 태스크를 생성한다. Task1과 Task2가 있는데, 간단히 말하면, Task1은 화면에 무한적으로 '2'라는 값을 출력하는 것이고, Task2는 입력되는 문자를 화면에 바로 뿌려주며, 그에 맞춰서 ASCII 코드를 0부터 차례대로 출력을 한다.

본 프로그램을 수행하면 Task1을 수행하게 된다. 그리고 F2키를 누르면, Task2로 전환이 이루어지는데, 화면과 프로그램이 동시에 전환이 되는 것이다.
이 상황에서 F1키를 눌러, Task1로 전환을 시도하면 잘 변환이 된다. 그리고 다시 F2키를 눌러, Task2로 전환을 시도하면 되지 않는다.

이것의 문제점을 파악하기 위해서 SoftIce를 이용하였다. F1이나 F2키에 의해서 수행되는 소스 위치에 break point를 지정하여 간단히 살펴볼 수 있었다.

결과는 내가 임의로 지정한 스택의 공간을 사용하지 않는 데서 따른 문제였다. 하지만 ISR을 수행하게 될 때는, 인터럽트가 발생되는 시점에 수행중인 프로그램의 CS:IP를 현재 사용중인 SS:SP에 저장을 하는 것이라서, 예측이 불가능한 상황에 ISR로 들어가는 것이 된다.

다시 처음부터 코드 레벨로 설명을 하면,

Task1는 CS=2A8E인 공간에 있고, Task2는 CS=4000인 공간에 할당되어 있다. Task1이 수행되는 상황에서 F2키에 의해서 ISR로 들어가게 되는데, 이 때는 SS=2A8E이기 때문에, Task1이 위치한 공간에 CS:IP(복귀주소)가 저장된다. 그리곤 Task2로 스위칭이 되는데, 이 때는 Task2를 생성하면서 미리 저장해놓은 스택공간(SS=4000)을 사용하여 pop이 이뤄지기 때문에, 복귀주소인 CS:IP도 SS=4000에 위치하게 된다. 결국, SS=4000, CS=4000 인 공간으로 전환이 이루어져 Task2는 수행되게 되는 것이다.

이젠 CPU는 Task2를 수행하고 있으며, CS=4000인 공간에 있다. 이 상황에서 Task1로 가기 위해서는 F1키를 눌러서 ISR로 들어가게 되는데, 인터럽트 발생되는 시점이 코드 수행 시점인데, CS가 4000이 아니라, 00C9가 되어 있다. 어쩔 수 없이 SS=00C9가 되며, 따라서 그 공간에 복귀주소인 CS:IP가 저장되게 된다. 결국 Task2가 ISR로 들어올 때는 CS=00C9이며, Task1로 전환된 후에, 다시 Task2로 전환되려고 할 때, 이 공간 CS=00C9의 스택공간 SS=00C9를 사용하게 된다. 근데, 문제는 주소가 아니라, 그 안에 내용이었다.

바껴서는 안될 스택 공간 내용이 어느새 바뀌어져 있는 것이다. 이 결과는 당연히 메모리 접근 문제로 발생될 수 밖에 없는 것이다.

직접 노트에 기록한 것을 보면 다음과 같다.

Task1에서 F2키에 의해서 Task2로 전환이 된 후,
F1키 눌렀을 때 Break Point에 의해서 멈춤
( CS:IP = 11CD:0397 [_my_proc 처음위치] )
이 때의 스택값
( SS:SP = 00C9:0A7A [내가 지정한 위치가 아님] )
---------------------------------------------------------
          00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F
00C9:0A70 .......................... C9 00 8F 84 97 2E
00C9:0A80 C8 FC 02 32 F7 2F B2 FC-01 01 02 01 F0 FF 00 40
00C9:0A90 B5 60 00 ......................................
---------------------------------------------------------
아무튼, 그래도 Task1로 전환이 되었다. 이것은 복귀값이 Task1의 스택공간을 사용했기 때문에, 문제가 없는 것이다.
Task2로의 전환을 위해,
F2키를 눌렀을 때 Break Point에 의해서 멈춤
 ( CS:IP = 11CD:0397 [_my_proc 처음위치] )
 이 때의 스택값 또한 내가 예상한 값들로 채워져 있었다.
 ( SS:SP = 2A8E:FFEE [내가 지정한 위치임] )
 TASK_SW 소스 수행....
 Task sw 준비
  ( SS:SP = 11CD:0260 [예상한 위치임] )
  pushf
  pusha
  .....
  ( SS:SP = 11CD:0244 [예상한 위치임] )
 SS:SP 교체로 Task sw 유도함
  ( saved_sp1 <- 11CD:0244
    SS:SP <- saved_sp2 (11CD:0344) )
  .....
  popa
  popf
  ( SS:SP = 11CD:0360 [예상한 위치임] )
 ISR 종료를 위해 코드수행 복귀주소의 반환을 위해서
 SS과 SP를 설정하는데, 이것은 저장되어 있는 SS:SP값으로 00C9:0A74가 되었다.
  pop dx ; ffff
  pop bx ; 3816
  pop cx ; 0000
---------------------------------------------------------
          00 01 02 03 04 05 06 07-08 09 0A 0B 0C 0D 0E 0F
00C9:0A70 00 00 63 06 FF FF 16 38-00 00 00 00 12 09 00 40
00C9:0A80 0D 01 32 02 4E 60 42 FF-46 32 C9 00 00 00 00 61
---------------------------------------------------------

스택값은 어느새 이렇게 바껴있었다. 위에 그려진 메모리와는 너무나 다르지 않은가...
나는 저 공간(00C9 Segment)을 사용하고 싶지 않다. 그렇지만, 키 입력이 들어가 있는 프로그램에서 인터럽트가 걸리면, 수행중인 프로그램이 아닌, 내부적인 코드 위치에서 바로 ISR로 들어가 버리는 것 같다. 그래서 cli나 sti 명령을 이용해서 키 입력 부분 AH=01/INT 21H에 감싸서 해봤지만, 소용이 없었다. 어차피 소프트웨어 인터럽트이기 때문인 것 같다. cli 명령에 의해서 영향을 받지 않기 때문이다. 또한 내부적으로 sti 해버리면 소용이 없지 않은가...

저 공간을 사용하지 않고 바로 Task1에서와 같이 전환이 된다면 문제가 없을텐데... 실제로 Task1과 비슷한 Task2를 수행했을 때에는 태스크 전환이 잘 이루어졌다.

http://asmlove.co.kr/zBdC7/viewtopic.php?t=1326 에도 질문을 방금 올렸었는데, 아직 답변이 없다. 결국 해결이 안될까?

Posted by wbhacker :