הפוסט הזה נועד לעריכת הכרות עם כמה APIs המשמשים לגישה ל-IO Ports. זוהי הקדמה לפוסט "short" שמציג דוגמא לטיפול ב-IO, כך שכאן יוצגו בעיקר APIs וההסבר מה הם עושים, כשהדוגמאות ינתנו בפוסט של short.
ישנן שתי אפשרויות למיפוי IO למעבד:
1. PMIO - Port Mapped IO. כאן ה-IO מתממשק ל-CPU דרך ממשק IO מיוחד, שקיים ב-CPU. במעבדי x86 קיים ממשק IO בנוסף לממשק הזיכרון הרגיל.
2. Memory Mapped IO - MMIO. כאן ה-IO ממופה למרחב הזיכרון הרגיל.
במעבדים בהם לא קיים ממשק IO נפרד, השימוש יהיה ב-MMIO בכל מקרה.
נציג את ה-API לטיפול ב-IO עבור שני המקרים: PMIO ו-MMIO. נראה שהטיפול בשני המקרים מאד דומה. בשלב ראשון צריך לבקש מה-kernel לשריין את כתובות הזיכרון השייכות ל-IO, ואח"כ אפשר להשתמש בסט הפקודות לקריאה ולכתיבה שיפורט עבור PMIO ו-MMIO.
טיפול ב-PMIO.
בשלב הראשון יש לבקש מהקרנל אישור להשתמש בכתובות אליהן ממופה ה-IO.
הנה ה-prototype של request_region:
truct resource *request_region(unsigned long first, unsigned long n, const char *name);
הפרמטרים שהפונקציה מקבלת:
first: הכתובת של ה-port הראשון.
n: מספר הפורטים.
name: השם של ה-device.
הפונקציה תחזיר NULL אם הפעולה לא הצליחה.
request_region למעשה לא עושה כלום חוץ מרגיסטרציה של הכתובות כדי שישורינו ע"י הקרנל. הקרנל ימנע מ-devices אחרים לקחת את אותן כתובות.
דוגמא:
if (! request_region(short_base, SHORT_NR_PORTS, "short")) {
printk(KERN_INFO "short: can't get I/O port address 0x%lx\n",
short_base);
return -ENODEV;
}
בסוף השימוש, צריך להחזיר את האיזור ששוריין עם release_region:
void release_region(unsigned long start, unsigned long len);
קריאה מה-IO וכתיבה ל-IO.
הפונקציות הבאות משמשות לקריאה וכתיבה מה-IO.
1. קריאה וכתיבה של byte.
הפונקציות הבאות משמשות לקריאה וכתיבה של סדרה של bytes או words או long מה-IO:
1. קריאה וכתיבה של סדרת bytes. אורך הסדרה = count.
2. קריאה וכתיבה של סדרת words. אורך הסדרה = count.
3. קריאה וכתיבה של סדרת long. אורך הסדרה = count.
תוספת pause לפונקציות הקריאה\כתיבה למקרה שהתקן ה-IO איטי מדי:
עהור כל אלת מהפונקציות הנ"ל אפשר להוסיף השהיה על-ידי התוספת p_ לשם הפונקציה. דוגמאות: insb_p, outl_p.
טיפול ב-MMIO
ה-API של פונקצית האלוקציה דומה לזה של ה- PMIO, עם תוספת של _mem בשם הפונקציה.
גם כאן צריך תחילה לבקש את איזור הזיכרון מהקרנל:
struct resource *request_mem_region(unsigned long start,unsigned long len, char *name);
ובאופן דומה השחרור בסוף התהליך - בד"כ ב-cleanup:
void release_mem_region(unsigned long start, unsigned long len);
ב-MMIO, לפני שנוכל להשתמש בזיכרון, צריך להפעיל פונקציה נוספת שתאפשר לקרנל גישה לזיכרון:
void *ioremap(unsigned long phys_addr, unsigned long size);
וגם את זה צריך לשחרר - בדכ בפונקציה cleanup - ע"י:
void iounmap(void * addr);
דוגמא להכנת ה-MMIO:
if (! request_mem_region(short_base, SHORT_NR_PORTS, "short")) {
printk(KERN_INFO "short: can't get I/O mem address 0x%lx\n",
short_base);
return -ENODEV;
}
short_base = (unsigned long) ioremap(short_base, SHORT_NR_PORTS);
והנה פונקציות הקריאה והכתיבה מ-הMMIO:
קריאה:
unsigned int ioread8(void *addr);
unsigned int ioread16(void *addr);
unsigned int ioread32(void *addr);
כתיבה:
void iowrite8(u8 value, void *addr); void iowrite16(u16 value, void *addr);
void iowrite32(u32 value, void *addr);
קריאת סידרת ערכים:
קריאת סידרת ערכים מסוג bytes/words/longs שמוצבעת ע"י addr לתוך buf. גודל הסדרה הוא count:
void ioread8_rep(void *addr, void *buf, unsigned long count); void ioread16_rep(void *addr, void *buf, unsigned long count); void ioread32_rep(void *addr, void *buf, unsigned long count);
כתיבת סידרת ערכים:
העתקת סדרת ערכים מסוג bytes/words/longs שמוצבעת ע"י addr לתוך buf. גודל הסדרה הוא count.
void iowrite8_rep(void *addr, const void *buf, unsigned long count); void iowrite16_rep(void *addr, const void *buf, unsigned long count); void iowrite32_rep(void *addr, const void *buf, unsigned long count);
אפשר גם להשתמש בפונקציות מסוג memset ו-memcpy:
void memset_io(void *addr, u8 value, unsigned int count); void memcpy_fromio(void *dest, void *source, unsigned int count); void memcpy_toio(void *dest, void *source, unsigned int count);
אין תגובות:
הוסף רשומת תגובה