Forum: PC-Programmierung Linux Treiber bauen


von Tobias P. (hubertus)


Lesenswert?

Hallo,
es geht zwar um Rmbedded Linux, aber da es nicht sehr embeddedspezifisch 
ist passts hier vielleicht besser.

Also, ich habe einen ARM Prozessor mit einem lauffähigen embedded Linux. 
Uart, Ethernet etc. geht. Diese Devices sind auch alle im Device Tree 
vermerkt und unter /dev habe ich entsprechende Devicefiles, z.B. für den 
Uart.

Am Prozessor ist noch ein kleiner FPGA dran, wo ich ein spezielles 
Device mit memory mapped IO synthetisiere. Jetzt muss ich natürlich, um 
das Ding aus Linux heraus ansprechen zu können, einen Treiber dafür 
haben.

Dazu ein paar Fragen:

Muss ich das "Spezialdevice" zwingend mit in den Device Tree aufnehmen?

Für das Ding brauche ich ja auch noch ein Device File unter /dev. Wer 
erzeugt das File?
Ich dachte eigentlich, den Treiber könnte ich als loadable Module bauen. 
Wenn das Device benutzt werden soll, dann wird das Modul geladen und 
irgend ein System call erzeugt mir das Device file? oder wie geht es 
richtig?
 ich lese zwar grade "Linux device driver development", aber irgendwie 
wird nicht sauber darauf eingegangen, woher das Device File kommt und 
wer das Modul für den Treiber lädt.

Dann - um auf die memory mapped IO Register des Device zugreifen zu 
können, rufe ich einfach ioremap() oder mmap() auf und mache den 
Memorybereich so zugreifbar? oder wie geht es richtig.
Danke & Gruss
Tobias

von Stephan (Gast)


Lesenswert?

Hi
Lies mal was zum Thema "User Space Drivers". Vielleicht kannst Du auf 
einen echten Treiber verzichten.
Gruss
S.

von Till U. (tuhde)


Lesenswert?

Tobias Plüss schrieb:

> Muss ich das "Spezialdevice" zwingend mit in den Device Tree aufnehmen?

Nein. Siehe Netzwerkdevices.

> Für das Ding brauche ich ja auch noch ein Device File unter /dev. Wer
> erzeugt das File?

Die Distribution oder Du oder udev.

> Ich dachte eigentlich, den Treiber könnte ich als loadable Module bauen.
> Wenn das Device benutzt werden soll, dann wird das Modul geladen und
> irgend ein System call erzeugt mir das Device file? oder wie geht es
> richtig?

Das Kernel Modul implementiert nur die Funktionalität des physikalischen 
Gerätes. Es registriert eine ( Major- und Minor- ) Device Nummer und 
wartet auf Arbeit. Der Rest sollte in Deinem Buch stehen.

Das Devicefile ist der Teil, auf den Benutzer mit ihren Berechtigungen 
über ioctl's oder File I/O zugreifen können. Das Devicefile greift auf 
eine bestimmte ( Major- und Minor- ) Device Nummer zu und stellt so die 
Verbindung zum Kernel Modul her.
Siehe auch: man mknod(1)

Alle Geräte, mit denen Benutzer direkt kommunizieren können sollen, 
brauchen ein Devicefile.

von Max (Gast)


Lesenswert?

Ist das Zynq von Xilinx? Dort ist schon alles vorhanden, man 
kommuniziert mit fpga über /dev.

von Tobias P. (hubertus)


Lesenswert?

Hallo zusammen,

> Lies mal was zum Thema "User Space Drivers". Vielleicht kannst Du auf

ja, das ist gut möglich dass das mit einem User Space Driver geht. Was 
ist das Kriterium hierfür?

> Ist das Zynq von Xilinx

nein, ist was anderes. Die Frage ist auch eher allgemein gemeint.


@Till
Also, verstehe ich das richtig:

Man lädt das Kernelmodul. Dieses registriert die beiden Nummern (Major 
und Minor). Zudem muss zuvor das Device File erstellt werden (oder aber 
man erstellt es beim Laden des Kernelmoduls automatisch, was auch noch 
nett wäre...) Dann gibts da noch eine Struktur, weiss den Namen nicht 
mehr genau auswendig, aber irgendwas mit ...ops - dort stehen 
Funktionspointer drin für open, read, write, ...
Wenn jetzt ein Devicefile mit File I/O geöffnet wird, schaut der Kernel, 
welche Major/Minor-Nummer dem Devicefile zugeordnet ist, und pickt sich 
das jenige Kernelmodul raus, welches ebendiese Nummern registriert hat. 
Über die Funktionspointer open, read, write, ... werden dann 
entsprechenden Callbackfunktionen aufgerufen. Richtig?

Weisst du ein gutes Beispiel, wo ich mir das ein wenig abgucken kann?

Oder kann ich den Beispielcode aus "Linux Device Driver Development" 
benutzen? Der dürfte zwar recht alt sein, weil er für Kernel 2.6x ist...


Gruss Tobias

von Till U. (tuhde)


Lesenswert?

>> Lies mal was zum Thema "User Space Drivers". Vielleicht kannst Du auf
>
> ja, das ist gut möglich dass das mit einem User Space Driver geht. Was
> ist das Kriterium hierfür?

Gast Stephan hebt hier auf eine kleine Erleichterung ab. Im Kernel 
herrscht ein anderes Memory Mapping als im User Space. Wenn Du jetzt 
einen Kernel Treiber schreibst, mußt Du zwischen User Space und Kernel 
Space Memory konvertieren. Was Gast Stephan nicht berücksichtigt, ist, 
daß Dein Prozessor memory mapped auf Dein Gerät zugreift. Die genannte 
Erleichterung gilt aber nur für den Zugriff von Programmen auf das 
Device File. Nur hierbei ist bei einem User Space Treiber keine 
Umwandlung nötig.

Ansonsten enthält ein User Space Treiber 99% den gleichen Code, wie sein 
Bruder im Kernel und ist definitiv auch ein "echter" Treiber.

Gängige Praxis ist, einen User Space Treiber zu entwickeln und den dann 
ins Kernel zu portieren.

> @Till
> Also, verstehe ich das richtig:
>
> Man lädt das Kernelmodul. Dieses registriert die beiden Nummern (Major
> und Minor). Zudem muss zuvor das Device File erstellt werden

Ja, aber alt, für viele Systeme und Kernel aber noch aktuell.

> (oder aber man erstellt es beim Laden des Kernelmoduls automatisch, was > auch 
noch nett wäre...)

Dein Modul, kann / darf das nicht, es läuft im Kernelspace, da gibt es 
keine Filesystemfunktionen. Dafür ist aber udev da. Wenn Dein Modul 
lädt, besser gesagt, wenn es ein gültiges Gerät erkennt und dieses 
registriert, meldet es dem Kernel seine Daten. Das Kernel erzeugt dann 
ein Event für udev. udev erzeugt dann ( im Userspace ) anhand von Regeln 
in /etc/udev.d/ ein Devicefile und evtl. verschiedene Sym-Links darauf. 
Hierbei hast Du dann mit Major- und Minor- Nummern nichts mehr zu tun.

> Dann gibts da noch eine Struktur, weiss den Namen nicht
> mehr genau auswendig, aber irgendwas mit ...ops - dort stehen
> Funktionspointer drin für open, read, write, ...
> Wenn jetzt ein Devicefile mit File I/O geöffnet wird, schaut der Kernel,
> welche Major/Minor-Nummer dem Devicefile zugeordnet ist, und pickt sich
> das jenige Kernelmodul raus, welches ebendiese Nummern registriert hat.
> Über die Funktionspointer open, read, write, ... werden dann
> entsprechenden Callbackfunktionen aufgerufen. Richtig?

Ja. Nur die ioctl hast Du vergessen.
Du mußt Dich auch noch mit Device Klassen aus einander setzen. Dein 
Gerät wird vermutlich nichts wirklich Neues machen. Durch das 
einsortieren in eine vorhandene Klasse kannst Du Dir eine Menge Arbeit 
sparen.

> Weisst du ein gutes Beispiel, wo ich mir das ein wenig abgucken kann?

In den Kernel Sourcen gibt es mehrere Skeleton Treiber, die sollten Dir 
helfen.

> Oder kann ich den Beispielcode aus "Linux Device Driver Development"
> benutzen? Der dürfte zwar recht alt sein, weil er für Kernel 2.6x ist...

Kenn' ich nicht, sollte aber gehen. In 2.6 war udev schon drin'.

von Hans Ulli K. (Gast)


Lesenswert?

Tobias Plüss schrieb:

Hallo Tobias ;-)

>
> Am Prozessor ist noch ein kleiner FPGA dran, wo ich ein spezielles
> Device mit memory mapped IO synthetisiere. Jetzt muss ich natürlich, um
> das Ding aus Linux heraus ansprechen zu können, einen Treiber dafür
> haben.
>
> Dazu ein paar Fragen:
>
> Muss ich das "Spezialdevice" zwingend mit in den Device Tree aufnehmen?

Wenn du es als Device im Userland anspfrechen willst jat.

>
> Für das Ding brauche ich ja auch noch ein Device File unter /dev. Wer
> erzeugt das File?

In Prinzip dein Treiber beim "allozieren" des Deivces

> Ich dachte eigentlich, den Treiber könnte ich als loadable Module bauen.
> Wenn das Device benutzt werden soll, dann wird das Modul geladen und
> irgend ein System call erzeugt mir das Device file? oder wie geht es
> richtig?

Die Frage ist erstmal was für ein Device Type dein FPGA darstellen kann.
Block oder Char Device.
Das bezieht sich nur auf den Datentransport.
Also einzelne Bytes oder in Block zu z.B. 512 Bytes (aka Floppy HDD),
Um Kommandos an dein Device zu geben gibt es ioctl's

>  ich lese zwar grade "Linux device driver development", aber irgendwie
> wird nicht sauber darauf eingegangen, woher das Device File kommt und
> wer das Modul für den Treiber lädt.
>

Düfte irgend wo in den Sourcen zu finden.
Aber erstmal die obige Frage beantworten Block oder Char.


> Dann - um auf die memory mapped IO Register des Device zugreifen zu
> können, rufe ich einfach ioremap() oder mmap() auf und mache den
> Memorybereich so zugreifbar? oder wie geht es richtig.

ioremap ist eine Kernelfunktion um dein Device für den Kernel vom 
physikalischen in den virtuellen Bereich zu mappen. Weil du nur per 
"virtueller" Adresse auf dein Device zugreifst.

mmap () ist eine Kernel/Userland Funktion um sagen wir das unnnötige 
kopieren von Daten zu verhindern.


> Danke & Gruss
> Tobias

von Tobias P. (hubertus)


Lesenswert?

Hallo Ulli und alle anderen,

so ich habe dank euren Infos die richtigen Stichworte bei Google 
eingeben können und bin nun auf folgendes gestossen:

http://hg.berlios.de/repos/kedr/file/b0f4d9d02d35/sources/examples/sample_target/cfake.c

Der Code sieht in meinen Augen eigentlich nicht soo schlecht aus. Ich 
habe ihn ein wenig modifiziert, und in den open / read usw. 
Callbackfunktionen einfach entsprechende printk() Aufrufe eingebaut und 
das Ding auf meinem PC compiliert. Nach einer anfänglichen kernel panic, 
weil ich irgendwo einen Pointer noch verbogen hatte, hats beim zweiten 
mal nun geklappt:

mit insmod test.ko wird das Modul geladen, und ein ls /dev bringt mir 
ein cfake0 Devicefile. Mit cat /var/log/syslog | grep cfake sehe ich 
meinen Text beim Open-Aufruf :-) Danach habe ich noch mit einem kleinen 
Bastel-C-Programm das Device File mit open() geöffnet, ein paar Bytes 
rein geschrieben und wieder zugemacht - konnte man alles im syslog 
verfolgen. Das Modul scheint also zu funktionieren. Und - besonders toll 
- mit rmmod test.ko verschwindet auch das Device File wieder und es wird 
sauber aufgeräumt.

Super! Das Modul scheint also so grundsätzlich mal zu funktionieren.

Jetzt muss ich mir nur noch überlegen, ob mein Device was ich 
hardwaremässig anspreche ein Block- oder Chardevice ist. Also, es hat 
ein paar Memory Mapped Register, eigentlich interessieren mich nur die. 
Kann ich mir also die Read / Write Funktionen sparen und nur ioctl 
implementieren? Ich dachte mir, mit ioctl könnte ich dann die 
Registernummern und die zu schreibenden Werte übergeben. So wie hier:
http://www.linuxforu.com/2011/08/io-control-in-linux/

Was meint ihr?

Gruss

von Hans Ulli K. (Gast)


Lesenswert?

Welche Mengen an Daten werden geschrieben/gelesen ??

Mein "Framework" ohne die HW zu kennen.
1
mknod cfake c 240 0
1
f = open("/dev/cfake", O_RDWR);
2
ioctl(f, CFAKE_FUNC1)
3
write(f, buffer, 100)
Um deinen FPGA zu "programmieren"

von Georg A. (georga)


Lesenswert?

Die Sache mit den Userspace-Treibern geht leider vernünftig nur bei x86, 
weil da mmap auf /dev/mem auch für den PCI/IO/Peripheriebereich geht. 
Bei ARM-Linux geht das nicht. Hab deswegen für ödes LED-Geblinke über 
GPIO extra einen Treiber schreiben müssen...

> Kann ich mir also die Read / Write Funktionen sparen und nur ioctl
> implementieren?

Sicher. read/write ist für viele Sachen gar tauglich.

von Tobias P. (hubertus)


Lesenswert?

@Georg
was meinst du mit "gar tauglich" ? :-)


Ich habe mal den Code ein wenig aufgeräumt und das was ich nicht brauche 
raus genommen. So sieht er jetzt aus:
1
#include <linux/version.h>
2
#include <linux/module.h>
3
#include <linux/moduleparam.h>
4
#include <linux/init.h>
5
#include <linux/kernel.h>
6
#include <linux/slab.h>
7
#include <linux/fs.h>
8
#include <linux/errno.h>
9
#include <linux/err.h>
10
#include <linux/cdev.h>
11
#include <linux/device.h>
12
#include <linux/mutex.h>
13
#include <asm/uaccess.h>
14
15
16
17
#define CFAKE_DEVICE_NAME "cfake"
18
19
struct cfake_device
20
{
21
  int major;
22
  dev_t devnbr;
23
  static struct class *class = NULL;
24
  struct cdev cdev;
25
};
26
27
static struct cfake_device* this_device = NULL;
28
29
static int
30
cfake_open(struct inode *inode, struct file *filp)
31
{
32
  unsigned int mj = imajor(inode);
33
  unsigned int mn = iminor(inode);
34
  struct cfake_device *dev = NULL;
35
36
  if((mj != this_device->major) || (mn < 0) || (mn > 1))
37
  {
38
    /* no such device */
39
    return -ENODEV;
40
  }
41
42
  /* store a pointer to struct cfake_dev here for other methods */
43
  filp->private_data = this_device; 
44
45
  if (inode->i_cdev != &this_device->cdev)
46
  {
47
    /* no such device */
48
    return -ENODEV;
49
  }
50
51
#if 0
52
  /* if opened the 1st time, allocate the buffer */
53
  if (dev->data == NULL)
54
  {
55
56
    dev->data = (unsigned char*)kzalloc(dev->buffer_size, GFP_KERNEL);
57
58
    if (dev->data == NULL)
59
60
    {
61
62
      printk(KERN_WARNING "[target] open(): out of memory\n");
63
64
      return -ENOMEM;
65
66
    }
67
68
  }
69
#endif
70
  return 0;
71
}
72
73
static int
74
cfake_release(struct inode *inode, struct file *fp)
75
{
76
  return 0;
77
}
78
79
static ssize_t
80
cfake_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
81
{
82
  return 0;
83
}
84
85
static ssize_t
86
cfake_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
87
{
88
  return count;
89
}
90
91
static loff_t
92
cfake_llseek(struct file *filp, loff_t off, int whence)
93
{
94
  return 0;
95
}
96
97
static struct file_operations cfake_fops = {
98
  .owner = THIS_MODULE,
99
  .read = cfake_read,
100
  .write = cfake_write,
101
  .open = cfake_open,
102
  .release = cfake_release,
103
  .llseek = cfake_llseek,
104
};
105
106
static int __init
107
cfake_init_module(void)
108
{
109
  int err = 0;
110
  struct device* node;
111
  
112
  /* allocate memory for storing the internal variables */
113
  this_device =
114
    (struct cfake_device*)kzalloc(sizeof(struct cfake_device), GFP_KERNEL);
115
116
  /* proceed only if memory could be allocated */
117
  if(this_device != NULL) {
118
    /* allocate a character device */
119
    err = alloc_chrdev_region(&this_device->devnbr, 0, 1, CFAKE_DEVICE_NAME);
120
    if(err < 0) {
121
      goto memfree;
122
    }
123
    
124
    /* create a device class */
125
    this_device->class = class_create(THIS_MODULE, CFAKE_DEVICE_NAME);
126
    if(IS_ERR(this_device->class)) {
127
      err = PTR_ERR(this_device->class);
128
      goto unregister;
129
    }
130
    
131
    /* initialise the character device */
132
    cdev_init(&this_device->cdev, &cfake_fops);
133
    this_device->cdev.owner = THIS_MODULE;
134
    
135
    /* add the character device */
136
    err = cdev_add(&this_device->cdev, this_device->devnbr, 1);
137
    if(err) {
138
      goto classdestroy;
139
    }
140
    
141
    /* create a device node of this class */
142
    node =
143
      device_create(this_device->class, NULL,
144
      this_device->devnbr, NULL, CFAKE_DEVICE_NAME);
145
    if(IS_ERR(node)) {
146
      err = PTR_ERR(node);
147
      goto deletedevice;
148
    }
149
    
150
    /* success */
151
    return 0;
152
  }
153
  
154
  /* no memory allocated */
155
  return -ENOMEM;
156
157
  /* device node creation failed */
158
deletedevice:
159
  cdev_del(&this_device->cdev);
160
161
  /* adding the character device failed */
162
classdestroy:
163
  class_destroy(this_device->class);
164
165
  /* class creation failed */
166
unregister:
167
  unregister_chrdev_region(MKDEV(this_device->major, 0), 1);
168
169
  /* character device allocation failed */
170
memfree:
171
  kzfree(this_device);
172
173
  return err;
174
}
175
176
static void __exit
177
cfake_exit_module(void)
178
{
179
  /* destroy the device node */
180
  device_destroy(cfake_class, MKDEV(cfake_major, 0));
181
  
182
  /* delete the character device */
183
  cdev_del(&cdev);
184
185
  /* destroy the device class */
186
  class_destroy(this_device->class);
187
188
  /* unregister the character devices */
189
  unregister_chrdev_region(MKDEV(cfake_major, 0), 1);
190
}
191
192
module_init(cfake_init_module);
193
module_exit(cfake_exit_module);
194
195
MODULE_AUTHOR("Tobias Pluess");
196
MODULE_LICENSE("GPL");

Der goto-Teil gefällt mir nicht so, aber es ist am einfachsten ;-)
sieht nicht so schlecht aus, oder? Habe das in der Zwischenzeit aber 
nicht mehr getestet. Aber vielleicht sieht einer noch Fehler. Was ich 
nun noch machen muss ist ioctl implementieren; ich habe da allerdings 
gesehen dass es verschiedene Varianten gibt; irgendwie ioctl_unlock 
usw., welches ist das richtige?

von Georg A. (georga)


Lesenswert?

Ich wollte noch ein "nicht" kaufen ;)

Das goto ist den Dijkstra-Jüngern zwar ein Graus, aber hier durchaus 
gerechtfertigt. Alles andere macht den Fluss noch undurchschaubarer bzw. 
fehlerträchtiger, und "try-catch" gibts ja auch nicht.

Die Sache mit dem Lock kommt noch vom "Big-Kernel-Lock", der bis Ende 
der 2.6er (oder so) die IO-Ops geschützt hat und jetzt noch aus 
Kompatibilitätsgründen da ist. Nimm den alten (nicht den unlocked*), und 
du musst dir keine grossen Gedanken machen. Die Performanceeffekte des 
unlocked* wären in der Anwendung ohnehin nicht bemerkbar. Da gehts um 
Multicore/Massiv-SMP.

von Tobias P. (hubertus)


Lesenswert?

Hi Georg,

ach soo :-)

Okay aber den Callback "nur" ioctl() gibt es m.E. nicht, ich finde nur 
die modifizierten Varianten. Was mache ich falsch?

Btw, Dijkstra war mir kein Begriff, hab aber kurz gegoogled ;-) 
Allerdings ist "GOTO Statement considered harmful" tatsächlich das, was 
ich mal gelernt habe :D

Ehm, etwas ist mir noch aufgefallen - mit

sudo insmod ./test.ko

wird das Modul geladen, und unter /dev wird meine Devicenode erstellt, 
soweit alles gut... wenn ich aber

sudo rmmod test

mache, dann wird auch das Modul entladen, aber die Devicenode bleibt 
bestehen bis ich den PC neu starte. Ist das richtiges bzw. gewolltes 
Verhalten? Ich hätte eigentlich erwartet dass die Devicenode beim 
Entladen des Moduls entfernt wird.

Gruss Tobias

von Georg A. (georga)


Lesenswert?

> Was mache ich falsch?

Das normale/alte würde compat_ioctl heissen.

> Allerdings ist "GOTO Statement considered harmful" tatsächlich das, was
> ich mal gelernt habe :D

Wobei die Goto-Nutzung in der Zeit aber auch extrem war. Die meisten 
Sprachen hatten nichts strukturierendes, oft nichtmal ein else zum if... 
Von daher halte ich es für übertrieben, heute beim Auftauchen von ein 
paar versprengten Gotos gleich wieder den ollen Dijkstra-Spruch 
rauszulassen.

> Ich hätte eigentlich erwartet dass die Devicenode beim
> Entladen des Moduls entfernt wird.

Sollte es, ein printk im exit-code zeigt, ob er überhaupt aufgerufen 
wird ;) Die Reihenfolge beim Aufräumen kommt mir aber komisch vor. Mach 
mal den class_destroy vor den cdev_del, so machens andere.. Ausserdem 
solltest du als letztes noch this_device freigeben (das wirds aber nicht 
sein).

von Hans Ulli K. (Gast)


Lesenswert?

Tobias Plüss schrieb:
> sudo rmmod test
>
> mache, dann wird auch das Modul entladen, aber die Devicenode bleibt
> bestehen bis ich den PC neu starte. Ist das richtiges bzw. gewolltes
> Verhalten? Ich hätte eigentlich erwartet dass die Devicenode beim
> Entladen des Moduls entfernt wird.
>
> Gruss Tobias

Was macht denn udev oder ein anderer Hotplugmanager (mdev / logfile)
Der Device-Node in /dev wird ja nicht per Kernel erzeugt, sondern der 
Kernel gibt per /proc/sys/kernel/hotplug an der Userspace weiter.

Das mit dem goto in den Sourcen finde ich normal. Außerdem ist eine 
return Anweisung auch ein goto, nur sieht man es nicht sofort.


Wer findet hier den Fehler ...
1
        spin_lock(&spinlock_1);
2
3
        /* Do something */
4
5
        if (fail) {
6
                spin_unlock(&spinlock_1);
7
                return;
8
        }
9
10
        spin_lock(&spinlock_2);
11
12
        /* Do something */
13
14
        if (fail) {
15
                spin_unlock(&spinlock_2);
16
                spin_unlock(&spinlock_1);
17
                return;
18
        }
19
20
        /* Do something */
21
22
        if (fail) {
23
                spin_unlock(&spinlock_1);
24
                return;
25
        }
26
27
28
        /* Do something */
29
30
        if (fail) {
31
                spin_unlock(&spinlock_1);
32
                spin_unlock(&spinlock_2);
33
                return;
34
        }
35
36
        spin_unlock(&spinlock_2);
37
        spin_unlock(&spinlock_1);

von Tobias P. (hubertus)


Lesenswert?

Hallo Hans Ulli und Georg,
sorry für die Verspätete Antwort, ich hatte viel zu tun ;-)

Ich werde noch meine Reihenfolge im Module-Exit Code korrigieren und 
dann heute Abend nochmal schauen, ob es so besser funktioniert.

Noch eine andere Frage:
angenommen, mein FPGA-Device bietet ein paar memory mapped Register an, 
sagen wir an der physikalischen Adresse 0xf0000000. Normalerweise klemmt 
die MMU ja Zugriffe auf nicht gemapptes Memory ab, oder? Das heisst, 
selbst wenn mein Treiber geladen wurde, kann ich noch nicht auf die 
Adresse 0xf0000000 einfach so zugreifen, das würde doch einen Fehler 
auslösen - wahrscheinlich segmentation fault, oder sowas. Kann ich mit 
mmap() oder so einer Funktion das Memory irgendwie so mappen, dass ich 
zugreifen darf? das ist mir noch nicht klar. Irgendwie muss ich ja auf 
die Adresse meines Devices zugreifen können damit ich die Register lesen 
und schreiben kann.

Das Lesen / Schreiben geschieht dann mit den Makros readl() bzw. 
writel(), ja?

Gruss Tobias


PS: Ulli, der Fehler liegt doch im 3. "if(fail)", oder? Da wird nur 
spinlock 1 unlocked, nicht aber die 2.

von Georg A. (georga)


Lesenswert?

Wenn du auf HW-Speicher im Kernel zugreifen willst, nimm 
ioremap(_nocache). Das liefert dann die (virtuelle) Startadresse zurück, 
auf der du dann den Bereich siehst. Ohne das gibt's einen Oops... Ein 
iounmap zum Aufräumen gibts auch. Schau dir zB. mal die diversen 
Kerneltreiber an, fast jeder nutzt das. Meistens kommt da die 
physikalische Addresse aus dem PCI-Config-Space, bei Embedded-Kram ist 
sie manchmal auch hardcoded.

> Das Lesen / Schreiben geschieht dann mit den Makros readl() bzw.
> writel(), ja?

Ja, sollte man nehmen. Wobei für einen Quick-Hack auf x86 auch einfach 
normales Gepointere geht.  readl/writel kümmern sich u.a. um 
little/big-Endian-Probleme (PCI ist zB. LE, auf BE-Maschinen hätte ein 
roher 32Bit-Read vertauschte Bytes). Auf seltsamen Architekturen (zB. 
Alpha...) macht das auch noch die Aufteilung der Byte/Word/Long-Zugriffe 
auf verschiedene Adressbereiche.

von Tobias P. (hubertus)


Lesenswert?

Hi Georg,

super, danke, damit komme ich zurecht, denke ich ;-)
Wenn ich noch ein Problem raus finde melde ich mich wieder hier.

Gruss & schönen Abend
Tobias

von Hans Ulli K. (Gast)


Lesenswert?

Tobias Plüss schrieb:
>
> PS: Ulli, der Fehler liegt doch im 3. "if(fail)", oder? Da wird nur
> spinlock 1 unlocked, nicht aber die 2.

Beachte mal die Reihenfolge des Lockings :
1
        spin_lock(&spinlock_1);
2
3
        /* Do something */
4
5
        if (fail) {
6
                spin_unlock(&spinlock_1);
7
                return;
8
        }
9
10
        spin_lock(&spinlock_2);
11
12
        /* Do something */
13
14
        if (fail) {
15
                spin_unlock(&spinlock_2);
16
                spin_unlock(&spinlock_1);
17
                return;
18
        }
19
20
        if (fail) {
21
                spin_unlock(&spinlock_1);
22
                spin_unlock(&spinlock_2);
23
                return;
24
        }

Beim zweiten "fail" wird falsch unlocked.

Im Programmfluss wurde erst das spinlock_1, dann spinlock_2 gesetzt.
Ein Unlock sollte, dann Rückwärts ablaufen.
Es ist quasi wie beim allozieren/free dieser struct.
1
struct {
2
        char *mem;
3
} data;
4
5
struct data *test;

Wenn du den Speicher von *test freigibst muss du ja auch erst ein
free (test->mem);
machen, sonnst hast du ein memory hole ...

Und da ist es echt einfacher diese Sache mit einem
goto
in C abzufangen und es ist einfacher zu lesen.


Mit dem mmap vom deinem FPGA, schaue mal in dem Bereich
drivers/gpu
Die Grafikkarten haben das gleiche "Problem" wie du. Da wird auch ein 
Bereich per mmap in den Userspace gemapped.

von Tobias P. (hubertus)


Lesenswert?

Hallo zusammen,

ich habe noch eine Frage hierzu!
Ich habe hier ein laufendes Embedded Linux System, das ich selber 
compiliert habe. Den Kernel habe ich mit arm-none-eabi-gcc kompiliert.

Da ich auf dem Target keinen Compiler habe, muss ich meinen Treiber 
Cross-Compilieren. Dazu habe ich folgendes Makefile:
1
ifneq ($(KERNELRELEASE),)
2
# We were called by kbuild
3
4
obj-m += test.o
5
6
else  # We were called from command line
7
8
KDIR := /hdd/sinetscu-dev/linux-socfpga
9
PWD  := $(shell pwd)
10
CROSS=arm-linux-gnueabihf-
11
12
default:
13
  $(MAKE) -C $(KDIR) SUBDIRS=$(PWD)  ARCH=arm CROSS_COMPILE=$(CROSS) modules
14
15
16
endif  # End kbuild check
17
######################### Version independent targets ##########################
18
19
clean:
20
  rm -f -r *.o *.ko .*cmd .tmp* core *.i

Vorerst möchte ich nur folgendes Fake-Modul compilieren:
1
#include <linux/version.h>
2
#include <linux/module.h>
3
#include <linux/moduleparam.h>
4
#include <linux/init.h>
5
#include <linux/kernel.h>
6
#include <linux/slab.h>
7
#include <linux/fs.h>
8
#include <linux/errno.h>
9
#include <linux/err.h>
10
#include <linux/cdev.h>
11
#include <linux/device.h>
12
#include <linux/mutex.h>
13
#include <asm/uaccess.h>
14
15
16
static int __init
17
cfake_init_module(void)
18
{
19
  printk(KERN_INFO "io driver loaded");
20
  return 0;
21
}
22
23
static void __exit
24
cfake_exit_module(void)
25
{
26
  printk(KERN_INFO "io driver unloaded");
27
}
28
29
module_init(cfake_init_module);
30
module_exit(cfake_exit_module);
31
32
MODULE_AUTHOR("Tobias Pluess <tobias.pluess@siemens.com>");
33
MODULE_LICENSE("GPL");

und dann müsste ich ja unter /var/log/messages meine Meldungen von dem 
Treiber sehen. Also, compilieren geht, ABER, wenn ich auf meinem Target 
(das mit Buildroot erstellt wurde) dann eingebe

insmod ./test.ko

dann kriege ich den Fehler:

test: version magic '3.10.0-00163-gd4db2a9-dirty SMP mod_unload ARMv7 
p2v8 ' should be '3.10.0-0016'
insmod: can't insert './test.ko': invalid module format


warum? ich habe für den Treiber ja dieselben Header verwendet wie für 
den Kernel. Was mache ich falsch?


Gruss Tobias

von Hans Ulli K. (Gast)


Lesenswert?

Tobias Plüss schrieb:
>
> test: version magic '3.10.0-00163-gd4db2a9-dirty SMP mod_unload ARMv7
> p2v8 ' should be '3.10.0-0016'
> insmod: can't insert './test.ko': invalid module format
>
>
> warum? ich habe für den Treiber ja dieselben Header verwendet wie für
> den Kernel. Was mache ich falsch?
>
>
> Gruss Tobias
Wenn ich das richtig lese "*-gd4db2a9-dirty" hast du den Treiber über 
das Makefile von Kconfig/Kbuild angeworfen.

Mache bitte in Kconfig die Option
"Automatically append version information to the version string"
aus.
An deine Kernelversion wird ein git Hash angefügt. Außerdem zeigt 
"dirty" an das du nicht commited hast.

von Tobias P. (hubertus)


Lesenswert?

Hi Hans Ulli,

s u p e r, das hat funktioniert! jetzt kann ich mein crosscompiliertes 
Modul auf meinen embedddd device laden. Toll! :D
debuggen kann man aber solch ein kernelmodul nicht so richtig oder?

Gruss

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.