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.
In your example, very useful, I see that you omitted clone_setup on attach and clone_cleanup on detach.
ReplyDeleteDo you think sometime in the future man pages will appear for clone_xxx functions?
Yes. They should be written.
ReplyDelete