Date: Thu, 30 Jan 1997 16:31:38 +1030 (CST) From: Michael Smith <msmith@atrad.adelaide.edu.au> To: mcgovern@spoon.beta.com (Brian J. McGovern) Cc: msmith@atrad.adelaide.edu.au, hackers@freebsd.org Subject: Device Drivers 101 Message-ID: <199701300601.QAA27658@genesis.atrad.adelaide.edu.au> In-Reply-To: <199701300505.AAA09649@spoon.beta.com> from "Brian J. McGovern" at "Jan 30, 97 00:05:43 am"
next in thread | previous in thread | raw e-mail | index | archive | help
Brian J. McGovern stands accused of saying: > > >Driver initialisation is seperated into two parts, known as 'probe' and > >'attach'. The purpose of the 'probe' routine is to ascertain whether > >the hardware is present, and optionally determine its configuration. > > >Probe/attach for ISA device drivers is triggered by the presence of a > >non-static isa_driver structure in the driver; at least the first three > >fields should be initialised, with the probe and attach routines and the > >name of the driver : > > Ok. I know the what. Any particular reason it has to be non-static? I assume > to cause it to blow up if there is another driver with the same name, but, > am I correct? The structures are collated at link time into what is known as a 'linker set'. This results in a statically-initialised array of all of the isa_device structures which can be processed by the startup code. The structure has to be non-static so that it is visible to the linker. > Secondly, what are the fields after the first 3? There is only one other field at the moment : struct isa_driver { int (*probe) __P((struct isa_device *idp)); /* test whether device is present */ int (*attach) __P((struct isa_device *idp)); /* setup driver for a device */ char *name; /* device name */ int sensitive_hw; /* true if other probes confuse us */ } Drivers with sensitive_hw set to true are probed before others to avoid being confused by other probes to the same space. > Also, I did a grep for "isa_driver" in /usr/include via a find (ie - > grep "isa_driver" `find .` to no avail. Which header should I include? #include <i386/isa/isa_device.h>, found in /sys/i386/isa/. As a kernel-only header, it't not found under /usr/include. > >struct isa_driver foodriver = { fooprobe, fooattach, "foo"}; > > >The 'fooprobe' function is called during startup to determine whether > >the device is present or not. It should return zero if the probe > >for the hardware failed, or the size of the I/O space occupied by > >the device if the probe succeeded. > > Please define "size of the I/O space". To me, this can mean many > things, probably all of which are wrong. Is it the number of ports a > device uses? Amount of memory (shared or otherwise)? And how about > our simulated pseudo device, which won't control hardware, but might > have a few K in buffers? The size of the range of I/O ports that the device occupies. This is, as has been observed elsewhere, a poor choice, as there are devices that occupy no I/O ports, and others that occupy several disjoint ranges. Pseudo-devices are handled differently. The 'vn' driver is a good place to look for a readable example (/sys/dev/vn/vn.c), but the basic strategy involves using the SYSINIT macro to register a startup function which calls the pseudo-device's init function, which in turn uses [cb]devsw_add and optionally devfs_add* to register the driver's existence. (For "normal" devices, the bus probe code for the bus to which the device belongs processes the linker set described above and calls the probe/attach pairs.) > >It is legitimate to alter the contents of the fields in the isa_device > >structure, if new values are determined by probing for the hardware. > >Note that the id_irq field is a bitmask, not a numeric value. The > >probe routine should not emit any text unless it comes up against > >something particularly alarming. > > Ok. id_irq is a bitmask. I'll have to save my other questions once I can > find struct isa_device. I am currently assuming that this contains the > info in the kernel config file when called. Same for a pseudo-device? Pseudo-devices don't get any config information. The isa_device structure is initialised by the config file data, yes. > >Attach is called, again once per instance of the driver in the config, > >when the device has been successfully probed and does not conflict. > Does the driver make the call as to the conflict? Or does the system look > at the struct isa_device, and if a conflict occurs, not call the probe > and attach routines? The bus code is responsible for deciding whether a driver is in conflict; drivers should ideally know nothing about the bus or other drivers. > >Once internal state for the driver has been established, you add an entry > >to the device switch for the driver. In the case of a character device, > >the following fragment is normally used : > > > > dev = makedev(CDEV_MAJOR,0); > > cdevsw_add(&dev,&foo_cdevsw,NULL); > > > > Ok. Looks clear enough. I'm assuming we're still in attach here... I'm > also assuming that block devices would call bdevsw_add (wrong name i think, > but I think I get the idea). The name is correct too 8) > How about STREAMS types or tty type devices that are linked off > through a line protocol? No STREAMS in the BSD kernel 8). Tty devices are character devices; there's extra state involved in interacting with the tty stack, which is logically layered on top of the character driver. This is a topic on which Bruce is best qualified to comment; I'm just going to refer to sio.c and translate what I read into English 8) > >Where CDEV_MAJOR is the major device number assigned to the driver > >(normally #defined somewhere obvious). > > > >A typical cdevsw initialisation might look like : > > > >static d_open_t fooopen; > >static d_close_t fooclose; > >static d_read_t fooread; > >static d_write_t foowrite; > >static d_ioctl_t fooioctl; > >static d_select_t fooselect; > > > >#define CDEV_MAJOR 20 > >static struct cdevsw foo_cdevsw = > >{ > > fooopen, fooclose, fooread, foowrite, > > fooioctl, nullstop, nullreset, nodevtotty, > > fooselect, nommap, NULL, driver_name, > > NULL, -1 > >}; > > > > Ok. Some of it makes sense. Is there a blank generic one that gives the > appropriate order? For instance, I see nullstop and nullreset. There should > also be a poll routine in there some where? Is it a NULL? a no? a -1? I don't believe that there's a blank. The structure and the blanks are listed in /sys/sys/conf.h. I should have been a little more thoughtful about the no vs. null comment : The no* versions return ENODEV, the null* versions always succeed but do nothing, the nx* versions return ENXIO (but are a bad idea according to the comment in sys/kern/subr_xxx.c where they are defined). BSD doesn't have a poll syscall; 'similar' functionality is afforded by select(2). > Ok. Is creating a devfs node mandatory? I know people would like to move to > it, but when/is it required? What makes the decision if it is optional? It's not mandatory, but if you don't put a call in to create it, someone else will 8) Normally devfs node creation is conditionalised on DEVFS. > >You can call this several times to create multiple nodes for a single > >instance of the driver. > > Ok. I assume this means that it'll generate the same major numbers with > appropriate minor numbers? Devfs doesn't really 'do' major and minor numbers; there's a logically direct translation from the named node to your switch entry and the unit number you supply when you create the node. -- ]] Mike Smith, Software Engineer msmith@gsoft.com.au [[ ]] Genesis Software genesis@gsoft.com.au [[ ]] High-speed data acquisition and (GSM mobile) 0411-222-496 [[ ]] realtime instrument control. (ph) +61-8-8267-3493 [[ ]] Unix hardware collector. "Where are your PEZ?" The Tick [[
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199701300601.QAA27658>