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>
(cherry picked from commit e2c5835767)
This commit is contained in:
1 parent
20fb7181e6
commit
3a71c7188b
3 files changed
+89
-11
No files matched your search
@@ -85,6 +85,11 @@ commands. Existing users must rerun `sudo ./install.sh` to replace the installed
|
|||||||
usb_modeswitch configuration, then unplug the adapter completely and plug it
|
usb_modeswitch configuration, then unplug the adapter completely and plug it
|
||||||
back in.
|
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:
|
If automatic switching does not occur, try the same sequence manually:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
# AIC8800 USB mass-storage mode switch rules
|
# AIC8800 USB mass-storage mode switch rules
|
||||||
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}=="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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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"
|
ACTION=="add", SUBSYSTEM=="block", ENV{DEVTYPE}=="disk", 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}=="572f", SYMLINK+="aicudisk572f", RUN+="/usr/bin/eject /dev/%k"
|
||||||
|
|
||||||
# AIC8800D80 "Pandora" clone: switch 1111:1111 to its operational USB ID.
|
# 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