mirror of
https://github.com/shenmintao/aic8800d80.git
synced 2026-11-10 16:49:17 +00:00
fix(udev): restrict mode switching to intended devices and events
Take the aic.rules portion of #129: restrict 1111:1111 switching to 88M80 USB devices, target the event's bus/device address, and eject only whole disks on add. Add selection fixtures and document the product restriction. Keep installer paths and existing firmware handling unchanged. Co-authored-by: rtk94 <richardtknepp@gmail.com>
This commit is contained in:
1 parent
bee40068b9
commit
e2c5835767
3 files changed
+89
-11
No files matched your search
@@ -77,6 +77,11 @@ commands. Existing users must rerun `sudo ./install.sh` to replace the installed
|
||||
usb_modeswitch configuration, then unplug the adapter completely and plug it
|
||||
back in.
|
||||
|
||||
Automatic switching requires the USB product string to start with `88M80`.
|
||||
Unrelated flash disks also use `1111:1111`, so that USB ID alone is not enough.
|
||||
If your adapter reports a different product string, verify its identity before
|
||||
trying the manual command below.
|
||||
|
||||
If automatic switching does not occur, try the same sequence manually:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
# AIC8800 USB mass-storage mode switch rules
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5721", SYMLINK+="aicudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5722", SYMLINK+="aicudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5723", SYMLINK+="tendaudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5724", SYMLINK+="ugreenax900", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5725", SYMLINK+="tendaudiskv2", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5726", SYMLINK+="tendaudiskv3", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5727", SYMLINK+="tendaudiskv4", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572a", SYMLINK+="tendaudiskv5", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572c", SYMLINK+="cudydiskv2", RUN+="/usr/bin/eject /dev/%k"
|
||||
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572f", SYMLINK+="aicudisk572f", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5721", SYMLINK+="aicudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5722", SYMLINK+="aicudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5723", SYMLINK+="tendaudisk", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5724", SYMLINK+="ugreenax900", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5725", SYMLINK+="tendaudiskv2", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5726", SYMLINK+="tendaudiskv3", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5727", SYMLINK+="tendaudiskv4", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572a", SYMLINK+="tendaudiskv5", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572c", SYMLINK+="cudydiskv2", RUN+="/usr/bin/eject /dev/%k"
|
||||
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572f", SYMLINK+="aicudisk572f", RUN+="/usr/bin/eject /dev/%k"
|
||||
|
||||
# AIC8800D80 "Pandora" clone: switch 1111:1111 to its operational USB ID.
|
||||
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="1111", ATTR{idProduct}=="1111", RUN+="/bin/sh -c 'if [ -x /usr/sbin/usb_modeswitch ]; then exec /usr/sbin/usb_modeswitch -c /etc/usb_modeswitch.d/1111:1111; elif [ -x /usr/bin/usb_modeswitch ]; then exec /usr/bin/usb_modeswitch -c /etc/usb_modeswitch.d/1111:1111; fi'"
|
||||
# 1111:1111 is a placeholder ID that other devices also report (some AIC
|
||||
# dongles expose a separate 1111:1111 "FLASH" driver disk next to an
|
||||
# already working Wi-Fi function), so only devices whose product string is
|
||||
# 88M80 are switched, and usb_modeswitch is pointed at this exact device.
|
||||
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTR{idVendor}=="1111", ATTR{idProduct}=="1111", ATTR{product}=="88M80*", RUN+="/bin/sh -c 'if [ -x /usr/sbin/usb_modeswitch ]; then exec /usr/sbin/usb_modeswitch -b $attr{busnum} -g $attr{devnum} -c /etc/usb_modeswitch.d/1111:1111; elif [ -x /usr/bin/usb_modeswitch ]; then exec /usr/bin/usb_modeswitch -b $attr{busnum} -g $attr{devnum} -c /etc/usb_modeswitch.d/1111:1111; fi'"
|
||||
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Check device/event selection using fixtures; does not switch real devices.
|
||||
|
||||
This models the basic equality/glob predicates used by aic.rules. Run
|
||||
udevadm verify separately to validate syntax with the real udev parser.
|
||||
"""
|
||||
|
||||
from fnmatch import fnmatchcase
|
||||
from pathlib import Path
|
||||
import re
|
||||
import unittest
|
||||
|
||||
|
||||
RULES = (Path(__file__).resolve().parents[2] / "aic.rules").read_text().splitlines()
|
||||
|
||||
|
||||
def matching_rules(device):
|
||||
result = []
|
||||
for rule in RULES:
|
||||
if not rule.strip() or rule.lstrip().startswith("#"):
|
||||
continue
|
||||
predicates = re.findall(r'([A-Z]+(?:\{[^}]+\})?)=="([^"]*)"', rule)
|
||||
if predicates and all(fnmatchcase(device.get(key, ""), value) for key, value in predicates):
|
||||
result.append(rule)
|
||||
return result
|
||||
|
||||
|
||||
class UdevTests(unittest.TestCase):
|
||||
def pandora(self, **changes):
|
||||
device = {"ACTION": "add", "SUBSYSTEM": "usb", "ENV{DEVTYPE}": "usb_device",
|
||||
"ATTR{idVendor}": "1111", "ATTR{idProduct}": "1111", "ATTR{product}": "88M80"}
|
||||
device.update(changes)
|
||||
return device
|
||||
|
||||
def test_88m80_uses_event_bus_and_device_numbers(self):
|
||||
for product in ("88M80", "88M80 USB WiFi"):
|
||||
with self.subTest(product=product):
|
||||
matches = matching_rules(self.pandora(**{"ATTR{product}": product}))
|
||||
self.assertEqual(len(matches), 1)
|
||||
# Both possible usb_modeswitch executable locations target this event's device.
|
||||
self.assertEqual(matches[0].count("-b $attr{busnum} -g $attr{devnum}"), 2)
|
||||
|
||||
def test_generic_1111_flash_disk_is_not_switched(self):
|
||||
for product in ("FLASH UDISK 2.30", "USB DISK", ""):
|
||||
with self.subTest(product=product):
|
||||
self.assertEqual(matching_rules(self.pandora(**{"ATTR{product}": product})), [])
|
||||
|
||||
def test_wrong_ids_interfaces_and_non_add_events_are_not_switched(self):
|
||||
for changes in ({"ACTION": "change"}, {"ACTION": "remove"},
|
||||
{"ENV{DEVTYPE}": "usb_interface"}, {"ATTR{idVendor}": "2222"},
|
||||
{"ATTR{idProduct}": "8d80"}):
|
||||
with self.subTest(changes=changes):
|
||||
self.assertEqual(matching_rules(self.pandora(**changes)), [])
|
||||
|
||||
def test_eject_only_on_add_of_a_whole_supported_disk(self):
|
||||
for product in ("5721", "5722", "5723", "5724", "5725", "5726", "5727", "572a", "572c", "572f"):
|
||||
disk = {"ACTION": "add", "SUBSYSTEM": "block", "ENV{DEVTYPE}": "disk", "KERNEL": "sdb",
|
||||
"ATTRS{idVendor}": "a69c", "ATTRS{idProduct}": product}
|
||||
with self.subTest(product=product):
|
||||
matches = matching_rules(disk)
|
||||
self.assertEqual(len(matches), 1)
|
||||
self.assertIn('/usr/bin/eject /dev/%k', matches[0])
|
||||
for changes in ({"ACTION": "change"}, {"ACTION": "remove"},
|
||||
{"ENV{DEVTYPE}": "partition", "KERNEL": "sdb1"}):
|
||||
self.assertEqual(matching_rules(dict(disk, **changes)), [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(verbosity=2)
|
||||
Reference in new issue
Block a user