-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbutton.html
360 lines (340 loc) · 19.2 KB
/
button.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>the un i blog</title>
<meta name="generator" content="Org Mode" />
<style>
#content { max-width: 60em; margin: auto; }
.title { text-align: center;
margin-bottom: .2em; }
.subtitle { text-align: center;
font-size: medium;
font-weight: bold;
margin-top:0; }
.todo { font-family: monospace; color: red; }
.done { font-family: monospace; color: green; }
.priority { font-family: monospace; color: orange; }
.tag { background-color: #eee; font-family: monospace;
padding: 2px; font-size: 80%; font-weight: normal; }
.timestamp { color: #bebebe; }
.timestamp-kwd { color: #5f9ea0; }
.org-right { margin-left: auto; margin-right: 0px; text-align: right; }
.org-left { margin-left: 0px; margin-right: auto; text-align: left; }
.org-center { margin-left: auto; margin-right: auto; text-align: center; }
.underline { text-decoration: underline; }
#postamble p, #preamble p { font-size: 90%; margin: .2em; }
p.verse { margin-left: 3%; }
pre {
border: 1px solid #e6e6e6;
border-radius: 3px;
background-color: #f2f2f2;
padding: 8pt;
font-family: monospace;
overflow: auto;
margin: 1.2em;
}
pre.src {
position: relative;
overflow: auto;
}
pre.src:before {
display: none;
position: absolute;
top: -8px;
right: 12px;
padding: 3px;
color: #555;
background-color: #f2f2f299;
}
pre.src:hover:before { display: inline; margin-top: 14px;}
/* Languages per Org manual */
pre.src-asymptote:before { content: 'Asymptote'; }
pre.src-awk:before { content: 'Awk'; }
pre.src-authinfo::before { content: 'Authinfo'; }
pre.src-C:before { content: 'C'; }
/* pre.src-C++ doesn't work in CSS */
pre.src-clojure:before { content: 'Clojure'; }
pre.src-css:before { content: 'CSS'; }
pre.src-D:before { content: 'D'; }
pre.src-ditaa:before { content: 'ditaa'; }
pre.src-dot:before { content: 'Graphviz'; }
pre.src-calc:before { content: 'Emacs Calc'; }
pre.src-emacs-lisp:before { content: 'Emacs Lisp'; }
pre.src-fortran:before { content: 'Fortran'; }
pre.src-gnuplot:before { content: 'gnuplot'; }
pre.src-haskell:before { content: 'Haskell'; }
pre.src-hledger:before { content: 'hledger'; }
pre.src-java:before { content: 'Java'; }
pre.src-js:before { content: 'Javascript'; }
pre.src-latex:before { content: 'LaTeX'; }
pre.src-ledger:before { content: 'Ledger'; }
pre.src-lisp:before { content: 'Lisp'; }
pre.src-lilypond:before { content: 'Lilypond'; }
pre.src-lua:before { content: 'Lua'; }
pre.src-matlab:before { content: 'MATLAB'; }
pre.src-mscgen:before { content: 'Mscgen'; }
pre.src-ocaml:before { content: 'Objective Caml'; }
pre.src-octave:before { content: 'Octave'; }
pre.src-org:before { content: 'Org mode'; }
pre.src-oz:before { content: 'OZ'; }
pre.src-plantuml:before { content: 'Plantuml'; }
pre.src-processing:before { content: 'Processing.js'; }
pre.src-python:before { content: 'Python'; }
pre.src-R:before { content: 'R'; }
pre.src-ruby:before { content: 'Ruby'; }
pre.src-sass:before { content: 'Sass'; }
pre.src-scheme:before { content: 'Scheme'; }
pre.src-screen:before { content: 'Gnu Screen'; }
pre.src-sed:before { content: 'Sed'; }
pre.src-sh:before { content: 'shell'; }
pre.src-sql:before { content: 'SQL'; }
pre.src-sqlite:before { content: 'SQLite'; }
/* additional languages in org.el's org-babel-load-languages alist */
pre.src-forth:before { content: 'Forth'; }
pre.src-io:before { content: 'IO'; }
pre.src-J:before { content: 'J'; }
pre.src-makefile:before { content: 'Makefile'; }
pre.src-maxima:before { content: 'Maxima'; }
pre.src-perl:before { content: 'Perl'; }
pre.src-picolisp:before { content: 'Pico Lisp'; }
pre.src-scala:before { content: 'Scala'; }
pre.src-shell:before { content: 'Shell Script'; }
pre.src-ebnf2ps:before { content: 'ebfn2ps'; }
/* additional language identifiers per "defun org-babel-execute"
in ob-*.el */
pre.src-cpp:before { content: 'C++'; }
pre.src-abc:before { content: 'ABC'; }
pre.src-coq:before { content: 'Coq'; }
pre.src-groovy:before { content: 'Groovy'; }
/* additional language identifiers from org-babel-shell-names in
ob-shell.el: ob-shell is the only babel language using a lambda to put
the execution function name together. */
pre.src-bash:before { content: 'bash'; }
pre.src-csh:before { content: 'csh'; }
pre.src-ash:before { content: 'ash'; }
pre.src-dash:before { content: 'dash'; }
pre.src-ksh:before { content: 'ksh'; }
pre.src-mksh:before { content: 'mksh'; }
pre.src-posh:before { content: 'posh'; }
/* Additional Emacs modes also supported by the LaTeX listings package */
pre.src-ada:before { content: 'Ada'; }
pre.src-asm:before { content: 'Assembler'; }
pre.src-caml:before { content: 'Caml'; }
pre.src-delphi:before { content: 'Delphi'; }
pre.src-html:before { content: 'HTML'; }
pre.src-idl:before { content: 'IDL'; }
pre.src-mercury:before { content: 'Mercury'; }
pre.src-metapost:before { content: 'MetaPost'; }
pre.src-modula-2:before { content: 'Modula-2'; }
pre.src-pascal:before { content: 'Pascal'; }
pre.src-ps:before { content: 'PostScript'; }
pre.src-prolog:before { content: 'Prolog'; }
pre.src-simula:before { content: 'Simula'; }
pre.src-tcl:before { content: 'tcl'; }
pre.src-tex:before { content: 'TeX'; }
pre.src-plain-tex:before { content: 'Plain TeX'; }
pre.src-verilog:before { content: 'Verilog'; }
pre.src-vhdl:before { content: 'VHDL'; }
pre.src-xml:before { content: 'XML'; }
pre.src-nxml:before { content: 'XML'; }
/* add a generic configuration mode; LaTeX export needs an additional
(add-to-list 'org-latex-listings-langs '(conf " ")) in .emacs */
pre.src-conf:before { content: 'Configuration File'; }
table { border-collapse:collapse; }
caption.t-above { caption-side: top; }
caption.t-bottom { caption-side: bottom; }
td, th { vertical-align:top; }
th.org-right { text-align: center; }
th.org-left { text-align: center; }
th.org-center { text-align: center; }
td.org-right { text-align: right; }
td.org-left { text-align: left; }
td.org-center { text-align: center; }
dt { font-weight: bold; }
.footpara { display: inline; }
.footdef { margin-bottom: 1em; }
.figure { padding: 1em; }
.figure p { text-align: center; }
.equation-container {
display: table;
text-align: center;
width: 100%;
}
.equation {
vertical-align: middle;
}
.equation-label {
display: table-cell;
text-align: right;
vertical-align: middle;
}
.inlinetask {
padding: 10px;
border: 2px solid gray;
margin: 10px;
background: #ffffcc;
}
#org-div-home-and-up
{ text-align: right; font-size: 70%; white-space: nowrap; }
textarea { overflow-x: auto; }
.linenr { font-size: smaller }
.code-highlighted { background-color: #ffff00; }
.org-info-js_info-navigation { border-style: none; }
#org-info-js_console-label
{ font-size: 10px; font-weight: bold; white-space: nowrap; }
.org-info-js_search-highlight
{ background-color: #ffff00; color: #000000; font-weight: bold; }
.org-svg { }
</style>
</head>
<body>
<div id="content" class="content">
<h1 class="title">the un i blog</h1>
<div id="outline-container-org113eb70" class="outline-2">
<h2 id="org113eb70"><span class="section-number-2">1.</span> THE BUTTON - short summary of tonight’s efforts</h2>
<div class="outline-text-2" id="text-1">
<p>
i just acquired a IRIScan™ Desk 5 Pro book scanner and it comes with an extra button. like, not within the scanner, another whole USB device. Just a single button it’s very funny.<br />
</p>
<div id="org798e6a7" class="figure">
<p><img src="./img/button.jpg" alt="button.jpg" /><br />
</p>
<p><span class="figure-number">Figure 1: </span>i took this image with the scanner using ffmpeg i feel like making this work properly might take a while still</p>
</div>
<p>
but none of this formally supports linux in any shape or capacity so i had to make it work myself. The end results are on <a href="https://github.com/uniwuni/the-button">github</a>!!<br />
</p>
<p>
of course the first thing you try is pressing the button. expectedly, it does absolutely nothing.<br />
running <code>lsusb</code> yields something marginally useful, namely a vendor id and a product id, but a, uh, rather interesting name:<br />
</p>
<pre class="example" id="org5a617d3">
...
Bus 001 Device 002: ID 046d:c05a Logitech, Inc. M90/M100 Optical Mouse
...
Bus 001 Device 056: ID 2e5a:2015
...
</pre>
<p>
So actually, the button is not only functionally useless so far, it is also nameless!<br />
</p>
<p>
There’s a couple standard things you check with new usb devices on linux - <code>/dev/input/by-id/</code> is a classic, but it does not show up there. Neither does it just appear to be one of the <code>/dev/input/event*</code> devices - at least that’s what libinput told me, and libinput is my friend who would never lie to me!!!!<br />
So there’s not really much left, eventually I arrive at <code>/dev/usb</code> and finally, two previously undiscovered devices appear - <code>/dev/usb/hiddev0</code> and <code>/dev/usb/hiddev1</code>. Even after a lot of looking up various stuff, I’m not quite sure what the first one is, but it appears to be my keyboard. Anyways, that gives us a decent chance at doing <i>something</i>, and indeed, <code>sudo cat /dev/usb/hiddev1</code> spews out some meaningless garbage every time I press the button. It also showed me that the polling rate of this thing is about 3 Hz. Anyways, it’s always the same 512 bytes:<br />
</p>
<pre class="example" id="org08ff8c2">
0000000 0002 ff00 0000 0000 0002 ff00 0001 0000
0000010 0002 ff00 0002 0000 0002 ff00 0003 0000
0000020 0002 ff00 0004 0000 0002 ff00 0005 0000
0000030 0002 ff00 0006 0000 0002 ff00 0007 0000
0000040 0002 ff00 0008 0000 0002 ff00 0009 0000
0000050 0002 ff00 000a 0000 0002 ff00 000b 0000
0000060 0002 ff00 000c 0000 0002 ff00 000d 0000
0000070 0002 ff00 000e 0000 0002 ff00 000f 0000
0000080 0002 ff00 0010 0000 0002 ff00 0011 0000
0000090 0002 ff00 0012 0000 0002 ff00 0013 0000
00000a0 0002 ff00 0014 0000 0002 ff00 0015 0000
00000b0 0002 ff00 0016 0000 0002 ff00 0017 0000
00000c0 0002 ff00 0018 0000 0002 ff00 0019 0000
00000d0 0002 ff00 001a 0000 0002 ff00 001b 0000
00000e0 0002 ff00 001c 0000 0002 ff00 001d 0000
00000f0 0002 ff00 001e 0000 0002 ff00 001f 0000
0000100 0002 ff00 0020 0000 0002 ff00 0021 0000
0000110 0002 ff00 0022 0000 0002 ff00 0023 0000
0000120 0002 ff00 0024 0000 0002 ff00 0025 0000
0000130 0002 ff00 0026 0000 0002 ff00 0027 0000
0000140 0002 ff00 0028 0000 0002 ff00 0029 0000
0000150 0002 ff00 002a 0000 0002 ff00 002b 0000
0000160 0002 ff00 002c 0000 0002 ff00 002d 0000
0000170 0002 ff00 002e 0000 0002 ff00 002f 0000
0000180 0002 ff00 0030 0000 0002 ff00 0031 0000
0000190 0002 ff00 0032 0000 0002 ff00 0033 0000
00001a0 0002 ff00 0034 0000 0002 ff00 0035 0000
00001b0 0002 ff00 0036 0000 0002 ff00 0037 0000
00001c0 0002 ff00 0038 0000 0002 ff00 0039 0000
00001d0 0002 ff00 003a 0000 0002 ff00 003b 0000
00001e0 0002 ff00 003c 0000 0002 ff00 003d 0000
00001f0 0002 ff00 003e 0000 0002 ff00 003f 0000
</pre>
<p>
No idea whether there’s any significance to them, I doubt it, but it’s nice to have something to rely on.<br />
</p>
<p>
The first thing I had to figure out was how to make the button more permanent - <code>/dev/usb/hiddev1</code> is a fairly volatile spot for anything to be on, and it might not last a reboot. This was fairly easy to achieve with a symlinking udev rule:<br />
</p>
<pre class="example">
ACTION=="add", ATTRS{idVendor}=="2e5a", ATTRS{idProduct}=="2015", SYMLINK+="button", MODE="0666"
</pre>
<p>
Storing this in <code>/etc/udev/rules.d/50-button.service</code> was sufficient to turn <code>/dev/button</code> into a more permanent and easily readable outlet for your favorite 2<sup>17</sup> bits.<br />
</p>
<p>
Actually waiting for the button to be pressed was a little more annoying though and made me turn to C (side note: during the whole development process, not a single segfault occurred i hope youre proud of me…..).<br />
</p>
<p>
First I tried just reading 512 bytes and more or less repeating if there wasn’t anything to be read. But this is not very efficient obviously so eventually I turned to <code>poll</code>, a decently nice syscall that tells you when there’s new stuff to be read and waits for a certain while. But not all bytes are sent at the exact same point in time, so I have to wait a slight bit until I can read 512 at once.<br />
</p>
<p>
At some point I got that more or less figured out, despite some struggles with finding the right thing to call and the right options to <code>open</code> and so on. Yet every time I pressed the button a couple times in a row, the program would hang and only read further when I sent SIGINT or something. Why was that? Well, since I actually want to stuff with the button presses, after every successful read I call a program via <code>system("~/.local/bin/button-pressed")</code>. Oops, turned out that <code>system</code> is blocking and starting a shell and launching a KDE notification (which is was that script did at the time) takes a decent bit such that the events kind of queue up.<br />
Nonetheless, this was decently easy to fix by just asynchronously calling the thing.<br />
</p>
<p>
So, at some point I got a tool that worked sufficiently well at detecting these button presses and calling <i>something</i>. Following the same laws of thought as every person who would write 80 lines of C for their button, I wasn’t quite satisfied yet and felt the need to automate some stuff further. So I turned to a systemd service. There was the easy option of just enabling and autorestarting it, and that worked fairly well, well, unless I disconnect the button (also it would waste performance by restarting the thing every idk 20 seconds), in which case reconnecting it would introduce delay until the autorestart and just feel kinda ugly either way.<br />
</p>
<p>
So I turned to the even sillier alternative and changed my udev rule:<br />
</p>
<pre class="example">
ACTION=="add", ATTRS{idVendor}=="2e5a", ATTRS{idProduct}=="2015", SYMLINK+="button", MODE="0666", TAG+="systemd", ENV{SYSTEMD_USER_WANTS}="the-button.service"
</pre>
<p>
With this rule, the service is enabled as long as the device is connected. But this yielded even more mysterious issues: every time I connected the thing, the instance of the program started immediately after would even immediatelier crash due to receiving a 64 byte long message on the first button press (every button press takes 512 bytes, remember?). But if I were to restart the service with or without pressing the button in the meantime, the new instance would work just fine.<br />
</p>
<p>
It took me another half an hour to figure that out, but the problem was fairly simple all along - <code>/dev/button</code> seems to behave differently the first couple milliseconds after the service gets started, so while for some reason it detects the button presses anyways, it probably reads something different? I’m not entirely sure, but there was a pretty easy fix - adding 300 ms of delay before opening the file seems to work fine and it’s not like you were gonna press it that quickly anyways.<br />
</p>
<p>
so yeah, now it works fairly well, and I guess I’ll use it to speed up the scanning process or something, which probably deserves another entry uhh thank you<br />
</p>
</div>
</div>
<div id="outline-container-org1c02b5e" class="outline-2">
<h2 id="org1c02b5e"><span class="section-number-2">2.</span> THE GLOWING BUTTON - summary of day two’s efforts</h2>
<div class="outline-text-2" id="text-2">
<p>
turns out the button also has a red LED and when I run the company’s software with wine (for which I needed to use 7zip to extract the data from the self-extracting setup because that setup wouldn’t work but it’s only a setup for multiple setups and the individual setups all work), the LED starts glowing. how does that work? well I had no clue so I googled something like “linux monitor usb” until I eventually arrived at <code>usbmon</code>, a kernel feature that allows you to do exactly that. By enabling the kernel module, doing a bunch of stuff and eventually running the program while watching the <code>usbmnon</code> output for the right interface, I figured out the LED gets enabled by the following 32 bytes:<br />
</p>
<pre class="example" id="org9291413">
0x30, 0x31, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30,
0x30, 0x30, 0x30, 0x30,
0x00, 0x3e, 0x00, 0x0d,
0x00, 0xa0, 0x00, 0x20,
0x00, 0x20, 0x00, 0x3c,
0x00, 0x21, 0x00, 0x2d
</pre>
<p>
As I later figured out by messing around a bit, exchanging the first 0x31 for a 0x30 disables it.<br />
</p>
<p>
So how do I actually send these bytes to the device? there’s obviously no particularly user-friendly interface to do that, since neither the average developer nor the average person with a lot of time on their hands needs to write USB drivers (or something like them) on the regular. Nonetheless I quickly arrived at <code>libusb</code>, which came the closest to user-friendly this stuff gets I suppose.<br />
</p>
<p>
By a lot of googling (and honestly some LLM help i’m sorry it’s a bit embarassing but just way faster and i proof readall of it trust me please), i wrote a program that just sends these 32 bytes to the right interface and the right endpoint!! the latter is also very important but at least it appears in both <code>lsusb -v</code> output and the <code>usbmon</code> output. nonetheless, it wouldn’t work immediately - since the button is also treated as a HID device, there’s already a driver loaded for it which blocks <code>libusb</code> from properly acquiring the interface, luckily, it provides a function to temporarily detach and reattach the driver. Finally, I was able to just run <code>button-light on</code> and <code>button-light off</code>, and have it work.<br />
</p>
<p>
But this caused a couple other problems, since the other program to watch the button presses breaks in the meantime because the file descriptor to <code>/dev/button/</code> becomes invalidated due to the driver being unloaded blah blah blah. This doesn’t seem easily fixable and I’m not super inclined on reopening the file within the program because of a lot of decently fucked up race conditions and so on, so I took the substantially more cursed route of only processing one input per run of <code>button</code>: It waits for a single button press, runs <code>button-pressed</code>, which turns the light on and off, then uses <code>exec</code> to launch itself again, avoiding any of these mishaps. This works well and while it obviously leaks PIDs, as far as I know it shouldn’t cause any permanent issues.<br />
</p>
</div>
</div>
</div>
<div id="postamble" class="status">
<p class="date">Date: 2024-07-30 Di 00:00</p>
</div>
</body>
</html>