xref: /dpdk/usertools/dpdk-devbind.py (revision b5685e39)
1#!/usr/bin/env python3
2# SPDX-License-Identifier: BSD-3-Clause
3# Copyright(c) 2010-2014 Intel Corporation
4#
5
6import sys
7import os
8import getopt
9import subprocess
10from glob import glob
11from os.path import exists, abspath, dirname, basename
12from os.path import join as path_join
13
14# The PCI base class for all devices
15network_class = {'Class': '02', 'Vendor': None, 'Device': None,
16                    'SVendor': None, 'SDevice': None}
17acceleration_class = {'Class': '12', 'Vendor': None, 'Device': None,
18                      'SVendor': None, 'SDevice': None}
19ifpga_class = {'Class': '12', 'Vendor': '8086', 'Device': '0b30',
20                    'SVendor': None, 'SDevice': None}
21encryption_class = {'Class': '10', 'Vendor': None, 'Device': None,
22                   'SVendor': None, 'SDevice': None}
23intel_processor_class = {'Class': '0b', 'Vendor': '8086', 'Device': None,
24                   'SVendor': None, 'SDevice': None}
25cavium_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a04b,a04d',
26              'SVendor': None, 'SDevice': None}
27cavium_fpa = {'Class': '08', 'Vendor': '177d', 'Device': 'a053',
28              'SVendor': None, 'SDevice': None}
29cavium_pkx = {'Class': '08', 'Vendor': '177d', 'Device': 'a0dd,a049',
30              'SVendor': None, 'SDevice': None}
31cavium_tim = {'Class': '08', 'Vendor': '177d', 'Device': 'a051',
32              'SVendor': None, 'SDevice': None}
33cavium_zip = {'Class': '12', 'Vendor': '177d', 'Device': 'a037',
34              'SVendor': None, 'SDevice': None}
35avp_vnic = {'Class': '05', 'Vendor': '1af4', 'Device': '1110',
36              'SVendor': None, 'SDevice': None}
37
38octeontx2_sso = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f9,a0fa',
39              'SVendor': None, 'SDevice': None}
40octeontx2_npa = {'Class': '08', 'Vendor': '177d', 'Device': 'a0fb,a0fc',
41              'SVendor': None, 'SDevice': None}
42octeontx2_dma = {'Class': '08', 'Vendor': '177d', 'Device': 'a081',
43              'SVendor': None, 'SDevice': None}
44octeontx2_ree = {'Class': '08', 'Vendor': '177d', 'Device': 'a0f4',
45              'SVendor': None, 'SDevice': None}
46
47intel_ioat_bdw = {'Class': '08', 'Vendor': '8086', 'Device': '6f20,6f21,6f22,6f23,6f24,6f25,6f26,6f27,6f2e,6f2f',
48              'SVendor': None, 'SDevice': None}
49intel_ioat_skx = {'Class': '08', 'Vendor': '8086', 'Device': '2021',
50              'SVendor': None, 'SDevice': None}
51intel_ioat_icx = {'Class': '08', 'Vendor': '8086', 'Device': '0b00',
52              'SVendor': None, 'SDevice': None}
53intel_idxd_spr = {'Class': '08', 'Vendor': '8086', 'Device': '0b25',
54              'SVendor': None, 'SDevice': None}
55intel_ntb_skx = {'Class': '06', 'Vendor': '8086', 'Device': '201c',
56              'SVendor': None, 'SDevice': None}
57intel_ntb_icx = {'Class': '06', 'Vendor': '8086', 'Device': '347e',
58              'SVendor': None, 'SDevice': None}
59
60network_devices = [network_class, cavium_pkx, avp_vnic, ifpga_class]
61baseband_devices = [acceleration_class]
62crypto_devices = [encryption_class, intel_processor_class]
63eventdev_devices = [cavium_sso, cavium_tim, octeontx2_sso]
64mempool_devices = [cavium_fpa, octeontx2_npa]
65compress_devices = [cavium_zip]
66regex_devices = [octeontx2_ree]
67misc_devices = [intel_ioat_bdw, intel_ioat_skx, intel_ioat_icx, intel_idxd_spr,
68                intel_ntb_skx, intel_ntb_icx,
69                octeontx2_dma]
70
71# global dict ethernet devices present. Dictionary indexed by PCI address.
72# Each device within this is itself a dictionary of device properties
73devices = {}
74# list of supported DPDK drivers
75dpdk_drivers = ["igb_uio", "vfio-pci", "uio_pci_generic"]
76# list of currently loaded kernel modules
77loaded_modules = None
78
79# command-line arg flags
80b_flag = None
81status_flag = False
82force_flag = False
83args = []
84
85
86def usage():
87    '''Print usage information for the program'''
88    argv0 = basename(sys.argv[0])
89    print("""
90Usage:
91------
92
93     %(argv0)s [options] DEVICE1 DEVICE2 ....
94
95where DEVICE1, DEVICE2 etc, are specified via PCI "domain:bus:slot.func" syntax
96or "bus:slot.func" syntax. For devices bound to Linux kernel drivers, they may
97also be referred to by Linux interface name e.g. eth0, eth1, em0, em1, etc.
98If devices are specified using PCI <domain:>bus:device:func format, then
99shell wildcards and ranges may be used, e.g. 80:04.*, 80:04.[0-3]
100
101Options:
102    --help, --usage:
103        Display usage information and quit
104
105    -s, --status:
106        Print the current status of all known network, crypto, event
107        and mempool devices.
108        For each device, it displays the PCI domain, bus, slot and function,
109        along with a text description of the device. Depending upon whether the
110        device is being used by a kernel driver, the igb_uio driver, or no
111        driver, other relevant information will be displayed:
112        * the Linux interface name e.g. if=eth0
113        * the driver being used e.g. drv=igb_uio
114        * any suitable drivers not currently using that device
115            e.g. unused=igb_uio
116        NOTE: if this flag is passed along with a bind/unbind option, the
117        status display will always occur after the other operations have taken
118        place.
119
120    --status-dev:
121        Print the status of given device group. Supported device groups are:
122        "net", "baseband", "crypto", "event", "mempool" and "compress"
123
124    -b driver, --bind=driver:
125        Select the driver to use or \"none\" to unbind the device
126
127    -u, --unbind:
128        Unbind a device (Equivalent to \"-b none\")
129
130    --force:
131        By default, network devices which are used by Linux - as indicated by
132        having routes in the routing table - cannot be modified. Using the
133        --force flag overrides this behavior, allowing active links to be
134        forcibly unbound.
135        WARNING: This can lead to loss of network connection and should be used
136        with caution.
137
138Examples:
139---------
140
141To display current device status:
142        %(argv0)s --status
143
144To display current network device status:
145        %(argv0)s --status-dev net
146
147To bind eth1 from the current driver and move to use igb_uio
148        %(argv0)s --bind=igb_uio eth1
149
150To unbind 0000:01:00.0 from using any driver
151        %(argv0)s -u 0000:01:00.0
152
153To bind 0000:02:00.0 and 0000:02:00.1 to the ixgbe kernel driver
154        %(argv0)s -b ixgbe 02:00.0 02:00.1
155
156To bind all functions on device 0000:02:00 to ixgbe kernel driver
157        %(argv0)s -b ixgbe 02:00.*
158
159    """ % locals())  # replace items from local variables
160
161# check if a specific kernel module is loaded
162def module_is_loaded(module):
163    global loaded_modules
164
165    if module == 'vfio_pci':
166        module = 'vfio-pci'
167
168    if loaded_modules:
169        return module in loaded_modules
170
171    # Get list of sysfs modules (both built-in and dynamically loaded)
172    sysfs_path = '/sys/module/'
173
174    # Get the list of directories in sysfs_path
175    sysfs_mods = [m for m in os.listdir(sysfs_path)
176                  if os.path.isdir(os.path.join(sysfs_path, m))]
177
178    # special case for vfio_pci (module is named vfio-pci,
179    # but its .ko is named vfio_pci)
180    sysfs_mods = [a if a != 'vfio_pci' else 'vfio-pci' for a in sysfs_mods]
181
182    loaded_modules = sysfs_mods
183
184    return module in sysfs_mods
185
186
187def check_modules():
188    '''Checks that igb_uio is loaded'''
189    global dpdk_drivers
190
191    # list of supported modules
192    mods = [{"Name": driver, "Found": False} for driver in dpdk_drivers]
193
194    # first check if module is loaded
195    for mod in mods:
196        if module_is_loaded(mod["Name"]):
197            mod["Found"] = True
198
199    # check if we have at least one loaded module
200    if True not in [mod["Found"] for mod in mods] and b_flag is not None:
201        print("Warning: no supported DPDK kernel modules are loaded", file=sys.stderr)
202
203    # change DPDK driver list to only contain drivers that are loaded
204    dpdk_drivers = [mod["Name"] for mod in mods if mod["Found"]]
205
206
207def has_driver(dev_id):
208    '''return true if a device is assigned to a driver. False otherwise'''
209    return "Driver_str" in devices[dev_id]
210
211
212def get_pci_device_details(dev_id, probe_lspci):
213    '''This function gets additional details for a PCI device'''
214    device = {}
215
216    if probe_lspci:
217        extra_info = subprocess.check_output(["lspci", "-vmmks", dev_id]).splitlines()
218        # parse lspci details
219        for line in extra_info:
220            if len(line) == 0:
221                continue
222            name, value = line.decode("utf8").split("\t", 1)
223            name = name.strip(":") + "_str"
224            device[name] = value
225    # check for a unix interface name
226    device["Interface"] = ""
227    for base, dirs, _ in os.walk("/sys/bus/pci/devices/%s/" % dev_id):
228        if "net" in dirs:
229            device["Interface"] = \
230                ",".join(os.listdir(os.path.join(base, "net")))
231            break
232    # check if a port is used for ssh connection
233    device["Ssh_if"] = False
234    device["Active"] = ""
235
236    return device
237
238def clear_data():
239    '''This function clears any old data'''
240    global devices
241    devices = {}
242
243def get_device_details(devices_type):
244    '''This function populates the "devices" dictionary. The keys used are
245    the pci addresses (domain:bus:slot.func). The values are themselves
246    dictionaries - one for each NIC.'''
247    global devices
248    global dpdk_drivers
249
250    # first loop through and read details for all devices
251    # request machine readable format, with numeric IDs and String
252    dev = {}
253    dev_lines = subprocess.check_output(["lspci", "-Dvmmnnk"]).splitlines()
254    for dev_line in dev_lines:
255        if len(dev_line) == 0:
256            if device_type_match(dev, devices_type):
257                # Replace "Driver" with "Driver_str" to have consistency of
258                # of dictionary key names
259                if "Driver" in dev.keys():
260                    dev["Driver_str"] = dev.pop("Driver")
261                if "Module" in dev.keys():
262                    dev["Module_str"] = dev.pop("Module")
263                # use dict to make copy of dev
264                devices[dev["Slot"]] = dict(dev)
265            # Clear previous device's data
266            dev = {}
267        else:
268            name, value = dev_line.decode("utf8").split("\t", 1)
269            value_list = value.rsplit(' ', 1)
270            if len(value_list) > 1:
271                # String stored in <name>_str
272                dev[name.rstrip(":") + '_str'] = value_list[0]
273            # Numeric IDs
274            dev[name.rstrip(":")] = value_list[len(value_list) - 1] \
275                .rstrip("]").lstrip("[")
276
277    if devices_type == network_devices:
278        # check what is the interface if any for an ssh connection if
279        # any to this host, so we can mark it later.
280        ssh_if = []
281        route = subprocess.check_output(["ip", "-o", "route"])
282        # filter out all lines for 169.254 routes
283        route = "\n".join(filter(lambda ln: not ln.startswith("169.254"),
284                             route.decode().splitlines()))
285        rt_info = route.split()
286        for i in range(len(rt_info) - 1):
287            if rt_info[i] == "dev":
288                ssh_if.append(rt_info[i+1])
289
290    # based on the basic info, get extended text details
291    for d in devices.keys():
292        if not device_type_match(devices[d], devices_type):
293            continue
294
295        # get additional info and add it to existing data
296        devices[d] = devices[d].copy()
297        # No need to probe lspci
298        devices[d].update(get_pci_device_details(d, False).items())
299
300        if devices_type == network_devices:
301            for _if in ssh_if:
302                if _if in devices[d]["Interface"].split(","):
303                    devices[d]["Ssh_if"] = True
304                    devices[d]["Active"] = "*Active*"
305                    break
306
307        # add igb_uio to list of supporting modules if needed
308        if "Module_str" in devices[d]:
309            for driver in dpdk_drivers:
310                if driver not in devices[d]["Module_str"]:
311                    devices[d]["Module_str"] = \
312                        devices[d]["Module_str"] + ",%s" % driver
313        else:
314            devices[d]["Module_str"] = ",".join(dpdk_drivers)
315
316        # make sure the driver and module strings do not have any duplicates
317        if has_driver(d):
318            modules = devices[d]["Module_str"].split(",")
319            if devices[d]["Driver_str"] in modules:
320                modules.remove(devices[d]["Driver_str"])
321                devices[d]["Module_str"] = ",".join(modules)
322
323
324def device_type_match(dev, devices_type):
325    for i in range(len(devices_type)):
326        param_count = len(
327            [x for x in devices_type[i].values() if x is not None])
328        match_count = 0
329        if dev["Class"][0:2] == devices_type[i]["Class"]:
330            match_count = match_count + 1
331            for key in devices_type[i].keys():
332                if key != 'Class' and devices_type[i][key]:
333                    value_list = devices_type[i][key].split(',')
334                    for value in value_list:
335                        if value.strip(' ') == dev[key]:
336                            match_count = match_count + 1
337            # count must be the number of non None parameters to match
338            if match_count == param_count:
339                return True
340    return False
341
342def dev_id_from_dev_name(dev_name):
343    '''Take a device "name" - a string passed in by user to identify a NIC
344    device, and determine the device id - i.e. the domain:bus:slot.func - for
345    it, which can then be used to index into the devices array'''
346
347    # check if it's already a suitable index
348    if dev_name in devices:
349        return dev_name
350    # check if it's an index just missing the domain part
351    if "0000:" + dev_name in devices:
352        return "0000:" + dev_name
353
354    # check if it's an interface name, e.g. eth1
355    for d in devices.keys():
356        if dev_name in devices[d]["Interface"].split(","):
357            return devices[d]["Slot"]
358    # if nothing else matches - error
359    raise ValueError("Unknown device: %s. "
360                     "Please specify device in \"bus:slot.func\" format" % dev_name)
361
362
363def unbind_one(dev_id, force):
364    '''Unbind the device identified by "dev_id" from its current driver'''
365    dev = devices[dev_id]
366    if not has_driver(dev_id):
367        print("Notice: %s %s %s is not currently managed by any driver" %
368              (dev["Slot"], dev["Device_str"], dev["Interface"]), file=sys.stderr)
369        return
370
371    # prevent us disconnecting ourselves
372    if dev["Ssh_if"] and not force:
373        print("Warning: routing table indicates that interface %s is active. "
374              "Skipping unbind" % dev_id, file=sys.stderr)
375        return
376
377    # write to /sys to unbind
378    filename = "/sys/bus/pci/drivers/%s/unbind" % dev["Driver_str"]
379    try:
380        f = open(filename, "a")
381    except:
382        sys.exit("Error: unbind failed for %s - Cannot open %s" %
383                 (dev_id, filename))
384    f.write(dev_id)
385    f.close()
386
387
388def bind_one(dev_id, driver, force):
389    '''Bind the device given by "dev_id" to the driver "driver". If the device
390    is already bound to a different driver, it will be unbound first'''
391    dev = devices[dev_id]
392    saved_driver = None  # used to rollback any unbind in case of failure
393
394    # prevent disconnection of our ssh session
395    if dev["Ssh_if"] and not force:
396        print("Warning: routing table indicates that interface %s is active. "
397              "Not modifying" % dev_id, file=sys.stderr)
398        return
399
400    # unbind any existing drivers we don't want
401    if has_driver(dev_id):
402        if dev["Driver_str"] == driver:
403            print("Notice: %s already bound to driver %s, skipping" %
404                  (dev_id, driver), file=sys.stderr)
405            return
406        saved_driver = dev["Driver_str"]
407        unbind_one(dev_id, force)
408        dev["Driver_str"] = ""  # clear driver string
409
410    # For kernels >= 3.15 driver_override can be used to specify the driver
411    # for a device rather than relying on the driver to provide a positive
412    # match of the device.  The existing process of looking up
413    # the vendor and device ID, adding them to the driver new_id,
414    # will erroneously bind other devices too which has the additional burden
415    # of unbinding those devices
416    if driver in dpdk_drivers:
417        filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
418        if os.path.exists(filename):
419            try:
420                f = open(filename, "w")
421            except:
422                print("Error: bind failed for %s - Cannot open %s"
423                      % (dev_id, filename), file=sys.stderr)
424                return
425            try:
426                f.write("%s" % driver)
427                f.close()
428            except:
429                print("Error: bind failed for %s - Cannot write driver %s to "
430                      "PCI ID " % (dev_id, driver), file=sys.stderr)
431                return
432        # For kernels < 3.15 use new_id to add PCI id's to the driver
433        else:
434            filename = "/sys/bus/pci/drivers/%s/new_id" % driver
435            try:
436                f = open(filename, "w")
437            except:
438                print("Error: bind failed for %s - Cannot open %s"
439                      % (dev_id, filename), file=sys.stderr)
440                return
441            try:
442                # Convert Device and Vendor Id to int to write to new_id
443                f.write("%04x %04x" % (int(dev["Vendor"],16),
444                        int(dev["Device"], 16)))
445                f.close()
446            except:
447                print("Error: bind failed for %s - Cannot write new PCI ID to "
448                      "driver %s" % (dev_id, driver), file=sys.stderr)
449                return
450
451    # do the bind by writing to /sys
452    filename = "/sys/bus/pci/drivers/%s/bind" % driver
453    try:
454        f = open(filename, "a")
455    except:
456        print("Error: bind failed for %s - Cannot open %s"
457              % (dev_id, filename), file=sys.stderr)
458        if saved_driver is not None:  # restore any previous driver
459            bind_one(dev_id, saved_driver, force)
460        return
461    try:
462        f.write(dev_id)
463        f.close()
464    except:
465        # for some reason, closing dev_id after adding a new PCI ID to new_id
466        # results in IOError. however, if the device was successfully bound,
467        # we don't care for any errors and can safely ignore IOError
468        tmp = get_pci_device_details(dev_id, True)
469        if "Driver_str" in tmp and tmp["Driver_str"] == driver:
470            return
471        print("Error: bind failed for %s - Cannot bind to driver %s"
472              % (dev_id, driver), file=sys.stderr)
473        if saved_driver is not None:  # restore any previous driver
474            bind_one(dev_id, saved_driver, force)
475        return
476
477    # For kernels > 3.15 driver_override is used to bind a device to a driver.
478    # Before unbinding it, overwrite driver_override with empty string so that
479    # the device can be bound to any other driver
480    filename = "/sys/bus/pci/devices/%s/driver_override" % dev_id
481    if os.path.exists(filename):
482        try:
483            f = open(filename, "w")
484        except:
485            sys.exit("Error: unbind failed for %s - Cannot open %s"
486                  % (dev_id, filename))
487        try:
488            f.write("\00")
489            f.close()
490        except:
491            sys.exit("Error: unbind failed for %s - Cannot open %s"
492                  % (dev_id, filename))
493
494
495def unbind_all(dev_list, force=False):
496    """Unbind method, takes a list of device locations"""
497
498    if dev_list[0] == "dpdk":
499        for d in devices.keys():
500            if "Driver_str" in devices[d]:
501                if devices[d]["Driver_str"] in dpdk_drivers:
502                    unbind_one(devices[d]["Slot"], force)
503        return
504
505    try:
506        dev_list = map(dev_id_from_dev_name, dev_list)
507    except ValueError as ex:
508        print(ex)
509        sys.exit(1)
510
511    for d in dev_list:
512        unbind_one(d, force)
513
514
515def bind_all(dev_list, driver, force=False):
516    """Bind method, takes a list of device locations"""
517    global devices
518
519    # a common user error is to forget to specify the driver the devices need to
520    # be bound to. check if the driver is a valid device, and if it is, show
521    # a meaningful error.
522    try:
523        dev_id_from_dev_name(driver)
524        # if we've made it this far, this means that the "driver" was a valid
525        # device string, so it's probably not a valid driver name.
526        sys.exit("Error: Driver '%s' does not look like a valid driver. " \
527                 "Did you forget to specify the driver to bind devices to?" % driver)
528    except ValueError:
529        # driver generated error - it's not a valid device ID, so all is well
530        pass
531
532    # check if we're attempting to bind to a driver that isn't loaded
533    if not module_is_loaded(driver.replace('-','_')):
534        sys.exit("Error: Driver '%s' is not loaded." % driver)
535
536    try:
537        dev_list = map(dev_id_from_dev_name, dev_list)
538    except ValueError as ex:
539        sys.exit(ex)
540
541    for d in dev_list:
542        bind_one(d, driver, force)
543
544    # For kernels < 3.15 when binding devices to a generic driver
545    # (i.e. one that doesn't have a PCI ID table) using new_id, some devices
546    # that are not bound to any other driver could be bound even if no one has
547    # asked them to. hence, we check the list of drivers again, and see if
548    # some of the previously-unbound devices were erroneously bound.
549    if not os.path.exists("/sys/bus/pci/devices/%s/driver_override" % d):
550        for d in devices.keys():
551            # skip devices that were already bound or that we know should be bound
552            if "Driver_str" in devices[d] or d in dev_list:
553                continue
554
555            # update information about this device
556            devices[d] = dict(devices[d].items() +
557                              get_pci_device_details(d, True).items())
558
559            # check if updated information indicates that the device was bound
560            if "Driver_str" in devices[d]:
561                unbind_one(d, force)
562
563
564def display_devices(title, dev_list, extra_params=None):
565    '''Displays to the user the details of a list of devices given in
566    "dev_list". The "extra_params" parameter, if given, should contain a string
567     with %()s fields in it for replacement by the named fields in each
568     device's dictionary.'''
569    strings = []  # this holds the strings to print. We sort before printing
570    print("\n%s" % title)
571    print("="*len(title))
572    if len(dev_list) == 0:
573        strings.append("<none>")
574    else:
575        for dev in dev_list:
576            if extra_params is not None:
577                strings.append("%s '%s %s' %s" % (dev["Slot"],
578                                               dev["Device_str"],
579                                               dev["Device"],
580                                               extra_params % dev))
581            else:
582                strings.append("%s '%s'" % (dev["Slot"], dev["Device_str"]))
583    # sort before printing, so that the entries appear in PCI order
584    strings.sort()
585    print("\n".join(strings))  # print one per line
586
587def show_device_status(devices_type, device_name, if_field=False):
588    global dpdk_drivers
589    kernel_drv = []
590    dpdk_drv = []
591    no_drv = []
592
593    # split our list of network devices into the three categories above
594    for d in devices.keys():
595        if device_type_match(devices[d], devices_type):
596            if not has_driver(d):
597                no_drv.append(devices[d])
598                continue
599            if devices[d]["Driver_str"] in dpdk_drivers:
600                dpdk_drv.append(devices[d])
601            else:
602                kernel_drv.append(devices[d])
603
604    n_devs = len(dpdk_drv) + len(kernel_drv) + len(no_drv)
605
606    # don't bother displaying anything if there are no devices
607    if n_devs == 0:
608        msg = "No '%s' devices detected" % device_name
609        print("")
610        print(msg)
611        print("".join('=' * len(msg)))
612        return
613
614    # print each category separately, so we can clearly see what's used by DPDK
615    if len(dpdk_drv) != 0:
616        display_devices("%s devices using DPDK-compatible driver" % device_name,
617                        dpdk_drv, "drv=%(Driver_str)s unused=%(Module_str)s")
618    if len(kernel_drv) != 0:
619        if_text = ""
620        if if_field:
621            if_text = "if=%(Interface)s "
622        display_devices("%s devices using kernel driver" % device_name, kernel_drv,
623                        if_text + "drv=%(Driver_str)s "
624                        "unused=%(Module_str)s %(Active)s")
625    if len(no_drv) != 0:
626        display_devices("Other %s devices" % device_name, no_drv,
627                        "unused=%(Module_str)s")
628
629def show_status():
630    '''Function called when the script is passed the "--status" option.
631    Displays to the user what devices are bound to the igb_uio driver, the
632    kernel driver or to no driver'''
633
634    if status_dev == "net" or status_dev == "all":
635        show_device_status(network_devices, "Network", if_field=True)
636
637    if status_dev == "baseband" or status_dev == "all":
638        show_device_status(baseband_devices, "Baseband")
639
640    if status_dev == "crypto" or status_dev == "all":
641        show_device_status(crypto_devices, "Crypto")
642
643    if status_dev == "event" or status_dev == "all":
644        show_device_status(eventdev_devices, "Eventdev")
645
646    if status_dev == "mempool" or status_dev == "all":
647        show_device_status(mempool_devices, "Mempool")
648
649    if status_dev == "compress" or status_dev == "all":
650        show_device_status(compress_devices , "Compress")
651
652    if status_dev == "misc" or status_dev == "all":
653        show_device_status(misc_devices, "Misc (rawdev)")
654
655    if status_dev == "regex" or status_dev == "all":
656        show_device_status(regex_devices, "Regex")
657
658
659def pci_glob(arg):
660    '''Returns a list containing either:
661    * List of PCI B:D:F matching arg, using shell wildcards e.g. 80:04.*
662    * Only the passed arg if matching list is empty'''
663    sysfs_path = "/sys/bus/pci/devices"
664    for _glob in [arg, '0000:' + arg]:
665        paths = [basename(path) for path in glob(path_join(sysfs_path, _glob))]
666        if paths:
667            return paths
668    return [arg]
669
670
671def parse_args():
672    '''Parses the command-line arguments given by the user and takes the
673    appropriate action for each'''
674    global b_flag
675    global status_flag
676    global status_dev
677    global force_flag
678    global args
679    if len(sys.argv) <= 1:
680        usage()
681        sys.exit(0)
682
683    try:
684        opts, args = getopt.getopt(sys.argv[1:], "b:us",
685                                   ["help", "usage", "status", "status-dev=",
686                                    "force", "bind=", "unbind", ])
687    except getopt.GetoptError as error:
688        print(str(error))
689        print("Run '%s --usage' for further information" % sys.argv[0])
690        sys.exit(1)
691
692    for opt, arg in opts:
693        if opt == "--help" or opt == "--usage":
694            usage()
695            sys.exit(0)
696        if opt == "--status-dev":
697            status_flag = True
698            status_dev = arg
699        if opt == "--status" or opt == "-s":
700            status_flag = True
701            status_dev = "all"
702        if opt == "--force":
703            force_flag = True
704        if opt == "-b" or opt == "-u" or opt == "--bind" or opt == "--unbind":
705            if b_flag is not None:
706                sys.exit("Error: binding and unbinding are mutually exclusive")
707            if opt == "-u" or opt == "--unbind":
708                b_flag = "none"
709            else:
710                b_flag = arg
711
712    # resolve any PCI globs in the args
713    new_args = []
714    for arg in args:
715        new_args.extend(pci_glob(arg))
716    args = new_args
717
718def do_arg_actions():
719    '''do the actual action requested by the user'''
720    global b_flag
721    global status_flag
722    global force_flag
723    global args
724
725    if b_flag is None and not status_flag:
726        print("Error: No action specified for devices. "
727              "Please give a -b or -u option", file=sys.stderr)
728        usage()
729        sys.exit(1)
730
731    if b_flag is not None and len(args) == 0:
732        print("Error: No devices specified.", file=sys.stderr)
733        usage()
734        sys.exit(1)
735
736    if b_flag == "none" or b_flag == "None":
737        unbind_all(args, force_flag)
738    elif b_flag is not None:
739        bind_all(args, b_flag, force_flag)
740    if status_flag:
741        if b_flag is not None:
742            clear_data()
743            # refresh if we have changed anything
744            get_device_details(network_devices)
745            get_device_details(baseband_devices)
746            get_device_details(crypto_devices)
747            get_device_details(eventdev_devices)
748            get_device_details(mempool_devices)
749            get_device_details(compress_devices)
750            get_device_details(regex_devices)
751            get_device_details(misc_devices)
752        show_status()
753
754
755def main():
756    '''program main function'''
757    # check if lspci is installed, suppress any output
758    with open(os.devnull, 'w') as devnull:
759        ret = subprocess.call(['which', 'lspci'],
760                              stdout=devnull, stderr=devnull)
761        if ret != 0:
762            sys.exit("'lspci' not found - please install 'pciutils'")
763    parse_args()
764    check_modules()
765    clear_data()
766    get_device_details(network_devices)
767    get_device_details(baseband_devices)
768    get_device_details(crypto_devices)
769    get_device_details(eventdev_devices)
770    get_device_details(mempool_devices)
771    get_device_details(compress_devices)
772    get_device_details(regex_devices)
773    get_device_details(misc_devices)
774    do_arg_actions()
775
776if __name__ == "__main__":
777    main()
778