OVH Cloud OVH Cloud

mmap, /dev/video et kernel 2.6

11 réponses
Avatar
Thomas Labourdette
Bonjour,

Avec le kernel 2.6.9, mmap échoue systématiquement lorsque le descripteur de
fichier est associé à un périphérique vidéo (bttv) alors que je n'ai aucun
problème sous un kernel 2.4

Soit le code suivant :
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
  unsigned char *map=NULL;
  int fd=open("/dev/video0",O_RDWR);
  if(fd==-1)
    return -1;
  map=mmap(0,230400,PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
  if(map==(unsigned char *)-1)
    {
      perror("");
      return -1;
    }
  else
    {
      puts("ok");
      munmap(map,10);
    }
  return 0;
}

sous un kernel 2.4 :
tlabourdette@proto2:~$ ./test_mmap
ok

sous un kernel 2.6 :
tlabourdette@dev1:~$ ./test_mmap
Invalid argument


Une idée ?

Merci d'avance

@+

--
Gilles AIDSOVETAGE (signature aléatoire)
LOI (à la con)
Il est interdit de traverser la rue en marchant sur les mains.
(Hartford, Connecticut)

10 réponses

1 2
Avatar
no_spam
On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:

Bonjour,

Avec le kernel 2.6.9, mmap échoue systématiquement lorsque le descripteur de
fichier est associé à un périphérique vidéo (bttv) alors que je n'ai aucun
problème sous un kernel 2.4

Soit le code suivant :
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main()
{
  unsigned char *map=NULL;
  int fd=open("/dev/video0",O_RDWR);
  if(fd==-1)
    return -1;
  map=mmap(0,230400,PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
^^^^^^

Est tu sur de cette valeur ? Il doit y avoir un ioctl qui te la donne
de façon plus sure....
De plus, le premier argument est un pointeur, donc NULL, pas 0.

  if(map==(unsigned char *)-1)
^^

MAP_FAILED est un peu mieux...

    {
      perror("");
      return -1;
    }
  else
    {
      puts("ok");
      munmap(map,10);
^^

C'est faux: tu as utilisé une autre longueur pour le map.
Mais comme tu ne testes pas le code retour, tu ne vois pas que ça ne
marche pas...

    }
  return 0;
}

sous un kernel 2.4 :
:~$ ./test_mmap
ok

sous un kernel 2.6 :
:~$ ./test_mmap
Invalid argument


Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.

Avatar
Thomas Labourdette
no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:

Bonjour,

Avec le kernel 2.6.9, mmap échoue systématiquement lorsque le descripteur
de fichier est associé à un périphérique vidéo (bttv) alors que je n'ai
aucun problème sous un kernel 2.4


map=mmap(0,230400,PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
^^^^^^

Est tu sur de cette valeur ? Il doit y avoir un ioctl qui te la donne
de façon plus sure....


Oui (dans le code de réelle, elle est calculé mais le résultat est celui là)

De plus, le premier argument est un pointeur, donc NULL, pas 0.


0 se converte à NULL

munmap(map,10);
^^

C'est faux: tu as utilisé une autre longueur pour le map.


Exact erreur de recopie

sous un kernel 2.4 :
:~$ ./test_mmap
ok

sous un kernel 2.6 :
:~$ ./test_mmap
Invalid argument

Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Même résultat. De plus si je veux que le driver du device video écrive dans
ce buffer, je dois bien l'acquérir en lecture/ecriture. C'est pour le
capture frame buffer.

Ce que je ne m'explique pas c'est la différence de comportement entre les 2
versions du kernel.

@+
--
Thomas THURGE (signature aléatoire)
" L'église est proche, mais la route est verglacée. Le bar est loin,
mais je marcherai avec prudence. " (Proverbe russe)


Avatar
no_spam
On Mon, 06 Dec 2004 18:51:05 +0100, Thomas Labourdette wrote:

no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:

Bonjour,

Avec le kernel 2.6.9, mmap échoue systématiquement lorsque le descripteur
de fichier est associé à un périphérique vidéo (bttv) alors que je n'ai
aucun problème sous un kernel 2.4


map=mmap(0,230400,PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
^^^^^^

Est tu sur de cette valeur ? Il doit y avoir un ioctl qui te la donne
de façon plus sure....


Oui (dans le code de réelle, elle est calculé mais le résultat est celui là)


Bon.

De plus, le premier argument est un pointeur, donc NULL, pas 0.


0 se converte à NULL


Non. C'est une erreur. 0 est un int NULL est un pointeur.
Le cast implicite d'int en pointeur est interdit et entraine
généralement des bugs. D'ailleurs, il n'y a que les long qui puissent
être castés en pointeur relativement sans danger.
Compile en -Wall -W -Werror, ça aide à faire des choses propres....

[...]

Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Même résultat. De plus si je veux que le driver du device video écrive dans
ce buffer, je dois bien l'acquérir en lecture/ecriture. C'est pour le
capture frame buffer.


Si c'est un frame buffer, ce n'est pas un device v4l, ça n'a rien à voir.
Et je maintiens que par contre écrire dans un buffer v4l est une
aberration. Donc, soit tu te trompes de device (les frame-buffer, c'est
/dev/fb<n>), soit l'open en RDWR et le map en PROT_WRITE sont faux.
En tout cas, ce qui est certain, c'est qu'un des paramêtres est invalide.
Soit c'est le fd (mais vu le code, ça ne devrait pas...), soit c'est la
longueur, soit ce sont les protections. C'est peut-être la longueur qui
est fausse: il suffit que les alignements de buffer changent d'un kernel
à l'autre pour que la longueur soit différente. Essaye, au cas ou, de
demander un nombre entier de pages, donc une longueur 233472, pour voir.
Ou mieux, fait l'ioctl qui demande au device la longueur de la mémoire à
mapper, c'est finalement la seule méthode valide.

Ce que je ne m'explique pas c'est la différence de comportement entre les 2
versions du kernel.


Il y a beaucoup de choses qui changent entre un 2.4 et un 2.6. Ce qui est
sur, c'est que si le code était correct, il marcherait sur les deux.



Avatar
Thomas Labourdette
no_spam a écrit le lundi 06 Décembre 2004 22:10 :

On Mon, 06 Dec 2004 18:51:05 +0100, Thomas Labourdette wrote:

no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:

De plus, le premier argument est un pointeur, donc NULL, pas 0.


0 se converte à NULL


Non. C'est une erreur.


Ce n'est pas une erreur. 0 se convertit implicitement en null pointer (merci
de revoir la norme C)

Compile en -Wall -W -Werror, ça aide à faire des choses propres....


Oui.
gcc -W -Werror -Wall -pedantic -o test_mmap test_mmap.c

rien en sortie (le test_mmap.c est le source donné dans ce post)

Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Même résultat. De plus si je veux que le driver du device video écrive
dans ce buffer, je dois bien l'acquérir en lecture/ecriture. C'est pour
le capture frame buffer.


Si c'est un frame buffer, ce n'est pas un device v4l, ça n'a rien à voir.


Ceux sont les appels VIDIOCSFBUF et VIDIOCGFBUF et associés (voir Frame
Buffer
dans /usr/src/kernel-source-2.x.x/Documentation/video4linux/API.html)

Essaye, au cas ou, de
demander un nombre entier de pages, donc une longueur 233472, pour voir.
Ou mieux, fait l'ioctl qui demande au device la longueur de la mémoire à
mapper, c'est finalement la seule méthode valide.


Échec dans les tous les cas .

Ce que je ne m'explique pas c'est la différence de comportement entre les
2 versions du kernel.


Il y a beaucoup de choses qui changent entre un 2.4 et un 2.6. Ce qui est
sur, c'est que si le code était correct, il marcherait sur les deux.


J'ai de la chance alors que mon code soit tombé en marche il y a plus d'un
an.

@+
--
Akim FEIMAL (signature aléatoire)
Très gravement brûlée, elle s'est éteinte pendant son transport à
l'hôpital.
(Dauphiné Actualité)




Avatar
Thomas Labourdette
no_spam a écrit le lundi 06 Décembre 2004 22:10 :

On Mon, 06 Dec 2004 18:51:05 +0100, Thomas Labourdette wrote:

no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:

De plus, le premier argument est un pointeur, donc NULL, pas 0.


0 se converte à NULL


Non. C'est une erreur.


Ce n'est pas une erreur. 0 se convertit implicitement en null pointer (merci
de revoir la norme C)

Compile en -Wall -W -Werror, ça aide à faire des choses propres....


Oui.
gcc -W -Werror -Wall -pedantic -o test_mmap test_mmap.c

rien en sortie (le test_mmap.c est le source donné dans ce post)

Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Même résultat. De plus si je veux que le driver du device video écrive
dans ce buffer, je dois bien l'acquérir en lecture/ecriture. C'est pour
le capture frame buffer.


Si c'est un frame buffer, ce n'est pas un device v4l, ça n'a rien à voir.


Ceux sont les appels VIDIOCMCAPTURE et associés (voir Reading Image
dans /usr/src/kernel-source-2.x.x/Documentation/video4linux/API.html (il y
aussi des notions de frame buffer dans V4L))

Essaye, au cas ou, de
demander un nombre entier de pages, donc une longueur 233472, pour voir.
Ou mieux, fait l'ioctl qui demande au device la longueur de la mémoire à
mapper, c'est finalement la seule méthode valide.


Échec dans les tous les cas .

Ce que je ne m'explique pas c'est la différence de comportement entre les
2 versions du kernel.


Il y a beaucoup de choses qui changent entre un 2.4 et un 2.6. Ce qui est
sur, c'est que si le code était correct, il marcherait sur les deux.


J'ai de la chance alors que mon code soit tombé en marche il y a plus d'un
an.

@+
--
Akim FEIMAL (signature aléatoire)
Très gravement brûlée, elle s'est éteinte pendant son transport à
l'hôpital.
(Dauphiné Actualité)




Avatar
Nicolas George
Thomas Labourdette wrote in message
:
Ce n'est pas une erreur. 0 se convertit implicitement en null pointer (merci
de revoir la norme C)


La norme C définit NULL comme « an implementation-defined null pointer
constant ». Si c'est « implementation-defined », ce n'est pas nécessairement
0.

Avatar
no_spam
On Tue, 07 Dec 2004 10:13:17 +0100, Thomas Labourdette wrote:

no_spam a écrit le lundi 06 Décembre 2004 22:10 :

On Mon, 06 Dec 2004 18:51:05 +0100, Thomas Labourdette wrote:

no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:
[...]




Une idée ?


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Même résultat. De plus si je veux que le driver du device video écrive
dans ce buffer, je dois bien l'acquérir en lecture/ecriture. C'est pour
le capture frame buffer.


Si c'est un frame buffer, ce n'est pas un device v4l, ça n'a rien à voir.


Ceux sont les appels VIDIOCMCAPTURE et associés (voir Reading Image
dans /usr/src/kernel-source-2.x.x/Documentation/video4linux/API.html (il y
aussi des notions de frame buffer dans V4L))


Non, il n'y a aucune notion de frame-buffer dans v4l, ça n'a strictement
rien à voir. V4L permet de capturer n'importe ou, notement dans le
frame-buffer, mais c'est un cas particulier.
La seule chose qu'il y ait, c'est que certaines cartes peuvent faire des
transferts directs dans la RAM vidéo en mode bus-master PCI, ce qui
accélère le transfert mais ne change rien d'autre par rapport à la
capture dans une zone de mémoire quelconque.
VIDIOCMCAPTURE peut être utilisé pour capturer l'image dans une fenetre
X. De toute façon, cet appel n'écrit pas dans le buffer de capture,
encore une fois.
Donc, ton buffer vidéo devrait être en read-only et ton buffer
d'arrivée (qui peut être le frame-buffer) en read-write.

Essaye, au cas ou, de
demander un nombre entier de pages, donc une longueur 233472, pour voir.
Ou mieux, fait l'ioctl qui demande au device la longueur de la mémoire à
mapper, c'est finalement la seule méthode valide.


Échec dans les tous les cas .

Ce que je ne m'explique pas c'est la différence de comportement entre les
2 versions du kernel.


Il y a beaucoup de choses qui changent entre un 2.4 et un 2.6. Ce qui est
sur, c'est que si le code était correct, il marcherait sur les deux.


J'ai de la chance alors que mon code soit tombé en marche il y a plus d'un
an.


Oui, tu en as la preuve: il ne marche pas avec une autre version
compatible de l'API.





Avatar
Thomas Labourdette
no_spam a écrit le mardi 07 Décembre 2004 15:01 :

VIDIOCMCAPTURE peut être utilisé pour capturer l'image dans une fenetre
X. De toute façon, cet appel n'écrit pas dans le buffer de capture,
encore une fois.


Il écrit où dans ce cas ?
VIDIOCMCAPTURE (avec VIDIOCSYNC) est utilisé pour récupérer une image du
device v4l. Et cette image est mise dans le buffer acquit via mmap (à
l'offset correspondant au n° de frame).
Concernant l'utilisation de mmap sur un device v4l, je ne suis pas le seul à
faire /l'erreur/. Ci-dessous un extrait du code de w3cam :
map = mmap (0, vid_buf.size, PROT_READ|PROT_WRITE,MAP_SHARED,dev,0);

@+
--
Sabine OKULAIR (signature aléatoire)
Si au crépuscule
Tu as quatre testicules
Ne te prends point pour Hercule
C'est quelqu'un qui t'encule !

Avatar
Thomas Labourdette
no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:


map=mmap(0,230400,PROT_READ | PROT_WRITE, MAP_SHARED, fd,0);
^^^^^^

Est tu sur de cette valeur ? Il doit y avoir un ioctl qui te la donne
de façon plus sure....


Exact. D'ailleurs dans le code de prod, j'utilise l'ioctl VIDIOCGMBUF pour
déterminer la taille. Toujours est-il que le programme retombe en marche
avec un noyau 2.6. Le problème qu'il y avait (et que je n'arrive pas à
reproduire :-( ) c'est que l'appel VIDIOCGMBUF retourne parfois une valeur
"size" erronée (sans que ioctl échoue). Par exemple : -8192000000 (de
tête). Dans ce cas là mmap echoue à juste titre.

Concernant PROT_WRITE, le programme ci-dessous échoue (à juste titre) si
PROT_WRITE n'est pas mis :

sans PROT_WRITE :
:~ ./test_mmap
17039360
mmap: Invalid argument

avec :
:~ ./test_mmap
17039360
ok

Le programme de test :
#define _POSIX_MAPPED_FILES
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <linux/videodev.h>

int main()
{

struct video_mbuf vid_buf;
unsigned char *map=NULL;
int fd=open("/dev/video0",O_RDWR);

if(fd==-1)
return -1;
if(ioctl(fd,VIDIOCGMBUF, &vid_buf)==-1)
{
perror("VIDIOCGMBUF");
return -1;
}
printf ("%dn",vid_buf.size);
map=mmap(0,vid_buf.size,PROT_READ|PROT_WRITE , MAP_SHARED, fd,0);
if(map==(unsigned char *)-1)
{
perror("mmap");
return -1;
}
else
{
puts("okn");
munmap(map,vid_buf.size);
}
return 0;
}

--
Yshi POTTE (signature aléatoire)
Il est faux de penser que les femmes ne savent pas
garder un secret..
Seulement, comme c'est difficile, elles s'y mettent
à plusieurs...


Avatar
Thomas Labourdette
no_spam a écrit le lundi 06 Décembre 2004 17:50 :

On Mon, 06 Dec 2004 17:26:34 +0100, Thomas Labourdette wrote:


Avec le kernel 2.6.9, mmap échoue systématiquement lorsque le descripteur
de fichier est associé à un périphérique vidéo (bttv) alors que je n'ai
aucun problème sous un kernel 2.4


Enlève le PROT_WRITE. Il me parait très douteux que tu puisse écrire
dans le buffer d'acquisition d'un device de capture vidéo.


Bon après les derniers test fait. Avec un noyau 2.4.x, on peut avoir
(PROT_READ | PROT_WRITE) ou juste (PROT_READ) avec un noyau 2.6 il *faut*
avoir (PROT_READ | PROT_WRITE).

J'ai regardé le code de certaines applications utilisant v4l et l'appel à
mmap contient (PROT_READ | PROT_WRITE).

Concernant mon erreur, en regardant l'arbre des versions, la version de prod
utilise uniquement PROT_READ et là est la cause de l'échec de l'application
sur un 2.6.

Le code de test que j'avais posté ne fonctionnait pas à cause de la taille
de la mémoire demandé (et sur lequel j'avais mis d'une façon automatique
PROT_WRITE).

Merci quand même à toi de m'avoir obligé à creuser plus le problème qui
était pour moi secondaire (mes plateformes sont en 2.4)

@+
--
Jessica, Raymond, Pacôme, Yves, Audrey, Jonathan PLURIENDELAVI (je suis
(signature aléatoire)
CAPITALISTE : Personne qui se rend en voiture climatisée de son bureau
climatisé a son club climatisé pour y prendre un bain de vapeur.


1 2