From owner-freebsd-drivers@FreeBSD.ORG Mon Sep 19 23:06:33 2005 Return-Path: X-Original-To: freebsd-drivers@freebsd.org Delivered-To: freebsd-drivers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id AFD9816A41F for ; Mon, 19 Sep 2005 23:06:33 +0000 (GMT) (envelope-from imp@bsdimp.com) Received: from harmony.bsdimp.com (vc4-2-0-87.dsl.netrack.net [199.45.160.85]) by mx1.FreeBSD.org (Postfix) with ESMTP id 4EFE043D45 for ; Mon, 19 Sep 2005 23:06:33 +0000 (GMT) (envelope-from imp@bsdimp.com) Received: from localhost (localhost.village.org [127.0.0.1] (may be forged)) by harmony.bsdimp.com (8.13.3/8.13.3) with ESMTP id j8JN5luF027291; Mon, 19 Sep 2005 17:05:47 -0600 (MDT) (envelope-from imp@bsdimp.com) Date: Mon, 19 Sep 2005 17:05:59 -0600 (MDT) Message-Id: <20050919.170559.68164164.imp@bsdimp.com> To: prichardson@lincoln.ac.uk From: "M. Warner Losh" In-Reply-To: <139F6E4BC4C585478FA84D02D90CC1CA04C5102C@aexcmb02.network.uni> References: <139F6E4BC4C585478FA84D02D90CC1CA04C5102C@aexcmb02.network.uni> X-Mailer: Mew version 3.3 on Emacs 21.3 / Mule 5.0 (SAKAKI) Mime-Version: 1.0 Content-Type: Text/Plain; charset=us-ascii Content-Transfer-Encoding: 7bit X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-2.0 (harmony.bsdimp.com [127.0.0.1]); Mon, 19 Sep 2005 17:05:48 -0600 (MDT) Cc: freebsd-drivers@freebsd.org Subject: Re: Newbie Device Driver writer... X-BeenThere: freebsd-drivers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Writing device drivers for FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 19 Sep 2005 23:06:33 -0000 In message: <139F6E4BC4C585478FA84D02D90CC1CA04C5102C@aexcmb02.network.uni> "Phil Richardson" 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; } Warner