From owner-freebsd-hackers Sun Feb 14 16:20:44 1999 Return-Path: Received: (from majordom@localhost) by hub.freebsd.org (8.8.8/8.8.8) id QAA03741 for freebsd-hackers-outgoing; Sun, 14 Feb 1999 16:20:44 -0800 (PST) (envelope-from owner-freebsd-hackers@FreeBSD.ORG) Received: from herring.nlsystems.com (nlsys.demon.co.uk [158.152.125.33]) by hub.freebsd.org (8.8.8/8.8.8) with ESMTP id QAA03529; Sun, 14 Feb 1999 16:20:32 -0800 (PST) (envelope-from dfr@nlsystems.com) Received: from localhost (dfr@localhost) by herring.nlsystems.com (8.9.2/8.8.8) with ESMTP id AAA49511; Mon, 15 Feb 1999 00:19:15 GMT (envelope-from dfr@nlsystems.com) Date: Mon, 15 Feb 1999 00:19:15 +0000 (GMT) From: Doug Rabson To: Bill Paul cc: hackers@FreeBSD.ORG, current@FreeBSD.ORG Subject: Re: How the hell does one create a new bus?! In-Reply-To: <199902142158.QAA00307@skynet.ctr.columbia.edu> Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: owner-freebsd-hackers@FreeBSD.ORG Precedence: bulk X-Loop: FreeBSD.ORG On Sun, 14 Feb 1999, Bill Paul wrote: > Grrrr. Alright, I give up: how does one create a new 'bus' layer? > By that I mean like /sys/dev/iicbus and /sys/dev/smbus. Yes, I've read > the code in these two directories, but I'm confused as to which parts > relate to the bus architecture in general and which parts are speficic > to the I2C and system management implementation. > > >From what I can tell, you need the following: > > - foobus_if.m, a file which describes the functions that drivers for > devices attached to this kind bus can perform. Each xxx_if.m file defines a set of functions which are implemented by (some) drivers as you can see. This can be used in a couple of ways: 1. If a number of different types of the 'bus' hardware can have the same set of child devices, one can define an interface which each of the different bus device implements that can be called by the attached child devices to hide the differences between the hardware. Code in the drivers for child devices might look like this: /* * A ficitious 'probe' method for a device attached to a pci bus. */ int foo_probe(device_t dev) { /* Call a function in the PCI interface on our parent */ if (PCI_READ_CONFIG(device_get_parent(dev), dev, 123, 4) == 456) return ENXIO; } 2. If all the devices attached to a bus can perform similar functions (but implement them in different ways), an interface can be defined to describe these functions. This interface can be called by any client which needs to use the device. In this case, each driver would implement the method, looking something like this: /* * Implementation of BOGUS_SET_LEVEL for the foo driver. */ static void foo_set_level(device_t dev, unsigned int level) { ...; } ... static device_method_t foo_methods[] = { /* Device interface */ ...; /* Bogus interface */ DEVMETHOD(bogus_set_level, foo_set_level), { 0, 0 } }; for an interface declaration (bogus_if.m) which looked like this: INTERFACE bogus; METHOD void set_level { device_t dev; unsigned int level; }; > > - foobus.c and foobus.h > WHAT EXACTLY GOES IN HERE!? > > - fooconf.c and fooconf.h > WHAT EXACTLY GOES IN HERE!? There really isn't any naming convention for files which make up the implementation of a driver (a bus is just a driver which implements the BUS interface and which contains some kind of support for adding child devices either by a PnP scan or by config(8) supplied tables). For SMB, the implementation of the smbus driver seems to be in smbus.[ch], with some kind of utility functions in smbconf.[ch]. To write a file with a 'minimum' driver (in this case a driver named 'foo' attached to a bus named 'bogus'), you might have something like this: struct foo_softc { ...; }; static devclass_t foo_devclass; #define FOO_SOFTC(unit) (struct foo_softc*) \ devclass_get_softc(foo_devclass, unit) static int foo_probe(device_t dev) { if (device exists) return 0; else return ENXIO; } static int foo_attach(device_t dev) { device_printf(dev, "I'm alive\n"); return 0; } static device_method_t foo_methods[] = { /* Device interface */ DEVMETHOD(device_probe, foo_probe), DEVMETHOD(device_attach, foo_attach), { 0, 0 } }; static driver_t foo_driver = { "foo", /* driver name */ foo_methods, /* implementation */ DRIVER_TYPE_NET, /* interrupt class */ sizeof(struct foo_softc), /* size of driver scratch */ }; DRIVER_MODULE(foo, bogus, foo_driver, foo_devclass, 0, 0); > > - Some type of top level driver for the device which implements the > bus. > The implementation of a 'bus' device requires implementing a few extra methods. Typically bus_print_child and bus_read_ivar are implemented by most busses. Generic implementations for many of the bus functions are provided (e.g. bus_generic_print_child) but these must be explicitly specified in the method table (there is no automatic fallback mechanism). Most bus drivers will add child devices to themselves during either probe or attach and will call bus_generic_attach() from their attach method (or just use bus_generic_attach directly instead of having their own implementation of attach) to probe and attach the child devices. > - Various drivers for devices attached to the bus. See above > > In my particular case, I'm trying to create a bus layer for MII-compliant > PHYs attached to ethernet controller chips. The ethernet controller is > the bus controller device: each MII management bus can have up to 32 > PHY devices attached to it (though in general you rarely have more than > two). Each ethernet driver exports three functions to the MII code: > mii_read(), mii_write() and mii_statchg(). The first two allow reading > and writing of the registers of a PHY at a particular address (i.e. > mii_read(dev, phyadd, regaddr), mii_write(dev, phyaddr, regaddr, val)). The > mii_statchg() routine is a callback routine: when one of the lower-level > PHY drivers does a media change, it calls back to the ethernet driver > to let it know. (This is necessary because some media changes also > require updating registers on the ethernet controller itself. For > instance, on the ThunderLAN chip, if the PHY is set for full duplex, the > ThunderLAN chip itself also has to be programmed for full duplex at the > same time). Here you would define an MII interface (mii_if.m) which describes mii_read() etc. Each ethernet driver would contain an implementation of a driver which implements this interface. The driver should be called 'mii' for all ethernet devices since the PHY driver won't know what kind of hardware they will be attached to. There is no problem with multiple drivers having the same name as long as there is no ambiguity. For instance if each driver was attached to a different kind of parent device (ethernet device) there is no ambiguity. If the device probe is performed using accurate PnP data (e.g. pci), then there is also no ambiguity. A fraction of the device tree might look like this: ... --------pcib0 The host-pci bridge | |-------pci0 Top pci bus | |-------xl0 An ethernet device | | | |-------mii0 and its mii component | |-------fxp0 | | | |-------mii1 | |-------pcib1 A pci-pci bridge | |-------pci1 With a second pci bus | |-------de0 | | | |-------mii2 | |-------de1 | |-------mii3 Since the code in -current doesn't use the bus interface for pci though, you will need to fake it out a little by providing a stub driver for each ethernet driver which attaches an mii device (I'm gradually working on moving the pci code to the new system but I don't have much FreeBSD time at the moment): root0 | |-------xl0 /* stub driver */ | | | |-------mii0 | |-------fxp0 | |-------mii1 The stub probe method for xl0 would create a device for its mii driver to use: xl_stub_probe(device_t dev) { /* * Add a device with the name 'mii' and allocate a unit * number for it automatically. */ device_add_child(dev, "mii", -1, NULL); return 0; } The stub driver would use bus_generic_attach() as its implementation of device_attach and would probably use bus_generic_print_child too to help in printing the probe message. The real attach method for the xl driver would attach an instance of the stub device to the root bus to allow the mii to be probed later when the root bus is probed: xl_attach(...) { ...; device_add_child(root_bus, "xl", -1, NULL); } > > The PHY drivers themselves (as they are now) export three functions to > the MII glue code: foophy_probe(), foophy_attach() and foophy_service(). > The middle MII layer exports mii_phy_probe(), mii_pollstat(), mii_statchg() > and mii_tick() back to the ethernet driver. For the PHY drivers, you would probably define a second interface to describe the service method and write the drivers as above, possibly calling MII_XXX methods on the parent device and registering the drivers under the mii bus: DRIVER_MODULE(foophy, mii, foophy_driver, foophy_devclass, 0, 0); This makes sure that the foophy driver is available to any device connected to any kind of mii. > > I've faked things up for now using linker sets, but I'd prefer to make > everything fit into the bus framework if possible. Unfortunately, there > doesn't appear to be a nice generic example that shows how to create a > new bus layer, and the existing code confuses the hell out of me. For > example, both the iicbus and smbus code use interrupt routines, but the > MII code doesn't need interrupts (PHY devices don't normally generate > interrupts). Also, how do I know exactly what goes into the foobus.c > and fooconf.c modules? The smbconf.c and iiconf.c modules appear to > have 'request_bus' routines that do things with tsleep(); what is this > for? Does every bus need this or is it specific to the I2C and SMB stuff? > The ppbus code is no help as it doesn't actually use this framework, and > the usb bus stuff is a mutant pile of macros and #ifdefs. I think that the request_bus and tsleep stuff is just part of the implementation of smbus and can be ignored (for your purposes). > > What this stuff needs is a developer's guide; a clearcut explanation of > how to create new busses, how to create top level bus drivers and how to > create drivers for devices attached to busses. If anyone can point me at > such a guide or can just explain to me how this nonsense is supposed to > work, I'd be grateful. I hope I've explained some things. There isn't a developer's guide as yet, I'm afraid and I don't really have time to write one :-(. Perhaps one of the people who has used the code might like to tackle it (or you might if you manage to struggle through implementing this thing...) If anyone wants to write such a guide, please go ahead and feel free to ask me any questions about how the thing is supposed to work. -- Doug Rabson Mail: dfr@nlsystems.com Nonlinear Systems Ltd. Phone: +44 181 442 9037 To Unsubscribe: send mail to majordomo@FreeBSD.org with "unsubscribe freebsd-hackers" in the body of the message