Newbie Device Driver writer...
John Baldwin
jhb at FreeBSD.org
Tue Sep 20 10:44:05 PDT 2005
On Monday 19 September 2005 07:05 pm, M. Warner Losh wrote:
> In message: <139F6E4BC4C585478FA84D02D90CC1CA04C5102C at aexcmb02.network.uni>
>
> "Phil Richardson" <prichardson at lincoln.ac.uk> writes:
> : Im rather new to device driver writing under FreeBSD (in particular
> : Im using 5.4). I have a PCI based IO card (4x32bit TTL presentation)
> : and have written a simplistic driver thus far (basic handling is
> : done completely through IOCTL calls - but it works).
>
> Sounds like an interesting device.
>
> : However now I have started to think about using the device in a real
> : way I realise it would be nice to implement asynchronous use. Maybe
> : not the right term (forgive me) - but in essence I would really like
> : to utilise this device along with other devices (such as a socket or
> : tty) and use the Select() call to determine whats happening to who,
> : and when.
>
> Event driven programming is what it sounds like.
>
> : However - I cannot find any reference anywhere within source code
> : (that I can recognise at any rate) within existing device drivers
> : (say sio.c) that suggest how you link a device driver to the
> : select() call itself (I was expecting to find something like
> : sio_select, much as sio_read, sio_write and sio_ioctl for example -
> : which shows my inexperience and definate lack of understanding).
>
> sio is a bad driver to look at, since it move much of its
> functionality into the tty layer, obscuring these simple things.
>
> : Anyone care to give some advice on how the kernel select() call can
> : be linked into a home-made driver? A pointer to some documentation
> : (dare I say that - documentation is a bit thin on device drivers so
> : ive found...) would be a help.
>
> You need to implment a d_poll_t function for your driver.
>
> : Trusting someone somewhere might have a pointer or clue out there.....
>
> I just started to take a look at our man pages, and realized that some
> work is needed to ferret out the information.
>
> First, you'll need to read up on the configuration phase of your
> device's life cycle. This is the traditional probe and attach
> routines. You can find good infromation about these in the various
> bus and device man pages (DEVICE_PROBE, DEVICE_ATTACH,
> bus_alloc_resource, etc).
>
> In your attach routine, you'll need to make the device visible to the
> filesystem. make_dev will do this. One of the arguments to make_dev
> is the cdevsw for your device. cdevsw is a structure that contains
> poiners to functions that get called for your device when the user
> interacts with it. I didn't see a man page for, so I'll give a
> quickie add-hoc one here.
>
> struct cdevsw {
> int d_version;
> u_int d_flags;
> const char *d_name;
> d_open_t *d_open;
> d_fdopen_t *d_fdopen;
> d_close_t *d_close;
> d_read_t *d_read;
> d_write_t *d_write;
> d_ioctl_t *d_ioctl;
> d_poll_t *d_poll;
> d_mmap_t *d_mmap;
> d_strategy_t *d_strategy;
> dumper_t *d_dump;
> d_kqfilter_t *d_kqfilter;
> d_purge_t *d_purge;
> d_spare2_t *d_spare2;
> uid_t d_uid;
> gid_t d_gid;
> mode_t d_mode;
> const char *d_kind;
>
> /* These fields should not be messed with by drivers */
> LIST_ENTRY(cdevsw) d_list;
> LIST_HEAD(, cdev) d_devs;
> int d_spare3;
> struct cdevsw *d_gianttrick;
> };
>
> d_version should be set to D_VERSION.
>
> d_flags should be one or more of the following:
> /*
> * Flags for d_flags which the drivers can set.
> */
> #define D_MEMDISK 0x00010000 /* memory type disk */
> #define D_TRACKCLOSE 0x00080000 /* track all closes */
> #define D_MMAP_ANON 0x00100000 /* special treatment in vm_mmap.c */
> #define D_PSEUDO 0x00200000 /* make_dev() can return NULL */
> #define D_NEEDGIANT 0x00400000 /* driver want Giant */
>
> Usually, D_NEEDGIANT and D_TRACKCLOSE are the only flags you need.
>
> >From the sounds of your device, it is unlikely to need anything else.
>
> d_name is the name of the device.
>
> d_open is called when the file is opened. You'll almost certainly
> want to create one of these.
>
> d_close is called when the last reference to the device goes away
> (unless D_TRACKCLOSE is called, in which case each close causes a
> call to d_close).
>
> d_read and d_write are called when the user calls read(2) and write(2)
> respectively.
>
> d_ioctl is used for device control.
>
> d_poll is used to indicate when the device has data.
>
> The rest aren't relevant for this discussion.
>
> cdevsw is usually initialized like so:
>
> static struct cdevsw crd_cdevsw = {
> .d_version = D_VERSION,
> .d_flags = D_NEEDGIANT,
> .d_open = fooopen,
> .d_close = fooclose,
> .d_read = fooread,
> .d_write = foowrite,
> .d_ioctl = fooioctl,
> .d_poll = foopoll,
> .d_name = "foo",
> };
>
> Most of the routines are straight forward, so I'll not comment on them
> here (feel free to ask questions, however).
>
> d_poll is coded as follows:
>
> static int
> foopoll(struct cdev *dev, int events, d_thread_t *td)
> {
> int revents = 0;
> int s;
> struct softc *sc = dev->si_drv1;
>
> if (events & (POLLIN | POLLRDNORM))
> revents |= events & (POLLIN | POLLRDNORM);
>
> if (events & (POLLOUT | POLLWRNORM))
> revents |= events & (POLLIN | POLLRDNORM);
>
> /* Read polling */
> if (events & POLLRDBAND)
> if (### test that there's more data ###)
> revents |= POLLRDBAND;
>
> if (revents == 0)
> selrecord(td, &slt->selp);
>
> return (revents);
> }
>
> then in your read routine, you'd have something like:
>
> static int
> fooread(struct cdev *dev, struct uio *uio, int ioflag)
> {
> struct softc *sc = dev->si_drv1;
>
>
> if !(### test that there's some data ###) && non-blocking
> return EAGAIN;
>
> while (### test that there's some data ###) && room in buffer
> copyout data
> if (!non-blocking)
> sleep for more data
> else
> break;
> }
You'll also need to call selwakeup() in your interrupt routine when data
becomes available to wake threads that have blocked in select().
--
John Baldwin <jhb at FreeBSD.org> <>< http://www.FreeBSD.org/~jhb/
"Power Users Use the Power to Serve" = http://www.FreeBSD.org
More information about the freebsd-drivers
mailing list