ここから転載
QNX4でハードウェアアクセスを行うドライバープログラムを作成する際に必要となるポートアクセスや割り込みハンドリングなど各種Tipsについて説明します。
コード例ではエラーについては扱いませんのが実際のプログラムでは必ずエラーチェックを入れてください。
QNX4のオンラインマニュアルには関連する関数やコード例がいろいろあります。必ず目を通してください。
リンクオプション
ポートアクセスや割り込みの禁止/解除を行うコードを作成する場合は、プログラムをリンクする時にプリビレッジオプションが必要となります。また、実行する際にはrootユーザで行う必要があります。
ccオプション -T
test.cをコンパイルしてtestにリンクする例
cc -o test -T 1 test.c
実行ユーザ
root
プロセス間通信
ドライバーはプロセスとして実現されるため、そのコントロールとしてプロセス同士のやり取りが必要となります。プロセス同士のやりとりを実現するプロセス間通信には次の3つがあります。
メッセージ通信は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つの関数を使用します。
共有メモリーの生成、引き渡し
共有メモリーのインポート、アクセス
コード例
プロセス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