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