OSDev.org

The Place to Start for Operating System Developers
It is currently Sun Jun 16, 2019 12:41 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: My AC97 driver is not work
PostPosted: Mon Feb 25, 2019 12:03 pm 
Offline
Member
Member

Joined: Sat Mar 10, 2018 10:16 am
Posts: 114
Good day!

I follow http://www.lowlevel.eu/wiki/AC97, but my AC97 driver is not playing sound. Sound card is on bus 0, device 4 and function 0.

Code:
//NAM offsets
#define AC97_NAM_PORT_RESET 0x00
#define AC97_NAM_PORT_MASTER_VOLUME 0x02
#define AC97_NAM_PORT_MONO_VOLUME 0x06
#define AC97_NAM_PORT_PC_BEEP 0x0A
#define AC97_NAM_PORT_PCM_VOLUME 0x18
#define AC97_NAM_PORT_EXT_AUDIO_ID 0x28
#define AC97_NAM_PORT_EXT_AUDIO_STC 0x2A
#define AC97_NAM_PORT_EXT_FRONT_SPLRATE 0x2C
#define AC97_NAM_PORT_EXT_LR_SPLRATE 0x0032

//NABM offsets
#define AC97_NABM_PORT_POBDBAR 0x10
#define AC97_NABM_PORT_POLVI 0x15
#define AC97_NABM_PORT_POCONTROL 0x1B
#define AC97_NABM_PORT_GLB_CTRL_STAT 0x60

//BUFFERS
struct buffer_desc{

void *buffer;
unsigned short length;
int reserved : 14;
unsigned int bup : 1;
unsigned int ioc : 1;
} __attribute__((packed));

struct buffer_desc *BufDescList; //Buffer Descriptor List

uint16_t sound_buffer[65536];

void wait(int miliseconds) {
    while(miliseconds>0) {
        for(int i=0; i<10000; i++) {
            inb(0x3F6);  //wait
        }
       
        miliseconds--;
    }
}

void ac97_init(uint16_t volume) {
    pci_write(sound_card_bus, sound_card_device, sound_card_function, 0x04, (pci_get_command(sound_card_bus, sound_card_device, sound_card_function) | 5) );

    //reset
    outw(sound_card_nam_bar + AC97_NAM_PORT_RESET, 42);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_GLB_CTRL_STAT, 2);
    wait(10);

    //set volume
    outw(sound_card_nam_bar + AC97_NAM_PORT_MASTER_VOLUME, (volume<<8) | volume);
    outw(sound_card_nam_bar + AC97_NAM_PORT_MONO_VOLUME, volume);
    outw(sound_card_nam_bar + AC97_NAM_PORT_PC_BEEP, volume);
    outw(sound_card_nam_bar + AC97_NAM_PORT_PCM_VOLUME, (volume<<8) | volume);
    wait(10);

    //sample output rate
    outw(sound_card_nam_bar + AC97_NAM_PORT_EXT_AUDIO_STC, (inw(sound_card_nam_bar + AC97_NAM_PORT_EXT_AUDIO_STC) | 1) );
    wait(10);
    outw(sound_card_nam_bar + AC97_NAM_PORT_EXT_FRONT_SPLRATE, 44100);
    outw(sound_card_nam_bar + AC97_NAM_PORT_EXT_LR_SPLRATE, 44100);
    wait(10);

}

void ac97_play_buffer(void) {
    BufDescList[0].buffer=((uint32_t)(sound_buffer));
    BufDescList[0].length=0xFFFE;
    BufDescList[0].bup=1;

    outl(sound_card_nabm_bar + AC97_NABM_PORT_POBDBAR, (uint32_t)(BufDescList));
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POLVI, 0);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POCONTROL, 0x15);
}


Please, where is bug?


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Mon Feb 25, 2019 12:49 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 1540
Code:
inb(0x3F6);  //wait

This does not tell the computer to wait.

Code:
BufDescList[0].buffer=((uint32_t)(sound_buffer));
...
outl(sound_card_nabm_bar + AC97_NABM_PORT_POBDBAR, (uint32_t)(BufDescList));

If you're using paging, these are both wrong, since the hardware can only work with physical addresses.

There may be other issues that I can't spot since I'm not familiar with AC'97.


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Wed Feb 27, 2019 1:03 pm 
Offline
Member
Member
User avatar

Joined: Fri Oct 27, 2006 9:42 am
Posts: 1455
Location: Athens, GA, USA
Sorry for the late response, but: is this on live hardware, or virtualized/emulated? I don't know if it is relevant, but as with video, audio hardware is not automatically multiplexed in virtual systems. While (to the best of my knowledge) QEMU, Virtualbox, and Bochs do support AC97 emulation, you need to have it enabled in the settings.

_________________
Rev. First Speaker Schol-R-LEA;2 LCF ELF JAM POEE KoR KCO PPWMTF
μή εἶναι βασιλικήν ἀτραπόν ἐπί γεωμετρίαν
Lisp programmers tend to seem very odd to outsiders, just like anyone else who has had a religious experience they can't quite explain to others.


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Thu Feb 28, 2019 10:41 am 
Offline
Member
Member

Joined: Sat Mar 10, 2018 10:16 am
Posts: 114
Very thank you! I change setting for qemu and some strange sound is play! But, after than, irq11 is fire forever. Please, did you know "stop" command for AC97? Or how stop firing irq?


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Tue Mar 05, 2019 4:07 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 557
Location: USA
Hi,

I, myself, have not done much with the AC97, but have started looking over it, and am curious to what you have found.

I did see a few things in your code though:

Code:
//NABM offsets
#define AC97_NABM_PORT_POBDBAR 0x10
#define AC97_NABM_PORT_POLVI 0x15
#define AC97_NABM_PORT_POCONTROL 0x1B
#define AC97_NABM_PORT_GLB_CTRL_STAT 0x60

I don't think that last one is at offset 0x60. Did you mean the Global Control and Global Status registers? They are at 0x2C and 0x30 respectively, and each are 32-bits in size.

Code:
//BUFFERS
struct buffer_desc{
void *buffer;
unsigned short length;
int reserved : 14;
unsigned int bup : 1;
unsigned int ioc : 1;
} __attribute__((packed));

I don't like...I can't even remember...oh bit-fields like this. It all depends on the compiler whether it places them in big-endian or little-endian order, and whether it actually only uses the said amount of bits or not. It is best to not use bit-fields and do the read/write yourself.

Code:
void wait(int miliseconds) {
    while(miliseconds>0) {
        for(int i=0; i<10000; i++) {
            inb(0x3F6);  //wait
        }
        miliseconds--;
    }
}

As mentioned before, this may or may not wait for very long. In fact, it could wait for an extremely long time if the hardware you are reading from faults due to the consecutive fast reading of the same register over and over.... Does happen....

Code:
    //reset
    outw(sound_card_nam_bar + AC97_NAM_PORT_RESET, 42);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_GLB_CTRL_STAT, 2);

The specs say any write to the reset register is a reset. Why 42?
Also, writing 2 to 0x60 is probably not where you think it should be.

Code:
void ac97_play_buffer(void) {
    BufDescList[0].buffer=((uint32_t)(sound_buffer));
    BufDescList[0].length=0xFFFE;
    BufDescList[0].bup=1;

Again, I would not rely upon the fact that the compiler did or did not create these bit-fields like you think it should have.

Anyway, I am just curious to see what you have found. Does your code continue to see an interrupt continuously afterward?

The specs say that you can clear the IOC and RUN bits in that channel's Control register (x_CR, offset 0x[channel]B) or simply "empty" the descriptor table.

Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Tue Mar 05, 2019 9:05 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 557
Location: USA
Digging a little deeper, once the interrupt fires, the x_SR register (the status register for that channel) will be set to the status. It looks like the hardware will fire an interrupt until you clear that register, remembering it is a WC register (Write/Clear, writing a 1 to a bit clears it).

Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Thu Mar 07, 2019 1:11 pm 
Offline
Member
Member

Joined: Sat Mar 10, 2018 10:16 am
Posts: 114
Thank you! I am grow up with code. The Buffer Descriptor List must be:

Code:
struct buffer_desc{
    uint32_t buffer;  //reference to buffer
    uint16_t length;  //lenght of buffer
    uint16_t type;  //type of action after play buffer
};

struct buffer_desc BufDescList[32]; //It must contain 32 buffers


Maximal lenght of buffer is 0xFFFE what mean array of lenght 0xFFFF.

Type of action can be:
0x8000 - IOC = after play buffer is fire irq(probably 0x0000 is too same)
0x4000 - BUP = after play buffer isn`t fire irq, only is start play next buffer.

I am change defines:
Code:

//NABM offsets
#define AC97_NABM_PORT_PISTATUS 0x06
#define AC97_NABM_PORT_PICONTROL 0x0B
#define AC97_NABM_PORT_POBDBAR 0x10
#define AC97_NABM_PORT_POLVI 0x15
#define AC97_NABM_PORT_POSTATUS 0x16
#define AC97_NABM_PORT_MCSTATUS 0x26
#define AC97_NABM_PORT_POCONTROL 0x1B
#define AC97_NABM_PORT_MCCONTROL 0x2B


Irq was fired because it must be acknowledge with reading and writing to status registers:
Code:
void ac97_irq(void) {
    int pi=0;
    int po=0;
    int mc=0;

    pi = (inb(sound_card_nabm_bar + AC97_NABM_PORT_PISTATUS) & 0x1C);
    po = (inb(sound_card_nabm_bar + AC97_NABM_PORT_POSTATUS) & 0x1C);
    mc = (inb(sound_card_nabm_bar + AC97_NABM_PORT_MCSTATUS) & 0x1C);

    outb(sound_card_nabm_bar + AC97_NABM_PORT_PISTATUS, pi);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POSTATUS, po);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_MCSTATUS, mc);
}


Method for playing is probably:
Code:
void ac97_play(void) {
    BufDescList[0].buffer=((uint32_t)(sound_buffer));
    BufDescList[0].length=0xFFFE;
    BufDescList[0].type=0x8000;

    outl(sound_card_nabm_bar + AC97_NABM_PORT_POBDBAR, (uint32_t)(BufDescList));
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POLVI, 0);
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POCONTROL, 0x15);
}


And for stopping:
Code:
void ac97_stop(void) {
    outb(sound_card_nabm_bar + AC97_NABM_PORT_POCONTROL, 0x00);
}


For resetting, I copy it code from lowlevel tutorial, but it contains lot of errors, than maybe it is error too. But from my experiments sound is work also without this code.

But I have new problem. In Qemu, sound is only one tone(and it have bad quality) and changing buffer isn`t change it. And sometimes Qemu isn`t play tone, only fire irq. I don`t now why.


Top
 Profile  
 
 Post subject: Re: My AC97 driver is not work
PostPosted: Thu Mar 07, 2019 3:33 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 557
Location: USA
From what I gather so far:

The value of 0xFFFE in the Buffer List Entry is not the length of the buffer but the count of 16-bit samples within the buffer. Therefore, a value of 0xFFFE means 65534 16-samples, a buffer size of 131,068 bytes.

Also, since the Current Entry register is read-only, you must start with that entry when inserting into the Buffer List. For example, after you play an audio buffer that used 2 of the 32 entries, entry 0 and 1, the next time you play something, the next entry played will start at entry 2, not entry 0 as you might think. You must use a head/tail type buffer ring and keep track of the Current Entry and Last Valid Entry when inserting buffers into the ring. The Last Valid Entry (LVE) register is writable, the Current Entry register is not.

Ben
- http://www.fysnet.net/osdesign_book_series.htm


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Acceleration, Bing [Bot] and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group