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