xref: /aosp_15_r20/external/autotest/client/cros/multimedia/usb_facade.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2015 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Li"""An interface to access the local USB facade."""
6*9c5db199SXin Li
7*9c5db199SXin Liimport glob
8*9c5db199SXin Liimport logging
9*9c5db199SXin Liimport os
10*9c5db199SXin Liimport time
11*9c5db199SXin Li
12*9c5db199SXin Lifrom autotest_lib.client.bin import utils
13*9c5db199SXin Lifrom autotest_lib.client.cros.audio import cras_dbus_utils
14*9c5db199SXin Lifrom autotest_lib.client.cros.audio import cras_utils
15*9c5db199SXin Li
16*9c5db199SXin Li
17*9c5db199SXin Liclass USBFacadeLocalError(Exception):
18*9c5db199SXin Li    """Error in USBFacadeLocal."""
19*9c5db199SXin Li    pass
20*9c5db199SXin Li
21*9c5db199SXin Li
22*9c5db199SXin Liclass USBFacadeLocal(object):
23*9c5db199SXin Li    """Facade to access the USB-related functionality.
24*9c5db199SXin Li
25*9c5db199SXin Li    Property:
26*9c5db199SXin Li      _drivers_manager: A USBDeviceDriversManager object used to manage the
27*9c5db199SXin Li                        status of drivers associated with the USB audio gadget
28*9c5db199SXin Li                        on the host side.
29*9c5db199SXin Li
30*9c5db199SXin Li    """
31*9c5db199SXin Li    _DEFAULT_DEVICE_PRODUCT_NAME = 'Linux USB Audio Gadget'
32*9c5db199SXin Li    _TIMEOUT_FINDING_USB_DEVICE_SECS = 10
33*9c5db199SXin Li    _TIMEOUT_CRAS_NODES_CHANGE_SECS = 30
34*9c5db199SXin Li
35*9c5db199SXin Li    def __init__(self):
36*9c5db199SXin Li        """Initializes the USB facade.
37*9c5db199SXin Li
38*9c5db199SXin Li        The _drivers_manager is set with a USBDeviceDriversManager, which is
39*9c5db199SXin Li        used to control the visibility and availability of a USB device on a
40*9c5db199SXin Li        host Cros device.
41*9c5db199SXin Li
42*9c5db199SXin Li        """
43*9c5db199SXin Li        self._drivers_manager = USBDeviceDriversManager()
44*9c5db199SXin Li
45*9c5db199SXin Li
46*9c5db199SXin Li    def _reenumerate_usb_devices(self):
47*9c5db199SXin Li        """Resets host controller to re-enumerate usb devices."""
48*9c5db199SXin Li        self._drivers_manager.reset_host_controller()
49*9c5db199SXin Li
50*9c5db199SXin Li
51*9c5db199SXin Li    def plug(self):
52*9c5db199SXin Li        """Sets and plugs the USB device into the host.
53*9c5db199SXin Li
54*9c5db199SXin Li        The USB device is initially set to one with the default product name,
55*9c5db199SXin Li        which is assumed to be the name of the USB audio gadget on Chameleon.
56*9c5db199SXin Li        This method blocks until Cras enumerate USB nodes within a timeout
57*9c5db199SXin Li        specified in _wait_for_nodes_changed.
58*9c5db199SXin Li
59*9c5db199SXin Li        """
60*9c5db199SXin Li        # Only supports controlling one USB device of default name.
61*9c5db199SXin Li        device_name = self._DEFAULT_DEVICE_PRODUCT_NAME
62*9c5db199SXin Li
63*9c5db199SXin Li        def find_usb_device():
64*9c5db199SXin Li            """Find USB device with name device_name.
65*9c5db199SXin Li
66*9c5db199SXin Li            @returns: True if succeed to find the device, False otherwise.
67*9c5db199SXin Li
68*9c5db199SXin Li            """
69*9c5db199SXin Li            try:
70*9c5db199SXin Li                self._drivers_manager.find_usb_device(device_name)
71*9c5db199SXin Li                return True
72*9c5db199SXin Li            except USBDeviceDriversManagerError:
73*9c5db199SXin Li                logging.debug('Can not find %s yet' % device_name)
74*9c5db199SXin Li                return False
75*9c5db199SXin Li
76*9c5db199SXin Li        if self._drivers_manager.has_found_device(device_name):
77*9c5db199SXin Li            if self._drivers_manager.drivers_are_bound():
78*9c5db199SXin Li                return
79*9c5db199SXin Li            self._drivers_manager.bind_usb_drivers()
80*9c5db199SXin Li            self._wait_for_nodes_changed()
81*9c5db199SXin Li        else:
82*9c5db199SXin Li            # If driver manager has not found device yet, re-enumerate USB
83*9c5db199SXin Li            # devices. The correct USB driver will be binded automatically.
84*9c5db199SXin Li            self._reenumerate_usb_devices()
85*9c5db199SXin Li            self._wait_for_nodes_changed()
86*9c5db199SXin Li            # Wait some time for paths and fields in sysfs to be created.
87*9c5db199SXin Li            utils.poll_for_condition(
88*9c5db199SXin Li                    condition=find_usb_device,
89*9c5db199SXin Li                    desc='Find USB device',
90*9c5db199SXin Li                    timeout=self._TIMEOUT_FINDING_USB_DEVICE_SECS)
91*9c5db199SXin Li
92*9c5db199SXin Li
93*9c5db199SXin Li    def unplug(self):
94*9c5db199SXin Li        """Unplugs the USB device from the host."""
95*9c5db199SXin Li        self._drivers_manager.unbind_usb_drivers()
96*9c5db199SXin Li
97*9c5db199SXin Li
98*9c5db199SXin Li    def _wait_for_nodes_changed(self):
99*9c5db199SXin Li        """Waits for Cras to enumerate USB nodes.
100*9c5db199SXin Li
101*9c5db199SXin Li        USB nodes will be plugged, but not necessarily selected.
102*9c5db199SXin Li
103*9c5db199SXin Li        """
104*9c5db199SXin Li        def find_usb_node():
105*9c5db199SXin Li            """Checks if USB input and output nodes are plugged.
106*9c5db199SXin Li
107*9c5db199SXin Li            @returns: True if USB input and output nodes are plugged. False
108*9c5db199SXin Li                      otherwise.
109*9c5db199SXin Li            """
110*9c5db199SXin Li            out_nodes, in_nodes = cras_utils.get_plugged_node_types()
111*9c5db199SXin Li            logging.info('Cras nodes: output: %s, input: %s',
112*9c5db199SXin Li                         out_nodes, in_nodes)
113*9c5db199SXin Li            return 'USB' in out_nodes and 'USB' in in_nodes
114*9c5db199SXin Li
115*9c5db199SXin Li        utils.poll_for_condition(
116*9c5db199SXin Li                condition=find_usb_node,
117*9c5db199SXin Li                desc='Find USB node',
118*9c5db199SXin Li                timeout=self._TIMEOUT_CRAS_NODES_CHANGE_SECS)
119*9c5db199SXin Li
120*9c5db199SXin Li
121*9c5db199SXin Liclass USBDeviceDriversManagerError(Exception):
122*9c5db199SXin Li    """Error in USBDeviceDriversManager."""
123*9c5db199SXin Li    pass
124*9c5db199SXin Li
125*9c5db199SXin Li
126*9c5db199SXin Liclass HostControllerDriver(object):
127*9c5db199SXin Li    """Abstract a host controller driver.
128*9c5db199SXin Li
129*9c5db199SXin Li    This class stores id and path like:
130*9c5db199SXin Li    path: /sys/bus/pci/drivers/echi_hcd
131*9c5db199SXin Li    id: 0000:00:1a.0
132*9c5db199SXin Li    Then, it can bind/unbind driver by writing
133*9c5db199SXin Li    0000:00:1a.0 to /sys/bus/pci/drivers/echi_hcd/bind
134*9c5db199SXin Li    and /sys/bus/pci/drivers/echi_hcd/unbind.
135*9c5db199SXin Li
136*9c5db199SXin Li    """
137*9c5db199SXin Li    def __init__(self, hcd_id, hcd_path):
138*9c5db199SXin Li        """Inits an HostControllerDriver object.
139*9c5db199SXin Li
140*9c5db199SXin Li        @param hcd_id: The HCD id, e.g. 0000:00:1a.0
141*9c5db199SXin Li        @param hcd_path: The path to HCD, e.g. /sys/bus/pci/drivers/echi_hcd.
142*9c5db199SXin Li
143*9c5db199SXin Li        """
144*9c5db199SXin Li        logging.debug('hcd id: %s, hcd path: %s', hcd_id, hcd_path)
145*9c5db199SXin Li        self._hcd_id = hcd_id
146*9c5db199SXin Li        self._hcd_path = hcd_path
147*9c5db199SXin Li
148*9c5db199SXin Li
149*9c5db199SXin Li    def reset(self):
150*9c5db199SXin Li        """Resets HCD by unbinding and binding driver."""
151*9c5db199SXin Li        utils.open_write_close(
152*9c5db199SXin Li            os.path.join(self._hcd_path, 'unbind'), self._hcd_id)
153*9c5db199SXin Li        utils.open_write_close(
154*9c5db199SXin Li            os.path.join(self._hcd_path, 'bind'), self._hcd_id)
155*9c5db199SXin Li
156*9c5db199SXin Li
157*9c5db199SXin Liclass USBDeviceDriversManager(object):
158*9c5db199SXin Li    """The class to control the USB drivers associated with a USB device.
159*9c5db199SXin Li
160*9c5db199SXin Li    By binding/unbinding certain USB driver, we can emulate the plug/unplug
161*9c5db199SXin Li    action on that bus. However, this method only applies when the USB driver
162*9c5db199SXin Li    has already been binded once.
163*9c5db199SXin Li    To solve above problem, we can unbind then bind USB host controller driver
164*9c5db199SXin Li    (HCD), then, HCD will re-enumerate all the USB devices. This method has
165*9c5db199SXin Li    a side effect that all the USB devices will be disconnected for several
166*9c5db199SXin Li    seconds, so we should only do it if needed.
167*9c5db199SXin Li    Note that there might be multiple HCDs, e.g. 0000:00:1a.0 for bus1 and
168*9c5db199SXin Li    0000:00:1b.0 for bus2.
169*9c5db199SXin Li
170*9c5db199SXin Li    Properties:
171*9c5db199SXin Li        _device_product_name: The product name given to the USB device.
172*9c5db199SXin Li        _device_bus_id: The bus ID of the USB device in the host.
173*9c5db199SXin Li        _hcd_ids: The host controller driver IDs.
174*9c5db199SXin Li        _hcds: A list of HostControllerDrivers.
175*9c5db199SXin Li
176*9c5db199SXin Li    """
177*9c5db199SXin Li    # The file to write to bind USB drivers of specified device
178*9c5db199SXin Li    _USB_BIND_FILE_PATH = '/sys/bus/usb/drivers/usb/bind'
179*9c5db199SXin Li    # The file to write to unbind USB drivers of specified device
180*9c5db199SXin Li    _USB_UNBIND_FILE_PATH = '/sys/bus/usb/drivers/usb/unbind'
181*9c5db199SXin Li    # The file path that exists when drivers are bound for current device
182*9c5db199SXin Li    _USB_BOUND_DRIVERS_FILE_PATH = '/sys/bus/usb/drivers/usb/%s/driver'
183*9c5db199SXin Li    # The pattern to glob usb drivers
184*9c5db199SXin Li    _USB_DRIVER_GLOB_PATTERN = '/sys/bus/usb/drivers/usb/usb?/'
185*9c5db199SXin Li    # The path to search for HCD on PCI or platform bus.
186*9c5db199SXin Li    # The HCD id should be filled in the end.
187*9c5db199SXin Li    _HCD_GLOB_PATTERNS = [
188*9c5db199SXin Li            '/sys/bus/pci/drivers/*/%s',
189*9c5db199SXin Li            '/sys/bus/platform/drivers/*/%s']
190*9c5db199SXin Li
191*9c5db199SXin Li
192*9c5db199SXin Li    def __init__(self):
193*9c5db199SXin Li        """Initializes the manager.
194*9c5db199SXin Li
195*9c5db199SXin Li        _device_product_name and _device_bus_id are initially set to None.
196*9c5db199SXin Li
197*9c5db199SXin Li        """
198*9c5db199SXin Li        self._device_product_name = None
199*9c5db199SXin Li        self._device_bus_id = None
200*9c5db199SXin Li        self._hcd_ids = None
201*9c5db199SXin Li        self._hcds = None
202*9c5db199SXin Li        self._find_hcd_ids()
203*9c5db199SXin Li        self._create_hcds()
204*9c5db199SXin Li
205*9c5db199SXin Li
206*9c5db199SXin Li    def _find_hcd_ids(self):
207*9c5db199SXin Li        """Finds host controller driver ids for USB.
208*9c5db199SXin Li
209*9c5db199SXin Li        We can find the HCD id for USB from driver's realpath.
210*9c5db199SXin Li        E.g. On ARM device:
211*9c5db199SXin Li        /sys/bus/usb/drivers/usb/usb1 links to
212*9c5db199SXin Li        /sys/devices/soc0/70090000.usb/xhci-hcd.0.auto/usb1
213*9c5db199SXin Li        => HCD id is xhci-hcd.0.auto
214*9c5db199SXin Li
215*9c5db199SXin Li        E.g. On X86 device:
216*9c5db199SXin Li        /sys/bus/usb/drivers/usb/usb1 links to
217*9c5db199SXin Li        /sys/devices/pci0000:00/0000:00:14.0/usb1
218*9c5db199SXin Li        => HCD id is 0000:00:14.0
219*9c5db199SXin Li
220*9c5db199SXin Li        There might be multiple HCD ids like 0000:00:1a.0 for usb1,
221*9c5db199SXin Li        and 0000:00:1d.0 for usb2.
222*9c5db199SXin Li
223*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if HCD id can not be found.
224*9c5db199SXin Li
225*9c5db199SXin Li        """
226*9c5db199SXin Li        def _get_dir_name(path):
227*9c5db199SXin Li            return os.path.basename(os.path.dirname(path))
228*9c5db199SXin Li
229*9c5db199SXin Li        hcd_ids = set()
230*9c5db199SXin Li
231*9c5db199SXin Li        for search_root_path in glob.glob(self._USB_DRIVER_GLOB_PATTERN):
232*9c5db199SXin Li            hcd_id = _get_dir_name(os.path.realpath(search_root_path))
233*9c5db199SXin Li            hcd_ids.add(hcd_id)
234*9c5db199SXin Li
235*9c5db199SXin Li        if not hcd_ids:
236*9c5db199SXin Li            raise USBDeviceDriversManagerError('Can not find HCD id')
237*9c5db199SXin Li
238*9c5db199SXin Li        self._hcd_ids = hcd_ids
239*9c5db199SXin Li        logging.debug('Found HCD ids: %s', self._hcd_ids)
240*9c5db199SXin Li
241*9c5db199SXin Li
242*9c5db199SXin Li    def _create_hcds(self):
243*9c5db199SXin Li        """Finds HCD paths from HCD id and create HostControllerDrivers.
244*9c5db199SXin Li
245*9c5db199SXin Li        HCD is under /sys/bus/pci/drivers/ for x86 boards, and under
246*9c5db199SXin Li        /sys/bus/platform/drivers/ for ARM boards.
247*9c5db199SXin Li
248*9c5db199SXin Li        For each HCD id, finds HCD by checking HCD id under it, e.g.
249*9c5db199SXin Li        /sys/bus/pci/drivers/ehci_hcd has 0000:00:1a.0 under it.
250*9c5db199SXin Li        Then, create a HostControllerDriver and store it in self._hcds.
251*9c5db199SXin Li
252*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if there are multiple
253*9c5db199SXin Li                 HCD path found for a given HCD id.
254*9c5db199SXin Li
255*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if no HostControllerDriver is found.
256*9c5db199SXin Li
257*9c5db199SXin Li        """
258*9c5db199SXin Li        self._hcds = []
259*9c5db199SXin Li
260*9c5db199SXin Li        for hcd_id in self._hcd_ids:
261*9c5db199SXin Li            for glob_pattern in self._HCD_GLOB_PATTERNS:
262*9c5db199SXin Li                glob_pattern = glob_pattern % hcd_id
263*9c5db199SXin Li                hcd_id_paths = glob.glob(glob_pattern)
264*9c5db199SXin Li                if not hcd_id_paths:
265*9c5db199SXin Li                    continue
266*9c5db199SXin Li                if len(hcd_id_paths) > 1:
267*9c5db199SXin Li                    raise USBDeviceDriversManagerError(
268*9c5db199SXin Li                            'More than 1 HCD id path found: %s' % hcd_id_paths)
269*9c5db199SXin Li                hcd_id_path = hcd_id_paths[0]
270*9c5db199SXin Li
271*9c5db199SXin Li                # Gets /sys/bus/pci/drivers/echi_hcd from
272*9c5db199SXin Li                # /sys/bus/pci/drivers/echi_hcd/0000:00:1a.0
273*9c5db199SXin Li                hcd_path = os.path.dirname(hcd_id_path)
274*9c5db199SXin Li                self._hcds.append(
275*9c5db199SXin Li                        HostControllerDriver(hcd_id=hcd_id, hcd_path=hcd_path))
276*9c5db199SXin Li
277*9c5db199SXin Li
278*9c5db199SXin Li    def reset_host_controller(self):
279*9c5db199SXin Li        """Resets host controller by unbinding then binding HCD.
280*9c5db199SXin Li
281*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if there is no HCD to control.
282*9c5db199SXin Li
283*9c5db199SXin Li        """
284*9c5db199SXin Li        if not self._hcds:
285*9c5db199SXin Li            raise USBDeviceDriversManagerError('HCD is not found yet')
286*9c5db199SXin Li        for hcd in self._hcds:
287*9c5db199SXin Li            hcd.reset()
288*9c5db199SXin Li
289*9c5db199SXin Li
290*9c5db199SXin Li    def _find_usb_device_bus_id(self, product_name):
291*9c5db199SXin Li        """Finds the bus ID of the USB device with the given product name.
292*9c5db199SXin Li
293*9c5db199SXin Li        @param product_name: The product name of the USB device as it appears
294*9c5db199SXin Li                             to the host.
295*9c5db199SXin Li
296*9c5db199SXin Li        @returns: The bus ID of the USB device if it is detected by the host
297*9c5db199SXin Li                  successfully; or None if there is no such device with the
298*9c5db199SXin Li                  given product name.
299*9c5db199SXin Li
300*9c5db199SXin Li        """
301*9c5db199SXin Li        def product_matched(path):
302*9c5db199SXin Li            """Checks if the product field matches expected product name.
303*9c5db199SXin Li
304*9c5db199SXin Li            @returns: True if the product name matches, False otherwise.
305*9c5db199SXin Li
306*9c5db199SXin Li            """
307*9c5db199SXin Li            read_product_name = utils.read_one_line(path)
308*9c5db199SXin Li            logging.debug('Read product at %s = %s', path, read_product_name)
309*9c5db199SXin Li            return read_product_name == product_name
310*9c5db199SXin Li
311*9c5db199SXin Li        # Find product field at these possible paths:
312*9c5db199SXin Li        # '/sys/bus/usb/drivers/usb/usbX/X-Y/product' => bus id is X-Y.
313*9c5db199SXin Li        # '/sys/bus/usb/drivers/usb/usbX/X-Y/X-Y.Z/product' => bus id is X-Y.Z.
314*9c5db199SXin Li
315*9c5db199SXin Li        for search_root_path in glob.glob(self._USB_DRIVER_GLOB_PATTERN):
316*9c5db199SXin Li            logging.debug('search_root_path: %s', search_root_path)
317*9c5db199SXin Li            for root, dirs, _ in os.walk(search_root_path):
318*9c5db199SXin Li                logging.debug('root: %s', root)
319*9c5db199SXin Li                for bus_id in dirs:
320*9c5db199SXin Li                    logging.debug('bus_id: %s', bus_id)
321*9c5db199SXin Li                    product_path = os.path.join(root, bus_id, 'product')
322*9c5db199SXin Li                    logging.debug('product_path: %s', product_path)
323*9c5db199SXin Li                    if not os.path.exists(product_path):
324*9c5db199SXin Li                        continue
325*9c5db199SXin Li                    if not product_matched(product_path):
326*9c5db199SXin Li                        continue
327*9c5db199SXin Li                    logging.debug(
328*9c5db199SXin Li                            'Bus ID of %s found: %s', product_name, bus_id)
329*9c5db199SXin Li                    return bus_id
330*9c5db199SXin Li
331*9c5db199SXin Li        logging.error('Bus ID of %s not found', product_name)
332*9c5db199SXin Li        return None
333*9c5db199SXin Li
334*9c5db199SXin Li
335*9c5db199SXin Li    def has_found_device(self, product_name):
336*9c5db199SXin Li        """Checks if the device has been found.
337*9c5db199SXin Li
338*9c5db199SXin Li        @param product_name: The product name of the USB device as it appears
339*9c5db199SXin Li                             to the host.
340*9c5db199SXin Li
341*9c5db199SXin Li        @returns: True if device has been found, False otherwise.
342*9c5db199SXin Li
343*9c5db199SXin Li        """
344*9c5db199SXin Li        return self._device_product_name == product_name
345*9c5db199SXin Li
346*9c5db199SXin Li
347*9c5db199SXin Li    def find_usb_device(self, product_name):
348*9c5db199SXin Li        """Sets _device_product_name and _device_bus_id if it can be found.
349*9c5db199SXin Li
350*9c5db199SXin Li        @param product_name: The product name of the USB device as it appears
351*9c5db199SXin Li                             to the host.
352*9c5db199SXin Li
353*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if device bus ID cannot be found
354*9c5db199SXin Li                 for the device with the given product name.
355*9c5db199SXin Li
356*9c5db199SXin Li        """
357*9c5db199SXin Li        device_bus_id = self._find_usb_device_bus_id(product_name)
358*9c5db199SXin Li        if device_bus_id is None:
359*9c5db199SXin Li            error_message = 'Cannot find device with product name: %s'
360*9c5db199SXin Li            raise USBDeviceDriversManagerError(error_message % product_name)
361*9c5db199SXin Li        else:
362*9c5db199SXin Li            self._device_product_name = product_name
363*9c5db199SXin Li            self._device_bus_id = device_bus_id
364*9c5db199SXin Li
365*9c5db199SXin Li
366*9c5db199SXin Li    def drivers_are_bound(self):
367*9c5db199SXin Li        """Checks whether the drivers with the of current device are bound.
368*9c5db199SXin Li
369*9c5db199SXin Li        If the drivers are already bound, calling bind_usb_drivers will be
370*9c5db199SXin Li        redundant and also result in an error.
371*9c5db199SXin Li
372*9c5db199SXin Li        @return: True if the path to the drivers exist, meaning the drivers
373*9c5db199SXin Li                 are already bound. False otherwise.
374*9c5db199SXin Li
375*9c5db199SXin Li        """
376*9c5db199SXin Li        if self._device_bus_id is None:
377*9c5db199SXin Li            raise USBDeviceDriversManagerError('USB Bus ID is not set yet.')
378*9c5db199SXin Li        driver_path = self._USB_BOUND_DRIVERS_FILE_PATH % self._device_bus_id
379*9c5db199SXin Li        return os.path.exists(driver_path)
380*9c5db199SXin Li
381*9c5db199SXin Li
382*9c5db199SXin Li    def bind_usb_drivers(self):
383*9c5db199SXin Li        """Binds the USB driver(s) of the current device to the host.
384*9c5db199SXin Li
385*9c5db199SXin Li        This is applied to all the drivers associated with and listed under
386*9c5db199SXin Li        the USB device with the current _device_product_name and _device_bus_id.
387*9c5db199SXin Li
388*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if device bus ID for this instance
389*9c5db199SXin Li                 has not been set yet.
390*9c5db199SXin Li
391*9c5db199SXin Li        """
392*9c5db199SXin Li        if self._device_bus_id is None:
393*9c5db199SXin Li            raise USBDeviceDriversManagerError('USB Bus ID is not set yet.')
394*9c5db199SXin Li        if self.drivers_are_bound():
395*9c5db199SXin Li            return
396*9c5db199SXin Li        utils.open_write_close(self._USB_BIND_FILE_PATH,
397*9c5db199SXin Li                self._device_bus_id)
398*9c5db199SXin Li
399*9c5db199SXin Li
400*9c5db199SXin Li    def unbind_usb_drivers(self):
401*9c5db199SXin Li        """Unbinds the USB driver(s) of the current device from the host.
402*9c5db199SXin Li
403*9c5db199SXin Li        This is applied to all the drivers associated with and listed under
404*9c5db199SXin Li        the USB device with the current _device_product_name and _device_bus_id.
405*9c5db199SXin Li
406*9c5db199SXin Li        @raises: USBDeviceDriversManagerError if device bus ID for this instance
407*9c5db199SXin Li                 has not been set yet.
408*9c5db199SXin Li
409*9c5db199SXin Li        """
410*9c5db199SXin Li        if self._device_bus_id is None:
411*9c5db199SXin Li            raise USBDeviceDriversManagerError('USB Bus ID is not set yet.')
412*9c5db199SXin Li        if not self.drivers_are_bound():
413*9c5db199SXin Li            return
414*9c5db199SXin Li        utils.open_write_close(self._USB_UNBIND_FILE_PATH,
415*9c5db199SXin Li                                    self._device_bus_id)
416