uhid 0.1.0

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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/ext/uhid/extconf.rb +3 -0
  3. data/ext/uhid/uhid.c +170 -0
  4. data/lib/uhid.rb +47 -0
  5. metadata +46 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 95a724401bf1d7cf2d8fb66a56412745c75ef806cd71a9c4b3d2a9febf4d80ed
4
+ data.tar.gz: 38dcf9a63b553e0e432ef3bcf1d6120dfb2119cf6160cec966f9c878ff0935e4
5
+ SHA512:
6
+ metadata.gz: 7ecac03e083702842cedc553ccf96fec554eb147b73d3ca7fe4f043aab3c1f9c0b74c86639302053091562ff02414a15911bfc9c4f180a8579538eaa0f80b4f0
7
+ data.tar.gz: 462ebabcefe0f029292faf59c4a836eb3e267d7fab28aa9c67f6350f5c470419aba0b667338efa87f4778c9d079e52fe31381e8febcf6ab37b43bb20abf58a1d
@@ -0,0 +1,3 @@
1
+ require "mkmf"
2
+ create_makefile "uhid/uhid"
3
+
data/ext/uhid/uhid.c ADDED
@@ -0,0 +1,170 @@
1
+ #include "ruby.h"
2
+ #include "linux/uhid.h"
3
+ #include <fcntl.h>
4
+
5
+ static void
6
+ cuhid_write(int fd, const struct uhid_event *ev)
7
+ {
8
+ ssize_t ret;
9
+
10
+ ret = write(fd, ev, sizeof(*ev));
11
+ if (ret < 0) {
12
+ rb_raise(rb_eRuntimeError, "Cannot write to uhid");
13
+ } else if (ret != sizeof(*ev)) {
14
+ rb_raise(rb_eRuntimeError, "Wrong size written to uhid: %zd != %zu",
15
+ ret, sizeof(ev));
16
+ }
17
+ }
18
+
19
+ static VALUE
20
+ cuhid_create(VALUE self, VALUE name, VALUE vendor, VALUE product, VALUE data)
21
+ {
22
+ Check_Type(name, T_STRING);
23
+ Check_Type(data, T_STRING);
24
+
25
+ struct uhid_event ev;
26
+
27
+ memset(&ev, 0, sizeof(ev));
28
+ ev.type = UHID_CREATE2;
29
+ strncpy((char*)ev.u.create2.name, StringValueCStr(name), 127);
30
+ ev.u.create2.rd_size = RSTRING_LEN(StringValue(data));
31
+ memcpy(ev.u.create2.rd_data, RSTRING_PTR(StringValue(data)), ev.u.create2.rd_size);
32
+ ev.u.create2.bus = BUS_USB;
33
+ ev.u.create2.vendor = (uint32_t)NUM2INT(vendor);
34
+ ev.u.create2.product = (uint32_t)NUM2INT(product);
35
+ ev.u.create2.version = 0;
36
+ ev.u.create2.country = 0;
37
+
38
+ ID file_var = rb_intern("@file");
39
+ VALUE file = rb_ivar_get(self, file_var);
40
+ Check_Type(file, T_FILE);
41
+ VALUE file_fileno = rb_funcall(file, rb_intern("fileno"), 0);
42
+ int fd = NUM2INT(file_fileno);
43
+
44
+ // This makes the open file non-blocking
45
+ int flags = fcntl(fd, F_GETFL, 0);
46
+ fcntl(fd, F_SETFL, flags | O_NONBLOCK);
47
+
48
+ cuhid_write(fd, &ev);
49
+
50
+ return Qnil;
51
+ }
52
+
53
+ /*
54
+ * Close the open uhid device.
55
+ */
56
+ static VALUE
57
+ cuhid_destroy(VALUE self)
58
+ {
59
+ struct uhid_event ev;
60
+
61
+ memset(&ev, 0, sizeof(ev));
62
+ ev.type = UHID_DESTROY;
63
+
64
+ ID file_var = rb_intern("@file");
65
+ VALUE file = rb_ivar_get(self, file_var);
66
+ Check_Type(file, T_FILE);
67
+ VALUE file_fileno = rb_funcall(file, rb_intern("fileno"), 0);
68
+ int fd = NUM2INT(file_fileno);
69
+
70
+ cuhid_write(fd, &ev);
71
+
72
+ return Qnil;
73
+ }
74
+
75
+ /*
76
+ * Write the given data to the host.
77
+ *
78
+ * Make sure you send the data in a format, expected
79
+ * by the host, e.g. in 64 byte chunks.
80
+ *
81
+ * @param data [String] the data to be sent to the host.
82
+ * @returns [Integer] the number of bytes sent.
83
+ */
84
+ static VALUE
85
+ cuhid_write_data(VALUE self, VALUE data)
86
+ {
87
+ const char* d = RSTRING_PTR(StringValue(data));
88
+ size_t l = RSTRING_LEN(StringValue(data));
89
+ size_t size = l;
90
+ if (size > UHID_DATA_MAX) {
91
+ size = UHID_DATA_MAX;
92
+ }
93
+
94
+ struct uhid_event ev;
95
+
96
+ memset(&ev, 0, sizeof(ev));
97
+ ev.type = UHID_INPUT2;
98
+ memcpy(ev.u.input2.data, d, size);
99
+ ev.u.input2.size = size;
100
+
101
+ ID file_var = rb_intern("@file");
102
+ VALUE file = rb_ivar_get(self, file_var);
103
+ Check_Type(file, T_FILE);
104
+ VALUE file_fileno = rb_funcall(file, rb_intern("fileno"), 0);
105
+ int fd = NUM2INT(file_fileno);
106
+
107
+ cuhid_write(fd, &ev);
108
+
109
+ // We the number of processed bytes
110
+ return INT2NUM(size);
111
+ }
112
+
113
+ /*
114
+ * Read data from the host.
115
+ *
116
+ * All returned hashes contain the type key that specifies the type
117
+ * of the received packet. Valid types are: START, STOP, OPEN, CLOSE,
118
+ * OUTPUT. Only OUTPUT Hashes contain a data field that holds the
119
+ * data received by the host.
120
+ *
121
+ * @returns [nil, Hash] a Hash that contains the received data or nil if no data is available.
122
+ */
123
+ static VALUE
124
+ cuhid_read_data(VALUE self)
125
+ {
126
+ ID file_var = rb_intern("@file");
127
+ VALUE file = rb_ivar_get(self, file_var);
128
+ Check_Type(file, T_FILE);
129
+ VALUE file_fileno = rb_funcall(file, rb_intern("fileno"), 0);
130
+ int fd = NUM2INT(file_fileno);
131
+
132
+ struct uhid_event ev;
133
+
134
+ memset(&ev, 0, sizeof(ev));
135
+ ssize_t ret = read(fd, &ev, sizeof(ev));
136
+
137
+ if (ret == -1) { // EAGAIN
138
+ return Qnil;
139
+ }
140
+
141
+ VALUE h = rb_hash_new();
142
+ switch (ev.type) {
143
+ case UHID_START:
144
+ rb_hash_aset(h, rb_str_new_cstr("type"), rb_str_new_cstr("START"));
145
+ return h;
146
+ case UHID_STOP:
147
+ rb_hash_aset(h, rb_str_new_cstr("type"), rb_str_new_cstr("STOP"));
148
+ return h;
149
+ case UHID_OPEN:
150
+ rb_hash_aset(h, rb_str_new_cstr("type"), rb_str_new_cstr("OPEN"));
151
+ return h;
152
+ case UHID_CLOSE:
153
+ rb_hash_aset(h, rb_str_new_cstr("type"), rb_str_new_cstr("CLOSE"));
154
+ return h;
155
+ case UHID_OUTPUT:
156
+ rb_hash_aset(h, rb_str_new_cstr("type"), rb_str_new_cstr("OUTPUT"));
157
+ rb_hash_aset(h, rb_str_new_cstr("data"), rb_str_new((const char *) ev.u.output.data, ev.u.output.size));
158
+ return h;
159
+ default:
160
+ return Qnil;
161
+ }
162
+ }
163
+
164
+ void Init_uhid() {
165
+ VALUE cUhid = rb_const_get(rb_cObject, rb_intern("Uhid"));
166
+ rb_define_private_method(cUhid, "create", cuhid_create, 4);
167
+ rb_define_private_method(cUhid, "destroy", cuhid_destroy, 0);
168
+ rb_define_method(cUhid, "write", cuhid_write_data, 1);
169
+ rb_define_method(cUhid, "read", cuhid_read_data, 0);
170
+ }
data/lib/uhid.rb ADDED
@@ -0,0 +1,47 @@
1
+ # Linux UHID class.
2
+ #
3
+ # This class allows the creation of virtual USB HID
4
+ # devices on Linux using the `uhid` kernel module.
5
+ #
6
+ # @author David P. Sugar (r4gus)
7
+ class Uhid
8
+ VERSION = "0.1.0"
9
+
10
+ # FIDO2/U2F USB report descriptor
11
+ RD_FIDO = "\x06\xd0\xf1\x09\x01\xa1\x01\x09"\
12
+ "\x20\x15\x00\x26\xff\x00\x75\x08"\
13
+ "\x95\x40\x81\x02\x09\x21\x15\x00"\
14
+ "\x26\xff\x00\x75\x08\x95\x40\x91"\
15
+ "\x02\xc0".force_encoding("BINARY")
16
+
17
+ # Create a new virtual HID device.
18
+ #
19
+ # @param name [String] the name of the device.
20
+ # @param rd [String] `BINARY` encoded report descriptor.
21
+ # @param vendor [Integer] vendor ID.
22
+ # @param product [Integer] product ID.
23
+ # @param path [String] the file path (usually `/dev/uhid`).
24
+ def initialize(
25
+ name = "ruby-fido",
26
+ rd = RD_FIDO,
27
+ vendor = 0x15d9,
28
+ product = 0x0a37,
29
+ path = "/dev/uhid"
30
+ )
31
+ @file = File.open(path, "r+", File::NONBLOCK)
32
+ ObjectSpace.define_finalizer(self, method(:finalize))
33
+ self.create(name, vendor, product, rd)
34
+ end
35
+
36
+ private
37
+
38
+ def finalize(_id)
39
+ self.destroy()
40
+ @file.close
41
+ end
42
+ end
43
+
44
+ require_relative "uhid/uhid"
45
+
46
+
47
+
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uhid
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - David Pierre Sugar
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-12-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Create virtual USB devices on Linux using the uhid module
14
+ email: david@thesugar.de
15
+ executables: []
16
+ extensions:
17
+ - ext/uhid/extconf.rb
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ext/uhid/extconf.rb
21
+ - ext/uhid/uhid.c
22
+ - lib/uhid.rb
23
+ homepage: ''
24
+ licenses:
25
+ - MIT
26
+ metadata: {}
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ required_rubygems_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ requirements: []
42
+ rubygems_version: 3.5.0.dev
43
+ signing_key:
44
+ specification_version: 4
45
+ summary: Linux uhid library
46
+ test_files: []