1 /* 2 * USB-ACPI glue code 3 * 4 * Copyright 2012 Red Hat <mjg@redhat.com> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the Free 8 * Software Foundation, version 2. 9 * 10 */ 11 #include <linux/module.h> 12 #include <linux/usb.h> 13 #include <linux/device.h> 14 #include <linux/errno.h> 15 #include <linux/kernel.h> 16 #include <linux/acpi.h> 17 #include <linux/pci.h> 18 #include <acpi/acpi_bus.h> 19 20 #include "usb.h" 21 22 static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle) 23 { 24 acpi_status status; 25 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 26 union acpi_object *upc; 27 int ret = 0; 28 29 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); 30 31 if (ACPI_FAILURE(status)) 32 return -ENODEV; 33 34 upc = buffer.pointer; 35 36 if (!upc || (upc->type != ACPI_TYPE_PACKAGE) 37 || upc->package.count != 4) { 38 ret = -EINVAL; 39 goto out; 40 } 41 42 if (upc->package.elements[0].integer.value) 43 udev->removable = USB_DEVICE_REMOVABLE; 44 else 45 udev->removable = USB_DEVICE_FIXED; 46 47 out: 48 kfree(upc); 49 return ret; 50 } 51 52 static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle) 53 { 54 acpi_status status; 55 struct acpi_pld pld; 56 57 status = acpi_get_physical_device_location(handle, &pld); 58 59 if (ACPI_FAILURE(status)) 60 return -ENODEV; 61 62 if (pld.user_visible) 63 udev->removable = USB_DEVICE_REMOVABLE; 64 else 65 udev->removable = USB_DEVICE_FIXED; 66 67 return 0; 68 } 69 70 static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) 71 { 72 struct usb_device *udev; 73 struct device *parent; 74 acpi_handle *parent_handle; 75 76 if (!is_usb_device(dev)) 77 return -ENODEV; 78 79 udev = to_usb_device(dev); 80 parent = dev->parent; 81 parent_handle = DEVICE_ACPI_HANDLE(parent); 82 83 if (!parent_handle) 84 return -ENODEV; 85 86 *handle = acpi_get_child(parent_handle, udev->portnum); 87 88 if (!*handle) 89 return -ENODEV; 90 91 /* 92 * PLD will tell us whether a port is removable to the user or 93 * not. If we don't get an answer from PLD (it's not present 94 * or it's malformed) then try to infer it from UPC. If a 95 * device isn't connectable then it's probably not removable. 96 */ 97 if (usb_acpi_check_pld(udev, *handle) != 0) 98 usb_acpi_check_upc(udev, *handle); 99 100 return 0; 101 } 102 103 static struct acpi_bus_type usb_acpi_bus = { 104 .bus = &usb_bus_type, 105 .find_bridge = NULL, 106 .find_device = usb_acpi_find_device, 107 }; 108 109 int usb_acpi_register(void) 110 { 111 return register_acpi_bus_type(&usb_acpi_bus); 112 } 113 114 void usb_acpi_unregister(void) 115 { 116 unregister_acpi_bus_type(&usb_acpi_bus); 117 } 118