Blame sysdeps/unix/sysv/linux/arm/ioperm.c

Packit 6c4009
/* Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Phil Blundell, based on the Alpha version by
Packit 6c4009
   David Mosberger.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library.  If not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
/* I/O port access on the ARM is something of a fiction.  What we do is to
Packit 6c4009
   map an appropriate area of /dev/mem into user space so that a program
Packit 6c4009
   can blast away at the hardware in such a way as to generate I/O cycles
Packit 6c4009
   on the bus.  To insulate user code from dependencies on particular
Packit 6c4009
   hardware we don't allow calls to inb() and friends to be inlined, but
Packit 6c4009
   force them to come through code in here every time.  Performance-critical
Packit 6c4009
   registers tend to be memory mapped these days so this should be no big
Packit 6c4009
   problem.  */
Packit 6c4009
Packit 6c4009
/* Once upon a time this file used mprotect to enable and disable
Packit 6c4009
   access to particular areas of I/O space.  Unfortunately the
Packit 6c4009
   mprotect syscall also has the side effect of enabling caching for
Packit 6c4009
   the area affected (this is a kernel limitation).  So we now just
Packit 6c4009
   enable all the ports all of the time.  */
Packit 6c4009
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <sys/mman.h>
Packit 6c4009
Packit 6c4009
#include <sys/sysctl.h>
Packit 6c4009
Packit 6c4009
#define MAX_PORT	0x10000
Packit 6c4009
Packit 6c4009
static struct {
Packit 6c4009
  unsigned long int	base;
Packit 6c4009
  unsigned long int	io_base;
Packit 6c4009
  unsigned int		shift;
Packit 6c4009
  unsigned int		initdone;	/* since all the above could be 0 */
Packit 6c4009
} io;
Packit 6c4009
Packit 6c4009
#define IO_ADDR(port)	(io.base + ((port) << io.shift))
Packit 6c4009
Packit 6c4009
/*
Packit 6c4009
 * Initialize I/O system.  The io_bae and port_shift values are fetched
Packit 6c4009
 * using sysctl (CTL_BUS, CTL_BUS_ISA, ISA_*).
Packit 6c4009
 */
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
init_iosys (void)
Packit 6c4009
{
Packit 6c4009
  static int iobase_name[] = { CTL_BUS, CTL_BUS_ISA, BUS_ISA_PORT_BASE };
Packit 6c4009
  static int ioshift_name[] = { CTL_BUS, CTL_BUS_ISA, BUS_ISA_PORT_SHIFT };
Packit 6c4009
  size_t len = sizeof(io.base);
Packit 6c4009
Packit 6c4009
  if (! __sysctl (iobase_name, 3, &io.io_base, &len, NULL, 0)
Packit 6c4009
      && ! __sysctl (ioshift_name, 3, &io.shift, &len, NULL, 0))
Packit 6c4009
    {
Packit 6c4009
      io.initdone = 1;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* sysctl has failed... */
Packit 6c4009
  __set_errno (ENODEV);
Packit 6c4009
  return -1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
_ioperm (unsigned long int from, unsigned long int num, int turn_on)
Packit 6c4009
{
Packit 6c4009
  if (! io.initdone && init_iosys () < 0)
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  /* this test isn't as silly as it may look like; consider overflows! */
Packit 6c4009
  if (from >= MAX_PORT || from + num > MAX_PORT)
Packit 6c4009
    {
Packit 6c4009
      __set_errno (EINVAL);
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (turn_on)
Packit 6c4009
    {
Packit 6c4009
      if (! io.base)
Packit 6c4009
	{
Packit 6c4009
	  int fd;
Packit 6c4009
Packit 6c4009
	  fd = __open ("/dev/mem", O_RDWR);
Packit 6c4009
	  if (fd < 0)
Packit 6c4009
	    return -1;
Packit 6c4009
Packit 6c4009
	  io.base =
Packit 6c4009
	    (unsigned long int) __mmap (0, MAX_PORT << io.shift,
Packit 6c4009
					PROT_READ | PROT_WRITE,
Packit 6c4009
					MAP_SHARED, fd, io.io_base);
Packit 6c4009
	  __close (fd);
Packit 6c4009
	  if ((long) io.base == -1)
Packit 6c4009
	    return -1;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
int
Packit 6c4009
_iopl (unsigned int level)
Packit 6c4009
{
Packit 6c4009
    if (level > 3)
Packit 6c4009
      {
Packit 6c4009
	__set_errno (EINVAL);
Packit 6c4009
	return -1;
Packit 6c4009
      }
Packit 6c4009
    if (level)
Packit 6c4009
      {
Packit 6c4009
	return _ioperm (0, MAX_PORT, 1);
Packit 6c4009
      }
Packit 6c4009
    return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_outb (unsigned char b, unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  *((volatile unsigned char *)(IO_ADDR (port))) = b;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_outw (unsigned short b, unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  *((volatile unsigned short *)(IO_ADDR (port))) = b;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
void
Packit 6c4009
_outl (unsigned int b, unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  *((volatile unsigned long *)(IO_ADDR (port))) = b;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
unsigned int
Packit 6c4009
_inb (unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  return *((volatile unsigned char *)(IO_ADDR (port)));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
unsigned int
Packit 6c4009
_inw (unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  return *((volatile unsigned short *)(IO_ADDR (port)));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
unsigned int
Packit 6c4009
_inl (unsigned long int port)
Packit 6c4009
{
Packit 6c4009
  return *((volatile unsigned long *)(IO_ADDR (port)));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
weak_alias (_ioperm, ioperm);
Packit 6c4009
weak_alias (_iopl, iopl);
Packit 6c4009
weak_alias (_inb, inb);
Packit 6c4009
weak_alias (_inw, inw);
Packit 6c4009
weak_alias (_inl, inl);
Packit 6c4009
weak_alias (_outb, outb);
Packit 6c4009
weak_alias (_outw, outw);
Packit 6c4009
weak_alias (_outl, outl);