Skip to content

Commit 09a4dd1

Browse files
committed
Add framework16-keyboard plugin
To update the keyboards of Framework Laptop 16 Signed-off-by: Daniel Schaefer <[email protected]>
1 parent c81b4a6 commit 09a4dd1

9 files changed

+296
-0
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: Plugin: Framework Laptop 16 Keyboard
3+
---
4+
5+
## Introduction
6+
7+
This plugin supports the [keyboards of the Framework 16 series](https://frame.work/tw/en/products/keyboard-module).
8+
9+
Devices are updated by triggering a reset via HID, and then entering the Raspberry Pi Pico bootrom.
10+
11+
The bootrom speaks the UF2 protocol.
12+
13+
## External Interface Access
14+
15+
This plugin requires read/write access to `/dev/hidraw*`.
16+
17+
## Version Considerations
18+
19+
This plugin has been available since fwupd version `2.0.14`.
20+
21+
## Owners
22+
23+
Anyone can submit a pull request to modify this plugin, but the following people should be
24+
consulted before making major or functional changes:
25+
26+
* Daniel Schaefer: @JohnAZoidberg
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Laptop 16 Keyboard Module - ANSI
2+
[HIDRAW\VEN_32AC&DEV_0012]
3+
VersionFormat = bcd
4+
Vendor = Framework
5+
Plugin = framework16_keyboard
6+
Flags = internal
7+
Icon = input-keyboard
8+
CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003
9+
10+
# Laptop 16 RGB Macropad
11+
[HIDRAW\VEN_32AC&DEV_0013]
12+
VersionFormat = bcd
13+
Vendor = Framework
14+
Plugin = framework16_keyboard
15+
Flags = internal
16+
Icon = input-keyboard
17+
CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003
18+
19+
# Laptop 16 Numpad
20+
[HIDRAW\VEN_32AC&DEV_0014]
21+
VersionFormat = bcd
22+
Vendor = Framework
23+
Plugin = framework16_keyboard
24+
Flags = internal
25+
Icon = input-keyboard
26+
CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003
27+
28+
# Laptop 16 Keyboard Module - ISO
29+
[HIDRAW\VEN_32AC&DEV_0018]
30+
VersionFormat = bcd
31+
Vendor = Framework
32+
Plugin = framework16_keyboard
33+
Flags = internal
34+
Icon = input-keyboard
35+
CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003
36+
37+
# Laptop 16 Keyboard Module - JIS
38+
[HIDRAW\VEN_32AC&DEV_0019]
39+
VersionFormat = bcd
40+
Vendor = Framework
41+
Plugin = framework16_keyboard
42+
Flags = internal
43+
Icon = input-keyboard
44+
CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2025 Framework Computer Inc
3+
*
4+
* SPDX-License-Identifier: LGPL-2.1-or-later
5+
*/
6+
7+
#include "config.h"
8+
9+
#include "fu-framework16-keyboard-device.h"
10+
#include "fu-framework16-keyboard-struct.h"
11+
12+
struct _FuFramework16KeyboardDevice {
13+
FuHidrawDevice parent_instance;
14+
guint8 iface_reset;
15+
};
16+
17+
G_DEFINE_TYPE(FuFramework16KeyboardDevice, fu_framework16_keyboard_device, FU_TYPE_HIDRAW_DEVICE)
18+
19+
#define FU_FRAMEWORK16_KEYBOARD_RAW_USAGE_PAGE 0xFF60
20+
#define FU_FRAMEWORK16_KEYBOARD_RAW_USAGE_ID 0x61
21+
22+
static gboolean
23+
fu_framework16_keyboard_device_detach(FuDevice *device, FuProgress *progress, GError **error)
24+
{
25+
FuFramework16KeyboardDevice *self = FU_FRAMEWORK16_KEYBOARD_DEVICE(device);
26+
g_autoptr(GError) error_local = NULL;
27+
g_autoptr(GByteArray) req = fu_struct_framework16_keyboard_reset_request_new();
28+
29+
// Initialize ->reserved to [0xFE; 30]
30+
for (guint i = 0; i < 30; i++)
31+
req->data[i + 2] = 0xFE;
32+
33+
if (!fu_hidraw_device_set_report(FU_HIDRAW_DEVICE(self),
34+
req->data,
35+
req->len,
36+
FU_IO_CHANNEL_FLAG_NONE,
37+
error)) {
38+
g_prefix_error(error, "failed to write packet: ");
39+
return FALSE;
40+
}
41+
42+
/* success */
43+
fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG);
44+
return TRUE;
45+
}
46+
47+
static void
48+
fu_framework16_keyboard_device_set_progress(FuDevice *self, FuProgress *progress)
49+
{
50+
fu_progress_set_id(progress, G_STRLOC);
51+
fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED);
52+
fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw");
53+
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach");
54+
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, "write");
55+
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "attach");
56+
fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 15, "reload");
57+
}
58+
59+
static void
60+
fu_framework16_keyboard_device_init(FuFramework16KeyboardDevice *self)
61+
{
62+
fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE);
63+
fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD);
64+
fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.uf2");
65+
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE);
66+
fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);
67+
fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS);
68+
fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID);
69+
fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN);
70+
/* revisions indicate incompatible hardware */
71+
fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV);
72+
fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE);
73+
fu_device_retry_set_delay(FU_DEVICE(self), 100);
74+
}
75+
76+
static gboolean
77+
fu_framework16_keyboard_device_setup(FuDevice *device, GError **error)
78+
{
79+
g_autoptr(FuHidDescriptor) descriptor = NULL;
80+
g_autoptr(FuHidReport) report = NULL;
81+
g_autoptr(FuDevice) device_usb = NULL;
82+
guint16 version = 0x000;
83+
84+
descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(device), error);
85+
if (descriptor == NULL) {
86+
g_warning("Failed to parse descriptor");
87+
return FALSE;
88+
}
89+
report = fu_hid_descriptor_find_report(descriptor,
90+
error,
91+
"usage-page",
92+
FU_FRAMEWORK16_KEYBOARD_RAW_USAGE_PAGE,
93+
"usage",
94+
FU_FRAMEWORK16_KEYBOARD_RAW_USAGE_ID,
95+
"collection",
96+
0x01,
97+
NULL);
98+
if (report == NULL) {
99+
g_warning("Report not found");
100+
return FALSE;
101+
}
102+
103+
device_usb = fu_device_get_backend_parent_with_subsystem(device, "usb:usb_device", NULL);
104+
if (device_usb == NULL) {
105+
g_warning("USB device NULL");
106+
return FALSE;
107+
}
108+
if (!fu_device_probe(device_usb, error)) {
109+
g_warning("USB probe failed");
110+
return FALSE;
111+
}
112+
version = fu_usb_device_get_release(FU_USB_DEVICE(device_usb));
113+
114+
/* VersionRaw is automatically set, not sure why Version isn't */
115+
fu_device_set_version(device, fu_version_from_uint16(version, FWUPD_VERSION_FORMAT_BCD));
116+
117+
return TRUE;
118+
}
119+
120+
static void
121+
fu_framework16_keyboard_device_class_init(FuFramework16KeyboardDeviceClass *klass)
122+
{
123+
FuDeviceClass *device_class = FU_DEVICE_CLASS(klass);
124+
device_class->setup = fu_framework16_keyboard_device_setup;
125+
device_class->detach = fu_framework16_keyboard_device_detach;
126+
device_class->set_progress = fu_framework16_keyboard_device_set_progress;
127+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2025 Framework Computer Inc
3+
*
4+
* SPDX-License-Identifier: LGPL-2.1-or-later
5+
*/
6+
7+
#pragma once
8+
9+
#include <fwupdplugin.h>
10+
11+
#define FU_TYPE_FRAMEWORK16_KEYBOARD_DEVICE (fu_framework16_keyboard_device_get_type())
12+
G_DECLARE_FINAL_TYPE(FuFramework16KeyboardDevice,
13+
fu_framework16_keyboard_device,
14+
FU,
15+
FRAMEWORK16_KEYBOARD_DEVICE,
16+
FuHidrawDevice)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2025 Framework Computer Inc
3+
*
4+
* SPDX-License-Identifier: LGPL-2.1-or-later
5+
*/
6+
7+
#include "config.h"
8+
9+
#include "fu-framework16-keyboard-device.h"
10+
#include "fu-framework16-keyboard-plugin.h"
11+
12+
struct _FuFramework16KeyboardPlugin {
13+
FuPlugin parent_instance;
14+
};
15+
16+
G_DEFINE_TYPE(FuFramework16KeyboardPlugin, fu_framework16_keyboard_plugin, FU_TYPE_PLUGIN)
17+
18+
static void
19+
fu_framework16_keyboard_plugin_init(FuFramework16KeyboardPlugin *self)
20+
{
21+
fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION);
22+
}
23+
24+
static void
25+
fu_framework16_keyboard_plugin_constructed(GObject *obj)
26+
{
27+
FuPlugin *plugin = FU_PLUGIN(obj);
28+
fu_plugin_add_device_gtype(plugin, FU_TYPE_FRAMEWORK16_KEYBOARD_DEVICE);
29+
fu_plugin_add_udev_subsystem(plugin, "hidraw");
30+
}
31+
32+
static void
33+
fu_framework16_keyboard_plugin_class_init(FuFramework16KeyboardPluginClass *klass)
34+
{
35+
FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass);
36+
plugin_class->constructed = fu_framework16_keyboard_plugin_constructed;
37+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright 2025 Framework Computer Inc
3+
*
4+
* SPDX-License-Identifier: LGPL-2.1-or-later
5+
*/
6+
7+
#pragma once
8+
9+
#include <fwupdplugin.h>
10+
11+
G_DECLARE_FINAL_TYPE(FuFramework16KeyboardPlugin,
12+
fu_framework16_keyboard_plugin,
13+
FU,
14+
FRAMEWORK16_KEYBOARD_PLUGIN,
15+
FuPlugin)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2025 Framework Computer Inc
2+
// SPDX-License-Identifier: LGPL-2.1-or-later
3+
4+
enum FuFramework16KeyboardResetRequest {
5+
BootloaderJump = 0x0B,
6+
}
7+
8+
#[derive(New, Default)]
9+
#[repr(C, packed)]
10+
struct FuStructFramework16KeyboardResetRequest {
11+
report_id: u8 == 0x00,
12+
cmd: u8 == 0x0B,
13+
reserved: [u8; 30], // == [0xFE; 30]
14+
}
15+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cargs = ['-DG_LOG_DOMAIN="FuPluginFramework16Keyboard"']
2+
3+
plugins += {meson.current_source_dir().split('/')[-1]: true}
4+
plugin_quirks += files('framework16-keyboard.quirk')
5+
plugin_builtins += static_library('fu_plugin_framework16_keyboard',
6+
rustgen.process('fu-framework16-keyboard.rs'),
7+
sources: [
8+
'fu-framework16-keyboard-device.c',
9+
'fu-framework16-keyboard-plugin.c',
10+
],
11+
include_directories: plugin_incdirs,
12+
link_with: plugin_libs,
13+
c_args: cargs,
14+
dependencies: plugin_deps,
15+
)

plugins/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ plugins = {
5454
'flashrom': false,
5555
'focalfp': false,
5656
'fpc': false,
57+
'framework16-keyboard': false,
5758
'fresco-pd': false,
5859
'genesys': false,
5960
'genesys-gl32xx': false,

0 commit comments

Comments
 (0)