1
2
3<!--
4    (C) Copyright 2018 The Fuchsia Authors. All rights reserved.
5    Use of this source code is governed by a BSD-style license that can be
6    found in the LICENSE file.
7-->
8
9# Getting Started
10
11This document is part of the [Driver Development Kit tutorial](ddk-tutorial.md) documentation.
12
13Writing a device driver is often viewed as a daunting task, fraught with complexities
14and requiring arcane knowledge of little-known kernel secrets.
15
16The goal of this section is to demystify the process; you'll learn everything you
17need to know about how to write device drivers, starting with what they do, how
18they work, and how they fit into the overall system.
19
20## Overview
21
22At the highest level, a device driver's job is to provide a uniform interface to
23a particular device, while hiding details specific to the device's implementation.
24
25Two different ethernet drivers, for example, both allow a client to send packets
26out an interface, using the exact same C language function.
27Each driver is responsible for managing its own hardware in a way that makes the
28client interfaces identical, even though the hardware is different.
29
30Note that the interfaces that are provided by the driver may be "intermediate" &mdash;
31that is, they might not necessarily represent the "final" device in the chain.
32
33Consider a PCI-based ethernet device.
34First, a base PCI driver is required that understands how to talk to the PCI bus itself.
35This driver doesn't know anything about ethernet, but it does know
36how to deal with the specific PCI chipset present on the machine.
37
38It enumerates the devices on that bus, collects information
39from the various registers on each device, and provides functions that allow
40its clients (such as the PCI-based ethernet driver) to perform PCI operations
41like allocating an interrupt or a DMA channel.
42
43Thus, this base PCI driver provides services to the ethernet driver, allowing
44the ethernet driver to manage its associated hardware.
45
46At the same time, other devices (such as a video card) could also use the base PCI
47driver in a similar manner to manage their hardware.
48
49## The Zircon model
50
51In order to provide maximum flexibility, drivers in the Zircon world are allowed
52to bind to matching "parent" devices, and publish "children" of their own.
53This hierarchy extends as required: one driver might publish a child, only to have
54another driver consider that child their parent, with the second driver publishing
55its own children, and so on.
56
57In order to understand how this works, let's follow the PCI-based ethernet example.
58
59The system starts by providing a special "PCI root" parent.
60Effectively, it's saying "I know that there's a PCI bus on this system, when you
61find it, bind it *here*."
62
63> The "Advanced Topics" section below has more details about this process.
64
65Drivers are evaluated by the system (a directory is searched), and drivers that
66match are automatically bound.
67
68In this case, a driver that binds to a "PCI root" parent is found, and bound.
69
70This is the base PCI driver.
71It's job is to configure the PCI bus, and enumerate the peripherals on the bus.
72
73The PCI bus has specific conventions for how peripherals are identified:
74a combination of a Vendor ID (**VID**) and Device ID (**DID**) uniquely identifies
75all possible PCI devices.
76During enumeration, these values are read from the peripheral, and new parent
77nodes are published containing the detected VID and DID (and a host of other
78information).
79
80Every time a new device is published, the same process as described above (for
81the initial PCI root device publication) repeats;
82that is, drivers are evaluated by the system, searching for drivers that match
83up with the new parents' characteristics.
84
85Whereas with the PCI root device we were searching for a driver that matched
86a certain kind of functionality (called a "protocol," we'll see this shortly), in
87this case, however, we're searching for drivers that match a different
88protocol, namely one that satisfies the requirements of "is a PCI device and
89has a given VID and DID."
90
91If a suitable driver is found (one that matches the required protocol, VID and
92DID), it's bound to the parent.
93
94As part of binding, we initialize the driver &mdash; this involves such operations
95as setting up the card for operation, bringing up the interface(s), and
96publishing a child or children of this device.
97In the case of the PCI ethernet driver, it publishes the "ethernet" interface,
98which conforms to yet another protocol, called the "ethernet implementation" protocol.
99This protocol represents a common protocol that's close to the functions that
100clients use (but is one step removed; we'll come back to this).
101
102### Protocols
103
104We mentioned three protocols above:
105
106*   the PCI root protocol (`ZX_PROTOCOL_PCIROOT`),
107*   the PCI device protocol (`ZX_PROTOCOL_PCI`), and
108*   the ethernet implementation protocol (`ZX_PROTOCOL_ETHERNET_IMPL`).
109
110The names in brackets are the C language constants corresponding to the protocols, for reference.
111
112So what is a protocol?
113
114A protocol is a strict interface definition.
115
116The ethernet driver published an interface that conforms to `ZX_PROTOCOL_ETHERNET_IMPL`.
117This means that it must provide a set of functions defined in a data structure
118(in this case, `ethmac_protocol_ops_t`).
119
120These functions are common to all devices implementing the protocol &mdash; for example,
121all ethernet devices must provide a function that queries the MAC address of the
122interface.
123
124Other protocols will of course have different requirements for the functions they
125must provide.
126For example a block device will publish an interface that conforms to the
127"block implementation protocol" (`ZX_PROTOCOL_BLOCK_IMPL`) and
128provide functions defined by `block_protocol_ops_t`.
129This protocol includes a function that returns the size of the device in blocks,
130for example.
131
132We'll examine these protocols in the following chapters.
133
134# Advanced Topics
135
136The above has presented a big picture view of Zircon drivers, with a focus on protocols.
137
138In this section, we'll examine some advanced topics, such as platform dependent
139and platform independent code decoupling,
140the "miscellaneous" protocol, and how protocols and processes are mapped.
141
142## Platform dependent vs platform independent
143
144Above, we mentioned that `ZX_PROTOCOL_ETHERNET_IMPL` was "close to" the functions used
145by the client, but one step removed.
146That's because there's one more protocol, `ZX_PROTOCOL_ETHERNET`, that sits between
147the client and the driver.
148This additional protocol is in place to handle functionality common to all ethernet
149drivers (in order to avoid code duplication).
150Such functionality includes buffer management, status reporting, and administrative
151functions.
152
153This is effectively a "platform dependent" vs "platform independent" decoupling;
154common code exists in the platform independent part (once), and driver-specific code
155is implemented in the platform dependent part.
156
157This architecture is repeated in multiple places.
158With block devices, for example, the hardware driver binds to the bus (e.g., PCI)
159and provides a `ZX_PROTOCOL_BLOCK_IMPL` protocol.
160The platform independent driver binds to `ZX_PROTOCOL_BLOCK_IMPL`, and publishes the
161client-facing protocol, `ZX_PROTOCOL_BLOCK`.
162
163You'll also see this with the display controllers, I<sup>2</sup>C bus, and serial drivers.
164
165## Miscellaneous protocol
166
167In [simple drivers](simple.md), we show the code for several drivers that illustrate
168basic functionality, but don't provide services related to a specific protocol
169(i.e., they are not "ethernet" or "block" devices).
170These drivers are bound to `ZX_PROTOCOL_MISC_PARENT`.
171
172> @@@ More content?
173
174## Process / protocol mapping
175
176In order to keep the discussions above simple, we didn't talk about process separation
177as it relates to the drivers.
178To understand the issues, let's see how other operating systems deal with them,
179and compare that to the Zircon approach.
180
181In a monolithic kernel, such as Linux, many drivers are implemented within the kernel.
182This means that they share the same address space, and effectively live in the same
183"process."
184
185The major problem with this approach is fault isolation / exploitation.
186A bad driver can take out the entire kernel, because it lives in the same address
187space and thus has privileged access to all kernel memory and resources.
188A compromised driver can present a security threat for the same reason.
189
190The other extreme, that is, putting each and every driver service into its own
191process, is used by some microkernel operating systems.
192Its major drawback is that if one driver relies on the services of another driver,
193the kernel must effect at least a context switch operation (if not a data transfer
194as well) between the two driver processes.
195While microkernel operating systems are usually designed to be fast at these
196kinds of operations, performing them at high frequency is undesirable.
197
198The approach taken by Zircon is based on the concept of a device host (**devhost**).
199A devhost is a process that contains a protocol stack &mdash; that is, one or
200more protocols that work together.
201The devhost loads drivers from ELF shared libraries (called Dynamic Shared Objects,
202or **DSO**s).
203In the [simple drivers](simple.md) section, we'll see the meta information
204that's contained in the DSO to facilitate the discovery process.
205
206The protocol stack effectively allows the creation of a complete "driver" for
207a device, consisting of platform dependent and platform independent components,
208in a self-contained process container.
209
210For the advanced reader, take a look at the `dm dump` command available from
211the Zircon command line.
212It displays a tree of devices, and shows you the process ID, DSO name, and
213other useful information.
214
215Here's a highly-edited version showing just the PCI ethernet driver parts:
216
217```
2181. [root]
2192.    [sys]
2203.       <sys> pid=1416 /boot/driver/bus-acpi.so
2214.          [acpi] pid=1416 /boot/driver/bus-acpi.so
2225.          [pci] pid=1416 /boot/driver/bus-acpi.so
223            ...
2246.             [00:02:00] pid=1416 /boot/driver/bus-pci.so
2257.                <00:02:00> pid=2052 /boot/driver/bus-pci.proxy.so
2268.                   [intel-ethernet] pid=2052 /boot/driver/intel-ethernet.so
2279.                      [ethernet] pid=2052 /boot/driver/ethernet.so
228```
229
230From the above, you can see that process ID `1416` (lines 3 through 6)
231is the Advanced Configuration and Power Interface (**ACPI**) driver, implemented
232by the DSO `bus-acpi.so`.
233
234During primary enumeration, the ACPI DSO detected a PCI bus.
235This caused the publication of a parent with `ZX_PROTOCOL_PCI_ROOT` (line 5,
236causing the appearance of the `[pci]` entry),
237which then caused the devhost to load the `bus-pci.so` DSO and bind to it.
238That DSO is the "base PCI driver" to which we've been referring throughout the
239discussions above.
240
241During its binding, the base PCI driver enumerated the PCI bus, and found an ethernet
242card (line 6 detects bus 0, device 2, function 0, shown as `[00:02:00]`).
243(Of course, many other devices were found as well, but we've removed them from
244the above listing for simplicity).
245
246The detection of this device then caused the base PCI driver to publish a new parent
247with `ZX_PROTOCOL_PCI` and the device's VID and DID.
248Additionally, a new devhost (process ID `2052`) was created and loaded with the
249`bus-pci.proxy.so` DSO (line 7).
250This proxy serves as the interface from the new devhost (pid `2052`) to the base PCI
251driver (pid `1416`).
252
253> This is where the decision was made to "sever" the device driver into its own
254> process &mdash; the new devhost and the base PCI driver now live in two
255> different processes.
256
257The new devhost `2052` then finds a matching child (the `intel-ethernet.so`
258DSO on line 8; it's considered a match because it has `ZX_PROTOCOL_PCI` and the correct
259VID and DID).
260That DSO publishes a `ZX_PROTOCOL_ETHERNET_IMPL`, which binds to a matching
261child (the `ethernet.so` DSO on line 9; it's considered a match because it has a
262`ZX_PROTOCOL_ETHERNET_IMPL` protocol).
263
264What's not shown by this chain is that the final DSO (`ethernet.so`) publishes
265a `ZX_PROTOCOL_ETHERNET` &mdash; that's the piece that clients can use, so of
266course there's no further "device" binding involved.
267
268
269