Writing Portable Device Drivers
Unlike on most typical embedded systems, accessing I/O memory on Linux cannot be done directly. This is due to the wide range of different memory types and maps present on the wide range of processors on which Linux runs. To access I/O memory in a portable manner, you must call ioremap() to gain access to a memory region and iounmap() to release access.
ioremap() is defined as:
void * ioremap (unsigned long offset, unsigned long size);
You pass in a starting offset of the region you wish to access and the size of the region in bytes. You cannot just use the return value as a memory location to read and write from directly, but rather it is a token that must be passed to different functions to read and write data.
The functions to read and write data using memory mapped by ioremap() are:
u8 readb (unsigned long token); /* read 8 bits */ u16 readw (unsigned long token); /* read 16 bits */ u32 readl (unsigned long token); /* read 32 bits */ void writeb (u8 value, unsigned long token); /* write 8 bits */ void writew (u16 value, unsigned long token); /* write 16 bits */ void writel (u32 value, unsigned long token); /* write 32 bits */
After you are finished accessing memory, you must call iounmap() to free up the memory so that others can use it if they want to.
The code example in Listing 4 from the Compaq PCI Hot Plug driver in drivers/hotplug/cpqphp_core.c shows how to access a PCI device's resource memory properly.
To access the PCI memory of a device, you again must use some general functions and not try to access the memory directly. This is due to the different ways the PCI bus can be accessed, depending on the type of hardware you have. If you use the general functions, then your PCI driver will be able to work on any type of Linux system that has a PCI bus.
To read data from the PCI bus use the following functions:
int pci_read_config_byte(struct pci_dev *dev, int where, u8 *val); int pci_read_config_word(struct pci_dev *dev, int where, u16 *val); int pci_read_config_dword(struct pci_dev *dev, int where, u32 *val);
and to write data, use these functions:
int pci_write_config_byte(struct pci_dev *dev, int where, u8 val); int pci_write_config_word(struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(struct pci_dev *dev, int where, u32 val);
These functions allow you to write 8, 16 or 32 bits to a specific location that is assigned to a specific PCI device. If you wish to access the memory location of a specific PCI device that has not been initialized by the Linux PCI core yet, you can use the following functions that are present in the pci_hotplug core code:
int pci_read_config_byte_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u8 *val); int pci_read_config_word_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u16 *val); int pci_read_config_dword_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u32 *val); int pci_write_config_byte_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u8 val); int pci_write_config_word_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u16 val); int pci_write_config_dword_nodev(struct pci_ops *ops, u8 bus, u8 device, u8 function, int where, u32 val);
An example of reading and writing to PCI memory by a driver can be seen in the USB OHCI driver at drivers/usb/usb-ohci.c (see Listing 5).
If you follow these different rules when creating a new Linux kernel device driver, or when modifying an existing one, the resulting code will run successfully on a wide range of processors. These rules are also good to remember when debugging a driver that only works on one platform (remember those endian issues).
The most important resource to remember is to look at existing kernel drivers that are known to work on different platforms. One of Linux's strengths is the open access of its code, which provides a powerful learning tool for aspiring driver authors.
Greg Kroah-Hartman is currently the Linux USB and PCI Hot Plug kernel maintainer. He works for IBM, doing various Linux kernel-related things and can be reached at email@example.com.
|Bitcoin on Amazon! Sort of...||Sep 28, 2016|
|Free Today: September Issue of Linux Journal (Retail value: $5.99)||Sep 27, 2016|
|nginx||Sep 27, 2016|
|Epiq Solutions' Sidekiq M.2||Sep 26, 2016|
|Nativ Disc||Sep 23, 2016|
|Android Browser Security--What You Haven't Been Told||Sep 22, 2016|
- Free Today: September Issue of Linux Journal (Retail value: $5.99)
- Android Browser Security--What You Haven't Been Told
- Readers' Choice Awards 2013
- Epiq Solutions' Sidekiq M.2
- The Many Paths to a Solution
- Nativ Disc
- Returning Values from Bash Functions
- Securing the Programmer
- Tech Tip: Really Simple HTTP Server with Python
Pick up any e-commerce web or mobile app today, and you’ll be holding a mashup of interconnected applications and services from a variety of different providers. For instance, when you connect to Amazon’s e-commerce app, cookies, tags and pixels that are monitored by solutions like Exact Target, BazaarVoice, Bing, Shopzilla, Liveramp and Google Tag Manager track every action you take. You’re presented with special offers and coupons based on your viewing and buying patterns. If you find something you want for your birthday, a third party manages your wish list, which you can share through multiple social- media outlets or email to a friend. When you select something to buy, you find yourself presented with similar items as kind suggestions. And when you finally check out, you’re offered the ability to pay with promo codes, gifts cards, PayPal or a variety of credit cards.Get the Guide