לדלג לתוכן

חיי התקן וניהול כוח

נניח ש־driver מחזיק timer שמסתיים בעוד מאה מילישניות. בינתיים ההתקן עובר למצב צריכת כוח נמוכה או מוסר. אם callback הטיימר ניגש לרגיסטר שכבר אינו זמין, העובדה שה־device context עדיין קיים אינה עוזרת. חיי האובייקט בתוכנה וחיי המשאב החומרתי אינם אותו פרק זמן.

נבנה טבלת מצבים שמפרידה בין קיום device, זמינות משאבי חומרה, מצב D0 וזכות לקבל עבודה חדשה. המערכת צריכה להישאר נכונה גם כאשר הסדר אינו "הקמה, הרבה עבודה, סיום נוח", אלא כולל כשל בהקמה או הסרה בזמן בקשה פעילה.

הקמה אינה אירוע יחיד

ב־KMDF, EvtDriverDeviceAdd יוצר את מבני התוכנה ואת התורים. ה־callbacks של PrepareHardware ו־ReleaseHardware מתאימים לקישור ולשחרור משאבים שהמערכת הקצתה להתקן. המעברים D0Entry ו־D0Exit מתארים כניסה ויציאה מן המצב הפעיל של ההתקן.

החלוקה מאפשרת לאותו אובייקט device לעבור כמה מחזורי כוח בלי להיבנות מחדש בכל פעם. מיפוי משאב יכול להיות קיים, ובכל זאת אסור לגשת להתקן בזמן שאינו פעיל. מנגד, כשל באמצע PrepareHardware דורש ניקוי של מה שכבר נוצר, גם אם D0Entry מעולם לא נקרא.

בדרייבר התוכנה של הפרויקט אין רגיסטרי חומרה. עדיין נשתמש ב־D0 כדי לשלוט בקבלה וב־timer, כדי שהלומד יוכל לראות את פרוטוקול החיים לפני הוספת התקן אמיתי.

עצירת קבלה לפני המתנה

הקטע הבא הוא חלק משולב מדרייבר הפרויקט. הוא מניח device context עם Online, Timer ו־PendingQueue, וכן סנכרון device-level עבור callbacks הקצרים. פונקציות PnP אינן מחזיקות את נעילת ה־I/O האוטומטית של ה־callbacks האלה, ולכן הן רוכשות אותה במפורש רק לצורך שינוי הדגל:

NTSTATUS AmitEvtD0Exit(WDFDEVICE device, WDF_POWER_DEVICE_STATE target) {
    UNREFERENCED_PARAMETER(target);
    PAGED_CODE();
    auto* context = AmitGetDeviceContext(device);

    WdfObjectAcquireLock(device);
    context->Online = FALSE;
    WdfObjectReleaseLock(device);

    WdfTimerStop(context->Timer, TRUE);
    WdfIoQueuePurgeSynchronously(context->PendingQueue);
    return STATUS_SUCCESS;
}

הסדר חשוב. תחילה מפרסמים שאין לקבל עבודה חדשה, אחר כך ממתינים ליציאת callback timer אם הוא רץ, ואז מרוקנים את התור שאינו מנוהל אוטומטית לפי כוח. אין להחזיק את נעילת ה־device בזמן WdfTimerStop עם המתנה, משום שה־timer עשוי להזדקק לאותה נעילה כדי לסיים.

הקוד פועל ב־PASSIVE_LEVEL, כפי שדורשים callbacks והפעולות הסינכרוניות המתאימות. קריאה לאותה פונקציית עזר מתוך DPC תהיה הפרת חוזה. המאקרו PAGED_CODE הוא בדיקת הנחה שימושית, אך אינו מוכיח שכל מסלול עתידי יקרא לפונקציה בזמן הנכון.

תורים מנוהלי כוח ותורים פרטיים

תור power-managed יכול להיעצר ולהתחיל על ידי ה־framework בהתאם למצב ההתקן. בקשה שכבר נמסרה ל־driver דורשת טיפול דרך EvtIoStop או פרוטוקול אחר לפי העיצוב. עצירת התור אינה מבטלת אוטומטית גישה של עבודה שכבר יצאה ממנו למשאב.

בפרויקט התור הראשי מנוהל לפי כוח, והתור manual של ההשהיה מסומן במפורש כלא מנוהל לפי כוח. לכן D0Exit מרוקן אותו ו־D0Entry פותח אותו מחדש. ההחלטה הזו נכתבת בקוד כדי שלא נסתמך על ערך ברירת מחדל שאינו מתאים להוכחה.

קריאת drain מאפשרת לבקשות קיימות להתבצע, בעוד purge מבטלת אותן לפי החוזה. אם ההתקן כבר אינו זמין, drain יכול להמתין לעבודה שאין מי שישלים. הבחירה חייבת להתאים לסוג המידע וליכולת ההתקן, ולא רק לרצון "לא לאבד בקשות".

הסרה מפתיעה

ב־surprise removal לא מקבלים בהכרח הזדמנות לבצע פקודה אחרונה לחומרה. ה־callback שמנסה "לוודא שההתקן נעלם" באמצעות קריאת רגיסטר יכול בעצמו להיות הגישה האסורה. צריך לסמן מצב, לעצור קבלה ולהשלים בקשות לפי היכולת שנותרה.

משאב תוכנה כגון buffer עדיין יכול להידרש לצורך השלמת בקשה, גם כאשר MMIO כבר אינו נגיש. לכן סדר השחרור אינו פשוט "מחק את כל ה־context". עוצרים משתמשים, ממתינים לפעולות שניתן להמתין להן, מסיימים בקשות ורק אז משחררים אחסון.

הפרוטוקול צריך גם להבחין בין file cleanup של לקוח אחד לבין הסרת ההתקן כולו. הראשון אינו עילה לכבות עבודה של כל הלקוחות, והשני אינו יכול להסתפק בניקוי handle יחיד.

ניסוי עם התקן תוכנה

ב־VM בלבד, התקינו את התקן התוכנה של הפרויקט, הציבו breakpoints על callbacks של כוח ושל הסרה ושלחו בקשה מושהית. לאחר מכן בצעו restart של התקן המעבדה באמצעות Device Manager או DevCon עם מזהה החומרה המדויק שלו. תעדו את הסדר שנצפה ואת מצב התור בכל עצירה.

הסדר המדויק יכול להיות מושפע מהפעולה ומה־build. הדוח צריך להשוות את התצפית לחוזה, ולא להכריז שכל הסרה בכל חומרה עוברת באותו רצף. אין לנסות את הפקודות על התקן אחסון, רשת או תצוגה של מחשב העבודה.

קריאה להעמקה

התיעוד של ניהול תורי I/O, עצירת timer וריקון מבטל של תור מגדיר את גבולות ההמתנה והבעלות.