ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
デバイスドライバの記述 Oracle Solaris 11.1 Information Library (日本語) |
パート I Oracle Solaris プラットフォーム用デバイスドライバの設計
2. Oracle Solaris カーネルとデバイスツリー
標準メッセージシグナル割り込みと拡張メッセージシグナル割り込み
22. ドライバのコンパイル、ロード、パッケージ化、およびテスト
23. デバイスドライバのデバッグ、テスト、およびチューニング
デバイスドライバが割り込みを受信および保守するには、ドライバが ddi_intr_add_handler(9F) を呼び出して割り込みハンドラをシステムに登録する必要があります。割り込みハンドラを登録すると、システムは割り込みハンドラを割り込み仕様に関連付けることができます。割り込みハンドラは、デバイスがその割り込みを担当している可能性のあるときに呼び出されます。ハンドラは、割り込みを処理するかどうかの判定、および処理する場合にはその割り込みの取り込みを担当します。
ヒント - サポートされている SPARC または x86 システムでデバイスの登録済み割り込み情報を取得するときは、mdb または kmdb デバッガの ::interrupts コマンドを使用します。
ドライバの割り込みハンドラを登録する場合、ドライバは通常、その attach(9E) エントリポイントで次の手順を実行します。
ddi_intr_get_supported_types(9F) を使用して、サポートされている割り込みのタイプを判定します。
ddi_intr_get_nintrs(9F) を使用して、サポートされている割り込みタイプの数を判定します。
kmem_zalloc(9F) を使用して、DDI 割り込みハンドルにメモリーを割り当てます。
割り当てる割り込みタイプごとに、次の手順を実行します。
ddi_intr_get_pri(9F) を使用して、割り込みの優先順位を取得します。
割り込みに新しい優先順位を設定する必要がある場合は、ddi_intr_set_pri(9F) を使用します。
mutex_init(9F) を使用して、ロックを初期化します。
ddi_intr_add_handler(9F) を使用して、割り込みのハンドラを登録します。
ddi_intr_enable(9F) を使用して、割り込みを有効にします。
次の手順を実行して、各割り込みを解放します。
ddi_intr_disable(9F) を使用して、各割り込みを無効にします。
ddi_intr_remove_handler(9F) を使用して、割り込みハンドラを削除します。
mutex_destroy(9F) を使用して、ロックを削除します。
ddi_intr_free(9F) および kmem_free(9F) を使用して DDI 割り込みハンドルに割り当てられたメモリーを解放し、割り込みを解放します。
例 8-5 レガシー割り込みの登録
次の例は、mydev というデバイスの割り込みハンドラをインストールする方法を示しています。この例では、mydev が 1 つの割り込みだけをサポートしていることを前提としています。
/* Determine which types of interrupts supported */ ret = ddi_intr_get_supported_types(mydevp->mydev_dip, &type); if ((ret != DDI_SUCCESS) || (!(type & DDI_INTR_TYPE_FIXED))) { cmn_err(CE_WARN, "Fixed type interrupt is not supported"); return (DDI_FAILURE); } /* Determine number of supported interrupts */ ret = ddi_intr_get_nintrs(mydevp->mydev_dip, DDI_INTR_TYPE_FIXED, &count); /* * Fixed interrupts can only have one interrupt. Check to make * sure that number of supported interrupts and number of * available interrupts are both equal to 1. */ if ((ret != DDI_SUCCESS) || (count != 1)) { cmn_err(CE_WARN, "No fixed interrupts"); return (DDI_FAILURE); } /* Allocate memory for DDI interrupt handles */ mydevp->mydev_htable = kmem_zalloc(sizeof (ddi_intr_handle_t), KM_SLEEP); ret = ddi_intr_alloc(mydevp->mydev_dip, mydevp->mydev_htable, DDI_INTR_TYPE_FIXED, 0, count, &actual, 0); if ((ret != DDI_SUCCESS) || (actual != 1)) { cmn_err(CE_WARN, "ddi_intr_alloc() failed 0x%x", ret); kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } /* Sanity check that count and available are the same. */ ASSERT(count == actual); /* Get the priority of the interrupt */ if (ddi_intr_get_pri(mydevp->mydev_htable[0], &mydevp->mydev_intr_pri)) { cmn_err(CE_WARN, "ddi_intr_alloc() failed 0x%x", ret); (void) ddi_intr_free(mydevp->mydev_htable[0]); kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } cmn_err(CE_NOTE, "Supported Interrupt pri = 0x%x", mydevp->mydev_intr_pri); /* Test for high level mutex */ if (mydevp->mydev_intr_pri >= ddi_intr_get_hilevel_pri()) { cmn_err(CE_WARN, "Hi level interrupt not supported"); (void) ddi_intr_free(mydevp->mydev_htable[0]); kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } /* Initialize the mutex */ mutex_init(&mydevp->mydev_int_mutex, NULL, MUTEX_DRIVER, DDI_INTR_PRI(mydevp->mydev_intr_pri)); /* Register the interrupt handler */ if (ddi_intr_add_handler(mydevp->mydev_htable[0], mydev_intr, (caddr_t)mydevp, NULL) !=DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_add_handler() failed"); mutex_destroy(&mydevp->mydev_int_mutex); (void) ddi_intr_free(mydevp->mydev_htable[0]); kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } /* Enable the interrupt */ if (ddi_intr_enable(mydevp->mydev_htable[0]) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_enable() failed"); (void) ddi_intr_remove_handler(mydevp->mydev_htable[0]); mutex_destroy(&mydevp->mydev_int_mutex); (void) ddi_intr_free(mydevp->mydev_htable[0]); kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t)); return (DDI_FAILURE); } return (DDI_SUCCESS); }
例 8-6 レガシー割り込みの削除
次の例は、レガシー割り込みを削除する方法を示しています。
/* disable interrupt */ (void) ddi_intr_disable(mydevp->mydev_htable[0]); /* Remove interrupt handler */ (void) ddi_intr_remove_handler(mydevp->mydev_htable[0]); /* free interrupt handle */ (void) ddi_intr_free(mydevp->mydev_htable[0]); /* free memory */ kmem_free(mydevp->mydev_htable, sizeof (ddi_intr_handle_t));
ドライバの割り込みハンドラを登録する場合、ドライバは通常、その attach(9E) エントリポイントで次の手順を実行します。
ddi_intr_get_supported_types(9F) を使用して、サポートされている割り込みのタイプを判定します。
ddi_intr_get_nintrs(9F) を使用して、サポートされている MSI 割り込みタイプの数を判定します。
ddi_intr_alloc(9F) を使用して、MSI 割り込みにメモリーを割り当てます。
割り当てる割り込みタイプごとに、次の手順を実行します。
ddi_intr_get_pri(9F) を使用して、割り込みの優先順位を取得します。
割り込みに新しい優先順位を設定する必要がある場合は、ddi_intr_set_pri(9F) を使用します。
mutex_init(9F) を使用して、ロックを初期化します。
ddi_intr_add_handler(9F) を使用して、割り込みのハンドラを登録します。
次の関数のいずれかを使用して、すべての割り込みを有効にします。
ブロック内のすべての割り込みを有効にするときは、ddi_intr_block_enable(9F) を使用します。
各割り込みを個別に有効にするときは、ループで ddi_intr_enable(9F) を使用します。
例 8-7 一連の MSI 割り込みの登録
次の例は、mydev というデバイスの MSI 割り込みを登録する方法を示しています。
/* Get supported interrupt types */ if (ddi_intr_get_supported_types(devinfo, &intr_types) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_supported_types failed"); goto attach_fail; } if (intr_types & DDI_INTR_TYPE_MSI) mydev_add_msi_intrs(mydevp); /* Check count, available and actual interrupts */ static int mydev_add_msi_intrs(mydev_t *mydevp) { dev_info_t *devinfo = mydevp->devinfo; int count, avail, actual; int x, y, rc, inum = 0; /* Get number of interrupts */ rc = ddi_intr_get_nintrs(devinfo, DDI_INTR_TYPE_MSI, &count); if ((rc != DDI_SUCCESS) || (count == 0)) { cmn_err(CE_WARN, "ddi_intr_get_nintrs() failure, rc: %d, " "count: %d", rc, count); return (DDI_FAILURE); } /* Get number of available interrupts */ rc = ddi_intr_get_navail(devinfo, DDI_INTR_TYPE_MSI, &avail); if ((rc != DDI_SUCCESS) || (avail == 0)) { cmn_err(CE_WARN, "ddi_intr_get_navail() failure, " "rc: %d, avail: %d\n", rc, avail); return (DDI_FAILURE); } if (avail < count) { cmn_err(CE_NOTE, "nitrs() returned %d, navail returned %d", count, avail); } /* Allocate memory for MSI interrupts */ mydevp->intr_size = count * sizeof (ddi_intr_handle_t); mydevp->htable = kmem_alloc(mydevp->intr_size, KM_SLEEP); rc = ddi_intr_alloc(devinfo, mydevp->htable, DDI_INTR_TYPE_MSI, inum, count, &actual, DDI_INTR_ALLOC_NORMAL); if ((rc != DDI_SUCCESS) || (actual == 0)) { cmn_err(CE_WARN, "ddi_intr_alloc() failed: %d", rc); kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } if (actual < count) { cmn_err(CE_NOTE, "Requested: %d, Received: %d", count, actual); } mydevp->intr_cnt = actual; /* * Get priority for first msi, assume remaining are all the same */ if (ddi_intr_get_pri(mydevp->htable[0], &mydev->intr_pri) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_get_pri() failed"); /* Free already allocated intr */ for (y = 0; y < actual; y++) { (void) ddi_intr_free(mydevp->htable[y]); } kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } /* Call ddi_intr_add_handler() */ for (x = 0; x < actual; x++) { if (ddi_intr_add_handler(mydevp->htable[x], mydev_intr, (caddr_t)mydevp, NULL) != DDI_SUCCESS) { cmn_err(CE_WARN, "ddi_intr_add_handler() failed"); /* Free already allocated intr */ for (y = 0; y < actual; y++) { (void) ddi_intr_free(mydevp->htable[y]); } kmem_free(mydevp->htable, mydevp->intr_size); return (DDI_FAILURE); } } (void) ddi_intr_get_cap(mydevp->htable[0], &mydevp->intr_cap); if (mydev->m_intr_cap & DDI_INTR_FLAG_BLOCK) { /* Call ddi_intr_block_enable() for MSI */ (void) ddi_intr_block_enable(mydev->m_htable, mydev->m_intr_cnt); } else { /* Call ddi_intr_enable() for MSI non block enable */ for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_enable(mydev->m_htable[x]); } } return (DDI_SUCCESS); }
例 8-8 MSI 割り込みの削除
次の例は、MSI 割り込みを削除する方法を示しています。
static void mydev_rem_intrs(mydev_t *mydev) { int x; /* Disable all interrupts */ if (mydev->m_intr_cap & DDI_INTR_FLAG_BLOCK) { /* Call ddi_intr_block_disable() */ (void) ddi_intr_block_disable(mydev->m_htable, mydev->m_intr_cnt); } else { for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_disable(mydev->m_htable[x]); } } /* Call ddi_intr_remove_handler() */ for (x = 0; x < mydev->m_intr_cnt; x++) { (void) ddi_intr_remove_handler(mydev->m_htable[x]); (void) ddi_intr_free(mydev->m_htable[x]); } kmem_free(mydev->m_htable, mydev->m_intr_size); }