AC97 — Änderungen 5966 → 7643

Zeile 20: Zeile 20:
 
QEMU (0.10.0) und VirtualBox (2.1.4) nutzen ICH. Die in diesem Tutorial vorgestellten Codes funktionieren leider anscheinend auch nur dort.
 
QEMU (0.10.0) und VirtualBox (2.1.4) nutzen ICH. Die in diesem Tutorial vorgestellten Codes funktionieren leider anscheinend auch nur dort.
  
−
Die Geräte besitzen zwei I/O-Räume, der erste heißt NAM-BAR (Native Audio Mixer BAR) und der zweite NABM-BAR (Native Audio Bus Master BAR). Mit den Registern in NAM-BAR wird der Mixer gesteuert (also Lautstärke, Samplerate, ...), mit denen in NABM-BAR wird das Abspielen gesteuert (Play/Pause, Soundbuffer, ...). Um diese I/O-Räume zu aktivieren und den Bus Master zu starten, muss der Wert des Registers "COMMAND" im PCI-Konfigurationsraum mit 0x05 "ge-Or-t" werden. Ein Beispiel:
+
Die Geräte besitzen zwei I/O-Räume, der erste heißt NAM-BAR (Native Audio Mixer BAR) und der zweite NABM-BAR (Native Audio Bus Master BAR). Mit den Registern in NAM-BAR wird der Mixer gesteuert (also Lautstärke, Samplerate, ...), mit denen in NABM-BAR wird das Abspielen gesteuert (Play/Pause, Soundbuffer, ...). Um diese I/O-Räume zu aktivieren und den Bus Master zu starten, muss der Wert des Registers „COMMAND“ im PCI-Konfigurationsraum mit 0x05 „ge-Or-t“ werden. Ein Beispiel:
  
 
<c>pciConfigWrite(Bus, Geraet, Funktion, PCI_COMMAND, pciConfigRead(Bus, Geraet, Funktion, PCI_COMMAND) | 5)</c>
 
<c>pciConfigWrite(Bus, Geraet, Funktion, PCI_COMMAND, pciConfigRead(Bus, Geraet, Funktion, PCI_COMMAND) | 5)</c>
Zeile 31: Zeile 31:
 
Dies kann zum Beispiel so geschehen:
 
Dies kann zum Beispiel so geschehen:
  
−
<c>void delay(int ms); //Wartet "ms" Millisekunden
+
<c>void delay(int ms); //Wartet „ms“ Millisekunden
−
uint16_t inw(int port); //Liest einen Wert vom Port "port" ein
+
uint16_t inw(int port); //Liest einen Wert vom I/O-Port „port“ ein
−
void outb(int port, uint8_t value); //Gibt den Wert "value" am Port "port" aus
+
void outb(int port, uint8_t value); //Gibt den Wert „value“ am I/O-Port „port“ aus
−
void outw(int port, uint16_t value); //Gibt den Wert "value" am Port "port" aus
+
void outw(int port, uint16_t value); //Gibt den Wert „value“ am I/O-Port „port“ aus
  
 
int nambar; //NAM-BAR
 
int nambar; //NAM-BAR
Zeile 42: Zeile 42:
 
//Code (in einer beliebigen Funktion):
 
//Code (in einer beliebigen Funktion):
 
outw(nambar + PORT_NAM_RESET, 42); //Resetten (jeder Wert ist hier möglich)
 
outw(nambar + PORT_NAM_RESET, 42); //Resetten (jeder Wert ist hier möglich)
−
outb(nabmbar + PORT_NABM_GLB_CTRL_STAT, 0x02); //Auch hier - 0x02 ist aber verpflichtend
+
outb(nabmbar + PORT_NABM_GLB_CTRL_STAT, 0x02); //Auch hier – 0x02 ist aber verpflichtend
 
delay(100);
 
delay(100);
 
volume = 0; //Am lautesten!
 
volume = 0; //Am lautesten!
Zeile 77: Zeile 77:
 
|}
 
|}
  
−
Achtung: Das sind zwei Samples - pro Kanal eins. So ergibt sich pro Buffer eine maximale Größe von 65536 * 2 Bytes = 128 Kilobytes. Dem Gerät können maximal 32 Buffer übergeben werden, die es hintereinander abspielt. Das sind dann maximal 4 MB (128 kB * 32) oder 23,8 Sekunden (44,1 kHz) bzw. 21,8 Sekunden (48 kHz). Man sieht, dass es sich um eine große Datenmenge bei relativ kurzer Spieldauer handelt - daher sind viel mehr als 32 Buffer wohl auch vom Speicherverbrauch her übertrieben. Die Lösung liegt darin, schon abgespielte Datenbuffer mit neuen Daten zu füllen und erneut abspielen zu lassen. So durchläuft das Gerät alle Buffer immer wieder, während der Treiber deren Inhalte immer wieder auffrischt.
+
Achtung: Das sind zwei Samples – pro Kanal eins. So ergibt sich pro Buffer eine maximale Größe von 65536 * 2&nbsp;Bytes = 128&nbsp;Kilobytes. Dem Gerät können maximal 32 Buffer übergeben werden, die es hintereinander abspielt. Das sind dann maximal 4&nbsp;MB (128&nbsp;kB * 32) oder 23,8 Sekunden (44,1&nbsp;kHz) bzw. 21,8 Sekunden (48&nbsp;kHz). Man sieht, dass es sich um eine große Datenmenge bei relativ kurzer Spieldauer handelt – daher sind viel mehr als 32 Buffer wohl auch vom Speicherverbrauch her übertrieben. Die Lösung liegt darin, schon abgespielte Datenbuffer mit neuen Daten zu füllen und erneut abspielen zu lassen. So durchläuft das Gerät alle Buffer immer wieder, während der Treiber deren Inhalte immer wieder auffrischt.
  
 
Kommen wir jetzt aber zur Praxis. Die Buffer werden durch acht Bytes lange Buffer Descriptors beschrieben, diese sind so aufgebaut:
 
Kommen wir jetzt aber zur Praxis. Die Buffer werden durch acht Bytes lange Buffer Descriptors beschrieben, diese sind so aufgebaut:
Zeile 93: Zeile 93:
  
 
* IOC (Interrupt On Completion): Wenn gesetzt, so sendet das Gerät ein Interrupt, wenn dieser Buffer abgespielt wurde.
 
* IOC (Interrupt On Completion): Wenn gesetzt, so sendet das Gerät ein Interrupt, wenn dieser Buffer abgespielt wurde.
−
* BUP (Buffer Underrun Policy): Dieses Bit wird dann wichtig, wenn dieser Buffer der letzte abzuspielende oder der nächste Buffer noch nicht bereit ist: Ist das Bit dann nicht gesetzt, so wird nach dem Abspielen das letzte Sample dieses Buffers gehalten, ist es gesetzt, so wird "0" gesendet. Typischerweise ist es beim letzten abzuspielenden Buffer gesetzt.
+
* BUP (Buffer Underrun Policy): Dieses Bit wird dann wichtig, wenn dieser Buffer der letzte abzuspielende oder der nächste Buffer noch nicht bereit ist: Ist das Bit dann nicht gesetzt, so wird nach dem Abspielen das letzte Sample dieses Buffers gehalten, ist es gesetzt, so wird „0“ gesendet. Typischerweise ist es beim letzten abzuspielenden Buffer gesetzt.
  
−
Die Buffer Descriptors trägt man in eine Liste ein, die (logischerweise) Buffer Descriptor List genannt wird. Hier passen - wie gesagt - maximal 32 Deskriptoren rein (also 256 Bytes maximale Länge). Die Anfangsadresse dieser Liste wird dann dem Gerät mitgeteilt, dann die Nummer des letzten gültigen Elements (höchstens 31) und schließlich kann man (symbolisch) auf den Play-Knopf drücken. Das könnte zum Beispiel so aussehen, hiermit können maximal besagte 23,8 Sekunden abgespielt werden:
+
Die Buffer Descriptors trägt man in eine Liste ein, die (logischerweise) Buffer Descriptor List genannt wird. Hier passen – wie gesagt – maximal 32 Deskriptoren rein (also 256 Bytes maximale Länge). Die Anfangsadresse dieser Liste wird dann dem Gerät mitgeteilt, dann die Nummer des letzten gültigen Elements (höchstens 31) und schließlich kann man (symbolisch) auf den Play-Knopf drücken. Das könnte zum Beispiel so aussehen, hiermit können maximal besagte 23,8 Sekunden abgespielt werden:
  
 
<c>struct buf_desc
 
<c>struct buf_desc
Zeile 115: Zeile 115:
 
{
 
{
 
   BufDescList[i].buffer = /* Physische Adresse des jeweiligen Buffers */;
 
   BufDescList[i].buffer = /* Physische Adresse des jeweiligen Buffers */;
−
   if (size >= 0x20000) //Noch mehr als 128 kB, also wird der Buffer voll
+
   if (size >= 0x20000) //Noch mehr als 128&nbsp;kB, also wird der Buffer voll
 
   {
 
   {
 
     //Maximale Länge ist 0xFFFE und NICHT 0xFFFF! Links und rechts
 
     //Maximale Länge ist 0xFFFE und NICHT 0xFFFF! Links und rechts
 
     //müssen gleich viele Samples sein, daher muss diese Zahl gerade sein.
 
     //müssen gleich viele Samples sein, daher muss diese Zahl gerade sein.
 
     BufDescList[i].length = 0xFFFE;
 
     BufDescList[i].length = 0xFFFE;
−
     size -= 0x20000; //128 kB weg
+
     size -= 0x20000; //128&nbsp;kB weg
 
   }
 
   }
 
   else
 
   else
Zeile 141: Zeile 141:
 
outb(nabmbar + PORT_NABM_POCONTROL, 0x15); //Abspielen, und danach auch Interrupt generieren!</c>
 
outb(nabmbar + PORT_NABM_POCONTROL, 0x15); //Abspielen, und danach auch Interrupt generieren!</c>
  
−
Funktioniert unter QEMU 0.10.0 und VirtualBox 2.1.4... Im Speicher müssen, wie oben gesagt, 16-Bit-Stereosamples mit einer Samplerate von 44,1 kHz (oder was ihr auch immer eingestellt habt), stehen.
+
Funktioniert unter QEMU 0.10.0 und VirtualBox 2.1.4... Im Speicher müssen, wie oben gesagt, 16-Bit-Stereosamples mit einer Samplerate von 44,1&nbsp;kHz (oder was ihr auch immer eingestellt habt), stehen.
  
 
== Anhang ==
 
== Anhang ==
Zeile 162: Zeile 162:
 
== Hinweise zu den Codes ==
 
== Hinweise zu den Codes ==
  
−
Die Codes stammen (ganz leicht modifiziert) aus dem Betriebssystem MyXomycota, stehen somit also unter der GPL. Da ich aber die Lizenz bestimmen kann, erlaube ich natürlich auch die Freigabe unter einer BSD-artigen Lizenz, der LGPL oder unter den CC-Lizenzen, die "by" enthalten. Credits wären natürlich schön, wenn der Code größtenteils unverändert übernommen wird.
+
Die Codes stammen (ganz leicht modifiziert) aus dem Betriebssystem MyXomycota, stehen somit also unter der GPL. Da ich aber die Lizenz bestimmen kann, erlaube ich natürlich auch die Freigabe unter einer BSD-artigen Lizenz, der LGPL oder unter den CC-Lizenzen, die „by“ enthalten. Credits wären natürlich schön, wenn der Code größtenteils unverändert übernommen wird.
 
Wer den Code unter anderen Lizenzen verwenden will, kann mich einfach fragen, ich sehe da keine Hindernisse. Ich bin fast täglich im IRC (irc.euirc.net#lost).
 
Wer den Code unter anderen Lizenzen verwenden will, kann mich einfach fragen, ich sehe da keine Hindernisse. Ich bin fast täglich im IRC (irc.euirc.net#lost).