Back to index

glibc  2.9
ioperm.c
Go to the documentation of this file.
00001 /* Copyright (C) 1998, 1999, 2003, 2005, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contributed by Phil Blundell, based on the Alpha version by
00004    David Mosberger.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 /* I/O port access on the ARM is something of a fiction.  What we do is to
00022    map an appropriate area of /dev/mem into user space so that a program
00023    can blast away at the hardware in such a way as to generate I/O cycles
00024    on the bus.  To insulate user code from dependencies on particular
00025    hardware we don't allow calls to inb() and friends to be inlined, but
00026    force them to come through code in here every time.  Performance-critical
00027    registers tend to be memory mapped these days so this should be no big
00028    problem.  */
00029 
00030 /* Once upon a time this file used mprotect to enable and disable
00031    access to particular areas of I/O space.  Unfortunately the
00032    mprotect syscall also has the side effect of enabling caching for
00033    the area affected (this is a kernel limitation).  So we now just
00034    enable all the ports all of the time.  */
00035 
00036 #include <errno.h>
00037 #include <fcntl.h>
00038 #include <stdio.h>
00039 #include <ctype.h>
00040 #include <stdlib.h>
00041 #include <string.h>
00042 #include <unistd.h>
00043 
00044 #include <sys/types.h>
00045 #include <sys/mman.h>
00046 
00047 #include <linux/version.h>
00048 #include <sys/sysctl.h>
00049 
00050 #define PATH_ARM_SYSTYPE    "/etc/arm_systype"
00051 #define PATH_CPUINFO        "/proc/cpuinfo"
00052 
00053 #define MAX_PORT     0x10000
00054 
00055 static struct {
00056   unsigned long int  base;
00057   unsigned long int  io_base;
00058   unsigned int              shift;
00059   unsigned int              initdone;     /* since all the above could be 0 */
00060 } io;
00061 
00062 #define IO_BASE_FOOTBRIDGE  0x7c000000
00063 #define IO_SHIFT_FOOTBRIDGE 0
00064 
00065 static struct platform {
00066   const char         *name;
00067   unsigned long int  io_base;
00068   unsigned int              shift;
00069 } platform[] = {
00070   /* All currently supported platforms are in fact the same. :-)  */
00071   {"Chalice-CATS",   IO_BASE_FOOTBRIDGE,  IO_SHIFT_FOOTBRIDGE},
00072   {"DEC-EBSA285",    IO_BASE_FOOTBRIDGE,  IO_SHIFT_FOOTBRIDGE},
00073   {"Corel-NetWinder",       IO_BASE_FOOTBRIDGE,  IO_SHIFT_FOOTBRIDGE},
00074   {"Rebel-NetWinder",       IO_BASE_FOOTBRIDGE,  IO_SHIFT_FOOTBRIDGE},
00075 };
00076 
00077 #define IO_ADDR(port)       (io.base + ((port) << io.shift))
00078 
00079 /*
00080  * Initialize I/O system.  There are several ways to get the information
00081  * we need.  Each is tried in turn until one succeeds.
00082  *
00083  * 1. Sysctl (CTL_BUS, CTL_BUS_ISA, ISA_*).  This is the preferred method
00084  *    but not all kernels support it.
00085  *
00086  * 2. Read the value (not the contents) of symlink PATH_ARM_SYSTYPE.
00087  *    - If it matches one of the entries in the table above, use the
00088  *      corresponding values.
00089  *    - If it begins with a number, assume this is a previously
00090  *      unsupported system and the values encode, in order,
00091  *      "<io_base>,<port_shift>".
00092  *
00093  * 3. Lookup the "system type" field in /proc/cpuinfo.  Again, if it
00094  *    matches an entry in the platform[] table, use the corresponding
00095  *    values.
00096  */
00097 
00098 /* The Linux kernel headers renamed this constant between 2.5.26 and
00099    2.5.27.  It was backported to 2.4 between 2.4.22 and 2.4.23.  */
00100 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,23)
00101 # define BUS_ISA CTL_BUS_ISA
00102 #endif
00103 
00104 static int
00105 init_iosys (void)
00106 {
00107   char systype[256];
00108   int i, n;
00109   static int iobase_name[] = { CTL_BUS, BUS_ISA, BUS_ISA_PORT_BASE };
00110   static int ioshift_name[] = { CTL_BUS, BUS_ISA, BUS_ISA_PORT_SHIFT };
00111   size_t len = sizeof(io.base);
00112 
00113   if (! __sysctl (iobase_name, 3, &io.io_base, &len, NULL, 0)
00114       && ! __sysctl (ioshift_name, 3, &io.shift, &len, NULL, 0))
00115     {
00116       io.initdone = 1;
00117       return 0;
00118     }
00119 
00120   n = __readlink (PATH_ARM_SYSTYPE, systype, sizeof (systype) - 1);
00121   if (n > 0)
00122     {
00123       systype[n] = '\0';
00124       if (isdigit (systype[0]))
00125        {
00126          if (sscanf (systype, "%li,%i", &io.io_base, &io.shift) == 2)
00127            {
00128              io.initdone = 1;
00129              return 0;
00130            }
00131          /* else we're likely going to fail with the system match below */
00132        }
00133     }
00134   else
00135     {
00136       FILE * fp;
00137 
00138       fp = fopen (PATH_CPUINFO, "r");
00139       if (! fp)
00140        return -1;
00141       while ((n = fscanf (fp, "Hardware\t: %256[^\n]\n", systype))
00142             != EOF)
00143        {
00144          if (n == 1)
00145            break;
00146          else
00147            fgets_unlocked (systype, 256, fp);
00148        }
00149       fclose (fp);
00150 
00151       if (n == EOF)
00152        {
00153          /* this can happen if the format of /proc/cpuinfo changes...  */
00154          fprintf (stderr,
00155                  "ioperm: Unable to determine system type.\n"
00156                  "\t(May need " PATH_ARM_SYSTYPE " symlink?)\n");
00157          __set_errno (ENODEV);
00158          return -1;
00159        }
00160     }
00161 
00162   /* translate systype name into i/o system: */
00163   for (i = 0; i < sizeof (platform) / sizeof (platform[0]); ++i)
00164     {
00165       if (strcmp (platform[i].name, systype) == 0)
00166        {
00167          io.shift = platform[i].shift;
00168          io.io_base = platform[i].io_base;
00169          io.initdone = 1;
00170          return 0;
00171        }
00172     }
00173 
00174   /* systype is not a known platform name... */
00175   __set_errno (ENODEV);
00176   return -1;
00177 }
00178 
00179 int
00180 _ioperm (unsigned long int from, unsigned long int num, int turn_on)
00181 {
00182   if (! io.initdone && init_iosys () < 0)
00183     return -1;
00184 
00185   /* this test isn't as silly as it may look like; consider overflows! */
00186   if (from >= MAX_PORT || from + num > MAX_PORT)
00187     {
00188       __set_errno (EINVAL);
00189       return -1;
00190     }
00191 
00192   if (turn_on)
00193     {
00194       if (! io.base)
00195        {
00196          int fd;
00197 
00198          fd = __open ("/dev/mem", O_RDWR);
00199          if (fd < 0)
00200            return -1;
00201 
00202          io.base =
00203            (unsigned long int) __mmap (0, MAX_PORT << io.shift,
00204                                    PROT_READ | PROT_WRITE,
00205                                    MAP_SHARED, fd, io.io_base);
00206          __close (fd);
00207          if ((long) io.base == -1)
00208            return -1;
00209        }
00210     }
00211 
00212   return 0;
00213 }
00214 
00215 
00216 int
00217 _iopl (unsigned int level)
00218 {
00219     if (level > 3)
00220       {
00221        __set_errno (EINVAL);
00222        return -1;
00223       }
00224     if (level)
00225       {
00226        return _ioperm (0, MAX_PORT, 1);
00227       }
00228     return 0;
00229 }
00230 
00231 
00232 void
00233 _outb (unsigned char b, unsigned long int port)
00234 {
00235   *((volatile unsigned char *)(IO_ADDR (port))) = b;
00236 }
00237 
00238 
00239 void
00240 _outw (unsigned short b, unsigned long int port)
00241 {
00242   *((volatile unsigned short *)(IO_ADDR (port))) = b;
00243 }
00244 
00245 
00246 void
00247 _outl (unsigned int b, unsigned long int port)
00248 {
00249   *((volatile unsigned long *)(IO_ADDR (port))) = b;
00250 }
00251 
00252 
00253 unsigned int
00254 _inb (unsigned long int port)
00255 {
00256   return *((volatile unsigned char *)(IO_ADDR (port)));
00257 }
00258 
00259 
00260 unsigned int
00261 _inw (unsigned long int port)
00262 {
00263   return *((volatile unsigned short *)(IO_ADDR (port)));
00264 }
00265 
00266 
00267 unsigned int
00268 _inl (unsigned long int port)
00269 {
00270   return *((volatile unsigned long *)(IO_ADDR (port)));
00271 }
00272 
00273 weak_alias (_ioperm, ioperm);
00274 weak_alias (_iopl, iopl);
00275 weak_alias (_inb, inb);
00276 weak_alias (_inw, inw);
00277 weak_alias (_inl, inl);
00278 weak_alias (_outb, outb);
00279 weak_alias (_outw, outw);
00280 weak_alias (_outl, outl);