Parallelport lesen und schreiben

Yoda

[Linux|FreeBSD] - User
Hallo Leute,

ich habe ein Linux-Programm, mit dem man die Pinbelegung des Parallelports auslesen kann und auch schreiben kann.

Das Programm stammt ursprünglich aus dem LINUX-Magazin vom Oktober 1999, Seite 34-36:
Code:
#include <sys/io.h>
#include <unistd.h>
#include <stdio.h>

main(int argc, char* argv[])
{
        int base=atoi(argv[1]);
        int value=atoi(argv[2]);

        // Schreibzugriff frei schalten
        if (ioperm(base,3,1) < 0)
        {
                perror("Fehler: Parallelport kann nicht frei geschaltet werden\n");
        }

        // Port schalten
        outb(value,base);
        outb(0,base+2);

        printf("%i\n",inb(base+1));

        // Port wieder für Schreibzugriffe sperren
        if (ioperm(base,3,0) < 0)
        {
                perror("Fehler: Parallelport kann nicht gesperrt werden\n");
        }
};

Zum setzen der Parallelport-Pins ruft man das Ding z.B. so auf: ./iotest 378 255

Leider kann ich das Programm unter FreeBSD nicht übersetzen
obwohl dort auch der GCC arbeitet:
Code:
# gcc -O2 -o iotest iotest.c
iotest.c:4:20: error: sys/io.h: No such file or directory

Nachdem, was ich hier im Forum gefunden habe (alles von 2006),
müsste es eigentlich gehen, wenn ich das
Code:
#include <sys/io.h>
gegen das hier
Code:
#include <sys/io.h>
austausche...

Leider geht es dann aber trotzdem noch nicht:
Code:
# gcc -O2 -o iotest iotest.c
iotest.c:4:20: error: asm/io.h: No such file or directory

allerdings gibt es soeine Datei im System:
Code:
# find /compat/ /lib* /usr/ /var/ | fgrep '/asm/io.h'
/usr/src/sys/ofed/include/asm/io.h

Ich fahre zur Zeit FreeBSD 9 STABLE (svn r227450):
Code:
# uname -a
FreeBSD erde 9.0-RC2 FreeBSD 9.0-RC2 #0 r227456: Sat Nov 12 01:46:33 CET 2011     root@erde:/usr/obj/usr/src/sys/MYKERNEL  amd64

Kann mir jemand helfen das Ding mit dem nativen FreeBSD-C-Compiler ans laufen zu bringen?
Was ist eigentlich der native C-Compiler unter FreeBSD?
Ist das wirklich der GCC?

Gruß
Yoda
 
Diese Linux API ist unter FreeBSD nicht verfügbar. Sieh dir mal ppi(4) an. Ja der GCC ist der Compiler mit FreeBSD selbst kompiliert wird. FreeBSD 9 steht zusätzlich auch clang zur Verfügung. Das Basissystem und der Kernel lassen sich unter i386 und amd64 auch damit bauen. Eines der Ziele für FreeBSD 10 ist es eine GPL freie Toolchain zu haben, weil die GCC Weiterentwicklung unter GPLv3 geschieht. Der FreeBSD 9 GCC ist ein gepatchter GCC 4.2.1 (letzter GPLv2 Release).
 
Also, zu dem Parallelport unter FreeBSD kann ich nicht viel sagen, aber zu dem Rest:

Code:
/usr/src/sys/ofed/include/asm/io.h
Die Datei gehört zu OFED, dem Infiniband-Stack, und ist daher unter Garantie nicht das, was du möchtest. Du willst eher
Code:
#include <machine/cpufunc.h>
nutzen. Der Header entspricht im Großen und Ganzen der sys/io.h von Linux.

----

yoda schrieb:
Was ist eigentlich der native C-Compiler unter FreeBSD?
Ist das wirklich der GCC?
Die kurze Antwort ist "ja". Die lange Antwort ist ein wenig komplizierter. FreeBSD nutzt seit Version 1.0 den GCC als Systemcompiler, derzeit in Version 4.2. Nur möchte man aus mehreren Gründen davon weg:
- Neuere GCC-Versionen stehen unter der GPL3 und können daher nicht importiert werden.
- Man will gern ein FreeBSD ohne GPL erreichen.
- Der GCC hat allgemein einen durchwachsenen Ruf.
Daher wird derzeit daran gearbeitet Clang (http://clang.llvm.org) zum Systemcompiler zu machen. In FreeBSD 9 ist er bereits enthalten und wird als /usr/bin/clang installiert. Man kann ihn allerdings zum Systemcompiler machen, wenn man eine Option in der /etc/src.conf setzt und das System neubaut. FreeBSD 10 - was ja noch ferne Zukunft ist - soll dann Clang als Systemcompiler einsetzen und GCC nur noch optional oder gar nicht mehr beinhalten.
 
Wenn in Zukunft der "clang" verwendet werden soll, dann ist es am besten, wenn dieses
Script auch für den "clang" prepariert wird.

Danke Yamagi für den Tipp, leider bekomme ich jetzt deutlich mehr Fehlermeldungen.
Dafür brauche ich noch ein paar Programmierer-Tipps.

Code:
# clang -o iotest iotest.c
In file included from iotest.c:4:
/usr/include/machine/cpufunc.h:43:2: error: #error this file needs sys/cdefs.h as a prerequisite
#error this file needs sys/cdefs.h as a prerequisite
 ^
/usr/include/machine/cpufunc.h:675:1: error: unknown type name 'u_int'
u_int   bsfl(u_int mask);
^
/usr/include/machine/cpufunc.h:675:12: error: unknown type name 'u_int'
u_int   bsfl(u_int mask);
             ^
/usr/include/machine/cpufunc.h:676:1: error: unknown type name 'u_int'
u_int   bsrl(u_int mask);
^
/usr/include/machine/cpufunc.h:676:12: error: unknown type name 'u_int'
u_int   bsrl(u_int mask);
             ^
/usr/include/machine/cpufunc.h:678:15: error: unknown type name 'u_int'
void    do_cpuid(u_int ax, u_int *p);
                 ^
/usr/include/machine/cpufunc.h:678:25: error: unknown type name 'u_int'
void    do_cpuid(u_int ax, u_int *p);
                           ^
/usr/include/machine/cpufunc.h:682:1: error: unknown type name 'u_char'; did you mean 'char'?
u_char  inb(u_int port);
^
/usr/include/machine/cpufunc.h:682:12: error: unknown type name 'u_int'
u_char  inb(u_int port);
            ^
/usr/include/machine/cpufunc.h:683:1: error: unknown type name 'u_int'
u_int   inl(u_int port);
^
/usr/include/machine/cpufunc.h:683:11: error: unknown type name 'u_int'
u_int   inl(u_int port);
            ^
/usr/include/machine/cpufunc.h:684:11: error: unknown type name 'u_int'
void    insb(u_int port, void *addr, size_t count);
             ^
/usr/include/machine/cpufunc.h:684:35: error: unknown type name 'size_t'
void    insb(u_int port, void *addr, size_t count);
                                     ^
/usr/include/machine/cpufunc.h:685:11: error: unknown type name 'u_int'
void    insl(u_int port, void *addr, size_t count);
             ^
/usr/include/machine/cpufunc.h:685:35: error: unknown type name 'size_t'
void    insl(u_int port, void *addr, size_t count);
                                     ^
/usr/include/machine/cpufunc.h:686:11: error: unknown type name 'u_int'
void    insw(u_int port, void *addr, size_t count);
             ^
/usr/include/machine/cpufunc.h:686:35: error: unknown type name 'size_t'
void    insw(u_int port, void *addr, size_t count);
                                     ^
/usr/include/machine/cpufunc.h:687:1: error: unknown type name 'register_t'
register_t      intr_disable(void);
^
/usr/include/machine/cpufunc.h:688:19: error: unknown type name 'register_t'
void    intr_restore(register_t rf);
                     ^
fatal error: too many errors emitted, stopping now [-ferror-limit=]
20 errors generated.

...fehlt da vielleicht noch eine *.h-Datei?

Kann da jemand mit Programmiererfahrungen etwas zu sagen?

Gruß
Yoda
 
Hallo Leute,

ich bin schon etwas weiter gekommen! ;-)

Es fehlten tatsächlich noch ein paar *.h-Dateien...

Mit diesem Code halten sich die Fehlermeldungen und Warnings in Grenzen:
Code:
#include <sys/cdefs.h>
#include <unistd.h>
#include <stdio.h>
#include <machine/cpufunc.h>
#include <machine/sysarch.h>

#define ioperm(startport,length,enable)

int main(int argc, char* argv[])
{
        int port_base=atoi(argv[1]);
        int port_value=atoi(argv[2]);

        // Schreibzugriff frei schalten
        ioperm(port_base,3,1)

        // Port schalten
        outb(port_base,port_value);
        outb(port_base+2,0);

        printf("%i\n",inb(port_base+1));

        // Port wieder für Schreibzugriffe sperren
        ioperm(port_base,3,0)
};

Wenn ich den mit dem GCC übersetze, dann klappt jetzt alles.

Allerdings, wenn ich den mit clang übersetze, sieht das so aus:
Code:
# clang -o iotest iotest.c
iotest.c:12:23: warning: implicit declaration of function 'atoi' is invalid in C99 [-Wimplicit-function-declaration]
        int port_base=atoi(argv[1]);
                      ^
1 warning generated.

Was muss ich für "clang"/"C99" an Stelle von "atoi" (und wie) verwenden?

Leider kann ich jetzt aber den Fehler-Code nicht mehr abfangen...

Kann mir da jemand helfen?

Gruß
Yoda
 
Einer der subjektiv größten Vorteile des clang Compiler sind die schönen leserlichen Fehlermeldungen. Da steht doch ganz klar das atoi nicht deklariert wurde. Also bietet sich ein man atoi an. Unter SYNOPSIS steht welche Header eingebunden werden müssen.
 
Einer der subjektiv größten Vorteile des clang Compiler sind die schönen leserlichen Fehlermeldungen. Da steht doch ganz klar das atoi nicht deklariert wurde. Also bietet sich ein man atoi an. Unter SYNOPSIS steht welche Header eingebunden werden müssen.

Danke! Das compilieren klappt jetzt ohne Meldungen... :D

Aber ist das normal, dass das Ding beim ausführen einen core-dump wirft?
Dabei ist es egal ob ich den GCC oder clang verwende:
Code:
# ./iotest 378 85
Bus error: 10 (core dumped)

Im System befindet sich ein Parallelport auf dem Board....
eigentlich sollte das dann auch funktionieren.
Unter Linux hat es jedenfalls funktioniert.

Hat jemand noch eine Idee?

Gruß
Yoda
 
Hast du mal das Corefile untersucht, warum es überhaupt erstellt wird? Normal wird das nicht sein. ;)
 
Natürlich ist das nicht normal das eine funktionierende Anwendung einen core dumped. Okay es mag Ausnahmen wie Emacs geben. Das sich bootstrapped indem es elisp files läd und läd und läd bis es sich vollgefressen hat. Anschließend dumped es core und belebt diesen Coredump zu einem Untoten Image wieder. Von solchen Perversionen abgesehen ist es nicht normal.
 
Code:
#define ioperm(startport,length,enable)
Verzeih meine Direktheit, aber du hast keine Ahnung, was du tust.
Ganz kurz: out ist ein privilegierter Befehl. Auf Linux kann man sich mittels ioperm das Recht holen, ihn zu benutzen. Das Makro bildet das einfach auf nichts ab. Auf FreeBSD gibt es ioperm nicht.
Es wurde bereits vorgeschlagen, ppi(4) zu verwenden, was ein vernünftiger Vorschlag ist.
 
Verzeih meine Direktheit, aber du hast keine Ahnung, was du tust.
Ganz kurz: out ist ein privilegierter Befehl. Auf Linux kann man sich mittels ioperm das Recht holen, ihn zu benutzen. Das Makro bildet das einfach auf nichts ab. Auf FreeBSD gibt es ioperm nicht.
Es wurde bereits vorgeschlagen, ppi(4) zu verwenden, was ein vernünftiger Vorschlag ist.

In diesem Fall hast Du recht.
Diesen "Schnipsel" habe ich irgendwo aufgeschnappt,
damit gingen dann ein paar Fehlermeldungen weg...

Um mit ppi etwas anfangen zu können,
bräuchte ich ein Beispielcode.

Gruß
Yoda
 
Hast du mal das Corefile untersucht, warum es überhaupt erstellt wird? Normal wird das nicht sein. ;)

Nein, womit kann ich dort rein schauen?
Aber ich glaube das Tron wohl Recht hat, das der Code einer gründlichen Überarbeitung bedarf...
Linux-GCC-Code ist wohl doch nicht so einfach auf FreeBSD-clang zu portieren.

Gruß
Yoda
 
Du musst das Binary mit -g kompilieren und kannst dann mit "gdb <binary> <corefile>" das Corefile untersuchen.
Dann kannst du erstmal mit dem gdb Befehl "backtrace" gucken, wo es schiefgegangen ist.
Ob der gdb mit Binaries funktioniert, die mit clang kompiliert wurden, weiß ich allerdings nicht.

EDIT: scheint zusammen zu klappen.
 
Du musst das Binary mit -g kompilieren und kannst dann mit "gdb <binary> <corefile>" das Corefile untersuchen.
Dann kannst du erstmal mit dem gdb Befehl "backtrace" gucken, wo es schiefgegangen ist.
Ob der gdb mit Binaries funktioniert, die mit clang kompiliert wurden, weiß ich allerdings nicht.

EDIT: scheint zusammen zu klappen.

Danke!
 
Die Selbstbeschreibung als User passt seitens Yoda. Lies die Doku und guck dir avrdude an, wenn du Beispielcode dafür brauchst wie du mit dem Parallelport kommunizierst. Klemm nen paar Taster und LEDs an den Parallelport eines entbehrlichen Rechners und probier es aus. Copy und Paste funktioniert nicht über OS Grenzen hinweg. Erst recht nicht mit solchen Lowlevel APIs.
 
Schnell zusammengehackt und mangels Hardware ungetestet:
Code:
#include <fcntl.h>
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ppi_port;

int
main(int argc, char *argv[])
{
	char ppi_dev[64];
	int status;
	int value;

	if (argc < 3)
	{
		printf("Usuage: %s device value\n", argv[0]);
		exit(1);
	}

    strlcpy(ppi_dev, argv[1], sizeof(ppi_dev));
	value = atoi(argv[2]);

	/* Open the device */
	ppi_port = open(ppi_dev, O_RDWR);

	if (ppi_port < 0)
	{
		printf("Could not open %s\n", ppi_dev);
		exit(1);
	}

	/* Set the data register */
	ioctl(ppi_port, PPISDATA, &value);

	/* Set the control register */
	ioctl(ppi_port, PPISCTRL, 0);

	/* Get the status register */
	ioctl(ppi_port, PPIGSTATUS, &status);

	/* And print it */
	printf("%X\n", status);

    return 0;
}
Da es meine erste Arbeit mit dem Geekport ist, auch keinerlei Garantie für korrekte Funktion... Der Aufruf ist dann z.B.: ./geekport /dev/ppi0 85".

EDIT: "ioctl(ppi_port, PPISCTRL, 0);" wird wahrscheinlich so explodieren, da dort statt eines Integers ein Pointer auf einen u_int8_t übergeben werden musss... Gesehen von Tron.
 
Zuletzt bearbeitet:
Schnell zusammengehackt und mangels Hardware ungetestet:
Code:
...
Da es meine erste Arbeit mit dem Geekport ist, auch keinerlei Garantie für korrekte Funktion... Der Aufruf ist dann z.B.: ./geekport /dev/ppi0 85".

EDIT: "ioctl(ppi_port, PPISCTRL, 0);" wird wahrscheinlich so explodieren, da dort statt eines Integers ein Pointer auf einen u_int8_t übergeben werden musss... Gesehen von Tron.

Danke Yamagi! :D

Das reagiert schonmal ohne angeschlossener HW (auf die HW warte ich noch, kommt dem nächst mit der POST), mit einem EXIT-Code "0".
Das ist schonmal sehr schön! :D

Allerdings verstehe ich nicht, was genau umgewandelt werden muss.
Ist das die "0" in der Klammer oder der Rückgabewert...
etwas anderes wird in der Zeile ja garnicht übergeben :confused:

Kannst Du mir mal den Link zum Code von Tron, wo Du das gesehen hast, geben?

Gruß
Yoda


EDIT:
Habs gefunden, es ist die "0":
Code:
# fgrep -rH PPISCTRL /usr/include/      
/usr/include/dev/ppbus/ppi.h:#define	PPISCTRL	_IOW('P', 18, u_int8_t)

Jetzt muss ich nur noch raus bekommen, wie man "int" nach "u_int8_t" umwandelt... ;-)



EDIT2:
Also, wenn ich das richrig verstanden habe, dann ist u_int8_t "unsigned integers of 8bits",
also eine Zahl zwischen "0" und "255"...
Damit sollte das bei "0" ja eigentlich egal sein... oder?
Denn dort wird ja keine Variable übergeben.

Gruß
Yoda
 
Zuletzt bearbeitet:
perfekt

Danke nochmal an Yamagi
Das macht, soweit ich das jetzt beurteilen kann, alles richtig.

Ich habe den Code leicht angepasst,
so das ich jetzt den Stand der Datenregister ausgegeben bekomme:
Code:
#include <fcntl.h>
#include <dev/ppbus/ppi.h>
#include <dev/ppbus/ppbconf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int ppi_port;

int
main(int argc, char *argv[])
{
        char ppi_dev[64];
        int status=0;
        int value=0;
        int vdaten=0;
        int ndaten=0;

        if (argc != 3)
        {
                printf("Usuage: %s device value\n", argv[0]);
                printf("z.B.  : %s /dev/ppi0 85\n", argv[0]);
                exit(1);
        }

        strlcpy(ppi_dev, argv[1], sizeof(ppi_dev));
        value = atoi(argv[2]);

        /* Open the device */
        ppi_port = open(ppi_dev, O_RDWR);

        if (ppi_port < 0)
        {
                printf("Could not open %s\n", ppi_dev);
                exit(1);
        }

        /* Get the data register */
        ioctl(ppi_port, PPIGDATA, &vdaten);

        /* Set the data register */
        ioctl(ppi_port, PPISDATA, &value);

        /* Set the control register */
        ioctl(ppi_port, PPISCTRL, 0);

        /* Get the status register */
        ioctl(ppi_port, PPIGSTATUS, &status);

        /* Get the data register */
        ioctl(ppi_port, PPIGDATA, &ndaten);

        /* And print it */
        printf("%u: %u -> %u\n", status, vdaten, ndaten);

    return 0;
}

Eine Letzte Frage habe ich noch:
Was kann ich an dem Rückgabewert des Statusregisters erkennen?

Gruß
Yoda
 
Zuletzt bearbeitet:
Es ist "man ppi". Wobei da auch nicht wahnsinnig viel drinsteht... Um die Frage beantworten zu können, muss man etwas mehr Ahnung als ich vom Parallelport haben.
 
gelöst

Es ist "man ppi". Wobei da auch nicht wahnsinnig viel drinsteht... Um die Frage beantworten zu können, muss man etwas mehr Ahnung als ich vom Parallelport haben.

Danke!

Ich glaube ich kann es mir jetzt zusammenreimen.

Der Parallelport besteht aus 4 Status-Pins, 8 Daten-Pins und 4 Kontroll-Pins...
möglicherweise ist das der aktuelle Wert von den Status-Pins.

Gruß
Yoda
 
Zurück
Oben