Kristen Carlson Accardi Docks, Bays, and Ports Kristen Carlson Accardi
Agenda How do hotpluggable devices work generically? How do hotpluggable devices work with ACPI? How do we work with hotpluggable devices in userspace? What are we going to talk about today? Docks, Bays and Ports (ok, I made up some excuse to use the word Port in the title – actually we are going to talk about the new Docking Station driver and the new removable Bay driver, using them as concrete examples of hotpluggable devices. First I'm going to give an overview of the process that all hotpluggable devices use regardless of what type they are. Then I'm going to show you how system manufacturers use ACPI to assist in this process. Not all hotpluggable devices require the use of ACPI, but for the Dock and the Bay driver, ACPI is used. Finally I will talk about how userspace gets notified about hotplug events and show you a very primative example of how you can get notifications from the new dock & bay driver and do what you want with them.
Hotplug Overview Detect something has changed (insertion or removal) Via Interrupt or via polling Usually requires that a bus be rescanned to detect the changes Initialize the new device (device driver will do this) Including device memory allocation or free, interrupt allocation etc. Create or Destroy Software representations of device Some buses require physical unlocking of the device to prevent unsafe removal or unlocking via software How we detect something has changed depends on the device. In PCI, this is generally accomplished via an intettrupt to the hotplug controller. This interrupt could be just to indicate that the latch on the hotplug slot has been closed, or it could indicated that adapter presence has changed. PCI hotplug interrupts cause the hotplug driver to rescan the PCI bus at a specific spot in the bus heirarchy since most controllers can indicate which PCI slot the interrupt occurred on. Other hotpluggable devices may require more extensive scanning. The new device will need to be intialized after it is detected – this is usually accomplished by loading the device driver for the new device, or it could also be accomplished by calling various ACPI methods (we will cover this soon) Usually devices are represented in the OS in various places – in Linux we can see part of this representation in /sys For removal – for some buses the hardware requires that power be disconnected before removing the device, or that the device be locked or unlocked. This is obviously device dependent.
ACPI Basics (very, very basic...) ACPI is a specification which essentially allows system manufacturers to write configuration and power management code to be executed by the OS via the ACPI interpreter ACPI software will capture ACPI events and route them to the interested drivers ACPI software allows drivers to execute specific methods that are provided by the system manufacturer that are found in the system's DSDT table, which is read by the ACPI subsystem at boot time To learn more about ACPI in Linux, check out acpi.sourceforge.net – the spec is > 600 pages long, so we will only talk about ACPI as it relates to hotplug. ACPI allows system vendors to ship code to configure devices exactly how they want them configured – this can sometimes cause problems if the OS expects things to be configured differently than the system vendor imagined. In Linux, we don't use many ACPI methods – but we use some because they are a useful abstraction at times. Many times system vendors will ship DSDT code that was never tested in Linux. (plug Linux firmware development kit). We are trying to make it easier for system vendors to not write code that screws us up.
Hot Plug Drivers and ACPI At driver load time, search for devices defined in the DSDT which are hotpluggable. You can do this by looking for specific methods which must be defined for removable devices, such as _EJ0, or _EJD, or you can look for other indications that a removable device is present Tell ACPI you are interested in Events occuring on a specific ACPI device – usually “Bus Check” or “Device Check”. These events indicate that the bus should be rescanned. If “Eject”, then usually call the provided ACPI method to perform the removal safely (will usually unlock or power down connector) If a new device, then configure, sometimes executing ACPI methods provided to do this PRT, DCK, - is device dependent what you would execute Drivers register a notification routine with ACPI subsystem to be called when specific events occur. You can do this for devices defined in the DSDT that may or may not be present already. All PCI slots which are hotpluggable must have an ACPI method called _EJ0 defined. All dock stations must have a function called _DCK defined. There are many other methods not discussed here – especially with regard to power management that can be executed for devices.
How Docking works with ACPI System vendor defines an ACPI object for the dock that implements a _DCK method _DCK is a control method, and not only defines the object as a dock station, but is also executed to control the isolation logic on the connector, as well as perform any other system specific configuration After _DCK is completed, the OS must reenumerate all enumerable buses as well as add non-enumerable devices Enumerable Buses are PCI, USB. Non- enumerable devices are devices which would not be detected via a bus scan – such as serial devices. System vendors will put any number of things in the _DCK method. They will most likely be reconfiguring some devices to recognize the new dock station devices. For example, the USB host controller might get some bits set to turn on a new port. Or a PCI bridge might be configured to request some I/O mem. (this can really screw things up btw).
Dock Station Example DSDT Docking is controlled by ACPI Device(DOCK1) { // Pass through dock – DOCK1 Name(_ADR, …) Method(_EJ0, 0) {…} Method(_DCK, 1) {…} If an Object contains a method called _DCK, then it is a docking station You can go to acpi.sourceforge.net to get instructions on how to look at your DSDT if you are curious.
The _DCK method definition Arguments: Arg0 1–Dock (that is, remove isolation from connector) 0–Undock (isolate from connector) Return Code: 1 if successful, 0 if failed.
Docking Basics Cold Docking/Undocking Always supported by Linux Warm Docking/Undocking Suspend, then dock or undock. This may or may not work Hot Docking/Undocking Insert or remove laptop while completely powered on. Not supported until now. We are really just going to talk about hot docking here, because it is the most fun.
A peek in the dock driver code static int __init dock_init(void) { int num = 0; dock_station = NULL; /* look for a dock station */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_dock, &num, NULL); ... } As you recall from slide 5, the first thing we have to do is check the system's DSDT table for a dock station. We do this by using this function “walk_namespace” simply tells the ACPI subsystem to walk through all the devices starting at the root and for each one call the function “find_dock” that I have defined to discover whether a dock station is present.
Dock driver (cont...) /** * is_dock - see if a device is a dock station * @handle: acpi handle of the device * * If an acpi object has a _DCK method, then it is by definition a dock * station, so return true. */ static int is_dock(acpi_handle handle) { acpi_status status; acpi_handle tmp; status = acpi_get_handle(handle, "_DCK", &tmp); if (ACPI_FAILURE(status)) return 0; return 1; } I promise – we are not doing a ful code walk through like I did at OLS :). I show you this function to show you how the device driver will simply look for the presence of the _DCK method in order to determine if a device defined in the ACPI namespace is a dock station or not.
Dock driver (cont ...) static void dock_notify(acpi_handle handle, u32 event, void *data) { struct dock_station *ds = data; switch (event) { case ACPI_NOTIFY_BUS_CHECK: if (!dock_in_progress(ds) && dock_present(ds)) { begin_dock(ds); dock(ds); if (!dock_present(ds)) { printk(KERN_ERR PREFIX "Unable to dock!\n"); break; } atomic_notifier_call_chain(&dock_notifier_list, .... case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_EJECT_REQUEST: handle_eject_request(ds, event); This is the notification routine that we registered with ACPI. It is called when events occur on the dock device that we identified at Init time. Here you can see that we care about “bus check”, “device check” and “eject request”. It's pretty straght forward, if it's a bus check, it means we were plugged into the dock station. We call a function to execute the _DCK method, and we call a function which alerts other drivers that they should rescan their various busses, or for non-enumerable devices, they would just insert themselfs here. Eject is pretty much the same thing only to accomplish the opposite result.
Dock driver (cont...) “Rescanning” is accomplished by other device drivers. The dock driver must execute the _DCK method upon docking and undocking as well as calling _EJ0 to eject safely arg_list.count = 1; arg_list.pointer = &arg; arg.type = ACPI_TYPE_INTEGER; arg.integer.value = 1; if (ACPI_FAILURE(acpi_evaluate_object(ds->handle, "_EJ0", &arg_list, NULL))) pr_debug("Failed to evaluate _EJ0!\n"); All dock stations are different. Some have PCI bus on them, some do not. All subsystems are different. Some require rescanning (PCI) some don't (USB). The dock driver allows other drivers to register functions to be called upon docking and undocking which will let these drivers handle the rescan. In this way, we allow the dock driver to remain small and simple and reuse existing code in subsystems. The code here shows how we execute an ACPI method with an argument. The _DCK method is executed in a similar way.
An ATA device that is listed as ejectable in ACPI namespace Removable Drive Bays What is it? An ATA device that is listed as ejectable in ACPI namespace How do we find it? Searching for the bay is a bit more difficult than finding the dock How do we insert/remove? More complicated due to PATA Demonstrate where the drive bay is on your laptop. Sometimes they are USB – but this driver only deals with ATA devices. Removable drive bays are sometimes given marketing names like “Ultrabay” or “Module Bay”. They are simply a tray that allows any ATA device that fits in it to be plugged in. These devices are either extra hard drives, or CD/DVD drives. Some are PATA, some are SATA. The bay driver was ported from the dock driver, in that it is exactly the same concept. Search ACPI name space for an ejectable drive bay, register notification handler, and then “do something”. It is a much simpler driver in that currently it doesn't need to interact with other subsystems (although that would be nice for the future).
Bay defined in DSDT ATA controller is identified by searching for ATA specific ACPI methods Device (SCND) { Name (_ADR, 0x01) Method (_GTM, 0, NotSerialized) Method (_STM, 3, NotSerialized) } Device is child of controller – Device contains the “Eject” routine Scope (\_SB.PCI0.IDE0.SCND.MSTR) { Method (_EJ0, 1, NotSerialized) Method (_STA, 0, NotSerialized) Here is a specific implementation of a removable drive bay. In the bay driver, we have to search for all ejectable devices, and then determine if either that device or it's parent is an ATA controller. We do this by searching for ATA specific ACPI methods.
Bay driver notification routine switch(event) { case ACPI_NOTIFY_BUS_CHECK: case ACPI_NOTIFY_DEVICE_CHECK: case ACPI_NOTIFY_EJECT_REQUEST: kobject_uevent(&dev->kobj, KOBJ_CHANGE); break; default: printk(KERN_ERR PREFIX "Bay: unknown event %d\n", event); } If you are wondering exactly what this driver does, you are correct, it actually does nothing but capture the event and send it up to user space via a uevent. This is due to PATA – and it's lack of hotplug support. But wait... we know if we have _EJ0, it's a requirement to call this to safely eject the device, so where does that happen? After user space is notified, they can write a file in sysfs to perform the eject function.
Userspace notifications for drive bay and dock Drive bays and dock stations both present a simple user interface via sysfs. They are both “platform” devices, and have files located in /sys/devices/platform/ To cause the driver to execute _EJ0 for bay, or _DCK for dock, you write “1” to the “eject” file. To check the status of the device (whether it is present or not) you can read the “present” or “docked” file. The drivers both indicate status changes via the CHANGED uevent. The bay driver will NOT eject until userspace writes the file, the dock driver WILL dock or undock without userspace intervention if the button is pressed. UDEV rules can be created to deal with these events Sure would be nice if I'd written a udev script to handle the bay...
Future Work? SATA does not require _EJ0, and therefore does not require special ACPI drivers for hotplug activities. Unfortunately, most laptops still ship with CDROM as PATA. But, that will change eventually Ideally, the bay eject could be done from the ATA subsystem rather than in a separate driver and would require (little) user intervention The Dock driver is feature complete in the kernel, and just needs someone to spiff up the user interface and “do stuff” during dock/undock It's hard to predict what things will need to be done, but as of now, my todo list on the drivers is pretty empty. Expect the bay driver in 2.6.21 if we are lucky