יום שישי

הקדמה ל-short: גישה ל-IO

הפוסט הזה נועד לעריכת הכרות עם כמה 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.




unsigned inb(unsigned port);

void outb(unsigned char byte, unsigned port);

2. קריאה וכתיבה של word - 16 bits
unsigned inw(unsigned port);

void outw(unsigned short word, unsigned port);

3. קריאה וכתיבה של long - 32 bits.
T
unsigned inl(unsigned port);

void outl(unsigned longword, unsigned port);


הפונקציות הבאות משמשות לקריאה וכתיבה של סדרה של bytes או words או long מה-IO:


1. קריאה וכתיבה של סדרת bytes. אורך הסדרה = count.




void insb(unsigned port, void *addr, unsigned long count);

void outsb(unsigned port, void *addr, unsigned long count);
2. קריאה וכתיבה של סדרת words. אורך הסדרה = count.

void insw(unsigned port, void *addr, unsigned long count);

void outsw(unsigned port, void *addr, unsigned long count);
3. קריאה וכתיבה של סדרת long. אורך הסדרה = count.

void insl(unsigned port, void *addr, unsigned long count);

void outsl(unsigned port, void *addr, unsigned 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);





אין תגובות:

הוסף רשומת תגובה