kern/99979: Get Ready for Kernel Module in C++

John Baldwin jhb at freebsd.org
Tue Jul 11 20:18:59 UTC 2006


On Tuesday 11 July 2006 14:48, M. Warner Losh wrote:
> In message: <200607111413.37238.jhb at freebsd.org>
>             John Baldwin <jhb at freebsd.org> writes:
> : On Tuesday 11 July 2006 12:33, M. Warner Losh wrote:
> : > In message: <200607111115.59844.jhb at freebsd.org>
> : >             John Baldwin <jhb at freebsd.org> writes:
> : > : and OS X both of which I've written a PCI driver for) we require 
device 
> : > : driver writers to go through a lot more hoops to do certain things 
like 
> : > : allocate resources.  At the very least there is much that can be 
improved 
> : in
> : > : our driver model.
> : > 
> : > bus_alloc_resources goes a long ways in this respect.
> : 
> : Yes, but in OS X I didn't even have to do that.  All I had to do was ask 
it to 
> : map a BAR if I wanted to use it.  It already "allocated" all the resources 
> : regardless.  Windows was the same way (though a bit weirder, you get a 
> : message that lists all your resources and you have to map them if you want 
to 
> : use them).
> 
> What's the difference in asking for a resource to be mapped, and
> calling a routine that allocates and maps the resource?

It's less ugly than it used to be, esp. with the bus_read_X() stuff.  There's 
no separate bus_alloc_resource/bus_setup_intr for interrupts though for 
example, just bus_setup_intr() equivalent.  This is pretty simple though:

	/* OS X */
	IOMemoryMap *myBarMap;
	void *myBar;
	u_int32_t reg;

	/* BAR 0 */
	myBarMap = provider->mapDeviceMemoryWithRegister(kIOPCIConfigBaseAddress0);
	myBar = myBarMap->getVirtualAddress();

	reg = OSReadLittleInt32(myBar, FOO_REG);
	reg |= BAR_FLAG;
	OSWriteLittleIntew(myBar, FOO_REG, reg);

	myBar->release();

In FreeBSD-7 this is something like:

	struct resource *res;
	int rid;
	u_int32_t reg;

	rid = PCIR_BAR(0);
	res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);

	/* XXX: Not sure about endian.. stream_read maybe? */
	reg = bus_read_4(res, FOO_REG);
	reg |= BAR_FLAG;
	bus_write_4(res, FOO_REG, reg);

	bus_release_resource(dev, SYS_RES_MEMORY, rid, res);

Which is very similar though there is some clutter (bus_release_resource() 
should take fewer args, like just dev and res, and it would be nice to say 
something like map_bar(dev, PCIR_BAR(0)) and get back a resource *).

Older FreeBSD looks like this:

	struct resource *res;
	bus_space_tag_t tag;
	bus_space_handle_t hnd;
	int rid;
	u_int32_t reg;

	rid = 0x10;
	res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid, 0ul, ~0ul, 1,
	    RF_ACTIVE);
	tag = rman_get_bustag(res);
	hnd = rman_get_bushandle(res);

	reg = bus_space_read_4(tag, hnd, FOO_REG);
	reg |= BAR_FLAG;
	bus_space_write_4(tag, hnd, FOO_REG, reg);

	bus_release_resource(dev, SYS_RES_MEMORY, rid, res);

Usually drivers come up with macros to hide the bus handles and tags which is 
an indication that they were quite unsightly (thankfully that's been fixed in 
7). :)

I don't recall the Windows syntax off the top of my head.  Note though that 
what verboseness there is in the OS X case comes from the really long and 
descriptive function names.  Shorter function names would make it far more 
compact.  Both Windows and OS X also allow you to only submap part of a bar 
if you want, and to specify the caching mode (UC, WC, WB, WT, etc.) when you 
map the bar.  Oh, and just because I'm on my soapbox, on OS X you can mmap 
device memory (or any sort of malloc'd memory) _really_ easily in userland 
using an arbitrary mapping type, because their mmap-type operation 
(clientForMemoryType on an IOUserClient) just asks the device driver to 
provide an IOMemoryMap (which describes the backing physical addresses along 
with the cache mode etc.) and then takes care of mapping that into UVA, but I 
digress on that point.

Anyways, if you took FreeBSD-7 and cut down some of the gunk similar to OS X 
you'd have something like:

	struct resource *res;
	u_int32_t reg;

	res = pci_alloc_bar(dev, PCIR_BAR(0));

	/* XXX: endian question again */
	reg = bus_read_4(res, FOO_REG);
	reg |= BAR_FLAG;
	bus_write_4(res, FOO_REG, reg);

	bus_release_resource(dev, res);

Which is at least somewhat better I think.

> Also, in FreeBSD, the resources are already allocated by the bus
> code.  It just changes ownership to the child when the request comes
> in...

Yes, this has been a recent improvement.

-- 
John Baldwin


More information about the freebsd-hackers mailing list