├── .gitattributes
├── .gitignore
├── LICENSE
├── Makefile
├── README.md
├── beepy-kbd.dts
├── beepy-kbd.map
└── src
├── bbq10kbd_featherwing_codes.h
├── bbq10kbd_pmod_codes.h
├── bbq20kbd_pmod_codes.h
├── bbqX0kbd_registers.h
├── config.h
├── debug_levels.h
├── i2c_helper.h
├── indicators.h
├── input_display.c
├── input_fw.c
├── input_iface.c
├── input_iface.h
├── input_meta.c
├── input_modifiers.c
├── input_rtc.c
├── input_touch.c
├── main.c
├── params_iface.c
├── params_iface.h
├── registers.h
├── sysfs_iface.c
└── sysfs_iface.h
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.vscode/*
2 | *.vscode-ctags
3 | *.json
4 | *.ko
5 | *.dtbo
6 | *.symvers
7 | *.mod
8 | *.mod.c
9 | *.mod.o
10 | *.o
11 | *.log
12 | *.order
13 | *.cmd
14 | *.o.d
15 | *.tmp
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C) 2018 khawes
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | obj-m += beepy-kbd.o
2 | beepy-kbd-objs += src/main.o src/params_iface.o src/sysfs_iface.o \
3 | src/input_iface.o src/input_fw.o src/input_rtc.o src/input_display.o \
4 | src/input_modifiers.o src/input_touch.o src/input_meta.o
5 | ccflags-y := -g -std=gnu99 -Wno-declaration-after-statement
6 |
7 | .PHONY: all clean install install_modules install_aux uninstall
8 |
9 | # KERNELRELEASE is set by DKMS, can be different inside chroot
10 | ifeq ($(KERNELRELEASE),)
11 | KERNELRELEASE := $(shell uname -r)
12 | endif
13 | # LINUX_DIR is set by Buildroot
14 | ifeq ($(LINUX_DIR),)
15 | LINUX_DIR := /lib/modules/$(KERNELRELEASE)/build
16 | endif
17 |
18 | # BUILD_DIR is set by DKMS, but not if running manually
19 | ifeq ($(BUILD_DIR),)
20 | BUILD_DIR := .
21 | endif
22 |
23 | BOOT_CONFIG_LINE := dtoverlay=beepy-kbd,irq_pin=4
24 | KMAP_LINE := KMAP=/usr/share/kbd/keymaps/beepy-kbd.map
25 |
26 | # Raspbian 12 moved config and cmdline to firmware
27 | ifeq ($(wildcard /boot/firmware/config.txt),)
28 | CONFIG=/boot/config.txt
29 | else
30 | CONFIG=/boot/firmware/config.txt
31 | endif
32 |
33 | all: beepy-kbd.dtbo
34 | $(MAKE) -C '$(LINUX_DIR)' M='$(shell pwd)'
35 |
36 | beepy-kbd.dtbo: beepy-kbd.dts
37 | dtc -@ -I dts -O dtb -W no-unit_address_vs_reg -o $@ $<
38 |
39 | install_modules:
40 | $(MAKE) -C '$(LINUX_DIR)' M='$(shell pwd)' modules_install
41 | # Rebuild dependencies
42 | depmod -A
43 |
44 | install: install_modules install_aux
45 |
46 | # Separate rule to be called from DKMS
47 | install_aux: beepy-kbd.dtbo
48 | # Install keymap
49 | @mkdir -p /usr/share/kbd/keymaps/
50 | install -D -m 0644 $(BUILD_DIR)/beepy-kbd.map /usr/share/kbd/keymaps/
51 | # Install device tree overlay
52 | install -D -m 0644 $(BUILD_DIR)/beepy-kbd.dtbo /boot/overlays/
53 | # Add configuration line if it wasn't already there
54 | @grep -qxF '$(BOOT_CONFIG_LINE)' $(CONFIG) \
55 | || printf '[all]\ndtparam=i2c_arm=on\n$(BOOT_CONFIG_LINE)\n' >> $(CONFIG)
56 | # Add auto-load module line if it wasn't already there
57 | @grep -qxF 'beepy-kbd' /etc/modules \
58 | || printf 'i2c-dev\nbeepy-kbd\n' >> /etc/modules
59 | # Configure keymap as default
60 | @grep -qxF '$(KMAP_LINE)' /etc/default/keyboard \
61 | || echo '$(KMAP_LINE)' >> /etc/default/keyboard
62 | @rm -f /etc/console-setup/cached_setup_keyboard.sh
63 | @DEBIAN_FRONTEND=noninteractive dpkg-reconfigure keyboard-configuration \
64 | || echo "dpkg-reconfigure failed, keymap may not be applied"
65 |
66 | uninstall:
67 | # Remove auto-load module line and create a backup file
68 | @sed -i.save '/beepy-kbd/d' /etc/modules
69 | # Remove configuration line and create a backup file
70 | @sed -i.save '/$(BOOT_CONFIG_LINE)/d' $(CONFIG)
71 | # Remove device tree overlay
72 | rm -f /boot/overlays/beepy-kbd.dtbo
73 | # Remove keymap
74 | rm -f /usr/share/kbd/keymaps/beepy-kbd.map
75 | # Remove keymap setting
76 | @sed -i.save '\|$(KMAP_LINE)|d' /etc/default/keyboard
77 | rm -f /etc/console-setup/cached_setup_keyboard.sh
78 | @DEBIAN_FRONTEND=noninteractive dpkg-reconfigure keyboard-configuration \
79 | || echo "dpkg-reconfigure failed, old keymap may not be applied"
80 |
81 | clean:
82 | $(MAKE) -C '$(LINUX_DIR)' M='$(shell pwd)' clean
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Beepy Keyboard Driver
3 | layout: default
4 | ---
5 |
6 | # Beepy Keyboard Driver
7 |
8 | - [User Guide](#user-guide)
9 | - [Indicators](#indicators)
10 | - [Basic key mappings](#basic-key-mappings)
11 | - [Sticky modifier keys](#sticky-modifier-keys)
12 | - [Meta mode](#meta-mode)
13 | - [Touchpad mode](#touchpad-mode)
14 | - [`sysfs` interface](#sysfs-interface)
15 | - [Module parameters](#module-parameters)
16 | - [Custom keymap](#custom-keymap)
17 | - [Developer Reference](#developer-reference)
18 | - [Building from source](#building-from-source)
19 | - [Direct write firmware updates](#direct-write-firmware-updates)
20 |
21 | ## User Guide
22 |
23 | The Beepy keyboard driver, `beepy-kbd`, controls interaction between the [RP2040 firmware `beepy-fw`](beepy-fw.html) and Linux running on the Raspberry Pi. It has several responsibilities, including
24 |
25 | * Communicating keyboard and touchpad input to Linux
26 | * Power and standby management
27 | * Synchronizing the real-time clock
28 | * Firmware update management
29 |
30 | ### Indicators
31 |
32 | Due to the limited number of keys, there are different shortcuts and modes mapped by the keyboard driver. You will see different types of indicators in the top right corner of the screen depending on the mode:
33 |
34 | *
[Shift modifier](#sticky-modifier-keys)
35 | *
[Physical Alt modifier](#sticky-modifier-keys)
36 | *
[Control modifier](#basic-key-mappings)
37 | *
[Alt modifier](#key-mappings)
38 | *
[Symbol mode modifier](#alt-and-sym-modifiers)
39 | *
[Meta mode modifier](#meta-mode)
40 | *
[Touchpad mode modifier](#touchpad-mode)
41 |
42 | ### Basic key mappings
43 |
44 | From left to right, the top row of the Beepy keyboard has the following keys:
45 |
46 | * "Call", a phone facing up.
47 | * Single click: Enter Control key ([sticky modifier](#sticky-modifier-keys)).
48 | * Short hold: Lock Control key ([sticky modifier](#sticky-modifier-keys)).
49 | * "Berry": a collection of sections shaped like a fruit.
50 | * Single click: Enter [Meta mode](#meta-mode).
51 | * Short hold (1s): Display [Meta mode reference overlay](#meta-mode).
52 | * "Touchpad": pressing the touchpad sensor will click and produce a key event.
53 | * Single click: by default, enable [touchpad mode](#touchpad-mode), sending arrow keys. If touchpad mode is already on, a single click will send `Enter`.
54 | * "Back": an arrow looping back onto itself.
55 | * Single click: send `Escape`. Commonly used to exit menus in utilities.
56 | * "End Call": a phone facing down, with a line underneath.
57 | * Single click: Send the [tmux prefix](beepy-tmux-menus.html). Prefix is [configurable in the driver keymap](#custom-keymaps).
58 | * Short hold (1s): Open the [tmux menu](beepy-tmux-menus.html). If the Pi has been shut down, a short hold will turn the Pi back on.
59 | * Long hold (5s): send a shutdown signal to the Pi.
60 |
61 | The alternate symbols printed directly on the keys are sent by pressing the `Physical Alt` key on the bottom left corner of the keyboard, then pressing the key on which the desired symbol is printed. While `Physical Alt` is active, you will see an `a` indicator in the top right corner of the screen:
. The combination `Physical Alt` + `Enter` is also mapped to `Tab`. `Physical Alt` is a "[sticky modifier key](#sticky-modifier-keys)".
62 |
63 | For additional symbols not printed directly on the keys, use the `Symbol` key on the bottom row of the keyboard. While `Symbol` is active, you will see an `S` indicator in the top right of the screen:
. Internally, `Symbol` sends AltGr (Right Alt), which is mapped to more symbols via the keymap file at `/usr/share/kbd/keymaps/beepy-kbd.map`. `Symbol` is a "[sticky modifier key](#sticky-modifier-keys)". You can view the Symbol key map by holding the `Symbol` key for 1 second. Modifying the keymap file will also update the Symbol key map displayed; below is the default key map:
64 |
65 | 
66 |
67 | Arrow keys and other movement keys such as Page Up and Page Down are accessible in [Meta mode](#meta-mode).
68 |
69 | ### Sticky modifier keys
70 |
71 | For easier typing, the keyboard driver implements sticky modifier keys. Pressing and releasing a modifier applies the modifier to the next alpha keypress only. If the same modifier key is pressed and released again, it will be canceled.
72 |
73 | Holding a modifier key while typing an alpha key will apply the modifier to all alpha keys until the modifier is released.
74 |
75 | While a modifier key is active, [visual indicators](#indicators) are drawn in the top right corner of the display, with indicators for
[Shift](#sticky-modifier-keys),
[Physical Alt](#sticky-modifier-keys),
[Control](#basic-key-mappings),
[Alt](#key-mappings),
[Symbol](#alt-and-sym-modifiers),
[Meta mode](#meta-mode), and
[Touchpad mode](#touchpad-mode).
76 |
77 | For example, to type a dot `.`, you can use `Physical Alt` sticky mode:
78 |
79 | * Press and release the `Physical Alt` key
80 | * The `a` indicator
appears, showing that `Physical Alt` mode will be applied to the next keypress only
81 | * Type the `M` key to input a dot `.`
82 | * The `a` indicator disappears
83 |
84 | Alternatively, to type a dot `.` followed by a comma `,`, you can also press and hold `Physical Alt`:
85 |
86 | * Press the `Physical Alt` key
87 | * The `a` indicator
appears, showing that `Physical Alt` mode is active
88 | * While holding `Physical Alt`, type the `M` key to input a dot `.`
89 | * While holding `Physical Alt`, type the `N` key to input a comma `,`
90 | * Release the `Physical Alt` key
91 | * The `a` indicator disappears
92 |
93 | ### Meta mode
94 |
95 | Meta mode is a modal layer that assists in rapidly moving the cursor and scrolling with single keypresses. To enter Meta mode, click the `Berry` key once. The Meta mode indicator
will appear in the top right corner of the screen, and the following keymap will be applied until Meta mode is dismissed using the `Back` key, or otherwise noted:
96 |
97 | - `E` Up `S` Down `W` Left `D` Right
98 | - Why not `WASD`? This way, you can place your thumb in the middle of all four of these keys, and fluidly move the cursor without mistyping on e.g. `Q` or `E`
99 | - `R` Home `F` End `O` Page Up `P` Page Down
100 | - `Q` Back one word (`Alt + Left`) `A` Forward one word (`Alt + Right`)
101 | - `T` Tab (dismisses Meta mode)
102 | - `X` Apply `Control` to next key (dismisses Meta mode)
103 | - `C` Apply `Alt` to next key (dismisses Meta mode)
104 | - `0` Toggle display inversion from white-on-black to black-on-white
105 | - `N` Decrease keyboard brightness
106 | - `M` Increase keyboard brightness
107 | - `$` Toggle keyboard backlight
108 | - `Back` Exit Meta mode
109 |
110 | Typing any other key while in Meta mode will exit Meta mode and send the key as if it was typed normally.
111 |
112 | You can view the Meta mode key map by holding the `Berry` key for 1 second:
113 |
114 | 
115 |
116 | ### Touchpad mode
117 |
118 | The Beepy touchpad is not actually touch sensitive, rather it is an optical trackpad. This has the downside of sending touchpad input if material other than a finger moves across it, such as a pocket. To reduce false positives, the default mode of the Beepy touchpad is to remain off until a key is used to turn it on. Touchpad behavior, including mouse and activation, can be configured via [module parameters](#module-parameters).
119 |
120 | Press the touchpad itself to turn on touchpad mode, and start sending arrow keys when you move your finger across the touchpad. While active, you will see the touchpad indicator
in the top-right corner of the screen.
121 |
122 | Clicking the touchpad itself again while the touchpad is active will send `Enter`. Pressing the `Back` key will exit touchpad mode.
123 |
124 | You can also hold the `Shift` key to temporarily turn on the touchpad until the `Shift` key is released. You will see the Shift indicator
instead of the touch indicator.
125 |
126 | If you release the `Shift` key *without* using the touchpad, you will instead get the [sticky modifier behavior](#sticky-modifier-keys) of applying Shift to the next alpha keypress. In this case, the Shift indicator will remain on the screen. Press and release the `Shift` key again to un-stick the modifier and hide the indicator.
127 |
128 | ### `sysfs` interface
129 |
130 | The keyboard driver creates several sysfs entries under `/sys/firmware/beepy` to expose different parts of the firmware. These entries can be manipulated like a normal file using traditional Unix tools such as `cat` and `tee`, and in shell scripts.
131 |
132 | * `battery_raw` Raw ADC output value for the battery level. Read-only.
133 | * `battery_volts` Approximate battery voltage. Read-only.
134 | * `battery_percent` Approximate battery percentage remaining. Read-only.
135 | * `led_red`, `led_green`, `led_blue` set LED color intensity from 0 to 255. Apply by writing to `led`. Write-only.
136 | * `led`: Also applies color settings. Write-only.
137 | - `0` Turn off LED.
138 | - `1` Turn on LED.
139 | - `2` Flash LED.
140 | - `3` Flash LED until key pressed. Overlays on top of an existing LED setting. For example, the following write sequence will set the LED to be solid red, but with a blue flash. Then, when a key is pressed, it will return to a solid red.
141 |
142 | # Set LED to solid red
143 | echo 255 | sudo tee /sys/firmware/beepy/led_red
144 | echo 0 | sudo tee /sys/firmware/beepy/led_green
145 | echo 0 | sudo tee /sys/firmware/beepy/led_blue
146 | echo 1 | sudo tee /sys/firmware/beepy/led
147 |
148 | # Set LED to flash blue until key pressed
149 | echo 0 | sudo tee /sys/firmware/beepy/led_red
150 | echo 0 | sudo tee /sys/firmware/beepy/led_green
151 | echo 255 | sudo tee /sys/firmware/beepy/led_blue
152 | echo 3 | sudo tee /sys/firmware/beepy/led
153 |
154 | * `keyboard_backlight` Set keyboard brightness from 0 to 255. Write-only. The default brightness is low, but still on. Setting the brightness to maximum will noticeably increase power draw.
155 | * `rewake_timer` Write to shut down the Pi, then power-on in that many minutes. Write-only. Useful for polling services in conjunction with `startup_reason`, such as with the [beepy-poll](beepy-poll.html) service.
156 | * `startup_reason` Contains the reason why the Pi was booted. Useful for polling services in conjunction with `rewake_timer`.
157 | - `fw_init` RP2040 initialized and booted Pi.
158 | - `power_button` Power button held to turn Pi back on.
159 | - `rewake` Rewake triggered from `rewake_timer`.
160 | - `rewake_canceled` During rewake polling, 0 was written to `rewake_timer`. This allows the `beepy-poll` service to cancel the poll and proceeded with a full boot.
161 | * `fw_version` Installed firmware version. Read-only.
162 | * `fw_update` Write to update firmware. Read-write See [Firmware updates](#firmware-updates).
163 | * `last_keypress` Milliseconds since last keypress. Read-only.
164 |
165 | ### Module parameters
166 |
167 | Configure various aspects of the driver itself. Write to `/sys/module/beepy_kbd/parameters/` to set with `echo | sudo tee /sys/module/beepy_kbd/parameters/`. Or unload and reload the module with `sudo modprobe -r beepy-kbd; sudo modprobe beepy-kbd param=val`.
168 |
169 | * `touch_act` One of `click` or `always`.
170 | - `click` Default, will disable touchpad until the touchpad button is clicked.
171 | - `always` Touchpad always on, swiping sends touch input, clicking sends `Enter`.
172 | * `touch_as`: one of `keys` or `mouse`.
173 | - `keys` Default, send arrow keys with the touchpad.
174 | - `mouse` Send mouse input (useful for X11).
175 | * `touch_shift` Default on. Send touch input while the Shift key is held.
176 | * `touch_min_squal` Reject touchpad input if surface quality as reported by touchpad sensor is lower than this threshold. Default `16`.
177 | * `touch_led_setting` One of `low`, `med`, `high`. Touchpad LED power setting. `high` is recommended for reliable input. Default `high`.
178 | * `touch_threshold` Touchpad movement amount required to send arrow key. Range `0 - 255`, default `8`.
179 | - `shutdown_grace` To avoid powering off the Pi while it is still running, this is set to the number of seconds to wait between a shutdown signal and the firmware removing power from the Pi. This helps ensure that the Pi has time to process the power-off command and to shut down cleanly. Default `30` seconds.
180 | - `auto_off` In most cases, the keyboard driver is loaded on boot and unloaded during shutdown. For substantial power savings, the default-enabled `auto_off` setting will trigger when the driver is unloaded. After a 30 second wait to allow for the driver to potentially be reloaded, the Pi will shutdown, wait for `shutdown_grace` seconds, then power off the Pi and enter deep sleep. Default on.
181 | * `sharp_path` Sharp DRM device to send overlay commands. Default: `/dev/dri/card0`.
182 | * `sysfs_gid` Group ID of sysfs entries in `/sys/firmware/beepy`. Set this to the result of `id -g` to allow access without `sudo`. Beepy Raspbian configures the first user group by default.
183 | * `handle_poweroff` Enable to have driver invoke `/sbin/poweroff` when power key held. not necessary for Beepy Raspbian, may be necessary if running a custom build of the driver on another Linux distribution. Default off.
184 |
185 | ### Custom keymap
186 |
187 | The `Physical Alt` and `Symbol` keymaps and the Tmux prefix sent by the `Berry` key can be edited in the file `/usr/share/kbd/keymaps/beepy-kbd.map`. To reapply the keymap without rebooting, run `loadkeys /usr/share/kbd/keymaps/beepy-kbd.map`.
188 |
189 | Holding the `Symbol` key to display the keymap will display the keymap directly from this file.
190 |
191 | ## Developer Reference
192 |
193 | ### Building from source
194 |
195 | * Install build prerequisites.
196 |
197 | sudo apt-get install raspberrypi-kernel-headers
198 |
199 | * Enable I2C Peripheral. Run `sudo raspi-config`, then under `Interface Options`, enable `I2C`.
200 |
201 | * Build, install, and enable the kernel module.
202 |
203 | make
204 | sudo make install
205 |
206 | To remove, run `sudo make uninstall`, then verify that `/etc/modules` and `/boot/firmware/config.txt` have removed the driver lines.
207 |
208 | ### Direct write firmware updates
209 |
210 | In most cases, you will use the [`update-beepy-fw` utility](beepy-fw.html#firmware-update-utility) to update firmware from a firmware package or file. However, you can also manually write firmware update data to the sysfs entry at `/sys/firmware/beepy/fw_update`.
211 |
212 | RP2040 firmware is loaded in two stages. The first stage is a modified version of [pico-flashloader](https://github.com/rhulme/pico-flashloader). It allows updates to be flashed to the second stage firmware while booted. The second stage is the actual Beepy firmware.
213 |
214 | Firmware updates are flashed by writing to `/sys/firmware/beepy/fw_update`:
215 |
216 | - Header line beginning with `+` e.g. `+Beepy`
217 | - Followed by the contents of an image in Intel HEX format
218 |
219 | Please wait until the system reboots on its own before removing power. If the update failed, will contain an error code and the firmware will not be modified.
220 |
221 | The header line `+...` will reset the update process, so an interrupted or failed update can be retried by restarting the firmware write.
222 |
223 | If the update completes successfully, the system will be rebooted.
224 | There is a delay configurable at `/sys/firmware/beepy/shutdown_grace` to allow the operating system to cleanly shut down before the Pi is powered off.
225 | The firmware is flashed right before the Pi boots back up, so please wait until the system reboots on its own before removing power.
226 |
--------------------------------------------------------------------------------
/beepy-kbd.dts:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0
2 | /dts-v1/;
3 | /plugin/;
4 |
5 | /{
6 | compatible = "brcm,bcm2835";
7 |
8 | fragment@0 {
9 | target = <&i2c1>;
10 |
11 | __overlay__ {
12 | #address-cells = <1>;
13 | #size-cells = <0>;
14 | beepy_kbd: beepy_kbd@1f {
15 | #address-cells = <1>;
16 | compatible = "beepy-kbd";
17 | reg = <0x1F>;
18 | irq-gpio = <&gpio 11 0x2>;
19 | interrupts = <11 2>;
20 | interrupt-parent = <&gpio>;
21 | interrupt-controller;
22 | #interrupt-cells = <1>;
23 | };
24 | };
25 | };
26 | __overrides__ {
27 | irq_pin = <&beepy_kbd>,"interrupts:0";
28 | };
29 | };
30 |
31 |
--------------------------------------------------------------------------------
/beepy-kbd.map:
--------------------------------------------------------------------------------
1 | # Reference
2 | # https://github.com/SFML/SFML/blob/master/src/SFML/Window/Unix/KeySymToUnicodeMapping.cpp
3 |
4 | # Symbol key
5 | altgr keycode 16 = asciitilde
6 | altgr keycode 17 = grave
7 | altgr keycode 18 = braceleft
8 | altgr keycode 19 = braceright
9 | altgr keycode 20 = bracketleft
10 | altgr keycode 21 = bracketright
11 | altgr keycode 22 = less
12 | altgr keycode 23 = greater
13 | altgr keycode 24 = asciicircum
14 | altgr keycode 25 = percent
15 |
16 | altgr keycode 30 = equal
17 | altgr keycode 31 = division
18 | altgr keycode 32 = plusminus
19 | altgr keycode 33 = periodcentered
20 | altgr keycode 34 = backslash
21 | altgr keycode 35 = bar
22 | altgr keycode 36 = ampersand
23 | altgr keycode 37 = quotedbl # leftdoublequotemark
24 | altgr keycode 38 = quotedbl # rightdoublequotemark
25 |
26 | altgr keycode 44 = yen
27 | altgr keycode 45 = euro
28 | altgr keycode 46 = sterling
29 | altgr keycode 47 = questiondown
30 | altgr keycode 48 = exclamdown
31 | altgr keycode 49 = guillemotleft
32 | altgr keycode 50 = guillemotright
33 |
34 | altgr keycode 113 = dollar
35 |
36 | # Press power key sends Control + this code
37 | # Use it for Tmux prefix
38 | control keycode 171 = Control_b
39 | # Short hold power key sends Tmux prefix + this key
40 | control keycode 174 = e
41 |
42 | # Meta Q / A move between words
43 | keycode 172 = F172
44 | keycode 173 = F173
45 | string F172 = "\033[1;5D"
46 | string F173 = "\033[1;5C"
47 |
48 | # Dollar key
49 | keycode 113 = dollar
50 |
51 | # Alt + enter
52 | keycode 147 = Tab
53 | # Alt + backspace
54 | keycode 133 = Delete
55 | # Alt + dollar key
56 | keycode 232 = asciitilde
57 |
58 | # Physical Alt key
59 | keycode 130 = zero
60 | keycode 135 = numbersign
61 | keycode 136 = one
62 | keycode 137 = two
63 | keycode 138 = three
64 | keycode 139 = parenleft
65 | keycode 140 = parenright
66 | keycode 141 = underscore
67 | keycode 142 = minus
68 | keycode 143 = plus
69 | keycode 144 = at
70 |
71 | keycode 149 = asterisk
72 | keycode 150 = four
73 | keycode 151 = five
74 | keycode 152 = six
75 | keycode 153 = slash
76 | keycode 154 = colon
77 | keycode 155 = semicolon
78 | keycode 156 = apostrophe
79 | keycode 157 = quotedbl
80 |
81 | keycode 163 = seven
82 | keycode 164 = eight
83 | keycode 165 = nine
84 | keycode 166 = question
85 | keycode 167 = exclam
86 | keycode 168 = comma
87 | keycode 169 = period
88 |
--------------------------------------------------------------------------------
/src/bbq10kbd_featherwing_codes.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * bbq10kbd_codes.h: Keyboard Layout and Scancodes-Keycodes mapping.
5 | */
6 |
7 | #ifndef BBQ10KBD_FEATHERWING_CODES_H_
8 | #define BBQ10KBD_FEATHERWING_CODES_H_
9 |
10 | /*
11 | * BBQ10KBD FEATHERWING KEYBOARD LAYOUT
12 | *
13 | * +------+-----+----+----+----+----+----+-----+-----+-------+
14 | * | | | UP | | |
15 | * | Ctrl | PgDn | LEFT HOME RIGHT | PgUp | MENU |
16 | * | | | DOWN | | |
17 | * +------+-----+----+----+----+----+----+-----+-----+-------+
18 | * | |
19 | * +------+-----+----+----+----+----+----+-----+-----+-------+
20 | * |# |1 |2 |3 |( | )|_ | -| +| @|
21 | * | Q | W | E | R | T | Y | U | I | O | P |
22 | * | S+| S-|PgDn|PgUp| \|UP |^ |= |{ |} |
23 | * +------+-----+----+----+----+----+----+-----+-----+-------+
24 | * |* |4 |5 |6 |/ | :|; | '| "| ESC|
25 | * | A | S | D | F | G | H | J | K | L | BKSP |
26 | * | ?| Sx| [| ]|LEFT|HOME|RGHT|V+ |V- |DLT |
27 | * +------+-----+----+----+----+----+----+-----+-----+-------+
28 | * | |7 |8 |9 |? | !|, | .| `| |
29 | * |LFTALT| Z | X | C | V | B | N | M | $ | ENTER |
30 | * | | K+| K-| °| <|DOWN|> |MENU |Vx | |
31 | * +------+-----+----+----+----+----+----+-----+-----+-------+
32 | * | |0 |TAB | | |
33 | * | LEFT_SHIFT | ~ | SPACE |RTALT| RIGHT_SHIFT |
34 | * | | Kx| &| | |
35 | * +------------+----+-------------------+-----+-------------+
36 | *
37 | * Notes:
38 | * Most Important to know:
39 | * To present a character as mentioned above a key in the above layout, use LFTALT + Key.
40 | * To present a character as mentioned below a key in the above layout, use RTALT + Key.
41 | *
42 | * Changes from arturo182's layout, if you are used to that, it's good to know these changes.
43 | * 1. Sym Key (Scancode 0x1D) on Keyboard is mapped to RTALT.
44 | * 3. The external buttons on the Keyboard Featherwing have been changed as below:
45 | *__3.1 Leftmost Button changed from Menu Key to Left Ctrl Key.
46 | *__3.2 Innet Left Button changed from Left Ctrl Key to Page Up Key.
47 | *__3.3 Inner Right Button changed from Back Key to Page Down Key.
48 | *__3.4 Outer Right Button changed from Left Shift Key to Menu Key.
49 | *__3.5 Center Key of 5-way Button changed from Enter Key to Home Key.
50 | */
51 |
52 |
53 | #define NUM_KEYCODES 256
54 |
55 | static unsigned short keycodes[NUM_KEYCODES] = {
56 | [0x01] = KEY_UP,
57 | [0x02] = KEY_DOWN,
58 | [0x03] = KEY_LEFT,
59 | [0x04] = KEY_RIGHT,
60 | [0x05] = KEY_HOME,
61 |
62 | [0x06] = KEY_LEFTCTRL,
63 | [0x11] = KEY_PAGEDOWN,
64 | [0x07] = KEY_PAGEUP,
65 | [0x12] = KEY_MENU,
66 |
67 | [0x1A] = KEY_LEFTALT,
68 | [0x1B] = KEY_LEFTSHIFT,
69 | [0x1D] = KEY_RIGHTALT,
70 | [0x1C] = KEY_RIGHTSHIFT,
71 |
72 | ['A'] = KEY_A,
73 | ['B'] = KEY_B,
74 | ['C'] = KEY_C,
75 | ['D'] = KEY_D,
76 | ['E'] = KEY_E,
77 | ['F'] = KEY_F,
78 | ['G'] = KEY_G,
79 | ['H'] = KEY_H,
80 | ['I'] = KEY_I,
81 | ['J'] = KEY_J,
82 | ['K'] = KEY_K,
83 | ['L'] = KEY_L,
84 | ['M'] = KEY_M,
85 | ['N'] = KEY_N,
86 | ['O'] = KEY_O,
87 | ['P'] = KEY_P,
88 | ['Q'] = KEY_Q,
89 | ['R'] = KEY_R,
90 | ['S'] = KEY_S,
91 | ['T'] = KEY_T,
92 | ['U'] = KEY_U,
93 | ['V'] = KEY_V,
94 | ['W'] = KEY_W,
95 | ['X'] = KEY_X,
96 | ['Y'] = KEY_Y,
97 | ['Z'] = KEY_Z,
98 |
99 | [' '] = KEY_SPACE,
100 | ['~'] = KEY_0,
101 | ['$'] = KEY_GRAVE,
102 |
103 | ['\b'] = KEY_BACKSPACE,
104 | ['\n'] = KEY_ENTER,
105 | /*
106 | * As per the kernel, a keyboard needs to indicate, in advance, which key values it can report.
107 | * In order to that, it should have unique scancodes pointing those scancode-keycode pairs.
108 | * With the configuration set for now, the keyboard never outputs lower case letters, numbers, and equal to sign.
109 | * We can use these as bogus scancodes, and map the keys we want the keyboard to say its pressed when modifier keys are used.
110 | * This can change however, in case future software versions of the keyboard micrcontroller itself changes to output other stuff.
111 | */
112 | ['1'] = KEY_1,
113 | ['2'] = KEY_2,
114 | ['3'] = KEY_3,
115 | ['4'] = KEY_4,
116 | ['5'] = KEY_5,
117 | ['6'] = KEY_6,
118 | ['7'] = KEY_7,
119 | ['8'] = KEY_8,
120 | ['9'] = KEY_9,
121 | ['a'] = KEY_VOLUMEDOWN,
122 | ['b'] = KEY_VOLUMEUP,
123 | ['c'] = KEY_MUTE,
124 | ['d'] = KEY_TAB,
125 | ['e'] = KEY_DELETE,
126 | ['f'] = KEY_ESC,
127 | ['='] = KEY_EQUAL,
128 | };
129 |
130 |
131 | #endif
132 |
--------------------------------------------------------------------------------
/src/bbq10kbd_pmod_codes.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * bbq10kbd_codes.h: Keyboard Layout and Scancodes-Keycodes mapping.
5 | */
6 |
7 | #ifndef BBQ10KBD_PMOD_CODES_H_
8 | #define BBQ10KBD_PMOD_CODES_H_
9 |
10 | /*
11 | * BBQ10KBD PMOD KEYBOARD LAYOUT
12 | *
13 | * +------+-----+----+----+----+----+----+-----+-----+-------+
14 | * |# |1 |2 |3 |( | )|_ | -| +| @|
15 | * | Q | W | E | R | T | Y | U | I | O | P |
16 | * | S+| S-|PgUp|PgDn| \|UP |^ |= |{ |} |
17 | * +------+-----+----+----+----+----+----+-----+-----+-------+
18 | * |* |4 |5 |6 |/ | :|; | '| "| ESC|
19 | * | A | S | D | F | G | H | J | K | L | BKSP |
20 | * | ?| Sx| [| ]|LEFT|HOME|RGHT|V+ |V- |DLT |
21 | * +------+-----+----+----+----+----+----+-----+-----+-------+
22 | * | |7 |8 |9 |? | !|, | .| `| |
23 | * |LFTALT| Z | X | C | V | B | N | M | $ | ENTER |
24 | * | | K+| K-| °| <|DOWN|> |MENU |Vx | |
25 | * +------+-----+----+----+----+----+----+-----+-----+-------+
26 | * | |0 |TAO | | |
27 | * | LEFT_SHIFT | ~ | SPACE |RTALT| RIGHT_SHIFT |
28 | * | | Kx| &| | |
29 | * +------------+----+-------------------+-----+-------------+
30 | *
31 | * Notes:
32 | * Most Important to know:
33 | * To present a character as mentioned above a key in the above layout, use LFTALT + Key.
34 | * To present a character as mentioned below a key in the above layout, use RTALT + Key.
35 | *
36 | * Changes from arturo182's layout, if you are used to that, it's good to know these changes.
37 | * 1. Sym Key (Scancode 0x1D) on Keyboard is mapped to RTALT.
38 | * 2. Note "|" above Enter Key as per original keyboard layout is removed and added under "A"
39 | * 3. The external buttons on the Keyboard Featherwing have been changed as below:
40 | *__3.1 Leftmost Button changed from Menu Key to Left Ctrl Key.
41 | *__3.2 Innet Left Button changed from Left Ctrl Key to Page Up Key.
42 | *__3.3 Inner Right Button changed from Back Key to Page Down Key.
43 | *__3.4 Outer Right Button changed from Left Shift Key to Menu Key.
44 | *__3.5 Center Key of 5-way Button changed from Enter Key to Home Key.
45 | */
46 |
47 |
48 | #define NUM_KEYCODES 256
49 |
50 | static unsigned short keycodes[NUM_KEYCODES] = {
51 |
52 |
53 | [0x1A] = KEY_LEFTALT,
54 | [0x1B] = KEY_LEFTSHIFT,
55 | [0x1D] = KEY_RIGHTALT,
56 | [0x1C] = KEY_RIGHTSHIFT,
57 |
58 | ['A'] = KEY_A,
59 | ['B'] = KEY_B,
60 | ['C'] = KEY_C,
61 | ['D'] = KEY_D,
62 | ['E'] = KEY_E,
63 | ['F'] = KEY_F,
64 | ['G'] = KEY_G,
65 | ['H'] = KEY_H,
66 | ['I'] = KEY_I,
67 | ['J'] = KEY_J,
68 | ['K'] = KEY_K,
69 | ['L'] = KEY_L,
70 | ['M'] = KEY_M,
71 | ['N'] = KEY_N,
72 | ['O'] = KEY_O,
73 | ['P'] = KEY_P,
74 | ['Q'] = KEY_Q,
75 | ['R'] = KEY_R,
76 | ['S'] = KEY_S,
77 | ['T'] = KEY_T,
78 | ['U'] = KEY_U,
79 | ['V'] = KEY_V,
80 | ['W'] = KEY_W,
81 | ['X'] = KEY_X,
82 | ['Y'] = KEY_Y,
83 | ['Z'] = KEY_Z,
84 |
85 | [' '] = KEY_SPACE,
86 | ['~'] = KEY_0,
87 | ['$'] = KEY_GRAVE,
88 |
89 | ['\b'] = KEY_BACKSPACE,
90 | ['\n'] = KEY_ENTER,
91 | /*
92 | * As per the kernel, a keyboard needs to indicate, in advance, which key values it can report.
93 | * In order to that, it should have unique scancodes pointing those scancode-keycode pairs.
94 | * With the configuration set for now, the keyboard never outputs lower case letters, numbers, and equal to sign.
95 | * We can use these as bogus scancodes, and map the keys we want the keyboard to say its pressed when modifier keys are used.
96 | * This can change however, in case future software versions of the keyboard micrcontroller itself changes to output other stuff.
97 | */
98 | ['1'] = KEY_1,
99 | ['2'] = KEY_2,
100 | ['3'] = KEY_3,
101 | ['4'] = KEY_4,
102 | ['5'] = KEY_5,
103 | ['6'] = KEY_6,
104 | ['7'] = KEY_7,
105 | ['8'] = KEY_8,
106 | ['9'] = KEY_9,
107 | ['a'] = KEY_VOLUMEDOWN,
108 | ['b'] = KEY_VOLUMEUP,
109 | ['c'] = KEY_MUTE,
110 | ['d'] = KEY_TAB,
111 | ['e'] = KEY_DELETE,
112 | ['f'] = KEY_ESC,
113 | ['='] = KEY_EQUAL,
114 | ['g'] = KEY_UP,
115 | ['h'] = KEY_DOWN,
116 | ['i'] = KEY_LEFT,
117 | ['j'] = KEY_RIGHT,
118 | ['k'] = KEY_HOME,
119 | ['m'] = KEY_PAGEUP,
120 | ['n'] = KEY_PAGEDOWN,
121 | };
122 |
123 |
124 | #endif
125 |
--------------------------------------------------------------------------------
/src/bbq20kbd_pmod_codes.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * bbq10kbd_codes.h: Keyboard Layout and Scancodes-Keycodes mapping.
5 | */
6 |
7 | #ifndef BBQ20KBD_PMOD_CODES_H_
8 | #define BBQ20KBD_PMOD_CODES_H_
9 |
10 | #include
11 |
12 | #define NUM_KEYCODES 256
13 |
14 | static const uint8_t keycodes[NUM_KEYCODES] = {
15 |
16 | // Map USB HID scancodes to Linux keycode
17 | [4] = KEY_A,
18 | [5] = KEY_B,
19 | [6] = KEY_C,
20 | [7] = KEY_D,
21 | [8] = KEY_E,
22 | [9] = KEY_F,
23 | [10] = KEY_G,
24 | [11] = KEY_H,
25 | [12] = KEY_I,
26 | [13] = KEY_J,
27 | [14] = KEY_K,
28 | [15] = KEY_L,
29 | [16] = KEY_M,
30 | [17] = KEY_N,
31 | [18] = KEY_O,
32 | [19] = KEY_P,
33 | [20] = KEY_Q,
34 | [21] = KEY_R,
35 | [22] = KEY_S,
36 | [23] = KEY_T,
37 | [24] = KEY_U,
38 | [25] = KEY_V,
39 | [26] = KEY_W,
40 | [27] = KEY_X,
41 | [28] = KEY_Y,
42 | [29] = KEY_Z,
43 | [30] = KEY_1,
44 | [31] = KEY_2,
45 | [33] = KEY_4,
46 | [34] = KEY_5,
47 | [35] = KEY_6,
48 | [36] = KEY_7,
49 | [37] = KEY_8,
50 | [38] = KEY_9,
51 | [39] = KEY_0,
52 | [40] = KEY_ENTER,
53 | [41] = KEY_ESC,
54 | [42] = KEY_BACKSPACE,
55 | [43] = KEY_TAB,
56 | [44] = KEY_SPACE,
57 | [45] = KEY_MINUS,
58 | [46] = KEY_EQUAL,
59 | [47] = KEY_LEFTBRACE,
60 | [48] = KEY_RIGHTBRACE,
61 | [49] = KEY_BACKSLASH,
62 | // No HASHTILDE
63 | [51] = KEY_SEMICOLON,
64 | [52] = KEY_APOSTROPHE,
65 | [53] = KEY_GRAVE,
66 | [54] = KEY_COMMA,
67 | [55] = KEY_DOT,
68 | [56] = KEY_SLASH,
69 | [57] = KEY_CAPSLOCK,
70 | [58] = KEY_F1,
71 | [59] = KEY_F2,
72 | [60] = KEY_F3,
73 | [61] = KEY_F4,
74 | [62] = KEY_F5,
75 | [63] = KEY_F6,
76 | [64] = KEY_F7,
77 | [65] = KEY_F8,
78 | [66] = KEY_F9,
79 | [67] = KEY_F10,
80 | [68] = KEY_F11,
81 | [69] = KEY_F12,
82 | [70] = KEY_SYSRQ,
83 | [71] = KEY_SCROLLLOCK,
84 | [72] = KEY_PAUSE,
85 | [73] = KEY_INSERT,
86 | [74] = KEY_HOME,
87 | [75] = KEY_PAGEUP,
88 | [76] = KEY_DELETE,
89 | [77] = KEY_END,
90 | [78] = KEY_PAGEDOWN,
91 | [79] = KEY_RIGHT,
92 | [80] = KEY_LEFT,
93 | [81] = KEY_DOWN,
94 | [82] = KEY_UP,
95 | [83] = KEY_NUMLOCK,
96 | [84] = KEY_KPSLASH,
97 | [85] = KEY_KPASTERISK,
98 | [86] = KEY_KPMINUS,
99 | [87] = KEY_KPPLUS,
100 | [88] = KEY_KPENTER,
101 | [89] = KEY_KP1,
102 | [90] = KEY_KP2,
103 | [91] = KEY_KP3,
104 | [92] = KEY_KP4,
105 | [93] = KEY_KP5,
106 | [94] = KEY_KP6,
107 | [95] = KEY_KP7,
108 | [96] = KEY_KP8,
109 | [97] = KEY_KP9,
110 | [98] = KEY_KP0,
111 | [99] = KEY_KPDOT,
112 | [100] = KEY_102ND,
113 | [101] = KEY_COMPOSE,
114 | [102] = KEY_POWER,
115 | [103] = KEY_KPEQUAL,
116 | [104] = KEY_F13,
117 | [105] = KEY_F14,
118 | [106] = KEY_F15,
119 | [107] = KEY_F16,
120 | [108] = KEY_F17,
121 | [109] = KEY_F18,
122 | [110] = KEY_F19,
123 | [111] = KEY_F20,
124 | [112] = KEY_F21,
125 | [113] = KEY_F22,
126 | [114] = KEY_F23,
127 | [115] = KEY_F24,
128 | [116] = KEY_OPEN,
129 | [117] = KEY_HELP,
130 | [118] = KEY_PROPS,
131 | [119] = KEY_FRONT,
132 | [120] = KEY_STOP,
133 | [121] = KEY_AGAIN,
134 | [122] = KEY_UNDO,
135 | [123] = KEY_CUT,
136 | [124] = KEY_COPY,
137 | [125] = KEY_PASTE,
138 | [126] = KEY_FIND,
139 | [127] = KEY_MUTE,
140 | [128] = KEY_VOLUMEUP,
141 | [129] = KEY_VOLUMEDOWN,
142 | [133] = KEY_KPCOMMA,
143 | [182] = KEY_KPLEFTPAREN,
144 | [183] = KEY_KPRIGHTPAREN,
145 | [224] = KEY_LEFTCTRL,
146 | [225] = KEY_LEFTSHIFT,
147 | [226] = KEY_LEFTALT,
148 | [227] = KEY_LEFTMETA,
149 | [228] = KEY_RIGHTCTRL,
150 | [229] = KEY_RIGHTSHIFT,
151 | [230] = KEY_RIGHTALT,
152 | [231] = KEY_RIGHTMETA,
153 |
154 | /*
155 | * As per the kernel, a keyboard needs to indicate, in advance, which key values it can report.
156 | * In order to that, it should have unique scancodes pointing those scancode-keycode pairs.
157 | * With the configuration set for now, the keyboard never outputs lower case letters, numbers, and equal to sign.
158 | * We can use these as bogus scancodes, and map the keys we want the keyboard to say its pressed when modifier keys are used.
159 | * This can change however, in case future software versions of the keyboard micrcontroller itself changes to output other stuff.
160 | */
161 |
162 | // These values are not expected to be generated by a keypress, but
163 | // reported to the input system by alternate modifiers such as the
164 | // physical Alt key.
165 | // If there were more than AltGr available in the Linux keymap for
166 | // modifiers, we wouldn't have to do this. Customize the actual
167 | // key for these values in the keymap file.
168 | // This is the largest contiguous gap in the HID -> Linux input code
169 | // list. HID scancodes on the list below are not safe to send from
170 | // the keyboard firmware, they will get converted by the driver
171 | // to alternate keycodes.
172 |
173 | // Produced by firmware for phys. Alt + key
174 | [135] = 135,
175 | [136] = 136,
176 | [137] = 137,
177 | [138] = 138,
178 | [139] = 139,
179 | [140] = 140,
180 | [141] = 141,
181 | [142] = 142,
182 | [143] = 143,
183 | [144] = 144,
184 | [145] = 145,
185 | [146] = 146,
186 | [147] = 147,
187 | [148] = 148,
188 | [149] = 149,
189 | [150] = 150,
190 | [151] = 151,
191 | [152] = 152,
192 | [153] = 153,
193 | [154] = 154,
194 | [155] = 155,
195 | [156] = 156,
196 | [157] = 157,
197 | [158] = 158,
198 | [159] = 159,
199 | [160] = 160,
200 | [161] = 161,
201 | [162] = 162,
202 | [163] = 163,
203 | [164] = 164,
204 | [165] = 165,
205 | [166] = 166,
206 | [167] = 167,
207 | [168] = 168,
208 | [169] = 169,
209 | [170] = 170,
210 | [171] = 171,
211 | [172] = 172,
212 | [173] = 173,
213 | [174] = 174,
214 | [175] = 175,
215 | [176] = 176,
216 | [177] = 177,
217 | [178] = 178,
218 | [179] = 179,
219 | [180] = 180,
220 | [181] = 181,
221 | [232] = 232,
222 | };
223 |
224 | #endif
225 |
--------------------------------------------------------------------------------
/src/bbqX0kbd_registers.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * bbq10kbd_registers.h: Registers in BBQ10 Keyboard Software.
5 | */
6 |
7 | #ifndef BBQX0KBD_REGISTERS_H_
8 | #define BBQX0KBD_REGISTERS_H_
9 |
10 | #include "config.h"
11 |
12 |
13 | #define BBQX0KBD_I2C_ADDRESS BBQX0KBD_ASSIGNED_I2C_ADDRESS
14 |
15 | #define BBQX0KBD_WRITE_MASK 0x80
16 | #define BBQX0KBD_FIFO_SIZE 31
17 |
18 | #define REG_VER 0x01
19 | #if (BBQX0KBD_TYPE == BBQ10KBD_FEATHERWING)
20 | #define BBQX0KBD_I2C_SW_VERSION 0x04 // Unused for now since the version numbering has reset.
21 | #endif
22 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
23 | #define BBQX0KBD_I2C_SW_VERSION 0x10
24 | #endif
25 | #define REG_CFG 0x02
26 | #define REG_CFG_USE_MODS BIT(7)
27 | #define REG_CFG_REPORT_MODS BIT(6)
28 | #define REG_CFG_PANIC_INT BIT(5)
29 | #define REG_CFG_KEY_INT BIT(4)
30 | #define REG_CFG_NUMLOCK_INT BIT(3)
31 | #define REG_CFG_CAPSLOCK_INT BIT(2)
32 | #define REG_CFG_OVERFLOW_INT BIT(1)
33 | #define REG_CFG_OVERFLOW_ON BIT(0)
34 | #define REG_CFG_DEFAULT_SETTING (REG_CFG_REPORT_MODS | REG_CFG_KEY_INT | REG_CFG_OVERFLOW_ON | REG_CFG_OVERFLOW_INT)
35 | // Configurations Used By Arturo182's Code.
36 | // #define REG_CFG_DEFAULT_SETTING (REG_CFG_OVERFLOW_ON | REG_CFG_OVERFLOW_INT | REG_CFG_CAPSLOCK_INT | REG_CFG_NUMLOCK_INT | REG_CFG_KEY_INT | REG_CFG_REPORT_MODS )
37 |
38 | #define REG_INT 0x03
39 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
40 | #define REG_INT_TOUCH BIT(6)
41 | #endif
42 | #define REG_INT_GPIO BIT(5)
43 | #define REG_INT_PANIC BIT(4)
44 | #define REG_INT_KEY BIT(3)
45 | #define REG_INT_NUMLOCK BIT(2)
46 | #define REG_INT_CAPSLOCK BIT(1)
47 | #define REG_INT_OVERFLOW BIT(0)
48 | #define REG_INT_RESET_VALUE 0x00
49 |
50 | #define REG_KEY 0x04
51 | #define REG_KEY_NUMLOCK BIT(6)
52 | #define REG_KEY_CAPSLOCK BIT(5)
53 | #define REG_KEY_KEYCOUNT_MASK 0x1F
54 |
55 | #define REG_BKL 0x05
56 |
57 | #define REG_DEB 0x06
58 |
59 | #define REG_FRQ 0x07
60 |
61 | #define REG_RST 0x08
62 | #define REG_FIF 0x09
63 |
64 | #define REG_BK2 0x0A
65 |
66 | #define REG_DIR 0x0B
67 | #define REG_DIR_INPUT 1
68 | #define REG_DIR_OUTPUT 0
69 |
70 | #define REG_PUE 0x0C
71 | #define REG_PUE_ENABLE 1
72 | #define REG_PUE_DISABLE 0
73 |
74 |
75 | #define REG_PUD 0x0D
76 | #define REG_PUD_PULL_UP 1
77 | #define REG_PUD_PULL_DOWN 0
78 |
79 |
80 | #define REG_GIO 0x0E
81 |
82 | #define REG_GIC 0x0F
83 | #define REG_GIC_INTERRUPT_TRIGGER 1
84 | #define REG_GIC_NO_INTERRUPT_TRIGGER 0
85 |
86 | #define REG_GIN 0x10
87 | #define REG_GIN_RESET_VALUE 0x00
88 |
89 | #define BBQ10_BRIGHTNESS_DELTA 16
90 |
91 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
92 | #define REG_CF2 0x14
93 | #define REG_CF2_USB_MOUSE_ON BIT(2)
94 | #define REG_CF2_USB_KEYB_ON BIT(1)
95 | #define REG_CF2_TOUCH_INT BIT(0)
96 | #define REG_CFG2_DEFAULT_SETTING (REG_CF2_TOUCH_INT)
97 |
98 | #define REG_TOX 0x15
99 | #define REG_TOY 0x16
100 |
101 | #define REG_ADC 0x17
102 | #define REG_LED 0x20
103 | #define REG_LED_R 0x21
104 | #define REG_LED_G 0x22
105 | #define REG_LED_B 0x23
106 | #endif
107 |
108 |
109 | #endif
110 |
--------------------------------------------------------------------------------
/src/config.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * config.h: File to store driver configurations.
5 | */
6 |
7 | #ifndef CONFIG_H_
8 | #define CONFIG_H_
9 |
10 | #define BBQX0KBD_DEFAULT_I2C_ADDRESS 0x1F
11 | #define BBQX0KBD_ASSIGNED_I2C_ADDRESS BBQX0KBD_DEFAULT_I2C_ADDRESS
12 |
13 | #define BBQ10KBD_PMOD 0
14 | #define BBQ10KBD_FEATHERWING 1
15 | #define BBQ20KBD_PMOD 2
16 | #define BBQX0KBD_TYPE BBQ20KBD_PMOD
17 |
18 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
19 | #define BBQ20KBD_TRACKPAD_AS_MOUSE 0
20 | #define BBQ20KBD_TRACKPAD_AS_KEYS 1
21 | #define BBQ20KBD_TRACKPAD_USE BBQ20KBD_TRACKPAD_AS_KEYS
22 | #endif
23 |
24 | #define BBQX0KBD_USE_INT 0
25 | #define BBQX0KBD_NO_INT 1
26 | #define BBQX0KBD_INT BBQX0KBD_USE_INT
27 |
28 | #if (BBQX0KBD_INT == BBQX0KBD_NO_INT)
29 | #define BBQX0KBD_POLL_PERIOD 40
30 | #else
31 | #define BBQX0KBD_INT_PIN 4
32 | #endif
33 |
34 |
35 | #endif
36 |
--------------------------------------------------------------------------------
/src/debug_levels.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0-only */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * debug_levels.h: Describes the debug levels that can be set for viewing code flow in dmesg.
5 | * Use dmesg -wH | grep beepy-kbd for easy viewing.
6 | */
7 |
8 | #ifndef DEBUG_LEVELS_H_
9 | #define DEBUG_LEVELS_H_
10 |
11 | /*
12 | * Set Debug Level with DEBUG_LEVEL.
13 | * Types of Debug Messages:
14 | * 1. Functional Entries. printk() & dev_info() used to show Entries in init & exit, probe, irq, logic.
15 | * 2. Read/Write Values. dev_info() used to show Values read and written from i2c functions.
16 | * 3. Logical Debugs. dev_info() used for showing Logic flow.
17 | * For All Informations -> DEBUG_LEVEL set as (DEBUG_LEVEL_FE | DEBUG_LEVEL_RW | DEBUG_LEVEL_LD)
18 | * For Only Read/Write in I2C Client -> DEBUG_LEVEL set as (DEBUG_LEVEL_RW)
19 | * For Informations on function entires-> DEBUG_LEVEL set as (DEBUG_LEVEL_FE)
20 | * For No Debug -> DEBUG_LEVEL set as (DEBUG_LEVEL_OFF)
21 | */
22 |
23 | #define DEBUG_LEVEL_OFF 0
24 | #define DEBUG_LEVEL_FE 1
25 | #define DEBUG_LEVEL_RW 2
26 | #define DEBUG_LEVEL_LD 4
27 |
28 | #define DEBUG_LEVEL DEBUG_LEVEL_OFF
29 | // #define DEBUG_LEVEL DEBUG_LEVEL_FE
30 | // #define DEBUG_LEVEL (DEBUG_LEVEL_LD)
31 | // #define DEBUG_LEVEL (DEBUG_LEVEL_FE | DEBUG_LEVEL_RW | DEBUG_LEVEL_LD)
32 | #endif
33 |
34 | #if (DEBUG_LEVEL & DEBUG_LEVEL_FE)
35 | #define dev_info_fe(...) dev_info(__VA_ARGS__)
36 | #else
37 | #define dev_info_fe(...)
38 | #endif
39 |
40 | #if (DEBUG_LEVEL & DEBUG_LEVEL_LD)
41 | #define dev_info_ld(...) dev_info(__VA_ARGS__)
42 | #else
43 | #define dev_info_ld(...)
44 | #endif
45 |
--------------------------------------------------------------------------------
/src/i2c_helper.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 |
3 | #ifndef I2C_HELPER_H_
4 | #define I2C_HELPER_H_
5 |
6 | #include
7 |
8 | #include "config.h"
9 | #include "registers.h"
10 | #include "debug_levels.h"
11 |
12 | // Parse 0 to 255 from string
13 | static inline int parse_u8(char const* buf)
14 | {
15 | int rc, result;
16 |
17 | // Parse string value
18 | if ((rc = kstrtoint(buf, 10, &result)) || (result < 0) || (result > 0xff)) {
19 | return -EINVAL;
20 | }
21 | return result;
22 | }
23 |
24 | // Read a single uint8_t value from I2C register
25 | static inline int kbd_read_i2c_u8(struct i2c_client* i2c_client, uint8_t reg_addr,
26 | uint8_t* dst)
27 | {
28 | int reg_value;
29 |
30 | // Read value over I2C
31 | if ((reg_value = i2c_smbus_read_byte_data(i2c_client, reg_addr)) < 0) {
32 | dev_err(&i2c_client->dev,
33 | "%s Could not read from register 0x%02X, error: %d\n",
34 | __func__, reg_addr, reg_value);
35 | return reg_value;
36 | }
37 |
38 | // Assign result to buffer
39 | *dst = reg_value & 0xFF;
40 |
41 | return 0;
42 | }
43 |
44 | // Write a single uint8_t value to I2C register
45 | static inline int kbd_write_i2c_u8(struct i2c_client* i2c_client, uint8_t reg_addr,
46 | uint8_t src)
47 | {
48 | int rc;
49 |
50 | // Write value over I2C
51 | if ((rc = i2c_smbus_write_byte_data(i2c_client,
52 | reg_addr | BBQX0KBD_WRITE_MASK, src))) {
53 |
54 | dev_err(&i2c_client->dev,
55 | "%s Could not write to register 0x%02X, Error: %d\n",
56 | __func__, reg_addr, rc);
57 | return rc;
58 | }
59 |
60 | return 0;
61 | }
62 |
63 | // Read a pair of uint8_t values from I2C register
64 | static inline int kbd_read_i2c_2u8(struct i2c_client* i2c_client, uint8_t reg_addr,
65 | uint8_t* dst)
66 | {
67 | int word_value;
68 |
69 | // Read value over I2C
70 | if ((word_value = i2c_smbus_read_word_data(i2c_client, reg_addr)) < 0) {
71 | dev_err(&i2c_client->dev,
72 | "%s Could not read from register 0x%02X, error: %d\n",
73 | __func__, reg_addr, word_value);
74 | return word_value;
75 | }
76 |
77 | // Assign result to buffer
78 | *dst = (uint8_t)(word_value & 0xFF);
79 | *(dst + 1) = (uint8_t)((word_value & 0xFF00) >> 8);
80 |
81 | return 0;
82 | }
83 |
84 | #endif
85 |
--------------------------------------------------------------------------------
/src/indicators.h:
--------------------------------------------------------------------------------
1 | #ifndef INDICATORS_H_
2 | #define INDICATORS_H_
3 |
4 | #define MAX_INDICATORS 7
5 | #define INDICATOR_WIDTH 14
6 | #define INDICATOR_HEIGHT 14
7 | #define INDICATORS_WIDTH (INDICATOR_WIDTH * MAX_INDICATORS)
8 |
9 | static u8 const ind_shift[] = {
10 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
11 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
12 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
13 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
14 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
15 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
16 | 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
17 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
18 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
19 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
20 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
21 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
22 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 | };
25 | static u8 const ind_phys_alt[] = {
26 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
27 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
28 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
29 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
30 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
31 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
32 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
33 | 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
34 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
35 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
36 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
37 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
38 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
39 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
40 | };
41 | static u8 const ind_control[] = {
42 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
43 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
44 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
45 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
46 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
47 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
48 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
49 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
50 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
51 | 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
52 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
53 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
54 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
55 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 | };
57 | static u8 const ind_alt[] = {
58 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
60 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
61 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
62 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
63 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
64 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
65 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
66 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
67 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
68 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
69 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
70 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 | };
73 | static u8 const ind_altgr[] = {
74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
76 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
77 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
78 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
79 | 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
80 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
81 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
82 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
83 | 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00,
84 | 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00,
85 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
86 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88 | };
89 | static u8 const ind_meta[] = {
90 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
91 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
92 | 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
93 | 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00,
94 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
95 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
96 | 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
97 | 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00,
98 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
99 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
100 | 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00,
101 | 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0x00,
102 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 | };
105 | static u8 const ind_touch[] = {
106 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
107 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
108 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
109 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
110 | 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
111 | 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff,
112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 | 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff,
115 | 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff,
116 | 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
117 | 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
118 | 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
119 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
120 | };
121 |
122 | #endif
123 |
--------------------------------------------------------------------------------
/src/input_display.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | // Input display subsystem
3 |
4 | #include
5 |
6 | #include "config.h"
7 | #include "input_iface.h"
8 | #include "params_iface.h"
9 |
10 | #include "indicators.h"
11 |
12 | // Display ioctl
13 |
14 | #define DRM_SHARP_REDRAW 0x00
15 |
16 | // Globals
17 |
18 | static uint32_t g_mono_invert;
19 |
20 | // Loaded from display driver
21 |
22 | extern void sharp_memory_set_invert(int setting);
23 | void (*__sharp_memory_set_invert)(int setting);
24 |
25 | extern void* sharp_memory_add_overlay(int x, int y, int width, int height,
26 | unsigned char const* pixels);
27 | void* (*__sharp_memory_add_overlay)(int x, int y, int width, int height,
28 | unsigned char const* pixels);
29 | extern void sharp_memory_remove_overlay(void* entry);
30 | void (*__sharp_memory_remove_overlay)(void* entry);
31 | extern void* sharp_memory_show_overlay(void* storage);
32 | void* (*__sharp_memory_show_overlay)(void* storage);
33 | extern void sharp_memory_hide_overlay(void* display);
34 | void (*__sharp_memory_hide_overlay)(void* display);
35 | extern void sharp_memory_clear_overlays(void);
36 | void (*__sharp_memory_clear_overlays)(void);
37 |
38 | struct mod_overlay_t
39 | {
40 | void *storage;
41 | void *display;
42 | };
43 |
44 | struct mod_overlay_t g_mod_overlays[MAX_INDICATORS];
45 |
46 | // Display helpers
47 |
48 | static int ioctl_call_uint32(char const* path, unsigned int cmd, uint32_t value)
49 | {
50 | struct file *filp;
51 |
52 | // Open file
53 | if (IS_ERR((filp = filp_open(path, O_WRONLY, 0)))) {
54 | // Silently return if display driver was not loaded
55 | return 0;
56 | }
57 |
58 | filp->f_op->unlocked_ioctl(filp, cmd, value);
59 |
60 | // Close file
61 | filp_close(filp, NULL);
62 |
63 | return 0;
64 | }
65 |
66 | static int ioctl_sharp_redraw(void)
67 | {
68 | return ioctl_call_uint32(params_get_sharp_path(),
69 | DRM_IO(DRM_COMMAND_BASE + DRM_SHARP_REDRAW), 0);
70 | }
71 |
72 | // Whether this is a path to a valid Sharp display device
73 | int input_display_valid_sharp_path(char const* path)
74 | {
75 | // Try to refresh screen
76 | return ioctl_call_uint32(path,
77 | DRM_IO(DRM_COMMAND_BASE + DRM_SHARP_REDRAW), 0);
78 | }
79 |
80 | // Invert display colors by writing to display driver parameter
81 | void input_display_invert(struct kbd_ctx* ctx)
82 | {
83 | // Update saved invert value
84 | g_mono_invert = (g_mono_invert) ? 0 : 1;
85 |
86 | if (__sharp_memory_set_invert == NULL) {
87 | __sharp_memory_set_invert = symbol_get(sharp_memory_set_invert);
88 | }
89 | if (__sharp_memory_set_invert == NULL) {
90 | return;
91 | }
92 |
93 | // Apply invert value
94 | __sharp_memory_set_invert(g_mono_invert);
95 | (void)ioctl_sharp_redraw();
96 | }
97 |
98 | // Set display indicator
99 | void input_display_set_indicator(int idx, unsigned char const* pixels)
100 | {
101 | int x;
102 |
103 | if ((idx >= MAX_INDICATORS) || (pixels == NULL)) {
104 | return;
105 | }
106 |
107 | if (g_mod_overlays[idx].storage == NULL) {
108 | x = - ((idx + 1) * INDICATOR_WIDTH);
109 |
110 | // Get overlay add function
111 | if (__sharp_memory_add_overlay == NULL) {
112 | __sharp_memory_add_overlay = symbol_get(sharp_memory_add_overlay);
113 | }
114 | if (__sharp_memory_add_overlay == NULL) {
115 | return;
116 | }
117 |
118 | // Add indicator overlay
119 | g_mod_overlays[idx].storage = __sharp_memory_add_overlay(
120 | x, 0, INDICATOR_WIDTH, INDICATOR_HEIGHT, pixels);
121 | }
122 |
123 | if (g_mod_overlays[idx].display == NULL) {
124 |
125 | if (__sharp_memory_show_overlay == NULL) {
126 | __sharp_memory_show_overlay = symbol_get(sharp_memory_show_overlay);
127 | }
128 | if (__sharp_memory_show_overlay == NULL) {
129 | return;
130 | }
131 |
132 | // Display indicator overlay and set display handle
133 | g_mod_overlays[idx].display = __sharp_memory_show_overlay(
134 | g_mod_overlays[idx].storage);
135 | }
136 |
137 | // Refresh display
138 | (void)ioctl_sharp_redraw();
139 | }
140 |
141 | // Clear display indicator
142 | void input_display_clear_indicator(int idx)
143 | {
144 | if (g_mod_overlays[idx].display != NULL) {
145 |
146 | // Get overlay hide function
147 | if (__sharp_memory_hide_overlay == NULL) {
148 | __sharp_memory_hide_overlay = symbol_get(sharp_memory_hide_overlay);
149 | }
150 | if (__sharp_memory_hide_overlay == NULL) {
151 | return;
152 | }
153 |
154 | // Hide overlay and reset display handle
155 | __sharp_memory_hide_overlay(g_mod_overlays[idx].display);
156 | g_mod_overlays[idx].display = NULL;
157 | (void)ioctl_sharp_redraw();
158 | }
159 | }
160 |
161 | // Clear all overlays
162 | void input_display_clear_overlays(void)
163 | {
164 | int i;
165 |
166 | // Get overlay clear function
167 | if (__sharp_memory_clear_overlays == NULL) {
168 | __sharp_memory_clear_overlays = symbol_get(sharp_memory_clear_overlays);
169 | }
170 | if (__sharp_memory_clear_overlays == NULL) {
171 | return;
172 | }
173 |
174 | // Invalidate all overlays
175 | for (i = 0; i < MAX_INDICATORS; i++) {
176 | g_mod_overlays[i].storage = NULL;
177 | g_mod_overlays[i].display = NULL;
178 | }
179 |
180 | // Clear all overlays
181 | __sharp_memory_clear_overlays();
182 |
183 | // Refresh display
184 | (void)ioctl_sharp_redraw();
185 | }
186 |
187 | int input_display_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
188 | {
189 | g_mono_invert = 0;
190 |
191 | // Clear all overlays
192 | input_display_clear_overlays();
193 |
194 | return 0;
195 | }
196 |
197 | void input_display_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
198 | {
199 | // Clear all overlays
200 | input_display_clear_overlays();
201 | }
202 |
--------------------------------------------------------------------------------
/src/input_fw.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | // Input firmware subsystem
3 |
4 | #include
5 |
6 | #include "config.h"
7 | #include "i2c_helper.h"
8 | #include "input_iface.h"
9 |
10 | #include "bbq20kbd_pmod_codes.h"
11 |
12 | // Globals
13 | static uint8_t g_brightness;
14 | static uint8_t g_last_brightness;
15 | static uint8_t g_handle_poweroff;
16 |
17 | // Helpers
18 |
19 | static void input_fw_run_poweroff(struct kbd_ctx* ctx)
20 | {
21 | // Set LED to red
22 | kbd_write_i2c_u8(ctx->i2c_client, REG_LED_R, 0xff);
23 | kbd_write_i2c_u8(ctx->i2c_client, REG_LED_G, 0x0);
24 | kbd_write_i2c_u8(ctx->i2c_client, REG_LED_B, 0x0);
25 | kbd_write_i2c_u8(ctx->i2c_client, REG_LED, 0x1);
26 |
27 | // Run poweroff
28 | static const char * const poweroff_argv[] = {
29 | "/sbin/poweroff", "now", NULL };
30 | call_usermodehelper(poweroff_argv[0], (char**)poweroff_argv, NULL, UMH_NO_WAIT);
31 | }
32 |
33 | int input_fw_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
34 | {
35 | int rc;
36 | uint8_t reg_value;
37 |
38 | // Initialize keyboard context
39 | g_brightness = 0x10;
40 | g_last_brightness = 0x00;
41 | g_handle_poweroff = 0;
42 |
43 | // Get firmware version
44 | if (kbd_read_i2c_u8(i2c_client, REG_VER, &ctx->version_number)) {
45 | return -ENODEV;
46 | }
47 | dev_info(&i2c_client->dev,
48 | "%s BBQX0KBD Software version: 0x%02X\n", __func__,
49 | g_ctx->version_number);
50 |
51 | // Write configuration 1
52 | if (kbd_write_i2c_u8(i2c_client, REG_CFG, REG_CFG_DEFAULT_SETTING)) {
53 | return -ENODEV;
54 | }
55 |
56 | // Read back configuration 1 setting
57 | if (kbd_read_i2c_u8(i2c_client, REG_CFG, ®_value)) {
58 | return -ENODEV;
59 | }
60 | dev_info_ld(&i2c_client->dev,
61 | "%s Configuration Register Value: 0x%02X\n", __func__, reg_value);
62 |
63 | // Write configuration 2
64 | // No USB output and disable touch. Touch settings will be updated later
65 | // based on module parameters
66 | if (kbd_write_i2c_u8(i2c_client, REG_CF2, 0)) {
67 | return -ENODEV;
68 | }
69 |
70 | // Read back configuration 2 setting
71 | if (kbd_read_i2c_u8(i2c_client, REG_CF2, ®_value)) {
72 | return rc;
73 | }
74 | dev_info_ld(&i2c_client->dev,
75 | "%s Configuration 2 Register Value: 0x%02X\n",
76 | __func__, reg_value);
77 |
78 | // Update keyboard brightness
79 | (void)kbd_write_i2c_u8(i2c_client, REG_BKL, g_brightness);
80 |
81 | // Notify firmware that driver has initialized
82 | // Clear boot indicator LED
83 | (void)kbd_write_i2c_u8(i2c_client, REG_LED, 0);
84 | (void)kbd_write_i2c_u8(i2c_client, REG_DRIVER_STATE, 1);
85 |
86 | return 0;
87 | }
88 |
89 | void input_fw_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
90 | {
91 | uint8_t reg_value;
92 |
93 | dev_info_fe(&i2c_client->dev,
94 | "%s Shutting Down Keyboard And Screen Backlight.\n", __func__);
95 |
96 | // Turn off LED and notify firmware that driver has shut down
97 | (void)kbd_write_i2c_u8(i2c_client, REG_LED, 0);
98 | (void)kbd_write_i2c_u8(i2c_client, REG_DRIVER_STATE, 0);
99 |
100 | // Turn off backlight
101 | (void)kbd_write_i2c_u8(i2c_client, REG_BKL, 0);
102 |
103 | // Reenable touch events
104 | (void)kbd_write_i2c_u8(i2c_client, REG_CF2, REG_CFG2_DEFAULT_SETTING);
105 |
106 | // Read back version
107 | (void)kbd_read_i2c_u8(i2c_client, REG_VER, ®_value);
108 | }
109 |
110 | int input_fw_consumes_keycode(struct kbd_ctx* ctx,
111 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state)
112 | {
113 | // Power key runs /sbin/poweroff if `handle_poweroff` is set
114 | if (keycode == KEY_POWER) {
115 | if ((state == KEY_STATE_PRESSED) && g_handle_poweroff) {
116 | input_fw_run_poweroff(ctx);
117 | }
118 |
119 | // Allow power key to be handled by OS
120 | }
121 |
122 | return 0;
123 | }
124 |
125 | // Brightness helpers
126 |
127 | void input_fw_decrease_brightness(struct kbd_ctx* ctx)
128 | {
129 | // Decrease by delta, min at 0x0 brightness
130 | g_brightness = (g_brightness < BBQ10_BRIGHTNESS_DELTA)
131 | ? 0x0
132 | : g_brightness - BBQ10_BRIGHTNESS_DELTA;
133 |
134 | // Set backlight using I2C
135 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_BKL, g_brightness);
136 | }
137 |
138 | void input_fw_increase_brightness(struct kbd_ctx* ctx)
139 | {
140 | // Increase by delta, max at 0xff brightness
141 | g_brightness = (g_brightness > (0xff - BBQ10_BRIGHTNESS_DELTA))
142 | ? 0xff
143 | : g_brightness + BBQ10_BRIGHTNESS_DELTA;
144 |
145 | // Set backlight using I2C
146 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_BKL, g_brightness);
147 | }
148 |
149 | void input_fw_toggle_brightness(struct kbd_ctx* ctx)
150 | {
151 | // Toggle, save last brightness in context
152 | if (g_last_brightness) {
153 | g_brightness = g_last_brightness;
154 | g_last_brightness = 0;
155 | } else {
156 | g_last_brightness = g_brightness;
157 | g_brightness = 0;
158 | }
159 |
160 | // Set backlight using I2C
161 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_BKL, g_brightness);
162 | }
163 |
164 | // I2C helpers
165 |
166 | int input_fw_enable_touch_interrupts(struct kbd_ctx* ctx)
167 | {
168 | int rc;
169 | uint8_t reg_value;
170 |
171 | // Get old CF2 value
172 | if ((rc = kbd_read_i2c_u8(ctx->i2c_client, REG_CF2, ®_value))) {
173 | return rc;
174 | }
175 |
176 | // Set touch interrupt bit
177 | reg_value |= REG_CF2_TOUCH_INT;
178 |
179 | // Write new CF2 value
180 | if ((rc = kbd_write_i2c_u8(ctx->i2c_client, REG_CF2, reg_value))) {
181 | return rc;
182 | }
183 |
184 | // Set touch state
185 | ctx->raised_touch_event = 1;
186 |
187 | return 0;
188 | }
189 |
190 | int input_fw_disable_touch_interrupts(struct kbd_ctx* ctx)
191 | {
192 | int rc;
193 | uint8_t reg_value;
194 |
195 | // Get old CF2 value
196 | if ((rc = kbd_read_i2c_u8(ctx->i2c_client, REG_CF2, ®_value))) {
197 | return rc;
198 | }
199 |
200 | // Clear touch interrupt bit
201 | reg_value &= ~REG_CF2_TOUCH_INT;
202 |
203 | // Write new CF2 value
204 | if ((rc = kbd_write_i2c_u8(ctx->i2c_client, REG_CF2, reg_value))) {
205 | return rc;
206 | }
207 |
208 | // Clear touch state
209 | ctx->raised_touch_event = 0;
210 |
211 | return 0;
212 | }
213 |
214 | // Transfer from I2C FIFO to internal context FIFO
215 | void input_fw_read_fifo(struct kbd_ctx* ctx)
216 | {
217 | uint8_t fifo_idx;
218 | int rc;
219 |
220 | // Read number of FIFO items
221 | if (kbd_read_i2c_u8(ctx->i2c_client, REG_KEY, &ctx->key_fifo_count)) {
222 | return;
223 | }
224 | ctx->key_fifo_count &= REG_KEY_KEYCOUNT_MASK;
225 |
226 | // Read and transfer all FIFO items
227 | for (fifo_idx = 0; fifo_idx < ctx->key_fifo_count; fifo_idx++) {
228 |
229 | // Read 2 fifo items
230 | if ((rc = kbd_read_i2c_2u8(ctx->i2c_client, REG_FIF,
231 | (uint8_t*)&ctx->key_fifo_data[fifo_idx]))) {
232 |
233 | dev_err(&ctx->i2c_client->dev,
234 | "%s Could not read REG_FIF, Error: %d\n", __func__, rc);
235 | return;
236 | }
237 |
238 | // Advance FIFO position
239 | dev_info_fe(&ctx->i2c_client->dev,
240 | "%s %02d: 0x%02x%02x State %d Scancode %d\n",
241 | __func__, fifo_idx,
242 | ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[0],
243 | ((uint8_t*)&ctx->key_fifo_data[fifo_idx])[1],
244 | ctx->key_fifo_data[fifo_idx].state,
245 | ctx->key_fifo_data[fifo_idx].scancode);
246 | }
247 | }
248 |
249 | // RTC helpers
250 |
251 | int input_fw_get_rtc(uint8_t* year, uint8_t* mon, uint8_t* day,
252 | uint8_t* hour, uint8_t* min, uint8_t* sec)
253 | {
254 | int rc;
255 |
256 | if (!g_ctx || !g_ctx->i2c_client) {
257 | return -EAGAIN;
258 | }
259 |
260 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_YEAR, year))) {
261 | return rc;
262 | }
263 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_MON, mon))) {
264 | return rc;
265 | }
266 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_MDAY, day))) {
267 | return rc;
268 | }
269 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_HOUR, hour))) {
270 | return rc;
271 | }
272 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_MIN, min))) {
273 | return rc;
274 | }
275 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_RTC_SEC, sec))) {
276 | return rc;
277 | }
278 |
279 | return 0;
280 | }
281 |
282 | int input_fw_set_rtc(uint8_t year, uint8_t mon, uint8_t day,
283 | uint8_t hour, uint8_t min, uint8_t sec)
284 | {
285 | int rc;
286 |
287 | if (!g_ctx || !g_ctx->i2c_client) {
288 | return -EAGAIN;
289 | }
290 |
291 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_YEAR, year))) {
292 | return rc;
293 | }
294 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_MON, mon))) {
295 | return rc;
296 | }
297 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_MDAY, day))) {
298 | return rc;
299 | }
300 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_HOUR, hour))) {
301 | return rc;
302 | }
303 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_MIN, min))) {
304 | return rc;
305 | }
306 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_SEC, sec))) {
307 | return rc;
308 | }
309 | if ((rc = kbd_write_i2c_u8(g_ctx->i2c_client, REG_RTC_COMMIT, 0x1))) {
310 | return rc;
311 | }
312 |
313 | return 0;
314 | }
315 |
316 | void input_fw_set_handle_poweroff(struct kbd_ctx* ctx, uint8_t handle_poweroff)
317 | {
318 | g_handle_poweroff = handle_poweroff;
319 | }
320 |
321 | void input_fw_set_auto_off(struct kbd_ctx* ctx, uint8_t auto_off)
322 | {
323 | uint8_t reg_value;
324 |
325 | // Get original value
326 | if (kbd_read_i2c_u8(ctx->i2c_client, REG_CF2, ®_value)) {
327 | return;
328 | }
329 |
330 | // Update auto-off bit
331 | if (auto_off) {
332 | reg_value |= REG_CF2_AUTO_OFF;
333 | } else {
334 | reg_value &= ~REG_CF2_AUTO_OFF;
335 | }
336 |
337 | // Update value
338 | if (kbd_write_i2c_u8(ctx->i2c_client, REG_CF2, reg_value)) {
339 | return;
340 | }
341 | }
342 |
--------------------------------------------------------------------------------
/src/input_iface.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * input_iface.c: Key handler implementation
5 | */
6 |
7 | #include
8 | #include
9 |
10 | #include "config.h"
11 | #include "debug_levels.h"
12 |
13 | #include "params_iface.h"
14 | #include "input_iface.h"
15 |
16 | #include "i2c_helper.h"
17 |
18 | #include "bbq20kbd_pmod_codes.h"
19 |
20 | // Global keyboard context and sysfs data
21 | struct kbd_ctx *g_ctx = NULL;
22 |
23 | // Main key event handler
24 | static void key_report_event(struct kbd_ctx* ctx,
25 | struct key_fifo_item const* ev)
26 | {
27 | uint8_t keycode;
28 |
29 | // Only handle key pressed, held, or released events
30 | if ((ev->state != KEY_STATE_PRESSED) && (ev->state != KEY_STATE_RELEASED)
31 | && (ev->state != KEY_STATE_HOLD)) {
32 | return;
33 | }
34 |
35 | // Post key scan event
36 | input_event(ctx->input_dev, EV_MSC, MSC_SCAN, ev->scancode);
37 |
38 | // Map input scancode to Linux input keycode
39 | keycode = ctx->keycode_map[ev->scancode];
40 | dev_info_fe(&ctx->i2c_client->dev,
41 | "%s state %d, scancode %d mapped to keycode %d\n",
42 | __func__, ev->state, ev->scancode, keycode);
43 |
44 | // Scancode mapped to ignored keycode
45 | if (keycode == 0) {
46 | return;
47 |
48 | // Scancode converted to keycode not in map
49 | } else if (keycode == KEY_UNKNOWN) {
50 | dev_warn(&ctx->i2c_client->dev,
51 | "%s Could not get Keycode for Scancode: [0x%02X]\n",
52 | __func__, ev->scancode);
53 | return;
54 | }
55 |
56 | // Update last keypress time
57 | g_ctx->last_keypress_at = ktime_get_boottime_ns();
58 |
59 | if (keycode == KEY_STOP) {
60 |
61 | // Pressing power button sends Tmux prefix (Control + code 171 in keymap)
62 | if (ev->state == KEY_STATE_PRESSED) {
63 | input_report_key(ctx->input_dev, KEY_LEFTCTRL, TRUE);
64 | input_report_key(ctx->input_dev, 171, TRUE);
65 | input_report_key(ctx->input_dev, 171, FALSE);
66 | input_report_key(ctx->input_dev, KEY_LEFTCTRL, FALSE);
67 |
68 | // Short hold power buttion opens Tmux menu (Control + code 174 in keymap)
69 | } else if (ev->state == KEY_STATE_HOLD) {
70 | input_report_key(ctx->input_dev, KEY_LEFTCTRL, TRUE);
71 | input_report_key(ctx->input_dev, 174, TRUE);
72 | input_report_key(ctx->input_dev, 174, FALSE);
73 | input_report_key(ctx->input_dev, KEY_LEFTCTRL, FALSE);
74 | }
75 | return;
76 | }
77 |
78 | // Subsystem key handling
79 | if (input_fw_consumes_keycode(ctx, &keycode, keycode, ev->state)
80 | || input_touch_consumes_keycode(ctx, &keycode, keycode, ev->state)
81 | || input_modifiers_consumes_keycode(ctx, &keycode, keycode, ev->state)
82 | || input_meta_consumes_keycode(ctx, &keycode, keycode, ev->state)) {
83 | return;
84 | }
85 |
86 | // Ignore hold keys at this point
87 | if (ev->state == KEY_STATE_HOLD) {
88 | return;
89 | }
90 |
91 | // Apply pending sticky modifiers
92 | keycode = input_modifiers_apply_pending(ctx, keycode);
93 |
94 | // Report key to input system
95 | input_report_key(ctx->input_dev, keycode, ev->state == KEY_STATE_PRESSED);
96 |
97 | // Reset sticky modifiers
98 | input_modifiers_reset(ctx);
99 | }
100 |
101 | static irqreturn_t input_irq_handler(int irq, void *param)
102 | {
103 | struct kbd_ctx *ctx;
104 | uint8_t irq_type;
105 | int8_t reg_value;
106 |
107 | // `param` is current keyboard context as started in _probe
108 | ctx = (struct kbd_ctx *)param;
109 |
110 | dev_info_ld(&ctx->i2c_client->dev,
111 | "%s Interrupt Fired. IRQ: %d\n", __func__, irq);
112 |
113 | // Read interrupt type from client
114 | if (kbd_read_i2c_u8(ctx->i2c_client, REG_INT, &irq_type)) {
115 | return IRQ_NONE;
116 | }
117 | dev_info_ld(&ctx->i2c_client->dev,
118 | "%s Interrupt type: 0x%02x\n", __func__, irq_type);
119 |
120 | // Reported no interrupt type
121 | if (irq_type == 0x00) {
122 | return IRQ_NONE;
123 | }
124 |
125 | // Client reported a key overflow
126 | if (irq_type & REG_INT_OVERFLOW) {
127 | dev_warn(&ctx->i2c_client->dev, "%s overflow occurred.\n", __func__);
128 | }
129 |
130 | // Client reported a key event
131 | if (irq_type & REG_INT_KEY) {
132 | input_fw_read_fifo(ctx);
133 | schedule_work(&ctx->work_struct);
134 | }
135 |
136 | // Client reported a touch event
137 | if (irq_type & REG_INT_TOUCH) {
138 |
139 | // Read touch X-coordinate
140 | if (kbd_read_i2c_u8(ctx->i2c_client, REG_TOX, ®_value)) {
141 | return IRQ_NONE;
142 | }
143 | ctx->touch.dx += reg_value;
144 |
145 | // Read touch Y-coordinate
146 | if (kbd_read_i2c_u8(ctx->i2c_client, REG_TOY, ®_value)) {
147 | return IRQ_NONE;
148 | }
149 | ctx->touch.dy += reg_value;
150 |
151 | // Set touch event flag and schedule touch work
152 | ctx->raised_touch_event = 1;
153 | schedule_work(&ctx->work_struct);
154 |
155 | } else {
156 |
157 | // Clear touch event flag
158 | ctx->raised_touch_event = 0;
159 | }
160 |
161 | return IRQ_HANDLED;
162 | }
163 |
164 | static void input_workqueue_handler(struct work_struct *work_struct_ptr)
165 | {
166 | struct kbd_ctx *ctx;
167 | uint8_t fifo_idx;
168 |
169 | // Get keyboard context from work struct
170 | ctx = container_of(work_struct_ptr, struct kbd_ctx, work_struct);
171 |
172 | // Process FIFO items
173 | for (fifo_idx = 0; fifo_idx < ctx->key_fifo_count; fifo_idx++) {
174 | key_report_event(ctx, &ctx->key_fifo_data[fifo_idx]);
175 | }
176 |
177 | // Reset pending FIFO count
178 | ctx->key_fifo_count = 0;
179 |
180 | // Handle any pending touch events
181 | if (ctx->raised_touch_event) {
182 | input_touch_report_event(ctx);
183 | ctx->raised_touch_event = 0;
184 | }
185 |
186 | // Synchronize input system and clear client interrupt flag
187 | input_sync(ctx->input_dev);
188 | if (kbd_write_i2c_u8(ctx->i2c_client, REG_INT, 0)) {
189 | return;
190 | }
191 | }
192 |
193 | int input_probe(struct i2c_client* i2c_client)
194 | {
195 | int rc, i;
196 |
197 | // Allocate keyboard context (managed by device lifetime)
198 | g_ctx = devm_kzalloc(&i2c_client->dev, sizeof(*g_ctx), GFP_KERNEL);
199 | if (!g_ctx) {
200 | return -ENOMEM;
201 | }
202 |
203 | // Allocate and copy keycode array
204 | g_ctx->keycode_map = devm_kmemdup(&i2c_client->dev, keycodes, NUM_KEYCODES,
205 | GFP_KERNEL);
206 | if (!g_ctx->keycode_map) {
207 | return -ENOMEM;
208 | }
209 |
210 | // Initialize keyboard context
211 | g_ctx->i2c_client = i2c_client;
212 | g_ctx->last_keypress_at = ktime_get_boottime_ns();
213 |
214 | // Run subsystem probes
215 | if ((rc = input_fw_probe(i2c_client, g_ctx))) {
216 | dev_err(&i2c_client->dev, "beepy-kbd: input_fw_probe failed\n");
217 | return rc;
218 | }
219 | if ((rc = input_rtc_probe(i2c_client, g_ctx))) {
220 | dev_err(&i2c_client->dev, "beepy-kbd: input_rtc_probe failed\n");
221 | return rc;
222 | }
223 | if ((rc = input_display_probe(i2c_client, g_ctx))) {
224 | dev_err(&i2c_client->dev, "beepy-kbd: input_display_probe failed\n");
225 | return rc;
226 | }
227 | if ((rc = input_modifiers_probe(i2c_client, g_ctx))) {
228 | dev_err(&i2c_client->dev, "beepy-kbd: input_modifiers_probe failed\n");
229 | return rc;
230 | }
231 | if ((rc = input_touch_probe(i2c_client, g_ctx))) {
232 | dev_err(&i2c_client->dev, "beepy-kbd: input_touch_probe failed\n");
233 | return rc;
234 | }
235 | if ((rc = input_meta_probe(i2c_client, g_ctx))) {
236 | dev_err(&i2c_client->dev, "beepy-kbd: input_meta_probe failed\n");
237 | return rc;
238 | }
239 |
240 | // Allocate input device
241 | if ((g_ctx->input_dev = devm_input_allocate_device(&i2c_client->dev)) == NULL) {
242 | dev_err(&i2c_client->dev,
243 | "%s Could not devm_input_allocate_device BBQX0KBD.\n", __func__);
244 | return -ENOMEM;
245 | }
246 |
247 | // Initialize input device
248 | g_ctx->input_dev->name = i2c_client->name;
249 | g_ctx->input_dev->id.bustype = BBQX0KBD_BUS_TYPE;
250 | g_ctx->input_dev->id.vendor = BBQX0KBD_VENDOR_ID;
251 | g_ctx->input_dev->id.product = BBQX0KBD_PRODUCT_ID;
252 | g_ctx->input_dev->id.version = BBQX0KBD_VERSION_ID;
253 |
254 | // Initialize input device keycodes
255 | g_ctx->input_dev->keycode = g_ctx->keycode_map;
256 | g_ctx->input_dev->keycodesize = sizeof(g_ctx->keycode_map[0]);
257 | g_ctx->input_dev->keycodemax = ARRAY_SIZE(keycodes);
258 |
259 | // Set input device keycode bits
260 | for (i = 0; i < NUM_KEYCODES; i++) {
261 | __set_bit(g_ctx->keycode_map[i], g_ctx->input_dev->keybit);
262 | }
263 | __clear_bit(KEY_RESERVED, g_ctx->input_dev->keybit);
264 | __set_bit(EV_REP, g_ctx->input_dev->evbit);
265 | __set_bit(EV_KEY, g_ctx->input_dev->evbit);
266 |
267 | // Set input device capabilities
268 | input_set_capability(g_ctx->input_dev, EV_MSC, MSC_SCAN);
269 | input_set_capability(g_ctx->input_dev, EV_REL, REL_X);
270 | input_set_capability(g_ctx->input_dev, EV_REL, REL_Y);
271 | input_set_capability(g_ctx->input_dev, EV_KEY, BTN_LEFT);
272 | input_set_capability(g_ctx->input_dev, EV_KEY, BTN_RIGHT);
273 |
274 | // Request IRQ handler for I2C client and initialize workqueue
275 | if ((rc = devm_request_threaded_irq(&i2c_client->dev,
276 | i2c_client->irq, NULL, input_irq_handler, IRQF_SHARED | IRQF_ONESHOT,
277 | i2c_client->name, g_ctx))) {
278 |
279 | dev_err(&i2c_client->dev,
280 | "Could not claim IRQ %d; error %d\n", i2c_client->irq, rc);
281 | return rc;
282 | }
283 | INIT_WORK(&g_ctx->work_struct, input_workqueue_handler);
284 |
285 | // Register input device with input subsystem
286 | dev_info(&i2c_client->dev,
287 | "%s registering input device", __func__);
288 | if ((rc = input_register_device(g_ctx->input_dev))) {
289 | dev_err(&i2c_client->dev,
290 | "Failed to register input device, error: %d\n", rc);
291 | return rc;
292 | }
293 |
294 | return 0;
295 | }
296 |
297 | void input_shutdown(struct i2c_client* i2c_client)
298 | {
299 | // Run subsystem shutdowns
300 | input_meta_shutdown(i2c_client, g_ctx);
301 | input_touch_shutdown(i2c_client, g_ctx);
302 | input_modifiers_shutdown(i2c_client, g_ctx);
303 | input_display_shutdown(i2c_client, g_ctx);
304 | input_rtc_shutdown(i2c_client, g_ctx);
305 | input_fw_shutdown(i2c_client, g_ctx);
306 |
307 | // Remove context from global state
308 | // (It is freed by the device-specific memory mananger)
309 | g_ctx = NULL;
310 | }
311 |
--------------------------------------------------------------------------------
/src/input_iface.h:
--------------------------------------------------------------------------------
1 | #ifndef INPUT_IFACE_H_
2 | #define INPUT_IFACE_H_
3 |
4 | // SPDX-License-Identifier: GPL-2.0-only
5 | /*
6 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
7 | */
8 |
9 | #include
10 | #include
11 | #include
12 |
13 | #include "registers.h"
14 |
15 | #define BBQX0KBD_BUS_TYPE BUS_I2C
16 | #define BBQX0KBD_VENDOR_ID 0x0001
17 | #define BBQX0KBD_PRODUCT_ID 0x0001
18 | #define BBQX0KBD_VERSION_ID 0x0001
19 |
20 | // From keyboard firmware source
21 | enum rp2040_key_state
22 | {
23 | KEY_STATE_IDLE = 0,
24 | KEY_STATE_PRESSED = 1,
25 | KEY_STATE_HOLD = 2,
26 | KEY_STATE_RELEASED = 3,
27 | KEY_STATE_LONG_HOLD = 4,
28 | };
29 | struct key_fifo_item
30 | {
31 | uint8_t scancode;
32 |
33 | uint8_t _ : 4;
34 | enum rp2040_key_state state : 4;
35 | };
36 |
37 | struct touch_ctx
38 | {
39 | enum {
40 | TOUCH_ACT_ALWAYS = 0,
41 | TOUCH_ACT_CLICK = 1
42 | } activation;
43 |
44 | enum {
45 | TOUCH_INPUT_AS_KEYS = 0,
46 | TOUCH_INPUT_AS_MOUSE = 1
47 | } input_as;
48 |
49 | uint8_t enabled;
50 | uint8_t enable_while_shift_held;
51 | uint8_t entry_while_shift_held;
52 | uint8_t threshold;
53 | int x, dx, y, dy;
54 | };
55 |
56 | struct kbd_ctx
57 | {
58 | struct work_struct work_struct;
59 | uint8_t version_number;
60 |
61 | struct i2c_client *i2c_client;
62 | struct input_dev *input_dev;
63 |
64 | // Map from input HID scancodes to Linux keycodes
65 | uint8_t *keycode_map;
66 |
67 | // Key state and touch FIFO queue
68 | uint8_t key_fifo_count;
69 | struct key_fifo_item key_fifo_data[BBQX0KBD_FIFO_SIZE];
70 | uint64_t last_keypress_at;
71 |
72 | uint8_t raised_touch_event;
73 | struct touch_ctx touch;
74 | };
75 |
76 | // Shared global state for global interfaces such as sysfs
77 | extern struct kbd_ctx *g_ctx;
78 |
79 | // Public interface
80 |
81 | int input_probe(struct i2c_client* i2c_client);
82 | void input_shutdown(struct i2c_client* i2c_client);
83 |
84 | // Internal interfaces
85 |
86 | // Firmware
87 |
88 | int input_fw_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
89 | void input_fw_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
90 |
91 | int input_fw_consumes_keycode(struct kbd_ctx* ctx,
92 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state);
93 |
94 | void input_fw_decrease_brightness(struct kbd_ctx* ctx);
95 | void input_fw_increase_brightness(struct kbd_ctx* ctx);
96 | void input_fw_toggle_brightness(struct kbd_ctx* ctx);
97 |
98 | int input_fw_enable_touch_interrupts(struct kbd_ctx* ctx);
99 | int input_fw_disable_touch_interrupts(struct kbd_ctx* ctx);
100 |
101 | void input_fw_read_fifo(struct kbd_ctx* ctx);
102 |
103 | int input_fw_get_rtc(uint8_t* year, uint8_t* mon, uint8_t* day,
104 | uint8_t* hour, uint8_t* min, uint8_t* sec);
105 | int input_fw_set_rtc(uint8_t year, uint8_t mon, uint8_t day,
106 | uint8_t hour, uint8_t min, uint8_t sec);
107 |
108 | void input_fw_set_handle_poweroff(struct kbd_ctx* ctx, uint8_t handle_poweroff);
109 | void input_fw_set_auto_off(struct kbd_ctx* ctx, uint8_t auto_off);
110 |
111 | // RTC
112 |
113 | int input_rtc_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
114 | void input_rtc_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
115 |
116 | // Display
117 |
118 | int input_display_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
119 | void input_display_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
120 |
121 | int input_display_valid_sharp_path(char const* path);
122 |
123 | void input_display_invert(struct kbd_ctx* ctx);
124 |
125 | void input_display_set_indicator(int idx, unsigned char const* pixels);
126 | void input_display_clear_indicator(int idx);
127 | void input_display_clear_overlays(void);
128 |
129 | // Modifiers
130 |
131 | int input_modifiers_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
132 | void input_modifiers_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
133 |
134 | int input_modifiers_consumes_keycode(struct kbd_ctx* ctx,
135 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state);
136 |
137 | uint8_t input_modifiers_apply_pending(struct kbd_ctx* ctx, uint8_t keycode);
138 | void input_modifiers_reset(struct kbd_ctx* ctx);
139 |
140 | void input_modifiers_send_control(struct kbd_ctx* ctx);
141 | void input_modifiers_send_alt(struct kbd_ctx* ctx);
142 |
143 | void input_modifiers_reset_shift(struct kbd_ctx* ctx);
144 |
145 | // Touch
146 |
147 | int input_touch_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
148 | void input_touch_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
149 |
150 | void input_touch_report_event(struct kbd_ctx *ctx);
151 |
152 | int input_touch_consumes_keycode(struct kbd_ctx* ctx,
153 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state);
154 |
155 | void input_touch_enable(struct kbd_ctx *ctx);
156 | void input_touch_disable(struct kbd_ctx *ctx);
157 |
158 | void input_touch_set_activation(struct kbd_ctx *ctx, uint8_t activation);
159 | void input_touch_set_input_as(struct kbd_ctx *ctx, uint8_t input_as);
160 |
161 | void input_touch_set_threshold(struct kbd_ctx *ctx, uint8_t threshold);
162 | void input_touch_set_indicator(struct kbd_ctx *ctx);
163 |
164 | // Meta mode
165 |
166 | int input_meta_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
167 | void input_meta_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx);
168 |
169 | int input_meta_consumes_keycode(struct kbd_ctx* ctx,
170 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state);
171 |
172 | void input_meta_enable(struct kbd_ctx* ctx);
173 | void input_meta_disable(struct kbd_ctx* ctx);
174 |
175 | #endif
176 |
--------------------------------------------------------------------------------
/src/input_meta.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | // Input meta mode subsystem
3 |
4 | #include
5 |
6 | #include "config.h"
7 | #include "input_iface.h"
8 | #include "params_iface.h"
9 |
10 | #include "indicators.h"
11 |
12 | #define SYMBOL_OVERLAY_PATH "/sbin/symbol-overlay"
13 |
14 | // Globals
15 |
16 | static uint8_t g_enabled;
17 | static uint8_t g_showing_indicator;
18 | // Clear the Meta menu overlay when Meta indicator cleared
19 | static uint8_t g_showing_overlay;
20 | // Store the last keycode sent to simulate a key
21 | // up event when the key is released after Meta is exited
22 | static uint8_t g_current_meta_keycode;
23 |
24 | // Call Meta menu overlay helper
25 | // Will return normally if overlay is not installed
26 | static void show_meta_menu(struct kbd_ctx* ctx)
27 | {
28 | static char const* overlay_argv[] = {SYMBOL_OVERLAY_PATH, "--meta", NULL, NULL};
29 |
30 | if (g_showing_overlay) {
31 | return;
32 | }
33 |
34 | g_showing_overlay = 1;
35 |
36 | // Call overlay helper
37 | overlay_argv[2] = params_get_sharp_path();
38 | call_usermodehelper(overlay_argv[0], (char**)overlay_argv, NULL, UMH_NO_WAIT);
39 | }
40 |
41 | // Called before checking "repeatable" meta mode keys,
42 | // These keys map to an internal driver function rather than another key
43 | // They will not be sent to the input system
44 | // The check is separate from the run so that key-up events can be ignored
45 | static bool is_single_function_key(struct kbd_ctx* ctx, uint8_t keycode)
46 | {
47 | switch (keycode) {
48 | case KEY_T: return TRUE; // Tab
49 | case KEY_X: return TRUE; // Control
50 | case KEY_C: return TRUE; // Alt
51 | case KEY_N: return TRUE; // Decrease brightness
52 | case KEY_M: return TRUE; // Increase brightness
53 | case KEY_MUTE: return TRUE; // Toggle brightness
54 | case KEY_0: return TRUE; // Invert display
55 | case KEY_ESC: return TRUE; // Exit meta mode
56 | case KEY_PROPS: return TRUE; // Exit meta mode
57 | }
58 |
59 | return FALSE;
60 | }
61 |
62 | // Return whether or not to exit meta mode
63 | static int run_single_function_key(struct kbd_ctx* ctx, uint8_t keycode)
64 | {
65 | switch (keycode) {
66 |
67 | case KEY_T:
68 | input_report_key(ctx->input_dev, KEY_TAB, 1);
69 | input_report_key(ctx->input_dev, KEY_TAB, 0);
70 | return 1;
71 |
72 | case KEY_X:
73 | input_modifiers_send_control(ctx);
74 | return 1;
75 |
76 | case KEY_C:
77 | input_modifiers_send_alt(ctx);
78 | return 1;
79 |
80 | case KEY_0:
81 | input_display_invert(ctx);
82 | return 1;
83 |
84 | case KEY_ESC:
85 | case KEY_PROPS:
86 | return 1;
87 |
88 | case KEY_N: input_fw_decrease_brightness(ctx); return 0;
89 | case KEY_M: input_fw_increase_brightness(ctx); return 0;
90 | case KEY_MUTE:
91 | input_fw_toggle_brightness(ctx);
92 | return 1;
93 | }
94 |
95 | return 0;
96 | }
97 |
98 | // Called after checking "single function" meta mode keys,
99 | // These keys, both press and release events, will be sent to the input system
100 | static uint8_t map_repeatable_key(struct kbd_ctx* ctx, uint8_t keycode)
101 | {
102 | switch (keycode) {
103 |
104 | case KEY_E: return KEY_UP;
105 | case KEY_S: return KEY_DOWN;
106 | case KEY_W: return KEY_LEFT;
107 | case KEY_D: return KEY_RIGHT;
108 |
109 | case KEY_R: return KEY_HOME;
110 | case KEY_F: return KEY_END;
111 |
112 | case KEY_O: return KEY_PAGEUP;
113 | case KEY_P: return KEY_PAGEDOWN;
114 |
115 | case KEY_Q: return 172;
116 | case KEY_A: return 173;
117 | }
118 |
119 | return 0;
120 | }
121 |
122 | int input_meta_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
123 | {
124 | g_enabled = 0;
125 | g_showing_indicator = 0;
126 | g_showing_overlay = 0;
127 |
128 | return 0;
129 | }
130 |
131 | void input_meta_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
132 | {
133 | input_meta_disable(ctx);
134 | }
135 |
136 | int input_meta_consumes_keycode(struct kbd_ctx* ctx,
137 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state)
138 | {
139 | uint8_t simulated_keycode;
140 |
141 | // Not in meta mode
142 | if (!g_enabled) {
143 |
144 | // Berry key enables meta mode
145 | if (keycode == KEY_PROPS) {
146 |
147 | if (state == KEY_STATE_RELEASED) {
148 | input_meta_enable(ctx);
149 |
150 | } else if (state == KEY_STATE_HOLD) {
151 | show_meta_menu(ctx);
152 | }
153 | return 1;
154 | }
155 |
156 | return 0;
157 | }
158 |
159 | // Ignore modifier keys in meta mode
160 | if ((keycode == KEY_LEFTSHIFT) || (keycode == KEY_RIGHTSHIFT)
161 | || (keycode == KEY_LEFTALT) || (keycode == KEY_RIGHTALT)
162 | || (keycode == KEY_LEFTCTRL) || (keycode == KEY_RIGHTCTRL)) {
163 | return 1;
164 | }
165 |
166 | // Keys in Meta mode will clear overlay
167 | if (g_showing_overlay) {
168 | input_display_clear_overlays();
169 | g_showing_overlay = 0;
170 | // Re-display indicator after clearing
171 | if (g_showing_indicator) {
172 | input_display_set_indicator(5, ind_meta);
173 | }
174 | }
175 |
176 | // Handle function dispatch meta mode keys
177 | if (is_single_function_key(ctx, keycode)) {
178 | if (state == KEY_STATE_RELEASED) {
179 |
180 | // Function will return whether to exit meta
181 | if (run_single_function_key(ctx, keycode)) {
182 | input_meta_disable(ctx);
183 | }
184 | }
185 | return 1;
186 | }
187 |
188 | // Remap to meta mode key
189 | simulated_keycode = map_repeatable_key(ctx, keycode);
190 |
191 | // No mapped meta mode key, disable and pass through key
192 | if (simulated_keycode == 0) {
193 | input_meta_disable(ctx);
194 | return 0;
195 | }
196 |
197 | // Report key to input system
198 | input_report_key(ctx->input_dev, simulated_keycode,
199 | state == KEY_STATE_PRESSED);
200 |
201 | // Save remapped key
202 | g_current_meta_keycode = (state == KEY_STATE_PRESSED)
203 | ? simulated_keycode
204 | : 0;
205 |
206 | return 1;
207 | }
208 |
209 | void input_meta_enable(struct kbd_ctx* ctx)
210 | {
211 | g_enabled = 1;
212 | g_current_meta_keycode = 0;
213 |
214 | // Set display indicator
215 | if (!g_showing_indicator) {
216 | input_display_set_indicator(5, ind_meta);
217 | g_showing_indicator = 1;
218 | }
219 | }
220 |
221 | void input_meta_disable(struct kbd_ctx* ctx)
222 | {
223 | g_enabled = 0;
224 |
225 | // Clear display indicator
226 | if (g_showing_indicator) {
227 | input_display_clear_indicator(5);
228 | g_showing_indicator = 0;
229 | }
230 |
231 | // Simulate key up event
232 | if (g_current_meta_keycode) {
233 | input_report_key(ctx->input_dev, g_current_meta_keycode, FALSE);
234 | g_current_meta_keycode = 0;
235 | }
236 | }
237 |
--------------------------------------------------------------------------------
/src/input_modifiers.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * input_modifiers.c: Sticky modifier keys implementation
5 | */
6 |
7 | #include
8 | #include
9 |
10 | #include "config.h"
11 | #include "debug_levels.h"
12 |
13 | #include "input_iface.h"
14 | #include "params_iface.h"
15 |
16 | #include "bbq20kbd_pmod_codes.h"
17 |
18 | #include "indicators.h"
19 |
20 | #define SYMBOL_OVERLAY_PATH "/sbin/symbol-overlay"
21 |
22 | struct sticky_modifier
23 | {
24 | uint8_t active;
25 | uint8_t held;
26 | uint8_t pending;
27 | uint8_t sticky;
28 | uint8_t locked;
29 |
30 | // Keycode to send to the input system when applied
31 | uint8_t keycode;
32 |
33 | // Display indicator index and code
34 | uint8_t indicator_idx;
35 | unsigned char const* indicator_pixels;
36 |
37 | // When sticky modifier system has determined that
38 | // modifier should be applied, run this callback
39 | // and report the returned keycode result to the input system
40 | void (*set_callback)(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier);
41 | void (*unset_callback)(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier);
42 | void (*lock_callback)(struct kbd_ctx* ctx, struct sticky_modifier* sticky_modifier);
43 | uint8_t(*map_callback)(struct kbd_ctx* ctx, uint8_t keycode);
44 | };
45 |
46 | // Globals
47 |
48 | // "Real" modifiers like Shift and Control are handled by simulating
49 | // input key events. Since phys. alt is hardcoded, the state is here
50 | static uint8_t g_apply_phys_alt;
51 | // Store the last keycode sent in the phys. alt map to simulate a key
52 | // up event when the key is released after phys. alt is released
53 | static uint8_t g_current_phys_alt_keycode;
54 | // Clear the symbol menu overlay when Sym indicator cleared
55 | static uint8_t g_showing_sym_menu;
56 |
57 | // Sticky modifier structs
58 | static struct sticky_modifier g_sticky_ctrl;
59 | static struct sticky_modifier g_sticky_shift;
60 | static struct sticky_modifier g_sticky_phys_alt;
61 | static struct sticky_modifier g_sticky_alt;
62 | static struct sticky_modifier g_sticky_altgr;
63 |
64 | // Sticky modifier helpers
65 |
66 | static void press_sticky_modifier(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier)
67 | {
68 | input_report_key(ctx->input_dev, sticky_modifier->keycode, TRUE);
69 | }
70 |
71 | static void release_sticky_modifier(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier)
72 | {
73 | input_report_key(ctx->input_dev, sticky_modifier->keycode, FALSE);
74 | }
75 |
76 | static void lock_sticky_modifier(struct kbd_ctx* ctx, struct sticky_modifier* sticky_modifier)
77 | {
78 | sticky_modifier->locked = 1;
79 |
80 | // Report modifier to input system as pressed
81 | sticky_modifier->set_callback(ctx, sticky_modifier);
82 | }
83 |
84 | static void enable_phys_alt(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier)
85 | {
86 | g_apply_phys_alt = 1;
87 | }
88 |
89 | static void disable_phys_alt(struct kbd_ctx* ctx, struct sticky_modifier const* sticky_modifier)
90 | {
91 | // Send key up event if there is a current phys. alt key being held
92 | if (g_current_phys_alt_keycode) {
93 | input_report_key(ctx->input_dev, g_current_phys_alt_keycode, FALSE);
94 | g_current_phys_alt_keycode = 0;
95 | }
96 |
97 | g_apply_phys_alt = 0;
98 | }
99 |
100 | static uint8_t map_phys_alt_keycode(struct kbd_ctx* ctx, uint8_t keycode)
101 | {
102 | if (!g_apply_phys_alt) {
103 | return keycode;
104 | }
105 |
106 | keycode += 119; // See map file for result keys
107 | g_current_phys_alt_keycode = keycode;
108 | return keycode;
109 | }
110 |
111 | // Call symbol menu overlay helper
112 | // Will return normally if overlay is not installed
113 | static void show_sym_menu(struct kbd_ctx* ctx, struct sticky_modifier* sticky_modifier)
114 | {
115 | static char const* overlay_argv[] = {SYMBOL_OVERLAY_PATH, NULL, NULL};
116 |
117 | g_showing_sym_menu = 1;
118 |
119 | // Call overlay helper
120 | overlay_argv[1] = params_get_sharp_path();
121 | call_usermodehelper(overlay_argv[0], (char**)overlay_argv, NULL, UMH_NO_WAIT);
122 | }
123 |
124 | // Sticky modifier keys follow BB Q10 convention
125 | // Holding modifier while typing alpha keys will apply to all alpha keys
126 | // until released.
127 | // One press and release will enter sticky mode, apply modifier key to
128 | // the next alpha key only. If the same modifier key is pressed and
129 | // released again in sticky mode, it will be canceled.
130 | static void transition_sticky_modifier(struct kbd_ctx* ctx,
131 | struct sticky_modifier* mod, enum rp2040_key_state state)
132 | {
133 | if (state == KEY_STATE_PRESSED) {
134 |
135 | // Set "held" state
136 | mod->held = 1;
137 |
138 | // If pressed again while sticky, clear sticky
139 | if (mod->sticky) {
140 | mod->sticky = 0;
141 |
142 | // Otherwise, set pending sticky to be applied on release
143 | } else {
144 | mod->pending = 1;
145 | }
146 |
147 | // In locked mode
148 | if (mod->locked) {
149 |
150 | // Clear lock mode
151 | mod->locked = 0;
152 |
153 | // Clear pending sticky so that it is not applied to next key
154 | mod->pending = 0;
155 | }
156 |
157 | // Report modifier to input system as pressed
158 | mod->set_callback(ctx, mod);
159 |
160 | // Set display indicator
161 | if (mod->indicator_pixels) {
162 | input_display_set_indicator(mod->indicator_idx,
163 | mod->indicator_pixels);
164 | }
165 |
166 | // Released
167 | } else if (state == KEY_STATE_RELEASED) {
168 |
169 | // Unset "held" state
170 | mod->held = 0;
171 |
172 | // Not in locked mode
173 | if (!mod->locked) {
174 |
175 | // If any alpha key was typed during hold,
176 | // `apply_sticky_modifiers` will clear "pending sticky" state.
177 | // If still in "pending sticky", set "sticky" state.
178 | if (mod->pending) {
179 |
180 | mod->sticky = 1;
181 | mod->pending = 0;
182 |
183 | } else {
184 | // Clear display indicator
185 | input_display_clear_indicator(mod->indicator_idx);
186 |
187 | // Clear symbol menu overlay if it was showing
188 | if (mod->keycode == KEY_RIGHTALT) {
189 | input_display_clear_overlays();
190 | g_showing_sym_menu = 0;
191 | }
192 | }
193 |
194 | // Report modifier to input system as released
195 | mod->unset_callback(ctx, mod);
196 | }
197 |
198 | // Held
199 | } else if (state == KEY_STATE_HOLD) {
200 |
201 | // If any alpha key was typed during hold,
202 | // `apply_sticky_modifiers` will clear "pending sticky" state.
203 | // If still in "pending sticky", set locked mode
204 | if (mod->pending && mod->lock_callback) {
205 | mod->lock_callback(ctx, mod);
206 | }
207 | }
208 | }
209 |
210 | // Called before sending an alpha key to apply any pending sticky modifiers
211 | static void apply_sticky_modifier(struct kbd_ctx* ctx,
212 | struct sticky_modifier* mod)
213 | {
214 | if (mod->held) {
215 | mod->pending = 0;
216 |
217 | } else if (mod->sticky) {
218 | mod->set_callback(ctx, mod);
219 | }
220 | }
221 |
222 | // Called after sending the alpha key to reset
223 | // any sticky modifiers
224 | static void reset_sticky_modifier(struct kbd_ctx* ctx,
225 | struct sticky_modifier* mod)
226 | {
227 | if (mod->sticky) {
228 | mod->sticky = 0;
229 |
230 | mod->unset_callback(ctx, mod);
231 |
232 | // Clear display indicator
233 | input_display_clear_indicator(mod->indicator_idx);
234 |
235 | // Clear symbol menu overlay if it was showing
236 | if (mod->keycode == KEY_RIGHTALT) {
237 | input_display_clear_overlays();
238 | g_showing_sym_menu = 0;
239 | }
240 | }
241 | }
242 |
243 | int input_modifiers_consumes_keycode(struct kbd_ctx* ctx,
244 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state)
245 | {
246 |
247 | if ((keycode == KEY_LEFTSHIFT) || (keycode == KEY_RIGHTSHIFT)) {
248 | transition_sticky_modifier(ctx, &g_sticky_shift, state);
249 | return 1;
250 |
251 | } else if (keycode == KEY_LEFTALT) {
252 | transition_sticky_modifier(ctx, &g_sticky_phys_alt, state);
253 | return 1;
254 |
255 | } else if (keycode == KEY_RIGHTALT) {
256 | transition_sticky_modifier(ctx, &g_sticky_altgr, state);
257 | return 1;
258 |
259 | } else if (keycode == KEY_OPEN) {
260 | transition_sticky_modifier(ctx, &g_sticky_ctrl, state);
261 | return 1;
262 | }
263 |
264 | return 0;
265 | }
266 |
267 | uint8_t input_modifiers_apply_pending(struct kbd_ctx* ctx, uint8_t keycode)
268 | {
269 | // Apply pending sticky modifiers
270 | apply_sticky_modifier(ctx, &g_sticky_shift);
271 | apply_sticky_modifier(ctx, &g_sticky_ctrl);
272 | apply_sticky_modifier(ctx, &g_sticky_phys_alt);
273 | apply_sticky_modifier(ctx, &g_sticky_alt);
274 | apply_sticky_modifier(ctx, &g_sticky_altgr);
275 |
276 | // Map phys. alt
277 | return g_sticky_phys_alt.map_callback(ctx, keycode);
278 | }
279 |
280 | void input_modifiers_reset(struct kbd_ctx* ctx)
281 | {
282 | // Reset sticky modifiers
283 | reset_sticky_modifier(ctx, &g_sticky_shift);
284 | reset_sticky_modifier(ctx, &g_sticky_ctrl);
285 | reset_sticky_modifier(ctx, &g_sticky_phys_alt);
286 | reset_sticky_modifier(ctx, &g_sticky_alt);
287 | reset_sticky_modifier(ctx, &g_sticky_altgr);
288 | }
289 |
290 | void input_modifiers_send_control(struct kbd_ctx* ctx)
291 | {
292 | transition_sticky_modifier(ctx, &g_sticky_ctrl, KEY_STATE_PRESSED);
293 | transition_sticky_modifier(ctx, &g_sticky_ctrl, KEY_STATE_RELEASED);
294 | }
295 |
296 | void input_modifiers_send_alt(struct kbd_ctx* ctx)
297 | {
298 | transition_sticky_modifier(ctx, &g_sticky_alt, KEY_STATE_PRESSED);
299 | transition_sticky_modifier(ctx, &g_sticky_alt, KEY_STATE_RELEASED);
300 | }
301 |
302 | static void default_init_sticky_modifier(struct sticky_modifier* mod)
303 | {
304 | mod->held = 0;
305 | mod->pending = 0;
306 | mod->sticky = 0;
307 | mod->locked = 0;
308 |
309 | mod->keycode = 0;
310 | mod->set_callback = NULL;
311 | mod->unset_callback = NULL;
312 | mod->map_callback = NULL;
313 | mod->indicator_idx = 0;
314 | mod->indicator_pixels = NULL;
315 | }
316 |
317 | int input_modifiers_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
318 | {
319 | g_apply_phys_alt = 0;
320 | g_current_phys_alt_keycode = 0;
321 | g_showing_sym_menu = 0;
322 |
323 | // Initialize sticky modifiers
324 | default_init_sticky_modifier(&g_sticky_ctrl);
325 | g_sticky_ctrl.keycode = KEY_LEFTCTRL;
326 | g_sticky_ctrl.set_callback = press_sticky_modifier;
327 | g_sticky_ctrl.unset_callback = release_sticky_modifier;
328 | g_sticky_ctrl.lock_callback = lock_sticky_modifier;
329 | g_sticky_ctrl.indicator_idx = 2;
330 | g_sticky_ctrl.indicator_pixels = ind_control;
331 |
332 | default_init_sticky_modifier(&g_sticky_shift);
333 | g_sticky_shift.keycode = KEY_LEFTSHIFT;
334 | g_sticky_shift.set_callback = press_sticky_modifier;
335 | g_sticky_shift.unset_callback = release_sticky_modifier;
336 | g_sticky_shift.lock_callback = lock_sticky_modifier;
337 | g_sticky_shift.indicator_idx = 0;
338 | g_sticky_shift.indicator_pixels = ind_shift;
339 |
340 | default_init_sticky_modifier(&g_sticky_phys_alt);
341 | g_sticky_phys_alt.keycode = KEY_RIGHTCTRL;
342 | g_sticky_phys_alt.set_callback = enable_phys_alt;
343 | g_sticky_phys_alt.unset_callback = disable_phys_alt;
344 | g_sticky_phys_alt.lock_callback = lock_sticky_modifier;
345 | g_sticky_phys_alt.map_callback = map_phys_alt_keycode;
346 | g_sticky_phys_alt.indicator_idx = 1;
347 | g_sticky_phys_alt.indicator_pixels = ind_phys_alt;
348 |
349 | default_init_sticky_modifier(&g_sticky_alt);
350 | g_sticky_alt.keycode = KEY_LEFTALT;
351 | g_sticky_alt.set_callback = press_sticky_modifier;
352 | g_sticky_alt.unset_callback = release_sticky_modifier;
353 | g_sticky_alt.lock_callback = lock_sticky_modifier;
354 | g_sticky_alt.indicator_idx = 3;
355 | g_sticky_alt.indicator_pixels = ind_alt;
356 |
357 | default_init_sticky_modifier(&g_sticky_altgr);
358 | g_sticky_altgr.keycode = KEY_RIGHTALT;
359 | g_sticky_altgr.set_callback = press_sticky_modifier;
360 | g_sticky_altgr.unset_callback = release_sticky_modifier;
361 | g_sticky_altgr.lock_callback = show_sym_menu;
362 | g_sticky_altgr.indicator_idx = 4;
363 | g_sticky_altgr.indicator_pixels = ind_altgr;
364 |
365 | return 0;
366 | }
367 |
368 | void input_modifiers_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
369 | {}
370 |
371 | // Clear the shift held state
372 | // Touch layer enables touch scrolling while shift is held,
373 | // so if any touch input was entered, it will clear pending shift
374 | void input_modifiers_reset_shift(struct kbd_ctx* ctx)
375 | {
376 | g_sticky_shift.pending = 0;
377 | g_sticky_shift.unset_callback(ctx, &g_sticky_shift);
378 | input_display_clear_indicator(g_sticky_shift.indicator_idx);
379 | }
380 |
--------------------------------------------------------------------------------
/src/input_rtc.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | // Input RTC subsystem
3 |
4 | #include
5 |
6 | #include "config.h"
7 | #include "input_iface.h"
8 |
9 | static int i2c_set_time(struct device *dev, struct rtc_time *tm)
10 | {
11 | int rc;
12 |
13 | if ((rc = input_fw_set_rtc((uint8_t)tm->tm_year, (uint8_t)tm->tm_mon,
14 | (uint8_t)tm->tm_mday, (uint8_t)tm->tm_hour, (uint8_t)tm->tm_min,
15 | (uint8_t)tm->tm_sec))) {
16 | printk(KERN_ERR "i2c_set_time failed: %d\n", rc);
17 | return rc;
18 | }
19 |
20 | printk(KERN_INFO "beepy-kbd: updated RTC\n");
21 |
22 | return 0;
23 | }
24 |
25 | static int i2c_read_time(struct device *dev, struct rtc_time *tm)
26 | {
27 | int rc;
28 | uint8_t year, mon, mday, hour, min, sec;
29 |
30 | if ((rc = input_fw_get_rtc(&year, &mon, &mday, &hour, &min, &sec))) {
31 | printk(KERN_ERR "i2c_read_time failed: %d\n", rc);
32 | return rc;
33 | }
34 |
35 | tm->tm_year = year;
36 | tm->tm_mon = mon;
37 | tm->tm_mday = mday;
38 | tm->tm_hour = hour;
39 | tm->tm_min = min;
40 | tm->tm_sec = sec;
41 |
42 | return 0;
43 | }
44 |
45 | static const struct rtc_class_ops beepy_rtc_ops = {
46 | .read_time = i2c_read_time,
47 | .set_time = i2c_set_time,
48 | };
49 |
50 | int input_rtc_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
51 | {
52 | struct rtc_device *rtc;
53 |
54 | // Register RTC device
55 | rtc = devm_rtc_device_register(&i2c_client->dev,
56 | "beepy-rtc", &beepy_rtc_ops, THIS_MODULE);
57 | if (IS_ERR(rtc)) {
58 | dev_err(&i2c_client->dev,
59 | "Failed to register RTC device\n");
60 | return PTR_ERR(rtc);
61 | }
62 |
63 | return 0;
64 | }
65 |
66 | void input_rtc_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
67 | {}
68 |
--------------------------------------------------------------------------------
/src/input_touch.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * input_iface.c: Key handler implementation
5 | */
6 |
7 | #include
8 | #include
9 |
10 | #include "config.h"
11 | #include "debug_levels.h"
12 |
13 | #include "input_iface.h"
14 | #include "i2c_helper.h"
15 |
16 | #include "indicators.h"
17 |
18 | static uint8_t g_touch_indicator = 0;
19 |
20 | static void enable_scale_2x(struct kbd_ctx* ctx)
21 | {
22 | uint8_t reg;
23 |
24 | // Set touchpad scaling factor to 2x for X and Y
25 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_REG, REG_TOUCHPAD_REG_SPEED);
26 | kbd_read_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, ®);
27 | reg |= REG_TOUCHPAD_SPEED_X_SCALE2;
28 | reg |= REG_TOUCHPAD_SPEED_Y_SCALE2;
29 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, reg);
30 |
31 | // Enable touchpad scaling
32 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_REG, REG_TOUCHPAD_REG_ENGINE);
33 | kbd_read_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, ®);
34 | reg |= REG_TOUCHPAD_ENGINE_XY_SCALE;
35 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, reg);
36 | }
37 |
38 | static void disable_scale_2x(struct kbd_ctx* ctx)
39 | {
40 | uint8_t reg;
41 |
42 | // Clear touchpad scaling factor to 2x for X and Y
43 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_REG, REG_TOUCHPAD_REG_SPEED);
44 | kbd_read_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, ®);
45 | reg &= ~REG_TOUCHPAD_SPEED_X_SCALE2;
46 | reg &= ~REG_TOUCHPAD_SPEED_Y_SCALE2;
47 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, reg);
48 |
49 | // Clear touchpad scaling
50 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_REG, REG_TOUCHPAD_REG_ENGINE);
51 | kbd_read_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, ®);
52 | reg &= ~REG_TOUCHPAD_ENGINE_XY_SCALE;
53 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, reg);
54 | }
55 |
56 | int input_touch_probe(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
57 | {
58 | ctx->touch.x = 0;
59 | ctx->touch.dx = 0;
60 | ctx->touch.y = 0;
61 | ctx->touch.dy = 0;
62 |
63 | ctx->touch.enable_while_shift_held = 1;
64 | ctx->touch.entry_while_shift_held = 0;
65 | ctx->touch.threshold = 8;
66 |
67 | // Default touch settings
68 | input_touch_set_activation(ctx, TOUCH_ACT_CLICK);
69 | input_touch_set_input_as(ctx, TOUCH_INPUT_AS_KEYS);
70 |
71 | return 0;
72 | }
73 |
74 | void input_touch_shutdown(struct i2c_client* i2c_client, struct kbd_ctx *ctx)
75 | {}
76 |
77 | void input_touch_report_event(struct kbd_ctx *ctx)
78 | {
79 | uint8_t x_threshold, y_threshold;
80 | #if (DEBUG_LEVEL & DEBUG_LEVEL_FE)
81 | uint8_t qual;
82 | #endif
83 |
84 | if (!ctx || !ctx->touch.enabled
85 | || ((ctx->touch.dx == 0) && (ctx->touch.dy == 0))) {
86 | return;
87 | }
88 |
89 | // Set minimum touch thresholds
90 | x_threshold = ctx->touch.threshold;
91 | y_threshold = ctx->touch.threshold;
92 |
93 | // Log touchpad surface quality
94 | #if (DEBUG_LEVEL & DEBUG_LEVEL_FE)
95 | kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_REG, 0x05);
96 | kbd_read_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_VAL, &qual);
97 |
98 | dev_info_fe(&ctx->i2c_client->dev,
99 | "Touch (%d, %d) Qual %d\n",
100 | ctx->touch.dx, ctx->touch.dy, qual);
101 | #endif
102 |
103 | // Report mouse movement
104 | if (ctx->touch.input_as == TOUCH_INPUT_AS_MOUSE) {
105 |
106 | // Report mouse movement
107 | input_report_rel(ctx->input_dev, REL_X, (int8_t)ctx->touch.dx);
108 | input_report_rel(ctx->input_dev, REL_Y, (int8_t)ctx->touch.dy);
109 | ctx->touch.dx = 0;
110 | ctx->touch.dy = 0;
111 |
112 | // Reset shift sticky state if touch entry was sent while held
113 | ctx->touch.entry_while_shift_held = 1;
114 |
115 | // Report arrow key movement
116 | } else if (ctx->touch.input_as == TOUCH_INPUT_AS_KEYS) {
117 |
118 | // Accumulate X / Y
119 | ctx->touch.x += ctx->touch.dx;
120 | ctx->touch.y += ctx->touch.dy;
121 |
122 | // Snap movement to arrow keys directions
123 | if ((ctx->touch.dx == 0) && (abs(ctx->touch.x) < x_threshold)) {
124 | ctx->touch.x = 0;
125 | }
126 | if ((ctx->touch.dy == 0) && (abs(ctx->touch.y) < y_threshold)) {
127 | ctx->touch.y = 0;
128 | }
129 | ctx->touch.dx = 0;
130 | ctx->touch.dy = 0;
131 |
132 | // Reset shift sticky state if touch entry was sent while held
133 | ctx->touch.entry_while_shift_held = 1;
134 |
135 | // Negative X: left arrow key
136 | if (ctx->touch.x <= -x_threshold) {
137 |
138 | do {
139 | input_report_key(ctx->input_dev, KEY_LEFT, TRUE);
140 | input_report_key(ctx->input_dev, KEY_LEFT, FALSE);
141 | ctx->touch.x += x_threshold;
142 | } while (ctx->touch.x <= -x_threshold);
143 |
144 | // Positive X: right arrow key
145 | } else if (ctx->touch.x > x_threshold) {
146 |
147 | do {
148 | input_report_key(ctx->input_dev, KEY_RIGHT, TRUE);
149 | input_report_key(ctx->input_dev, KEY_RIGHT, FALSE);
150 | ctx->touch.x -= x_threshold;
151 | } while (ctx->touch.x > x_threshold);
152 | }
153 |
154 | // Negative Y: up arrow key
155 | if (ctx->touch.y <= -y_threshold) {
156 |
157 | do {
158 | input_report_key(ctx->input_dev, KEY_UP, TRUE);
159 | input_report_key(ctx->input_dev, KEY_UP, FALSE);
160 | ctx->touch.y += y_threshold;
161 | } while (ctx->touch.y <= -y_threshold);
162 |
163 | // Positive Y: down arrow key
164 | } else if (ctx->touch.y > y_threshold) {
165 |
166 | do {
167 | input_report_key(ctx->input_dev, KEY_DOWN, TRUE);
168 | input_report_key(ctx->input_dev, KEY_DOWN, FALSE);
169 | ctx->touch.y -= y_threshold;
170 | } while (ctx->touch.y > y_threshold);
171 | }
172 | }
173 | }
174 |
175 | // Touch enabled: touchpad click sends enter / mouse click
176 | // Touch disabled: touchpad click enables touch mode
177 | int input_touch_consumes_keycode(struct kbd_ctx* ctx,
178 | uint8_t *remapped_keycode, uint8_t keycode, uint8_t state)
179 | {
180 | // Touchpad click
181 | // Touch off: enable touch
182 | // Touch on: enter or mouse click
183 | if (keycode == KEY_COMPOSE) {
184 |
185 | if (ctx->touch.enabled) {
186 |
187 | // Keys mode, send enter
188 | if ((ctx->touch.input_as == TOUCH_INPUT_AS_KEYS)
189 | && (state == KEY_STATE_RELEASED)) {
190 | input_report_key(ctx->input_dev, KEY_ENTER, TRUE);
191 | input_report_key(ctx->input_dev, KEY_ENTER, FALSE);
192 |
193 | // Mouse mode, send mouse click
194 | } else if (ctx->touch.input_as == TOUCH_INPUT_AS_MOUSE) {
195 | input_report_key(ctx->input_dev, BTN_LEFT,
196 | (state == KEY_STATE_PRESSED));
197 | }
198 |
199 | return 1;
200 |
201 | // If touch off, touchpad click will turn touch on
202 | } else if (state == KEY_STATE_RELEASED) {
203 | input_touch_enable(ctx);
204 |
205 | // Don't show indicator in mouse mode
206 | if (ctx->touch.input_as == TOUCH_INPUT_AS_KEYS) {
207 | input_touch_set_indicator(ctx);
208 | }
209 | }
210 |
211 | // Back key disables touch mode if touch enabled
212 | } else if (ctx->touch.enabled && (keycode == KEY_ESC)) {
213 |
214 | if (state == KEY_STATE_RELEASED) {
215 | input_touch_disable(ctx);
216 | }
217 |
218 | return 1;
219 |
220 | // Enable touch while shift is held
221 | } else if ((keycode == KEY_LEFTSHIFT) || (keycode == KEY_RIGHTSHIFT)) {
222 | if ((ctx->touch.activation == TOUCH_ACT_CLICK)
223 | && ctx->touch.enable_while_shift_held) {
224 |
225 | if (!ctx->touch.enabled && (state == KEY_STATE_PRESSED)) {
226 | ctx->touch.entry_while_shift_held = 0;
227 | input_touch_enable(ctx);
228 |
229 | } else if (ctx->touch.enabled && (state == KEY_STATE_RELEASED)) {
230 | input_touch_disable(ctx);
231 | if (ctx->touch.entry_while_shift_held) {
232 | ctx->touch.entry_while_shift_held = 0;
233 | input_modifiers_reset_shift(ctx);
234 | }
235 | }
236 | }
237 | }
238 |
239 | return 0;
240 | }
241 |
242 | void input_touch_enable(struct kbd_ctx *ctx)
243 | {
244 | ctx->touch.enabled = 1;
245 | input_fw_enable_touch_interrupts(ctx);
246 | }
247 |
248 | void input_touch_disable(struct kbd_ctx *ctx)
249 | {
250 | ctx->touch.enabled = 0;
251 | input_fw_disable_touch_interrupts(ctx);
252 |
253 | if (g_touch_indicator) {
254 | g_touch_indicator = 0;
255 | input_display_clear_indicator(6);
256 | }
257 | }
258 |
259 | void input_touch_set_activation(struct kbd_ctx *ctx, uint8_t activation)
260 | {
261 | if (activation == TOUCH_ACT_ALWAYS) {
262 | ctx->touch.activation = TOUCH_ACT_ALWAYS;
263 | input_touch_enable(ctx);
264 |
265 | } else if (activation == TOUCH_ACT_CLICK) {
266 | ctx->touch.activation = TOUCH_ACT_CLICK;
267 | input_touch_disable(ctx);
268 | }
269 | }
270 |
271 | void input_touch_set_input_as(struct kbd_ctx *ctx, uint8_t input_as)
272 | {
273 | ctx->touch.input_as = input_as;
274 |
275 | // Scale setting for touch input as keys
276 | if (input_as == TOUCH_INPUT_AS_KEYS) {
277 | enable_scale_2x(ctx);
278 |
279 | // Mouse does not apply scaling
280 | } else if (input_as == TOUCH_INPUT_AS_MOUSE) {
281 | disable_scale_2x(ctx);
282 | }
283 | }
284 |
285 | void input_touch_set_threshold(struct kbd_ctx *ctx, uint8_t threshold)
286 | {
287 | ctx->touch.threshold = threshold;
288 | }
289 |
290 | void input_touch_set_indicator(struct kbd_ctx *ctx)
291 | {
292 | g_touch_indicator = 1;
293 | input_display_set_indicator(6, ind_touch);
294 | }
295 |
--------------------------------------------------------------------------------
/src/main.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * main.c: Main C File.
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | #include "config.h"
13 | #include "debug_levels.h"
14 |
15 | #include "input_iface.h"
16 | #include "params_iface.h"
17 | #include "sysfs_iface.h"
18 |
19 | #if (BBQX0KBD_INT != BBQX0KBD_USE_INT)
20 | #error "Only supporting interrupts mode right now"
21 | #endif
22 |
23 | #if (BBQX0KBD_TYPE != BBQ20KBD_PMOD)
24 | #error "Only supporting BBQ20 keyboard right now"
25 | #endif
26 |
27 | static int beepy_kbd_probe
28 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
29 | (struct i2c_client* i2c_client, struct i2c_device_id const* i2c_id)
30 | #else
31 | (struct i2c_client* i2c_client)
32 | #endif
33 | {
34 | int rc;
35 |
36 | // Initialize key handler system
37 | if ((rc = input_probe(i2c_client))) {
38 | return rc;
39 | }
40 |
41 | // Initialize module parameters
42 | if ((rc = params_probe())) {
43 | return rc;
44 | }
45 |
46 | // Initialize sysfs interface
47 | if ((rc = sysfs_probe(i2c_client))) {
48 | return rc;
49 | }
50 |
51 | return 0;
52 | }
53 |
54 | static void beepy_kbd_shutdown(struct i2c_client* i2c_client)
55 | {
56 | sysfs_shutdown(i2c_client);
57 | params_shutdown();
58 | input_shutdown(i2c_client);
59 | }
60 |
61 | static void beepy_kbd_remove(struct i2c_client* i2c_client)
62 | {
63 | dev_info_fe(&i2c_client->dev,
64 | "%s Removing beepy-kbd.\n", __func__);
65 |
66 | beepy_kbd_shutdown(i2c_client);
67 | }
68 |
69 | // Driver definitions
70 |
71 | // Device IDs
72 | static const struct i2c_device_id beepy_kbd_i2c_device_id[] = {
73 | { "beepy-kbd", 0, },
74 | { }
75 | };
76 | MODULE_DEVICE_TABLE(i2c, beepy_kbd_i2c_device_id);
77 | static const struct of_device_id beepy_kbd_of_device_id[] = {
78 | { .compatible = "beepy-kbd", },
79 | { }
80 | };
81 | MODULE_DEVICE_TABLE(of, beepy_kbd_of_device_id);
82 |
83 | // Callbacks
84 | static struct i2c_driver beepy_kbd_driver = {
85 | .driver = {
86 | .name = "beepy-kbd",
87 | .of_match_table = beepy_kbd_of_device_id,
88 | },
89 | .probe = beepy_kbd_probe,
90 | .shutdown = beepy_kbd_shutdown,
91 | .remove = beepy_kbd_remove,
92 | .id_table = beepy_kbd_i2c_device_id,
93 | };
94 |
95 | // Module constructor
96 | static int __init beepy_kbd_init(void)
97 | {
98 | int rc;
99 |
100 | // Adding the I2C driver will call the _probe function to continue setup
101 | if ((rc = i2c_add_driver(&beepy_kbd_driver))) {
102 | pr_err("%s Could not initialise beepy-kbd! Error: %d\n",
103 | __func__, rc);
104 | return rc;
105 | }
106 | pr_info("%s Initalised beepy-kbd.\n", __func__);
107 |
108 | return rc;
109 | }
110 | module_init(beepy_kbd_init);
111 |
112 | // Module destructor
113 | static void __exit beepy_kbd_exit(void)
114 | {
115 | pr_info("%s Exiting beepy-kbd.\n", __func__);
116 | i2c_del_driver(&beepy_kbd_driver);
117 | }
118 | module_exit(beepy_kbd_exit);
119 |
120 | MODULE_LICENSE("GPL");
121 | MODULE_AUTHOR("wallComputer and Andrew D'Angelo ");
122 | MODULE_DESCRIPTION("BB Classic keyboard driver for Beepy");
123 | MODULE_VERSION("2.11");
124 |
--------------------------------------------------------------------------------
/src/params_iface.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * params_iface.c: Module parameters
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 |
11 | #include "config.h"
12 |
13 | #include "i2c_helper.h"
14 | #include "input_iface.h"
15 | #include "params_iface.h"
16 |
17 | // Kernel module parameters
18 | static char *touch_act_setting = "click"; // "click" or "always"
19 | static char *touch_shift_setting = "1"; // Hold Shift to temporarily enable touch
20 | static char *touch_as_setting = "keys"; // "keys" or "mouse"
21 | static char *touch_min_squal_setting = "16"; // Minimum surface quality to accept touch event
22 | static char *touch_led_setting = "high"; // "low", "med", "high"
23 | static uint32_t touch_threshold_setting = 8; // Touchpad move offset
24 | static char *handle_poweroff_setting = "0"; // Enable to have module invoke poweroff
25 | static char *shutdown_grace_setting = "30"; // 30 seconds between shutdown signal and poweroff
26 | static char *sharp_path_setting = "/dev/dri/card0"; // Path to Sharp display device
27 | static uint32_t sysfs_gid_setting = 0; // GID of files in /sys/firmware/beepy
28 | static char *auto_off_setting = // Enable to trigger a 30 second poweroff timer on driver unload
29 | #ifdef DEBUG
30 | "0";
31 | #else
32 | "1";
33 | #endif
34 |
35 | // Update touchpad activation setting in global context, if available
36 | static int set_touch_act_setting(struct kbd_ctx* ctx, char const* val)
37 | {
38 | // If no state was passed, just update the local setting without
39 | // changing I2C touch interrupts
40 | if (!ctx) {
41 | return 0;
42 | }
43 |
44 | // Touchpad only active when clicked
45 | if (strcmp(val, "click") == 0) {
46 | input_touch_set_activation(ctx, TOUCH_ACT_CLICK);
47 | return 0;
48 |
49 | // Touchpad always active
50 | } else if (strcmp(val, "always") == 0) {
51 | input_touch_set_activation(ctx, TOUCH_ACT_ALWAYS);
52 | return 0;
53 | }
54 |
55 | // Invalid parameter value
56 | return -1;
57 | }
58 |
59 | // Copy provided value to buffer and strip it of newlines
60 | static char* copy_and_strip(char* buf, size_t buf_len, const char* val)
61 | {
62 | strncpy(buf, val, buf_len - 1);
63 | buf[buf_len - 1] = '\0';
64 | return strstrip(buf);
65 | }
66 |
67 | // Activate touch when clicked, or always enabled
68 | static int touch_act_setting_param_set(const char *val, const struct kernel_param* kp)
69 | {
70 | char buf[8];
71 | char *stripped_val;
72 |
73 | stripped_val = copy_and_strip(buf, sizeof(buf), val);
74 |
75 | return (set_touch_act_setting(g_ctx, stripped_val) < 0)
76 | ? -EINVAL
77 | : param_set_charp(stripped_val, kp);
78 | }
79 |
80 | static const struct kernel_param_ops touch_act_setting_param_ops = {
81 | .set = touch_act_setting_param_set,
82 | .get = param_get_charp,
83 | };
84 | module_param_cb(touch_act, &touch_act_setting_param_ops, &touch_act_setting, 0664);
85 | MODULE_PARM_DESC(touch_act_setting, "Touchpad enabled after clicking (\"click\") or always enabled (\"always\")");
86 |
87 | // Update touch shift enable in global context
88 | static int set_touch_shift_setting(struct kbd_ctx *ctx, char const* val)
89 | {
90 | // If no state was passed, exit
91 | if (!ctx) {
92 | return 0;
93 | }
94 |
95 | ctx->touch.enable_while_shift_held = val[0] != '0';
96 | return 0;
97 | }
98 |
99 | // Hold shift to temporarily enable touch
100 | static int touch_shift_setting_param_set(const char *val, const struct kernel_param *kp)
101 | {
102 | char *stripped_val;
103 | char stripped_val_buf[2];
104 |
105 | // Copy provided value to buffer and strip it of newlines
106 | strncpy(stripped_val_buf, val, 2);
107 | stripped_val_buf[1] = '\0';
108 | stripped_val = strstrip(stripped_val_buf);
109 |
110 | return (set_touch_shift_setting(g_ctx, stripped_val) < 0)
111 | ? -EINVAL
112 | : param_set_charp(stripped_val, kp);
113 | }
114 |
115 | static const struct kernel_param_ops touch_shift_setting_param_ops = {
116 | .set = touch_shift_setting_param_set,
117 | .get = param_get_charp,
118 | };
119 |
120 | module_param_cb(touch_shift, &touch_shift_setting_param_ops, &touch_shift_setting, 0664);
121 | MODULE_PARM_DESC(touch_shift_setting, "Set to 1 to enable touch while Shift key is held");
122 |
123 | // Update touchpad mode setting in global context, if available
124 | static int set_touch_as_setting(struct kbd_ctx* ctx, char const* val)
125 | {
126 | // If no state was passed, just update the local setting without
127 | // changing I2C touch interrupts
128 | if (!ctx) {
129 | return 0;
130 | }
131 |
132 | // Touchpad sends arrow keys
133 | if (strcmp(val, "keys") == 0) {
134 | input_touch_set_input_as(ctx, TOUCH_INPUT_AS_KEYS);
135 | return 0;
136 |
137 | // Touchpad sends mouse
138 | } else if (strcmp(val, "mouse") == 0) {
139 | input_touch_set_input_as(ctx, TOUCH_INPUT_AS_MOUSE);
140 | return 0;
141 | }
142 |
143 | // Invalid parameter value
144 | return -1;
145 | }
146 |
147 | // Touchpad sends arrow keys or mouse
148 | static int touch_as_setting_param_set(const char *val, const struct kernel_param* kp)
149 | {
150 | char buf[8];
151 | char *stripped_val;
152 |
153 | stripped_val = copy_and_strip(buf, sizeof(buf), val);
154 |
155 | return (set_touch_as_setting(g_ctx, stripped_val) < 0)
156 | ? -EINVAL
157 | : param_set_charp(stripped_val, kp);
158 | }
159 |
160 | static const struct kernel_param_ops touch_as_setting_param_ops = {
161 | .set = touch_as_setting_param_set,
162 | .get = param_get_charp,
163 | };
164 | module_param_cb(touch_as, &touch_as_setting_param_ops, &touch_as_setting, 0664);
165 | MODULE_PARM_DESC(touch_as_setting, "Touchpad sends arrow keys (\"keys\") or mouse (\"mouse\")");
166 |
167 | // Set touchpad minimum surface quality level
168 | static int set_touch_min_squal_setting(struct kbd_ctx *ctx, char const* val)
169 | {
170 | int parsed_val;
171 |
172 | // If no state was passed, exit
173 | if (!ctx) {
174 | return 0;
175 | }
176 |
177 | // Parse setting
178 | if ((parsed_val = parse_u8(val)) < 0) {
179 | return parsed_val;
180 | }
181 |
182 | // Write setting to firmware
183 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_MIN_SQUAL, (uint8_t)parsed_val);
184 |
185 | return 0;
186 | }
187 |
188 | // Touchpad minimum surface quality level
189 | static int touch_min_squal_param_set(const char *val, const struct kernel_param *kp)
190 | {
191 | char *stripped_val;
192 | char stripped_val_buf[4];
193 |
194 | // Copy provided value to buffer and strip it of newlines
195 | strncpy(stripped_val_buf, val, 4);
196 | stripped_val_buf[3] = '\0';
197 | stripped_val = strstrip(stripped_val_buf);
198 |
199 | return (set_touch_min_squal_setting(g_ctx, stripped_val) < 0)
200 | ? -EINVAL
201 | : param_set_charp(stripped_val, kp);
202 | }
203 |
204 | static const struct kernel_param_ops touch_min_squal_param_ops = {
205 | .set = touch_min_squal_param_set,
206 | .get = param_get_charp,
207 | };
208 |
209 | module_param_cb(touch_min_squal, &touch_min_squal_param_ops, &touch_min_squal_setting, 0664);
210 | MODULE_PARM_DESC(touch_min_squal_setting, "Minimum surface quality to accept touch event");
211 |
212 | // Set touchpad move threshold
213 | static int set_touch_threshold_setting(struct kbd_ctx *ctx, char const* val)
214 | {
215 | int parsed_val;
216 |
217 | // If no state was passed, exit
218 | if (!ctx) {
219 | return 0;
220 | }
221 |
222 | // Parse setting
223 | if ((parsed_val = parse_u8(val)) < 0) {
224 | return parsed_val;
225 | }
226 |
227 | // Check setting
228 | if ((parsed_val < 4) || (parsed_val > 255)) {
229 | return -1;
230 | }
231 |
232 | // Store setting
233 | input_touch_set_threshold(ctx, (uint8_t)parsed_val);
234 |
235 | return 0;
236 | }
237 |
238 | // Touchpad move threshold
239 | static int touch_threshold_param_set(const char *val, const struct kernel_param *kp)
240 | {
241 | char *stripped_val;
242 | char stripped_val_buf[4];
243 |
244 | // Copy provided value to buffer and strip it of newlines
245 | strncpy(stripped_val_buf, val, 4);
246 | stripped_val_buf[3] = '\0';
247 | stripped_val = strstrip(stripped_val_buf);
248 |
249 | return (set_touch_threshold_setting(g_ctx, stripped_val) < 0)
250 | ? -EINVAL
251 | : param_set_uint(stripped_val, kp);
252 | }
253 |
254 | static const struct kernel_param_ops touch_threshold_param_ops = {
255 | .set = touch_threshold_param_set,
256 | .get = param_get_uint,
257 | };
258 |
259 | module_param_cb(touch_threshold, &touch_threshold_param_ops, &touch_threshold_setting, 0664);
260 | MODULE_PARM_DESC(touch_threshold_setting, "Send touch event above this threshold (minimum 4, default 8)");
261 |
262 | // Set touchpad LED power level
263 | static int set_touch_led_setting(struct kbd_ctx* ctx, char const* val)
264 | {
265 | if (strcmp(val, "low") == 0) {
266 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_LED, TOUCHPAD_LED_LOW);
267 | return 0;
268 |
269 | } else if (strcmp(val, "med") == 0) {
270 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_LED, TOUCHPAD_LED_MED);
271 | return 0;
272 |
273 | } else if (strcmp(val, "high") == 0) {
274 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_TOUCHPAD_LED, TOUCHPAD_LED_HIGH);
275 | return 0;
276 | }
277 |
278 | // Invalid parameter value
279 | return -1;
280 | }
281 |
282 | // Touchpad LED level
283 | static int touch_led_setting_param_set(const char *val, const struct kernel_param* kp)
284 | {
285 | char buf[8];
286 | char *stripped_val;
287 |
288 | stripped_val = copy_and_strip(buf, sizeof(buf), val);
289 |
290 | return (set_touch_led_setting(g_ctx, stripped_val) < 0)
291 | ? -EINVAL
292 | : param_set_charp(stripped_val, kp);
293 | }
294 |
295 | static const struct kernel_param_ops touch_led_setting_param_ops = {
296 | .set = touch_led_setting_param_set,
297 | .get = param_get_charp,
298 | };
299 | module_param_cb(touch_led, &touch_led_setting_param_ops, &touch_led_setting, 0664);
300 | MODULE_PARM_DESC(touch_led_setting, "Touchpad LED power level (\"low\", \"med\", \"high\")");
301 |
302 | // Update poweroff setting in global context, if available
303 | static int set_handle_poweroff_setting(struct kbd_ctx *ctx, char const* val)
304 | {
305 | // If no state was passed, exit
306 | if (!ctx) {
307 | return 0;
308 | }
309 |
310 | input_fw_set_handle_poweroff(ctx, val[0] != '0');
311 | return 0;
312 | }
313 |
314 | // Whether or not to handle poweroff in driver (for minimal system without ACPI)
315 | static int handle_poweroff_setting_param_set(const char *val, const struct kernel_param *kp)
316 | {
317 | char *stripped_val;
318 | char stripped_val_buf[2];
319 |
320 | // Copy provided value to buffer and strip it of newlines
321 | strncpy(stripped_val_buf, val, 2);
322 | stripped_val_buf[1] = '\0';
323 | stripped_val = strstrip(stripped_val_buf);
324 |
325 | return (set_handle_poweroff_setting(g_ctx, stripped_val) < 0)
326 | ? -EINVAL
327 | : param_set_charp(stripped_val, kp);
328 | }
329 |
330 | static const struct kernel_param_ops handle_poweroff_setting_param_ops = {
331 | .set = handle_poweroff_setting_param_set,
332 | .get = param_get_charp,
333 | };
334 |
335 | module_param_cb(handle_poweroff, &handle_poweroff_setting_param_ops, &handle_poweroff_setting, 0664);
336 | MODULE_PARM_DESC(handle_poweroff_setting, "Set to 1 to invoke /sbin/poweroff when power key is held");
337 |
338 | // Update shutdown grace time in firmware
339 | static int set_shutdown_grace_setting(struct kbd_ctx *ctx, char const* val)
340 | {
341 | int parsed_val;
342 |
343 | // If no state was passed, exit
344 | if (!ctx) {
345 | return 0;
346 | }
347 |
348 | // Parse setting
349 | if ((parsed_val = parse_u8(val)) < 0) {
350 | return parsed_val;
351 | }
352 |
353 | // Check setting (minimum 5 seconds)
354 | if ((parsed_val < 5) || (parsed_val > 255)) {
355 | return -EINVAL;
356 | }
357 |
358 | // Write setting to firmware
359 | (void)kbd_write_i2c_u8(ctx->i2c_client, REG_SHUTDOWN_GRACE, (uint8_t)parsed_val);
360 |
361 | return 0;
362 | }
363 |
364 | // Time between shutdown signal and power off in seconds
365 | static int shutdown_grace_param_set(const char *val, const struct kernel_param *kp)
366 | {
367 | char *stripped_val;
368 | char stripped_val_buf[4];
369 |
370 | // Copy provided value to buffer and strip it of newlines
371 | strncpy(stripped_val_buf, val, 4);
372 | stripped_val_buf[3] = '\0';
373 | stripped_val = strstrip(stripped_val_buf);
374 |
375 | return (set_shutdown_grace_setting(g_ctx, stripped_val) < 0)
376 | ? -EINVAL
377 | : param_set_charp(stripped_val, kp);
378 | }
379 |
380 | static const struct kernel_param_ops shutdown_grace_param_ops = {
381 | .set = shutdown_grace_param_set,
382 | .get = param_get_charp,
383 | };
384 |
385 | module_param_cb(shutdown_grace, &shutdown_grace_param_ops, &shutdown_grace_setting, 0664);
386 | MODULE_PARM_DESC(shutdown_grace_setting, "Set delay in seconds from shutdown signal to poweroff");
387 |
388 | // Path to Sharp DRM device
389 | static int sharp_path_param_set(const char *val, const struct kernel_param *kp)
390 | {
391 | char *stripped_val;
392 | char stripped_val_buf[64];
393 |
394 | // Copy provided value to buffer and strip it of newlines
395 | strncpy(stripped_val_buf, val, sizeof(stripped_val_buf));
396 | stripped_val_buf[sizeof(stripped_val_buf) - 1] = '\0';
397 | stripped_val = strstrip(stripped_val_buf);
398 |
399 | return (input_display_valid_sharp_path(stripped_val))
400 | ? param_set_charp(stripped_val, kp)
401 | : -EINVAL;
402 | }
403 |
404 | static const struct kernel_param_ops sharp_path_param_ops = {
405 | .set = param_set_charp,
406 | .get = param_get_charp,
407 | };
408 |
409 | module_param_cb(sharp_path, &sharp_path_param_ops, &sharp_path_setting, 0664);
410 | MODULE_PARM_DESC(sharp_path_setting, "Set path to Sharp display DRM device");
411 |
412 | // Time between shutdown signal and power off in seconds
413 | static int sysfs_gid_param_set(const char *val, const struct kernel_param *kp)
414 | {
415 | char *stripped_val;
416 | char stripped_val_buf[11];
417 |
418 | // Copy provided value to buffer and strip it of newlines
419 | strncpy(stripped_val_buf, val, 11);
420 | stripped_val_buf[10] = '\0';
421 | stripped_val = strstrip(stripped_val_buf);
422 |
423 | return param_set_uint(stripped_val, kp);
424 | }
425 |
426 | static const struct kernel_param_ops sysfs_gid_param_ops = {
427 | .set = sysfs_gid_param_set,
428 | .get = param_get_uint,
429 | };
430 |
431 | module_param_cb(sysfs_gid, &sysfs_gid_param_ops, &sysfs_gid_setting, 0664);
432 | MODULE_PARM_DESC(sysfs_gid_setting, "Set group ID for entries in /sys/firmware/beepy");
433 |
434 | // Trigger shutdown on driver unload
435 | static int set_auto_off_setting(struct kbd_ctx *ctx, char const* val)
436 | {
437 | // If no state was passed, exit
438 | if (!ctx) {
439 | return 0;
440 | }
441 |
442 | input_fw_set_auto_off(ctx, val[0] != '0');
443 | return 0;
444 | }
445 |
446 | static int auto_off_setting_param_set(const char *val, const struct kernel_param *kp)
447 | {
448 | char *stripped_val;
449 | char stripped_val_buf[2];
450 |
451 | // Copy provided value to buffer and strip it of newlines
452 | strncpy(stripped_val_buf, val, 2);
453 | stripped_val_buf[1] = '\0';
454 | stripped_val = strstrip(stripped_val_buf);
455 |
456 | return (set_auto_off_setting(g_ctx, stripped_val) < 0)
457 | ? -EINVAL
458 | : param_set_charp(stripped_val, kp);
459 | }
460 |
461 | static const struct kernel_param_ops auto_off_setting_param_ops = {
462 | .set = auto_off_setting_param_set,
463 | .get = param_get_charp,
464 | };
465 |
466 | module_param_cb(auto_off, &auto_off_setting_param_ops, &auto_off_setting, 0664);
467 | MODULE_PARM_DESC(auto_off_setting, "Automatically shut down and enter deep sleep when driver is unloaded");
468 |
469 | // No setup
470 | int params_probe(void)
471 | {
472 | int rc;
473 |
474 | // Set initial touchpad settings based on module's parameter
475 | if ((rc = set_touch_act_setting(g_ctx, touch_act_setting)) < 0) {
476 | return rc;
477 | }
478 | if ((rc = set_touch_as_setting(g_ctx, touch_as_setting)) < 0) {
479 | return rc;
480 | }
481 | if ((rc = set_touch_min_squal_setting(g_ctx, touch_min_squal_setting)) < 0) {
482 | return rc;
483 | }
484 | if ((rc = set_handle_poweroff_setting(g_ctx, handle_poweroff_setting)) < 0) {
485 | return rc;
486 | }
487 | if ((rc = set_auto_off_setting(g_ctx, auto_off_setting)) < 0) {
488 | return rc;
489 | }
490 |
491 | return 0;
492 | }
493 |
494 | // No cleanup
495 | void params_shutdown(void)
496 | {
497 | return;
498 | }
499 |
500 | char const* params_get_sharp_path(void)
501 | {
502 | return sharp_path_setting;
503 | }
504 |
505 | uint32_t params_get_sysfs_gid(void)
506 | {
507 | return sysfs_gid_setting;
508 | }
509 |
--------------------------------------------------------------------------------
/src/params_iface.h:
--------------------------------------------------------------------------------
1 |
2 | #ifndef PARAMS_IFACE_H_
3 | #define PARAMS_IFACE_H_
4 |
5 | // SPDX-License-Identifier: GPL-2.0-only
6 | /*
7 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
8 | */
9 |
10 | #include "input_iface.h"
11 |
12 | int params_probe(void);
13 | void params_shutdown(void);
14 |
15 | char const* params_get_sharp_path(void);
16 | uint32_t params_get_sysfs_gid(void);
17 |
18 | #endif
19 |
--------------------------------------------------------------------------------
/src/registers.h:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: GPL-2.0 */
2 | /*
3 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
4 | * bbq10kbd_registers.h: Registers in BBQ10 Keyboard Software.
5 | */
6 |
7 | #ifndef BBQX0KBD_REGISTERS_H_
8 | #define BBQX0KBD_REGISTERS_H_
9 |
10 | #include "config.h"
11 |
12 |
13 | #define BBQX0KBD_I2C_ADDRESS BBQX0KBD_ASSIGNED_I2C_ADDRESS
14 |
15 | #define BBQX0KBD_WRITE_MASK 0x80
16 | #define BBQX0KBD_FIFO_SIZE 31
17 |
18 | #define REG_VER 0x01
19 | #if (BBQX0KBD_TYPE == BBQ10KBD_FEATHERWING)
20 | #define BBQX0KBD_I2C_SW_VERSION 0x04 // Unused for now since the version numbering has reset.
21 | #endif
22 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
23 | #define BBQX0KBD_I2C_SW_VERSION 0x10
24 | #endif
25 | #define REG_CFG 0x02
26 | #define REG_CFG_USE_MODS BIT(7)
27 | #define REG_CFG_REPORT_MODS BIT(6)
28 | #define REG_CFG_PANIC_INT BIT(5)
29 | #define REG_CFG_KEY_INT BIT(4)
30 | #define REG_CFG_NUMLOCK_INT BIT(3)
31 | #define REG_CFG_CAPSLOCK_INT BIT(2)
32 | #define REG_CFG_OVERFLOW_INT BIT(1)
33 | #define REG_CFG_OVERFLOW_ON BIT(0)
34 | #define REG_CFG_DEFAULT_SETTING (REG_CFG_REPORT_MODS | REG_CFG_KEY_INT | REG_CFG_OVERFLOW_ON | REG_CFG_OVERFLOW_INT)
35 | // Configurations Used By Arturo182's Code.
36 | // #define REG_CFG_DEFAULT_SETTING (REG_CFG_OVERFLOW_ON | REG_CFG_OVERFLOW_INT | REG_CFG_CAPSLOCK_INT | REG_CFG_NUMLOCK_INT | REG_CFG_KEY_INT | REG_CFG_REPORT_MODS )
37 |
38 | #define REG_INT 0x03
39 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
40 | #define REG_INT_TOUCH BIT(6)
41 | #endif
42 | #define REG_INT_GPIO BIT(5)
43 | #define REG_INT_PANIC BIT(4)
44 | #define REG_INT_KEY BIT(3)
45 | #define REG_INT_NUMLOCK BIT(2)
46 | #define REG_INT_CAPSLOCK BIT(1)
47 | #define REG_INT_OVERFLOW BIT(0)
48 | #define REG_INT_RESET_VALUE 0x00
49 |
50 | #define REG_KEY 0x04
51 | #define REG_KEY_NUMLOCK BIT(6)
52 | #define REG_KEY_CAPSLOCK BIT(5)
53 | #define REG_KEY_KEYCOUNT_MASK 0x1F
54 |
55 | #define REG_BKL 0x05
56 |
57 | #define REG_DEB 0x06
58 |
59 | #define REG_FRQ 0x07
60 |
61 | #define REG_RST 0x08
62 | #define REG_FIF 0x09
63 | #define KEY_IDLE_STATE 0
64 | #define KEY_PRESSED_STATE 1
65 | #define KEY_PRESSED_AND_HELD_STATE 2
66 | #define KEY_RELEASED_STATE 3
67 |
68 | #define REG_BK2 0x0A
69 |
70 | #define REG_DIR 0x0B
71 | #define REG_DIR_INPUT 1
72 | #define REG_DIR_OUTPUT 0
73 |
74 | #define REG_PUE 0x0C
75 | #define REG_PUE_ENABLE 1
76 | #define REG_PUE_DISABLE 0
77 |
78 |
79 | #define REG_PUD 0x0D
80 | #define REG_PUD_PULL_UP 1
81 | #define REG_PUD_PULL_DOWN 0
82 |
83 |
84 | #define REG_GIO 0x0E
85 |
86 | #define REG_GIC 0x0F
87 | #define REG_GIC_INTERRUPT_TRIGGER 1
88 | #define REG_GIC_NO_INTERRUPT_TRIGGER 0
89 |
90 | #define REG_GIN 0x10
91 | #define REG_GIN_RESET_VALUE 0x00
92 |
93 | #define BBQ10_BRIGHTNESS_DELTA 64
94 |
95 | #if (BBQX0KBD_TYPE == BBQ20KBD_PMOD)
96 | #define REG_CF2 0x14
97 | #define REG_CF2_AUTO_OFF BIT(3)
98 | #define REG_CF2_USB_MOUSE_ON BIT(2)
99 | #define REG_CF2_USB_KEYB_ON BIT(1)
100 | #define REG_CF2_TOUCH_INT BIT(0)
101 | #ifdef DEBUG
102 | #define REG_CFG2_DEFAULT_SETTING (REG_CF2_TOUCH_INT)
103 | #else
104 | #define REG_CFG2_DEFAULT_SETTING (REG_CF2_TOUCH_INT | REG_CF2_AUTO_OFF)
105 | #endif
106 |
107 | #define REG_TOX 0x15
108 | #define REG_TOY 0x16
109 |
110 | #define REG_ADC 0x17
111 | #define REG_LED 0x20
112 | #define REG_LED_R 0x21
113 | #define REG_LED_G 0x22
114 | #define REG_LED_B 0x23
115 |
116 | #define REG_REWAKE_MINS 0x24
117 | #define REG_SHUTDOWN_GRACE 0x25
118 |
119 | #define REG_RTC_SEC 0x26
120 | #define REG_RTC_MIN 0x27
121 | #define REG_RTC_HOUR 0x28
122 | #define REG_RTC_MDAY 0x29
123 | #define REG_RTC_MON 0x2A
124 | #define REG_RTC_YEAR 0x2B
125 | #define REG_RTC_COMMIT 0x2C
126 |
127 | #define REG_DRIVER_STATE 0x2D
128 |
129 | #define REG_STARTUP_REASON 0x2E
130 | #define STARTUP_REASON_FW_INIT 0x1
131 | #define STARTUP_REASON_BUTTON 0x2
132 | #define STARTUP_REASON_REWAKE 0x3
133 | #define STARTUP_REASON_REWAKE_CANCELED 0x4
134 |
135 | #define REG_UPDATE_DATA 0x30
136 | #define UPDATE_OFF 0
137 | #define UPDATE_RECV 1
138 | #define UPDATE_FAILED 2
139 | #define UPDATE_FAILED_LINE_OVERFLOW 3
140 | #define UPDATE_FAILED_FLASH_EMPTY 4
141 | #define UPDATE_FAILED_FLASH_OVERFLOW 5
142 | #define UPDATE_FAILED_BAD_LINE 6
143 | #define UPDATE_FAILED_BAD_CHECKSUM 7
144 |
145 | #define REG_TOUCHPAD_REG 0x40
146 | #define REG_TOUCHPAD_VAL 0x41
147 |
148 | #define REG_TOUCHPAD_MIN_SQUAL 0x42
149 | #define REG_TOUCHPAD_LED 0x43
150 | #define TOUCHPAD_LED_MED 0x0
151 | #define TOUCHPAD_LED_HIGH 0x3
152 | #define TOUCHPAD_LED_LOW 0x5
153 |
154 | #define REG_TOUCHPAD_REG_ENGINE 0x60
155 | #define REG_TOUCHPAD_ENGINE_XY_SCALE (1 << 1)
156 | #define REG_TOUCHPAD_REG_SPEED 0x63
157 | #define REG_TOUCHPAD_SPEED_X_SCALE2 (1 << 6)
158 | #define REG_TOUCHPAD_SPEED_Y_SCALE2 (1 << 7)
159 |
160 | #endif
161 |
162 |
163 | #endif
164 |
--------------------------------------------------------------------------------
/src/sysfs_iface.c:
--------------------------------------------------------------------------------
1 | // SPDX-License-Identifier: GPL-2.0-only
2 | /*
3 | * Kernel driver for Q20 keyboard by ardangelo
4 | * References work by arturo182, wallComputer
5 | * sysfs.c: /sys/firmware/beepy interface
6 | */
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 |
15 | #include "config.h"
16 |
17 | #include "i2c_helper.h"
18 | #include "input_iface.h"
19 | #include "params_iface.h"
20 | #include "sysfs_iface.h"
21 |
22 | // Read battery level over I2C
23 | static int read_raw_battery_level(void)
24 | {
25 | int rc;
26 | uint8_t battery_level[2];
27 |
28 | // Make sure I2C client was initialized
29 | if ((g_ctx == NULL) || (g_ctx->i2c_client == NULL)) {
30 | return -EINVAL;
31 | }
32 |
33 | // Read battery level
34 | if ((rc = kbd_read_i2c_2u8(g_ctx->i2c_client, REG_ADC, battery_level)) < 0) {
35 | return rc;
36 | }
37 |
38 | // Calculate raw battery level
39 | return (battery_level[1] << 8) | battery_level[0];
40 | }
41 |
42 | static int parse_and_write_i2c_u8(char const* buf, size_t count, uint8_t reg)
43 | {
44 | int parsed;
45 |
46 | // Parse string entry
47 | if ((parsed = parse_u8(buf)) < 0) {
48 | return -EINVAL;
49 | }
50 |
51 | // Write value to LED register if available
52 | if (g_ctx && g_ctx->i2c_client) {
53 | kbd_write_i2c_u8(g_ctx->i2c_client, reg, (uint8_t)parsed);
54 | }
55 |
56 | return count;
57 | }
58 |
59 | // Sysfs entries
60 |
61 | // Raw battery level
62 | static ssize_t battery_raw_show(struct kobject *kobj, struct kobj_attribute *attr,
63 | char *buf)
64 | {
65 | int total_level;
66 |
67 | // Read raw level
68 | if ((total_level = read_raw_battery_level()) < 0) {
69 | return total_level;
70 | }
71 |
72 | // Format into buffer
73 | return sprintf(buf, "%d\n", total_level);
74 | }
75 | struct kobj_attribute battery_raw_attr
76 | = __ATTR(battery_raw, 0444, battery_raw_show, NULL);
77 |
78 | // Battery volts level
79 | static ssize_t battery_volts_show(struct kobject *kobj, struct kobj_attribute *attr,
80 | char *buf)
81 | {
82 | int volts_fp;
83 |
84 | // Read raw level
85 | if ((volts_fp = read_raw_battery_level()) < 0) {
86 | return volts_fp;
87 | }
88 |
89 | // Calculate voltage in fixed point
90 | volts_fp *= 330 * 21;
91 | volts_fp /= 4095;
92 |
93 | // Format into buffer
94 | return sprintf(buf, "%d.%03d\n", volts_fp / 1000, volts_fp % 1000);
95 | }
96 | struct kobj_attribute battery_volts_attr
97 | = __ATTR(battery_volts, 0444, battery_volts_show, NULL);
98 |
99 | // Battery percent approximate
100 | static ssize_t battery_percent_show(struct kobject *kobj, struct kobj_attribute *attr,
101 | char *buf)
102 | {
103 | int percent;
104 |
105 | // Read raw level
106 | if ((percent = read_raw_battery_level()) < 0) {
107 | return percent;
108 | }
109 |
110 | // Calculate voltage in fixed point
111 | percent *= 330 * 21;
112 | percent /= 4095;
113 |
114 | // Range from 3.2V min to 4.2V max
115 | // `percent` currently contains the fp voltage value (v * 1000; e.g. in the range from 3200 to 4200)
116 | // To convert to a percentage in the range from 0 - 1 we subtract the value at 0% (3200)
117 | // and divide by the difference between lowest value and higest value (4200 - 3200). Then
118 | // multiply by 100 to get 0 - 100. This can all be simplified to:
119 | percent -= 3200;
120 | percent /= 10;
121 |
122 | // If the voltage goes above 4.2v the percentage will go above 100. This can happen while plugged in.
123 | if (percent > 100) {
124 | percent = 100;
125 | // If the voltage drops below 3.2v the percentage will go below 0, so cap at 0.
126 | } else if (percent < 0) {
127 | percent = 0;
128 | }
129 |
130 | // Format into buffer
131 | return sprintf(buf, "%d\n", percent);
132 | }
133 | struct kobj_attribute battery_percent_attr
134 | = __ATTR(battery_percent, 0444, battery_percent_show, NULL);
135 |
136 | // LED on or off
137 | static ssize_t led_store(struct kobject *kobj, struct kobj_attribute *attr,
138 | char const *buf, size_t count)
139 | {
140 | return parse_and_write_i2c_u8(buf, count, REG_LED);
141 | }
142 | struct kobj_attribute led_attr = __ATTR(led, 0220, NULL, led_store);
143 |
144 | // LED red value
145 | static ssize_t led_red_store(struct kobject *kobj, struct kobj_attribute *attr,
146 | char const *buf, size_t count)
147 | {
148 | return parse_and_write_i2c_u8(buf, count, REG_LED_R);
149 | }
150 | struct kobj_attribute led_red_attr = __ATTR(led_red, 0220, NULL, led_red_store);
151 |
152 | // LED green value
153 | static ssize_t led_green_store(struct kobject *kobj, struct kobj_attribute *attr,
154 | char const *buf, size_t count)
155 | {
156 | return parse_and_write_i2c_u8(buf, count, REG_LED_G);
157 | }
158 | struct kobj_attribute led_green_attr = __ATTR(led_green, 0220, NULL, led_green_store);
159 |
160 | // LED blue value
161 | static ssize_t __used led_blue_store(struct kobject *kobj, struct kobj_attribute *attr,
162 | char const *buf, size_t count)
163 | {
164 | return parse_and_write_i2c_u8(buf, count, REG_LED_B);
165 | }
166 | struct kobj_attribute led_blue_attr = __ATTR(led_blue, 0220, NULL, led_blue_store);
167 |
168 | // Keyboard backlight value
169 | static ssize_t __used keyboard_backlight_store(struct kobject *kobj,
170 | struct kobj_attribute *attr, char const *buf, size_t count)
171 | {
172 | return parse_and_write_i2c_u8(buf, count, REG_BKL);
173 | }
174 | struct kobj_attribute keyboard_backlight_attr
175 | = __ATTR(keyboard_backlight, 0220, NULL, keyboard_backlight_store);
176 |
177 | // Shutdown and rewake timer in minutes
178 | static ssize_t __used rewake_timer_store(struct kobject *kobj,
179 | struct kobj_attribute *attr, char const *buf, size_t count)
180 | {
181 | return parse_and_write_i2c_u8(buf, count, REG_REWAKE_MINS);
182 | }
183 | struct kobj_attribute rewake_timer_attr
184 | = __ATTR(rewake_timer, 0220, NULL, rewake_timer_store);
185 |
186 | // Firmware version
187 | static ssize_t fw_version_show(struct kobject *kobj, struct kobj_attribute *attr,
188 | char *buf)
189 | {
190 | int rc;
191 | uint8_t version;
192 |
193 | // Make sure I2C client was initialized
194 | if ((g_ctx == NULL) || (g_ctx->i2c_client == NULL)) {
195 | return -EINVAL;
196 | }
197 |
198 | // Read firmware version
199 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_VER, &version)) < 0) {
200 | return rc;
201 | }
202 |
203 | return sprintf(buf, "%d.%d\n", version >> 4, version & 0xf);
204 | }
205 | struct kobj_attribute fw_version_attr
206 | = __ATTR(fw_version, 0444, fw_version_show, NULL);
207 |
208 | // Why the Pi was powered on
209 | static ssize_t startup_reason_show(struct kobject *kobj, struct kobj_attribute *attr,
210 | char *buf)
211 | {
212 | int rc;
213 | uint8_t reason;
214 |
215 | // Make sure I2C client was initialized
216 | if ((g_ctx == NULL) || (g_ctx->i2c_client == NULL)) {
217 | return -EINVAL;
218 | }
219 |
220 | // Read startup reason
221 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_STARTUP_REASON, &reason)) < 0) {
222 | return rc;
223 | }
224 |
225 | switch (reason) {
226 |
227 | case STARTUP_REASON_FW_INIT: return sprintf(buf, "fw_init\n");
228 | case STARTUP_REASON_BUTTON: return sprintf(buf, "power_button\n");
229 | case STARTUP_REASON_REWAKE: return sprintf(buf, "rewake\n");
230 | case STARTUP_REASON_REWAKE_CANCELED: return sprintf(buf, "rewake_canceled\n");
231 | }
232 |
233 | return sprintf(buf, "unknown: %d\n", reason);
234 | }
235 | struct kobj_attribute startup_reason_attr
236 | = __ATTR(startup_reason, 0444, startup_reason_show, NULL);
237 |
238 | // Firmware update
239 | static ssize_t __used fw_update_store(struct kobject *kobj,
240 | struct kobj_attribute *attr, char const *buf, size_t count)
241 | {
242 | int rc;
243 | size_t i;
244 | uint8_t update_state;
245 | char const* update_error = NULL;
246 |
247 | // Write value to update
248 | if (g_ctx && g_ctx->i2c_client) {
249 |
250 | // Read update status
251 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_UPDATE_DATA, &update_state)) < 0) {
252 | return rc;
253 | }
254 |
255 | // Start a new update
256 | if ((update_state == UPDATE_OFF) || (update_state >= UPDATE_FAILED)) {
257 | dev_info(&g_ctx->i2c_client->dev,
258 | "fw_update: starting new update, writing %zu bytes\n", count);
259 |
260 | // In-progress update
261 | } else if (update_state == UPDATE_RECV) {
262 | dev_info(&g_ctx->i2c_client->dev,
263 | "fw_update: writing %zu bytes\n", count);
264 | }
265 |
266 | for (i = 0; i < count; i++) {
267 | kbd_write_i2c_u8(g_ctx->i2c_client, REG_UPDATE_DATA, (uint8_t)buf[i]);
268 | }
269 |
270 | // Read update status
271 | if ((rc = kbd_read_i2c_u8(g_ctx->i2c_client, REG_UPDATE_DATA, &update_state)) < 0) {
272 | return rc;
273 | }
274 |
275 | // Successful update
276 | if (update_state == UPDATE_OFF) {
277 | dev_info(&g_ctx->i2c_client->dev,
278 | "fw_update: wrote %zu bytes, update completed\n", count);
279 |
280 | // Update still in-progress
281 | } else if (update_state == UPDATE_RECV) {
282 | dev_info(&g_ctx->i2c_client->dev,
283 | "fw_update: wrote %zu bytes\n", count);
284 |
285 | // Update failed
286 | } else if (update_state >= UPDATE_FAILED) {
287 |
288 | update_error = "update failed";
289 |
290 | switch (update_state) {
291 | case UPDATE_FAILED_LINE_OVERFLOW:
292 | update_error = "hex line too long"; break;
293 | case UPDATE_FAILED_FLASH_EMPTY:
294 | update_error = "flash image empty"; break;
295 | case UPDATE_FAILED_FLASH_OVERFLOW:
296 | update_error = "flash image > 64k"; break;
297 | case UPDATE_FAILED_BAD_LINE:
298 | update_error = "could not parse hex line"; break;
299 | case UPDATE_FAILED_BAD_CHECKSUM:
300 | update_error = "bad checksum"; break;
301 | }
302 |
303 | dev_info(&g_ctx->i2c_client->dev,
304 | "fw_update: failed: %s\n", update_error);
305 | return -EINVAL;
306 | }
307 | }
308 |
309 | return count;
310 | }
311 | struct kobj_attribute fw_update_attr
312 | = __ATTR(fw_update, 0220, NULL, fw_update_store);
313 |
314 | // Time since last keypress in milliseconds
315 | static ssize_t last_keypress_show(struct kobject *kobj, struct kobj_attribute *attr,
316 | char *buf)
317 | {
318 | uint64_t last_keypress_ms;
319 |
320 | if (g_ctx) {
321 |
322 | // Get time in ns
323 | last_keypress_ms = ktime_get_boottime_ns();
324 | if (g_ctx->last_keypress_at < last_keypress_ms) {
325 | last_keypress_ms -= g_ctx->last_keypress_at;
326 |
327 | // Calculate time in milliseconds
328 | last_keypress_ms = div_u64(last_keypress_ms, 1000000);
329 |
330 | // Format into buffer
331 | return sprintf(buf, "%lld\n", last_keypress_ms);
332 | }
333 | }
334 |
335 | return sprintf(buf, "-1\n");
336 | }
337 | struct kobj_attribute last_keypress_attr
338 | = __ATTR(last_keypress, 0444, last_keypress_show, NULL);
339 |
340 | // Sysfs attributes (entries)
341 | struct kobject *beepy_kobj = NULL;
342 | static struct attribute *beepy_attrs[] = {
343 | &battery_raw_attr.attr,
344 | &battery_volts_attr.attr,
345 | &battery_percent_attr.attr,
346 | &led_attr.attr,
347 | &led_red_attr.attr,
348 | &led_green_attr.attr,
349 | &led_blue_attr.attr,
350 | &keyboard_backlight_attr.attr,
351 | &rewake_timer_attr.attr,
352 | &startup_reason_attr.attr,
353 | &fw_version_attr.attr,
354 | &fw_update_attr.attr,
355 | &last_keypress_attr.attr,
356 | NULL,
357 | };
358 | static struct attribute_group beepy_attr_group = {
359 | .attrs = beepy_attrs
360 | };
361 |
362 | static void beepy_get_ownership
363 | #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
364 | (struct kobject *kobj, kuid_t *uid, kgid_t *gid)
365 | #else
366 | (struct kobject const *kobj, kuid_t *uid, kgid_t *gid)
367 | #endif
368 | {
369 | if (gid != NULL) {
370 | gid->val = params_get_sysfs_gid();
371 | }
372 | }
373 |
374 | static struct kobj_type beepy_ktype = {
375 | .get_ownership = beepy_get_ownership,
376 | .sysfs_ops = &kobj_sysfs_ops
377 | };
378 |
379 | int sysfs_probe(struct i2c_client* i2c_client)
380 | {
381 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
382 | int rc;
383 | #endif
384 |
385 | // Allocate custom sysfs type
386 | if ((beepy_kobj = devm_kzalloc(&i2c_client->dev, sizeof(*beepy_kobj), GFP_KERNEL)) == NULL) {
387 | return -ENOMEM;
388 | }
389 |
390 | // Create sysfs entries for beepy with custom type
391 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
392 | rc =
393 | #endif
394 | kobject_init_and_add(beepy_kobj, &beepy_ktype, firmware_kobj, "beepy");
395 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
396 | if (rc < 0) {
397 | kobject_put(beepy_kobj);
398 | return rc;
399 | }
400 | #endif
401 |
402 | // Create sysfs attributes
403 | if (sysfs_create_group(beepy_kobj, &beepy_attr_group)) {
404 | kobject_put(beepy_kobj);
405 | return -ENOMEM;
406 | }
407 |
408 | return 0;
409 | }
410 |
411 | void sysfs_shutdown(struct i2c_client* i2c_client)
412 | {
413 | // Remove sysfs entry
414 | if (beepy_kobj) {
415 | kobject_put(beepy_kobj);
416 | beepy_kobj = NULL;
417 | }
418 | }
419 |
--------------------------------------------------------------------------------
/src/sysfs_iface.h:
--------------------------------------------------------------------------------
1 | #ifndef SYSFS_IFACE_H_
2 | #define SYSFS_IFACE_H_
3 |
4 | // SPDX-License-Identifier: GPL-2.0-only
5 | /*
6 | * Keyboard Driver for Blackberry Keyboards BBQ10 from arturo182. Software written by wallComputer.
7 | */
8 |
9 | int sysfs_probe(struct i2c_client* i2c_client);
10 | void sysfs_shutdown(struct i2c_client* i2c_client);
11 |
12 | #endif
13 |
--------------------------------------------------------------------------------