xref: /haiku/docs/develop/kernel/obsolete_pnp_manager.rst (revision 3d4afef9cba2f328e238089d4609d00d4b1524f3)
1Plug and Play Manager
2=====================
3
4This file contains the documentation written by Thomas Kurschel that was originally
5found in the headers of his pnp_manager.
6It's outdated but could be used as a basis for the real documentation.
7
8PNP Manager
9-----------
10
11PnP manager; Takes care of registration and loading of PnP drivers
12
13Read pnp_driver.h first to understand the basic idea behind PnP drivers.
14
15To register a driver node, use register_driver. If the device got lost,
16use unregister_driver (note: if the parent node is removed, your node
17get removed automatically as your driver has obviously nothing to work
18with anymore). To get access to a (parent) device, use load_driver/
19unload_driver.
20
21To let the manager find a consumer (see pnp_driver.h), you can either
22specify its name directly during registration, using a
23PNP_DRIVER_FIXED_CONSUMER attribute, or let the manager search the
24appropriate consumer(s) via a PNP_DRIVER_DYNAMIC_CONSUMER attribute.
25
26Searching of dynamic consumers is done as follows:
27
28- First, the manager searches for a Specific driver in the base
29  directory (see below)
30- If no Specific driver is found, all Generic drivers stored under
31  "generic" sub-directory are informed in turn until one returns success
32- Finally, _all_ Universal drivers, stored in the "universal" sub-
33  directory, are informed
34
35Specification of the base directory and of the names of Specific
36drivers is done via a file name pattern given by a
37PNP_DRIVER_DYNAMIC_CONSUMER attribute.
38
39First, all substrings of the form "%attribute_name%" are replaced by the
40content of the attribute "attribute_name" as follows:
41
42- if the attribute contains an integer value, its content is converted to hex
43  (lowercase) with a fixed length according to the attribute's value range
44- the content of string attributes is quoted by " and invalid characters
45  (i.e. /%" and all characters outside 32..126) are replaced by their
46  unsigned decimal value, delimited by %
47- other attribute types cannot be used
48
49Second, the resulting name is split into chunks according to the presence
50of | characters (you can escape % and | with a ^ character). These
51characters are only delimiters and get removed before further processing.
52The directory before the first | character is the base directory (see
53above). It contains the "generic" and the "universal" subdirectories.
54The names of the specific drivers are created by first taking the entire
55file name, then by removing the last chunk, then by removing the last
56two chunks and so on until only the first chunk is left.
57
58As drivers can contain multiple modules, the module name is constructed
59by appending the content of the PNP_DRIVER_TYPE attribute to the driver's file
60name, seperated by a slash character (note: this only applies to dynamic
61consumers; for fixed consumers, you specify the module name directly via
62PNP_DRIVER_FIXED_CONSUMER).
63
64E.g. given a dynamic consumer pattern of
65"pci/vendor=%vendor_id%|, device=%device_id%" for a device with the
66attributes vendor_id=0x123 and device_id=0xabcd (both being uint16), the
67PnP manager tries the specific drivers "pci/vendor=0123, device=abcd" and
68(if the first one fails/doesn't exist) "pci/vendor=0123". If they both
69refuse to handle the device, all drivers under "pci/generic" are tried
70until one accepts the device. Finally, all drivers under "pci/universal"
71are	loaded, whatever happened before.
72
73In practise, you should try to use specific drivers as much as possible.
74If detection based on device IDs is impossible (e.g. because the bus
75doesn't support them at all), you can put the driver under "generic".
76Generic drivers can also be used to specify wrappers that try to load old-
77style drivers if no new driver can be found. Also, they can be used to
78report an error or invoke an user program that tries downloading a
79proper Specific driver. Universal drivers are mainly used for
80informational purposes, e.g. to publish data about each found device,
81or to provide raw access to all devices.
82
83If the device uses physical address space or I/O space or ISA DMA
84channels (called I/O resources), the driver has to acquire these
85resources. During hardware detection (usually via probe()),
86acquire_io_resources() must be called to get exclusive access.
87If no hardware could be found, they must be released via
88release_io_resources(). If detection was successful, the list of
89the (acquired) resources must be passed to register_device().
90Resources can either belong to one hardware detection or to a device.
91If a hardware detection collides with another, it has to wait;
92if it collides with a device whose driver is not loaded, the
93driver loading is blocked. When detection fails, i.e. if
94release_io_resources() is called, all blocked drivers can be loaded
95again. If the detection fails, i.e. the resources are transferred
96via register_device(), all blocked devices are unregistered and
97pending load requests aborted. If a hardware detection collides
98with a device whose driver is loaded, acquire_io_resources() fails
99with B_BUSY. As this makes a hardware rescan impossible if the
100driver is loaded, you should define	PNP_DRIVER_NO_LIVE_RESCAN
101for nodes that use I/O resources (see below).
102
103To search for new drivers for a given device node, use rescan(). This
104marks all consumer devices as being verified and calls probe()
105of all consumers drivers (see above) to let them rescan the parent
106for devices. The <depth> parameter determines the nesting level, e.g.
1072 means that first the consumers are scanned and then the consumers
108of the consumers.
109
110Normally, all devices can be rescanned. If a driver cannot handle
111a rescan safely when it is loaded (i.e. used by a consumer), it
112must set PNP_DRIVER_NO_LIVE_RESCAN, in which case the device is
113ignored during rescan if the driver is loaded and attempts
114to load the driver during a rescan are blocked until the rescan
115is finished. If rescanning a device is not possible at all, it must
116have set PNP_DRIVER_NEVER_RESCAN to always ignore it.
117
118To distinguish between new devices, lost devices and redetected
119devices, consumer devices should provide a connection code and a
120device identifier. They are specified by PNP_DRIVER_CONNECTION and
121PNP_DRIVER_CONNECTION respectively, and are expanded in the same way
122as PNP_DRIVER_DYNAMIC_CONSUMER. It is assumed that there can be only
123one device per connection and that a device can be uniquely identify
124by a device identifier. If a consumer device is registered on the
125same connection as an existing device but with a different device
126identifier, the old device gets unregistered automatically. If both
127connection and device identifier are the same, registration is
128handled as a redetection and ignored (unless a different type or
129driver module is specified - in this case, the device is replaced).
130Devices that were not redetected during a rescan get unregistered
131unless they were ignored (see above).
132
133.. code-block:: cpp
134
135    // interface of PnP manager
136    typedef struct device_manager_info {
137	module_info info;
138
139	// load driver
140	// node - node whos driver is to be loaded
141	// user_cookie - cookie to be passed to init_device of driver
142	// interface - interface of loaded driver
143	// cookie - device cookie issued by loaded driver
144	status_t (*init_driver)(device_node_handle node, void *userCookie,
145					driver_module_info **interface, void **cookie);
146	// unload driver
147	status_t (*uninit_driver)(device_node_handle node);
148
149	// rescan node for new dynamic drivers
150	// node - node whose dynamic drivers are to be scanned
151	status_t (*rescan)(device_node_handle node);
152
153	// register device
154	// parent - parent node
155	// attributes - NULL-terminated array of node attributes
156	// io_resources - NULL-terminated array of I/O resources (can be NULL)
157	// node - new node handle
158	// on return, io_resources are invalid: on success I/O resources belong
159	// to node, on fail they are released;
160	// if device is already registered, B_OK is returned but *node is NULL
161	status_t (*register_device)(device_node_handle parent,
162					const device_attr *attrs,
163					const io_resource_handle *io_resources,
164					device_node_handle *node);
165	// unregister device
166	// all nodes having this node as their parent are unregistered too.
167	// if the node contains PNP_MANAGER_ID_GENERATOR/PNP_MANAGER_AUTO_ID
168	// pairs, the id specified this way is freed too
169	status_t (*unregister_device)(device_node_handle node);
170
171	// find device by node content
172	// the given attributes must _uniquely_ identify a device node;
173	// parent - parent node (-1 for don't-care)
174	// attrs - list of attributes (can be NULL)
175	// The node you got will be automatically put on the next call
176	// to this function.
177	status_t (*get_next_child_device)(device_node_handle parent,
178		device_node_handle *_node, const device_attr *attrs);
179
180	// get parent device node
181	device_node_handle (*get_parent)(device_node_handle node);
182
183	// Must be called after get_next_child_device() (if you don't iterate through)
184	// and get_parent() to make sure the node is freed when it's not used anymore
185	void (*put_device_node)(device_node_handle node);
186
187	// acquire I/O resources
188	// resources - NULL-terminated array of resources to acquire
189	// handles - NULL-terminated array of handles (one per resource);
190	//           array must be provided by caller
191	// return B_BUSY if a resource is used by a loaded driver
192	status_t (*acquire_io_resources)(io_resource *resources,
193					io_resource_handle *handles);
194	// release I/O resources
195	// handles - NULL-terminated array of handles
196	status_t (*release_io_resources)(const io_resource_handle *handles);
197
198	// create unique id
199	// generator - name of id set
200	// if result >= 0 - unique id
201	//    result < 0 - error code
202	int32 (*create_id)(const char *generator);
203	// free unique id
204	status_t (*free_id)(const char *generator, uint32 id);
205
206	// helpers to extract attribute by name.
207	// if <recursive> is true, parent nodes are scanned if
208	// attribute isn't found in current node; unless you declared
209	// the attribute yourself, use recursive search to handle
210	// intermittent nodes, e.g. defined by filter drivers, transparently.
211	// for raw and string attributes, you get a copy that must
212	// be freed by caller
213	status_t (*get_attr_uint8)(device_node_handle node,
214					const char *name, uint8 *value, bool recursive);
215	status_t (*get_attr_uint16)(device_node_handle node,
216					const char *name, uint16 *value, bool recursive);
217	status_t (*get_attr_uint32)(device_node_handle node,
218					const char *name, uint32 *value, bool recursive);
219	status_t (*get_attr_uint64)(device_node_handle node,
220					const char *name, uint64 *value, bool recursive);
221	status_t (*get_attr_string)(device_node_handle node,
222					const char *name, char **value, bool recursive);
223	status_t (*get_attr_raw)(device_node_handle node,
224					const char *name, void **data, size_t *_size,
225					bool recursive);
226
227	// get next attribute of node;
228	// on call, *<attr_handle> must contain handle of an attribute;
229	// on return, *<attr_handle> is replaced by the next attribute or
230	// NULL if it was the last;
231	// to get the first attribute, <attr_handle> must point to NULL;
232	// the returned handle must be released by either passing it to
233	// another get_next_attr() call or by using release_attr()
234	// directly
235	status_t (*get_next_attr)(device_node_handle node,
236					device_attr_handle *attrHandle);
237
238	// release attribute handle <attr_handle> of <node>;
239	// see get_next_attr
240	status_t (*release_attr)(device_node_handle node,
241					device_attr_handle attr_handle);
242
243	// retrieve attribute data with handle given;
244	// <attr> is only valid as long as you don't release <attr_handle>
245	// implicitely or explicitely
246	status_t (*retrieve_attr)(device_attr_handle attr_handle,
247					const device_attr **attr);
248
249	// change/add attribute <attr> of/to node
250	status_t (*write_attr)(device_node_handle node,
251					const device_attr *attr);
252
253	// remove attribute of node by name
254	// <name> is name of attribute
255	status_t (*remove_attr)(device_node_handle node, const char *name);
256    } device_manager_info;
257
258PNP Driver
259----------
260
261Required interface of PnP drivers
262
263In contrast to standard BeOS drivers, PnP drivers are normal modules
264having the interface described below.
265
266Every device is described by its driver via a PnP node with properties
267described in PnP Node Attributes. Devices are organized in a hierarchy,
268e.g. a devfs device is a hard disk device that is connected to a
269controller, which is a PCI device, that is connected to a PCI bus.
270Every device is connected to its lower-level device	via a parent link
271stored in its Node. The higher-level is called the consumer of the
272lower-level device. If the lower-level device gets removed, all its
273consumers are removed too.
274
275In our example, the hierarchy is
276
277  devfs device -> hard disk -> controller -> PCI device -> PCI bus
278
279If the PCI bus is removed, everything up to including the devfs device
280is removed too.
281
282The driver hierarchy is constructed bottom-up, i.e. the lower-level
283driver searches for a corresponding consumer, which in turns searches
284for its consumer and so on. The lowest driver is usually something like
285a PCI bus, the highest driver is normally a devfs entry (see pnp_devfs.h).
286Registration of devices and the search for appropriate consumers is
287done via the pnp_manager (see pnp_manager.h).
288
289When a potential consumer is found, it gets informed about the new
290lower-level device and can either refuse its handling or accept it.
291On accept, it has to create a new node with the	lower-level device
292node as its parent.
293
294Loading of drivers is done on demand, i.e. if the consumer wants to
295access its lower-level device, it explicitely loads the corresponding
296driver, and once it doesn't need it anymore, the lower-level driver
297must be unloaded. Usually, this process happens recursively, i.e. in
298our example, the hard disk driver loads the controller driver, which
299loads the PCI device driver which loads the PCI bus driver. The same
300process applies to unloading.
301
302Because of this dynamic loading, drivers must store persistent data
303in the node of their devices. Please be aware that you cannot modify
304a node once published.
305
306If a device gets removed, you must unregister its node. As said, the
307PnP manager will automatically unregister all consumers too. The
308corresponding drivers are notified to stop talking to their	lower-level
309devices and to terminate running requests. Normally, you want to use a
310dedicated variable that	is verified at each call to make sure that the
311parent is still there. The notification is done independantly of the
312driver being loaded by its consumer(s) or not. If it isn't loaded,
313the notification callback gets NULL as the device cookie; normally, the
314driver returns immediately in this case. As soon as both the device
315is removed and the driver is unloaded, device_cleanup gets called to
316free resources that couldn't be safely removed in device_removed when
317the driver was still loaded.
318
319If a device has exactly one consumer, they often interact in some way.
320To simplify that, the consumer can pass a user-cookie to its parent
321during load. In this case, it's up to the parent driver to get a
322pointer to the interface of the consumer. Effectively, such consumers
323have one interface for their consumers (base on pnp_driver_info), and
324a another for their parents (with a completely driver-specific
325structure).
326
327In terms of synchronization, loading/unloading/remove-notifications
328are executed synchronously, i.e. if e.g. a device is to be unloaded
329but	the drive currently handles a remove-notification, the unloading
330is delayed until the nofication callback returns. If multiple consumers
331load a driver, the driver gets initialized only once; subsequent load
332requests increase an internal load count only and return immediately.
333In turn, unloading only happens once the load count reaches zero.
334
335.. code-block:: cpp
336
337    struct driver_module_info {
338	module_info info;
339
340	float (*supports_device)(device_node_handle parent, bool *_noConnection);
341		// check whether this parent is supported
342
343	status_t (*register_device)(device_node_handle parent);
344		// Register your device node.
345
346	status_t (*init_driver)(device_node_handle node, void *user_cookie, void **_cookie);
347		// driver is loaded.
348		// node - node of device
349		// user_cookie - cookie passed by loading driver
350		// cookie - cookie issued by this driver
351
352	status_t (*uninit_driver)(void *cookie);
353		// driver gets unloaded.
354
355	void (*device_removed)(device_node_handle node, void *cookie);
356		// a device node, registered by this driver, got removed.
357		// if the driver wasn't loaded when this happenes, no (un)init_device
358		// is called and thus <cookie> is NULL;
359
360	void (*device_cleanup)(device_node_handle node);
361		// a device node, registered by this driver, got removed and
362		// the driver got unloaded
363
364	void (*get_supported_paths)(const char ***_busses, const char ***_devices);
365    };
366
367PNP Bus
368-------
369
370Required interface of PnP bus drivers
371
372Busses consist of two node layers: the lower layer defines the bus,
373the upper layer defines the abstract devices connected to the bus.
374Both layers are handled by a bus manager. Actual device nodes are
375on top of abstract device nodes.
376
377E.g. if we have a PCI bus with an IDE controller on it, we get
378
379IDE controller -> PCI device -> PCI bus
380
381with:
382
383* IDE controller = actual device node
384* PCI device = abstract device node
385* PCI bus = bus node
386
387The PCI bus manager establishes both the PCI devices and the PCI busses.
388
389Abstract device nodes act as a gateway between actual device nodes
390and the corresponding bus node. They are constructed by the bus
391node driver via	its rescan() hook. To identify a bus node, define
392PNP_BUS_IS_BUS as an attribute of it. As a result, the PnP manager
393will call the rescan() method of the bus driver whenever the
394bus is to be rescanned. Afterwards, all possible dynamic consumers
395are informed as done for normal nodes.
396
397Normally, potential device drivers are notified immediately when
398rescan() registers a new abstract device node. But sometimes, device
399drivers need to know _all_ devices connected to the bus for correct
400detection. To ensure this, the bus node must define
401PNP_BUS_NOTIFY_CONSUMERS_AFTER_RESCAN. In this case, scanning for
402consumers is postponed until rescan() has finished.
403
404If hot-plugging of devices can be detected automatically (e.g. USB),
405you should define PNP_DRIVER_ALWAYS_LOADED, so the bus driver is
406always loaded and thus capable of handling hot-plug events generated
407by the bus controller hardware.
408
409