doc/fr_FR.ISO8859-1/books/developers-handbook/pci/chapter.sgml
2002-02-14 14:25:03 +00:00

376 lines
11 KiB
Text
Raw Blame History

<!--
The FreeBSD Documentation Project
The FreeBSD French Documentation Project
$Id: chapter.sgml,v 1.1 2002-02-14 14:25:02 gioria Exp $
Original revision: 1.3
$FreeBSD$
-->
<chapter id="pci">
<title>Les p&#233;riph&#233;riques PCI</title>
<para>Ce chap&icirc;tre traitera des m&eacute;canismes de FreeBSD pour
&eacute;crire un pilote de p&eacute;riph&eacute;rique pour un p&eacute;riph&eacute;rique sur
bus PCI.</para>
<sect1>
<title>Rechercher et rattacher</title>
<para>Informations ici sur comment le code du bus PCI fait un cycle
sur les p&eacute;riph&eacute;riques non rattach&eacute;s et voir si le nouvellement charg&eacute;
pilote de p&eacute;riph&eacute;rique chargeable dans le noyau (kld)
sera rattach&eacute; &agrave; l'un d'eux.</para>
<programlisting>/*
* Simple KLD pour jouer avec les fonctions PCI.
*
* Murray Stokely
*/
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#include &lt;sys/types.h&gt;
#include &lt;sys/module.h&gt;
#include &lt;sys/systm.h&gt; /* uprintf */
#include &lt;sys/errno.h&gt;
#include &lt;sys/param.h&gt; /* defines used in kernel.h */
#include &lt;sys/kernel.h&gt; /* types used in module initialization */
#include &lt;sys/conf.h&gt; /* cdevsw struct */
#include &lt;sys/uio.h&gt; /* uio struct */
#include &lt;sys/malloc.h&gt;
#include &lt;sys/bus.h&gt; /* structs, prototypes for pci bus stuff */
#include &lt;pci/pcivar.h&gt; /* For get_pci macros! */
/* Prototypes des fonctions */
d_open_t mypci_open;
d_close_t mypci_close;
d_read_t mypci_read;
d_write_t mypci_write;
/* Points d'entr&eacute;e du pilote de p&eacute;riph&eacute;rique caract&egrave;re */
static struct cdevsw mypci_cdevsw = {
mypci_open,
mypci_close,
mypci_read,
mypci_write,
noioctl,
nopoll,
nommap,
nostrategy,
"mypci",
36, /* reserved for lkms - /usr/src/sys/conf/majors */
nodump,
nopsize,
D_TTY,
-1
};
/* variables */
static dev_t sdev;
/* Nous sommes plus interresses dans la recherche/attachement
que dans l'ouverture/fermeture/lecture/ecriture a ce point */
int
mypci_open(dev_t dev, int oflags, int devtype, struct proc *p)
{
int err = 0;
uprintf("Peripherique \"monpci\" ouvert avec succes.\n");
return(err);
}
int
mypci_close(dev_t dev, int fflag, int devtype, struct proc *p)
{
int err=0;
uprintf("Peripherique \"monpci.\ "ferme\n");
return(err);
}
int
mypci_read(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("lecture dans monpci!\n");
return err;
}
int
mypci_write(dev_t dev, struct uio *uio, int ioflag)
{
int err = 0;
uprintf("Ecriture dans monpci!\n");
return(err);
}
/* PCI Support Functions */
/*
* Retourne la chaine d'identification si ce peripherique est le notre
*/
static int
mypci_probe(device_t dev)
{
uprintf("MonPCI Probe\n"
"ID Fabricant: 0x%x\n"
"ID Peripherique : 0x%x\n",pci_get_vendor(dev),pci_get_device(dev));
if (pci_get_vendor(dev) == 0x11c1) {
uprintf("Nous avons le WinModem, recherche reussi!\n");
return 0;
}
return ENXIO;
}
/* La fonction Attach n'est appel&eacute;e que si
la recherche est reussie*/
static int
mypci_attach(device_t dev)
{
uprintf("Rattachement de MonPCI pour: ID Peripherique: 0x%x\n",pci_get_vendor(dev));
sdev = make_dev(<literal>&</literal>mypci_cdevsw,
0,
UID_ROOT,
GID_WHEEL,
0600,
"monpci");
uprintf("Peripherique Monpci charge.\n");
return ENXIO;
}
/* Detach le peripherique. */
static int
mypci_detach(device_t dev)
{
uprintf("Monpci detache!\n");
return 0;
}
/* Appele lors de l'arret du systeme apres sync. */
static int
mypci_shutdown(device_t dev)
{
uprintf("Monpci arrete!\n");
return 0;
}
/*
* routine de suspension du peripherique
*/
static int
mypci_suspend(device_t dev)
{
uprintf("Monpci suspendu!\n");
return 0;
}
/*
* routine de reprise du peripherique
*/
static int
mypci_resume(device_t dev)
{
uprintf("Monpci resume!\n");
return 0;
}
static device_method_t mypci_methods[] = {
/* Interface Peripherique*/
DEVMETHOD(device_probe, mypci_probe),
DEVMETHOD(device_attach, mypci_attach),
DEVMETHOD(device_detach, mypci_detach),
DEVMETHOD(device_shutdown, mypci_shutdown),
DEVMETHOD(device_suspend, mypci_suspend),
DEVMETHOD(device_resume, mypci_resume),
{ 0, 0 }
};
static driver_t mypci_driver = {
"monpci",
mypci_methods,
0,
/* sizeof(struct mypci_softc), */
};
static devclass_t mypci_devclass;
DRIVER_MODULE(mypci, pci, mypci_driver, mypci_devclass, 0, 0);</programlisting>
<para>Informations compl&eacute;mentaires
<itemizedlist>
<listitem><simpara><ulink url="http://www.pcisig.org">PCI
Special Interest Group</ulink></simpara></listitem>
<listitem><simpara>PCI System Architecture, Fourth Edition by
Tom Shanley, et al.</simpara></listitem>
</itemizedlist>
</para>
</sect1>
<sect1>
<title>Les ressources du bus</title>
<para>FreeBSD fournit un m&eacute;canisme orient&eacute; objet pour demander
des ressources du bus parent. Pratiquement tous les p&eacute;riph&eacute;riques
seront un fils membre d'un type de bus (PCI, ISA, USB, SCSI, etc)
et ces p&eacute;riph&eacute;riques n&eacute;cessite des ressources issues de leur bus parent
(comme des segments de m&eacute;moire, des interruptions or des canaux DMA).</para>
<sect2>
<title>Registres d'adresse de base</title>
<para>Pour faire de particuli&egrave;rement utile avec un p&eacute;riph&eacute;rique PCI,
vous aurez besoin d'obtenir les <emphasis>registres d'adresse de base</emphasis>
(Base Address Registers ou BARs) de l'espace de configuration PCI.
Les d&eacute;tails sp&eacute;cifiques au PCI sur l'obtention du registre d'adresse de base
sont masqu&eacute;s dans la fonction <function>bus_alloc_resource()</function>.</para>
<para>Par exemple, un pilote typique aura sa fonction <function>attach()</function>
similaire &agrave; ceci : </para>
<programlisting> sc->bar0id = 0x10;
sc->bar0res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar0id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar0res == NULL) {
uprintf("Allocation memoire du registre PCI de base 0 echouee!\n");
error = ENXIO;
goto fail1;
}
sc->bar1id = 0x14;
sc->bar1res = bus_alloc_resource(dev, SYS_RES_MEMORY, &amp;(sc->bar1id),
0, ~0, 1, RF_ACTIVE);
if (sc->bar1res == NULL) {
uprintf("Allocation memoire du registre PCI de base 1 echouee!\n");
error = ENXIO;
goto fail2;
}
sc->bar0_bt = rman_get_bustag(sc->bar0res);
sc->bar0_bh = rman_get_bushandle(sc->bar0res);
sc->bar1_bt = rman_get_bustag(sc->bar1res);
sc->bar1_bh = rman_get_bushandle(sc->bar1res);
</programlisting>
<para>Des r&eacute;f&eacute;rences pour chaque registre d'adresse de base sont gard&eacute;es
dans la structure <structname>softc</structname> afin qu'elle puisse
&ecirc;tre utilis&eacute;e pour &eacute;crire dans le p&eacute;riph&eacute;rique plus tard.</para>
<para>Ces r&eacute;f&eacute;rences peuvent alors &ecirc;tre utilis&eacute;es pour lire ou &eacute;crire
dans les registres du p&eacute;riph&eacute;rique avec les fonctions <function>bus_space_*</function>.
Par exemple, un pilote peut contenir une fonction raccourci
pour lire dans un registre sp&eacute;cifique &agrave; une carte comme cela :
</para>
<programlisting>uint16_t
board_read(struct ni_softc *sc, uint16_t address) {
return bus_space_read_2(sc->bar1_bt, sc->bar1_bh, address);
}
</programlisting>
<para>De fa<66>on similaire, une autre peut &eacute;crire dans les registres avec : </para>
<programlisting>void
board_write(struct ni_softc *sc, uint16_t address, uint16_t value) {
bus_space_write_2(sc->bar1_bt, sc->bar1_bh, address, value);
}
</programlisting>
<para>Ces fonctions existent en versions 8bit, 16bit et 32bit
et vous devriez utiliser
<function>bus_space_{read|write}_{1|2|4}</function>
en cons&eacute;quence.</para>
</sect2>
<sect2>
<title>Les interruptions</title>
<para>Les interruptions sont allou&eacute;s &agrave; partir du code orient&eacute; objet du
bus de fa<66>on similaire aux ressources m&eacute;moire. D'abord une ressource
IRQ doit &ecirc;tre allou&eacute;e &agrave; partir du bus parent, et alors le
gestionnaire d'interruption doit &ecirc;tre r&egrave;gl&eacute; pour traiter cet IRQ.</para>
<para>A nouveau, un exemple de fonction
<function>attach()</function> en dit plusqu'un long discours.</para>
<programlisting>/* Recupere la ressource IRQ */
sc->irqid = 0x0;
sc->irqres = bus_alloc_resource(dev, SYS_RES_IRQ, &amp;(sc->irqid),
0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->irqres == NULL) {
uprintf("Allocation IRQ echouee!\n");
error = ENXIO;
goto fail3;
}
/* Maitnenant nous choisissons notre gestionnaire d'interruption */
error = bus_setup_intr(dev, sc->irqres, INTR_TYPE_MISC,
my_handler, sc, &amp;(sc->handler));
if (error) {
printf("Ne peut regler l'IRQ\n");
goto fail4;
}
sc->irq_bt = rman_get_bustag(sc->irqres);
sc->irq_bh = rman_get_bushandle(sc->irqres);
</programlisting>
</sect2>
<sect2>
<title>DMA</title>
<para>Sur les PC, les p&eacute;riph&eacute;riques qui veulent utiliser la gestion de
bus DMA doivent travailler avec des adresses physiques. C'est un probl&egrave;me
puisque FreeBSD utilise une m&eacute;moire virtuelle et travaille presque
exclusivement avec des adresses virtuelles. Heureusement, il y a une
fonction <function>vtophys()</function> pour nous aider.</para>
<programlisting>#include &lt;vm/vm.h&gt;
#include &lt;vm/pmap.h&gt;
#define vtophys(virtual_address) (...)
</programlisting>
<para>La solution est toutefois un peu diff&eacute;rente sur Alpha, et
ce que nous voulons r&eacute;ellement est une fonction appel&eacute;e
<function>vtobus()</function>.</para>
<programlisting>#if defined(__alpha__)
#define vtobus(va) alpha_XXX_dmamap((vm_offset_t)va)
#else
#define vtobus(va) vtophys(va)
#endif
</programlisting>
</sect2>
<sect2>
<title>D&#233;sallouer les resources</title>
<para>Il est tr&egrave;s important de d&eacute;sallouer toutes les ressources
qui furent allou&eacute;es pendant <function>attach()</function>.
Unsoin tout particulier doit &ecirc;tre pris pour d&eacute;sallouer
les bonnes choses m&ecirc;me lors d'un &eacute;chec afin que le syst&egrave;me reste
utilisable lorsque votre driver meurt.</para>
</sect2>
</sect1>
</chapter>