嵌入式linux中文站在线图书

Previous Page Next Page

Accessing I/O Regions

PC-compatible systems have 64K I/O ports, all of which may be driven from user space. User access to I/O ports on Linux is controlled by two functions: ioperm() and iopl(). ioperm() controls access permissions to the first 0x3ff ports. iopl() changes the I/O privilege level of the calling process, thus allowing among other things, unrestricted access to all ports. Only the super-user can invoke both these functions.

To write data to an I/O port, use outb(), outw(), outl(), or their cousins. To read data from a port, use inb(), inw(), inl(), or their relatives. Let's implement a simple program that reads the seconds ticking inside the RTC chip. I/O regions in the PC CMOS, of which the RTC is a part, are accessed via an index port (0x70) and a data port (0x71), as shown in Table 5.1 of Chapter 5, "Character Drivers." To read a byte of data from offset off within an I/O address range, write off to the index port and read the associated data from the data port. Listing 19.2 reads the seconds field of the RTC; but to use it to obtain data from other I/O regions, change the arguments passed to dump_port() suitably.

Listing 19.2. Utility to Dump Bytes from an I/O Region

#include 

void
dump_port(unsigned char addr_port, unsigned char data_port,
          unsigned short offset, unsigned short length)
{
  unsigned char i, *data;

  if (!(data = (unsigned char *)malloc(length))) {
    perror("Bad Malloc\n");
    exit(1);
  }

  /* Write the offset to the index port
     and read data from the data port */
  for(i=offset; i

You may also accomplish the same task by operating on /dev/port. This will incur a performance penalty because code flow has to pass through a kernel driver, but you have the flexibility to control access permissions on the device node without using iopl() or ioperm(). Here's the /dev/port equivalent of Listing 19.2:

#include 
#include 

int
main(int argc, char *argv[])
{
  char seconds=0;
  char data = 0;
  int fd = open("/dev/port", O_RDWR);

  lseek(fd, 0x70, SEEK_SET);
  write(fd, &data, 1);

  lseek(fd, 0x71, SEEK_SET);
  read(fd, &seconds, 1);
  printf("%02X ", seconds);
}

In Chapter 5, you learned to talk to your computer's parallel port via a kernel driver. Let's now implement a sample program that interacts with a parallel port device from user space. The kernel's parallel port subsystem provides a character driver called ppdev that exports parallel port access to user land. Ppdev creates device nodes, /dev/parportX, where X is the parallel port number. Applications can open /dev/parportX, exchange data via read()/write() system calls, and issue a variety of ioctl() commands. Using kernel interfaces, such as ppdev, is preferable to directly operating over I/O ports using ioperm(), iopl(), or /dev/port. The former technique is safer, works across architectures, and functions over different device form factors such as USB-to-parallel converters.

Consider the simple LED board that you used in Chapter 5. It had 8 LEDs interfaced to pins 2 to 9 on a standard 25-pin parallel connector. Listing 19.3 implements a simple user application that glows alternate diodes on this parallel port LED board using the ppdev interface. It's the user-space equivalent of the kernel driver developed in Listing 5.6 of Chapter 5.

Listing 19.3. Controlling a Parallel Port LED Board from User Space

#include 
#include 
#include 
#include 
#include 

int main(int argc, char *argv[])
{
  int led_fd;
  char data = 0xAA; /* Bit pattern to glow alternate LEDs */

  /* Open /dev/parport0. This assumes that the LED connector board
     is connected to the first parallel port on your computer */
  if ((led_fd = open("/dev/parport0", O_RDWR)) < 0) {
    perror("Bad Open\n");
    exit(1);
  }

  /* Claim the port */
  if (ioctl(led_fd, PPCLAIM)) {
    perror("Bad Claim\n");
    exit(2);
  }

  /* Set pins to forward direction and write a
     byte to glow alternate LEDs */
  if (ioctl(led_fd, PPWDATA, &data)) {
    perror("Bad Write\n");
    exit(3);
  }

  /* Release the port */
  if (ioctl(led_fd, PPRELEASE)) {
    perror("Bad Release\n");
    exit(4);
  }

  /* Close /dev/parport0 */
  close(led_fd);
}

					  

Previous Page Next Page