sdwire 0.3.1__tar.gz → 0.5.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
110
+ .pdm.toml
111
+ .pdm-python
112
+ .pdm-build/
113
+
114
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
115
+ __pypackages__/
116
+
117
+ # Celery stuff
118
+ celerybeat-schedule
119
+ celerybeat.pid
120
+
121
+ # SageMath parsed files
122
+ *.sage.py
123
+
124
+ # Environments
125
+ .env
126
+ .venv
127
+ env/
128
+ venv/
129
+ ENV/
130
+ env.bak/
131
+ venv.bak/
132
+
133
+ # Spyder project settings
134
+ .spyderproject
135
+ .spyproject
136
+
137
+ # Rope project settings
138
+ .ropeproject
139
+
140
+ # mkdocs documentation
141
+ /site
142
+
143
+ # mypy
144
+ .mypy_cache/
145
+ .dmypy.json
146
+ dmypy.json
147
+
148
+ # Pyre type checker
149
+ .pyre/
150
+
151
+ # pytype static type analyzer
152
+ .pytype/
153
+
154
+ # Cython debug symbols
155
+ cython_debug/
156
+
157
+ # PyCharm
158
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
159
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
160
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
161
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
162
+ #.idea/
sdwire-0.5.0/PKG-INFO ADDED
@@ -0,0 +1,211 @@
1
+ Metadata-Version: 2.5
2
+ Name: sdwire
3
+ Version: 0.5.0
4
+ Summary: CLI application to interact with Badgerd SDWire devices
5
+ Author-email: Talha Can Havadar <havadartalha@gmail.com>
6
+ License-Expression: GPL-3.0-only
7
+ License-File: LICENSE
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
12
+ Requires-Python: <4,>=3.12
13
+ Requires-Dist: click<9,>=8.1.7
14
+ Requires-Dist: pyftdi<0.57,>=0.56.0
15
+ Requires-Dist: pyusb<2,>=1.2.1
16
+ Description-Content-Type: text/markdown
17
+
18
+ # CLI for Badgerd SDWire Devices
19
+
20
+ Application also supports legacy SDWireC and non-Badger'd sdwires as well as
21
+ new Badgerd SDwire Gen2 devices.
22
+
23
+ Please see below for usage:
24
+
25
+ ```
26
+ ❯ sdwire --help
27
+ Usage: sdwire [OPTIONS] COMMAND [ARGS]...
28
+
29
+ SDWire CLI - Control SDWire devices from command line.
30
+
31
+ Options:
32
+ --debug Enable debug output
33
+ --version Show the version and exit.
34
+ --help Show this message and exit.
35
+
36
+ Commands:
37
+ block-dev Print the block device path of the SD card, for use in scripts.
38
+ list List all connected SDWire devices with their block device...
39
+ state Query and display the state of all connected SDWire devices.
40
+ switch dut/target => connects the sdcard interface to target device
41
+
42
+ ❯ sdwire switch --help
43
+ Usage: sdwire switch [OPTIONS] COMMAND [ARGS]...
44
+
45
+ dut/target => connects the sdcard interface to target device
46
+
47
+ ts/host => connects the sdcard interface to host machine
48
+
49
+ off => disconnects the sdcard interface from both host and target
50
+
51
+ Options:
52
+ -s, --serial TEXT Serial number of the sdwire device, if there is only one
53
+ sdwire connected then it will be used by default
54
+ --help Show this message and exit.
55
+
56
+ Commands:
57
+ dut dut/target => connects the sdcard interface to target device
58
+ host ts/host => connects the sdcard interface to host machine
59
+ off off => disconnects the sdcard interface from both host and target
60
+ target dut/target => connects the sdcard interface to target device
61
+ ts ts/host => connects the sdcard interface to host machine
62
+ ```
63
+
64
+ ## Installing
65
+
66
+ Using pip
67
+
68
+ ```
69
+ pip install sdwire
70
+ ```
71
+
72
+ Using apt
73
+
74
+ ```
75
+ sudo add-apt-repository ppa:tchavadar/badgerd
76
+ sudo apt install python3-sdwire
77
+ ```
78
+
79
+ Using a container (podman)
80
+
81
+ ```
82
+ podman pull ghcr.io/badger-embedded/sdwire-cli:latest
83
+ # or build locally from the repository root
84
+ podman build -t sdwire -f Containerfile .
85
+ ```
86
+
87
+ ## Running in a container
88
+
89
+ The image's entrypoint is `sdwire`, so arguments are passed directly. The
90
+ container needs access to the host USB devices (and block devices for
91
+ `sdwire list`), the simplest way is `--privileged`:
92
+
93
+ ```
94
+ podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli list
95
+ podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli switch dut
96
+ ```
97
+
98
+ Replace the image name with `sdwire` if you built it locally. A shell alias
99
+ makes it feel like a native install:
100
+
101
+ ```
102
+ alias sdwire='podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli'
103
+ ```
104
+
105
+ > **udev rules are required on the host.** udev runs on the host, not in the
106
+ > container, so install `udev/90-sdwire.rules` there for proper USB access and
107
+ > SDWire3 power management (autosuspend):
108
+ >
109
+ > ```
110
+ > sudo cp udev/90-sdwire.rules /etc/udev/rules.d/
111
+ > sudo udevadm control --reload-rules && sudo udevadm trigger
112
+ > ```
113
+ >
114
+ > Without them, USB access and card switching may fail with permission errors.
115
+
116
+ ## Listing SDWire Devices
117
+
118
+ `sdwire list` command will search through usb devices connected to the system
119
+ and prints out the list of gen2 and legacy devices.
120
+
121
+ ```
122
+ ❯ sdwire list
123
+ Serial Product Info
124
+ sdwire_gen2_101 [SDWire-Gen2::Badgerd Technologies]
125
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
126
+ ```
127
+
128
+ ## Switching SD Card Connection
129
+
130
+ `sdwire switch` command switches the sd card connection to specified direction.
131
+ If there is more than one sdwire connected to then you need specify which sdwire
132
+ you want to alter with `--serial` or `-s` options.
133
+
134
+ If there is only one sdwire connected then you dont need to specify the serial,
135
+ it will pick the one connected automatically. See the examples below.
136
+
137
+ ```
138
+ ❯ sdwire list
139
+ Serial Product Info
140
+ sdwire_gen2_101 [SDWire-Gen2::Badgerd Technologies]
141
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
142
+
143
+ ❯ sdwire switch -s bdgrd_sdwirec_522 target
144
+
145
+ ❯ sdwire switch target
146
+ Usage: sdwire switch [OPTIONS] COMMAND [ARGS]...
147
+ Try 'sdwire switch --help' for help.
148
+
149
+ Error: There is more then 1 sdwire device connected, please use --serial|-s to specify!
150
+
151
+ ❯ sdwire list
152
+ Serial Product Info
153
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
154
+
155
+ ❯ sdwire switch host
156
+ ```
157
+
158
+ ## Getting the Block Device Path
159
+
160
+ `sdwire block-dev` prints only the block device path of the SD card (e.g.
161
+ `/dev/sdb`), so it can be used directly in scripts. Errors and warnings go to
162
+ stderr, so stdout stays clean. Like `switch`, use `--serial`/`-s` when more than
163
+ one sdwire is connected.
164
+
165
+ ```
166
+ ❯ sdwire switch -s bdgrd_sdwirec_522 host
167
+ ❯ sudo bmaptool copy disk.img "$(sdwire block-dev -s bdgrd_sdwirec_522)"
168
+ ```
169
+
170
+ Right after `sdwire switch host` the block device needs a moment to become
171
+ usable: SDWire3 re-enumerates (~2 s) and SDWireC needs the kernel to detect the
172
+ card (up to ~2 s). `block-dev` waits up to 5 seconds for it. If the device
173
+ reports target mode it fails immediately instead of waiting. On macOS, an
174
+ existing disk node is assumed to be usable.
175
+
176
+ | Exit code | Meaning |
177
+ | --------- | -------------------------------------------------------------------------- |
178
+ | 0 | Path printed to stdout |
179
+ | 1 | Device found but no usable block device (target mode, no SD card, timeout) |
180
+ | 2 | No device, unknown serial, or more than one device without `--serial` |
181
+
182
+ ```
183
+ ❯ sdwire switch target
184
+ ❯ sdwire block-dev
185
+ Error: 20120501030900000.1 is in target mode, run 'sdwire switch host' first
186
+ ❯ echo $?
187
+ 1
188
+ ```
189
+
190
+ ### SDWire3 and Linux USB power management
191
+
192
+ On Linux, the SDWire3 only hands the SD card to the target while the USB device
193
+ is runtime-suspended. `sdwire switch target` enables this automatically
194
+ (`power/autosuspend_delay_ms=0`, `power/control=auto`) and verifies that
195
+ `power/runtime_status` becomes `suspended`; otherwise it exits with an error.
196
+ Writing to sysfs requires root, or install `udev/90-sdwire.rules`, which applies
197
+ the setting when the device is plugged in.
198
+
199
+ To do it by hand (replace `2-8` with your device's sysfs name, e.g. `5-1`):
200
+
201
+ ```
202
+ echo 0 | sudo tee /sys/bus/usb/devices/2-8/power/autosuspend_delay_ms
203
+ echo auto | sudo tee /sys/bus/usb/devices/2-8/power/control
204
+ ```
205
+
206
+ ## Testing
207
+
208
+ Unit tests: `uv run pytest tests`.
209
+
210
+ End-to-end tests run the installed CLI against simulated devices (umockdev) or
211
+ real hardware. See [tests/e2e/README.md](tests/e2e/README.md).
sdwire-0.5.0/README.md ADDED
@@ -0,0 +1,194 @@
1
+ # CLI for Badgerd SDWire Devices
2
+
3
+ Application also supports legacy SDWireC and non-Badger'd sdwires as well as
4
+ new Badgerd SDwire Gen2 devices.
5
+
6
+ Please see below for usage:
7
+
8
+ ```
9
+ ❯ sdwire --help
10
+ Usage: sdwire [OPTIONS] COMMAND [ARGS]...
11
+
12
+ SDWire CLI - Control SDWire devices from command line.
13
+
14
+ Options:
15
+ --debug Enable debug output
16
+ --version Show the version and exit.
17
+ --help Show this message and exit.
18
+
19
+ Commands:
20
+ block-dev Print the block device path of the SD card, for use in scripts.
21
+ list List all connected SDWire devices with their block device...
22
+ state Query and display the state of all connected SDWire devices.
23
+ switch dut/target => connects the sdcard interface to target device
24
+
25
+ ❯ sdwire switch --help
26
+ Usage: sdwire switch [OPTIONS] COMMAND [ARGS]...
27
+
28
+ dut/target => connects the sdcard interface to target device
29
+
30
+ ts/host => connects the sdcard interface to host machine
31
+
32
+ off => disconnects the sdcard interface from both host and target
33
+
34
+ Options:
35
+ -s, --serial TEXT Serial number of the sdwire device, if there is only one
36
+ sdwire connected then it will be used by default
37
+ --help Show this message and exit.
38
+
39
+ Commands:
40
+ dut dut/target => connects the sdcard interface to target device
41
+ host ts/host => connects the sdcard interface to host machine
42
+ off off => disconnects the sdcard interface from both host and target
43
+ target dut/target => connects the sdcard interface to target device
44
+ ts ts/host => connects the sdcard interface to host machine
45
+ ```
46
+
47
+ ## Installing
48
+
49
+ Using pip
50
+
51
+ ```
52
+ pip install sdwire
53
+ ```
54
+
55
+ Using apt
56
+
57
+ ```
58
+ sudo add-apt-repository ppa:tchavadar/badgerd
59
+ sudo apt install python3-sdwire
60
+ ```
61
+
62
+ Using a container (podman)
63
+
64
+ ```
65
+ podman pull ghcr.io/badger-embedded/sdwire-cli:latest
66
+ # or build locally from the repository root
67
+ podman build -t sdwire -f Containerfile .
68
+ ```
69
+
70
+ ## Running in a container
71
+
72
+ The image's entrypoint is `sdwire`, so arguments are passed directly. The
73
+ container needs access to the host USB devices (and block devices for
74
+ `sdwire list`), the simplest way is `--privileged`:
75
+
76
+ ```
77
+ podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli list
78
+ podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli switch dut
79
+ ```
80
+
81
+ Replace the image name with `sdwire` if you built it locally. A shell alias
82
+ makes it feel like a native install:
83
+
84
+ ```
85
+ alias sdwire='podman run --rm --privileged ghcr.io/badger-embedded/sdwire-cli'
86
+ ```
87
+
88
+ > **udev rules are required on the host.** udev runs on the host, not in the
89
+ > container, so install `udev/90-sdwire.rules` there for proper USB access and
90
+ > SDWire3 power management (autosuspend):
91
+ >
92
+ > ```
93
+ > sudo cp udev/90-sdwire.rules /etc/udev/rules.d/
94
+ > sudo udevadm control --reload-rules && sudo udevadm trigger
95
+ > ```
96
+ >
97
+ > Without them, USB access and card switching may fail with permission errors.
98
+
99
+ ## Listing SDWire Devices
100
+
101
+ `sdwire list` command will search through usb devices connected to the system
102
+ and prints out the list of gen2 and legacy devices.
103
+
104
+ ```
105
+ ❯ sdwire list
106
+ Serial Product Info
107
+ sdwire_gen2_101 [SDWire-Gen2::Badgerd Technologies]
108
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
109
+ ```
110
+
111
+ ## Switching SD Card Connection
112
+
113
+ `sdwire switch` command switches the sd card connection to specified direction.
114
+ If there is more than one sdwire connected to then you need specify which sdwire
115
+ you want to alter with `--serial` or `-s` options.
116
+
117
+ If there is only one sdwire connected then you dont need to specify the serial,
118
+ it will pick the one connected automatically. See the examples below.
119
+
120
+ ```
121
+ ❯ sdwire list
122
+ Serial Product Info
123
+ sdwire_gen2_101 [SDWire-Gen2::Badgerd Technologies]
124
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
125
+
126
+ ❯ sdwire switch -s bdgrd_sdwirec_522 target
127
+
128
+ ❯ sdwire switch target
129
+ Usage: sdwire switch [OPTIONS] COMMAND [ARGS]...
130
+ Try 'sdwire switch --help' for help.
131
+
132
+ Error: There is more then 1 sdwire device connected, please use --serial|-s to specify!
133
+
134
+ ❯ sdwire list
135
+ Serial Product Info
136
+ bdgrd_sdwirec_522 [sd-wire::SRPOL]
137
+
138
+ ❯ sdwire switch host
139
+ ```
140
+
141
+ ## Getting the Block Device Path
142
+
143
+ `sdwire block-dev` prints only the block device path of the SD card (e.g.
144
+ `/dev/sdb`), so it can be used directly in scripts. Errors and warnings go to
145
+ stderr, so stdout stays clean. Like `switch`, use `--serial`/`-s` when more than
146
+ one sdwire is connected.
147
+
148
+ ```
149
+ ❯ sdwire switch -s bdgrd_sdwirec_522 host
150
+ ❯ sudo bmaptool copy disk.img "$(sdwire block-dev -s bdgrd_sdwirec_522)"
151
+ ```
152
+
153
+ Right after `sdwire switch host` the block device needs a moment to become
154
+ usable: SDWire3 re-enumerates (~2 s) and SDWireC needs the kernel to detect the
155
+ card (up to ~2 s). `block-dev` waits up to 5 seconds for it. If the device
156
+ reports target mode it fails immediately instead of waiting. On macOS, an
157
+ existing disk node is assumed to be usable.
158
+
159
+ | Exit code | Meaning |
160
+ | --------- | -------------------------------------------------------------------------- |
161
+ | 0 | Path printed to stdout |
162
+ | 1 | Device found but no usable block device (target mode, no SD card, timeout) |
163
+ | 2 | No device, unknown serial, or more than one device without `--serial` |
164
+
165
+ ```
166
+ ❯ sdwire switch target
167
+ ❯ sdwire block-dev
168
+ Error: 20120501030900000.1 is in target mode, run 'sdwire switch host' first
169
+ ❯ echo $?
170
+ 1
171
+ ```
172
+
173
+ ### SDWire3 and Linux USB power management
174
+
175
+ On Linux, the SDWire3 only hands the SD card to the target while the USB device
176
+ is runtime-suspended. `sdwire switch target` enables this automatically
177
+ (`power/autosuspend_delay_ms=0`, `power/control=auto`) and verifies that
178
+ `power/runtime_status` becomes `suspended`; otherwise it exits with an error.
179
+ Writing to sysfs requires root, or install `udev/90-sdwire.rules`, which applies
180
+ the setting when the device is plugged in.
181
+
182
+ To do it by hand (replace `2-8` with your device's sysfs name, e.g. `5-1`):
183
+
184
+ ```
185
+ echo 0 | sudo tee /sys/bus/usb/devices/2-8/power/autosuspend_delay_ms
186
+ echo auto | sudo tee /sys/bus/usb/devices/2-8/power/control
187
+ ```
188
+
189
+ ## Testing
190
+
191
+ Unit tests: `uv run pytest tests`.
192
+
193
+ End-to-end tests run the installed CLI against simulated devices (umockdev) or
194
+ real hardware. See [tests/e2e/README.md](tests/e2e/README.md).
@@ -0,0 +1,44 @@
1
+ [project]
2
+ name = "sdwire"
3
+ version = "0.5.0"
4
+ description = "CLI application to interact with Badgerd SDWire devices"
5
+ authors = [{ name = "Talha Can Havadar", email = "havadartalha@gmail.com" }]
6
+ requires-python = ">=3.12,<4"
7
+ readme = "README.md"
8
+ license = "GPL-3.0-only"
9
+ classifiers = [
10
+ "Programming Language :: Python :: 3",
11
+ "Programming Language :: Python :: 3.12",
12
+ "Programming Language :: Python :: 3.13",
13
+ "Programming Language :: Python :: 3.14",
14
+ ]
15
+ dependencies = [
16
+ "click>=8.1.7,<9",
17
+ "pyusb>=1.2.1,<2",
18
+ "pyftdi>=0.56.0,<0.57",
19
+ ]
20
+
21
+ [project.scripts]
22
+ sdwire = "sdwire:main.main"
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "black>=24.8.0,<25",
27
+ "pytest>=9.2.0,<10",
28
+ ]
29
+ # harness of the end-to-end tests (tests/e2e), installed on its own in CI
30
+ e2e = [
31
+ "pytest>=9.2.0,<10",
32
+ "pytest-timeout>=2.4.0,<3",
33
+ "syrupy>=6.1.1,<7",
34
+ ]
35
+
36
+ [tool.hatch.build.targets.sdist]
37
+ include = ["sdwire"]
38
+
39
+ [tool.hatch.build.targets.wheel]
40
+ include = ["sdwire"]
41
+
42
+ [build-system]
43
+ requires = ["hatchling"]
44
+ build-backend = "hatchling.build"
@@ -5,6 +5,7 @@ to their corresponding block device paths (e.g., /dev/sda on Linux, /dev/disk2 o
5
5
  It uses USB hierarchy and topology information for accurate device mapping.
6
6
  """
7
7
 
8
+ import os
8
9
  import platform
9
10
  import logging
10
11
  import re
@@ -23,6 +24,43 @@ from sdwire.constants import (
23
24
 
24
25
  log = logging.getLogger(__name__)
25
26
 
27
+ SYSFS_ROOT = "/sys"
28
+
29
+
30
+ def _read_sysfs(path: str) -> Optional[str]:
31
+ try:
32
+ with open(path) as f:
33
+ return f.read().strip()
34
+ except OSError:
35
+ return None
36
+
37
+
38
+ def _sysfs_block_dir(block_dev: str, sysfs_root: str) -> str:
39
+ return os.path.join(sysfs_root, "class", "block", os.path.basename(block_dev))
40
+
41
+
42
+ def is_block_dev_usable(block_dev: str, sysfs_root: str = SYSFS_ROOT) -> bool:
43
+ """Return True when the block device has a medium and is ready for I/O.
44
+
45
+ Card readers (e.g. SDWireC) keep their block device while no medium is
46
+ present, reporting a size of 0. Uses sysfs only, so no permissions on the
47
+ device node are needed. Without sysfs (macOS) existence of the node is
48
+ the best available signal.
49
+ """
50
+ if not os.path.isdir(os.path.join(sysfs_root, "class", "block")):
51
+ return os.path.exists(block_dev)
52
+ base = _sysfs_block_dir(block_dev, sysfs_root)
53
+ size = _read_sysfs(os.path.join(base, "size"))
54
+ if not size or not size.isdigit() or int(size) == 0:
55
+ return False
56
+ state = _read_sysfs(os.path.join(base, "device", "state"))
57
+ return state in (None, "running")
58
+
59
+
60
+ def is_block_dev_readonly(block_dev: str, sysfs_root: str = SYSFS_ROOT) -> bool:
61
+ base = _sysfs_block_dir(block_dev, sysfs_root)
62
+ return _read_sysfs(os.path.join(base, "ro")) == "1"
63
+
26
64
 
27
65
  def map_usb_device_to_block_device(usb_device: usb.core.Device) -> Optional[str]:
28
66
  """Map a USB device to its corresponding system block device path using USB hierarchy.