Използване на входна памет
Въпреки популярността на I/O портовете в света на x86, основният механизъм, използван за комуникация с устройства, са свързаните с паметта регистри и паметта на устройството. И двете се наричат I/O памет, защото разликата между регистрите и паметта не се вижда от софтуера. Начинът на достъп до I/O паметта зависи от компютърната архитектура, шината и използваното устройство, въпреки че принципите са едни и същи навсякъде. Дискусията в тази глава се занимава основно с ISA и PCI памет и също се опитва да предаде обща информация. Въпреки че достъпът до PCI памет е обхванат тук, подробното обсъждане на PCI се отлага до Глава 12. Така че докато деименуването на указателя работи (сега) на x86 платформи, лошото използване на правилни макроси възпрепятства преносимостта и четливостта на драйвера. Придобиване и картографиране на I/O паметI/O областите на паметта трябва да бъдат разпределени преди да бъдат използвани. Интерфейсът за получаване на области от паметта (дефиниран в ) е: struct ресурс *request_mem_region(неподписано дълго начало, неподписано дълго len, char *име); Тази функция заделя len байта памет, започвайки от start. Ако всичко върви добре, се връща не-NULL указател; в противен случай се връща NULL. Цялата разпределена I/O памет е посочена в /proc/iomem. Когато вече не са необходими, областите на паметта трябва да бъдат освободени: void release_mem_region(неподписано дълго начало, неподписано дълго обектив); Има и стара функция за проверка на I/O памет: int check_mem_region(неподписан дълъг старт, неподписан дълъг len); Но,подобно на check_region, тази функция не е безопасна и трябва да се избягва. Функциите се извикват съгласно следната дефиниция: void *ioremap(неподписан дълъг phys_addr, неподписан дълъг размер); void *ioremap_nocache(неподписан дълъг phys_addr, неподписан дълъг размер); void iounmap(void * addr); I/O достъп до паметтаНа някои платформи можете да използвате върнатата стойност на ioremap като указател. Тази употреба не е преносима и разработчиците на ядрото работят все повече и повече за премахването на всяка такава употреба. Правилният начин за достъп до I/O паметта е наборът от функции (дефинирани с ), предоставени за тази цел. За да четете от I/O памет, използвайте едно от следните: unsigned int ioread8(void *addr); unsigned int ioread16(void *addr); unsigned int ioread32(void *addr); Има подобен набор от функции за запис в I/O памет: void iowrite8(u8 стойност, void *addr); void iowrite16(u16 стойност, void *addr); void iowrite32(u32 стойност, void *addr); 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); 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); Функциите ioread четат стойности за броене от дадения адрес към дадения buf, а функциите iowrite записват стойности за броене от дадения buf към дадения адрес. забележи, чеброят се изразява по отношение на размера на данните, които се записват; ioread32_rep чете броя на 32-битовите стойности, започвайки от buf. Всички горепосочени функции извършват вход/изход на даден адрес. Вместо това, ако искате да работите с I/O блок памет, можете да използвате едно от следните: void memset_io(void *addr, u8 стойност, unsigned int count); void memcpy_fromio(void *dest, void *source, unsigned int count); void memcpy_toio(void *dest, void *source, unsigned int count); Тези функции се държат като своите двойници на C библиотеката. Ако прочетете изходния код на ядрото, ще видите много извиквания към стария набор от функции, когато използвате I/O памет. Тези функции все още работят, но не се препоръчва използването им в нов код. Освен всичко друго, те са по-малко сигурни, защото не извършват някакъв вид проверка на типа. Ние обаче ги представяме тук: Тези макроси се използват за извличане на 8-битови, 16-битови и 32-битови стойности на данни от I/O памет. void writeb(неподписана стойност, адрес); void writew(неподписана стойност, адрес); void writel(неподписана стойност, адрес); Подобно на предишните функции, тези функции (макроси) се използват за запис на 8-битови, 16-битови и 32-битови елементи от данни. Някои 64-битови платформи също предлагат readq и writeq за операции с памет с четири думи (8 байта) на PCI шината. Спецификацията на четирите думи е исторически останала от времето, когато всички реални процесори са имали 16-битови думи. Всъщност името L, използвано за 32-битови стойности, също стана неправилно, но преименуването на всичко би объркало нещата още повече. Портове като паметI/OНякои хардуери имат интересна функция: някои версии използват I/O портове, докато други използват I/O памет. Регистрите, експортирани към процесора, така или иначе са същите, но методът на достъп е различен. Като начин да се улесни живота на драйверите, работещи с този вид хардуер, и като начин за минимизиране на очевидните разлики между I/O порт и достъп до паметта, ядрото 2.6 предоставя функция, наречена ioport_map: void *ioport_map(неподписан дълъг порт, неподписан int брой); Когато вече не е необходимо, това съпоставяне трябва да бъде отменено: void ioport_unmap(void *addr); Тези функции правят I/O портовете да изглеждат като памет. Имайте предвид обаче, че I/O портовете все още трябва да бъдат разпределени от request_region, преди да могат да бъдат преназначени по този начин. Повторно използване на съкратено за I/O паметНапример, ето как използвахме short, за да осветим светодиодите за отстраняване на грешки на платката за отстраняване на грешки на MIPS: mips.root# ./short_load use_mem=1 база=0xb7ffffc0 mips.root# ехо -n 7 > /dev/short0 Използването на кратко за I/O памет е същото като за I/O портове. Обърнете внимание на използването на бариера на паметта тук. Тъй като iowrite8 вероятно ще се превърне в директно присвояване на много архитектури, е необходима бариера на паметта, за да се гарантира, че записите са в очаквания ред. short използва inb и outb, за да покаже как се прави това. Въпреки това би било лесно упражнение за читателя да модифицира кратко, за да преназначи I/O портове с ioport_map и значително да опрости останалата част от кода. ISA памет под 1 MBЕдин от найизвестните области на входно-изходната памет са областта ISA, намираща се на персонални компютри. Това е област от паметта между 640 KB (0xA0000) и 1 MB (0x100000). Като такъв, той се намира точно в средата на обикновената RAM памет на системата. Това позициониране може да изглежда малко странно; това е артефакт от решение, взето в началото на 80-те години на миналия век, когато 640 KB памет изглеждаше повече, отколкото някой някога би могъл да използва. Въпреки че ISA I/O памет съществува само на компютри от клас x86, смятаме, че си струва няколко думи и пример за драйвер за нея. За да демонстрираме достъп до паметта на ISA, използваме друг малък глупав модул (част от примерните източници). По принцип се нарича глупаво, съкращение от Simple Tool for Unloading and Printing ISA Data или нещо подобно. #define ISA_BASE 0xA0000 #define ISA_MAX 0x100000 /* за редовен достъп до паметта */ /* този ред се появява в silly_init */ io_base = ioremap(ISA_BASE, ISA_MAX - ISA_BASE); ioremap връща указател, който може да се използва с ioread8 и други функции, обяснени в раздела "I/O достъп до паметта". Следващите две устройства са /dev/sillyw (второстепенен номер 1) и /dev/sillyl (второстепенен номер 2). Те работят като /dev/sillyb, само че използват 16-битови и 32-битови функции. Ето изпълнението на write for sillyl, отново част от превключвателя: iowrite32(*(u32 *)ptr, добавяне); Последното устройство е /dev/sillycp (малък номер 3), което използва функциите memcpy_*io за изпълнение на същата задача. Ето същността на неговата реализация за четене: memcpy_fromio(ptr, добавяне, броене); Тъй катоioremap е използван за достъп до областта на паметта на ISA, глупаво е да извикате iounmap при разтоварване на модула: isa_readb и приятелиПоглед към изходния код на ядрото ще изведе друг набор от процедури с имена като isa_readb. Всъщност всяка от току-що описаните функции има isa_ еквивалент. Тези функции осигуряват достъп до паметта на ISA без необходимост от отделна стъпка на ioremap. Разработчиците на ядрото обаче казват, че тези функции са предназначени да бъдат временни помощни средства за пренасяне на драйвери и че може да изчезнат в бъдеще. Следователно употребата им трябва да се избягва. |