Configuring Intel HDA

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: Configuring Intel HDA

Post by SpyderTL »

I'm going to save you a ton of work, and give you the answer you are looking for.

In QEMU, the HDAudio base address is 0xFEBF0000. You can simply hard-code this address for your Intel HD Audio logic, for now.

Just promise that you will come back later and replace it with working PCI device enumeration logic. :)

You will know it's working properly when the PCI device address you get for the HD Audio device matches this value.

You're welcome.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

The problem is that I feel like I'm missing something. I wonder if we're going about this the wrong way, so might someone start from the beginning and explain the offset calculation method? I'm sorry, I'm just struggling to understand how I would go about actually implementing this. If I have a 32-bit integer of all zeros, and set bits 0-7 to 4 (100b), than the number becomes '4h' and not, say' 'Ah'. There's a disconnect between me and you guys and I'm trying to close the gap but I feel like its not being explained clearly enough and the explanations just raise more questions. :(
Edit: I figured it out, thanks to the code example provided by Klakap. Thanks! :)
User avatar
eekee
Member
Member
Posts: 892
Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:

Re: Configuring Intel HDA

Post by eekee »

Binary operations take some getting used to! "Or" just merges the bits without carrying over the excess to higher bits. It will act like addition if there are no ones in the same positons in both numbers. To illustrate:

Acting the same, in decimal: 4 + 2 = 6; 4 or 2 = 6. Acting the same, in binary: 1 0 0 + 1 0 = 1 1 0; 1 0 0 or 1 0 = 1 1 0.

Acting different, in decimal: 6 + 2 = 8; 6 or 2 = 6. Acting different, in binary: 1 1 0 + 1 0 = 1 0 0 0; 1 1 0 or 1 0 = 1 1 0.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

eekee wrote:Binary operations take some getting used to! "Or" just merges the bits without carrying over the excess to higher bits. It will act like addition if there are no ones in the same positons in both numbers. To illustrate:

Acting the same, in decimal: 4 + 2 = 6; 4 or 2 = 6. Acting the same, in binary: 1 0 0 + 1 0 = 1 1 0; 1 0 0 or 1 0 = 1 1 0.

Acting different, in decimal: 6 + 2 = 8; 6 or 2 = 6. Acting different, in binary: 1 1 0 + 1 0 = 1 0 0 0; 1 1 0 or 1 0 = 1 1 0.
Thanks! I'm now using the offsets in the PCI article but am still getting zeros in all five BARs. Not really sure what I'm messing up now...
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Configuring Intel HDA

Post by DavidCooper »

What are you getting for the four bytes at offset 8 then? I want to see what you're getting for the bytes at A and B, because A should contain the value 3 and B should hold a 4, if you're looking at an HDA device.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

Offset 8 is 1. (Bits 0-3 are 1 -- bits 4-31 are unset.)
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Configuring Intel HDA

Post by DavidCooper »

Ethin wrote:Offset 8 is 1. (Bits 0-3 are 1 -- bits 4-31 are unset.)
Well, your problem is in finding the HDA device, because you're looking in the wrong place for it. You say in your first post "My OS detects the audio controller:"
PCI: probe: found Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (Multimedia controller)
I don't know how you did that probe to find that information. How are you finding PCI devices? My OS currently uses the "brute fore" method described on the PCI page to find out what's there, then it checks the class and subclass codes of anything that's present to see if it's a device which my OS can handle, specifically looking for USB controllers and HDA. In each case, that involves looking at offset 8 to see the values held by offsets A and B. That means that the four bytes I send to the PCI CONFIG_ADDRESS port go through all 256 possible bus numbers (using bits 16-23) and using each one 32 times so that I can go through all possible device numbers for each (using bits 11-15). If you aren't doing something of that kind and successfully finding the right class and subclass values, I don't know how you're identifying the HDA device to look for its BAR 0. Clearly you're not finding it if you aren't finding the right class and subclass values there. You need to post your code for finding PCI devices and identifying HDA.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

My code for finding PCI devices is as follows, in rust:

Code: Select all

fn get_vendor_id(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0)
}

fn get_device_id(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 2)
}

fn get_class_id(bus: u16, device: u16, function: u16) -> u32 {
    (read_word(bus, device, function, 0xA) & !0x00FF) >> 8
}

fn get_prog_if(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0x8).get_bits(8..15)
}

fn get_header_type(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0x0C).get_bits(16..23)
}

fn get_subclass_id(bus: u16, device: u16, function: u16) -> u32 {
    (read_word(bus, device, function, 0x08) & !0xFF00)
}

fn get_status(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0x04).get_bits(24..31)
}

fn get_command(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0x04).get_bits(16..23)
}

fn get_rev(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0x08).get_bits(0..7)
}
This roughly translates to (in C):

Code: Select all

// This code is only provided to make the C code work properly. The syntax is a bit different in rust
int32_t get_bit(int32_t num, uint8_t bit) {
    if (bit < 32) {
        return (num & (1 << bit));
    } else {
        return -1;
    }
}

int32_t get_bits(int32_t number, uint8_t start, uint8_t end) {
    if (start < 32 && end <= 32 && start < end) {
        // shift away high bits
        int32_t bits = number << (32 - end) >> (32 - end);
        // shift away low bits
        return bits >> start;
    } else {
        return -1;
    }
}

// The typedefs u8, u16, u32, u64, i8, ... map to their equivalent C types

u32 get_vendor_id(u16 bus, u16 device, u16 function) {
    return read_word(bus, device, function, 0);
}

u32 get_device_id(u16 bus, u16 device, u16 function) {
    return read_word(bus, device, function, 2);
}

u32 get_class_id(u16 bus, u16 device, u16 function) {
    return (read_word(bus, device, function, 0xA) & !0x00FF) >> 8;
}

u32 get_prog_if(u16 bus, u16 device, u16 function) {
    return get_bits(read_word(bus, device, function, 0x08), 8, 15);
}

u32 get_header_type(u16 bus, u16 device, u16 function) {
    return get_bits(read_word(bus, device, function, 0x0C), 16, 23);
}

u32 get_subclass_id(u16 bus, u16 device, u16 function) {
    return (read_word(bus, device, function, 0x08) & !0xFF00);
}

u32 get_status(u16 bus, u16 device, u16 function) {
    return get_bits(read_word(bus, device, function, 0x04), 24, 31);
}

u32 get_command(u16 bus, u16 device, u16 function) {
    return get_bits(read_word(bus, device, function, 0x04), 16, 23);
}

u32 get_rev(u16 bus, u16 device, u16 function) {
    return get_bits(read_word(bus, device, function, 0x08), 0, 7);
}
I'm doing the brute-force method to find PCI devices as well. My kernel loops through the buses, slots and functions:

Code: Select all

// C (original is rust)
void probe() {
    for (int bus = 0; bus < 256; bus++) {
        for (int slot = 0; slot < 32; slot++) {
            for (int function = 0; function < 8; function++) {
                // Get vendor, device, class and subclass codes.
                u32 vendor = get_vendor_id(bus, slot, function);
                if (vendor == 0xFFFF) {
                    continue;
                }
                u32 device = get_device_id(bus, slot, function);
                u32 class = get_class_id(bus, slot, function);
                u32  subclass = get_subclass_id(bus, slot, function);
                // Assumed printf function
                printf(
                    "PCI: probe: found %s %s (%s)",
                    get_vendor_string(vendor),
                    get_device_string(device),
                    get_subclass_string(class, subclass)
                );
// continue probe...
I get vendor strings and so on by doing something majorly inefficient, but it works for now. I'll definitely change it later though! My kernel has a file, pcidb.rs, which embeds the entire PCI ID repository as a giant switch statement. (In rust its called a match statement, and each "case" in a switch statement is called an "arm".) I know, its majorly inefficient and disgusting, which is why I'll definitely change it later! :) Modifying the printf statement (in my kernel its printkln) to print device data, I get this for the HDA device:
PCI: probe: found Intel Corporation 82801FB/FBM/FR/FW/FRW (ICH6 Family) High Definition Audio Controller (Multimedia audio controller)
PCI: probe: codes: vendor = 8086h, device = 2668h, class = 4h, subclass = 1h, bus = 0h, slot = 3h, function = 0h
PCI: probe: Offset 8: 1, bits (4 each): 1, 0, 0, 0, 0, 0, 0, 0
nullplan
Member
Member
Posts: 1801
Joined: Wed Aug 30, 2017 8:24 am

Re: Configuring Intel HDA

Post by nullplan »

Ethin wrote:

Code: Select all

fn get_vendor_id(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 0)
}

fn get_device_id(bus: u16, device: u16, function: u16) -> u32 {
    read_word(bus, device, function, 2)
}
So what is that last argument to read_word()? Usually, it would be an offset in bytes or in words, depending on your use. So get_device_id() doesn't really make sense to me, but it seems to work for you. I would have thought that last argument needs to be 0 in both cases, only you cut out different bits from the result.
Carpe diem!
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

The last argument to read_word is the offset (in words). Its pretty much taken directly from the wiki.
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Configuring Intel HDA

Post by DavidCooper »

class = 4h, subclass = 1h
That's a step forward. The PCI wiki page says that's a multimedia audio controller, while subclass 3h is an audio device (and can be AC97 or HDA). I've only ever encountered the latter, but it now looks as if there are two different subclass values that can lead to an HDA device. The puzzle now is why the BAR 0 address is zero. I'd try going through all eight function numbers to see if the device is actually made available in one of those. Spyder probably knows the answer to this though.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

My PCI code may be broken somehow. Could that be the case?
User avatar
DavidCooper
Member
Member
Posts: 1150
Joined: Wed Oct 27, 2010 4:53 pm
Location: Scotland

Re: Configuring Intel HDA

Post by DavidCooper »

Ethin wrote:My PCI code may be broken somehow. Could that be the case?
I don't know, but you are getting a class and subclass that look viable. Have you tried looking through the other seven function numbers (bits 8 to 10) at that location (without changing the bus number and device number) to see if the HDA device is made available on one of those. If there's nothing at any of them, then I don't know what else to try, but people may be holding back from making suggestions until you've confirmed that you've checked those other function numbers.
Help the people of Laos by liking - https://www.facebook.com/TheSBInitiative/?ref=py_c

MSB-OS: http://www.magicschoolbook.com/computing/os-project - direct machine code programming
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: Configuring Intel HDA

Post by Ethin »

If you mean the prog if, status, and command offsets, I have those, which may answer your question:
I added extra codes after the existing ones in the kernel PCI debug output. Here they are now:
PCI: probe: codes: vendor = 8086h, device = 2668h, class = 4h, subclass = 1h, prog if=0h, rev=1h, status=0h, command=0h, bus = 0h, slot = 3h, function = 0h
Here is the other information from the kernel:
* BIST is 0
* The header type is 00h.
* Latency timer is 0h.
* Cache line size is 0h.
In the register table for header type 00h (I called it the "General Device Table"):
* BARS0-5, cis PTR, subsystem ID, subsystem vendor ID, expansion ROM address, max latency, min grant, and caps PTR are all 0h.
* Interrupt pin is 01h.
* Interrupt line is 11 (43 in my case).
Does that answer your question?
User avatar
eekee
Member
Member
Posts: 892
Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:

Re: Configuring Intel HDA

Post by eekee »

If this doesn't work out, I've got a little suggestion I'm putting out here while I remember: USB audio. It's not a magic pill, USB can be trouble too, but once you've got the host controller working there's a standard protocol for USB audio, and the hardware can be cheap.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
Post Reply