Forum: PC-Programmierung C_Linux_PCI-Buskommunikation


von Alibaba (Gast)


Lesenswert?

System Linux
kernel 2.6.27.7


Hallo,

ich habe ein verständnissproblem mit dem Datentransfer, zwischen meiner 
Messkarte die auf dem Pci-X Bus hängt.

Und zwar geht es mir speziell um den Zugriff auf die 
PCI_Base1+0x00D8(r/w).
Ich habe bereits auf die Karte schreiben und lesen können dies geschah 
allerdings mit Treiberbefehlen die bereits vorhanden waren
(z.B. int rc = ioctl (hnd, ICS554_GET_IoInfo,pInfo);).
Bin momentan ratlos wie ich auf die PCI_Base1+0x00D8(r/w) schreiben 
kann.
Ich finde keinen Befehl oder ähnliches.

Jemand eine Idee?? Für tipps bin ich dankbar.

von Oliver R. (superberti)


Lesenswert?

Hi,

> Bin momentan ratlos wie ich auf die PCI_Base1+0x00D8(r/w) schreiben
> kann.
> Ich finde keinen Befehl oder ähnliches.

Wenn Dein Treiber diese Möglichkeit nicht bietet, dann geht es so 
einfach auch nicht.

>
> Jemand eine Idee?? Für tipps bin ich dankbar.

Du kommst dann nicht umhin

- den Treiber mit einem neuen IOCTL entsprechend anzupassen (falls du 
die Quellen dafür hast)

- selbst einen Treiber zu schreiben (dann bräuchte man das genaue 
Datenblatt für die Karte)

Das Standardwerk für solche Zwecke ist "Linux device drivers" in der 
dritten Auflage. Wahrscheinlich würde für Deine Zwecke ein einfacher 
char-driver reichen.
Es gibt allerdings auch inzwischen ein User-Mode-Treiber Framework, was 
für Deine Zwecke sicherlich einfacher ist, ich habe die Entwicklung 
hierbei allerdings nicht verfolgt und kann dazu wenig sagen.

Gruß, Oliver

von Puppe (Gast)


Lesenswert?

Das wesentlich bessere Buch ist "Essential Linux Device Drivers" - wenn 
man sich schon so ein Buch leisten möchte.

von Oliver R. (superberti)


Lesenswert?

Hallo,

> Das wesentlich bessere Buch ist "Essential Linux Device Drivers" - wenn
> man sich schon so ein Buch leisten möchte.

interessant, dieses Buch kannte ich noch gar nicht. Zu meiner 
Verteidigung muss ich allerdings sagen, dass dieses Buch auch noch gar 
nicht existierte, als ich meine letzten Treiber schrieb.
Habe ich glatt in meine Bestellliste aufgenommen!

Gruß, Oliver

von Alibaba (Gast)


Lesenswert?

Oliver R. schrieb:
> Wenn Dein Treiber diese Möglichkeit nicht bietet, dann geht es so
> einfach auch nicht.

Mein treiber bietet diese Möglichkeit. Ich besitze das 
Software/Operating Manuel allerdings kann ich nicht finden wie ich 
speziell auf diese Base/adresse schreiben kann. Normalerweise gibt es 
Funktionen wie

"StatusGet(hDevice, &Stat);" // diese fkt funktioniert mit IOCTL

mit denen kann man auf bestimmte Registerbänke zugreifen.

wie schreibt man einen neuen IOCTL?? muss ich unbedingt ein buch lesen?
oder gibt es Bibs oder ähnlichen mit Fkt. wie

PCIset(PCIadr,BaseNr,W/R?,ÜbergabeWert);

oder  stelle ich mir das zu leicht vor??

von Oliver R. (superberti)


Lesenswert?

Hi,

> Oliver R. schrieb:
>> Wenn Dein Treiber diese Möglichkeit nicht bietet, dann geht es so
>> einfach auch nicht.
>
> Mein treiber bietet diese Möglichkeit. Ich besitze das

Offensichtlich bietet er doch KEINE Möglichkeit auf diese Adresse zu 
schreiben/lesen.

> Software/Operating Manuel allerdings kann ich nicht finden wie ich
> speziell auf diese Base/adresse schreiben kann. Normalerweise gibt es
> Funktionen wie
>
> "StatusGet(hDevice, &Stat);" // diese fkt funktioniert mit IOCTL
>
> mit denen kann man auf bestimmte Registerbänke zugreifen.
>
> wie schreibt man einen neuen IOCTL?? muss ich unbedingt ein buch lesen?

Du kannst auf die Hardware NUR mit einem Treiber zugreifen! Einen neuen 
IOCTL schreibst Du, indem du den Treiber veränderst, neu kompilierst und 
das Treibermodul austauschst bzw. den Kernel. Dazu brauchst Du die 
Treiberquellen und ein Kernel-compilierfähiges System.
Ja, ein Buch hilft dabei weiter, es gibt aber auch im Netz viele 
Anleitungen. Im Treiber könnte die IOCTL-Behandlungsfunktion ungefähr so 
aussehen:

/*
 * ioctl entry point.
 */
static int mein_ioctl(struct inode* inode, struct file* file, unsigned 
int cmd, unsigned long arg )
{
[...]
}

Diese Stelle muss man finden und um einen IOCTL ergänzen, der auf das 
gewünschte Register schreibt/liest.

> oder gibt es Bibs oder ähnlichen mit Fkt. wie
>
> PCIset(PCIadr,BaseNr,W/R?,ÜbergabeWert);

Auf die Hardware direkt zugreifen darf nur der Kernel und seine 
Treibermodule. Aus Deiner Anwendung (die im User-Mode läuft) heraus geht 
das nicht (Ausnahmen s. meine erste Mail).

>
> oder  stelle ich mir das zu leicht vor??

Bedauerlicherweise: Ja!

Gruß,

Oliver

von Alibaba (Gast)


Lesenswert?

Oliver R. schrieb:
> Im Treiber könnte die IOCTL-Behandlungsfunktion ungefähr so
> aussehen:
>
> /*
>  * ioctl entry point.
>  */
.
.
.

Ich glaube da habe ich etwas gefunden im header hardwlow.h.

int ADCOverRangeReset (HANDLE hnd, STATUS *pStat)
{
  int rc;

  rc = ioctl (hnd, Hardw_SET_ADCOverRangeReset, pStat);
  Hardw_FORMAT_RESULT(hnd,rc,"ADCOverRangeReset");
  return((int)rc);
}

könnte das eine Behandlungstrucktur sein?
"SET_ADCOverRangeReset" finde ich ich in einen anderen header nämlich im

Hardwioctl.h
#define..
.
.

"#define Hardw_GET_OverflowDone          _IOW(Hardw_IO_MAGIC, 134, u32)"
.
.


und in der Hardwload.c
case
.
.

case Hardw_SET_ADCOverRangeReset:
{
  if(copy_from_user(&val64,(void*)arg,sizeof(u64))) {
  return -EFAULT;
}
 PDEBUG("hardw(ioctl_Hardw):    SET_ADCOverRangeReset0x%llx\n",val64);

WRITE_ICSDEV_U32(board, STATUS_OFFSET, (u32)(0xFLL & val64));
    break;
  }

.
.


Ich finde allerdings nichts was auf eine PCI_Adresse hindeutet oder irre 
ich mich da??

von Oliver R. (superberti)


Lesenswert?

Hi nochmal,
>
> Ich glaube da habe ich etwas gefunden im header hardwlow.h.
>
> int ADCOverRangeReset (HANDLE hnd, STATUS *pStat)
> {
>   int rc;
>
>   rc = ioctl (hnd, Hardw_SET_ADCOverRangeReset, pStat);
>   Hardw_FORMAT_RESULT(hnd,rc,"ADCOverRangeReset");
>   return((int)rc);
> }

Nein, das ist eine Usermode-Funktion, da ioctl() auch eine 
Usermode-Funktion ist. Sie dient zur Kommunikation mit dem Treiber bei 
"unüblichen" Kommandos, sprich Spezialfunktionen, die über das 
Lesen/Schreiben hinausgehen. Zur Identifikation benutzen aber sowohl 
Usermode-Programm als auch der Treiber eine gemeinsame IOCTL-Definition 
(s.u.)

>
> könnte das eine Behandlungstrucktur sein?
> "SET_ADCOverRangeReset" finde ich ich in einen anderen header nämlich im
>
> Hardwioctl.h
> #define..
> .
> .
>
> "#define Hardw_GET_OverflowDone          _IOW(Hardw_IO_MAGIC, 134, u32)"
> .

Hier wird in der Tat ein IOCTL definiert, hier könntest Du einen eigenen 
IOCTL hinzufügen.


>
> und in der Hardwload.c
> case
> .
> .
>
> case Hardw_SET_ADCOverRangeReset:
> {
>   if(copy_from_user(&val64,(void*)arg,sizeof(u64))) {
>   return -EFAULT;
> }
>  PDEBUG("hardw(ioctl_Hardw):    SET_ADCOverRangeReset0x%llx\n",val64);
>
> WRITE_ICSDEV_U32(board, STATUS_OFFSET, (u32)(0xFLL & val64));
>     break;
>   }
>

Ja, das könnte die IOCTL-Behandlungsfunktion sein. Du hast leider nicht 
den Funktionskopf mit angehangen.

>
> Ich finde allerdings nichts was auf eine PCI_Adresse hindeutet oder irre
> ich mich da??

Ich kenne ja den Treiber nicht, aber hier sieht es so aus, als ob die 
Variable "board" auf die Basisadresse der PCI-Karte zeigt. Deren 
Register/Speicher werden in den Adressraum des Kernels eingeblendet. 
"STATUS_OFFSET" scheint dann der Offset zu "board" zu sein und 
adressiert ein bestimmtes Register. Du könntest also evtl. mit 
"WRITE_ICSDEV_U32(board,0x00D8,Wert)" einen uint an die von Dir 
gewünschte Adresse schreiben. Schau im Code nach, was die Funktion genau 
macht.

Gruß,

Oliver

von Alibaba (Gast)


Lesenswert?

Oliver R. schrieb:
> Ja, das könnte die IOCTL-Behandlungsfunktion sein. Du hast leider nicht
> den Funktionskopf mit angehangen.

danke für die Erklärung.

Hier noch der Kopf.
switch (cmd)
{
 case..:
 ..
 case
 ..
}

von Alibaba (Gast)


Lesenswert?

Ich glaube ich habe da was gefunden:

//IOCTL wird definiert/////////////////////////////////7

Hardw.h
#define FIFO_CLOCK_OFFSET                    0xD8




//IOCTL-Behandlungsfunktion////////////////////////////
Hardwload.h:

switch (cmd)
{


  case Hardw_SET_FifoClock:
  {
    u32 dwval, i;

    if(copy_from_user(&val64,(void*)arg,sizeof(u64))) {
      return -EFAULT;
    }
    PDEBUG("Hardw(ioctl_Hardw): Hardw_SET_FifoClock 0x%llx\n",val64);
    dwval = (u32)(0xFFFFFFFFLL & val64);
    for(i = 0; i < 32; i++) {
      WRITE_ICSDEV_U32(board, FIFO_CLOCK_OFFSET, (u32)(0x1L & dwval));
      udelay(10);
      dwval >>= 1;
    }
    break;
  }



}


//Usermode-Funktion///////////////////////////////////////
Hardwlow.c
int HardwFifoClockSet (HANDLE hnd, unsigned long long *pClock)
{
  int rc;

  rc = ioctl (hnd, Hardw_SET_FifoClock, pClock);
  ICS_FORMAT_RESULT(hnd,rc,"FifoClockSet");
  return((int)rc);
}



es deutet allerdings nichts bis auf
#define FIFO_CLOCK_OFFSET                    0xD8

darauf hin, das es die BASE1+0X00D8 ist(FifoLevelControl).
Könnte trotzdem so sein das sei was ich suche??

Nochmal danke für die gute Hilfe

von Oliver R. (superberti)


Lesenswert?

Hallo,

>
> es deutet allerdings nichts bis auf
> #define FIFO_CLOCK_OFFSET                    0xD8
>
> darauf hin, das es die BASE1+0X00D8 ist(FifoLevelControl).
> Könnte trotzdem so sein das sei was ich suche??

Schwer zu sagen, FIFO clock und FIFO level hören sich ja doch 
unterschiedlich an. Schau Dir mal im Treiber an, wo die Variable "board" 
belegt wird. Evtl. in einer Zeile wie dieser:

board = pci_resource_start(dev, BAR3);

Gruß,

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.