PCI devices usually have some memory assosiated with them. This is usually for setting properties of the device. Often these registers are flags or contain multiple peices of information so writing simple structures to access individual flags or bytes helps readability and usability of your code. Here is a sample register that I recommend you use as a basis for each of your registers.
public struct RevisionRegister
{
private UInt32 data;
private RevisionRegister(UInt32 a)
{
this.data = a;
}
#region Implicit Conversions
public static implicit operator UInt32(RevisionRegister a)
{
return a.data;
}
public static implicit operator RevisionRegister(UInt32 u)
{
return new RevisionRegister(u);
}
#endregion
#region Field Accessors
// example of a bit accessor
public bool Bit1
{
get { return (data & 0x1) == 0x1; }
set { data = (data & 0xfffffffe) | (UInt32)(value ? 0x1 : 0x0); }
}
//example of a byte accessors
public byte Major
{
get { return (byte)((data & 0xff00) >> 8); }
set { data = (data & 0xffff00ff) | (UInt32)(value << 8); }
}
public byte Minor
{
get { return (byte)(data & 0xff); }
set { data = (data & 0xffffff00) | (UInt32)(value); }
}
#endregion
}
As you can see, access the flags or bytes etc this way is alot clearer than doing it inline in your code. the implicit casting operators make your job alot easier later :) It saves you having to cast UInt32s (or whatever) into your register type and back again.
For example:
RevisionRegister rev = GetRevision();
Console.WriteLine(rev.Minor);
rev.Major = 2;
SetRevision(rev);
Next, create a class with all your registers in it to read and write to them: (or only read is they are read only)
public class ExampleRegisters
{
private MemoryAddressSpace MyMemoryRegisters;
public ExampleRegisters(MemoryAddressSpace mem)
{
MyMemoryRegisters = mem;
}
public RevisionRegister Revision {
get { return MyMemoryRegisters.Read32(0x40); }
set { MyMemoryRegisters.Write32(0x40, value); }
}
// etc
}
Your DeviceRegisters class should take the MemoryAddressSpace (which provides memory access to the registers) in the constructor (this is taken from the PCIDevice). Then properties are defined for each register. In the above example it assumes the RevisionRegister is the register at offset 0x40. There should be a property for each register.
Now you need to create the DeviceRegister instance at the construction of your DeviceDriver. But first:
How to find a PCIDevice:
For now PCIDevices are stored in the collection
Cosmos.Hardware.PC.Bus.PCIBus.Devices (this might be changed later to make them be added to the Device class like other devices.)
This collection should be created during Boot. Your driver should have a ScanDevices function. This will probably change to a DriverProvider interface when we get interface support. This example finds usb controllers:
public static void ScanDevices()
{
foreach (PCIDevice pci in Cosmos.Hardware.PC.Bus.PCIBus.Devices)
{
if (pci.ClassCode == 0x0c && //bus
pci.SubClass == 0x03 && //usb
pci.ProgIF == 0x10) //ohci :D
{
//(as this is an open standard, vendor/device specific
implementations should all work the same)
Device.Add(new USBHostOHCI(pci)); // add the device to our
list of devices
}
}
}
Since OHCI (usb 1.0) has a standard interface, specific vendor implementations are probably not needed, so this checks for a certain class/subclass and programming interface of device. The following example checks for a specific chipset (the RTL chipset)
public static void ScanDevices()
{
foreach (PCIDevice device in Cosmos.Hardware.PC.Bus.PCIBus.Devices)
{
if (device.VendorID == 0x10EC && device.DeviceID == 0x8139)
Device.Add(new RTL8139(device));
}
}
So, on instantiation of your driver you should keep a reference to
it's PCIDevice and create your Register class:
public MyFakeDevice : Device
{
private PCIDeviceNormal device;
private ExampleRegister regs;
public MyFakeDevice(PCIDevice device)
{
this.device = device as PCIDeviceNormal;
this.regs = new ExampleRegisters(device.GetAddressSpace(0) as MemoryAddressSpace);
// check documentation to see which memory address space
you should be using
}
}
So now you have the basics of a device driver!
Good luck writing the rest of your code :)
Stephen Remde (smremde)