20061130

SPI and IIC into head

After discovering a number of embarassing typos, I've recently committed support for the AT91RM9200 to head for iic and spi.

The only wrinkle in the SPI integration was that i had to make it polled. When I made it use the interrupt handler, I had random corruption for reasons that are still unknown.

For the moment, the code that deals with the dataflash assumes that it is an 8MB part. This will change in the future as I get more experience with other parts.

20061127

Clonable Devices

I've seen questions on clonable devices come up from time to time. When do you want to use a clonable device? There are many times. The most common is to get 'per-open' semantics from a driver. Linux has this by default, but the structure of the vfs system in FreeBSD makes supporting it directly very difficult. Clonable devices are also useful when you want to open /dev/foo and dynamically create /dev/foo0, /dev/foo1, etc. Or when you want to have all attempts to open /dev/rodent/* succeed, yet you don't wish to enumerate all possible devices. I've heard some folks say that they want an open of /dev/tcp/client/hostname to connect to hostname, for example, and you can't enumerate all hostnames. I'll not comment on the wisdom of that design, but it is one that people understand.

Cloning it a bit underdocumented right now, as is most of the cdevsw interaction. But here's a sketch.

First, you need to register a clone event handler. There is one of these for your driver, even if it has multiple instances. The method I recommend is doing it in your attach routine if you haven't done so yet, and removing it in your detach routine:

static eventhandler_tag ehtag;
static int ehtagref;

int
foo_attach(device_t)
{
...
if (ehtag == NULL)
ehtag = EVENTHANDLER_REGISTER(dev_clone, foo_clone, 0, 1000);
ehtagref++;
...
}

int
foo_detach(device_t)
{
if (--ehtagref == 0 && ehtag != NULL)
EVENTHANDLER_DEREGISTER(dev_clone, ehtag);
}

You are then setup with a clone handler that gets called in response to all /dev/ opens that don't already have a static entry in /dev. This is a big magical, but apart from files not appearing in ls it works out well.

static struct clonedevs *foo_dev_clones = NULL;

/* Clone device */
static void
foo_dev_clone(void *arg, struct ucred *cred, char *name, int namelen,
struct cdev **dev)
{
if (*dev != NULL)
return;
/* If you want a /dev/foo control device */
if (strcmp(name, "foo") == 0)
unit = -1;
else if (dev_stdclone(name, NULL, "foo", &unit) != 1)
return; /* Bail on names we don't know */

/* find any existing device, or allocate new unit number */
if (clone_create(&foo_dev_clones, &foo_dev_cdevsw, &unit, dev, 0)) {
*dev = make_dev(&foo_dev_cdevsw, unit2minor(unit),
UID_ROOT, GID_WHEEL, 0600, "foo%d", unit);
if (*dev != NULL) {
dev_ref(*dev);
(*dev)->si_flags |= SI_CHEAPCLONE;
}
}
}

Your open routine will then get the new dev and you can save stuff off in it.


Finally, I'm looking for a good way to edit my posts so that the code comes out indented correctly. If anybody know how to do this, please let me know.