Linux Kernel & Device Driver Programming

Ch 9 - Communicating with Hardware

 

Topics


I/O Ports versus I/O Memory


Please do not be confused by two slightly different uses of "memory mapping" in Linux.

First, there is the mmap system call, which can be used to "map" a portion (or all) of a file onto a portion of a processes virtual memory space. The mapping is implemented using the page-swapping machinery provided to support virtual memory.

Second, there is memory-mapped I/O, in which some registers and/or local memory of an I/O (controller) device are mapped to physical memory addresses. That is, when a reference is made to a particular physical memory address, that reference is not routed to a normal (RAM) memory device; instead, it is routed to the appropriate I/O device.

These two concepts sometimes are used in combination, since when a program running on the CPU wants to access I/O device memory (that is, to do memory-mapped I/O), the paged virtual memory system needs to map some virtual address to the physical address, which is in turn mapped to the I/O address.


Bus Architecture of a PC Using a VIA Chipset for the Pentium IV

(From a VIA Technologies web page, at http://www.via.com.)


Observe the various buses, the devices connected to each of them, and the roles of the Bridges in connecting the busses.


Important differences between I/O registers and RAM


Suppressing Compiler/CPU Optimizations


Port I/O Operation Macros


Memory-Mapped I/O Operations


The ioread8, ioread16, etc. replace older-style readb, readw, etc., which are now deprecated.


PC Parallel Port


Parallel Port Device Pinout Diagram


Reading the example module short

Note: The links below are to copies of the module files that I have hand-converted to HTML. It may also be helpful to use the cross-referenced version of the example short.


We will use this example again later, when we look at interrupt handling.


I/O Ports

e.g.,

0000-001f : dma1
0020-003f : pic1
0040-005f : timer
0060-006f : keyboard
0070-007f : rtc
0080-008f : dma page reg
00a0-00bf : pic2
00c0-00df : dma2
00f0-00ff : fpu
0170-0177 : ide1
01f0-01f7 : ide0
02f8-02ff : serial(auto)
0376-0376 : ide1
03c0-03df : vga+
03f6-03f6 : ide0
03f8-03ff : serial(auto)
0cf8-0cff : PCI conf1
1000-103f : 3Com Corporation 3c905 100BaseTX [Boomerang]
  1000-103f : 00:0c.0
1050-1053 : Advanced Micro Devices [AMD] AMD-760 MP [IGD4-2P] System Controller
2000-2fff : PCI Bus #01
  2000-20ff : ATI Technologies Inc 3D Rage Pro AGP 1X/2X
f000-f00f : Advanced Micro Devices [AMD] AMD-766 [ViperPlus] IDE
  f000-f007 : ide0
  f008-f00f : ide1

I/O Port Registry API

int check_region(unsigned long start, unsinged long len);
struct resource *request_region(unsigned long start, unsigned long len, char *name);
void release_region(unsigned long start, unsigned long len);

Is it obvious why two independent drivers cannot safely share the same port? Is it also obvious that two different threads of control in the same driver cannot (in general) safely share access to the same port?

Discuss in class how this could cause serious problems where the protocol for communicating with a device requires a sequence of steps to be performed in a particular order with no other operations on the device interleaved, e.g., with the video frame grabber used for the projects in this course in 2003-2004.

In kernel version 2.6.11 linux/ioport.h the I/O port registration calls are actually macros:

#define request_region(start,n,name)	__request_region(&ioport_resource, (start), (n), (name))
extern struct resource * __request_region(struct resource *, unsigned long start, unsigned long n, const char *name);

#define check_region(start,n)	__check_region(&ioport_resource, (start), (n))
extern int __check_region(struct resource *, unsigned long, unsigned long);

#define release_region(start,n)	__release_region(&ioport_resource, (start), (n))
extern void __release_region(struct resource *, unsigned long, unsigned long);

I/O Port Registry Usage

See example of call to request_region in short.c.

Also see example in skull_init.c:

#include <linux/ioport.h>
#include <linux/errno.h>
static int skull_detect (unsigned int port; unsigned int range)
{
  int err;
  if ((err = check_region (port, range)) = 0) return err; /* busy */
  if (skull_probe_hw (port, range) != 0) return -ENODEV; /* not found */
  request_region (port, range, "skull"); /* "can't fail" */
  return 0;
}

And in skull_clean.c:

static void skull_release (unsigned int port, unsigned int range)
{
  release_region (port, range);
}

I/O Memory Registry

00000000-0009f7ff : System RAM
0009f800-0009ffff : reserved
000a0000-000bffff : Video RAM area
000c0000-000c7fff : Video ROM
000dc000-000dcfff : Advanced Micro Devices [AMD] AMD-766 [ViperPlus] USB
000dc000-000dcfff : usb-ohci
000e0000-000effff : Extension ROM
000f0000-000fffff : System ROM
00100000-3ffeffff : System RAM
00100000-0026b019 : Kernel code
0026b01a-0037b9c3 : Kernel data
3fff0000-3ffffbff : ACPI Tables
3ffffc00-3fffffff : ACPI Non-volatile Storage
f4001000-f4001fff : Advanced Micro Devices [AMD] AMD-760 MP [IGD4-2P] System Controller
f4100000-f41fffff : PCI Bus #01
f4100000-f4100fff : ATI Technologies Inc 3D Rage Pro AGP 1X/2X
f5000000-f5ffffff : PCI Bus #01
f5000000-f5ffffff : ATI Technologies Inc 3D Rage Pro AGP 1X/2X
f8000000-fbffffff : Advanced Micro Devices [AMD] AMD-760 MP [IGD4-2P] System Controller
fec00000-fec0ffff : reserved
fee00000-fee00fff : reserved
fff80000-ffffffff : reserved

I/O Memory Registry API

int check_mem_region (unsigned long start, unsigned long len);
int request_mem_region (unsigned long start, unsigend long len, char * name);
int release_mem_region (unsgined long start, unsigned long len);

I/O Memory Registry API Usage

See example of correct usage with request_mem_region in short.c, and later call to release_mem_region.

Example of incorrect (old-style) usage:

if (check_mem_region (mem_add,mem_size)) {
   printk ("drivername: memory already in use\n"); return -EBUSY;
}
request_mem_region (mem_addr, mem_size, "drivername");

Do you see the problem?


Resource Structure

declared in linux/ioport.h:

struct resource {
  const char *name;
  unsigned long start, end;
  unsigned long flags;
  struct resource *parent, *sibling, *child;
}

See also © 2003, 2004, 2005 T. P. Baker ($Id: ch9.html,v 1.1 2010/06/07 14:29:15 baker Exp baker $)