2
# This file is part of Checkbox.
4
# Copyright 2011 Canonical Ltd.
6
# Checkbox is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
11
# Checkbox is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
22
from checkbox.lib.bit import (
26
from checkbox.lib.input import Input
27
from checkbox.lib.pci import Pci
28
from checkbox.lib.usb import Usb
33
r"v(?P<vendor_id>[%(hex)s]{8})"
34
r"d(?P<product_id>[%(hex)s]{8})"
35
r"sv(?P<subvendor_id>[%(hex)s]{8})"
36
r"sd(?P<subproduct_id>[%(hex)s]{8})"
37
r"bc(?P<class>[%(hex)s]{2})"
38
r"sc(?P<subclass>[%(hex)s]{2})"
39
r"i(?P<interface>[%(hex)s]{2})"
40
% {"hex": string.hexdigits})
43
r"(?P<vendor_name>[%(upper)s]{3})"
44
r"(?P<product_id>[%(hex)s]{4}):"
45
% {"upper": string.uppercase, "hex": string.hexdigits})
48
r"v(?P<vendor_id>[%(hex)s]{4})"
49
r"p(?P<product_id>[%(hex)s]{4})"
50
r"d(?P<revision>[%(hex)s]{4})"
51
r"dc(?P<class>[%(hex)s]{2})"
52
r"dsc(?P<subclass>[%(hex)s]{2})"
53
r"dp(?P<protocol>[%(hex)s]{2})"
54
r"ic(?P<interface_class>[%(hex)s]{2})"
55
r"isc(?P<interface_subclass>[%(hex)s]{2})"
56
r"ip(?P<interface_protocol>[%(hex)s]{2})"
57
% {"hex": string.hexdigits})
60
r"t-0x(?P<type>[%(hex)s]{2})"
61
% {"hex": string.hexdigits})
65
__slots__ = ("_environment", "_bits", "_stack",)
67
def __init__(self, environment, bits=None, stack=[]):
68
self._environment = environment
74
# Change the bus from 'acpi' to 'pnp' for some devices
75
if PNP_RE.match(self._environment.get("MODALIAS", "")) \
76
and self.path.endswith(":00"):
79
# Change the bus from 'block' to parent
80
if self._environment.get("DEVTYPE") == "disk" and self._stack:
81
return self._stack[-1]._environment.get("SUBSYSTEM")
83
bus = self._environment.get("SUBSYSTEM")
91
if "IFINDEX" in self._environment:
94
if "PCI_CLASS" in self._environment:
95
pci_class_string = self._environment["PCI_CLASS"]
96
pci_class = int(pci_class_string, 16)
98
# Strip prog_if if defined
99
if pci_class > 0xFFFF:
102
subclass_id = pci_class & 0xFF
103
class_id = (pci_class >> 8) & 0xFF
105
if class_id == Pci.BASE_CLASS_NETWORK:
106
if subclass_id == Pci.CLASS_NETWORK_WIRELESS:
111
if class_id == Pci.BASE_CLASS_DISPLAY:
112
if subclass_id == Pci.CLASS_DISPLAY_VGA:
117
if class_id == Pci.BASE_CLASS_SERIAL \
118
and subclass_id == Pci.CLASS_SERIAL_USB:
121
if class_id == Pci.BASE_CLASS_STORAGE:
122
if subclass_id == Pci.CLASS_STORAGE_SCSI:
125
if subclass_id == Pci.CLASS_STORAGE_IDE:
128
if subclass_id == Pci.CLASS_STORAGE_FLOPPY:
131
if subclass_id == Pci.CLASS_STORAGE_RAID:
134
if class_id == Pci.BASE_CLASS_COMMUNICATION \
135
and subclass_id == Pci.CLASS_COMMUNICATION_MODEM:
138
if class_id == Pci.BASE_CLASS_INPUT \
139
and subclass_id == Pci.CLASS_INPUT_SCANNER:
142
if class_id == Pci.BASE_CLASS_MULTIMEDIA:
143
if subclass_id == Pci.CLASS_MULTIMEDIA_VIDEO:
146
if subclass_id == Pci.CLASS_MULTIMEDIA_AUDIO \
147
or subclass_id == Pci.CLASS_MULTIMEDIA_AUDIO_DEVICE:
150
if class_id == Pci.BASE_CLASS_SERIAL \
151
and subclass_id == Pci.CLASS_SERIAL_FIREWIRE:
154
if class_id == Pci.BASE_CLASS_BRIDGE \
155
and (subclass_id == Pci.CLASS_BRIDGE_PCMCIA \
156
or subclass_id == Pci.CLASS_BRIDGE_CARDBUS):
159
if "TYPE" in self._environment and "INTERFACE" in self._environment:
160
interface_class, interface_subclass, interface_protocol = (
161
int(i) for i in self._environment["INTERFACE"].split("/"))
163
if interface_class == Usb.BASE_CLASS_AUDIO:
166
if interface_class == Usb.BASE_CLASS_PRINTER:
169
if interface_class == Usb.BASE_CLASS_STORAGE:
170
if interface_subclass == Usb.CLASS_STORAGE_FLOPPY:
173
if interface_subclass == Usb.CLASS_STORAGE_SCSI:
176
if interface_class == Usb.BASE_CLASS_VIDEO:
179
if interface_class == Usb.BASE_CLASS_WIRELESS:
180
if interface_protocol == Usb.PROTOCOL_BLUETOOTH:
185
if "KEY" in self._environment:
186
key = self._environment["KEY"].strip("=")
187
bitmask = get_bitmask(key)
189
for i in range(Input.KEY_Q, Input.KEY_P + 1):
190
if not test_bit(i, bitmask, self._bits):
195
if test_bit(Input.KEY_CAMERA, bitmask, self._bits):
198
if test_bit(Input.BTN_TOUCH, bitmask, self._bits):
201
if test_bit(Input.BTN_MOUSE, bitmask, self._bits):
204
if "ID_TYPE" in self._environment:
205
id_type = self._environment["ID_TYPE"]
210
if id_type == "disk":
213
if id_type == "video":
216
if "DEVTYPE" in self._environment:
217
devtype = self._environment["DEVTYPE"]
218
if devtype == "disk":
219
if "ID_CDROM" in self._environment:
222
if "ID_DRIVE_FLOPPY" in self._environment:
225
if devtype == "scsi_device":
226
match = SCSI_RE.match(self._environment.get("MODALIAS", ""))
227
type = int(match.group("type"), 16) if match else -1
229
# Check FLASH drives, see /lib/udev/rules.d/80-udisks.rules
230
if type in (0, 7, 14) \
231
and not any(d.driver == "rts_pstor" for d in self._stack):
249
if "DRIVER" in self._environment:
250
if self._environment["DRIVER"] == "floppy":
260
if "DRIVER" in self._environment:
261
return self._environment["DRIVER"]
263
# Check parent device for driver
265
parent = self._stack[-1]
266
if "DRIVER" in parent._environment:
267
return parent._environment["DRIVER"]
273
return self._environment.get("DEVPATH")
276
def product_id(self):
278
match = PCI_RE.match(self._environment.get("MODALIAS", ""))
280
return int(match.group("product_id"), 16)
283
match = USB_RE.match(self._environment.get("MODALIAS", ""))
285
return int(match.group("product_id"), 16)
288
match = PNP_RE.match(self._environment.get("MODALIAS", ""))
290
product_id = int(match.group("product_id"), 16)
291
# Ignore interrupt controllers
292
if product_id > 0x0100:
300
match = PCI_RE.match(self._environment.get("MODALIAS", ""))
302
return int(match.group("vendor_id"), 16)
305
match = USB_RE.match(self._environment.get("MODALIAS", ""))
307
return int(match.group("vendor_id"), 16)
312
def subproduct_id(self):
313
if "PCI_SUBSYS_ID" in self._environment:
314
pci_subsys_id = self._environment["PCI_SUBSYS_ID"]
315
subvendor_id, subproduct_id = pci_subsys_id.split(":")
316
return int(subproduct_id, 16)
321
def subvendor_id(self):
322
if "PCI_SUBSYS_ID" in self._environment:
323
pci_subsys_id = self._environment["PCI_SUBSYS_ID"]
324
subvendor_id, subproduct_id = pci_subsys_id.split(":")
325
return int(subvendor_id, 16)
331
for element in ("NAME",
333
"POWER_SUPPLY_MODEL_NAME"):
334
if element in self._environment:
335
return self._environment[element].strip('"')
338
if self._environment.get("DEVTYPE") == "scsi_device":
339
for device in reversed(self._stack):
340
if device._environment.get("ID_BUS") == "usb":
341
return decode_id(device._environment["ID_MODEL_ENC"])
343
if self._environment.get("DEVTYPE") == "disk" \
344
and self._environment.get("ID_BUS") == "ata":
345
return decode_id(self._environment["ID_MODEL_ENC"])
348
if self.driver == "floppy":
349
return u"Platform Device"
355
if "RFKILL_NAME" in self._environment:
358
if "POWER_SUPPLY_MANUFACTURER" in self._environment:
359
return self._environment["POWER_SUPPLY_MANUFACTURER"]
362
match = PNP_RE.match(self._environment.get("MODALIAS", ""))
364
return match.group("vendor_name")
367
if self._environment.get("DEVTYPE") == "scsi_device":
368
for device in reversed(self._stack):
369
if device._environment.get("ID_BUS") == "usb":
370
return decode_id(device._environment["ID_VENDOR_ENC"])
376
"""Parser for the udevadm command."""
378
device_factory = UdevadmDevice
380
def __init__(self, stream, bits=None):
384
def _ignoreDevice(self, device):
385
# Ignore devices without bus information
389
# Ignore devices without product information
390
if not device.product and device.product_id is None:
393
# Ignore invalid subsystem information
394
if ((device.subproduct_id is None
395
and device.subvendor_id is not None)
396
or (device.subproduct_id is not None
397
and device.subvendor_id is None)):
400
# Ignore ACPI devices
401
if device.bus == "acpi":
406
def getAttributes(self, path):
409
def run(self, result):
410
# Some attribute lines have a space character after the
411
# ':', others don't have it (see udevadm-info.c).
412
line_pattern = re.compile(r"(?P<key>[A-Z]):\s*(?P<value>.*)")
413
multi_pattern = re.compile(r"(?P<key>[^=]+)=(?P<value>.*)")
416
output = self.stream.read()
417
for record in re.split("\n{2,}", output):
418
record = record.strip()
422
# Determine path and environment
426
for line in record.split("\n"):
427
line_match = line_pattern.match(line)
430
# Append to last environment element
431
environment[element] += line
434
key = line_match.group("key")
435
value = line_match.group("value")
440
key_match = multi_pattern.match(value)
443
"Device property not supported: %s" % value)
444
element = key_match.group("key")
445
environment[element] = key_match.group("value")
449
if stack[-1].path + "/" in path:
453
# Set default DEVPATH
454
environment.setdefault("DEVPATH", path)
456
device = self.device_factory(environment, self.bits, list(stack))
457
if not self._ignoreDevice(device):
458
result.addDevice(device)
464
encoded_id = id.encode("utf-8")
465
decoded_id = encoded_id.decode("string-escape").decode("utf-8")
466
return decoded_id.strip()