Ok we have decided on the more flexible approach of using IOKit
matching to bring in slave protocol drivers.
In this email I shall discuss some of the tricks of bringing up a
'family' library. And how to structure the project in such a way that
it appears to be a single 'driver(kext)'. I will document the most
formal way of doing things, it is possible to take various shortcuts
once you know what you are doing.
The key to creating a matched in layer of drivers is to create a nub.
In general a nub does a couple of things but its single most important
job is to allow a client driver to 'communicate' with a remote service,
i.e. something that provides addressing information. For example:-
consider an IOUSBDevice, it is the nub that the USB family publishes
and it also provides 'addressing' information, specifically if you open
a 'bulk' pipe using this nub then the client doesn't need concern
itself with the actual addressing of the packets, the nub will
automatically hand over that information to the controller.
Once you have worked out the APIs that you would like to publish then
code up a nub as a simple IOService subclass make sure that you get the
OSMetaClass macros right (this data are used to determine the type and
inheritance of the new nub). One function that you will also need is
the matchPropertyTable() function. This function is the guts of the
passive matching system.
For example:- (Simplified version of the IOSerialBSDClient matching
system)
bool IOSerialBSDClient::
matchPropertyTable(OSDictionary *table)
{
if (!super::matchPropertyTable(table)) {
return false; // One of the name based matches has failed,
that's it.
}
// Do some name matching
return compareProperty(table, gIOTTYDeviceKey)
&& compareProperty(table, gIOTTYBaseNameKey)
&& compareProperty(table, gIOTTYSuffixKey)
&& compareProperty(table, gIOCalloutDeviceKey)
}
Note the guts of the matching system is done by the compareProperty()
function, see the HeaderDoc for details but essentially it makes sure
that either the desired key does not exist in which case it returns
true or that the desired key is the same in the nub's properties and in
the matching dictionary passed in in table. If the matching is not
identical then false is returned.
Here you probably would just have a single property to compare that is
some sort of type of the nub, I suggest a property like {
edu_duke_cs_NubType = Ethernet; }. Your implementation of
matchPropertyTable would then come down to (This is all of the matching
you need for a single key match, pretty cool huh!)
Finally make sure that you put the nub's header somewhere where your
other projects can include it.
So now we have a nub class that also does a simple type check match now
we have to create it and cause it to be matched. The is pretty simple,
in your start() routine do some thing like this.
... // other initilisation and work loop creation goes here.
// Get the master nub creation table from our properties
// BTW there is no need to do a copyProperty as we can rely on our
properties
// being valid until we return from start, after that all bets are
basically off though
// you have been warned.
OSArray *nubList = OSDynamicCast(OSArray,
getProperties(gMPIMasterNubListKey));
if (!nubList)
return true; // Nothing more to do just return success
OSCollectionIterator *nubIter =
OSCollectionIterator::withCollection(nubList);
OSDictionary *nubProperties;
while ( (nubProperties = (OSDictionary *)
keysIter->getNextObject()) ) {
if (OSDynamicCast(OSDictionary, nubProperties) {
edu_duke_cs_MPINub *nub = OSTypeAlloc(edu_duke_cs_MPINub);
if (nub) {
if (!nub->init(nubProperties)
|| !nub->attach(this)) {
nub->release();
continue; // Probably should log the failure too
}
// We now have a valid nub that is connected into the service plane.
time to start matching
nub->registerService(); // That's it - matching has started now
}
}
}
nubIter->release();
return true;
}
The last thing to do with the Master driver is to publish it as a
kernel extension library. To do this you add the
OSBundleCompatibleVersion property to your Master Drivers Info.plist
(at the top level, not in the IOKitPersonality Property)
I shan't cover the writing of the protocol driver. It is basically a
normal driver that inherits from any family that seems appropriate
(IOEthernetController in our example) except it uses your nub as a
provider class and your 'Type' property to match.
Notes:
1> It is possible to short circuit the entire nub creation by calling
registerService() on your master driver and using IOMatchCategory to
allow multiple drivers to match against it. This is sort of easier but
I find that once I have created the nub class all sorts of interesting
things end up living there it is just too convenient.
2> On the other hand don't go overboard. Don't create multiple nub
classes one per protocol, remember that all IOService subclasses use a
minimum of 4K of wired resource per class, (not per instance). Also it
doesn't really make sense, it is the job of the nub to publish the
'Master' driver's interfaces not a tailored interface for each client.
3> If you want to go whole hog you would design a true Family - Master
- Slave complex delivered in three kexts. The Family would represent
the super class of the hardware and the nub. Then you master driver
would be a subclass using RRBC interfaces published in the Parent.
4> Kext On Disk Structure
edu_duke_cs_MPIFamily.kext
Contents
PlugIns
edu_duke_cs_MPIMaster.kext
Contents
Info.plist # Contains the Master's matching property and the Nub list
MacOS
edu_duke_cs_MPIMaster
edu_duke_cs_MPIEthernet.kext
Contents
Info.plist # Contains the Nub's matching property OSBundleRequired
of the Master
MacOS
edu_duke_cs_MPIEthernet
5> Teardown. I suggest in development you provide some 'setProperties'
ioctl that can create and terminate nubs. This will make you
development life quite a bit easier. Do remember though that your
slave driver should open() their nub and close it when they have
finished.
6> I have ignored power management as that is a whole different can of
worms.
That's about it, obviously I have skipped a lot of detail but I have
listed the major steps to do with creation of matching. Good luck