signal02

ここから転載

QNX4でハードウェアアクセスを行うドライバープログラムを作成する際に必要となるポートアクセスや割り込みハンドリングなど各種Tipsについて説明します。

  • リンクオプション
  • プロセス間通信
  • ポートアクセス
  • 物理メモリーアクセス

  • 割り込みハンドリング
  • 共有メモリーアクセス
  • PCIコンフィギュレーション情報アクセス

    コード例ではエラーについては扱いませんのが実際のプログラムでは必ずエラーチェックを入れてください。

    QNX4のオンラインマニュアルには関連する関数やコード例がいろいろあります。必ず目を通してください。



    リンクオプション



    ポートアクセスや割り込みの禁止/解除を行うコードを作成する場合は、プログラムをリンクする時にプリビレッジオプションが必要となります。また、実行する際にはrootユーザで行う必要があります。

    ccオプション -T



    test.cをコンパイルしてtestにリンクする例

    cc -o test -T 1 test.c

    実行ユーザ



    root





    プロセス間通信



    ドライバーはプロセスとして実現されるため、そのコントロールとしてプロセス同士のやり取りが必要となります。プロセス同士のやりとりを実現するプロセス間通信には次の3つがあります。


  • メッセージ通信 (Send()/Receive()/Reply())
  • シグナル
  • 共有メモリー


    メッセージ通信はQNX4の基本のプロセス間通信となります。2つのプロセス間のタイミングでデータの引き渡しが簡単に行えます。

    シグナルは他のプロセスのハンドラーを起動するだけで十分な場合に使用します。この動作はソフトウェア的な割り込みとも言えます。

    共有メモリーはプロセスの間で多量のデータを引き渡せますが、2つのプロセス間の同期を扱いませんので注意が必要となります。利点は複数のプロセスで共有が可能な点です。利用方法については後述します。

    プロセス間通信の相手を知るためにQNX4が持つname"名前"機能を使用します。QNX4の場合、他のネットワークノード上のプロセスとのやり取りが必要な場合、名前を参照する時に自動的に通信用のバーチャルサーキットプロセスが生成されます。













    機能関数名ヘッダー
    メッセージ送信Send()sys/kernel.h
    メッセージ受信Receive()sys/kernel.h
    メッセージ返信Reply()sys/kernel.h
    名前登録qnx_name_attach()sys/name.h
    名前削除qnx_name_detach()sys/name.h
    名前参照qnx_name_locate()sys/name.h
    シグナル関数登録/削除signal()sinal.h
    シグナル発生kill()signal.h


    コード例



    メッセージ通信(送信側)

    #include <sys/name.h>
    #include <sys/kernel.h>

    pid_t pid;
    char send_buf[SEND_BUF_SIZE], reply_buf[REPLY_BUF_SIZE];

    /* 名前による送信先プロセスIDの検索(通信前に1回だけ行う) */
    pid = qnx_name_locate( 0, "/Receiver", 0, NULL );

    ...
    /* メッセージ送信 (返信データはreply_bufにセットされている) */
    Send( pid, send_buf, reply_buf, sizeof(send_buf), sizeof(reply_buf) );

    メッセージ通信(受信側)

    #include <sys/name.h>
    #include <sys/kernel.h>

    int name_id;
    pid_t pid;
    char receive_buf[SEND_BUF_SIZE], reply_buf[REPLY_BUF_SIZE];

    /* 名前の登録 (初期化時) */
    name_id = qnx_name_attach( 0, "/Receiver" );

    ...

    /* メッセージ受信 */
    pid = Receive( 0, receive_buf, sizeof(receive_buf) );

    /* メッセージ返信 (返信データをreply_bufにセット) */
    Reply( pid, reply_buf, sizeof(reply_buf) );

    ...

    /* 名前の削除 (終了時) */
    qnx_detach_attach( name_id );


    シグナル(送信側)


    #include <singal.h>

    /* シグナルの発生 */
    kill( ReceiveProcessId, SIGUSR1 );


    シグナル(受信側)


    #include <singal.h>

    /* シグナルの登録 */
    signal( SIGUSR1, signal_handler );
    ...

    void signal_handler( int sig_no )
    {
      /* シグナル受信時処理 */
    }


    (注意1) 通信相手を特定することも可能です。その場合はReceive()関数呼び出し時にプロセスIDを指定します。

    (注意2) ユーザに自由に解放されているシグナルはSIGUSR1/SIGUSR2の2つとなります。





    ポートアクセス




    ポートアクセス関数(inp/inpw/inpd/outp/outpw/outpd)を呼び出します。










    機能関数名ヘッダー
    1バイト入力inp()conio.h
    1ワード(2バイト)入力inpw()conio.h
    1ロングワード(4バイト)入力inpd()conio.h
    1バイト出力outp()conio.h
    1ワード(2バイト)出力outpw()conio.h
    1ロングワード(4バイト)出力outpd()conio.h


    コード例



    アドレス0x378のデータを入力してbit0をセットし出力する。


    #include <conio.h>

    unsigned int data;

    data = inp(0x378);
    outp(0x378,data | 0x01);






    物理メモリーアクセス




    x86の持つセグメント管理機能を用いてアクセスセグメントを作成し物理メモリーをアクセスします。





    機能関数名ヘッダー
    物理メモリーアクセスセグメント作成qnx_segment_overlay()sys/seginfo.h
    アクセスポインター作成MK_FP()i86.h


    コード例



    0xb8000から32kバイトをクリアーする。


    #include <sys/seginfo.h>
    #include <i86.h>

    unsigned mem_seg;
    char __far *p;

    mem_seg = qnx_segment_overlay( 0xb8000, 32768 );
    p = MK_FP( mem_seg, 0x0 );
    {
    int off;
    for ( off=0; off<32768; ++off ) *p++ = 0x0;
    }


    (注1) 32bit版プログラムでは他のposix環境との互換性でshm_open()/mmap()を使用することがマニュアルでは薦められていますが、セグメントを利用した方がシンプルなコードとなります。

    (注2) qnx_segment_overlay()で作成されたセグメントは開放されません。何回もqnx_segment_overlay()を呼び出さないようにプログラミングする必要があります。





    割り込みハンドリング



    CPUの割り込みを禁止/解除のコードをプログラムに埋め込むことが可能です。この場合は割り込み禁止区間を短くして割り込み応答時間が伸びないようにすることが必要です。

    割り込みが受け付けられた時に飛んでくるハンドラーは単純に関数を用いて登録します。また解除を可能です。









    機能関数名ヘッダー
    割り込み禁止_disable()i86.h
    割り込み解除_enable()i86.h
    割り込みハンドラー登録qnx_hint_attach()sys/irqinfo.h
    割り込みハンドラー解除qnx_hint_detach()sys/irqinfo.h
    割り込みマスク設定qnx_hint_mask()sys/irqinfo.h


    コード例



    irq5の割り込みを受けるハンドラーを登録/削除します。


    #include <sys/types.h>
    #include <sys/irqinfo.h>
    #include <i86.h>

    int counter = 0;

    #pragma off(check_stack)
    pid_t irq_handler()
    {
       /* 割り込み処理 */
       ++counter;
    }
    #pragma off(check_stack)

    ...
    irqNo = 5;

    /* 割り込みハンドラー登録 */
    id = qnx_hint_attach( irqNo, &handler, FP_SEG(&counter)

    /* 割り込みマスク解除*/
    qnx_hint_mask( irqNo, 1 );

    /* 割り込みハンドラー解除 */
    qnx_hint_detach( id );
    ...

    (注意1) 割り込みハンドラーにはスタックチェックコードが含まれないようにする必要があります。もしあるとスタックチェックコードの部分でアボートしてしまいます。#pragamaディレクティブを使用するか、ccでコンパイルする時に"-Wc,-s"オプションを追加します。

    (注意2) 割り込み番号を-1にした場合、50msec間隔のタイミング割り込みとして扱われます。物理割り込みと違いシステムで複数のタイミング割り込みを利用することが可能です。おもにタイムアウト等の時間制御に用います。






    共有メモリーアクセス




    x86メモリー管理関数(qnx_segment_XXXX())を利用して共有メモリーを実現することが可能です。x86の持つセグメント管理機能を物理メモリーアクセス同様、用いています。







    機能関数名ヘッダー
    メモリーセグメント作成qnx_segment_alloc()sys/seginfo.h
    メモリーセグメント開放qnx_segment_free()sys/seginfo.h
    メモリーセグメント受け渡し条件設定qnx_segment_arm()sys/seginfo.h
    メモリーセグメントインポートqnx_segment_get()sys/seginfo.h


    共有メモリーを生成し、引き渡し、インポート、アクセスする場合、上記の3つの関数を使用します。

    共有メモリーの生成、引き渡し

  • 共有メモリー用のメモリーセグメントを作成する。 qnx_segment_alloc()
  • 他のプロセスで利用可能なようにセグメントの条件を設定する。 qnx_segment_arm()

  • プロセス間通信やプログラム起動パラメータ等を通して共有メモリーのセグメント情報を送る。


    共有メモリーのインポート、アクセス


  • 送られてきた共有メモリーのセグメントをインポートする。 qnx_segment_get()
  • アクセス用ポイントを作成して共有メモリー領域をアクセスする。

    コード例



    プロセス1 共有メモリーの生成、引き渡し


    #include <sys/seginfo.h>
    #include <i86.h>

    unsigned mem_seg;
    char __far *p;

    /* 生成 */
    mem_seg = qnx_segment_alloc( MemorySize );

    /* 他のプロセスで使用可能にする */
    qnx_segment_arm( -1, mem_seg, 0 );  /* -1はすべてのプロセスを示す */

    ...

    /* 解放 */
    qnx_segment_free( mem_seg );


    プロセス2 共有メモリーのインポート、アクセス


    #include <sys/seginfo.h>
    #include <i86.h>

    pid_t cre_pid;
    unsigned common_seg, mem_seg;
    char __far *p;

    ...
    /* cre_pid = 共有メモリーを生成したプロセスのpid */
    /* common_seg = 生成した共有メモリーのsegment値 */
    /* 2つの値はプロセス間通信か起動パラメータを通して通知します */

    mem_seg = qnx_segment_get( cre_pid, common_seg, 0 );
    p = MK_FP( mem_seg, 0x0 );

    *p = 0x00;

    (注1) 作成し共有メモリーセグメントはプログラムの終了時に解放します。 qnx_segemnt_free()

    (注2) 共有メモリーのため共有しているプロセスが全部終了した時点で解放する必要があります。

    (注3) サンプルプログラムではすべてのプロセスで使用可能にしていますが特定のプロセスにだけ共有メモリーを使用可能にすることができます。各関数の資料を確認してください。





    PCIコンフィギュレーション情報アクセス



    PCI BIOSインタフェース関数(_CA_PCI_XXXX())が用意されています。

    PCI BIOSインタフェース関数を利用してPCIデバイスに設定されているI/Oポートアドレス設定情報、メモリーアドレス設定、割り込み設定情報を読み出し、前出のポートアクセス物理メモリーアクセス割り込みハンドリングで示した方法でPCIバス上のハードウェアをアクセスします。







    機能関数名ヘッダー
    PCI BIOSチェック_CA_PCI_BIOS_Present()sys/pci.h
    PCIデバイス検索_CA_PCI_Find_Device()sys/pci.h
    PCIコンフィギュレーションデータリード_CA_PCI_Find_Read_Config_Byte/Word/DWord()sys/pci.h


    コード例



    DEC21041チップのポート設定情報、メモリー設定情報、割り込み設定情報を読み出します。


    #include <sys/pci.h>

    unsigned vendor_id, device_id;
    int flg;
    int irq_no;
    unsigned int port_adr, mem_adr;
    unsigned bus_num, devfunc_num;
    unsigned index;

    /* PCI BIOS チェック */
    {
      unsigned lastbus, version, hardware;
      flg = _CA_PCI_BIOS_Present( &lastbus, &version, &hardware );
      if ( flg != PCI_SUCCESS ) {
        /* PCI BIOS 存在せず */
      }
    }
    /* PCIデバイス検索 */
    {
      vendor_id = 0x1011;     /* Digital */
      device_id = 0x14;      /* 21041 */
      index = 0;
      flg = _CA_PCI_Find_Device( device_id, vendor_id, index, &bus_num, &devfunc_num );
      if ( flg != PCI_SUCCESS ) {
        /* PCIデバイス存在せず */
      }
    }
    /* PCIデバイスコンフィギュレーション情報リード */
    /* 割り込み設定 */
    {
      unsigned short ds;
      _CA_PCI_Read_Config_Word( bus_num, devfunc_num, 0x3c, 1, (char *)&ds )
      irq_no = (int)ds;
    }
    /* ポート設定情報 */
    {
      int off;
      unsigned long dl;
      for ( off=0; off<5; ++off ) {
        _CA_PCI_Read_Config_DWord( bus_num, devfunc_num, 0x10+off*4, 1, (char *)&dl );
        if ( dl == 0x0 || dl == 0xffffffff ) continue;
        if ( (dl & 0x03) = 0x1 ) break;  /* ポートアドレス情報 */
      }
      port_adr = (unsigned int)(dl & 0xfffffffc);
    }
    /* メモリー設定情報 */
    {
      int off;
      unsigned long dl;
      for ( off=0; off<5; ++off ) {
        _CA_PCI_Read_Config_DWord( bus_num, devfunc_num, 0x10+off*4, 1, (char *)&dl );
        if ( dl == 0x0 || dl == 0xffffffff ) continue;
        if ( (dl & 0x3) = 0x0 ) break;  /* メモリーアドレス情報 */
      }
      mem_adr = (unsigned int)(dl & 0xfffffffc);
    }

    ...

    (注意1) 割り込み/ポート/メモリーの未定義を示す値はPCI BIOSによって微妙に違います。ご使用のマシンで設定される値を確認してください。

    (注意2) PCIバスに接続されているPCIデバイスについての情報はQNX4に付属しているshow_pciコマンドで確認できます。

    (注意3) 同じデバイスがPCIバス時に複数存在することもあります。この場合はindexによって区別します。

    # show_pci

    PCI version   = 2.10

    Class      = Mass Storage (IDE)
    Vendor ID    = 8086h, INTEL CORPORATION
    Device ID    = 7010h,
    PCI index    = 0h
        IO @ f000h
    PCI Int Pin   = NC
    Interrupt line = 0

    Class      = Network (Ethernet)
    Vendor ID    = 1011h, Digital
    Device ID    = 14h, DC21041
    PCI index    = 0h
    IO @ 6100h MEM @ e0000000h
    PCI Int Pin   = INT A
    Interrupt line = 11

  • signal01

    ■[UNIX] signal関数の使用例

    #include

    #include "ourhdr.h"


    /* シグナルハンドラの定義 */
    static void
    sig_usr(int signo)
    {
      if (signo == SIGUSR1) {
        printf("received SIGUSER1\n");
      } else if (signo == SIGUSR2) {
        printf("received SIGUSER2\n");
      } else {
        err_dump("received signal %d\n", signo);
      }

      return;
    }

    int
    main(void)
    {
      /* シグナルハンドラを設定する。signal()は戻り値として今までのシグナルハンド
       * ラもしくはSIG_ERRを返す。
       */
      if (signal(SIGUSR1, sig_usr) == SIG_ERR) {
        err_sys("cat't catch SIGUSER1");
      }
      if (signal(SIGUSR2, sig_usr) == SIG_ERR) {
        err_sys("cat't catch SIGUSER2");
      }

      for (;;) {
        pause();
      }
    }

    =========================

    $ ./a.out &
    [1] 26081
    $ kill -USR1 26081
    $ received SIGUSER1
    $ kill -USR2 26081
    $ received SIGUSER2
    $ kill 26081
    [1]+ 終了しました   ./a.out