| 0/0 |
2026/05/10 19:26 |
flow |
patching |
1h12m
Results: map[Fixes:map[Hash:c04148f915e5ba7947752e6348e0da4cdab1329e Title:Input: add driver for USB VoIP phones with CM109 chipset] KernelBranch:master KernelCommit:7fd2df204f342fc17d1a0bfcd474b24232fb0f32 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git PatchDescription:Input: cm109 - prevent concurrent control URB submissions
The cm109 driver uses two URBs to communicate with the device: urb_ctl
(Control OUT) and urb_irq (Interrupt IN). These URBs are designed to run
in a continuous, strictly alternating loop.
However, the driver also supports sound events to toggle the buzzer.
When a sound event is received, cm109_toggle_buzzer_async() is called.
If urb_ctl is not currently pending, it immediately submits urb_ctl
out-of-band to update the buzzer state. This breaks the strict
alternation and causes both urb_irq and urb_ctl to be active
concurrently.
When urb_irq subsequently completes, cm109_urb_irq_callback() calls
cm109_submit_ctl(). cm109_submit_ctl() blindly assumes urb_ctl is idle,
updates the DMA buffer, and calls usb_submit_urb(dev->urb_ctl). Since
urb_ctl is already active, the USB core catches it and triggers the
"URB submitted while active" warning.
To fix this, check if dev->ctl_urb_pending is already set in
cm109_submit_ctl(). If it is, defer the submission by setting
dev->buzzer_pending = 1 and return immediately.
Additionally, update cm109_submit_buzz_toggle() to also copy the latest
dev->keybit to HID_OR1 and HID_OR2, ensuring that the deferred key
matrix column update is not lost. Since cm109_submit_ctl() and
cm109_submit_buzz_toggle() now prepare the exact same data for urb_ctl,
refactor cm109_submit_ctl() to simply call cm109_submit_buzz_toggle()
after checking and updating the pending flags. PatchDiff:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..0114d7293 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -357,8 +360,6 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
static void cm109_submit_ctl(struct cm109_dev *dev)
{
- int error;
-
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
@@ -366,22 +367,15 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
+ cm109_submit_buzz_toggle(dev);
}
/*
Recipients:[map[Email:dmitry.torokhov@gmail.com Name:Dmitry Torokhov To:false] map[Email:linux-input@vger.kernel.org Name:open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)... To:false] map[Email:linux-kernel@vger.kernel.org Name: To:true]]]
|
| 1/1 |
2026/05/10 19:26 |
action |
base-commit-picker |
0m
Results: map[KernelBranch:master KernelCommit:7fd2df204f342fc17d1a0bfcd474b24232fb0f32 KernelRepo:git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git]
|
| 2/1 |
2026/05/10 19:26 |
action |
syz-repro-to-c-repro |
0m
Results: map[SimplifiedCRepro:// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/usb/ch9.h>
static unsigned long long procid;
static void sleep_ms(uint64_t ms)
{
usleep(ms * 1000);
}
#define MAX_FDS 30
#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
#define USB_MAX_FDS 6
struct usb_endpoint_index {
struct usb_endpoint_descriptor desc;
int handle;
};
struct usb_iface_index {
struct usb_interface_descriptor* iface;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bInterfaceClass;
struct usb_endpoint_index eps[USB_MAX_EP_NUM];
int eps_num;
};
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
uint8_t bDeviceClass;
uint8_t bMaxPower;
int config_length;
struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
int ifaces_num;
int iface_cur;
};
struct usb_info {
int fd;
struct usb_device_index index;
};
static struct usb_info usb_devices[USB_MAX_FDS];
static struct usb_device_index* lookup_usb_index(int fd)
{
for (int i = 0; i < USB_MAX_FDS; i++) {
if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd)
return &usb_devices[i].index;
}
return NULL;
}
static int usb_devices_num;
static bool parse_usb_descriptor(const char* buffer, size_t length, struct usb_device_index* index)
{
if (length < sizeof(*index->dev) + sizeof(*index->config))
return false;
memset(index, 0, sizeof(*index));
index->dev = (struct usb_device_descriptor*)buffer;
index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev));
index->bDeviceClass = index->dev->bDeviceClass;
index->bMaxPower = index->config->bMaxPower;
index->config_length = length - sizeof(*index->dev);
index->iface_cur = -1;
size_t offset = 0;
while (true) {
if (offset + 1 >= length)
break;
uint8_t desc_length = buffer[offset];
uint8_t desc_type = buffer[offset + 1];
if (desc_length <= 2)
break;
if (offset + desc_length > length)
break;
if (desc_type == USB_DT_INTERFACE && index->ifaces_num < USB_MAX_IFACE_NUM) {
struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset);
index->ifaces[index->ifaces_num].iface = iface;
index->ifaces[index->ifaces_num].bInterfaceNumber = iface->bInterfaceNumber;
index->ifaces[index->ifaces_num].bAlternateSetting = iface->bAlternateSetting;
index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass;
index->ifaces_num++;
}
if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) {
struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1];
if (iface->eps_num < USB_MAX_EP_NUM) {
memcpy(&iface->eps[iface->eps_num].desc, buffer + offset, sizeof(iface->eps[iface->eps_num].desc));
iface->eps_num++;
}
}
offset += desc_length;
}
return true;
}
static struct usb_device_index* add_usb_index(int fd, const char* dev, size_t dev_len)
{
int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED);
if (i >= USB_MAX_FDS)
return NULL;
if (!parse_usb_descriptor(dev, dev_len, &usb_devices[i].index))
return NULL;
__atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE);
return &usb_devices[i].index;
}
struct vusb_connect_string_descriptor {
uint32_t len;
char* str;
} __attribute__((packed));
struct vusb_connect_descriptors {
uint32_t qual_len;
char* qual;
uint32_t bos_len;
char* bos;
uint32_t strs_len;
struct vusb_connect_string_descriptor strs[0];
} __attribute__((packed));
static const char default_string[] = {
8, USB_DT_STRING,
's', 0, 'y', 0, 'z', 0
};
static const char default_lang_id[] = {
4, USB_DT_STRING,
0x09, 0x04
};
static bool lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl,
struct usb_qualifier_descriptor* qual,
char** response_data, uint32_t* response_length)
{
struct usb_device_index* index = lookup_usb_index(fd);
uint8_t str_idx;
if (!index)
return false;
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE:
*response_data = (char*)index->dev;
*response_length = sizeof(*index->dev);
return true;
case USB_DT_CONFIG:
*response_data = (char*)index->config;
*response_length = index->config_length;
return true;
case USB_DT_STRING:
str_idx = (uint8_t)ctrl->wValue;
if (descs && str_idx < descs->strs_len) {
*response_data = descs->strs[str_idx].str;
*response_length = descs->strs[str_idx].len;
return true;
}
if (str_idx == 0) {
*response_data = (char*)&default_lang_id[0];
*response_length = default_lang_id[0];
return true;
}
*response_data = (char*)&default_string[0];
*response_length = default_string[0];
return true;
case USB_DT_BOS:
*response_data = descs->bos;
*response_length = descs->bos_len;
return true;
case USB_DT_DEVICE_QUALIFIER:
if (!descs->qual) {
qual->bLength = sizeof(*qual);
qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
qual->bcdUSB = index->dev->bcdUSB;
qual->bDeviceClass = index->dev->bDeviceClass;
qual->bDeviceSubClass = index->dev->bDeviceSubClass;
qual->bDeviceProtocol = index->dev->bDeviceProtocol;
qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0;
qual->bNumConfigurations = index->dev->bNumConfigurations;
qual->bRESERVED = 0;
*response_data = (char*)qual;
*response_length = sizeof(*qual);
return true;
}
*response_data = descs->qual;
*response_length = descs->qual_len;
return true;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
return false;
}
typedef bool (*lookup_connect_out_response_t)(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done);
static bool lookup_connect_response_out_generic(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done)
{
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_SET_CONFIGURATION:
*done = true;
return true;
default:
break;
}
break;
}
return false;
}
struct vusb_descriptor {
uint8_t req_type;
uint8_t desc_type;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_descriptors {
uint32_t len;
struct vusb_descriptor* generic;
struct vusb_descriptor* descs[0];
} __attribute__((packed));
struct vusb_response {
uint8_t type;
uint8_t req;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_responses {
uint32_t len;
struct vusb_response* generic;
struct vusb_response* resps[0];
} __attribute__((packed));
static bool lookup_control_response(const struct vusb_descriptors* descs, const struct vusb_responses* resps,
struct usb_ctrlrequest* ctrl, char** response_data, uint32_t* response_length)
{
int descs_num = 0;
int resps_num = 0;
if (descs)
descs_num = (descs->len - offsetof(struct vusb_descriptors, descs)) / sizeof(descs->descs[0]);
if (resps)
resps_num = (resps->len - offsetof(struct vusb_responses, resps)) / sizeof(resps->resps[0]);
uint8_t req = ctrl->bRequest;
uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK;
uint8_t desc_type = ctrl->wValue >> 8;
if (req == USB_REQ_GET_DESCRIPTOR) {
int i;
for (i = 0; i < descs_num; i++) {
struct vusb_descriptor* desc = descs->descs[i];
if (!desc)
continue;
if (desc->req_type == req_type && desc->desc_type == desc_type) {
*response_length = desc->len;
if (*response_length != 0)
*response_data = &desc->data[0];
else
*response_data = NULL;
return true;
}
}
if (descs && descs->generic) {
*response_data = &descs->generic->data[0];
*response_length = descs->generic->len;
return true;
}
} else {
int i;
for (i = 0; i < resps_num; i++) {
struct vusb_response* resp = resps->resps[i];
if (!resp)
continue;
if (resp->type == req_type && resp->req == req) {
*response_length = resp->len;
if (*response_length != 0)
*response_data = &resp->data[0];
else
*response_data = NULL;
return true;
}
}
if (resps && resps->generic) {
*response_data = &resps->generic->data[0];
*response_length = resps->generic->len;
return true;
}
}
return false;
}
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[0];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[0];
};
#define USB_RAW_EPS_NUM_MAX 30
#define USB_RAW_EP_NAME_MAX 16
#define USB_RAW_EP_ADDR_ANY 0xff
struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
};
struct usb_raw_ep_limits {
__u16 maxpacket_limit;
__u16 max_streams;
__u32 reserved;
};
struct usb_raw_ep_info {
__u8 name[USB_RAW_EP_NAME_MAX];
__u32 addr;
struct usb_raw_ep_caps caps;
struct usb_raw_ep_limits limits;
};
struct usb_raw_eps_info {
struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
#define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
#define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
static int usb_raw_open()
{
return open("/dev/raw-gadget", O_RDWR);
}
static int usb_raw_init(int fd, uint32_t speed, const char* driver, const char* device)
{
struct usb_raw_init arg;
strncpy((char*)&arg.driver_name[0], driver, sizeof(arg.driver_name));
strncpy((char*)&arg.device_name[0], device, sizeof(arg.device_name));
arg.speed = speed;
return ioctl(fd, USB_RAW_IOCTL_INIT, &arg);
}
static int usb_raw_run(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_RUN, 0);
}
static int usb_raw_configure(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0);
}
static int usb_raw_vbus_draw(int fd, uint32_t power)
{
return ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power);
}
static int usb_raw_ep0_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io);
}
static int usb_raw_ep0_read(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_READ, io);
}
static int usb_raw_event_fetch(int fd, struct usb_raw_event* event)
{
return ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event);
}
static int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor* desc)
{
return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc);
}
static int usb_raw_ep_disable(int fd, int ep)
{
return ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, ep);
}
static int usb_raw_ep0_stall(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
static int lookup_interface(int fd, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
for (int i = 0; i < index->ifaces_num; i++) {
if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber &&
index->ifaces[i].bAlternateSetting == bAlternateSetting)
return i;
}
return -1;
}
#define USB_MAX_PACKET_SIZE 4096
struct usb_raw_control_event {
struct usb_raw_event inner;
struct usb_ctrlrequest ctrl;
char data[USB_MAX_PACKET_SIZE];
};
struct usb_raw_ep_io_data {
struct usb_raw_ep_io inner;
char data[USB_MAX_PACKET_SIZE];
};
static void set_interface(int fd, int n)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return;
if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) {
int rv = usb_raw_ep_disable(fd, index->ifaces[index->iface_cur].eps[ep].handle);
if (rv < 0) {
} else {
}
}
}
if (n >= 0 && n < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[n].eps_num; ep++) {
int rv = usb_raw_ep_enable(fd, &index->ifaces[n].eps[ep].desc);
if (rv < 0) {
} else {
index->ifaces[n].eps[ep].handle = rv;
}
}
index->iface_cur = n;
}
}
static int configure_device(int fd)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
int rv = usb_raw_vbus_draw(fd, index->bMaxPower);
if (rv < 0) {
return rv;
}
rv = usb_raw_configure(fd);
if (rv < 0) {
return rv;
}
set_interface(fd, 0);
return 0;
}
static volatile long syz_usb_connect_impl(uint64_t speed, uint64_t dev_len, const char* dev,
const struct vusb_connect_descriptors* descs,
lookup_connect_out_response_t lookup_connect_response_out)
{
if (!dev) {
return -1;
}
int fd = usb_raw_open();
if (fd < 0) {
return fd;
}
if (fd >= MAX_FDS) {
close(fd);
return -1;
}
struct usb_device_index* index = add_usb_index(fd, dev, dev_len);
if (!index) {
return -1;
}
char device[32];
sprintf(&device[0], "dummy_udc.%llu", procid);
int rv = usb_raw_init(fd, speed, "dummy_udc", &device[0]);
if (rv < 0) {
return rv;
}
rv = usb_raw_run(fd);
if (rv < 0) {
return rv;
}
bool done = false;
while (!done) {
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL)
continue;
char* response_data = NULL;
uint32_t response_length = 0;
struct usb_qualifier_descriptor qual;
if (event.ctrl.bRequestType & USB_DIR_IN) {
if (!lookup_connect_response_in(fd, descs, &event.ctrl, &qual, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
continue;
}
} else {
if (!lookup_connect_response_out(fd, descs, &event.ctrl, &done)) {
usb_raw_ep0_stall(fd);
continue;
}
response_data = NULL;
response_length = event.ctrl.wLength;
}
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
rv = configure_device(fd);
if (rv < 0) {
return rv;
}
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if (event.ctrl.bRequestType & USB_DIR_IN) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
}
sleep_ms(200);
return fd;
}
static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
uint64_t speed = a0;
uint64_t dev_len = a1;
const char* dev = (const char*)a2;
const struct vusb_connect_descriptors* descs = (const struct vusb_connect_descriptors*)a3;
return syz_usb_connect_impl(speed, dev_len, dev, descs, &lookup_connect_response_out_generic);
}
static volatile long syz_usb_control_io(volatile long a0, volatile long a1, volatile long a2)
{
int fd = a0;
const struct vusb_descriptors* descs = (const struct vusb_descriptors*)a1;
const struct vusb_responses* resps = (const struct vusb_responses*)a2;
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = USB_MAX_PACKET_SIZE;
int rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL) {
return -1;
}
char* response_data = NULL;
uint32_t response_length = 0;
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
if (!lookup_control_response(descs, resps, &event.ctrl, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
return -1;
}
} else {
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD ||
event.ctrl.bRequest == USB_REQ_SET_INTERFACE) {
int iface_num = event.ctrl.wIndex;
int alt_set = event.ctrl.wValue;
int iface_index = lookup_interface(fd, iface_num, alt_set);
if (iface_index < 0) {
} else {
set_interface(fd, iface_index);
}
}
response_length = event.ctrl.wLength;
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
if ((event.ctrl.bRequestType & USB_DIR_IN) && !event.ctrl.wLength) {
response_length = USB_MAX_PACKET_SIZE;
}
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
sleep_ms(200);
return 0;
}
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x0 (8 bytes)
// dev_len: len = 0x2d (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 00 75 16 b7 10 8c 0d 0e 00 8f 8e 00 18 03 01 09 02 1b 00 01 00 00 00 00 09 04 08 00 01 03 00 00 00 09 05} (length 0x26)
// }
// union ANYUNION {
// ANYBLOB: buffer: {8f cf 44 20 82 9e f1 88} (length 0x8)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000001040, "\x12\x01\x00\x00\x75\x16\xb7\x10\x8c\x0d\x0e\x00\x8f\x8e\x00\x18\x03\x01\x09\x02\x1b\x00\x01\x00\x00\x00\x00\x09\x04\x08\x00\x01\x03\x00\x00\x00\x09\x05", 38);
memcpy((void*)0x200000001066, "\x8f\xcf\x44\x20\x82\x9e\xf1\x88", 8);
res = -1;
res = syz_usb_connect(/*speed=*/0, /*dev_len=*/0x2d, /*dev=*/0x200000001040, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_open_dev$tty1 arguments: [
// dev: const = 0xc (8 bytes)
// major: const = 0x4 (8 bytes)
// minor: intptr = 0x1 (8 bytes)
// ]
// returns fd_tty
res = -1;
res = syz_open_dev(/*dev=*/0xc, /*major=*/4, /*minor=*/1);
if (res != -1)
r[1] = res;
// syz_usb_control_io$hid arguments: [
// fd: fd_usb_hid (resource)
// descs: nil
// resps: nil
// ]
syz_usb_control_io(/*fd=*/r[0], /*descs=*/0, /*resps=*/0);
// dup arguments: [
// oldfd: fd (resource)
// ]
// returns fd
res = syscall(__NR_dup, /*oldfd=*/r[1]);
if (res != -1)
r[2] = res;
// write$UHID_INPUT arguments: [
// fd: fd_uhid (resource)
// data: ptr[in, uhid_req[UHID_INPUT, uhid_input_req]] {
// uhid_req[UHID_INPUT, uhid_input_req] {
// type: const = 0xfc (4 bytes)
// data: uhid_input_req {
// data: buffer: {a2 e3 ad 21 ed 0d 1b f9 1b 29 55 09 87 f7 0e 06 d0 38 e7 ff 7f c6 e5 53 9b 0d 3d 0e 8b 08 9b 3f 3b 09 6c 06 08 90 e0 87 8f 0e 1a c6 e7 04 9b 09 6e 95 9b 44 9a 24 0d 5b 67 f3 98 8f 7e f3 19 52 01 00 ff e8 d1 78 70 8c 52 3c 92 1b 1b 5b 31 07 0b 07 5d 0d 36 cd 3b 78 13 0d aa 61 d8 e8 04 00 00 00 58 02 b7 7f 07 22 72 27 b7 ba 67 e0 e7 86 57 a6 f5 c2 a8 74 e6 2a 9c cd c0 d3 1a 0c 9f 31 8c 0d a1 99 3b d1 60 e2 33 df 4a 62 17 9c 6f 30 e0 65 cd 5b 91 cd 0a e1 93 97 37 35 b3 6d 5b 1b 63 dd 1c 00 30 5d 3f 46 63 5e b0 16 d5 b1 dd a9 8e 2d 74 9b e7 bd 1d f1 fb 3b 23 1f dc db 50 75 a9 aa a1 b4 69 c3 09 00 00 00 00 00 00 00 75 27 1b 28 63 29 d1 69 93 42 88 fd 78 9a a3 7d 6e 98 b2 24 fd 44 b6 5b 31 33 4f fc 55 cc 82 cd 3a c3 2e cd b0 8c ed 6f 90 81 b4 dd 0d 8b 38 f3 cd 44 98 be e8 00 49 08 41 bd b1 14 f6 b7 63 83 70 9d 8f 5c 55 43 2a 90 9f da 03 9a ec 54 a1 23 6e 80 f6 a8 ab ad ea 76 62 49 6b dd bb 42 be 6b fb 2f 17 95 9d 1f 41 6e 56 c7 1b 19 31 87 02 62 f5 e8 01 11 92 42 ca 02 6b fc 82 1e 7e 7d af 24 51 13 8e 64 5b b8 0c 61 76 69 31 4e 2f be 70 de 98 ec 76 a9 e4 0d ad 47 f3 6f d9 f7 d0 d4 2a 4b 5f 11 85 cc dc f1 6f f4 62 95 d8 a0 fa 17 71 3c 58 02 63 09 33 a9 a3 4a f6 74 f3 f3 9f e2 34 91 23 7c 08 82 2d ec 11 09 11 e8 93 d0 a8 c4 f6 77 74 7a bc 36 09 34 b8 29 10 ff 85 bf d9 95 08 3b ba 29 87 a6 73 99 ea c4 27 d1 45 d5 46 a4 0b 9f 6f f1 4a c4 88 ec 13 0f 91 85 0a 27 af 95 44 ae 15 a7 e4 54 de a0 59 18 b4 12 43 51 3f 00 00 00 00 00 00 00 0a 36 21 c5 6c ea 8d 20 fa 91 1a 0c 41 db 6e be 8c ac 64 f1 76 79 14 1d 54 b3 4b bc 99 63 ac 4f 4b b3 30 96 03 f1 d4 ab 96 62 03 86 1b 5b 15 a8 41 f2 b5 75 a8 bd 0d 78 24 8e be 4d 9a 80 00 26 95 10 4f 67 4c 24 31 dc a1 41 fa e2 69 ca b7 0e 9a 66 f3 c3 a9 a6 3e 96 39 e1 f5 9c 0e de 26 c6 b5 d7 4b 07 8a 5e 15 c3 16 34 e5 ae 09 8c e9 ee 70 77 1a aa 18 11 9a 86 7e 10 88 33 49 75 e9 f7 34 83 b6 a6 2f a6 78 ca 14 ff d9 f9 db 2a 78 69 d8 58 64 05 65 26 f8 89 af 43 a6 05 60 80 57 22 86 52 24 49 df 46 6c 63 2b 35 70 24 3f 98 9c ce 7c d9 f4 65 e4 1e 61 0c 20 d8 04 21 d6 53 a5 52 00 00 00 82 13 b7 04 c7 fb 08 2f f2 75 90 67 8e f9 f1 90 ba e9 79 09 50 70 41 d8 60 42 0c 56 64 b2 79 21 b1 4d c1 db 88 92 fd 32 d0 ad 7b c9 46 81 35 91 ad 8d ef f4 b0 5f 60 ce a0 da 77 10 ac 00 00 00 00 00 00 80 00 be a3 7c e0 d0 d4 aa 20 2f 92 8f 28 38 1a ab 14 4a 5d 42 9a 04 a6 a2 b8 3c 70 68 ae 94 9e d0 6e 28 8e 81 0b ac 9c 76 60 00 25 e1 9c 90 7f 8e a2 e2 01 00 00 00 82 71 a1 f5 f8 52 8f 22 7e 79 c1 38 9d bd ff fe 49 2f 21 57 9d 2c 15 b8 c7 0c db 1c 33 2d 86 d8 73 41 43 27 50 86 1e c2 bc 34 51 ed ca 19 4b 22 1c fe c4 60 3d 27 6b ba a1 df a6 d4 fb 8a 48 a7 6e af c9 a9 a0 27 0e 4c 10 d6 4c d5 a6 24 27 26 4f 40 77 fe 76 3c 43 47 08 33 ac 96 c4 5f 35 7c bb ab a8 f1 b1 fd cc 7c bb 61 a7 cd b9 74 4e d7 f9 12 9a ed e2 be 21 cc fd c4 e9 13 4f 86 84 b3 a4 f3 54 da 9a 79 5e 96 33 4e 20 7d ff 70 f1 98 80 37 b2 ed 3a af 57 5c 0b 88 d8 f1 46 68 40 78 41 6d 59 fd ee 53 25 92 89 74 d1 2d ad 99 da c4 4c 3f 00 08 04 70 96 a4 40 02 be bc 24 20 ae d9 2f a9 b6 57 8b 47 79 41 5d 4a c0 1b 75 d5 49 5c 11 80 45 65 1c f4 1c 2f c4 8b 77 8e fa 5e a5 67 77 47 43 0a f4 16 2b 98 7b 80 c3 e0 01 cd 34 e5 c9 2f 76 cc 4c 24 ee b8 bc 4e 9a c2 ae d9 e5 38 03 ed 0c a4 ae 3a 97 37 d2 14 06 00 05 ea 6f 17 83 e2 87 b3 be e9 6e 3a 72 6e af e2 fd fa a7 8d 1f 48 c1 3b 64 df 07 84 77 54 b8 40 0d aa a6 9b f5 c8 f4 35 0a ea e9 ca 12 07 e7 82 83 cd 0b 20 ce b3 60 c7 e6 58 82 81 63 e2 d2 5c 4a a3 48 56 1f 92 7e 88 f6 3a a7 0e 73 a5 e6 9b 3d f3 49 59 03 f0 65 72 e1 e0 07 fa 55 a2 99 9f 59 6d 06 73 12 f5 77 9e 8d bf dc f3 42 71 38 f3 d4 44 d2 63 9a 10 47 7f 9b ec 4b 0b bb 6e 3c 04 be 68 98 1f 39 22 03 dd 0e e3 ef 47 8e 16 da cf c5 e3 e0 3c f7 ab 8e 39 02 f1 b0 ff 03 4e f6 55 b2 53 ca 50 93 83 81 5b 1b 6f c6 52 2d 4e 4f dc 11 a4 8c f4 2d 48 60 46 75 fd e2 b9 4c f0 05 00 a2 69 08 91 ab f8 ab 9c 01 50 73 01 4d 9e 08 d4 33 8b 87 80 bd ec d4 36 cf 05 41 35 9b af ff a4 52 37 f1 04 b9 62 10 40 3b 2d e9 ef ed 49 6f 42 35 5b c7 87 2c 82 74 67 cf a5 c4 e7 27 30 d5 6b d0 68 ed 21 1c f8 47 53 5e de cb 7b 37 3f 78 b0 95 b6 84 41 a3 4c b5 16 82 a8 ae 4d 24 ad 04 65 f3 92 7f 88 9b 81 30 76 03 8e 79 a7 96 2f b3 85 a8 1a a1 02 0f 06 c4 c2 ba 1d d5 ca c7 c1 88 76 da 86 5d 25 87 34 dd 73 58 3d f2 92 89 24 48 03 9e f7 99 cf 06 30 be cd cc e0 45 79 b5 56 1d c8 25 ab 82 98 27 94 5e 02 0c 1f 67 ee 61 5f eb 62 43 37 8e 06 10 06 0f 02 cc a4 e9 1b 2f 00 1e db 3d 78 fb 4b 55 66 8d da 93 ae c9 2a 5d e2 03 71 7a a4 9c 2d 28 4a cf ab e2 62 fc cf cb b2 b7 5a 21 83 c4 6e b6 5c a8 10 4e 1b 4d a7 fb b7 7a b2 fc 04 3a ea d8 7c 32 ab 87 5e e7 c2 e7 b7 01 9c 98 2c d3 b4 3e ae b1 a5 fb 13 5c 0c 7d ce e8 fe 65 16 a3 28 03 2f 88 c0 42 89 18 24 65 9e 9e 94 26 5c 80 3b 35 ee 5f 83 a2 b2 10 52 01 06 89 af 9c 25 1f e5 29 4b 3d 18 02 d5 67 6d 95 f1 60 ec 97 b1 ad 94 87 2c b2 04 46 42 c3 7b 4a 6c c6 c0 4e ff c1 67 2d b7 e4 b6 8d 78 7d 9a 7a 50 8a e5 4b 3c d7 36 9d de 50 e8 c7 7d 95 a3 d3 61 c0 40 ba bb 17 16 07 ca ac 2a 35 59 ad 4f 75 46 5f 49 c0 d0 ae 37 16 db 6e 00 cb 11 db 4a 5f ad e2 a5 7c 10 23 8e 20 4a 67 73 7c 3b 42 aa e5 01 b2 0f 76 94 a0 0f 16 e2 d0 17 40 35 a2 c2 26 56 dc 29 88 0a ce bd be 8d db d7 5c 2f 99 8d 8a c2 df ad 2b a3 a5 04 76 7b 6b 45 a4 59 57 f2 4d 75 8e d0 24 b3 84 9c 11 d4 12 a2 a0 3b 40 47 49 70 22 d9 c3 0e 23 ef 4d f5 c8 96 44 f4 8b b5 36 f7 94 5b 59 d7 bc dd ff 75 44 13 d1 35 27 3e a8 e7 5f 22 f2 16 c6 b9 99 0a e7 18 06 f2 c0 0b 40 25 c4 8b 75 c0 f7 3c db 9a 7b 8f a3 67 b5 00 28 06 7e 7f 16 f4 dd 56 9d 46 2f 4f 19 ea cd b3 ed 70 ee eb b4 48 3f 8f d7 77 d4 43 e8 b4 04 27 db 6f e2 90 68 c0 ca 3d 24 14 44 2e 8f 3a 15 47 04 b0 e5 1b c6 64 a1 37 b2 6b e7 19 f4 f7 c9 a5 67 8a 67 4d fc 95 df 80 b9 ce 37 5d d6 49 c8 c7 04 e5 09 bd 88 c8 e6 3d 8c 7d d6 70 71 11 5c 89 82 ba 46 af 4d 6a dc c9 f6 8a 75 b9 39 7b 03 51 53 fa e4 63 66 e7 20 5d d8 d6 f3 75 25 c1 a0 e9 46 10 dd 94 32 3f 6c 15 d0 85 19 71 49 bf d6 65 55 48 cf d9 c5 2c 97 11 93 7f 79 ab b1 a1 24 f1 21 04 65 48 3c d3 b2 d7 83 78 cf b8 5e d8 2e 7d a0 f6 eb 6d 27 9f 2a e4 55 92 5d 0f 6f 1b a5 71 eb a2 81 f2 a6 54 fb 39 dd ff 3b 48 44 39 ff 15 8e 7c 54 19 e0 37 f3 e3 ad 03 8f 22 11 f1 03 31 95 56 3c 7f 93 cd 54 b9 09 4f 22 6e 78 32 71 e1 e5 a2 a2 c1 07 12 ea b6 25 d6 49 31 cd 4f fe 67 38 d9 7b 9b 5e f8 28 ee 9f b0 59 fc 01 af 0e 79 c1 e1 4b 1d 25 98 8c 69 a3 99 56 7c 1d 93 76 8f 79 71 d3 14 88 b8 65 8a 20 87 8b 7c 1d d7 ba 02 fc 42 93 9d de 3d 4a 33 39 a6 5d 50 7d c5 9c 51 09 7b 40 51 77 05 da 56 e9 eb f0 af a5 32 82 bf 86 db b5 8c 54 80 69 ff 6e b9 5a ad e7 cc 66 d7 bb ef 72 47 79 ca 1f 73 1b 33 46 ff 17 70 50 37 3d 79 ff 7b 3e 7f 9b c0 c1 b4 b2 66 a8 87 8b 90 ba aa 03 9d 3e 3b 63 97 9a c3 df 6e 6f 48 59 af d5 02 38 c7 54 7a 39 b6 08 10 93 80 44 ae 18 5d 2b a3 e0 0a 4e 73 67 68 64 ae 09 0d 81 ea ee 5e e6 cf 1d 0a b3 78 dd 4d d8 91 e9 37 c2 ea 54 10 e0 51 30 05 00 00 00 00 00 00 00 39 11 fa b9 64 c2 71 55 00 27 69 7b 52 16 06 87 46 16 02 f8 8d f1 65 d8 84 b3 6e c2 b6 c2 5a 2f 33 c7 15 68 7e 9d 4a fb 96 d6 86 1a ca 47 da 73 d6 f3 14 43 45 f4 88 43 dd 01 4e 5c 5a d8 fe 99 57 54 bd 9c f3 2f ce 1e 31 91 9c 4b 20 82 fb 0a 30 b9 de ae 84 be d4 b2 80 45 63 40 73 c9 c5 8c 89 d9 e9 9c 81 76 91 77 c6 d5 94 f8 8a 4f ac fd 4c 73 5a 20 30 7c 73 7a fa 2d 60 39 94 73 29 6b 83 1d bd 93 3d 93 99 4b a3 06 42 79 b1 0e a0 c5 83 3f 41 f1 57 ea 23 02 99 3d be 43 3b 1a a3 a3 76 6d 54 39 02 04 84 f4 11 3c 4c 85 94 65 c3 b4 15 c3 43 f7 f1 40 f3 19 53 9d 5b f3 72 aa ae a1 cc 43 a6 c5 cb e5 97 58 bf ee 29 16 58 0d ac 4b 00 8e 59 5f 43 74 91 d8 7a be d0 2c ef cd 9d b5 3d 94 d0 2d ae e6 79 18 e5 d6 78 74 63 18 3b 4b 87 c1 05 00 00 00 2f 78 09 95 9b c0 48 85 06 13 d1 7c a5 10 55 f2 f4 16 a4 4f e1 80 d2 d5 0c 31 2c ca 7c b1 4a 2b dc 33 1f 57 a9 81 71 39 a2 06 fc 76 95 72 27 ff ff 2d e2 0a 4b 8e 37 37 fb b4 29 13 77 7c 06 37 6f 79 9e ba 36 7e 21 f9 4c a5 98 70 5f 5d cb 76 7d 6f 09 00 d6 b0 f6 09 5e 53 c4 c4 23 4d 0c 1f be 43 4f 6a b8 f4 3c 00 13 ee 93 b8 39 46 ee 77 59 e8 9d 7b dd 1a 32 d7 b3 11 71 1b 75 7f e4 3c 06 d2 1a 35 81 0d 8f e9 8b 27 fa ea 8a a1 2b c8 71 6e ef c5 c9 7c 45 ac 33 ee ec 96 4c 52 14 bc 3a 93 59 bd ea 1c cc ab 94 f1 5e 36 31 9c b3 4e bc ac ed b8 2c 2e d3 de 5a 8a 8f 00 11 e8 f7 4e 82 d7 f9 60 93 53 0e 76 69 28 39 d7 96 19 39 ad fd ee ea ff 19 d1 1e fc af b6 d5 46 fe f2 71 e8 9d 6c c2 38 9e 81 ff 58 ce fc ce 3f bf 46 25 a7 e7 de 40 e4 2e 07 b3 44 49 e1 5e 06 5c c7 34 00 02 00 00 00 00 00 00 f2 88 a4 51 0d e0 3d ab 19 d2 62 85 ed a8 91 56 d5 0d d3 85 a6 03 33 ba 5b bf 5d 77 cd 70 07 ad 15 19 ad 54 70 de 3d d6 d6 08 0c af cc f8 a9 74 06 bb 6b 68 a1 f0 c4 54 98 20 a7 3c 88 0f 47 5f 73 2a e0 03 98 e8 bd 1f 41 08 b7 80 7f b3 3b 72 68 5e c3 7a 2d 3f 76 64 13 a6 04 59 51 62 46 e5 a1 d9 98 a2 01 7a ef 09 48 a6 8c f2 55 31 5a b8 0d d3 49 e8 91 ae f5 95 dc 4d 47 0e 8a c3 2a 30 8e 15 fc 37 d0 6a ea c2 89 c0 52 3f 48 3e 1f f7 40 8c 60 87 f1 ab 65 2f 2e f9 1d 4f 2b 01 98 7b 0f 46 da 03 4e 5c 3f 74 5a 7e e8 10 1a 39 34 c5 4e 24 b4 8e c0 27 5e 2d 06 87 dc 74 6b 08 27 cb f6 52 f4 06 c6 b9 5f 27 22 e5 8c 05 f7 52 ce 21 26 59 6e 1c d7 65 5b 90 48 01 78 4c 41 6b 22 f7 3d 32 46 78 e2 72 4f 43 f1 fe 68 7c 7e 8a 60 c2 8b 82 b6 52 83 41 b6 48 cd d5 6f ed 7c dc bb 15 75 91 2d 5e cd 36 de a3 bc a0 b7 42 7d 83 92 c6 28 94 55 e8 f8 d2 ab 22 42 72 92 51 ae 03 3a 9e 02 21 0e 62 df 05 46 a7 4b 33 3a 1c 48 f9 5f d5 4a cb 57 41 25 9e 8c 54 88 ef ee e3 27 41 5c c1 94 51 43 2c 6f 14 c2 76 93 10 2a 3c d8 48 57 cd 65 86 fc 5c a9 a9 3e b0 14 5f ac 06 62 ff 86 10 7f 99 8a 8e f7 df 8a a1 40 46 c5 5b 03 d3 d4 7f 88 a8 d6 0f 77 74 a2 ee 08 75 88 97 fb 41 1a 94 b3 c2 fc 5d 5f 0d b4 2c 04 56 ec 01 5f 08 e5 24 7d 33 ae 2d 35 60 3f f8 45 4c 16 f8 34 28 56 93 51 25 10 2b b7 84 ed 71 48 b6 ce 43 1b 63 ee 35 6b 0c 78 5f 2f 47 b9 0e 29 38 9f 22 fc 5b 59 a7 0e fa ea 2b d4 01 95 af 44 86 22 0d 70 2e 30 bf c4 3c 10 ec 23 ea 62 83 99 4a 7d de 4d cb 61 fe a6 b6 51 fb 1d 62 45 8d 07 41 a1 28 30 05 2f cc 46 0d b0 43 af e5 25 62 9b 40 d7 ce e4 58 e4 cb 5e 93 0e d6 24 80 6c 43 a0 06 e3 93 36 d0 7c 2b 80 81 c1 28 ad 27 06 f4 82 61 f7 89 74 84 c2 97 a1 a6 61 3b c1 8f 5a 38 d4 42 76 8a f3 80 41 ef e0 3d 15 2e f9 5f f5 69 e7 6d b2 39 1f 45 09 d7 f3 39 d9 2f db 4a 89 36 49 49 da 39 80 00 00 00 00 00 00 00 d8 0a 4f e6 54 57 83 76 e5 99 af f3 56 5b 1d 53 1f 30 91 2b 99 45 03 0b 81 ea 99 35 fd 46 ed b4 4a 78 f6 15 25 54 90 a4 b6 21 50 1f 2a 9e 4d 24 62 4c 4d ac 92 74 11 8c 67 58 4f 5d 37 47 55 53 4d 7f 68 f6 79 c4 ff 51 6a 9c 86 1a 0e 7e 65 86 8f cb 2b f1 cb 9a ea 4e 05 df 72 27 9f db 0d 2b 9e 93 5c 5a f3 cf 47 4b 06 79 df c2 48 c1 f5 ae a4 b8 b3 2c 5d 29 5e 57 07 9d 0f e6 62 a4 6b 7f 71 cd 47 74 4d b8 6c 50 b7 04 c9 71 d9 02 95 c7 b2 c7 43 9a 2d 78 cc fa 79 b5 fc 2b ff 6b bf 84 02 62 bf 89 39 4b 3e 06 91 95 12 64 d2 70 0c 83 8f a2 c7 b3 42 52 60 f5 95 54 e5 02 dc ea 39 cb 31 3b 00 00 00 00 00 00 4c a7 c1 2f 45 85 8d 62 84 ca 62 70 d6 b2 f0 e5 8f de d8 a7 b4 a3 02 a9 7b c6 41 df 07 72 0b a2 b2 6b bf cc 80 7c a0 ab b1 b4 43 22 26 9c 21 c5 ec 68 cb 06 8e a8 80 67 d9 05 ea 91 7b b0 3e ef da eb de ab f2 d0 dc e8 09 97 c9 15 c8 94 9d e9 92 58 7c 2c b5 fe 36 d7 d3 e5 db 21 b0 94 b8 b7 79 40 b5 f0 77 22 e4 7a 08 d3 67 e5 f8 4c 96 ec 66 4b 72 93 4b 99 b3 10 9a f6 5d 77 e8 6a bd 68 59 cd df 4b ba e1 f0 93 04 62 df 15 fd db c4 85 62 ea 35 11 a8 06 5e f0 28 cf 12 f1 4d cf 6e be cd 8d 98 48 36 17 4f af 1a a6 09 e5 f1 ee 11 62 df a1 3b dc 1f a7 cf aa db a8 5c 72 e9 75 8f 03 a7 05 00 be 53 f8 d2 a1 df b1 c6 8c c1 64 b0 a0 78 0d 97 1a 96 ea 2c 4d 4c a0 39 8c 22 35 98 0a 93 07 b3 d5 bd 3b 01 fa ff d0 a5 db ed 28 81 a9 70 0a f5 61 ac 8c 6b 00 00 00 00 00 00 00 f9 6f 06 81 7f b9 03 72 9a 7d b6 ff 95 76 97 c9 ed e7 88 5d 94 ff b0 96 9b e0 da f6 0a f9 31 09 eb 1d ee 72 e4 36 3f 51 af 62 af 6f b2 a6 df 3b ec 89 82 2a 7a 0b 67 80 58 fa 3f ef 86 fa ec 21 6e b6 99 21 62 f8 dc bf 71 9c 14 8c d2 f9 c5 5f 49 01 20 3a 9a 8a 2c 3e 90 f3 94 3d bc 10 36 0a 1a 49 70 0d 1d fb f6 6d 69 f6 fb af 50 6c 8b cc e8 bb 0d 87 2a 02 23 89 26 40 7a 4e dd d5 d0 fc 5a 75 2f 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x1000)
// size: len = 0x8f5 (2 bytes)
// }
// }
// }
// len: len = 0x1006 (8 bytes)
// ]
*(uint32_t*)0x200000000000 = 0xfc;
memcpy((void*)0x200000000004, "\xa2\xe3\xad\x21\xed\x0d\x1b\xf9\x1b\x29\x55\x09\x87\xf7\x0e\x06\xd0\x38\xe7\xff\x7f\xc6\xe5\x53\x9b\x0d\x3d\x0e\x8b\x08\x9b\x3f\x3b\x09\x6c\x06\x08\x90\xe0\x87\x8f\x0e\x1a\xc6\xe7\x04\x9b\x09\x6e\x95\x9b\x44\x9a\x24\x0d\x5b\x67\xf3\x98\x8f\x7e\xf3\x19\x52\x01\x00\xff\xe8\xd1\x78\x70\x8c\x52\x3c\x92\x1b\x1b\x5b\x31\x07\x0b\x07\x5d\x0d\x36\xcd\x3b\x78\x13\x0d\xaa\x61\xd8\xe8\x04\x00\x00\x00\x58\x02\xb7\x7f\x07\x22\x72\x27\xb7\xba\x67\xe0\xe7\x86\x57\xa6\xf5\xc2\xa8\x74\xe6\x2a\x9c\xcd\xc0\xd3\x1a\x0c\x9f\x31\x8c\x0d\xa1\x99\x3b\xd1\x60\xe2\x33\xdf\x4a\x62\x17\x9c\x6f\x30\xe0\x65\xcd\x5b\x91\xcd\x0a\xe1\x93\x97\x37\x35\xb3\x6d\x5b\x1b\x63\xdd\x1c\x00\x30\x5d\x3f\x46\x63\x5e\xb0\x16\xd5\xb1\xdd\xa9\x8e\x2d\x74\x9b\xe7\xbd\x1d\xf1\xfb\x3b\x23\x1f\xdc\xdb\x50\x75\xa9\xaa\xa1\xb4\x69\xc3\x09\x00\x00\x00\x00\x00\x00\x00\x75\x27\x1b\x28\x63\x29\xd1\x69\x93\x42\x88\xfd\x78\x9a\xa3\x7d\x6e\x98\xb2\x24\xfd\x44\xb6\x5b\x31\x33\x4f\xfc\x55\xcc\x82\xcd\x3a\xc3\x2e\xcd\xb0\x8c\xed\x6f\x90\x81\xb4\xdd\x0d\x8b\x38\xf3\xcd\x44\x98\xbe\xe8\x00\x49\x08\x41\xbd\xb1\x14\xf6\xb7\x63\x83\x70\x9d\x8f\x5c\x55\x43\x2a\x90\x9f\xda\x03\x9a\xec\x54\xa1\x23\x6e\x80\xf6\xa8\xab\xad\xea\x76\x62\x49\x6b\xdd\xbb\x42\xbe\x6b\xfb\x2f\x17\x95\x9d\x1f\x41\x6e\x56\xc7\x1b\x19\x31\x87\x02\x62\xf5\xe8\x01\x11\x92\x42\xca\x02\x6b\xfc\x82\x1e\x7e\x7d\xaf\x24\x51\x13\x8e\x64\x5b\xb8\x0c\x61\x76\x69\x31\x4e\x2f\xbe\x70\xde\x98\xec\x76\xa9\xe4\x0d\xad\x47\xf3\x6f\xd9\xf7\xd0\xd4\x2a\x4b\x5f\x11\x85\xcc\xdc\xf1\x6f\xf4\x62\x95\xd8\xa0\xfa\x17\x71\x3c\x58\x02\x63\x09\x33\xa9\xa3\x4a\xf6\x74\xf3\xf3\x9f\xe2\x34\x91\x23\x7c\x08\x82\x2d\xec\x11\x09\x11\xe8\x93\xd0\xa8\xc4\xf6\x77\x74\x7a\xbc\x36\x09\x34\xb8\x29\x10\xff\x85\xbf\xd9\x95\x08\x3b\xba\x29\x87\xa6\x73\x99\xea\xc4\x27\xd1\x45\xd5\x46\xa4\x0b\x9f\x6f\xf1\x4a\xc4\x88\xec\x13\x0f\x91\x85\x0a\x27\xaf\x95\x44\xae\x15\xa7\xe4\x54\xde\xa0\x59\x18\xb4\x12\x43\x51\x3f\x00\x00\x00\x00\x00\x00\x00\x0a\x36\x21\xc5\x6c\xea\x8d\x20\xfa\x91\x1a\x0c\x41\xdb\x6e\xbe\x8c\xac\x64\xf1\x76\x79\x14\x1d\x54\xb3\x4b\xbc\x99\x63\xac\x4f\x4b\xb3\x30\x96\x03\xf1\xd4\xab\x96\x62\x03\x86\x1b\x5b\x15\xa8\x41\xf2\xb5\x75\xa8\xbd\x0d\x78\x24\x8e\xbe\x4d\x9a\x80\x00\x26\x95\x10\x4f\x67\x4c\x24\x31\xdc\xa1\x41\xfa\xe2\x69\xca\xb7\x0e\x9a\x66\xf3\xc3\xa9\xa6\x3e\x96\x39\xe1\xf5\x9c\x0e\xde\x26\xc6\xb5\xd7\x4b\x07\x8a\x5e\x15\xc3\x16\x34\xe5\xae\x09\x8c\xe9\xee\x70\x77\x1a\xaa\x18\x11\x9a\x86\x7e\x10\x88\x33\x49\x75\xe9\xf7\x34\x83\xb6\xa6\x2f\xa6\x78\xca\x14\xff\xd9\xf9\xdb\x2a\x78\x69\xd8\x58\x64\x05\x65\x26\xf8\x89\xaf\x43\xa6\x05\x60\x80\x57\x22\x86\x52\x24\x49\xdf\x46\x6c\x63\x2b\x35\x70\x24\x3f\x98\x9c\xce\x7c\xd9\xf4\x65\xe4\x1e\x61\x0c\x20\xd8\x04\x21\xd6\x53\xa5\x52\x00\x00\x00\x82\x13\xb7\x04\xc7\xfb\x08\x2f\xf2\x75\x90\x67\x8e\xf9\xf1\x90\xba\xe9\x79\x09\x50\x70\x41\xd8\x60\x42\x0c\x56\x64\xb2\x79\x21\xb1\x4d\xc1\xdb\x88\x92\xfd\x32\xd0\xad\x7b\xc9\x46\x81\x35\x91\xad\x8d\xef\xf4\xb0\x5f\x60\xce\xa0\xda\x77\x10\xac\x00\x00\x00\x00\x00\x00\x80\x00\xbe\xa3\x7c\xe0\xd0\xd4\xaa\x20\x2f\x92\x8f\x28\x38\x1a\xab\x14\x4a\x5d\x42\x9a\x04\xa6\xa2\xb8\x3c\x70\x68\xae\x94\x9e\xd0\x6e\x28\x8e\x81\x0b\xac\x9c\x76\x60\x00\x25\xe1\x9c\x90\x7f\x8e\xa2\xe2\x01\x00\x00\x00\x82\x71\xa1\xf5\xf8\x52\x8f\x22\x7e\x79\xc1\x38\x9d\xbd\xff\xfe\x49\x2f\x21\x57\x9d\x2c\x15\xb8\xc7\x0c\xdb\x1c\x33\x2d\x86\xd8\x73\x41\x43\x27\x50\x86\x1e\xc2\xbc\x34\x51\xed\xca\x19\x4b\x22\x1c\xfe\xc4\x60\x3d\x27\x6b\xba\xa1\xdf\xa6\xd4\xfb\x8a\x48\xa7\x6e\xaf\xc9\xa9\xa0\x27\x0e\x4c\x10\xd6\x4c\xd5\xa6\x24\x27\x26\x4f\x40\x77\xfe\x76\x3c\x43\x47\x08\x33\xac\x96\xc4\x5f\x35\x7c\xbb\xab\xa8\xf1\xb1\xfd\xcc\x7c\xbb\x61\xa7\xcd\xb9\x74\x4e\xd7\xf9\x12\x9a\xed\xe2\xbe\x21\xcc\xfd\xc4\xe9\x13\x4f\x86\x84\xb3\xa4\xf3\x54\xda\x9a\x79\x5e\x96\x33\x4e\x20\x7d\xff\x70\xf1\x98\x80\x37\xb2\xed\x3a\xaf\x57\x5c\x0b\x88\xd8\xf1\x46\x68\x40\x78\x41\x6d\x59\xfd\xee\x53\x25\x92\x89\x74\xd1\x2d\xad\x99\xda\xc4\x4c\x3f\x00\x08\x04\x70\x96\xa4\x40\x02\xbe\xbc\x24\x20\xae\xd9\x2f\xa9\xb6\x57\x8b\x47\x79\x41\x5d\x4a\xc0\x1b\x75\xd5\x49\x5c\x11\x80\x45\x65\x1c\xf4\x1c\x2f\xc4\x8b\x77\x8e\xfa\x5e\xa5\x67\x77\x47\x43\x0a\xf4\x16\x2b\x98\x7b\x80\xc3\xe0\x01\xcd\x34\xe5\xc9\x2f\x76\xcc\x4c\x24\xee\xb8\xbc\x4e\x9a\xc2\xae\xd9\xe5\x38\x03\xed\x0c\xa4\xae\x3a\x97\x37\xd2\x14\x06\x00\x05\xea\x6f\x17\x83\xe2\x87\xb3\xbe\xe9\x6e\x3a\x72\x6e\xaf\xe2\xfd\xfa\xa7\x8d\x1f\x48\xc1\x3b\x64\xdf\x07\x84\x77\x54\xb8\x40\x0d\xaa\xa6\x9b\xf5\xc8\xf4\x35\x0a\xea\xe9\xca\x12\x07\xe7\x82\x83\xcd\x0b\x20\xce\xb3\x60\xc7\xe6\x58\x82\x81\x63\xe2\xd2\x5c\x4a\xa3\x48\x56\x1f\x92\x7e\x88\xf6\x3a\xa7\x0e\x73\xa5\xe6\x9b\x3d\xf3\x49\x59\x03\xf0\x65\x72\xe1\xe0\x07\xfa\x55\xa2\x99\x9f\x59\x6d\x06\x73\x12\xf5\x77\x9e\x8d\xbf\xdc\xf3\x42\x71\x38\xf3\xd4\x44\xd2\x63\x9a\x10\x47\x7f\x9b\xec\x4b\x0b\xbb\x6e\x3c\x04\xbe\x68\x98\x1f\x39\x22\x03\xdd\x0e\xe3\xef\x47\x8e\x16\xda\xcf\xc5\xe3\xe0\x3c\xf7\xab\x8e\x39\x02\xf1\xb0\xff\x03\x4e\xf6\x55\xb2\x53\xca\x50\x93\x83\x81\x5b\x1b\x6f\xc6\x52\x2d\x4e\x4f\xdc\x11\xa4\x8c\xf4\x2d\x48\x60\x46\x75\xfd\xe2\xb9\x4c\xf0\x05\x00\xa2\x69\x08\x91\xab\xf8\xab\x9c\x01\x50\x73\x01\x4d\x9e\x08\xd4\x33\x8b\x87\x80\xbd\xec\xd4\x36\xcf\x05\x41\x35\x9b\xaf\xff\xa4\x52\x37\xf1\x04\xb9\x62\x10\x40\x3b\x2d\xe9\xef\xed\x49\x6f\x42\x35\x5b\xc7\x87\x2c\x82\x74\x67\xcf\xa5\xc4\xe7\x27\x30\xd5\x6b\xd0\x68\xed\x21\x1c\xf8\x47\x53\x5e\xde\xcb\x7b\x37\x3f\x78\xb0\x95\xb6\x84\x41\xa3\x4c\xb5\x16\x82\xa8\xae\x4d\x24\xad\x04\x65\xf3\x92\x7f\x88\x9b\x81\x30\x76\x03\x8e\x79\xa7\x96\x2f\xb3\x85\xa8\x1a\xa1\x02\x0f\x06\xc4\xc2\xba\x1d\xd5\xca\xc7\xc1\x88\x76\xda\x86\x5d\x25\x87\x34\xdd\x73\x58\x3d\xf2\x92\x89\x24\x48\x03\x9e\xf7\x99\xcf\x06\x30\xbe\xcd\xcc\xe0\x45\x79\xb5\x56\x1d\xc8\x25\xab\x82\x98\x27\x94\x5e\x02\x0c\x1f\x67\xee\x61\x5f\xeb\x62\x43\x37\x8e\x06\x10\x06\x0f\x02\xcc\xa4\xe9\x1b\x2f\x00\x1e\xdb\x3d\x78\xfb\x4b\x55\x66\x8d\xda\x93\xae\xc9\x2a\x5d\xe2\x03\x71\x7a\xa4\x9c\x2d\x28\x4a\xcf\xab\xe2\x62\xfc\xcf\xcb\xb2\xb7\x5a\x21\x83\xc4\x6e\xb6\x5c\xa8\x10\x4e\x1b\x4d\xa7\xfb\xb7\x7a\xb2\xfc\x04\x3a\xea\xd8\x7c\x32\xab\x87\x5e\xe7\xc2\xe7\xb7\x01\x9c\x98\x2c\xd3\xb4\x3e\xae\xb1\xa5\xfb\x13\x5c\x0c\x7d\xce\xe8\xfe\x65\x16\xa3\x28\x03\x2f\x88\xc0\x42\x89\x18\x24\x65\x9e\x9e\x94\x26\x5c\x80\x3b\x35\xee\x5f\x83\xa2\xb2\x10\x52\x01\x06\x89\xaf\x9c\x25\x1f\xe5\x29\x4b\x3d\x18\x02\xd5\x67\x6d\x95\xf1\x60\xec\x97\xb1\xad\x94\x87\x2c\xb2\x04\x46\x42\xc3\x7b\x4a\x6c\xc6\xc0\x4e\xff\xc1\x67\x2d\xb7\xe4\xb6\x8d\x78\x7d\x9a\x7a\x50\x8a\xe5\x4b\x3c\xd7\x36\x9d\xde\x50\xe8\xc7\x7d\x95\xa3\xd3\x61\xc0\x40\xba\xbb\x17\x16\x07\xca\xac\x2a\x35\x59\xad\x4f\x75\x46\x5f\x49\xc0\xd0\xae\x37\x16\xdb\x6e\x00\xcb\x11\xdb\x4a\x5f\xad\xe2\xa5\x7c\x10\x23\x8e\x20\x4a\x67\x73\x7c\x3b\x42\xaa\xe5\x01\xb2\x0f\x76\x94\xa0\x0f\x16\xe2\xd0\x17\x40\x35\xa2\xc2\x26\x56\xdc\x29\x88\x0a\xce\xbd\xbe\x8d\xdb\xd7\x5c\x2f\x99\x8d\x8a\xc2\xdf\xad\x2b\xa3\xa5\x04\x76\x7b\x6b\x45\xa4\x59\x57\xf2\x4d\x75\x8e\xd0\x24\xb3\x84\x9c\x11\xd4\x12\xa2\xa0\x3b\x40\x47\x49\x70\x22\xd9\xc3\x0e\x23\xef\x4d\xf5\xc8\x96\x44\xf4\x8b\xb5\x36\xf7\x94\x5b\x59\xd7\xbc\xdd\xff\x75\x44\x13\xd1\x35\x27\x3e\xa8\xe7\x5f\x22\xf2\x16\xc6\xb9\x99\x0a\xe7\x18\x06\xf2\xc0\x0b\x40\x25\xc4\x8b\x75\xc0\xf7\x3c\xdb\x9a\x7b\x8f\xa3\x67\xb5\x00\x28\x06\x7e\x7f\x16\xf4\xdd\x56\x9d\x46\x2f\x4f\x19\xea\xcd\xb3\xed\x70\xee\xeb\xb4\x48\x3f\x8f\xd7\x77\xd4\x43\xe8\xb4\x04\x27\xdb\x6f\xe2\x90\x68\xc0\xca\x3d\x24\x14\x44\x2e\x8f\x3a\x15\x47\x04\xb0\xe5\x1b\xc6\x64\xa1\x37\xb2\x6b\xe7\x19\xf4\xf7\xc9\xa5\x67\x8a\x67\x4d\xfc\x95\xdf\x80\xb9\xce\x37\x5d\xd6\x49\xc8\xc7\x04\xe5\x09\xbd\x88\xc8\xe6\x3d\x8c\x7d\xd6\x70\x71\x11\x5c\x89\x82\xba\x46\xaf\x4d\x6a\xdc\xc9\xf6\x8a\x75\xb9\x39\x7b\x03\x51\x53\xfa\xe4\x63\x66\xe7\x20\x5d\xd8\xd6\xf3\x75\x25\xc1\xa0\xe9\x46\x10\xdd\x94\x32\x3f\x6c\x15\xd0\x85\x19\x71\x49\xbf\xd6\x65\x55\x48\xcf\xd9\xc5\x2c\x97\x11\x93\x7f\x79\xab\xb1\xa1\x24\xf1\x21\x04\x65\x48\x3c\xd3\xb2\xd7\x83\x78\xcf\xb8\x5e\xd8\x2e\x7d\xa0\xf6\xeb\x6d\x27\x9f\x2a\xe4\x55\x92\x5d\x0f\x6f\x1b\xa5\x71\xeb\xa2\x81\xf2\xa6\x54\xfb\x39\xdd\xff\x3b\x48\x44\x39\xff\x15\x8e\x7c\x54\x19\xe0\x37\xf3\xe3\xad\x03\x8f\x22\x11\xf1\x03\x31\x95\x56\x3c\x7f\x93\xcd\x54\xb9\x09\x4f\x22\x6e\x78\x32\x71\xe1\xe5\xa2\xa2\xc1\x07\x12\xea\xb6\x25\xd6\x49\x31\xcd\x4f\xfe\x67\x38\xd9\x7b\x9b\x5e\xf8\x28\xee\x9f\xb0\x59\xfc\x01\xaf\x0e\x79\xc1\xe1\x4b\x1d\x25\x98\x8c\x69\xa3\x99\x56\x7c\x1d\x93\x76\x8f\x79\x71\xd3\x14\x88\xb8\x65\x8a\x20\x87\x8b\x7c\x1d\xd7\xba\x02\xfc\x42\x93\x9d\xde\x3d\x4a\x33\x39\xa6\x5d\x50\x7d\xc5\x9c\x51\x09\x7b\x40\x51\x77\x05\xda\x56\xe9\xeb\xf0\xaf\xa5\x32\x82\xbf\x86\xdb\xb5\x8c\x54\x80\x69\xff\x6e\xb9\x5a\xad\xe7\xcc\x66\xd7\xbb\xef\x72\x47\x79\xca\x1f\x73\x1b\x33\x46\xff\x17\x70\x50\x37\x3d\x79\xff\x7b\x3e\x7f\x9b\xc0\xc1\xb4\xb2\x66\xa8\x87\x8b\x90\xba\xaa\x03\x9d\x3e\x3b\x63\x97\x9a\xc3\xdf\x6e\x6f\x48\x59\xaf\xd5\x02\x38\xc7\x54\x7a\x39\xb6\x08\x10\x93\x80\x44\xae\x18\x5d\x2b\xa3\xe0\x0a\x4e\x73\x67\x68\x64\xae\x09\x0d\x81\xea\xee\x5e\xe6\xcf\x1d\x0a\xb3\x78\xdd\x4d\xd8\x91\xe9\x37\xc2\xea\x54\x10\xe0\x51\x30\x05\x00\x00\x00\x00\x00\x00\x00\x39\x11\xfa\xb9\x64\xc2\x71\x55\x00\x27\x69\x7b\x52\x16\x06\x87\x46\x16\x02\xf8\x8d\xf1\x65\xd8\x84\xb3\x6e\xc2\xb6\xc2\x5a\x2f\x33\xc7\x15\x68\x7e\x9d\x4a\xfb\x96\xd6\x86\x1a\xca\x47\xda\x73\xd6\xf3\x14\x43\x45\xf4\x88\x43\xdd\x01\x4e\x5c\x5a\xd8\xfe\x99\x57\x54\xbd\x9c\xf3\x2f\xce\x1e\x31\x91\x9c\x4b\x20\x82\xfb\x0a\x30\xb9\xde\xae\x84\xbe\xd4\xb2\x80\x45\x63\x40\x73\xc9\xc5\x8c\x89\xd9\xe9\x9c\x81\x76\x91\x77\xc6\xd5\x94\xf8\x8a\x4f\xac\xfd\x4c\x73\x5a\x20\x30\x7c\x73\x7a\xfa\x2d\x60\x39\x94\x73\x29\x6b\x83\x1d\xbd\x93\x3d\x93\x99\x4b\xa3\x06\x42\x79\xb1\x0e\xa0\xc5\x83\x3f\x41\xf1\x57\xea\x23\x02\x99\x3d\xbe\x43\x3b\x1a\xa3\xa3\x76\x6d\x54\x39\x02\x04\x84\xf4\x11\x3c\x4c\x85\x94\x65\xc3\xb4\x15\xc3\x43\xf7\xf1\x40\xf3\x19\x53\x9d\x5b\xf3\x72\xaa\xae\xa1\xcc\x43\xa6\xc5\xcb\xe5\x97\x58\xbf\xee\x29\x16\x58\x0d\xac\x4b\x00\x8e\x59\x5f\x43\x74\x91\xd8\x7a\xbe\xd0\x2c\xef\xcd\x9d\xb5\x3d\x94\xd0\x2d\xae\xe6\x79\x18\xe5\xd6\x78\x74\x63\x18\x3b\x4b\x87\xc1\x05\x00\x00\x00\x2f\x78\x09\x95\x9b\xc0\x48\x85\x06\x13\xd1\x7c\xa5\x10\x55\xf2\xf4\x16\xa4\x4f\xe1\x80\xd2\xd5\x0c\x31\x2c\xca\x7c\xb1\x4a\x2b\xdc\x33\x1f\x57\xa9\x81\x71\x39\xa2\x06\xfc\x76\x95\x72\x27\xff\xff\x2d\xe2\x0a\x4b\x8e\x37\x37\xfb\xb4\x29\x13\x77\x7c\x06\x37\x6f\x79\x9e\xba\x36\x7e\x21\xf9\x4c\xa5\x98\x70\x5f\x5d\xcb\x76\x7d\x6f\x09\x00\xd6\xb0\xf6\x09\x5e\x53\xc4\xc4\x23\x4d\x0c\x1f\xbe\x43\x4f\x6a\xb8\xf4\x3c\x00\x13\xee\x93\xb8\x39\x46\xee\x77\x59\xe8\x9d\x7b\xdd\x1a\x32\xd7\xb3\x11\x71\x1b\x75\x7f\xe4\x3c\x06\xd2\x1a\x35\x81\x0d\x8f\xe9\x8b\x27\xfa\xea\x8a\xa1\x2b\xc8\x71\x6e\xef\xc5\xc9\x7c\x45\xac\x33\xee\xec\x96\x4c\x52\x14\xbc\x3a\x93\x59\xbd\xea\x1c\xcc\xab\x94\xf1\x5e\x36\x31\x9c\xb3\x4e\xbc\xac\xed\xb8\x2c\x2e\xd3\xde\x5a\x8a\x8f\x00\x11\xe8\xf7\x4e\x82\xd7\xf9\x60\x93\x53\x0e\x76\x69\x28\x39\xd7\x96\x19\x39\xad\xfd\xee\xea\xff\x19\xd1\x1e\xfc\xaf\xb6\xd5\x46\xfe\xf2\x71\xe8\x9d\x6c\xc2\x38\x9e\x81\xff\x58\xce\xfc\xce\x3f\xbf\x46\x25\xa7\xe7\xde\x40\xe4\x2e\x07\xb3\x44\x49\xe1\x5e\x06\x5c\xc7\x34\x00\x02\x00\x00\x00\x00\x00\x00\xf2\x88\xa4\x51\x0d\xe0\x3d\xab\x19\xd2\x62\x85\xed\xa8\x91\x56\xd5\x0d\xd3\x85\xa6\x03\x33\xba\x5b\xbf\x5d\x77\xcd\x70\x07\xad\x15\x19\xad\x54\x70\xde\x3d\xd6\xd6\x08\x0c\xaf\xcc\xf8\xa9\x74\x06\xbb\x6b\x68\xa1\xf0\xc4\x54\x98\x20\xa7\x3c\x88\x0f\x47\x5f\x73\x2a\xe0\x03\x98\xe8\xbd\x1f\x41\x08\xb7\x80\x7f\xb3\x3b\x72\x68\x5e\xc3\x7a\x2d\x3f\x76\x64\x13\xa6\x04\x59\x51\x62\x46\xe5\xa1\xd9\x98\xa2\x01\x7a\xef\x09\x48\xa6\x8c\xf2\x55\x31\x5a\xb8\x0d\xd3\x49\xe8\x91\xae\xf5\x95\xdc\x4d\x47\x0e\x8a\xc3\x2a\x30\x8e\x15\xfc\x37\xd0\x6a\xea\xc2\x89\xc0\x52\x3f\x48\x3e\x1f\xf7\x40\x8c\x60\x87\xf1\xab\x65\x2f\x2e\xf9\x1d\x4f\x2b\x01\x98\x7b\x0f\x46\xda\x03\x4e\x5c\x3f\x74\x5a\x7e\xe8\x10\x1a\x39\x34\xc5\x4e\x24\xb4\x8e\xc0\x27\x5e\x2d\x06\x87\xdc\x74\x6b\x08\x27\xcb\xf6\x52\xf4\x06\xc6\xb9\x5f\x27\x22\xe5\x8c\x05\xf7\x52\xce\x21\x26\x59\x6e\x1c\xd7\x65\x5b\x90\x48\x01\x78\x4c\x41\x6b\x22\xf7\x3d\x32\x46\x78\xe2\x72\x4f\x43\xf1\xfe\x68\x7c\x7e\x8a\x60\xc2\x8b\x82\xb6\x52\x83\x41\xb6\x48\xcd\xd5\x6f\xed\x7c\xdc\xbb\x15\x75\x91\x2d\x5e\xcd\x36\xde\xa3\xbc\xa0\xb7\x42\x7d\x83\x92\xc6\x28\x94\x55\xe8\xf8\xd2\xab\x22\x42\x72\x92\x51\xae\x03\x3a\x9e\x02\x21\x0e\x62\xdf\x05\x46\xa7\x4b\x33\x3a\x1c\x48\xf9\x5f\xd5\x4a\xcb\x57\x41\x25\x9e\x8c\x54\x88\xef\xee\xe3\x27\x41\x5c\xc1\x94\x51\x43\x2c\x6f\x14\xc2\x76\x93\x10\x2a\x3c\xd8\x48\x57\xcd\x65\x86\xfc\x5c\xa9\xa9\x3e\xb0\x14\x5f\xac\x06\x62\xff\x86\x10\x7f\x99\x8a\x8e\xf7\xdf\x8a\xa1\x40\x46\xc5\x5b\x03\xd3\xd4\x7f\x88\xa8\xd6\x0f\x77\x74\xa2\xee\x08\x75\x88\x97\xfb\x41\x1a\x94\xb3\xc2\xfc\x5d\x5f\x0d\xb4\x2c\x04\x56\xec\x01\x5f\x08\xe5\x24\x7d\x33\xae\x2d\x35\x60\x3f\xf8\x45\x4c\x16\xf8\x34\x28\x56\x93\x51\x25\x10\x2b\xb7\x84\xed\x71\x48\xb6\xce\x43\x1b\x63\xee\x35\x6b\x0c\x78\x5f\x2f\x47\xb9\x0e\x29\x38\x9f\x22\xfc\x5b\x59\xa7\x0e\xfa\xea\x2b\xd4\x01\x95\xaf\x44\x86\x22\x0d\x70\x2e\x30\xbf\xc4\x3c\x10\xec\x23\xea\x62\x83\x99\x4a\x7d\xde\x4d\xcb\x61\xfe\xa6\xb6\x51\xfb\x1d\x62\x45\x8d\x07\x41\xa1\x28\x30\x05\x2f\xcc\x46\x0d\xb0\x43\xaf\xe5\x25\x62\x9b\x40\xd7\xce\xe4\x58\xe4\xcb\x5e\x93\x0e\xd6\x24\x80\x6c\x43\xa0\x06\xe3\x93\x36\xd0\x7c\x2b\x80\x81\xc1\x28\xad\x27\x06\xf4\x82\x61\xf7\x89\x74\x84\xc2\x97\xa1\xa6\x61\x3b\xc1\x8f\x5a\x38\xd4\x42\x76\x8a\xf3\x80\x41\xef\xe0\x3d\x15\x2e\xf9\x5f\xf5\x69\xe7\x6d\xb2\x39\x1f\x45\x09\xd7\xf3\x39\xd9\x2f\xdb\x4a\x89\x36\x49\x49\xda\x39\x80\x00\x00\x00\x00\x00\x00\x00\xd8\x0a\x4f\xe6\x54\x57\x83\x76\xe5\x99\xaf\xf3\x56\x5b\x1d\x53\x1f\x30\x91\x2b\x99\x45\x03\x0b\x81\xea\x99\x35\xfd\x46\xed\xb4\x4a\x78\xf6\x15\x25\x54\x90\xa4\xb6\x21\x50\x1f\x2a\x9e\x4d\x24\x62\x4c\x4d\xac\x92\x74\x11\x8c\x67\x58\x4f\x5d\x37\x47\x55\x53\x4d\x7f\x68\xf6\x79\xc4\xff\x51\x6a\x9c\x86\x1a\x0e\x7e\x65\x86\x8f\xcb\x2b\xf1\xcb\x9a\xea\x4e\x05\xdf\x72\x27\x9f\xdb\x0d\x2b\x9e\x93\x5c\x5a\xf3\xcf\x47\x4b\x06\x79\xdf\xc2\x48\xc1\xf5\xae\xa4\xb8\xb3\x2c\x5d\x29\x5e\x57\x07\x9d\x0f\xe6\x62\xa4\x6b\x7f\x71\xcd\x47\x74\x4d\xb8\x6c\x50\xb7\x04\xc9\x71\xd9\x02\x95\xc7\xb2\xc7\x43\x9a\x2d\x78\xcc\xfa\x79\xb5\xfc\x2b\xff\x6b\xbf\x84\x02\x62\xbf\x89\x39\x4b\x3e\x06\x91\x95\x12\x64\xd2\x70\x0c\x83\x8f\xa2\xc7\xb3\x42\x52\x60\xf5\x95\x54\xe5\x02\xdc\xea\x39\xcb\x31\x3b\x00\x00\x00\x00\x00\x00\x4c\xa7\xc1\x2f\x45\x85\x8d\x62\x84\xca\x62\x70\xd6\xb2\xf0\xe5\x8f\xde\xd8\xa7\xb4\xa3\x02\xa9\x7b\xc6\x41\xdf\x07\x72\x0b\xa2\xb2\x6b\xbf\xcc\x80\x7c\xa0\xab\xb1\xb4\x43\x22\x26\x9c\x21\xc5\xec\x68\xcb\x06\x8e\xa8\x80\x67\xd9\x05\xea\x91\x7b\xb0\x3e\xef\xda\xeb\xde\xab\xf2\xd0\xdc\xe8\x09\x97\xc9\x15\xc8\x94\x9d\xe9\x92\x58\x7c\x2c\xb5\xfe\x36\xd7\xd3\xe5\xdb\x21\xb0\x94\xb8\xb7\x79\x40\xb5\xf0\x77\x22\xe4\x7a\x08\xd3\x67\xe5\xf8\x4c\x96\xec\x66\x4b\x72\x93\x4b\x99\xb3\x10\x9a\xf6\x5d\x77\xe8\x6a\xbd\x68\x59\xcd\xdf\x4b\xba\xe1\xf0\x93\x04\x62\xdf\x15\xfd\xdb\xc4\x85\x62\xea\x35\x11\xa8\x06\x5e\xf0\x28\xcf\x12\xf1\x4d\xcf\x6e\xbe\xcd\x8d\x98\x48\x36\x17\x4f\xaf\x1a\xa6\x09\xe5\xf1\xee\x11\x62\xdf\xa1\x3b\xdc\x1f\xa7\xcf\xaa\xdb\xa8\x5c\x72\xe9\x75\x8f\x03\xa7\x05\x00\xbe\x53\xf8\xd2\xa1\xdf\xb1\xc6\x8c\xc1\x64\xb0\xa0\x78\x0d\x97\x1a\x96\xea\x2c\x4d\x4c\xa0\x39\x8c\x22\x35\x98\x0a\x93\x07\xb3\xd5\xbd\x3b\x01\xfa\xff\xd0\xa5\xdb\xed\x28\x81\xa9\x70\x0a\xf5\x61\xac\x8c\x6b\x00\x00\x00\x00\x00\x00\x00\xf9\x6f\x06\x81\x7f\xb9\x03\x72\x9a\x7d\xb6\xff\x95\x76\x97\xc9\xed\xe7\x88\x5d\x94\xff\xb0\x96\x9b\xe0\xda\xf6\x0a\xf9\x31\x09\xeb\x1d\xee\x72\xe4\x36\x3f\x51\xaf\x62\xaf\x6f\xb2\xa6\xdf\x3b\xec\x89\x82\x2a\x7a\x0b\x67\x80\x58\xfa\x3f\xef\x86\xfa\xec\x21\x6e\xb6\x99\x21\x62\xf8\xdc\xbf\x71\x9c\x14\x8c\xd2\xf9\xc5\x5f\x49\x01\x20\x3a\x9a\x8a\x2c\x3e\x90\xf3\x94\x3d\xbc\x10\x36\x0a\x1a\x49\x70\x0d\x1d\xfb\xf6\x6d\x69\xf6\xfb\xaf\x50\x6c\x8b\xcc\xe8\xbb\x0d\x87\x2a\x02\x23\x89\x26\x40\x7a\x4e\xdd\xd5\xd0\xfc\x5a\x75\x2f\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 4096);
*(uint16_t*)0x200000001004 = 0x8f5;
syscall(__NR_write, /*fd=*/r[2], /*data=*/0x200000000000ul, /*len=*/0x1006ul);
return 0;
}
]
|
| 3/1 |
2026/05/10 19:26 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/cbad7996b082969065ced471e435d1359c15f2ef]
|
| 4/1 |
2026/05/10 19:26 |
action |
kernel-builder |
14m
Results: map[KernelObj:/app/workdir/cache/build/61ec01f50f0285f299482cf81d495e8e9fdcb082]
|
| 5/1 |
2026/05/10 19:41 |
action |
crash-reproducer |
1m
Results: map[ReproducedBugTitle:WARNING in cm109_urb_irq_callback/usb_submit_urb ReproducedCrashReport:cm109 1-1:0.8: cm109_urb_irq_callback: urb status -71
------------[ cut here ]------------
URB ffff888046210500 submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x7b/0x18b0 drivers/usb/core/urb.c:379, CPU#1: swapper/1/0
Modules linked in:
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:usb_submit_urb+0x7e/0x18b0 drivers/usb/core/urb.c:379
Code: 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 17 72 06 fb 49 83 3e 00 74 40 e8 6c 63 9c fa 48 8d 3d 65 92 ff 08 48 89 de <67> 48 0f b9 3a b8 f0 ff ff ff eb 11 e8 51 63 9c fa eb 05 e8 4a 63
RSP: 0018:ffffc900001f8a00 EFLAGS: 00010046
RAX: ffffffff8727c654 RBX: ffff888046210500 RCX: ffff8880404b0000
RDX: 0000000000000100 RSI: ffff888046210500 RDI: ffffffff902758c0
RBP: 000000000000000f R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff5200003f140 R12: 0000000000000820
R13: ffff88804fd73030 R14: ffff888046210508 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff8880ebc29000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558409200a78 CR3: 0000000046b1e000 CR4: 00000000000006f0
Call Trace:
<IRQ>
cm109_submit_ctl drivers/input/misc/cm109.c:380 [inline]
cm109_urb_irq_callback+0x6e2/0xc90 drivers/input/misc/cm109.c:431
__usb_hcd_giveback_urb+0x376/0x540 drivers/usb/core/hcd.c:1657
dummy_timer+0xbc0/0x4650 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2011
handle_softirqs+0x22a/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1061
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ab 90 02 e9 d3 f5 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 53 33 21 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffc9000046fe20 EFLAGS: 00000202
RAX: 0000000000076dab RBX: ffffffff8199768a RCX: 0000000080000001
RDX: 0000000000000001 RSI: ffffffff8deddb4e RDI: ffffffff8c0ea120
RBP: ffffc9000046ff10 R08: ffff88807ee339db R09: 1ffff1100fdc673b
R10: dffffc0000000000 R11: ffffed100fdc673c R12: 0000000000000001
R13: 1ffff11008096000 R14: 0000000000000001 R15: 1ffff11008096000
arch_safe_halt arch/x86/kernel/process.c:766 [inline]
default_idle+0x9/0x20 arch/x86/kernel/process.c:767
default_idle_call+0x72/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x36a/0x5f0 kernel/sched/idle.c:352
cpu_startup_entry+0x43/0x60 kernel/sched/idle.c:451
start_secondary+0x101/0x110 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x147
</TASK>
----------------
Code disassembly (best guess):
0: 89 f0 mov %esi,%eax
2: 48 c1 e8 03 shr $0x3,%rax
6: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
b: 74 08 je 0x15
d: 4c 89 f7 mov %r14,%rdi
10: e8 17 72 06 fb call 0xfb06722c
15: 49 83 3e 00 cmpq $0x0,(%r14)
19: 74 40 je 0x5b
1b: e8 6c 63 9c fa call 0xfa9c638c
20: 48 8d 3d 65 92 ff 08 lea 0x8ff9265(%rip),%rdi # 0x8ff928c
27: 48 89 de mov %rbx,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 f0 ff ff ff mov $0xfffffff0,%eax
34: eb 11 jmp 0x47
36: e8 51 63 9c fa call 0xfa9c638c
3b: eb 05 jmp 0x42
3d: e8 .byte 0xe8
3e: 4a rex.WX
3f: 63 .byte 0x63
ReproducedFaultInjection:]
|
| 6/1 |
2026/05/10 19:42 |
action |
codesearch-prepare |
10m
Results: map[Index:codesearch-index]
|
| 7/1 |
2026/05/10 19:53 |
agent |
debugger |
7m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash is:
cm109 1-1:0.8: cm109_urb_irq_callback: urb status -71
------------[ cut here ]------------
URB ffff888046210500 submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x7b/0x18b0 drivers/usb/core/urb.c:379, CPU#1: swapper/1/0
Modules linked in:
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:usb_submit_urb+0x7e/0x18b0 drivers/usb/core/urb.c:379
Code: 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 17 72 06 fb 49 83 3e 00 74 40 e8 6c 63 9c fa 48 8d 3d 65 92 ff 08 48 89 de <67> 48 0f b9 3a b8 f0 ff ff ff eb 11 e8 51 63 9c fa eb 05 e8 4a 63
RSP: 0018:ffffc900001f8a00 EFLAGS: 00010046
RAX: ffffffff8727c654 RBX: ffff888046210500 RCX: ffff8880404b0000
RDX: 0000000000000100 RSI: ffff888046210500 RDI: ffffffff902758c0
RBP: 000000000000000f R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff5200003f140 R12: 0000000000000820
R13: ffff88804fd73030 R14: ffff888046210508 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff8880ebc29000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558409200a78 CR3: 0000000046b1e000 CR4: 00000000000006f0
Call Trace:
<IRQ>
cm109_submit_ctl drivers/input/misc/cm109.c:380 [inline]
cm109_urb_irq_callback+0x6e2/0xc90 drivers/input/misc/cm109.c:431
__usb_hcd_giveback_urb+0x376/0x540 drivers/usb/core/hcd.c:1657
dummy_timer+0xbc0/0x4650 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2011
handle_softirqs+0x22a/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1061
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ab 90 02 e9 d3 f5 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 53 33 21 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffc9000046fe20 EFLAGS: 00000202
RAX: 0000000000076dab RBX: ffffffff8199768a RCX: 0000000080000001
RDX: 0000000000000001 RSI: ffffffff8deddb4e RDI: ffffffff8c0ea120
RBP: ffffc9000046ff10 R08: ffff88807ee339db R09: 1ffff1100fdc673b
R10: dffffc0000000000 R11: ffffed100fdc673c R12: 0000000000000001
R13: 1ffff11008096000 R14: 0000000000000001 R15: 1ffff11008096000
arch_safe_halt arch/x86/kernel/process.c:766 [inline]
default_idle+0x9/0x20 arch/x86/kernel/process.c:767
default_idle_call+0x72/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x36a/0x5f0 kernel/sched/idle.c:352
cpu_startup_entry+0x43/0x60 kernel/sched/idle.c:451
start_secondary+0x101/0x110 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x147
</TASK>
----------------
Code disassembly (best guess):
0: 89 f0 mov %esi,%eax
2: 48 c1 e8 03 shr $0x3,%rax
6: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
b: 74 08 je 0x15
d: 4c 89 f7 mov %r14,%rdi
10: e8 17 72 06 fb call 0xfb06722c
15: 49 83 3e 00 cmpq $0x0,(%r14)
19: 74 40 je 0x5b
1b: e8 6c 63 9c fa call 0xfa9c638c
20: 48 8d 3d 65 92 ff 08 lea 0x8ff9265(%rip),%rdi # 0x8ff928c
27: 48 89 de mov %rbx,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 f0 ff ff ff mov $0xfffffff0,%eax
34: eb 11 jmp 0x47
36: e8 51 63 9c fa call 0xfa9c638c
3b: eb 05 jmp 0x42
3d: e8 .byte 0xe8
3e: 4a rex.WX
3f: 63 .byte 0x63
The following C code is a draft of the vulnerable syscall sequence. Keep in mind that
it may lack the precise threading, sandboxing, and some arguments of a working reproducer:
// autogenerated by syzkaller (https://github.com/google/syzkaller)
#define _GNU_SOURCE
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/usb/ch9.h>
static unsigned long long procid;
static void sleep_ms(uint64_t ms)
{
usleep(ms * 1000);
}
#define MAX_FDS 30
#define USB_MAX_IFACE_NUM 4
#define USB_MAX_EP_NUM 32
#define USB_MAX_FDS 6
struct usb_endpoint_index {
struct usb_endpoint_descriptor desc;
int handle;
};
struct usb_iface_index {
struct usb_interface_descriptor* iface;
uint8_t bInterfaceNumber;
uint8_t bAlternateSetting;
uint8_t bInterfaceClass;
struct usb_endpoint_index eps[USB_MAX_EP_NUM];
int eps_num;
};
struct usb_device_index {
struct usb_device_descriptor* dev;
struct usb_config_descriptor* config;
uint8_t bDeviceClass;
uint8_t bMaxPower;
int config_length;
struct usb_iface_index ifaces[USB_MAX_IFACE_NUM];
int ifaces_num;
int iface_cur;
};
struct usb_info {
int fd;
struct usb_device_index index;
};
static struct usb_info usb_devices[USB_MAX_FDS];
static struct usb_device_index* lookup_usb_index(int fd)
{
for (int i = 0; i < USB_MAX_FDS; i++) {
if (__atomic_load_n(&usb_devices[i].fd, __ATOMIC_ACQUIRE) == fd)
return &usb_devices[i].index;
}
return NULL;
}
static int usb_devices_num;
static bool parse_usb_descriptor(const char* buffer, size_t length, struct usb_device_index* index)
{
if (length < sizeof(*index->dev) + sizeof(*index->config))
return false;
memset(index, 0, sizeof(*index));
index->dev = (struct usb_device_descriptor*)buffer;
index->config = (struct usb_config_descriptor*)(buffer + sizeof(*index->dev));
index->bDeviceClass = index->dev->bDeviceClass;
index->bMaxPower = index->config->bMaxPower;
index->config_length = length - sizeof(*index->dev);
index->iface_cur = -1;
size_t offset = 0;
while (true) {
if (offset + 1 >= length)
break;
uint8_t desc_length = buffer[offset];
uint8_t desc_type = buffer[offset + 1];
if (desc_length <= 2)
break;
if (offset + desc_length > length)
break;
if (desc_type == USB_DT_INTERFACE && index->ifaces_num < USB_MAX_IFACE_NUM) {
struct usb_interface_descriptor* iface = (struct usb_interface_descriptor*)(buffer + offset);
index->ifaces[index->ifaces_num].iface = iface;
index->ifaces[index->ifaces_num].bInterfaceNumber = iface->bInterfaceNumber;
index->ifaces[index->ifaces_num].bAlternateSetting = iface->bAlternateSetting;
index->ifaces[index->ifaces_num].bInterfaceClass = iface->bInterfaceClass;
index->ifaces_num++;
}
if (desc_type == USB_DT_ENDPOINT && index->ifaces_num > 0) {
struct usb_iface_index* iface = &index->ifaces[index->ifaces_num - 1];
if (iface->eps_num < USB_MAX_EP_NUM) {
memcpy(&iface->eps[iface->eps_num].desc, buffer + offset, sizeof(iface->eps[iface->eps_num].desc));
iface->eps_num++;
}
}
offset += desc_length;
}
return true;
}
static struct usb_device_index* add_usb_index(int fd, const char* dev, size_t dev_len)
{
int i = __atomic_fetch_add(&usb_devices_num, 1, __ATOMIC_RELAXED);
if (i >= USB_MAX_FDS)
return NULL;
if (!parse_usb_descriptor(dev, dev_len, &usb_devices[i].index))
return NULL;
__atomic_store_n(&usb_devices[i].fd, fd, __ATOMIC_RELEASE);
return &usb_devices[i].index;
}
struct vusb_connect_string_descriptor {
uint32_t len;
char* str;
} __attribute__((packed));
struct vusb_connect_descriptors {
uint32_t qual_len;
char* qual;
uint32_t bos_len;
char* bos;
uint32_t strs_len;
struct vusb_connect_string_descriptor strs[0];
} __attribute__((packed));
static const char default_string[] = {
8, USB_DT_STRING,
's', 0, 'y', 0, 'z', 0
};
static const char default_lang_id[] = {
4, USB_DT_STRING,
0x09, 0x04
};
static bool lookup_connect_response_in(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl,
struct usb_qualifier_descriptor* qual,
char** response_data, uint32_t* response_length)
{
struct usb_device_index* index = lookup_usb_index(fd);
uint8_t str_idx;
if (!index)
return false;
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_GET_DESCRIPTOR:
switch (ctrl->wValue >> 8) {
case USB_DT_DEVICE:
*response_data = (char*)index->dev;
*response_length = sizeof(*index->dev);
return true;
case USB_DT_CONFIG:
*response_data = (char*)index->config;
*response_length = index->config_length;
return true;
case USB_DT_STRING:
str_idx = (uint8_t)ctrl->wValue;
if (descs && str_idx < descs->strs_len) {
*response_data = descs->strs[str_idx].str;
*response_length = descs->strs[str_idx].len;
return true;
}
if (str_idx == 0) {
*response_data = (char*)&default_lang_id[0];
*response_length = default_lang_id[0];
return true;
}
*response_data = (char*)&default_string[0];
*response_length = default_string[0];
return true;
case USB_DT_BOS:
*response_data = descs->bos;
*response_length = descs->bos_len;
return true;
case USB_DT_DEVICE_QUALIFIER:
if (!descs->qual) {
qual->bLength = sizeof(*qual);
qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
qual->bcdUSB = index->dev->bcdUSB;
qual->bDeviceClass = index->dev->bDeviceClass;
qual->bDeviceSubClass = index->dev->bDeviceSubClass;
qual->bDeviceProtocol = index->dev->bDeviceProtocol;
qual->bMaxPacketSize0 = index->dev->bMaxPacketSize0;
qual->bNumConfigurations = index->dev->bNumConfigurations;
qual->bRESERVED = 0;
*response_data = (char*)qual;
*response_length = sizeof(*qual);
return true;
}
*response_data = descs->qual;
*response_length = descs->qual_len;
return true;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
return false;
}
typedef bool (*lookup_connect_out_response_t)(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done);
static bool lookup_connect_response_out_generic(int fd, const struct vusb_connect_descriptors* descs,
const struct usb_ctrlrequest* ctrl, bool* done)
{
switch (ctrl->bRequestType & USB_TYPE_MASK) {
case USB_TYPE_STANDARD:
switch (ctrl->bRequest) {
case USB_REQ_SET_CONFIGURATION:
*done = true;
return true;
default:
break;
}
break;
}
return false;
}
struct vusb_descriptor {
uint8_t req_type;
uint8_t desc_type;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_descriptors {
uint32_t len;
struct vusb_descriptor* generic;
struct vusb_descriptor* descs[0];
} __attribute__((packed));
struct vusb_response {
uint8_t type;
uint8_t req;
uint32_t len;
char data[0];
} __attribute__((packed));
struct vusb_responses {
uint32_t len;
struct vusb_response* generic;
struct vusb_response* resps[0];
} __attribute__((packed));
static bool lookup_control_response(const struct vusb_descriptors* descs, const struct vusb_responses* resps,
struct usb_ctrlrequest* ctrl, char** response_data, uint32_t* response_length)
{
int descs_num = 0;
int resps_num = 0;
if (descs)
descs_num = (descs->len - offsetof(struct vusb_descriptors, descs)) / sizeof(descs->descs[0]);
if (resps)
resps_num = (resps->len - offsetof(struct vusb_responses, resps)) / sizeof(resps->resps[0]);
uint8_t req = ctrl->bRequest;
uint8_t req_type = ctrl->bRequestType & USB_TYPE_MASK;
uint8_t desc_type = ctrl->wValue >> 8;
if (req == USB_REQ_GET_DESCRIPTOR) {
int i;
for (i = 0; i < descs_num; i++) {
struct vusb_descriptor* desc = descs->descs[i];
if (!desc)
continue;
if (desc->req_type == req_type && desc->desc_type == desc_type) {
*response_length = desc->len;
if (*response_length != 0)
*response_data = &desc->data[0];
else
*response_data = NULL;
return true;
}
}
if (descs && descs->generic) {
*response_data = &descs->generic->data[0];
*response_length = descs->generic->len;
return true;
}
} else {
int i;
for (i = 0; i < resps_num; i++) {
struct vusb_response* resp = resps->resps[i];
if (!resp)
continue;
if (resp->type == req_type && resp->req == req) {
*response_length = resp->len;
if (*response_length != 0)
*response_data = &resp->data[0];
else
*response_data = NULL;
return true;
}
}
if (resps && resps->generic) {
*response_data = &resps->generic->data[0];
*response_length = resps->generic->len;
return true;
}
}
return false;
}
#define UDC_NAME_LENGTH_MAX 128
struct usb_raw_init {
__u8 driver_name[UDC_NAME_LENGTH_MAX];
__u8 device_name[UDC_NAME_LENGTH_MAX];
__u8 speed;
};
enum usb_raw_event_type {
USB_RAW_EVENT_INVALID = 0,
USB_RAW_EVENT_CONNECT = 1,
USB_RAW_EVENT_CONTROL = 2,
};
struct usb_raw_event {
__u32 type;
__u32 length;
__u8 data[0];
};
struct usb_raw_ep_io {
__u16 ep;
__u16 flags;
__u32 length;
__u8 data[0];
};
#define USB_RAW_EPS_NUM_MAX 30
#define USB_RAW_EP_NAME_MAX 16
#define USB_RAW_EP_ADDR_ANY 0xff
struct usb_raw_ep_caps {
__u32 type_control : 1;
__u32 type_iso : 1;
__u32 type_bulk : 1;
__u32 type_int : 1;
__u32 dir_in : 1;
__u32 dir_out : 1;
};
struct usb_raw_ep_limits {
__u16 maxpacket_limit;
__u16 max_streams;
__u32 reserved;
};
struct usb_raw_ep_info {
__u8 name[USB_RAW_EP_NAME_MAX];
__u32 addr;
struct usb_raw_ep_caps caps;
struct usb_raw_ep_limits limits;
};
struct usb_raw_eps_info {
struct usb_raw_ep_info eps[USB_RAW_EPS_NUM_MAX];
};
#define USB_RAW_IOCTL_INIT _IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN _IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH _IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE _IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ _IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE _IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE _IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE _IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ _IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE _IO('U', 9)
#define USB_RAW_IOCTL_VBUS_DRAW _IOW('U', 10, __u32)
#define USB_RAW_IOCTL_EPS_INFO _IOR('U', 11, struct usb_raw_eps_info)
#define USB_RAW_IOCTL_EP0_STALL _IO('U', 12)
#define USB_RAW_IOCTL_EP_SET_HALT _IOW('U', 13, __u32)
#define USB_RAW_IOCTL_EP_CLEAR_HALT _IOW('U', 14, __u32)
#define USB_RAW_IOCTL_EP_SET_WEDGE _IOW('U', 15, __u32)
static int usb_raw_open()
{
return open("/dev/raw-gadget", O_RDWR);
}
static int usb_raw_init(int fd, uint32_t speed, const char* driver, const char* device)
{
struct usb_raw_init arg;
strncpy((char*)&arg.driver_name[0], driver, sizeof(arg.driver_name));
strncpy((char*)&arg.device_name[0], device, sizeof(arg.device_name));
arg.speed = speed;
return ioctl(fd, USB_RAW_IOCTL_INIT, &arg);
}
static int usb_raw_run(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_RUN, 0);
}
static int usb_raw_configure(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0);
}
static int usb_raw_vbus_draw(int fd, uint32_t power)
{
return ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, power);
}
static int usb_raw_ep0_write(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, io);
}
static int usb_raw_ep0_read(int fd, struct usb_raw_ep_io* io)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_READ, io);
}
static int usb_raw_event_fetch(int fd, struct usb_raw_event* event)
{
return ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, event);
}
static int usb_raw_ep_enable(int fd, struct usb_endpoint_descriptor* desc)
{
return ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, desc);
}
static int usb_raw_ep_disable(int fd, int ep)
{
return ioctl(fd, USB_RAW_IOCTL_EP_DISABLE, ep);
}
static int usb_raw_ep0_stall(int fd)
{
return ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0);
}
static int lookup_interface(int fd, uint8_t bInterfaceNumber, uint8_t bAlternateSetting)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
for (int i = 0; i < index->ifaces_num; i++) {
if (index->ifaces[i].bInterfaceNumber == bInterfaceNumber &&
index->ifaces[i].bAlternateSetting == bAlternateSetting)
return i;
}
return -1;
}
#define USB_MAX_PACKET_SIZE 4096
struct usb_raw_control_event {
struct usb_raw_event inner;
struct usb_ctrlrequest ctrl;
char data[USB_MAX_PACKET_SIZE];
};
struct usb_raw_ep_io_data {
struct usb_raw_ep_io inner;
char data[USB_MAX_PACKET_SIZE];
};
static void set_interface(int fd, int n)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return;
if (index->iface_cur >= 0 && index->iface_cur < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[index->iface_cur].eps_num; ep++) {
int rv = usb_raw_ep_disable(fd, index->ifaces[index->iface_cur].eps[ep].handle);
if (rv < 0) {
} else {
}
}
}
if (n >= 0 && n < index->ifaces_num) {
for (int ep = 0; ep < index->ifaces[n].eps_num; ep++) {
int rv = usb_raw_ep_enable(fd, &index->ifaces[n].eps[ep].desc);
if (rv < 0) {
} else {
index->ifaces[n].eps[ep].handle = rv;
}
}
index->iface_cur = n;
}
}
static int configure_device(int fd)
{
struct usb_device_index* index = lookup_usb_index(fd);
if (!index)
return -1;
int rv = usb_raw_vbus_draw(fd, index->bMaxPower);
if (rv < 0) {
return rv;
}
rv = usb_raw_configure(fd);
if (rv < 0) {
return rv;
}
set_interface(fd, 0);
return 0;
}
static volatile long syz_usb_connect_impl(uint64_t speed, uint64_t dev_len, const char* dev,
const struct vusb_connect_descriptors* descs,
lookup_connect_out_response_t lookup_connect_response_out)
{
if (!dev) {
return -1;
}
int fd = usb_raw_open();
if (fd < 0) {
return fd;
}
if (fd >= MAX_FDS) {
close(fd);
return -1;
}
struct usb_device_index* index = add_usb_index(fd, dev, dev_len);
if (!index) {
return -1;
}
char device[32];
sprintf(&device[0], "dummy_udc.%llu", procid);
int rv = usb_raw_init(fd, speed, "dummy_udc", &device[0]);
if (rv < 0) {
return rv;
}
rv = usb_raw_run(fd);
if (rv < 0) {
return rv;
}
bool done = false;
while (!done) {
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = sizeof(event.ctrl);
rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL)
continue;
char* response_data = NULL;
uint32_t response_length = 0;
struct usb_qualifier_descriptor qual;
if (event.ctrl.bRequestType & USB_DIR_IN) {
if (!lookup_connect_response_in(fd, descs, &event.ctrl, &qual, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
continue;
}
} else {
if (!lookup_connect_response_out(fd, descs, &event.ctrl, &done)) {
usb_raw_ep0_stall(fd);
continue;
}
response_data = NULL;
response_length = event.ctrl.wLength;
}
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD &&
event.ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
rv = configure_device(fd);
if (rv < 0) {
return rv;
}
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if (event.ctrl.bRequestType & USB_DIR_IN) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
}
sleep_ms(200);
return fd;
}
static volatile long syz_usb_connect(volatile long a0, volatile long a1, volatile long a2, volatile long a3)
{
uint64_t speed = a0;
uint64_t dev_len = a1;
const char* dev = (const char*)a2;
const struct vusb_connect_descriptors* descs = (const struct vusb_connect_descriptors*)a3;
return syz_usb_connect_impl(speed, dev_len, dev, descs, &lookup_connect_response_out_generic);
}
static volatile long syz_usb_control_io(volatile long a0, volatile long a1, volatile long a2)
{
int fd = a0;
const struct vusb_descriptors* descs = (const struct vusb_descriptors*)a1;
const struct vusb_responses* resps = (const struct vusb_responses*)a2;
struct usb_raw_control_event event;
event.inner.type = 0;
event.inner.length = USB_MAX_PACKET_SIZE;
int rv = usb_raw_event_fetch(fd, (struct usb_raw_event*)&event);
if (rv < 0) {
return rv;
}
if (event.inner.type != USB_RAW_EVENT_CONTROL) {
return -1;
}
char* response_data = NULL;
uint32_t response_length = 0;
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
if (!lookup_control_response(descs, resps, &event.ctrl, &response_data, &response_length)) {
usb_raw_ep0_stall(fd);
return -1;
}
} else {
if ((event.ctrl.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD ||
event.ctrl.bRequest == USB_REQ_SET_INTERFACE) {
int iface_num = event.ctrl.wIndex;
int alt_set = event.ctrl.wValue;
int iface_index = lookup_interface(fd, iface_num, alt_set);
if (iface_index < 0) {
} else {
set_interface(fd, iface_index);
}
}
response_length = event.ctrl.wLength;
}
struct usb_raw_ep_io_data response;
response.inner.ep = 0;
response.inner.flags = 0;
if (response_length > sizeof(response.data))
response_length = 0;
if (event.ctrl.wLength < response_length)
response_length = event.ctrl.wLength;
if ((event.ctrl.bRequestType & USB_DIR_IN) && !event.ctrl.wLength) {
response_length = USB_MAX_PACKET_SIZE;
}
response.inner.length = response_length;
if (response_data)
memcpy(&response.data[0], response_data, response_length);
else
memset(&response.data[0], 0, response_length);
if ((event.ctrl.bRequestType & USB_DIR_IN) && event.ctrl.wLength) {
rv = usb_raw_ep0_write(fd, (struct usb_raw_ep_io*)&response);
} else {
rv = usb_raw_ep0_read(fd, (struct usb_raw_ep_io*)&response);
}
if (rv < 0) {
return rv;
}
sleep_ms(200);
return 0;
}
static long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
{
if (a0 == 0xc || a0 == 0xb) {
char buf[128];
sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1, (uint8_t)a2);
return open(buf, O_RDWR, 0);
} else {
unsigned long nb = a1;
char buf[1024];
char* hash;
strncpy(buf, (char*)a0, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = 0;
while ((hash = strchr(buf, '#'))) {
*hash = '0' + (char)(nb % 10);
nb /= 10;
}
return open(buf, a2 & ~O_CREAT, 0);
}
}
uint64_t r[3] = {0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff};
int main(void)
{
syscall(__NR_mmap, /*addr=*/0x1ffffffff000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200000000000ul, /*len=*/0x1000000ul, /*prot=PROT_WRITE|PROT_READ|PROT_EXEC*/7ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
syscall(__NR_mmap, /*addr=*/0x200001000000ul, /*len=*/0x1000ul, /*prot=*/0ul, /*flags=MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE*/0x32ul, /*fd=*/(intptr_t)-1, /*offset=*/0ul);
const char* reason;
(void)reason;
intptr_t res = 0;
if (write(1, "executing program\n", sizeof("executing program\n") - 1)) {}
// syz_usb_connect arguments: [
// speed: usb_device_speed = 0x0 (8 bytes)
// dev_len: len = 0x2d (8 bytes)
// dev: ptr[inout, array[ANYUNION]] {
// array[ANYUNION] {
// union ANYUNION {
// ANYBLOB: buffer: {12 01 00 00 75 16 b7 10 8c 0d 0e 00 8f 8e 00 18 03 01 09 02 1b 00 01 00 00 00 00 09 04 08 00 01 03 00 00 00 09 05} (length 0x26)
// }
// union ANYUNION {
// ANYBLOB: buffer: {8f cf 44 20 82 9e f1 88} (length 0x8)
// }
// }
// }
// conn_descs: nil
// ]
// returns fd_usb
memcpy((void*)0x200000001040, "\x12\x01\x00\x00\x75\x16\xb7\x10\x8c\x0d\x0e\x00\x8f\x8e\x00\x18\x03\x01\x09\x02\x1b\x00\x01\x00\x00\x00\x00\x09\x04\x08\x00\x01\x03\x00\x00\x00\x09\x05", 38);
memcpy((void*)0x200000001066, "\x8f\xcf\x44\x20\x82\x9e\xf1\x88", 8);
res = -1;
res = syz_usb_connect(/*speed=*/0, /*dev_len=*/0x2d, /*dev=*/0x200000001040, /*conn_descs=*/0);
if (res != -1)
r[0] = res;
// syz_open_dev$tty1 arguments: [
// dev: const = 0xc (8 bytes)
// major: const = 0x4 (8 bytes)
// minor: intptr = 0x1 (8 bytes)
// ]
// returns fd_tty
res = -1;
res = syz_open_dev(/*dev=*/0xc, /*major=*/4, /*minor=*/1);
if (res != -1)
r[1] = res;
// syz_usb_control_io$hid arguments: [
// fd: fd_usb_hid (resource)
// descs: nil
// resps: nil
// ]
syz_usb_control_io(/*fd=*/r[0], /*descs=*/0, /*resps=*/0);
// dup arguments: [
// oldfd: fd (resource)
// ]
// returns fd
res = syscall(__NR_dup, /*oldfd=*/r[1]);
if (res != -1)
r[2] = res;
// write$UHID_INPUT arguments: [
// fd: fd_uhid (resource)
// data: ptr[in, uhid_req[UHID_INPUT, uhid_input_req]] {
// uhid_req[UHID_INPUT, uhid_input_req] {
// type: const = 0xfc (4 bytes)
// data: uhid_input_req {
// data: buffer: {a2 e3 ad 21 ed 0d 1b f9 1b 29 55 09 87 f7 0e 06 d0 38 e7 ff 7f c6 e5 53 9b 0d 3d 0e 8b 08 9b 3f 3b 09 6c 06 08 90 e0 87 8f 0e 1a c6 e7 04 9b 09 6e 95 9b 44 9a 24 0d 5b 67 f3 98 8f 7e f3 19 52 01 00 ff e8 d1 78 70 8c 52 3c 92 1b 1b 5b 31 07 0b 07 5d 0d 36 cd 3b 78 13 0d aa 61 d8 e8 04 00 00 00 58 02 b7 7f 07 22 72 27 b7 ba 67 e0 e7 86 57 a6 f5 c2 a8 74 e6 2a 9c cd c0 d3 1a 0c 9f 31 8c 0d a1 99 3b d1 60 e2 33 df 4a 62 17 9c 6f 30 e0 65 cd 5b 91 cd 0a e1 93 97 37 35 b3 6d 5b 1b 63 dd 1c 00 30 5d 3f 46 63 5e b0 16 d5 b1 dd a9 8e 2d 74 9b e7 bd 1d f1 fb 3b 23 1f dc db 50 75 a9 aa a1 b4 69 c3 09 00 00 00 00 00 00 00 75 27 1b 28 63 29 d1 69 93 42 88 fd 78 9a a3 7d 6e 98 b2 24 fd 44 b6 5b 31 33 4f fc 55 cc 82 cd 3a c3 2e cd b0 8c ed 6f 90 81 b4 dd 0d 8b 38 f3 cd 44 98 be e8 00 49 08 41 bd b1 14 f6 b7 63 83 70 9d 8f 5c 55 43 2a 90 9f da 03 9a ec 54 a1 23 6e 80 f6 a8 ab ad ea 76 62 49 6b dd bb 42 be 6b fb 2f 17 95 9d 1f 41 6e 56 c7 1b 19 31 87 02 62 f5 e8 01 11 92 42 ca 02 6b fc 82 1e 7e 7d af 24 51 13 8e 64 5b b8 0c 61 76 69 31 4e 2f be 70 de 98 ec 76 a9 e4 0d ad 47 f3 6f d9 f7 d0 d4 2a 4b 5f 11 85 cc dc f1 6f f4 62 95 d8 a0 fa 17 71 3c 58 02 63 09 33 a9 a3 4a f6 74 f3 f3 9f e2 34 91 23 7c 08 82 2d ec 11 09 11 e8 93 d0 a8 c4 f6 77 74 7a bc 36 09 34 b8 29 10 ff 85 bf d9 95 08 3b ba 29 87 a6 73 99 ea c4 27 d1 45 d5 46 a4 0b 9f 6f f1 4a c4 88 ec 13 0f 91 85 0a 27 af 95 44 ae 15 a7 e4 54 de a0 59 18 b4 12 43 51 3f 00 00 00 00 00 00 00 0a 36 21 c5 6c ea 8d 20 fa 91 1a 0c 41 db 6e be 8c ac 64 f1 76 79 14 1d 54 b3 4b bc 99 63 ac 4f 4b b3 30 96 03 f1 d4 ab 96 62 03 86 1b 5b 15 a8 41 f2 b5 75 a8 bd 0d 78 24 8e be 4d 9a 80 00 26 95 10 4f 67 4c 24 31 dc a1 41 fa e2 69 ca b7 0e 9a 66 f3 c3 a9 a6 3e 96 39 e1 f5 9c 0e de 26 c6 b5 d7 4b 07 8a 5e 15 c3 16 34 e5 ae 09 8c e9 ee 70 77 1a aa 18 11 9a 86 7e 10 88 33 49 75 e9 f7 34 83 b6 a6 2f a6 78 ca 14 ff d9 f9 db 2a 78 69 d8 58 64 05 65 26 f8 89 af 43 a6 05 60 80 57 22 86 52 24 49 df 46 6c 63 2b 35 70 24 3f 98 9c ce 7c d9 f4 65 e4 1e 61 0c 20 d8 04 21 d6 53 a5 52 00 00 00 82 13 b7 04 c7 fb 08 2f f2 75 90 67 8e f9 f1 90 ba e9 79 09 50 70 41 d8 60 42 0c 56 64 b2 79 21 b1 4d c1 db 88 92 fd 32 d0 ad 7b c9 46 81 35 91 ad 8d ef f4 b0 5f 60 ce a0 da 77 10 ac 00 00 00 00 00 00 80 00 be a3 7c e0 d0 d4 aa 20 2f 92 8f 28 38 1a ab 14 4a 5d 42 9a 04 a6 a2 b8 3c 70 68 ae 94 9e d0 6e 28 8e 81 0b ac 9c 76 60 00 25 e1 9c 90 7f 8e a2 e2 01 00 00 00 82 71 a1 f5 f8 52 8f 22 7e 79 c1 38 9d bd ff fe 49 2f 21 57 9d 2c 15 b8 c7 0c db 1c 33 2d 86 d8 73 41 43 27 50 86 1e c2 bc 34 51 ed ca 19 4b 22 1c fe c4 60 3d 27 6b ba a1 df a6 d4 fb 8a 48 a7 6e af c9 a9 a0 27 0e 4c 10 d6 4c d5 a6 24 27 26 4f 40 77 fe 76 3c 43 47 08 33 ac 96 c4 5f 35 7c bb ab a8 f1 b1 fd cc 7c bb 61 a7 cd b9 74 4e d7 f9 12 9a ed e2 be 21 cc fd c4 e9 13 4f 86 84 b3 a4 f3 54 da 9a 79 5e 96 33 4e 20 7d ff 70 f1 98 80 37 b2 ed 3a af 57 5c 0b 88 d8 f1 46 68 40 78 41 6d 59 fd ee 53 25 92 89 74 d1 2d ad 99 da c4 4c 3f 00 08 04 70 96 a4 40 02 be bc 24 20 ae d9 2f a9 b6 57 8b 47 79 41 5d 4a c0 1b 75 d5 49 5c 11 80 45 65 1c f4 1c 2f c4 8b 77 8e fa 5e a5 67 77 47 43 0a f4 16 2b 98 7b 80 c3 e0 01 cd 34 e5 c9 2f 76 cc 4c 24 ee b8 bc 4e 9a c2 ae d9 e5 38 03 ed 0c a4 ae 3a 97 37 d2 14 06 00 05 ea 6f 17 83 e2 87 b3 be e9 6e 3a 72 6e af e2 fd fa a7 8d 1f 48 c1 3b 64 df 07 84 77 54 b8 40 0d aa a6 9b f5 c8 f4 35 0a ea e9 ca 12 07 e7 82 83 cd 0b 20 ce b3 60 c7 e6 58 82 81 63 e2 d2 5c 4a a3 48 56 1f 92 7e 88 f6 3a a7 0e 73 a5 e6 9b 3d f3 49 59 03 f0 65 72 e1 e0 07 fa 55 a2 99 9f 59 6d 06 73 12 f5 77 9e 8d bf dc f3 42 71 38 f3 d4 44 d2 63 9a 10 47 7f 9b ec 4b 0b bb 6e 3c 04 be 68 98 1f 39 22 03 dd 0e e3 ef 47 8e 16 da cf c5 e3 e0 3c f7 ab 8e 39 02 f1 b0 ff 03 4e f6 55 b2 53 ca 50 93 83 81 5b 1b 6f c6 52 2d 4e 4f dc 11 a4 8c f4 2d 48 60 46 75 fd e2 b9 4c f0 05 00 a2 69 08 91 ab f8 ab 9c 01 50 73 01 4d 9e 08 d4 33 8b 87 80 bd ec d4 36 cf 05 41 35 9b af ff a4 52 37 f1 04 b9 62 10 40 3b 2d e9 ef ed 49 6f 42 35 5b c7 87 2c 82 74 67 cf a5 c4 e7 27 30 d5 6b d0 68 ed 21 1c f8 47 53 5e de cb 7b 37 3f 78 b0 95 b6 84 41 a3 4c b5 16 82 a8 ae 4d 24 ad 04 65 f3 92 7f 88 9b 81 30 76 03 8e 79 a7 96 2f b3 85 a8 1a a1 02 0f 06 c4 c2 ba 1d d5 ca c7 c1 88 76 da 86 5d 25 87 34 dd 73 58 3d f2 92 89 24 48 03 9e f7 99 cf 06 30 be cd cc e0 45 79 b5 56 1d c8 25 ab 82 98 27 94 5e 02 0c 1f 67 ee 61 5f eb 62 43 37 8e 06 10 06 0f 02 cc a4 e9 1b 2f 00 1e db 3d 78 fb 4b 55 66 8d da 93 ae c9 2a 5d e2 03 71 7a a4 9c 2d 28 4a cf ab e2 62 fc cf cb b2 b7 5a 21 83 c4 6e b6 5c a8 10 4e 1b 4d a7 fb b7 7a b2 fc 04 3a ea d8 7c 32 ab 87 5e e7 c2 e7 b7 01 9c 98 2c d3 b4 3e ae b1 a5 fb 13 5c 0c 7d ce e8 fe 65 16 a3 28 03 2f 88 c0 42 89 18 24 65 9e 9e 94 26 5c 80 3b 35 ee 5f 83 a2 b2 10 52 01 06 89 af 9c 25 1f e5 29 4b 3d 18 02 d5 67 6d 95 f1 60 ec 97 b1 ad 94 87 2c b2 04 46 42 c3 7b 4a 6c c6 c0 4e ff c1 67 2d b7 e4 b6 8d 78 7d 9a 7a 50 8a e5 4b 3c d7 36 9d de 50 e8 c7 7d 95 a3 d3 61 c0 40 ba bb 17 16 07 ca ac 2a 35 59 ad 4f 75 46 5f 49 c0 d0 ae 37 16 db 6e 00 cb 11 db 4a 5f ad e2 a5 7c 10 23 8e 20 4a 67 73 7c 3b 42 aa e5 01 b2 0f 76 94 a0 0f 16 e2 d0 17 40 35 a2 c2 26 56 dc 29 88 0a ce bd be 8d db d7 5c 2f 99 8d 8a c2 df ad 2b a3 a5 04 76 7b 6b 45 a4 59 57 f2 4d 75 8e d0 24 b3 84 9c 11 d4 12 a2 a0 3b 40 47 49 70 22 d9 c3 0e 23 ef 4d f5 c8 96 44 f4 8b b5 36 f7 94 5b 59 d7 bc dd ff 75 44 13 d1 35 27 3e a8 e7 5f 22 f2 16 c6 b9 99 0a e7 18 06 f2 c0 0b 40 25 c4 8b 75 c0 f7 3c db 9a 7b 8f a3 67 b5 00 28 06 7e 7f 16 f4 dd 56 9d 46 2f 4f 19 ea cd b3 ed 70 ee eb b4 48 3f 8f d7 77 d4 43 e8 b4 04 27 db 6f e2 90 68 c0 ca 3d 24 14 44 2e 8f 3a 15 47 04 b0 e5 1b c6 64 a1 37 b2 6b e7 19 f4 f7 c9 a5 67 8a 67 4d fc 95 df 80 b9 ce 37 5d d6 49 c8 c7 04 e5 09 bd 88 c8 e6 3d 8c 7d d6 70 71 11 5c 89 82 ba 46 af 4d 6a dc c9 f6 8a 75 b9 39 7b 03 51 53 fa e4 63 66 e7 20 5d d8 d6 f3 75 25 c1 a0 e9 46 10 dd 94 32 3f 6c 15 d0 85 19 71 49 bf d6 65 55 48 cf d9 c5 2c 97 11 93 7f 79 ab b1 a1 24 f1 21 04 65 48 3c d3 b2 d7 83 78 cf b8 5e d8 2e 7d a0 f6 eb 6d 27 9f 2a e4 55 92 5d 0f 6f 1b a5 71 eb a2 81 f2 a6 54 fb 39 dd ff 3b 48 44 39 ff 15 8e 7c 54 19 e0 37 f3 e3 ad 03 8f 22 11 f1 03 31 95 56 3c 7f 93 cd 54 b9 09 4f 22 6e 78 32 71 e1 e5 a2 a2 c1 07 12 ea b6 25 d6 49 31 cd 4f fe 67 38 d9 7b 9b 5e f8 28 ee 9f b0 59 fc 01 af 0e 79 c1 e1 4b 1d 25 98 8c 69 a3 99 56 7c 1d 93 76 8f 79 71 d3 14 88 b8 65 8a 20 87 8b 7c 1d d7 ba 02 fc 42 93 9d de 3d 4a 33 39 a6 5d 50 7d c5 9c 51 09 7b 40 51 77 05 da 56 e9 eb f0 af a5 32 82 bf 86 db b5 8c 54 80 69 ff 6e b9 5a ad e7 cc 66 d7 bb ef 72 47 79 ca 1f 73 1b 33 46 ff 17 70 50 37 3d 79 ff 7b 3e 7f 9b c0 c1 b4 b2 66 a8 87 8b 90 ba aa 03 9d 3e 3b 63 97 9a c3 df 6e 6f 48 59 af d5 02 38 c7 54 7a 39 b6 08 10 93 80 44 ae 18 5d 2b a3 e0 0a 4e 73 67 68 64 ae 09 0d 81 ea ee 5e e6 cf 1d 0a b3 78 dd 4d d8 91 e9 37 c2 ea 54 10 e0 51 30 05 00 00 00 00 00 00 00 39 11 fa b9 64 c2 71 55 00 27 69 7b 52 16 06 87 46 16 02 f8 8d f1 65 d8 84 b3 6e c2 b6 c2 5a 2f 33 c7 15 68 7e 9d 4a fb 96 d6 86 1a ca 47 da 73 d6 f3 14 43 45 f4 88 43 dd 01 4e 5c 5a d8 fe 99 57 54 bd 9c f3 2f ce 1e 31 91 9c 4b 20 82 fb 0a 30 b9 de ae 84 be d4 b2 80 45 63 40 73 c9 c5 8c 89 d9 e9 9c 81 76 91 77 c6 d5 94 f8 8a 4f ac fd 4c 73 5a 20 30 7c 73 7a fa 2d 60 39 94 73 29 6b 83 1d bd 93 3d 93 99 4b a3 06 42 79 b1 0e a0 c5 83 3f 41 f1 57 ea 23 02 99 3d be 43 3b 1a a3 a3 76 6d 54 39 02 04 84 f4 11 3c 4c 85 94 65 c3 b4 15 c3 43 f7 f1 40 f3 19 53 9d 5b f3 72 aa ae a1 cc 43 a6 c5 cb e5 97 58 bf ee 29 16 58 0d ac 4b 00 8e 59 5f 43 74 91 d8 7a be d0 2c ef cd 9d b5 3d 94 d0 2d ae e6 79 18 e5 d6 78 74 63 18 3b 4b 87 c1 05 00 00 00 2f 78 09 95 9b c0 48 85 06 13 d1 7c a5 10 55 f2 f4 16 a4 4f e1 80 d2 d5 0c 31 2c ca 7c b1 4a 2b dc 33 1f 57 a9 81 71 39 a2 06 fc 76 95 72 27 ff ff 2d e2 0a 4b 8e 37 37 fb b4 29 13 77 7c 06 37 6f 79 9e ba 36 7e 21 f9 4c a5 98 70 5f 5d cb 76 7d 6f 09 00 d6 b0 f6 09 5e 53 c4 c4 23 4d 0c 1f be 43 4f 6a b8 f4 3c 00 13 ee 93 b8 39 46 ee 77 59 e8 9d 7b dd 1a 32 d7 b3 11 71 1b 75 7f e4 3c 06 d2 1a 35 81 0d 8f e9 8b 27 fa ea 8a a1 2b c8 71 6e ef c5 c9 7c 45 ac 33 ee ec 96 4c 52 14 bc 3a 93 59 bd ea 1c cc ab 94 f1 5e 36 31 9c b3 4e bc ac ed b8 2c 2e d3 de 5a 8a 8f 00 11 e8 f7 4e 82 d7 f9 60 93 53 0e 76 69 28 39 d7 96 19 39 ad fd ee ea ff 19 d1 1e fc af b6 d5 46 fe f2 71 e8 9d 6c c2 38 9e 81 ff 58 ce fc ce 3f bf 46 25 a7 e7 de 40 e4 2e 07 b3 44 49 e1 5e 06 5c c7 34 00 02 00 00 00 00 00 00 f2 88 a4 51 0d e0 3d ab 19 d2 62 85 ed a8 91 56 d5 0d d3 85 a6 03 33 ba 5b bf 5d 77 cd 70 07 ad 15 19 ad 54 70 de 3d d6 d6 08 0c af cc f8 a9 74 06 bb 6b 68 a1 f0 c4 54 98 20 a7 3c 88 0f 47 5f 73 2a e0 03 98 e8 bd 1f 41 08 b7 80 7f b3 3b 72 68 5e c3 7a 2d 3f 76 64 13 a6 04 59 51 62 46 e5 a1 d9 98 a2 01 7a ef 09 48 a6 8c f2 55 31 5a b8 0d d3 49 e8 91 ae f5 95 dc 4d 47 0e 8a c3 2a 30 8e 15 fc 37 d0 6a ea c2 89 c0 52 3f 48 3e 1f f7 40 8c 60 87 f1 ab 65 2f 2e f9 1d 4f 2b 01 98 7b 0f 46 da 03 4e 5c 3f 74 5a 7e e8 10 1a 39 34 c5 4e 24 b4 8e c0 27 5e 2d 06 87 dc 74 6b 08 27 cb f6 52 f4 06 c6 b9 5f 27 22 e5 8c 05 f7 52 ce 21 26 59 6e 1c d7 65 5b 90 48 01 78 4c 41 6b 22 f7 3d 32 46 78 e2 72 4f 43 f1 fe 68 7c 7e 8a 60 c2 8b 82 b6 52 83 41 b6 48 cd d5 6f ed 7c dc bb 15 75 91 2d 5e cd 36 de a3 bc a0 b7 42 7d 83 92 c6 28 94 55 e8 f8 d2 ab 22 42 72 92 51 ae 03 3a 9e 02 21 0e 62 df 05 46 a7 4b 33 3a 1c 48 f9 5f d5 4a cb 57 41 25 9e 8c 54 88 ef ee e3 27 41 5c c1 94 51 43 2c 6f 14 c2 76 93 10 2a 3c d8 48 57 cd 65 86 fc 5c a9 a9 3e b0 14 5f ac 06 62 ff 86 10 7f 99 8a 8e f7 df 8a a1 40 46 c5 5b 03 d3 d4 7f 88 a8 d6 0f 77 74 a2 ee 08 75 88 97 fb 41 1a 94 b3 c2 fc 5d 5f 0d b4 2c 04 56 ec 01 5f 08 e5 24 7d 33 ae 2d 35 60 3f f8 45 4c 16 f8 34 28 56 93 51 25 10 2b b7 84 ed 71 48 b6 ce 43 1b 63 ee 35 6b 0c 78 5f 2f 47 b9 0e 29 38 9f 22 fc 5b 59 a7 0e fa ea 2b d4 01 95 af 44 86 22 0d 70 2e 30 bf c4 3c 10 ec 23 ea 62 83 99 4a 7d de 4d cb 61 fe a6 b6 51 fb 1d 62 45 8d 07 41 a1 28 30 05 2f cc 46 0d b0 43 af e5 25 62 9b 40 d7 ce e4 58 e4 cb 5e 93 0e d6 24 80 6c 43 a0 06 e3 93 36 d0 7c 2b 80 81 c1 28 ad 27 06 f4 82 61 f7 89 74 84 c2 97 a1 a6 61 3b c1 8f 5a 38 d4 42 76 8a f3 80 41 ef e0 3d 15 2e f9 5f f5 69 e7 6d b2 39 1f 45 09 d7 f3 39 d9 2f db 4a 89 36 49 49 da 39 80 00 00 00 00 00 00 00 d8 0a 4f e6 54 57 83 76 e5 99 af f3 56 5b 1d 53 1f 30 91 2b 99 45 03 0b 81 ea 99 35 fd 46 ed b4 4a 78 f6 15 25 54 90 a4 b6 21 50 1f 2a 9e 4d 24 62 4c 4d ac 92 74 11 8c 67 58 4f 5d 37 47 55 53 4d 7f 68 f6 79 c4 ff 51 6a 9c 86 1a 0e 7e 65 86 8f cb 2b f1 cb 9a ea 4e 05 df 72 27 9f db 0d 2b 9e 93 5c 5a f3 cf 47 4b 06 79 df c2 48 c1 f5 ae a4 b8 b3 2c 5d 29 5e 57 07 9d 0f e6 62 a4 6b 7f 71 cd 47 74 4d b8 6c 50 b7 04 c9 71 d9 02 95 c7 b2 c7 43 9a 2d 78 cc fa 79 b5 fc 2b ff 6b bf 84 02 62 bf 89 39 4b 3e 06 91 95 12 64 d2 70 0c 83 8f a2 c7 b3 42 52 60 f5 95 54 e5 02 dc ea 39 cb 31 3b 00 00 00 00 00 00 4c a7 c1 2f 45 85 8d 62 84 ca 62 70 d6 b2 f0 e5 8f de d8 a7 b4 a3 02 a9 7b c6 41 df 07 72 0b a2 b2 6b bf cc 80 7c a0 ab b1 b4 43 22 26 9c 21 c5 ec 68 cb 06 8e a8 80 67 d9 05 ea 91 7b b0 3e ef da eb de ab f2 d0 dc e8 09 97 c9 15 c8 94 9d e9 92 58 7c 2c b5 fe 36 d7 d3 e5 db 21 b0 94 b8 b7 79 40 b5 f0 77 22 e4 7a 08 d3 67 e5 f8 4c 96 ec 66 4b 72 93 4b 99 b3 10 9a f6 5d 77 e8 6a bd 68 59 cd df 4b ba e1 f0 93 04 62 df 15 fd db c4 85 62 ea 35 11 a8 06 5e f0 28 cf 12 f1 4d cf 6e be cd 8d 98 48 36 17 4f af 1a a6 09 e5 f1 ee 11 62 df a1 3b dc 1f a7 cf aa db a8 5c 72 e9 75 8f 03 a7 05 00 be 53 f8 d2 a1 df b1 c6 8c c1 64 b0 a0 78 0d 97 1a 96 ea 2c 4d 4c a0 39 8c 22 35 98 0a 93 07 b3 d5 bd 3b 01 fa ff d0 a5 db ed 28 81 a9 70 0a f5 61 ac 8c 6b 00 00 00 00 00 00 00 f9 6f 06 81 7f b9 03 72 9a 7d b6 ff 95 76 97 c9 ed e7 88 5d 94 ff b0 96 9b e0 da f6 0a f9 31 09 eb 1d ee 72 e4 36 3f 51 af 62 af 6f b2 a6 df 3b ec 89 82 2a 7a 0b 67 80 58 fa 3f ef 86 fa ec 21 6e b6 99 21 62 f8 dc bf 71 9c 14 8c d2 f9 c5 5f 49 01 20 3a 9a 8a 2c 3e 90 f3 94 3d bc 10 36 0a 1a 49 70 0d 1d fb f6 6d 69 f6 fb af 50 6c 8b cc e8 bb 0d 87 2a 02 23 89 26 40 7a 4e dd d5 d0 fc 5a 75 2f 90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00} (length 0x1000)
// size: len = 0x8f5 (2 bytes)
// }
// }
// }
// len: len = 0x1006 (8 bytes)
// ]
*(uint32_t*)0x200000000000 = 0xfc;
memcpy((void*)0x200000000004, "\xa2\xe3\xad\x21\xed\x0d\x1b\xf9\x1b\x29\x55\x09\x87\xf7\x0e\x06\xd0\x38\xe7\xff\x7f\xc6\xe5\x53\x9b\x0d\x3d\x0e\x8b\x08\x9b\x3f\x3b\x09\x6c\x06\x08\x90\xe0\x87\x8f\x0e\x1a\xc6\xe7\x04\x9b\x09\x6e\x95\x9b\x44\x9a\x24\x0d\x5b\x67\xf3\x98\x8f\x7e\xf3\x19\x52\x01\x00\xff\xe8\xd1\x78\x70\x8c\x52\x3c\x92\x1b\x1b\x5b\x31\x07\x0b\x07\x5d\x0d\x36\xcd\x3b\x78\x13\x0d\xaa\x61\xd8\xe8\x04\x00\x00\x00\x58\x02\xb7\x7f\x07\x22\x72\x27\xb7\xba\x67\xe0\xe7\x86\x57\xa6\xf5\xc2\xa8\x74\xe6\x2a\x9c\xcd\xc0\xd3\x1a\x0c\x9f\x31\x8c\x0d\xa1\x99\x3b\xd1\x60\xe2\x33\xdf\x4a\x62\x17\x9c\x6f\x30\xe0\x65\xcd\x5b\x91\xcd\x0a\xe1\x93\x97\x37\x35\xb3\x6d\x5b\x1b\x63\xdd\x1c\x00\x30\x5d\x3f\x46\x63\x5e\xb0\x16\xd5\xb1\xdd\xa9\x8e\x2d\x74\x9b\xe7\xbd\x1d\xf1\xfb\x3b\x23\x1f\xdc\xdb\x50\x75\xa9\xaa\xa1\xb4\x69\xc3\x09\x00\x00\x00\x00\x00\x00\x00\x75\x27\x1b\x28\x63\x29\xd1\x69\x93\x42\x88\xfd\x78\x9a\xa3\x7d\x6e\x98\xb2\x24\xfd\x44\xb6\x5b\x31\x33\x4f\xfc\x55\xcc\x82\xcd\x3a\xc3\x2e\xcd\xb0\x8c\xed\x6f\x90\x81\xb4\xdd\x0d\x8b\x38\xf3\xcd\x44\x98\xbe\xe8\x00\x49\x08\x41\xbd\xb1\x14\xf6\xb7\x63\x83\x70\x9d\x8f\x5c\x55\x43\x2a\x90\x9f\xda\x03\x9a\xec\x54\xa1\x23\x6e\x80\xf6\xa8\xab\xad\xea\x76\x62\x49\x6b\xdd\xbb\x42\xbe\x6b\xfb\x2f\x17\x95\x9d\x1f\x41\x6e\x56\xc7\x1b\x19\x31\x87\x02\x62\xf5\xe8\x01\x11\x92\x42\xca\x02\x6b\xfc\x82\x1e\x7e\x7d\xaf\x24\x51\x13\x8e\x64\x5b\xb8\x0c\x61\x76\x69\x31\x4e\x2f\xbe\x70\xde\x98\xec\x76\xa9\xe4\x0d\xad\x47\xf3\x6f\xd9\xf7\xd0\xd4\x2a\x4b\x5f\x11\x85\xcc\xdc\xf1\x6f\xf4\x62\x95\xd8\xa0\xfa\x17\x71\x3c\x58\x02\x63\x09\x33\xa9\xa3\x4a\xf6\x74\xf3\xf3\x9f\xe2\x34\x91\x23\x7c\x08\x82\x2d\xec\x11\x09\x11\xe8\x93\xd0\xa8\xc4\xf6\x77\x74\x7a\xbc\x36\x09\x34\xb8\x29\x10\xff\x85\xbf\xd9\x95\x08\x3b\xba\x29\x87\xa6\x73\x99\xea\xc4\x27\xd1\x45\xd5\x46\xa4\x0b\x9f\x6f\xf1\x4a\xc4\x88\xec\x13\x0f\x91\x85\x0a\x27\xaf\x95\x44\xae\x15\xa7\xe4\x54\xde\xa0\x59\x18\xb4\x12\x43\x51\x3f\x00\x00\x00\x00\x00\x00\x00\x0a\x36\x21\xc5\x6c\xea\x8d\x20\xfa\x91\x1a\x0c\x41\xdb\x6e\xbe\x8c\xac\x64\xf1\x76\x79\x14\x1d\x54\xb3\x4b\xbc\x99\x63\xac\x4f\x4b\xb3\x30\x96\x03\xf1\xd4\xab\x96\x62\x03\x86\x1b\x5b\x15\xa8\x41\xf2\xb5\x75\xa8\xbd\x0d\x78\x24\x8e\xbe\x4d\x9a\x80\x00\x26\x95\x10\x4f\x67\x4c\x24\x31\xdc\xa1\x41\xfa\xe2\x69\xca\xb7\x0e\x9a\x66\xf3\xc3\xa9\xa6\x3e\x96\x39\xe1\xf5\x9c\x0e\xde\x26\xc6\xb5\xd7\x4b\x07\x8a\x5e\x15\xc3\x16\x34\xe5\xae\x09\x8c\xe9\xee\x70\x77\x1a\xaa\x18\x11\x9a\x86\x7e\x10\x88\x33\x49\x75\xe9\xf7\x34\x83\xb6\xa6\x2f\xa6\x78\xca\x14\xff\xd9\xf9\xdb\x2a\x78\x69\xd8\x58\x64\x05\x65\x26\xf8\x89\xaf\x43\xa6\x05\x60\x80\x57\x22\x86\x52\x24\x49\xdf\x46\x6c\x63\x2b\x35\x70\x24\x3f\x98\x9c\xce\x7c\xd9\xf4\x65\xe4\x1e\x61\x0c\x20\xd8\x04\x21\xd6\x53\xa5\x52\x00\x00\x00\x82\x13\xb7\x04\xc7\xfb\x08\x2f\xf2\x75\x90\x67\x8e\xf9\xf1\x90\xba\xe9\x79\x09\x50\x70\x41\xd8\x60\x42\x0c\x56\x64\xb2\x79\x21\xb1\x4d\xc1\xdb\x88\x92\xfd\x32\xd0\xad\x7b\xc9\x46\x81\x35\x91\xad\x8d\xef\xf4\xb0\x5f\x60\xce\xa0\xda\x77\x10\xac\x00\x00\x00\x00\x00\x00\x80\x00\xbe\xa3\x7c\xe0\xd0\xd4\xaa\x20\x2f\x92\x8f\x28\x38\x1a\xab\x14\x4a\x5d\x42\x9a\x04\xa6\xa2\xb8\x3c\x70\x68\xae\x94\x9e\xd0\x6e\x28\x8e\x81\x0b\xac\x9c\x76\x60\x00\x25\xe1\x9c\x90\x7f\x8e\xa2\xe2\x01\x00\x00\x00\x82\x71\xa1\xf5\xf8\x52\x8f\x22\x7e\x79\xc1\x38\x9d\xbd\xff\xfe\x49\x2f\x21\x57\x9d\x2c\x15\xb8\xc7\x0c\xdb\x1c\x33\x2d\x86\xd8\x73\x41\x43\x27\x50\x86\x1e\xc2\xbc\x34\x51\xed\xca\x19\x4b\x22\x1c\xfe\xc4\x60\x3d\x27\x6b\xba\xa1\xdf\xa6\xd4\xfb\x8a\x48\xa7\x6e\xaf\xc9\xa9\xa0\x27\x0e\x4c\x10\xd6\x4c\xd5\xa6\x24\x27\x26\x4f\x40\x77\xfe\x76\x3c\x43\x47\x08\x33\xac\x96\xc4\x5f\x35\x7c\xbb\xab\xa8\xf1\xb1\xfd\xcc\x7c\xbb\x61\xa7\xcd\xb9\x74\x4e\xd7\xf9\x12\x9a\xed\xe2\xbe\x21\xcc\xfd\xc4\xe9\x13\x4f\x86\x84\xb3\xa4\xf3\x54\xda\x9a\x79\x5e\x96\x33\x4e\x20\x7d\xff\x70\xf1\x98\x80\x37\xb2\xed\x3a\xaf\x57\x5c\x0b\x88\xd8\xf1\x46\x68\x40\x78\x41\x6d\x59\xfd\xee\x53\x25\x92\x89\x74\xd1\x2d\xad\x99\xda\xc4\x4c\x3f\x00\x08\x04\x70\x96\xa4\x40\x02\xbe\xbc\x24\x20\xae\xd9\x2f\xa9\xb6\x57\x8b\x47\x79\x41\x5d\x4a\xc0\x1b\x75\xd5\x49\x5c\x11\x80\x45\x65\x1c\xf4\x1c\x2f\xc4\x8b\x77\x8e\xfa\x5e\xa5\x67\x77\x47\x43\x0a\xf4\x16\x2b\x98\x7b\x80\xc3\xe0\x01\xcd\x34\xe5\xc9\x2f\x76\xcc\x4c\x24\xee\xb8\xbc\x4e\x9a\xc2\xae\xd9\xe5\x38\x03\xed\x0c\xa4\xae\x3a\x97\x37\xd2\x14\x06\x00\x05\xea\x6f\x17\x83\xe2\x87\xb3\xbe\xe9\x6e\x3a\x72\x6e\xaf\xe2\xfd\xfa\xa7\x8d\x1f\x48\xc1\x3b\x64\xdf\x07\x84\x77\x54\xb8\x40\x0d\xaa\xa6\x9b\xf5\xc8\xf4\x35\x0a\xea\xe9\xca\x12\x07\xe7\x82\x83\xcd\x0b\x20\xce\xb3\x60\xc7\xe6\x58\x82\x81\x63\xe2\xd2\x5c\x4a\xa3\x48\x56\x1f\x92\x7e\x88\xf6\x3a\xa7\x0e\x73\xa5\xe6\x9b\x3d\xf3\x49\x59\x03\xf0\x65\x72\xe1\xe0\x07\xfa\x55\xa2\x99\x9f\x59\x6d\x06\x73\x12\xf5\x77\x9e\x8d\xbf\xdc\xf3\x42\x71\x38\xf3\xd4\x44\xd2\x63\x9a\x10\x47\x7f\x9b\xec\x4b\x0b\xbb\x6e\x3c\x04\xbe\x68\x98\x1f\x39\x22\x03\xdd\x0e\xe3\xef\x47\x8e\x16\xda\xcf\xc5\xe3\xe0\x3c\xf7\xab\x8e\x39\x02\xf1\xb0\xff\x03\x4e\xf6\x55\xb2\x53\xca\x50\x93\x83\x81\x5b\x1b\x6f\xc6\x52\x2d\x4e\x4f\xdc\x11\xa4\x8c\xf4\x2d\x48\x60\x46\x75\xfd\xe2\xb9\x4c\xf0\x05\x00\xa2\x69\x08\x91\xab\xf8\xab\x9c\x01\x50\x73\x01\x4d\x9e\x08\xd4\x33\x8b\x87\x80\xbd\xec\xd4\x36\xcf\x05\x41\x35\x9b\xaf\xff\xa4\x52\x37\xf1\x04\xb9\x62\x10\x40\x3b\x2d\xe9\xef\xed\x49\x6f\x42\x35\x5b\xc7\x87\x2c\x82\x74\x67\xcf\xa5\xc4\xe7\x27\x30\xd5\x6b\xd0\x68\xed\x21\x1c\xf8\x47\x53\x5e\xde\xcb\x7b\x37\x3f\x78\xb0\x95\xb6\x84\x41\xa3\x4c\xb5\x16\x82\xa8\xae\x4d\x24\xad\x04\x65\xf3\x92\x7f\x88\x9b\x81\x30\x76\x03\x8e\x79\xa7\x96\x2f\xb3\x85\xa8\x1a\xa1\x02\x0f\x06\xc4\xc2\xba\x1d\xd5\xca\xc7\xc1\x88\x76\xda\x86\x5d\x25\x87\x34\xdd\x73\x58\x3d\xf2\x92\x89\x24\x48\x03\x9e\xf7\x99\xcf\x06\x30\xbe\xcd\xcc\xe0\x45\x79\xb5\x56\x1d\xc8\x25\xab\x82\x98\x27\x94\x5e\x02\x0c\x1f\x67\xee\x61\x5f\xeb\x62\x43\x37\x8e\x06\x10\x06\x0f\x02\xcc\xa4\xe9\x1b\x2f\x00\x1e\xdb\x3d\x78\xfb\x4b\x55\x66\x8d\xda\x93\xae\xc9\x2a\x5d\xe2\x03\x71\x7a\xa4\x9c\x2d\x28\x4a\xcf\xab\xe2\x62\xfc\xcf\xcb\xb2\xb7\x5a\x21\x83\xc4\x6e\xb6\x5c\xa8\x10\x4e\x1b\x4d\xa7\xfb\xb7\x7a\xb2\xfc\x04\x3a\xea\xd8\x7c\x32\xab\x87\x5e\xe7\xc2\xe7\xb7\x01\x9c\x98\x2c\xd3\xb4\x3e\xae\xb1\xa5\xfb\x13\x5c\x0c\x7d\xce\xe8\xfe\x65\x16\xa3\x28\x03\x2f\x88\xc0\x42\x89\x18\x24\x65\x9e\x9e\x94\x26\x5c\x80\x3b\x35\xee\x5f\x83\xa2\xb2\x10\x52\x01\x06\x89\xaf\x9c\x25\x1f\xe5\x29\x4b\x3d\x18\x02\xd5\x67\x6d\x95\xf1\x60\xec\x97\xb1\xad\x94\x87\x2c\xb2\x04\x46\x42\xc3\x7b\x4a\x6c\xc6\xc0\x4e\xff\xc1\x67\x2d\xb7\xe4\xb6\x8d\x78\x7d\x9a\x7a\x50\x8a\xe5\x4b\x3c\xd7\x36\x9d\xde\x50\xe8\xc7\x7d\x95\xa3\xd3\x61\xc0\x40\xba\xbb\x17\x16\x07\xca\xac\x2a\x35\x59\xad\x4f\x75\x46\x5f\x49\xc0\xd0\xae\x37\x16\xdb\x6e\x00\xcb\x11\xdb\x4a\x5f\xad\xe2\xa5\x7c\x10\x23\x8e\x20\x4a\x67\x73\x7c\x3b\x42\xaa\xe5\x01\xb2\x0f\x76\x94\xa0\x0f\x16\xe2\xd0\x17\x40\x35\xa2\xc2\x26\x56\xdc\x29\x88\x0a\xce\xbd\xbe\x8d\xdb\xd7\x5c\x2f\x99\x8d\x8a\xc2\xdf\xad\x2b\xa3\xa5\x04\x76\x7b\x6b\x45\xa4\x59\x57\xf2\x4d\x75\x8e\xd0\x24\xb3\x84\x9c\x11\xd4\x12\xa2\xa0\x3b\x40\x47\x49\x70\x22\xd9\xc3\x0e\x23\xef\x4d\xf5\xc8\x96\x44\xf4\x8b\xb5\x36\xf7\x94\x5b\x59\xd7\xbc\xdd\xff\x75\x44\x13\xd1\x35\x27\x3e\xa8\xe7\x5f\x22\xf2\x16\xc6\xb9\x99\x0a\xe7\x18\x06\xf2\xc0\x0b\x40\x25\xc4\x8b\x75\xc0\xf7\x3c\xdb\x9a\x7b\x8f\xa3\x67\xb5\x00\x28\x06\x7e\x7f\x16\xf4\xdd\x56\x9d\x46\x2f\x4f\x19\xea\xcd\xb3\xed\x70\xee\xeb\xb4\x48\x3f\x8f\xd7\x77\xd4\x43\xe8\xb4\x04\x27\xdb\x6f\xe2\x90\x68\xc0\xca\x3d\x24\x14\x44\x2e\x8f\x3a\x15\x47\x04\xb0\xe5\x1b\xc6\x64\xa1\x37\xb2\x6b\xe7\x19\xf4\xf7\xc9\xa5\x67\x8a\x67\x4d\xfc\x95\xdf\x80\xb9\xce\x37\x5d\xd6\x49\xc8\xc7\x04\xe5\x09\xbd\x88\xc8\xe6\x3d\x8c\x7d\xd6\x70\x71\x11\x5c\x89\x82\xba\x46\xaf\x4d\x6a\xdc\xc9\xf6\x8a\x75\xb9\x39\x7b\x03\x51\x53\xfa\xe4\x63\x66\xe7\x20\x5d\xd8\xd6\xf3\x75\x25\xc1\xa0\xe9\x46\x10\xdd\x94\x32\x3f\x6c\x15\xd0\x85\x19\x71\x49\xbf\xd6\x65\x55\x48\xcf\xd9\xc5\x2c\x97\x11\x93\x7f\x79\xab\xb1\xa1\x24\xf1\x21\x04\x65\x48\x3c\xd3\xb2\xd7\x83\x78\xcf\xb8\x5e\xd8\x2e\x7d\xa0\xf6\xeb\x6d\x27\x9f\x2a\xe4\x55\x92\x5d\x0f\x6f\x1b\xa5\x71\xeb\xa2\x81\xf2\xa6\x54\xfb\x39\xdd\xff\x3b\x48\x44\x39\xff\x15\x8e\x7c\x54\x19\xe0\x37\xf3\xe3\xad\x03\x8f\x22\x11\xf1\x03\x31\x95\x56\x3c\x7f\x93\xcd\x54\xb9\x09\x4f\x22\x6e\x78\x32\x71\xe1\xe5\xa2\xa2\xc1\x07\x12\xea\xb6\x25\xd6\x49\x31\xcd\x4f\xfe\x67\x38\xd9\x7b\x9b\x5e\xf8\x28\xee\x9f\xb0\x59\xfc\x01\xaf\x0e\x79\xc1\xe1\x4b\x1d\x25\x98\x8c\x69\xa3\x99\x56\x7c\x1d\x93\x76\x8f\x79\x71\xd3\x14\x88\xb8\x65\x8a\x20\x87\x8b\x7c\x1d\xd7\xba\x02\xfc\x42\x93\x9d\xde\x3d\x4a\x33\x39\xa6\x5d\x50\x7d\xc5\x9c\x51\x09\x7b\x40\x51\x77\x05\xda\x56\xe9\xeb\xf0\xaf\xa5\x32\x82\xbf\x86\xdb\xb5\x8c\x54\x80\x69\xff\x6e\xb9\x5a\xad\xe7\xcc\x66\xd7\xbb\xef\x72\x47\x79\xca\x1f\x73\x1b\x33\x46\xff\x17\x70\x50\x37\x3d\x79\xff\x7b\x3e\x7f\x9b\xc0\xc1\xb4\xb2\x66\xa8\x87\x8b\x90\xba\xaa\x03\x9d\x3e\x3b\x63\x97\x9a\xc3\xdf\x6e\x6f\x48\x59\xaf\xd5\x02\x38\xc7\x54\x7a\x39\xb6\x08\x10\x93\x80\x44\xae\x18\x5d\x2b\xa3\xe0\x0a\x4e\x73\x67\x68\x64\xae\x09\x0d\x81\xea\xee\x5e\xe6\xcf\x1d\x0a\xb3\x78\xdd\x4d\xd8\x91\xe9\x37\xc2\xea\x54\x10\xe0\x51\x30\x05\x00\x00\x00\x00\x00\x00\x00\x39\x11\xfa\xb9\x64\xc2\x71\x55\x00\x27\x69\x7b\x52\x16\x06\x87\x46\x16\x02\xf8\x8d\xf1\x65\xd8\x84\xb3\x6e\xc2\xb6\xc2\x5a\x2f\x33\xc7\x15\x68\x7e\x9d\x4a\xfb\x96\xd6\x86\x1a\xca\x47\xda\x73\xd6\xf3\x14\x43\x45\xf4\x88\x43\xdd\x01\x4e\x5c\x5a\xd8\xfe\x99\x57\x54\xbd\x9c\xf3\x2f\xce\x1e\x31\x91\x9c\x4b\x20\x82\xfb\x0a\x30\xb9\xde\xae\x84\xbe\xd4\xb2\x80\x45\x63\x40\x73\xc9\xc5\x8c\x89\xd9\xe9\x9c\x81\x76\x91\x77\xc6\xd5\x94\xf8\x8a\x4f\xac\xfd\x4c\x73\x5a\x20\x30\x7c\x73\x7a\xfa\x2d\x60\x39\x94\x73\x29\x6b\x83\x1d\xbd\x93\x3d\x93\x99\x4b\xa3\x06\x42\x79\xb1\x0e\xa0\xc5\x83\x3f\x41\xf1\x57\xea\x23\x02\x99\x3d\xbe\x43\x3b\x1a\xa3\xa3\x76\x6d\x54\x39\x02\x04\x84\xf4\x11\x3c\x4c\x85\x94\x65\xc3\xb4\x15\xc3\x43\xf7\xf1\x40\xf3\x19\x53\x9d\x5b\xf3\x72\xaa\xae\xa1\xcc\x43\xa6\xc5\xcb\xe5\x97\x58\xbf\xee\x29\x16\x58\x0d\xac\x4b\x00\x8e\x59\x5f\x43\x74\x91\xd8\x7a\xbe\xd0\x2c\xef\xcd\x9d\xb5\x3d\x94\xd0\x2d\xae\xe6\x79\x18\xe5\xd6\x78\x74\x63\x18\x3b\x4b\x87\xc1\x05\x00\x00\x00\x2f\x78\x09\x95\x9b\xc0\x48\x85\x06\x13\xd1\x7c\xa5\x10\x55\xf2\xf4\x16\xa4\x4f\xe1\x80\xd2\xd5\x0c\x31\x2c\xca\x7c\xb1\x4a\x2b\xdc\x33\x1f\x57\xa9\x81\x71\x39\xa2\x06\xfc\x76\x95\x72\x27\xff\xff\x2d\xe2\x0a\x4b\x8e\x37\x37\xfb\xb4\x29\x13\x77\x7c\x06\x37\x6f\x79\x9e\xba\x36\x7e\x21\xf9\x4c\xa5\x98\x70\x5f\x5d\xcb\x76\x7d\x6f\x09\x00\xd6\xb0\xf6\x09\x5e\x53\xc4\xc4\x23\x4d\x0c\x1f\xbe\x43\x4f\x6a\xb8\xf4\x3c\x00\x13\xee\x93\xb8\x39\x46\xee\x77\x59\xe8\x9d\x7b\xdd\x1a\x32\xd7\xb3\x11\x71\x1b\x75\x7f\xe4\x3c\x06\xd2\x1a\x35\x81\x0d\x8f\xe9\x8b\x27\xfa\xea\x8a\xa1\x2b\xc8\x71\x6e\xef\xc5\xc9\x7c\x45\xac\x33\xee\xec\x96\x4c\x52\x14\xbc\x3a\x93\x59\xbd\xea\x1c\xcc\xab\x94\xf1\x5e\x36\x31\x9c\xb3\x4e\xbc\xac\xed\xb8\x2c\x2e\xd3\xde\x5a\x8a\x8f\x00\x11\xe8\xf7\x4e\x82\xd7\xf9\x60\x93\x53\x0e\x76\x69\x28\x39\xd7\x96\x19\x39\xad\xfd\xee\xea\xff\x19\xd1\x1e\xfc\xaf\xb6\xd5\x46\xfe\xf2\x71\xe8\x9d\x6c\xc2\x38\x9e\x81\xff\x58\xce\xfc\xce\x3f\xbf\x46\x25\xa7\xe7\xde\x40\xe4\x2e\x07\xb3\x44\x49\xe1\x5e\x06\x5c\xc7\x34\x00\x02\x00\x00\x00\x00\x00\x00\xf2\x88\xa4\x51\x0d\xe0\x3d\xab\x19\xd2\x62\x85\xed\xa8\x91\x56\xd5\x0d\xd3\x85\xa6\x03\x33\xba\x5b\xbf\x5d\x77\xcd\x70\x07\xad\x15\x19\xad\x54\x70\xde\x3d\xd6\xd6\x08\x0c\xaf\xcc\xf8\xa9\x74\x06\xbb\x6b\x68\xa1\xf0\xc4\x54\x98\x20\xa7\x3c\x88\x0f\x47\x5f\x73\x2a\xe0\x03\x98\xe8\xbd\x1f\x41\x08\xb7\x80\x7f\xb3\x3b\x72\x68\x5e\xc3\x7a\x2d\x3f\x76\x64\x13\xa6\x04\x59\x51\x62\x46\xe5\xa1\xd9\x98\xa2\x01\x7a\xef\x09\x48\xa6\x8c\xf2\x55\x31\x5a\xb8\x0d\xd3\x49\xe8\x91\xae\xf5\x95\xdc\x4d\x47\x0e\x8a\xc3\x2a\x30\x8e\x15\xfc\x37\xd0\x6a\xea\xc2\x89\xc0\x52\x3f\x48\x3e\x1f\xf7\x40\x8c\x60\x87\xf1\xab\x65\x2f\x2e\xf9\x1d\x4f\x2b\x01\x98\x7b\x0f\x46\xda\x03\x4e\x5c\x3f\x74\x5a\x7e\xe8\x10\x1a\x39\x34\xc5\x4e\x24\xb4\x8e\xc0\x27\x5e\x2d\x06\x87\xdc\x74\x6b\x08\x27\xcb\xf6\x52\xf4\x06\xc6\xb9\x5f\x27\x22\xe5\x8c\x05\xf7\x52\xce\x21\x26\x59\x6e\x1c\xd7\x65\x5b\x90\x48\x01\x78\x4c\x41\x6b\x22\xf7\x3d\x32\x46\x78\xe2\x72\x4f\x43\xf1\xfe\x68\x7c\x7e\x8a\x60\xc2\x8b\x82\xb6\x52\x83\x41\xb6\x48\xcd\xd5\x6f\xed\x7c\xdc\xbb\x15\x75\x91\x2d\x5e\xcd\x36\xde\xa3\xbc\xa0\xb7\x42\x7d\x83\x92\xc6\x28\x94\x55\xe8\xf8\xd2\xab\x22\x42\x72\x92\x51\xae\x03\x3a\x9e\x02\x21\x0e\x62\xdf\x05\x46\xa7\x4b\x33\x3a\x1c\x48\xf9\x5f\xd5\x4a\xcb\x57\x41\x25\x9e\x8c\x54\x88\xef\xee\xe3\x27\x41\x5c\xc1\x94\x51\x43\x2c\x6f\x14\xc2\x76\x93\x10\x2a\x3c\xd8\x48\x57\xcd\x65\x86\xfc\x5c\xa9\xa9\x3e\xb0\x14\x5f\xac\x06\x62\xff\x86\x10\x7f\x99\x8a\x8e\xf7\xdf\x8a\xa1\x40\x46\xc5\x5b\x03\xd3\xd4\x7f\x88\xa8\xd6\x0f\x77\x74\xa2\xee\x08\x75\x88\x97\xfb\x41\x1a\x94\xb3\xc2\xfc\x5d\x5f\x0d\xb4\x2c\x04\x56\xec\x01\x5f\x08\xe5\x24\x7d\x33\xae\x2d\x35\x60\x3f\xf8\x45\x4c\x16\xf8\x34\x28\x56\x93\x51\x25\x10\x2b\xb7\x84\xed\x71\x48\xb6\xce\x43\x1b\x63\xee\x35\x6b\x0c\x78\x5f\x2f\x47\xb9\x0e\x29\x38\x9f\x22\xfc\x5b\x59\xa7\x0e\xfa\xea\x2b\xd4\x01\x95\xaf\x44\x86\x22\x0d\x70\x2e\x30\xbf\xc4\x3c\x10\xec\x23\xea\x62\x83\x99\x4a\x7d\xde\x4d\xcb\x61\xfe\xa6\xb6\x51\xfb\x1d\x62\x45\x8d\x07\x41\xa1\x28\x30\x05\x2f\xcc\x46\x0d\xb0\x43\xaf\xe5\x25\x62\x9b\x40\xd7\xce\xe4\x58\xe4\xcb\x5e\x93\x0e\xd6\x24\x80\x6c\x43\xa0\x06\xe3\x93\x36\xd0\x7c\x2b\x80\x81\xc1\x28\xad\x27\x06\xf4\x82\x61\xf7\x89\x74\x84\xc2\x97\xa1\xa6\x61\x3b\xc1\x8f\x5a\x38\xd4\x42\x76\x8a\xf3\x80\x41\xef\xe0\x3d\x15\x2e\xf9\x5f\xf5\x69\xe7\x6d\xb2\x39\x1f\x45\x09\xd7\xf3\x39\xd9\x2f\xdb\x4a\x89\x36\x49\x49\xda\x39\x80\x00\x00\x00\x00\x00\x00\x00\xd8\x0a\x4f\xe6\x54\x57\x83\x76\xe5\x99\xaf\xf3\x56\x5b\x1d\x53\x1f\x30\x91\x2b\x99\x45\x03\x0b\x81\xea\x99\x35\xfd\x46\xed\xb4\x4a\x78\xf6\x15\x25\x54\x90\xa4\xb6\x21\x50\x1f\x2a\x9e\x4d\x24\x62\x4c\x4d\xac\x92\x74\x11\x8c\x67\x58\x4f\x5d\x37\x47\x55\x53\x4d\x7f\x68\xf6\x79\xc4\xff\x51\x6a\x9c\x86\x1a\x0e\x7e\x65\x86\x8f\xcb\x2b\xf1\xcb\x9a\xea\x4e\x05\xdf\x72\x27\x9f\xdb\x0d\x2b\x9e\x93\x5c\x5a\xf3\xcf\x47\x4b\x06\x79\xdf\xc2\x48\xc1\xf5\xae\xa4\xb8\xb3\x2c\x5d\x29\x5e\x57\x07\x9d\x0f\xe6\x62\xa4\x6b\x7f\x71\xcd\x47\x74\x4d\xb8\x6c\x50\xb7\x04\xc9\x71\xd9\x02\x95\xc7\xb2\xc7\x43\x9a\x2d\x78\xcc\xfa\x79\xb5\xfc\x2b\xff\x6b\xbf\x84\x02\x62\xbf\x89\x39\x4b\x3e\x06\x91\x95\x12\x64\xd2\x70\x0c\x83\x8f\xa2\xc7\xb3\x42\x52\x60\xf5\x95\x54\xe5\x02\xdc\xea\x39\xcb\x31\x3b\x00\x00\x00\x00\x00\x00\x4c\xa7\xc1\x2f\x45\x85\x8d\x62\x84\xca\x62\x70\xd6\xb2\xf0\xe5\x8f\xde\xd8\xa7\xb4\xa3\x02\xa9\x7b\xc6\x41\xdf\x07\x72\x0b\xa2\xb2\x6b\xbf\xcc\x80\x7c\xa0\xab\xb1\xb4\x43\x22\x26\x9c\x21\xc5\xec\x68\xcb\x06\x8e\xa8\x80\x67\xd9\x05\xea\x91\x7b\xb0\x3e\xef\xda\xeb\xde\xab\xf2\xd0\xdc\xe8\x09\x97\xc9\x15\xc8\x94\x9d\xe9\x92\x58\x7c\x2c\xb5\xfe\x36\xd7\xd3\xe5\xdb\x21\xb0\x94\xb8\xb7\x79\x40\xb5\xf0\x77\x22\xe4\x7a\x08\xd3\x67\xe5\xf8\x4c\x96\xec\x66\x4b\x72\x93\x4b\x99\xb3\x10\x9a\xf6\x5d\x77\xe8\x6a\xbd\x68\x59\xcd\xdf\x4b\xba\xe1\xf0\x93\x04\x62\xdf\x15\xfd\xdb\xc4\x85\x62\xea\x35\x11\xa8\x06\x5e\xf0\x28\xcf\x12\xf1\x4d\xcf\x6e\xbe\xcd\x8d\x98\x48\x36\x17\x4f\xaf\x1a\xa6\x09\xe5\xf1\xee\x11\x62\xdf\xa1\x3b\xdc\x1f\xa7\xcf\xaa\xdb\xa8\x5c\x72\xe9\x75\x8f\x03\xa7\x05\x00\xbe\x53\xf8\xd2\xa1\xdf\xb1\xc6\x8c\xc1\x64\xb0\xa0\x78\x0d\x97\x1a\x96\xea\x2c\x4d\x4c\xa0\x39\x8c\x22\x35\x98\x0a\x93\x07\xb3\xd5\xbd\x3b\x01\xfa\xff\xd0\xa5\xdb\xed\x28\x81\xa9\x70\x0a\xf5\x61\xac\x8c\x6b\x00\x00\x00\x00\x00\x00\x00\xf9\x6f\x06\x81\x7f\xb9\x03\x72\x9a\x7d\xb6\xff\x95\x76\x97\xc9\xed\xe7\x88\x5d\x94\xff\xb0\x96\x9b\xe0\xda\xf6\x0a\xf9\x31\x09\xeb\x1d\xee\x72\xe4\x36\x3f\x51\xaf\x62\xaf\x6f\xb2\xa6\xdf\x3b\xec\x89\x82\x2a\x7a\x0b\x67\x80\x58\xfa\x3f\xef\x86\xfa\xec\x21\x6e\xb6\x99\x21\x62\xf8\xdc\xbf\x71\x9c\x14\x8c\xd2\xf9\xc5\x5f\x49\x01\x20\x3a\x9a\x8a\x2c\x3e\x90\xf3\x94\x3d\xbc\x10\x36\x0a\x1a\x49\x70\x0d\x1d\xfb\xf6\x6d\x69\xf6\xfb\xaf\x50\x6c\x8b\xcc\xe8\xbb\x0d\x87\x2a\x02\x23\x89\x26\x40\x7a\x4e\xdd\xd5\xd0\xfc\x5a\x75\x2f\x90\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 4096);
*(uint16_t*)0x200000001004 = 0x8f5;
syscall(__NR_write, /*fd=*/r[2], /*data=*/0x200000000000ul, /*len=*/0x1006ul);
return 0;
}
Reply: The root cause of this crash is a race condition in the `cm109` driver's URB state machine, where a control URB (`urb_ctl`) is submitted while it is already active.
### Root Cause Analysis
The driver uses two URBs to communicate with the device:
1. `urb_ctl` (Control OUT): Sends the current key matrix column (`dev->keybit`) and the buzzer state.
2. `urb_irq` (Interrupt IN): Reads the key matrix row state.
These URBs are designed to run in a continuous, strictly alternating loop:
- When `urb_ctl` completes, `cm109_urb_ctl_callback()` submits `urb_irq`.
- When `urb_irq` completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()` which submits `urb_ctl`.
However, the driver also supports sound events (`SND_BELL` / `SND_TONE`) to toggle the buzzer. When a sound event is received (e.g., when the syzkaller reproducer writes a `\a` BEL character to `/dev/tty1`), `cm109_input_ev()` calls `cm109_toggle_buzzer_async()`.
If `urb_ctl` is not currently pending (which is true 50% of the time, whenever `urb_irq` is in flight), `cm109_toggle_buzzer_async()` immediately submits `urb_ctl` out-of-band to update the buzzer state. This breaks the strict alternation and causes **both** `urb_irq` and `urb_ctl` to be active concurrently.
When `urb_irq` subsequently completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()`. `cm109_submit_ctl()` blindly assumes `urb_ctl` is idle, updates the DMA buffer, and calls `usb_submit_urb(dev->urb_ctl)`. Since `urb_ctl` is already active, the USB core catches it and triggers the `URB ... submitted while active` warning.
### Sequence of Events
1. The normal polling loop is running. `urb_irq` is submitted and active (`dev->irq_urb_pending = 1`, `dev->ctl_urb_pending = 0`).
2. A sound event is received. `cm109_toggle_buzzer_async()` is called.
3. Since `dev->ctl_urb_pending == 0`, it sets `dev->ctl_urb_pending = 1` and submits `urb_ctl` via `cm109_submit_buzz_toggle()`. Now **both** URBs are active.
4. `urb_irq` completes. `cm109_urb_irq_callback()` processes the key data and calls `cm109_submit_ctl()`.
5. `cm109_submit_ctl()` clears `dev->irq_urb_pending = 0`, sets `dev->ctl_urb_pending = 1`, and calls `usb_submit_urb(dev->urb_ctl)`.
6. **CRASH:** `urb_ctl` is already active from step 3.
### Proposed Fix
To fix this, `cm109_submit_ctl()` must check if `dev->ctl_urb_pending` is already set. If it is, it should defer the submission by setting `dev->buzzer_pending = 1` (which acts as a general "resubmit `urb_ctl`" flag in the completion handler) and return immediately.
Additionally, `cm109_submit_buzz_toggle()` (which is called by the completion handler when `buzzer_pending` is set) must be updated to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`, ensuring that the deferred key matrix column update is not lost.
Here is the logic for the fix:
```c
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -366,6 +369,11 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
```
|
| 8/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 40349
output: 52
thoughts: 304
Thoughts: **Analyzing a Kernel Crash Report**
Okay, so I've got a kernel crash report here, and the crux of it seems to be an issue with the `cm109` USB driver. Specifically, the report states an `urb status -71` which points to a "protocol stall" or a similar USB communication error. The warning further indicates that a USB Request Block (URB) was submitted while active. The call trace looks interesting:
I immediately look at the critical part: the `cm109` driver. The warning comes from `usb_submit_urb`, suggesting a problem when the driver tries to send data to the device. The trace shows a callback, `cm109_urb_irq_callback`, which is called after URB completion and it seems there's a problem here and from there it goes to the generic USB handling layer. It seems that the `cm109_submit_ctl` function is at fault, from which `cm109_urb_irq_callback` is called. It looks like we're resubmitting the URB when it's still marked as active, which is a big no-no.
Next, I need to understand the timing of the URB submissions and completions within the `cm109` driver. I need to figure out exactly why the driver thinks it's okay to submit again. Something is clearly going wrong with either the interrupt handling or the URB status checking logic in the driver. A race condition is likely.
|
| 9/2 |
2026/05/10 19:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_urb_irq_callback]
Results: map[SourceCode: 387: /*
388: * IRQ handler
389: */
390: static void cm109_urb_irq_callback(struct urb *urb)
391: {
392: struct cm109_dev *dev = urb->context;
393: const int status = urb->status;
394:
395: dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
396: dev->irq_data->byte[0],
397: dev->irq_data->byte[1],
398: dev->irq_data->byte[2],
399: dev->irq_data->byte[3],
400: dev->keybit);
401:
402: if (status) {
403: if (status == -ESHUTDOWN)
404: return;
405: dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
406: __func__, status);
407: goto out;
408: }
409:
410: /* Special keys */
411: cm109_report_special(dev);
412:
413: /* Scan key column */
414: if (dev->keybit == 0xf) {
415:
416: /* Any changes ? */
417: if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
418: goto out;
419:
420: dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
421: dev->keybit = 0x1;
422: } else {
423: report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
424:
425: dev->keybit <<= 1;
426: if (dev->keybit > 0x8)
427: dev->keybit = 0xf;
428: }
429:
430: out:
431: cm109_submit_ctl(dev);
432: }
SourceFile:drivers/input/misc/cm109.c]
|
| 10/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 41778
output: 49
thoughts: 38
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into the Control URB Resubmission**
Alright, so the callback is responsible for getting the control Universal Serial Bus Request Block (URB) back in motion. It's using `cm109_submit_ctl(dev)` to do it, which is the standard way to handle a resubmission. I need to take a closer look at that `cm109_submit_ctl` function. I'm assuming it handles any error checking, re-initializing the URB as needed, and, of course, actually submitting it to the USB controller. There could be nuances here, such as how it handles different error codes, whether it uses a separate work queue, or if there is any synchronization needed with other operations. Time to check the code!
|
| 11/2 |
2026/05/10 19:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_submit_ctl]
Results: map[SourceCode: 358: static void cm109_submit_ctl(struct cm109_dev *dev)
359: {
360: int error;
361:
362: guard(spinlock_irqsave)(&dev->ctl_submit_lock);
363:
364: dev->irq_urb_pending = 0;
365:
366: if (unlikely(dev->shutdown))
367: return;
368:
369: if (dev->buzzer_state)
370: dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
371: else
372: dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
373:
374: dev->ctl_data->byte[HID_OR1] = dev->keybit;
375: dev->ctl_data->byte[HID_OR2] = dev->keybit;
376:
377: dev->buzzer_pending = 0;
378: dev->ctl_urb_pending = 1;
379:
380: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
381: if (error)
382: dev_err(&dev->intf->dev,
383: "%s: usb_submit_urb (urb_ctl) failed %d\n",
384: __func__, error);
385: }
SourceFile:drivers/input/misc/cm109.c]
|
| 12/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 42512
output: 49
thoughts: 153
Thoughts: **Debugging a USB Control Transfer Issue**
Okay, so I've got a crash related to a USB device, specifically an issue with a control transfer. The error message is screaming about `URB ffff888046210500 submitted while active`. Right, this means the `dev->urb_ctl`, that control USB Request Block, is already in use. That's the problem. I need to figure out *why* it's being submitted while it's active.
Aha! `cm109_submit_ctl` is the function responsible for submitting this control transfer, and it's called from `cm109_urb_irq_callback`. This is interesting. I need to understand the relationship. Is this callback actually for the `dev->urb_ctl` transfer, or is it for some other interrupt URB, `dev->urb_irq` perhaps? This is a critical distinction.
Time to go digging. I'll need to jump back into the driver code, likely starting with `cm109_probe` or something similar. I need to see how these URBs, `urb_irq` and `urb_ctl`, are initialized and configured. What are their respective purposes? What's the control flow? Understanding how these are set up is the key to untangling this concurrency issue. I need to trace back from `cm109_urb_irq_callback` to understand which URB's lifecycle this callback is actually managing. From there, I can get a better handle on why that control URB is being resubmitted.
|
| 13/2 |
2026/05/10 19:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_usb_probe]
Results: map[SourceCode: 677: static int cm109_usb_probe(struct usb_interface *intf,
678: const struct usb_device_id *id)
679: {
680: struct usb_device *udev = interface_to_usbdev(intf);
681: struct driver_info *nfo = (struct driver_info *)id->driver_info;
682: struct usb_host_interface *interface;
683: struct usb_endpoint_descriptor *endpoint;
684: struct cm109_dev *dev;
685: struct input_dev *input_dev = NULL;
686: int ret, pipe, i;
687: int error = -ENOMEM;
688:
689: interface = intf->cur_altsetting;
690:
691: if (interface->desc.bNumEndpoints < 1)
692: return -ENODEV;
693:
694: endpoint = &interface->endpoint[0].desc;
695:
696: if (!usb_endpoint_is_int_in(endpoint))
697: return -ENODEV;
698:
699: dev = kzalloc_obj(*dev);
700: if (!dev)
701: return -ENOMEM;
702:
703: spin_lock_init(&dev->ctl_submit_lock);
704: mutex_init(&dev->pm_mutex);
705:
706: dev->udev = udev;
707: dev->intf = intf;
708:
709: dev->idev = input_dev = input_allocate_device();
710: if (!input_dev)
711: goto err_out;
712:
713: /* allocate usb buffers */
714: dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
715: GFP_KERNEL, &dev->irq_dma);
716: if (!dev->irq_data)
717: goto err_out;
718:
719: dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
720: GFP_KERNEL, &dev->ctl_dma);
721: if (!dev->ctl_data)
722: goto err_out;
723:
724: dev->ctl_req = kmalloc_obj(*(dev->ctl_req));
725: if (!dev->ctl_req)
726: goto err_out;
727:
728: /* allocate urb structures */
729: dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
730: if (!dev->urb_irq)
731: goto err_out;
732:
733: dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
734: if (!dev->urb_ctl)
735: goto err_out;
736:
737: /* get a handle to the interrupt data pipe */
738: pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
739: ret = usb_maxpacket(udev, pipe);
740: if (ret != USB_PKT_LEN)
741: dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
742: ret, USB_PKT_LEN);
743:
744: /* initialise irq urb */
745: usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
746: USB_PKT_LEN,
747: cm109_urb_irq_callback, dev, endpoint->bInterval);
748: dev->urb_irq->transfer_dma = dev->irq_dma;
749: dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
750: dev->urb_irq->dev = udev;
751:
752: /* initialise ctl urb */
753: dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
754: USB_DIR_OUT;
755: dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
756: dev->ctl_req->wValue = cpu_to_le16(0x200);
757: dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
758: dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
759:
760: usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
761: (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
762: cm109_urb_ctl_callback, dev);
763: dev->urb_ctl->transfer_dma = dev->ctl_dma;
764: dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
765: dev->urb_ctl->dev = udev;
766:
767: /* find out the physical bus location */
768: usb_make_path(udev, dev->phys, sizeof(dev->phys));
769: strlcat(dev->phys, "/input0", sizeof(dev->phys));
770:
771: /* register settings for the input device */
772: input_dev->name = nfo->name;
773: input_dev->phys = dev->phys;
774: usb_to_input_id(udev, &input_dev->id);
775: input_dev->dev.parent = &intf->dev;
776:
777: input_set_drvdata(input_dev, dev);
778: input_dev->open = cm109_input_open;
779: input_dev->close = cm109_input_close;
780: input_dev->event = cm109_input_ev;
781:
782: input_dev->keycode = dev->keymap;
783: input_dev->keycodesize = sizeof(unsigned char);
784: input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
785:
786: input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
787: input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
788:
789: /* register available key events */
790: for (i = 0; i < KEYMAP_SIZE; i++) {
791: unsigned short k = keymap(i);
792: dev->keymap[i] = k;
793: __set_bit(k, input_dev->keybit);
794: }
795: __clear_bit(KEY_RESERVED, input_dev->keybit);
796:
797: error = input_register_device(dev->idev);
798: if (error)
799: goto err_out;
800:
801: usb_set_intfdata(intf, dev);
802:
803: return 0;
804:
805: err_out:
806: input_free_device(input_dev);
807: cm109_usb_cleanup(dev);
808: return error;
809: }
SourceFile:drivers/input/misc/cm109.c]
|
| 14/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 45326
output: 51
thoughts: 65
Thoughts: Here's my take on the thought process:
**Diving into the USB Driver Callbacks**
Alright, let's get down to brass tacks. I'm focusing on the `cm109` driver. I see `cm109_urb_irq_callback` which, as the name strongly suggests, is the interrupt request callback for the USB Request Block (URB) associated with interrupts. Makes sense. Then there's `cm109_urb_ctl_callback`. And that, obviously, is the control URB callback. Given that I'm already digging into this code, let's zero in on `cm109_urb_ctl_callback`. It's the control path that's often the place where things go sideways or have subtle setup issues. Time to dig in and see what's going on there.
|
| 15/2 |
2026/05/10 19:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_urb_ctl_callback]
Results: map[SourceCode: 434: static void cm109_urb_ctl_callback(struct urb *urb)
435: {
436: struct cm109_dev *dev = urb->context;
437: const int status = urb->status;
438: int error;
439:
440: dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
441: dev->ctl_data->byte[0],
442: dev->ctl_data->byte[1],
443: dev->ctl_data->byte[2],
444: dev->ctl_data->byte[3]);
445:
446: if (status) {
447: if (status == -ESHUTDOWN)
448: return;
449: dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
450: __func__, status);
451: }
452:
453: guard(spinlock_irqsave)(&dev->ctl_submit_lock);
454:
455: dev->ctl_urb_pending = 0;
456:
457: if (unlikely(dev->shutdown))
458: return;
459:
460: if (dev->buzzer_pending || status) {
461: dev->buzzer_pending = 0;
462: dev->ctl_urb_pending = 1;
463: cm109_submit_buzz_toggle(dev);
464: } else if (likely(!dev->irq_urb_pending)) {
465: /* ask for key data */
466: dev->irq_urb_pending = 1;
467: error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
468: if (error)
469: dev_err(&dev->intf->dev,
470: "%s: usb_submit_urb (urb_irq) failed %d\n",
471: __func__, error);
472: }
473: }
SourceFile:drivers/input/misc/cm109.c]
|
| 16/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 46306
output: 26
thoughts: 855
Thoughts: **Analyzing the cm109 Control Transfer Submission**
Okay, so I'm looking at `cm109_submit_ctl`. The core of this function is pretty straightforward: it sets up some control data, specifically the buzzer state and keybit, and then calls `usb_submit_urb` to send it over the control URB (`dev->urb_ctl`). I see the buzzer state is managed by setting or clearing the `BUZZER_ON` bit in `HID_OR0`, and the `keybit` is replicated in `HID_OR1` and `HID_OR2`.
Hmm, a potential issue jumps out immediately. There's no check *before* the `usb_submit_urb` call to see if a previous control transfer is already pending. `dev->ctl_urb_pending` is always set to `1` regardless. This suggests a potential race condition. Is it possible for `cm109_submit_ctl` to be called *while* a previous `urb_ctl` is still active? That would be a problem.
The code suggests that `cm109_submit_ctl` is called from `cm109_urb_irq_callback`, which makes sense. Let's see... the `cm109_urb_irq_callback` function is called after the completion of an interrupt URB (`urb_irq`). This function uses an interrupt transfer (`urb_irq`), which suggests the normal data flow is:
1. `urb_ctl` completes -> `cm109_urb_ctl_callback`
2. It submits `urb_irq`
3. `urb_irq` completes -> `cm109_urb_irq_callback`
4. It submits `urb_ctl` via `cm109_submit_ctl`
However, the problem is, it's possible that `cm109_submit_ctl` could be called from other locations.
I'm going to track down all calls to `cm109_submit_ctl` to determine if there are other potential callers which are a cause of concern.
|
| 17/2 |
2026/05/10 19:53 |
tool |
codesearch-find-references |
0m
Args: map[Name:cm109_submit_ctl]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_urb_irq_callback SourceFile:drivers/input/misc/cm109.c SourceLine:431]] TruncatedOutput:false]
|
| 18/2 |
2026/05/10 19:53 |
llm |
debugger |
0m
Model:
Tokens:
input: 47734
output: 51
thoughts: 47
Thoughts: Okay, let's see...
**Analyzing the Call Stack**
Hold on a second. I need to clarify something about `cm109_submit_ctl`. The code seems to suggest it's *only* called from `cm109_urb_irq_callback`. That raises a flag. I need to be absolutely certain of this. Single call paths can be tricky. Let's dig deeper. I'll need to check the cross-references to see if this is true and understand all call sites. Now, where else is `cm109_submit_buzz_toggle` called from? This is the next logical function to investigate given the context. Knowing the other caller functions will either confirm my suspicions about `cm109_submit_ctl` or illuminate any alternative execution paths I need to consider. Let's see...
|
| 19/2 |
2026/05/10 19:53 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_submit_buzz_toggle]
Results: map[SourceCode: 342: static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
343: {
344: int error;
345:
346: if (dev->buzzer_state)
347: dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
348: else
349: dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
350:
351: error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
352: if (error)
353: dev_err(&dev->intf->dev,
354: "%s: usb_submit_urb (urb_ctl) failed %d\n",
355: __func__, error);
356: }
SourceFile:drivers/input/misc/cm109.c]
|
| 20/2 |
2026/05/10 19:54 |
llm |
debugger |
1m
Model:
Tokens:
input: 48290
output: 28
thoughts: 41
Thoughts: Here's my thought process, as it relates to the code:
**Analyzing `cm109_submit_buzz_toggle` Calls**
Okay, so `cm109_submit_buzz_toggle` is being called and, importantly, it's also submitting `dev->urb_ctl` as part of its operation. That's a key detail to keep in mind, and it's something I need to understand thoroughly. I need to trace this `urb_ctl` interaction through the whole system. Now I need to see *who* is calling this `cm109_submit_buzz_toggle` function. A thorough understanding of its callers is crucial here. Understanding the control flow around it will give the context that I need. Let's dig in and track down all the places this function gets invoked. I'll need to figure out the use case for each call to fully grasp its impact.
|
| 21/2 |
2026/05/10 19:55 |
tool |
codesearch-find-references |
0m
Args: map[Name:cm109_submit_buzz_toggle]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_toggle_buzzer_async SourceFile:drivers/input/misc/cm109.c SourceLine:484] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_urb_ctl_callback SourceFile:drivers/input/misc/cm109.c SourceLine:463]] TruncatedOutput:false]
|
| 22/2 |
2026/05/10 19:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 48693
output: 52
thoughts: 17
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `cm109_toggle_buzzer_async`**
Alright, let's dive into this `cm109_toggle_buzzer_async` function. I see it's been flagged, so I need to understand what's happening here. The name implies it's responsible for asynchronously toggling a buzzer, which immediately makes me think about potential concurrency issues and the underlying hardware interaction. I need to quickly establish if this function is behaving correctly within the context of the larger system. Is there any obvious thread safety issues or race conditions I should be on the lookout for, given that it's asynchronous? What's the implementation using? Is it using interrupts, timers, or some kind of polling mechanism to manage the buzzer? And importantly, how does it handle potential errors or failures in the buzzer hardware? We can't let a faulty buzzer lock things up. This could be a critical piece of infrastructure, depending on the role this buzzer is playing. I'm hoping it's well-documented and has appropriate error handling. Time to dig in!
|
| 23/2 |
2026/05/10 19:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_toggle_buzzer_async]
Results: map[SourceCode: 475: static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
476: {
477: guard(spinlock_irqsave)(&dev->ctl_submit_lock);
478:
479: if (dev->ctl_urb_pending) {
480: /* URB completion will resubmit */
481: dev->buzzer_pending = 1;
482: } else {
483: dev->ctl_urb_pending = 1;
484: cm109_submit_buzz_toggle(dev);
485: }
486: }
SourceFile:drivers/input/misc/cm109.c]
|
| 24/2 |
2026/05/10 19:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 49232
output: 49
thoughts: 587
Thoughts: **Analyzing the cm109 Buzzer Control Logic**
Alright, let's break down this buzzer control mechanism. `cm109_toggle_buzzer_async` first checks `dev->ctl_urb_pending`. If it's already pending, it just flags `dev->buzzer_pending`. Seems reasonable – queuing the request. Otherwise, it sets `dev->ctl_urb_pending` to 1 and kicks off `cm109_submit_buzz_toggle`. Standard procedure.
Now, the potential wrinkle. I'm thinking about the implications if `cm109_submit_ctl` gets called within `cm109_urb_irq_callback`. Looking at that code block, specifically lines 362 through 380, it seems like `dev->ctl_urb_pending` gets set to 1 *again* in `cm109_submit_ctl` (line 378), regardless. That's a re-submission if it's already set! That is a problem, isn't it?
So, how can `dev->ctl_urb_pending` *already* be 1 when `cm109_urb_irq_callback` is called? The critical question: what's the typical workflow? What's the state machine here? We have to trace how `urb_ctl` gets submitted in the first place, or in what situations it is re-submitted. Looking back at `cm109_input_open` will provide information about initial submissions.
|
| 25/2 |
2026/05/10 19:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_input_open]
Results: map[SourceCode: 542: static int cm109_input_open(struct input_dev *idev)
543: {
544: struct cm109_dev *dev = input_get_drvdata(idev);
545: int error;
546:
547: error = usb_autopm_get_interface(dev->intf);
548: if (error < 0) {
549: dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
550: __func__, error);
551: return error;
552: }
553:
554: scoped_guard(mutex, &dev->pm_mutex) {
555: dev->buzzer_state = 0;
556: dev->key_code = -1; /* no keys pressed */
557: dev->keybit = 0xf;
558:
559: /* issue INIT */
560: dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
561: dev->ctl_data->byte[HID_OR1] = dev->keybit;
562: dev->ctl_data->byte[HID_OR2] = dev->keybit;
563: dev->ctl_data->byte[HID_OR3] = 0x00;
564:
565: dev->ctl_urb_pending = 1;
566: error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
567: if (!error) {
568: dev->open = 1;
569: return 0;
570: }
571: }
572:
573: dev->ctl_urb_pending = 0;
574: usb_autopm_put_interface(dev->intf);
575:
576: dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
577: __func__, error);
578:
579: return error;
580: }
SourceFile:drivers/input/misc/cm109.c]
|
| 26/2 |
2026/05/10 19:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 50855
output: 49
thoughts: 3983
Thoughts: **Analyzing the USB Driver's Control and Interrupt URB Interaction**
Okay, so I'm looking at this code and tracing the interactions between the control (urb_ctl) and interrupt (urb_irq) URBs. It seems like the basic intended behavior is a clean alternation: urb_ctl submits, completes, urb_ctl callback is called, and then the interrupt URB is submitted, completes, interrupt callback is called which submits the control urb again. The buzz toggling feature complicates things since it can submit the urb_ctl.
The problem arises when an interrupt URB is already pending, and a buzzer toggle is requested which submits the control URB. Now we have a condition where both URBs are active. The interrupt callback then tries to submit the control URB again, which results in a double-submit, a crash.
I’m seeing a race condition where `cm109_toggle_buzzer_async` submits `urb_ctl` and sets `dev->ctl_urb_pending`, and then if `urb_irq` completes before `urb_ctl`, `cm109_submit_ctl` attempts to submit `urb_ctl` *again*. Even without the double submit, the code is trying to write key data to DMA memory while it's active - not good.
The code assumes that the interrupt URB is the one responsible for getting the key data but the buzzer feature can send `urb_ctl` at any time! So, it appears that the `cm109_submit_ctl` function is missing a critical check: it *must* verify that `dev->ctl_urb_pending` is 0 before submitting.
If `dev->ctl_urb_pending` is already 1, because the buzzer task is active, then this new submit of the ctl UBR by `cm109_submit_ctl` MUST be skipped because we can not have two urb_ctl running simultaneously.
The root issue here is the lack of proper synchronization and state management between the control and interrupt URBs. It appears they are supposed to alternate to ensure the correct `keybit` information. A better approach might be to have `cm109_submit_ctl` set a flag indicating that a new `keybit` update is pending instead of double submitting a URB and letting the currently running URB complete before submitting `urb_irq`.
|
| 27/2 |
2026/05/10 19:55 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_input_ev]
Results: map[SourceCode: 599: static int cm109_input_ev(struct input_dev *idev, unsigned int type,
600: unsigned int code, int value)
601: {
602: struct cm109_dev *dev = input_get_drvdata(idev);
603:
604: dev_dbg(&dev->intf->dev,
605: "input_ev: type=%u code=%u value=%d\n", type, code, value);
606:
607: if (type != EV_SND)
608: return -EINVAL;
609:
610: switch (code) {
611: case SND_TONE:
612: case SND_BELL:
613: dev->buzzer_state = !!value;
614: if (!dev->resetting)
615: cm109_toggle_buzzer_async(dev);
616: return 0;
617:
618: default:
619: return -EINVAL;
620: }
621: }
SourceFile:drivers/input/misc/cm109.c]
|
| 28/2 |
2026/05/10 19:55 |
llm |
debugger |
0m
Model:
Tokens:
input: 55747
output: 52
thoughts: 4857
Thoughts: **Bug Analysis and Proposed Fix for USB Device Driver**
Okay, so I've been tracing the execution flow here, and I've zeroed in on a critical race condition. The `cm109_toggle_buzzer_async` function, which is triggered by an event, calls `cm109_toggle_buzzer_async`, which in turn, has the potential to cause the crash. `cm109_toggle_buzzer_async` is responsible for toggling the buzzer state. Essentially, the problem stems from how the driver handles control URBs (`urb_ctl`) in relation to interrupt URBs (`urb_irq`). The interrupt URB is the main loop, constantly scanning key data from the device. The control URB is used for out-of-band communication, such as the buzzer.
Here's the sequence: If `dev->ctl_urb_pending` is zero, it means the control transfer is not active and `cm109_toggle_buzzer_async` submits the `urb_ctl`. Then, it could happen that `urb_irq` is active. If `cm109_toggle_buzzer_async` submits the `urb_ctl` while `urb_irq` is still running, it is possible for both `urb_ctl` and `urb_irq` to be active concurrently. However, `cm109_submit_ctl` is called after `urb_irq` completes, which will try to submit `urb_ctl` AGAIN, because it does not check if `dev->ctl_urb_pending` is already 1. This double submission can lead to a crash.
My proposed fix involves the `cm109_submit_ctl` function. It's this function that's the culprit. Whenever `cm109_submit_ctl` is called, it should check if `dev->ctl_urb_pending` is already set. If so, it means `urb_ctl` is already active (submitted either by `cm109_toggle_buzzer_async` for buzzer control or the main loop). In this case, `cm109_submit_ctl` should *not* submit the control URB again. Instead, I think we should modify this to be a bit more robust:
1. `cm109_submit_ctl` should now check if `dev->ctl_urb_pending` is true, and if so, early exit.
2. In order to ensure that the proper data does get sent, we should have a flag that is set to indicate that we've skipped the control submission, and the next completion call should submit the control URB.
One consequence of this fix is that it is going to cause a delay in when the new data gets sent. This is ok, since the `cm109_urb_ctl_callback` will resubmit the control URB when it gets called.
This approach will ensure that `urb_ctl` is only submitted once at a time, preventing the double-submission and the subsequent crash. The trade-off is that there may be a slight delay in transmitting new key information or buzzer state changes, but it's acceptable for the sake of stability.
|
| 29/2 |
2026/05/10 19:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_urb_irq_callback]
Results: map[SourceCode: 387: /*
388: * IRQ handler
389: */
390: static void cm109_urb_irq_callback(struct urb *urb)
391: {
392: struct cm109_dev *dev = urb->context;
393: const int status = urb->status;
394:
395: dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
396: dev->irq_data->byte[0],
397: dev->irq_data->byte[1],
398: dev->irq_data->byte[2],
399: dev->irq_data->byte[3],
400: dev->keybit);
401:
402: if (status) {
403: if (status == -ESHUTDOWN)
404: return;
405: dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
406: __func__, status);
407: goto out;
408: }
409:
410: /* Special keys */
411: cm109_report_special(dev);
412:
413: /* Scan key column */
414: if (dev->keybit == 0xf) {
415:
416: /* Any changes ? */
417: if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
418: goto out;
419:
420: dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
421: dev->keybit = 0x1;
422: } else {
423: report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
424:
425: dev->keybit <<= 1;
426: if (dev->keybit > 0x8)
427: dev->keybit = 0xf;
428: }
429:
430: out:
431: cm109_submit_ctl(dev);
432: }
SourceFile:drivers/input/misc/cm109.c]
|
| 30/2 |
2026/05/10 19:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 62104
output: 44
thoughts: 271
Thoughts: **My Internal Process on the `dev->keybit` Logic**
Okay, let's break this down. First thought: wait, is `dev->keybit` the column we just read? No, that can't be right. Ah, it's actually the column we requested in the *previous* `urb_ctl` call. Okay, got it.
Now, if `dev->keybit` is `0xf`, that's when we're checking for changes. If there *are* changes, it resets `dev->keybit` to `0x1`. Then, `cm109_submit_ctl` gets called, and it *sends* `dev->keybit`, which, in this case, would be `0x1`.
Then, `urb_irq` completes, and `dev->keybit` is indeed `0x1`. And, that’s when `report_key(dev, dev->keymap[...])` gets called. Crucially, then it *sets* `dev->keybit` to `0x2`. Then, `cm109_submit_ctl` is called *again*, this time sending `dev->keybit`, which is `0x2`.
So, `dev->keybit` is, as I understand it, ALWAYS the column that is *about to* be requested. It’s a sort of anticipatory flag, and that makes perfect sense for control flow purposes. It sets up the next read.
Hang on though. If `dev->keybit` is `0x1`, then the `report_key` function uses `dev->keymap[dev->irq_data->byte[HID_IR1]]`. But, isn't `dev->keymap` a one-dimensional array? I'd better double check that. Let's dig into `report_key`.
|
| 31/2 |
2026/05/10 19:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:report_key]
Results: map[SourceCode: 290: /*
291: * Completes a request by converting the data into events for the
292: * input subsystem.
293: */
294: static void report_key(struct cm109_dev *dev, int key)
295: {
296: struct input_dev *idev = dev->idev;
297:
298: if (dev->key_code >= 0) {
299: /* old key up */
300: input_report_key(idev, dev->key_code, 0);
301: }
302:
303: dev->key_code = key;
304: if (key >= 0) {
305: /* new valid key */
306: input_report_key(idev, key, 1);
307: }
308:
309: input_sync(idev);
310: }
SourceFile:drivers/input/misc/cm109.c]
|
| 32/2 |
2026/05/10 19:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 63134
output: 43
thoughts: 109
Thoughts: **Decoding the Key Report**
Okay, so `report_key` seems straightforward enough, it's just reporting the key. But my initial thought is, how does it determine *which* column that key belongs to? My brain automatically jumps to the underlying mechanism.
Aha! Looking closer at the code snippet, it's immediately apparent: `dev->irq_data->byte[HID_IR1]` is actually being used as an *index* into `dev->keymap`! That's the crucial link. The index value represents the specific scan code of the key press. That's how it associates the reported key with its corresponding column. I need to make sure I understand the bounds - let's quickly review the definitions of `KEYMAP_SIZE` and the `keymap` macro to verify they make sense. We wouldn't want an out-of-bounds access!
|
| 33/2 |
2026/05/10 19:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:keymap]
Results: map[SourceCode: 288: static unsigned short (*keymap)(int) = keymap_kip1000;
SourceFile:drivers/input/misc/cm109.c]
|
| 34/2 |
2026/05/10 19:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 63536
output: 49
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, presented in the first person:
**Navigating the Keymap Landscape**
Right, let's dive into this. The initial thought, "Let's check `keymap_kip1000`," is essentially my internal checklist kicking in. I immediately recognize `keymap_kip1000` as a potentially crucial element. It's the starting point, likely holding the configuration data that dictates how a specific component, possibly a specialized input device or a specific process's interpretation of input signals, interacts within the system. My mind immediately begins to mentally map the potential implications. I'm already formulating questions: What system is this keymap for? What are its dependencies? What are the known issues? I'm anticipating the potential complexities of this map and its impact on overall functionality. Essentially, I know that this keymap will be the key to understanding how the device inputs behave.
|
| 35/2 |
2026/05/10 19:56 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:keymap_kip1000]
Results: map[SourceCode: 148: /* Map device buttons to internal key events.
149: *
150: * The "up" and "down" keys, are symbolised by arrows on the button.
151: * The "pickup" and "hangup" keys are symbolised by a green and red phone
152: * on the button.
153:
154: Komunikate KIP1000 Keyboard Matrix
155:
156: -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
157: | | | |
158: <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
159: | | | |
160: END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
161: | | | |
162: OK -- * -- 0 -- # --> GPI pin 7 (0x80)
163: | | | |
164:
165: /|\ /|\ /|\ /|\
166: | | | |
167: GPO
168: pin: 3 2 1 0
169: 0x8 0x4 0x2 0x1
170:
171: */
172: static unsigned short keymap_kip1000(int scancode)
173: {
174: switch (scancode) { /* phone key: */
175: case 0x82: return KEY_NUMERIC_0; /* 0 */
176: case 0x14: return KEY_NUMERIC_1; /* 1 */
177: case 0x12: return KEY_NUMERIC_2; /* 2 */
178: case 0x11: return KEY_NUMERIC_3; /* 3 */
179: case 0x24: return KEY_NUMERIC_4; /* 4 */
180: case 0x22: return KEY_NUMERIC_5; /* 5 */
181: case 0x21: return KEY_NUMERIC_6; /* 6 */
182: case 0x44: return KEY_NUMERIC_7; /* 7 */
183: case 0x42: return KEY_NUMERIC_8; /* 8 */
184: case 0x41: return KEY_NUMERIC_9; /* 9 */
185: case 0x81: return KEY_NUMERIC_POUND; /* # */
186: case 0x84: return KEY_NUMERIC_STAR; /* * */
187: case 0x88: return KEY_ENTER; /* pickup */
188: case 0x48: return KEY_ESC; /* hangup */
189: case 0x28: return KEY_LEFT; /* IN */
190: case 0x18: return KEY_RIGHT; /* OUT */
191: default: return special_keymap(scancode);
192: }
193: }
SourceFile:drivers/input/misc/cm109.c]
|
| 36/2 |
2026/05/10 19:56 |
llm |
debugger |
0m
Model:
Tokens:
input: 64770
output: 45
thoughts: 472
Thoughts: **Decoding the HID Scan Code**
Okay, so I'm trying to wrap my head around this `scancode` business. Initially, my gut said it's probably a combination of the row and column. But, hang on, `dev->irq_data->byte[HID_IR1]` is *the* scancode? I need to double-check that `cm109_urb_irq_callback` function again, let's take another look:
```c
413: /* Scan key column */
414: if (dev->keybit == 0xf) {
415:
416: /* Any changes ? */
417: if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
418: goto out;
419:
420: dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
421: dev->keybit = 0x1;
422: } else {
423: report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
424:
425: dev->keybit <<= 1;
426: if (dev->keybit > 0x8)
427: dev->keybit = 0xf;
428: }
```
Okay, so I'm following this... If `dev->keybit == 0xf`, it's essentially sending `0xf` – all columns are high, right? Then, if a row is high, that indicates a key press on that specific row. Then, it sets `dev->keybit = 0x1` and sends that out. The subsequent `urb_irq` will then provide the row data specifically for column `0x1`. So, then the code *reports* the key using `dev->irq_data->byte[HID_IR1]`. Wait… so, is `dev->irq_data->byte[HID_IR1]` really holding *both* the row and column information? I have to be sure. Time to check `special_keymap` to make sure!
|
| 37/2 |
2026/05/10 19:57 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:special_keymap]
Results: map[SourceCode: 135: static unsigned short special_keymap(int code)
136: {
137: if (code > 0xff) {
138: switch (code - 0xff) {
139: case RECORD_MUTE: return KEY_MICMUTE;
140: case PLAYBACK_MUTE: return KEY_MUTE;
141: case VOLUME_DOWN: return KEY_VOLUMEDOWN;
142: case VOLUME_UP: return KEY_VOLUMEUP;
143: }
144: }
145: return KEY_RESERVED;
146: }
SourceFile:drivers/input/misc/cm109.c]
|
| 38/2 |
2026/05/10 19:57 |
llm |
debugger |
0m
Model:
Tokens:
input: 66054
output: 47
thoughts: 3785
Thoughts: **Analysis of CM109 Key Scanning and Buzzer Control**
Okay, so I've been digging into this CM109 driver, and I think I've got a handle on how the key scanning and buzzer control work together. The core of the issue seems to be the interaction between the interrupt and control URBs, and how changes to the buzzer state or `keybit` affect the flow of data.
My initial thought was that a single byte `dev->irq_data->byte[HID_IR1]` is all the device gives us. So, if `dev->keybit` is set to `0x1`, the device returns row data in the upper nibble and column data in the lower nibble, and indeed, `0x82` means row `0x80` and column `0x02`. Got it. The device reports the key *combination*. This also explains why `report_key` doesn't care whether its the old or new `keybit`, because the device is reporting the *actual* column scanned.
This means that if we skip sending the `urb_ctl` (control URB) when the `keybit` changes, the device will scan the old `keybit` again. Then, `cm109_urb_irq_callback` is called with the data of the OLD `keybit`. But, importantly, `cm109_urb_irq_callback` *unconditionally* increments `dev->keybit`. So, if we skip sending `urb_ctl`, the *next* interrupt will contain data from the OLD keybit. `cm109_urb_irq_callback` calls `report_key` again with old data and increments the `keybit` AGAIN, and then the keybit advances, but the device is stuck scanning the old column. Is this a problem, or are keys only reported if they are different from `dev->key_code`?
Ah, `report_key` only reports new keys by checking if the current `key` is different than the saved `dev->key_code`. If the old key is reported again, it would send a "key up" then "key down" which would cause key bouncing. So we *must* send `urb_ctl` if `dev->keybit` changed!
But `cm109_toggle_buzzer_async` is the *only* thing that submits `urb_ctl` out-of-band. So, if we remove its ability to submit `urb_ctl`, and just set `dev->buzzer_pending = 1` instead? The next time `urb_ctl` is submitted it'll pick up on the buzzer state, right? `cm109_submit_ctl` does update `dev->ctl_data` from `dev->buzzer_state` so that will work. However, if the key-scanning loop *isn't* running, the `urb_ctl` will never be submitted.
Ah, but the scan loop *always* runs because if `dev->keybit == 0xf` and there are no changes, it submits `urb_ctl` anyway. So, the scan loop *always* runs.
Okay, so the loop is always running. So, why does `cm109_toggle_buzzer_async` submit the `urb_ctl` at all? Maybe to reduce latency? `urb_irq` runs every `endpoint->bInterval`. Let's check `cm109_usb_probe` and it says the interval is `endpoint->bInterval` which is full speed interrupt endpoints are usually 1-255 ms. So, let's say it's 10ms. A ten ms delay is probably okay for a buzzer. But does the *device* send the interrupt packet every 10 ms or only when there's a change? It might only send an interrupt packet when there's a key press.
If the device only sends on changes then `urb_irq` might be pending for a long time. It could only send an interrupt packet when a key is pressed! So, the loop isn't *always* running. Then `cm109_toggle_buzzer_async` must submit `urb_ctl` immediately. Control URB (endpoint 0) and interrupt URB are independent. So, it *can* submit simultaneously. If it submits `urb_ctl` *while* `urb_irq` is pending, `cm109_submit_ctl` called by `cm109_urb_irq_callback` will attempt to resubmit. So, `cm109_submit_ctl` MUST check if `urb_ctl` is active! It should set a
|
| 39/2 |
2026/05/10 19:57 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/input/misc/cm109.c IncludeLines:true Name:cm109_dev]
Results: map[SourceCode: 87: /* CM109 device structure */
88: struct cm109_dev {
89: struct input_dev *idev; /* input device */
90: struct usb_device *udev; /* usb device */
91: struct usb_interface *intf;
92:
93: /* irq input channel */
94: struct cm109_ctl_packet *irq_data;
95: dma_addr_t irq_dma;
96: struct urb *urb_irq;
97:
98: /* control output channel */
99: struct cm109_ctl_packet *ctl_data;
100: dma_addr_t ctl_dma;
101: struct usb_ctrlrequest *ctl_req;
102: struct urb *urb_ctl;
103: /*
104: * The 3 bitfields below are protected by ctl_submit_lock.
105: * They have to be separate since they are accessed from IRQ
106: * context.
107: */
108: unsigned irq_urb_pending:1; /* irq_urb is in flight */
109: unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
110: unsigned buzzer_pending:1; /* need to issue buzz command */
111: spinlock_t ctl_submit_lock;
112:
113: unsigned char buzzer_state; /* on/off */
114:
115: /* flags */
116: unsigned open:1;
117: unsigned resetting:1;
118: unsigned shutdown:1;
119:
120: /* This mutex protects writes to the above flags */
121: struct mutex pm_mutex;
122:
123: unsigned short keymap[KEYMAP_SIZE];
124:
125: char phys[64]; /* physical device path */
126: int key_code; /* last reported key */
127: int keybit; /* 0=new scan 1,2,4,8=scan columns */
128: u8 gpi; /* Cached value of GPI (high nibble) */
129: };
SourceFile:drivers/input/misc/cm109.c]
|
| 40/2 |
2026/05/10 19:57 |
llm |
debugger |
3m
Model:
Tokens:
input: 71591
output: 1225
thoughts: 26339
Thoughts: **Root Cause Analysis**
The `cm109` driver for a USB device uses two USB Request Blocks (URBs): `urb_irq`, an interrupt IN endpoint used to read key states, and `urb_ctl`, a control OUT endpoint used to send the current key column and buzzer state to the device. The normal operational flow is a continuous, alternating loop:
1. `urb_ctl` completes, and its callback, `cm109_urb_ctl_callback`, submits `urb_irq`.
2. `urb_irq` completes, and its callback, `cm109_urb_irq_callback`, calls `cm109_submit_ctl`, which then submits `urb_ctl`.
The problem arises from how the driver handles sound events, such as `SND_BELL` or `SND_TONE`, which can trigger buzzer toggling. When a sound event is received (e.g., by writing a BEL character to `/dev/tty1`), `cm109_input_ev` is called. This, in turn, calls `cm109_toggle_buzzer_async`, which may immediately submit `urb_ctl` to update the buzzer state *even if* `urb_irq` is already pending (meaning the alternation loop is running in the background).
This out-of-band submission of `urb_ctl` breaks the strict alternation. `cm109_submit_ctl`, which is called when `urb_irq` completes, assumes that `urb_ctl` is *not* already active. It proceeds to update the DMA buffer and then calls `usb_submit_urb(dev->urb_ctl)` again. Since `urb_ctl` *is* already active (submitted by `cm109_toggle_buzzer_async`), this results in the crash with an error message such as `URB ... submitted while active`.
**Sequence of Events Leading to the Crash**
Here's a step-by-step trace of how the crash happens when a sound event triggers the buzzer:
1. The system is running and the device is connected; the normal urb loop is active.
2. `urb_irq` is active: the interrupt IN transfer is in progress, so `dev->irq_urb_pending` is 1, and `dev->ctl_urb_pending` is 0.
3. A sound event (e.g., BEL character on `/dev/tty1`) triggers the buzzer.
4. `cm109_input_ev` is called. It calls `cm109_toggle_buzzer_async`.
5. `cm109_toggle_buzzer_async` submits `urb_ctl` to turn on the buzzer. Since `ctl_urb_pending` is zero, `urb_ctl` is submitted.
6. `urb_ctl` submission causes `dev->ctl_urb_pending` to be set to 1.
7. `urb_ctl` is now active, with `dev->ctl_urb_pending = 1` and `dev->irq_urb_pending = 1`.
8. `urb_irq` completes. `cm109_urb_irq_callback` is called.
9. `cm109_urb_irq_callback` calls `cm109_submit_ctl`.
10. `cm109_submit_ctl` sees `dev->ctl_urb_pending == 0`. It updates `dev->ctl_data` (DMA buffer) and submits `urb_ctl`. This fails because the URB is already active. This is a BUG.
**Proposed Fix**
To fix this, we need to defer the submission of `urb_ctl` in `cm109_submit_ctl` if `urb_ctl` is already pending. Here's a suggested patch:
1. In `cm109_submit_ctl`: Add a check for `dev->ctl_urb_pending`. If `urb_ctl` is already pending, just set `dev->buzzer_pending = 1` and return:
```c
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -355,6 +355,10 @@
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
if
|
| 41/1 |
2026/05/10 20:00 |
action |
kernel-scratch-checkouter |
0m
Results: map[KernelScratchSrc:/app/workdir/cache/tmp/tmp537745989]
|
| 42/1 |
2026/05/10 20:00 |
loop |
|
30m
|
| 43/2 |
2026/05/10 20:00 |
iteration |
0 |
30m
|
| 44/3 |
2026/05/10 20:00 |
agent |
patch-generator |
6m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, and an initial explanation of the root cause done by another
kernel expert.
Use the codeeditor tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.
Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).
Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.
Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.
If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.
If you will end up removing the WARN_ON macro because the condition can legitimately happen,
add a pr_err call that logs that the unlikely condition has happened. The pr_err message
must not include "WARNING" string.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
cm109 1-1:0.8: cm109_urb_irq_callback: urb status -71
------------[ cut here ]------------
URB ffff888046210500 submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x7b/0x18b0 drivers/usb/core/urb.c:379, CPU#1: swapper/1/0
Modules linked in:
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:usb_submit_urb+0x7e/0x18b0 drivers/usb/core/urb.c:379
Code: 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 17 72 06 fb 49 83 3e 00 74 40 e8 6c 63 9c fa 48 8d 3d 65 92 ff 08 48 89 de <67> 48 0f b9 3a b8 f0 ff ff ff eb 11 e8 51 63 9c fa eb 05 e8 4a 63
RSP: 0018:ffffc900001f8a00 EFLAGS: 00010046
RAX: ffffffff8727c654 RBX: ffff888046210500 RCX: ffff8880404b0000
RDX: 0000000000000100 RSI: ffff888046210500 RDI: ffffffff902758c0
RBP: 000000000000000f R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff5200003f140 R12: 0000000000000820
R13: ffff88804fd73030 R14: ffff888046210508 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff8880ebc29000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558409200a78 CR3: 0000000046b1e000 CR4: 00000000000006f0
Call Trace:
<IRQ>
cm109_submit_ctl drivers/input/misc/cm109.c:380 [inline]
cm109_urb_irq_callback+0x6e2/0xc90 drivers/input/misc/cm109.c:431
__usb_hcd_giveback_urb+0x376/0x540 drivers/usb/core/hcd.c:1657
dummy_timer+0xbc0/0x4650 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2011
handle_softirqs+0x22a/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1061
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ab 90 02 e9 d3 f5 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 53 33 21 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffc9000046fe20 EFLAGS: 00000202
RAX: 0000000000076dab RBX: ffffffff8199768a RCX: 0000000080000001
RDX: 0000000000000001 RSI: ffffffff8deddb4e RDI: ffffffff8c0ea120
RBP: ffffc9000046ff10 R08: ffff88807ee339db R09: 1ffff1100fdc673b
R10: dffffc0000000000 R11: ffffed100fdc673c R12: 0000000000000001
R13: 1ffff11008096000 R14: 0000000000000001 R15: 1ffff11008096000
arch_safe_halt arch/x86/kernel/process.c:766 [inline]
default_idle+0x9/0x20 arch/x86/kernel/process.c:767
default_idle_call+0x72/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x36a/0x5f0 kernel/sched/idle.c:352
cpu_startup_entry+0x43/0x60 kernel/sched/idle.c:451
start_secondary+0x101/0x110 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x147
</TASK>
----------------
Code disassembly (best guess):
0: 89 f0 mov %esi,%eax
2: 48 c1 e8 03 shr $0x3,%rax
6: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
b: 74 08 je 0x15
d: 4c 89 f7 mov %r14,%rdi
10: e8 17 72 06 fb call 0xfb06722c
15: 49 83 3e 00 cmpq $0x0,(%r14)
19: 74 40 je 0x5b
1b: e8 6c 63 9c fa call 0xfa9c638c
20: 48 8d 3d 65 92 ff 08 lea 0x8ff9265(%rip),%rdi # 0x8ff928c
27: 48 89 de mov %rbx,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 f0 ff ff ff mov $0xfffffff0,%eax
34: eb 11 jmp 0x47
36: e8 51 63 9c fa call 0xfa9c638c
3b: eb 05 jmp 0x42
3d: e8 .byte 0xe8
3e: 4a rex.WX
3f: 63 .byte 0x63
The explanation of the root cause of the bug is:
The root cause of this crash is a race condition in the `cm109` driver's URB state machine, where a control URB (`urb_ctl`) is submitted while it is already active.
### Root Cause Analysis
The driver uses two URBs to communicate with the device:
1. `urb_ctl` (Control OUT): Sends the current key matrix column (`dev->keybit`) and the buzzer state.
2. `urb_irq` (Interrupt IN): Reads the key matrix row state.
These URBs are designed to run in a continuous, strictly alternating loop:
- When `urb_ctl` completes, `cm109_urb_ctl_callback()` submits `urb_irq`.
- When `urb_irq` completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()` which submits `urb_ctl`.
However, the driver also supports sound events (`SND_BELL` / `SND_TONE`) to toggle the buzzer. When a sound event is received (e.g., when the syzkaller reproducer writes a `\a` BEL character to `/dev/tty1`), `cm109_input_ev()` calls `cm109_toggle_buzzer_async()`.
If `urb_ctl` is not currently pending (which is true 50% of the time, whenever `urb_irq` is in flight), `cm109_toggle_buzzer_async()` immediately submits `urb_ctl` out-of-band to update the buzzer state. This breaks the strict alternation and causes **both** `urb_irq` and `urb_ctl` to be active concurrently.
When `urb_irq` subsequently completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()`. `cm109_submit_ctl()` blindly assumes `urb_ctl` is idle, updates the DMA buffer, and calls `usb_submit_urb(dev->urb_ctl)`. Since `urb_ctl` is already active, the USB core catches it and triggers the `URB ... submitted while active` warning.
### Sequence of Events
1. The normal polling loop is running. `urb_irq` is submitted and active (`dev->irq_urb_pending = 1`, `dev->ctl_urb_pending = 0`).
2. A sound event is received. `cm109_toggle_buzzer_async()` is called.
3. Since `dev->ctl_urb_pending == 0`, it sets `dev->ctl_urb_pending = 1` and submits `urb_ctl` via `cm109_submit_buzz_toggle()`. Now **both** URBs are active.
4. `urb_irq` completes. `cm109_urb_irq_callback()` processes the key data and calls `cm109_submit_ctl()`.
5. `cm109_submit_ctl()` clears `dev->irq_urb_pending = 0`, sets `dev->ctl_urb_pending = 1`, and calls `usb_submit_urb(dev->urb_ctl)`.
6. **CRASH:** `urb_ctl` is already active from step 3.
### Proposed Fix
To fix this, `cm109_submit_ctl()` must check if `dev->ctl_urb_pending` is already set. If it is, it should defer the submission by setting `dev->buzzer_pending = 1` (which acts as a general "resubmit `urb_ctl`" flag in the completion handler) and return immediately.
Additionally, `cm109_submit_buzz_toggle()` (which is called by the completion handler when `buzzer_pending` is set) must be updated to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`, ensuring that the deferred key matrix column update is not lost.
Here is the logic for the fix:
```c
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -366,6 +369,11 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
```
Reply: I have implemented a fix for the race condition in the `cm109` driver's URB state machine that was causing the `URB submitted while active` crash.
### What was done and why:
1. **Prevent Concurrent URB Submission:** I added a check for `dev->ctl_urb_pending` at the beginning of `cm109_submit_ctl()`. If it is already set, it means `urb_ctl` is currently active (e.g., it was submitted out-of-band by `cm109_toggle_buzzer_async()` due to a sound event). In this case, we set `dev->buzzer_pending = 1` to defer the submission and return immediately. This directly prevents the crash by ensuring the URB is never submitted while it's already in flight.
2. **Preserve Key Matrix State:** I updated `cm109_submit_buzz_toggle()` to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`. This is crucial because when the deferred submission is eventually processed by the completion handler (`cm109_urb_ctl_callback`), it calls `cm109_submit_buzz_toggle()`. By including the `keybit` update here, we ensure that the deferred key matrix column update is not lost and the device continues scanning correctly.
3. **Code Refactoring & Deduplication:** Since `cm109_submit_ctl()` and `cm109_submit_buzz_toggle()` now prepare the exact same data for `urb_ctl` (buzzer state and keybit), I refactored `cm109_submit_ctl()` to simply call `cm109_submit_buzz_toggle()` after checking and updating the pending flags. This removes code duplication and makes the URB submission logic cleaner and more consistent across the driver.
4. **Concurrency Safety:** I verified that `cm109_submit_buzz_toggle()` is always called with the `ctl_submit_lock` held across all its call sites (`cm109_toggle_buzzer_async`, `cm109_urb_ctl_callback`, and now `cm109_submit_ctl`). This ensures that reading `dev->keybit` and modifying `dev->ctl_data` remains perfectly safe from concurrency issues.
|
| 45/4 |
2026/05/10 20:00 |
llm |
patch-generator |
2m
Model:
Tokens:
input: 7735
output: 27
thoughts: 618
Thoughts: **Analyzing the cm109 Driver Crash**
Okay, so I've got a crash report indicating a problem with the `cm109` driver, specifically that `URB ffff888046210500` was submitted while it was active within `usb_submit_urb`. Sounds like a classic race condition scenario, which usually means the driver's URB state machine is getting tangled up.
Looking at the driver's code, I see it uses two URBs: `urb_ctl` and `urb_irq`. The intention, as I understand it, is for these to alternate in a continuous loop. However, `cm109_toggle_buzzer_async()` is able to submit `urb_ctl` out-of-band under the condition that `dev->ctl_urb_pending` is 0. This is the root of the issue, because this action can break the proper alternation and cause both `urb_irq` and `urb_ctl` to be active simultaneously, which will cause issues.
When `urb_irq` completes, the `cm109_urb_irq_callback()` function then calls `cm109_submit_ctl()`. My assessment is that `cm109_submit_ctl()` blindly assumes `urb_ctl` is available, updates the DMA buffer, and then calls `usb_submit_urb(dev->urb_ctl)`. Since `urb_ctl` is already active, the USB core rightly catches this and triggers the warning. No surprise the system is throwing a fit.
I see the proposed fix involves adding a `dev->ctl_urb_pending` flag to track the pending state of the control URB. The changes seem to modify `cm109_submit_buzz_toggle` to update the buffer before submission and `cm109_submit_ctl` to check if `dev->ctl_urb_pending` is already set before trying to resubmit the URB. This looks like a reasonable fix to me. If `dev->ctl_urb_pending` is already set, we set a `buzzer_pending` flag which will be handled when the existing request is processed. This should prevent the concurrent submission and the associated race condition. I'll need to test this thoroughly, of course, but it seems like a solid approach to resolving this issue.
|
| 46/4 |
2026/05/10 20:03 |
tool |
read-file |
0m
Args: map[File:drivers/input/misc/cm109.c]
Results: map[Contents:// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the VoIP USB phones with CM109 chipsets.
*
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
*/
/*
* Tested devices:
* - Komunikate KIP1000
* - Genius G-talk
* - Allied-Telesis Corega USBPH01
* - ...
*
* This driver is based on the yealink.c driver
*
* Thanks to:
* - Authors of yealink.c
* - Thomas Reitmayr
* - Oliver Neukum for good review comments and code
* - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
* - Dmitry Torokhov for valuable input and review
*
* Todo:
* - Read/write EEPROM
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "20080805"
#define DRIVER_AUTHOR "Alfred E. Heggestad"
#define DRIVER_DESC "CM109 phone driver"
static char *phone = "kip1000";
module_param(phone, charp, S_IRUSR);
MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
enum {
/* HID Registers */
HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
HID_OR1 = 0x01, /* GPO - General Purpose Output */
HID_OR2 = 0x02, /* Set GPIO to input/output mode */
HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
/* HID_IR0 */
RECORD_MUTE = 1 << 3,
PLAYBACK_MUTE = 1 << 2,
VOLUME_DOWN = 1 << 1,
VOLUME_UP = 1 << 0,
/* HID_OR0 */
/* bits 7-6
0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
and SPDIF
1: HID_OR0-3 are used as generic HID registers
2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
EEPROM_DATA0-1, EEPROM_CTRL (see Note)
3: Reserved
*/
HID_OR_GPO_BUZ_SPDIF = 0 << 6,
HID_OR_GENERIC_HID_REG = 1 << 6,
HID_OR_MAP_MCU_EEPROM = 2 << 6,
BUZZER_ON = 1 << 5,
/* up to 256 normal keys, up to 15 special key combinations */
KEYMAP_SIZE = 256 + 15,
};
/* CM109 protocol packet */
struct cm109_ctl_packet {
u8 byte[4];
} __attribute__ ((packed));
enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
/* CM109 device structure */
struct cm109_dev {
struct input_dev *idev; /* input device */
struct usb_device *udev; /* usb device */
struct usb_interface *intf;
/* irq input channel */
struct cm109_ctl_packet *irq_data;
dma_addr_t irq_dma;
struct urb *urb_irq;
/* control output channel */
struct cm109_ctl_packet *ctl_data;
dma_addr_t ctl_dma;
struct usb_ctrlrequest *ctl_req;
struct urb *urb_ctl;
/*
* The 3 bitfields below are protected by ctl_submit_lock.
* They have to be separate since they are accessed from IRQ
* context.
*/
unsigned irq_urb_pending:1; /* irq_urb is in flight */
unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
unsigned buzzer_pending:1; /* need to issue buzz command */
spinlock_t ctl_submit_lock;
unsigned char buzzer_state; /* on/off */
/* flags */
unsigned open:1;
unsigned resetting:1;
unsigned shutdown:1;
/* This mutex protects writes to the above flags */
struct mutex pm_mutex;
unsigned short keymap[KEYMAP_SIZE];
char phys[64]; /* physical device path */
int key_code; /* last reported key */
int keybit; /* 0=new scan 1,2,4,8=scan columns */
u8 gpi; /* Cached value of GPI (high nibble) */
};
/******************************************************************************
* CM109 key interface
*****************************************************************************/
static unsigned short special_keymap(int code)
{
if (code > 0xff) {
switch (code - 0xff) {
case RECORD_MUTE: return KEY_MICMUTE;
case PLAYBACK_MUTE: return KEY_MUTE;
case VOLUME_DOWN: return KEY_VOLUMEDOWN;
case VOLUME_UP: return KEY_VOLUMEUP;
}
}
return KEY_RESERVED;
}
/* Map device buttons to internal key events.
*
* The "up" and "down" keys, are symbolised by arrows on the button.
* The "pickup" and "hangup" keys are symbolised by a green and red phone
* on the button.
Komunikate KIP1000 Keyboard Matrix
-> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
| | | |
<- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
| | | |
END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
| | | |
OK -- * -- 0 -- # --> GPI pin 7 (0x80)
| | | |
/|\ /|\ /|\ /|\
| | | |
GPO
pin: 3 2 1 0
0x8 0x4 0x2 0x1
*/
static unsigned short keymap_kip1000(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x14: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x11: return KEY_NUMERIC_3; /* 3 */
case 0x24: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x21: return KEY_NUMERIC_6; /* 6 */
case 0x44: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x41: return KEY_NUMERIC_9; /* 9 */
case 0x81: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x88: return KEY_ENTER; /* pickup */
case 0x48: return KEY_ESC; /* hangup */
case 0x28: return KEY_LEFT; /* IN */
case 0x18: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
Contributed by Shaun Jackman <sjackman@gmail.com>
Genius G-Talk keyboard matrix
0 1 2 3
4: 0 4 8 Talk
5: 1 5 9 End
6: 2 6 # Up
7: 3 7 * Down
*/
static unsigned short keymap_gtalk(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0;
case 0x21: return KEY_NUMERIC_1;
case 0x41: return KEY_NUMERIC_2;
case 0x81: return KEY_NUMERIC_3;
case 0x12: return KEY_NUMERIC_4;
case 0x22: return KEY_NUMERIC_5;
case 0x42: return KEY_NUMERIC_6;
case 0x82: return KEY_NUMERIC_7;
case 0x14: return KEY_NUMERIC_8;
case 0x24: return KEY_NUMERIC_9;
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* Talk (green handset) */
case 0x28: return KEY_ESC; /* End (red handset) */
case 0x48: return KEY_UP; /* Menu up (rocker switch) */
case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
default: return special_keymap(scancode);
}
}
/*
* Keymap for Allied-Telesis Corega USBPH01
* http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
*
* Contributed by july@nat.bg
*/
static unsigned short keymap_usbph01(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0; /* 0 */
case 0x21: return KEY_NUMERIC_1; /* 1 */
case 0x41: return KEY_NUMERIC_2; /* 2 */
case 0x81: return KEY_NUMERIC_3; /* 3 */
case 0x12: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x42: return KEY_NUMERIC_6; /* 6 */
case 0x82: return KEY_NUMERIC_7; /* 7 */
case 0x14: return KEY_NUMERIC_8; /* 8 */
case 0x24: return KEY_NUMERIC_9; /* 9 */
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* IN */
case 0x88: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
* Keymap for ATCom AU-100
* http://www.atcom.cn/products.html
* http://www.packetizer.com/products/au100/
* http://www.voip-info.org/wiki/view/AU-100
*
* Contributed by daniel@gimpelevich.san-francisco.ca.us
*/
static unsigned short keymap_atcom(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x11: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x14: return KEY_NUMERIC_3; /* 3 */
case 0x21: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x24: return KEY_NUMERIC_6; /* 6 */
case 0x41: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x44: return KEY_NUMERIC_9; /* 9 */
case 0x84: return KEY_NUMERIC_POUND; /* # */
case 0x81: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* left arrow */
case 0x88: return KEY_RIGHT; /* right arrow */
default: return special_keymap(scancode);
}
}
static unsigned short (*keymap)(int) = keymap_kip1000;
/*
* Completes a request by converting the data into events for the
* input subsystem.
*/
static void report_key(struct cm109_dev *dev, int key)
{
struct input_dev *idev = dev->idev;
if (dev->key_code >= 0) {
/* old key up */
input_report_key(idev, dev->key_code, 0);
}
dev->key_code = key;
if (key >= 0) {
/* new valid key */
input_report_key(idev, key, 1);
}
input_sync(idev);
}
/*
* Converts data of special key presses (volume, mute) into events
* for the input subsystem, sends press-n-release for mute keys.
*/
static void cm109_report_special(struct cm109_dev *dev)
{
static const u8 autorelease = RECORD_MUTE | PLAYBACK_MUTE;
struct input_dev *idev = dev->idev;
u8 data = dev->irq_data->byte[HID_IR0];
unsigned short keycode;
int i;
for (i = 0; i < 4; i++) {
keycode = dev->keymap[0xff + BIT(i)];
if (keycode == KEY_RESERVED)
continue;
input_report_key(idev, keycode, data & BIT(i));
if (data & autorelease & BIT(i)) {
input_sync(idev);
input_report_key(idev, keycode, 0);
}
}
input_sync(idev);
}
/******************************************************************************
* CM109 usb communication interface
*****************************************************************************/
static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
static void cm109_submit_ctl(struct cm109_dev *dev)
{
int error;
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
/*
* IRQ handler
*/
static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
dev->irq_data->byte[1],
dev->irq_data->byte[2],
dev->irq_data->byte[3],
dev->keybit);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
goto out;
}
/* Special keys */
cm109_report_special(dev);
/* Scan key column */
if (dev->keybit == 0xf) {
/* Any changes ? */
if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
goto out;
dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
dev->keybit = 0x1;
} else {
report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
dev->keybit <<= 1;
if (dev->keybit > 0x8)
dev->keybit = 0xf;
}
out:
cm109_submit_ctl(dev);
}
static void cm109_urb_ctl_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
dev->ctl_data->byte[1],
dev->ctl_data->byte[2],
dev->ctl_data->byte[3]);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
}
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_pending || status) {
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
} else if (likely(!dev->irq_urb_pending)) {
/* ask for key data */
dev->irq_urb_pending = 1;
error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_irq) failed %d\n",
__func__, error);
}
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
{
int error;
if (on)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_control_msg(dev->udev,
usb_sndctrlpipe(dev->udev, 0),
dev->ctl_req->bRequest,
dev->ctl_req->bRequestType,
le16_to_cpu(dev->ctl_req->wValue),
le16_to_cpu(dev->ctl_req->wIndex),
dev->ctl_data,
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
if (error < 0 && error != -EINTR)
dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
__func__, error);
}
static void cm109_stop_traffic(struct cm109_dev *dev)
{
dev->shutdown = 1;
/*
* Make sure other CPUs see this
*/
smp_wmb();
usb_kill_urb(dev->urb_ctl);
usb_kill_urb(dev->urb_irq);
cm109_toggle_buzzer_sync(dev, 0);
dev->shutdown = 0;
smp_wmb();
}
static void cm109_restore_state(struct cm109_dev *dev)
{
if (dev->open) {
/*
* Restore buzzer state.
* This will also kick regular URB submission
*/
cm109_toggle_buzzer_async(dev);
}
}
/******************************************************************************
* input event interface
*****************************************************************************/
static int cm109_input_open(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
int error;
error = usb_autopm_get_interface(dev->intf);
if (error < 0) {
dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
__func__, error);
return error;
}
scoped_guard(mutex, &dev->pm_mutex) {
dev->buzzer_state = 0;
dev->key_code = -1; /* no keys pressed */
dev->keybit = 0xf;
/* issue INIT */
dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
if (!error) {
dev->open = 1;
return 0;
}
}
dev->ctl_urb_pending = 0;
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
return error;
}
static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
/*
* Once we are here event delivery is stopped so we
* don't need to worry about someone starting buzzer
* again
*/
cm109_stop_traffic(dev);
dev->open = 0;
}
usb_autopm_put_interface(dev->intf);
}
static int cm109_input_ev(struct input_dev *idev, unsigned int type,
unsigned int code, int value)
{
struct cm109_dev *dev = input_get_drvdata(idev);
dev_dbg(&dev->intf->dev,
"input_ev: type=%u code=%u value=%d\n", type, code, value);
if (type != EV_SND)
return -EINVAL;
switch (code) {
case SND_TONE:
case SND_BELL:
dev->buzzer_state = !!value;
if (!dev->resetting)
cm109_toggle_buzzer_async(dev);
return 0;
default:
return -EINVAL;
}
}
/******************************************************************************
* Linux interface and usb initialisation
*****************************************************************************/
struct driver_info {
char *name;
};
static const struct driver_info info_cm109 = {
.name = "CM109 USB driver",
};
enum {
VENDOR_ID = 0x0d8c, /* C-Media Electronics */
PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
};
/* table of devices that work with this driver */
static const struct usb_device_id cm109_usb_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID_CM109,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t) &info_cm109
},
/* you can add more devices here with product ID 0x0008 - 0x000f */
{ }
};
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
kfree(dev->ctl_req);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
kfree(dev);
}
static void cm109_usb_disconnect(struct usb_interface *interface)
{
struct cm109_dev *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(dev->idev);
cm109_usb_cleanup(dev);
}
static int cm109_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct driver_info *nfo = (struct driver_info *)id->driver_info;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct cm109_dev *dev;
struct input_dev *input_dev = NULL;
int ret, pipe, i;
int error = -ENOMEM;
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints < 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
spin_lock_init(&dev->ctl_submit_lock);
mutex_init(&dev->pm_mutex);
dev->udev = udev;
dev->intf = intf;
dev->idev = input_dev = input_allocate_device();
if (!input_dev)
goto err_out;
/* allocate usb buffers */
dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->irq_dma);
if (!dev->irq_data)
goto err_out;
dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->ctl_dma);
if (!dev->ctl_data)
goto err_out;
dev->ctl_req = kmalloc_obj(*(dev->ctl_req));
if (!dev->ctl_req)
goto err_out;
/* allocate urb structures */
dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_irq)
goto err_out;
dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_ctl)
goto err_out;
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
ret = usb_maxpacket(udev, pipe);
if (ret != USB_PKT_LEN)
dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
ret, USB_PKT_LEN);
/* initialise irq urb */
usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
USB_PKT_LEN,
cm109_urb_irq_callback, dev, endpoint->bInterval);
dev->urb_irq->transfer_dma = dev->irq_dma;
dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_irq->dev = udev;
/* initialise ctl urb */
dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_OUT;
dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
dev->ctl_req->wValue = cpu_to_le16(0x200);
dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
(void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
cm109_urb_ctl_callback, dev);
dev->urb_ctl->transfer_dma = dev->ctl_dma;
dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_ctl->dev = udev;
/* find out the physical bus location */
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
/* register settings for the input device */
input_dev->name = nfo->name;
input_dev->phys = dev->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = cm109_input_open;
input_dev->close = cm109_input_close;
input_dev->event = cm109_input_ev;
input_dev->keycode = dev->keymap;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
/* register available key events */
for (i = 0; i < KEYMAP_SIZE; i++) {
unsigned short k = keymap(i);
dev->keymap[i] = k;
__set_bit(k, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
error = input_register_device(dev->idev);
if (error)
goto err_out;
usb_set_intfdata(intf, dev);
return 0;
err_out:
input_free_device(input_dev);
cm109_usb_cleanup(dev);
return error;
}
static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
guard(mutex)(&dev->pm_mutex);
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_resume(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_resume\n");
guard(mutex)(&dev->pm_mutex);
cm109_restore_state(dev);
return 0;
}
static int cm109_usb_pre_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
mutex_lock(&dev->pm_mutex);
/*
* Make sure input events don't try to toggle buzzer
* while we are resetting
*/
dev->resetting = 1;
smp_wmb();
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_post_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev->resetting = 0;
smp_wmb();
cm109_restore_state(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static struct usb_driver cm109_driver = {
.name = "cm109",
.probe = cm109_usb_probe,
.disconnect = cm109_usb_disconnect,
.suspend = cm109_usb_suspend,
.resume = cm109_usb_resume,
.reset_resume = cm109_usb_resume,
.pre_reset = cm109_usb_pre_reset,
.post_reset = cm109_usb_post_reset,
.id_table = cm109_usb_table,
.supports_autosuspend = 1,
};
static int __init cm109_select_keymap(void)
{
/* Load the phone keymap */
if (!strcasecmp(phone, "kip1000")) {
keymap = keymap_kip1000;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Komunikate KIP1000 phone loaded\n");
} else if (!strcasecmp(phone, "gtalk")) {
keymap = keymap_gtalk;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Genius G-talk phone loaded\n");
} else if (!strcasecmp(phone, "usbph01")) {
keymap = keymap_usbph01;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
} else if (!strcasecmp(phone, "atcom")) {
keymap = keymap_atcom;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for ATCom AU-100 phone loaded\n");
} else {
printk(KERN_ERR KBUILD_MODNAME ": "
"Unsupported phone: %s\n", phone);
return -EINVAL;
}
return 0;
}
static int __init cm109_init(void)
{
int err;
err = cm109_select_keymap();
if (err)
return err;
err = usb_register(&cm109_driver);
if (err)
return err;
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
return 0;
}
static void __exit cm109_exit(void)
{
usb_deregister(&cm109_driver);
}
module_init(cm109_init);
module_exit(cm109_exit);
MODULE_DEVICE_TABLE(usb, cm109_usb_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
]
|
| 47/4 |
2026/05/10 20:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 18834
output: 816
thoughts: 4728
Thoughts: **Analyzing the CM109 USB Driver Patch**
Alright, let's break this down. It appears we're looking at a patch that modifies how buzzer toggling and key data are handled in the `cm109` USB driver. The core change seems to be adding `dev->keybit` updates to `HID_OR1` and `HID_OR2` within `cm109_submit_buzz_toggle`. Initially, I'm verifying the control flow with `cm109_submit_buzz_toggle` and `cm109_submit_ctl`. I am quickly grasping how `cm109_submit_ctl` is called from `cm109_urb_irq_callback`. I observe how the code deals with pending URBs and buzzer states using `dev->ctl_urb_pending` and `dev->buzzer_pending`. Now, after careful review and several mental executions of code blocks, I believe there's a race condition where updating the key bit might be missed if the buzzer toggle is requested during an interrupt. The fix ensures that the key data is consistently sent with every `urb_ctl` submission, including those triggered by buzzer toggles.
The fix seems to correct the behavior by sending the current key state with the buzzer control message. This prevents potential issues where the device might miss key data updates if a buzzer toggle is requested while an interrupt URB is pending. Then I was going to see if the two functions could be merged, making the code more streamlined. As a next step, I will test the changes and also confirm my assumptions regarding the control flow in the scenarios where we have concurrent URBs in progress.
|
| 48/4 |
2026/05/10 20:03 |
tool |
codeeditor |
0m
Args: map[CurrentCode:static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
static void cm109_submit_ctl(struct cm109_dev *dev)
{
int error;
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
} NewCode:static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
static void cm109_submit_ctl(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->ctl_urb_pending) {
dev->buzzer_pending = 1;
return;
}
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
} SourceFile:drivers/input/misc/cm109.c]
Results:
|
| 49/4 |
2026/05/10 20:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 24738
output: 28
thoughts: 22
Thoughts: Okay, here's my summary, taking the perspective of someone knowledgeable in the field:
**Investigating the Usage of `cm109_submit_buzz_toggle`**
Right, let's get down to brass tacks. I need to be absolutely certain about the scope of this `cm109_submit_buzz_toggle` function. It's crucial, when dealing with code, to immediately verify whether a function is isolated or has broader implications. So, my initial thought is clear: I need to determine if this specific function is utilized elsewhere in the codebase. Understanding its connections is paramount to determining its overall impact and potential consequences if I were to modify it. Is this used throughout the system, or just in this narrow scope? This is the starting point for any deeper analysis.
|
| 50/4 |
2026/05/10 20:03 |
tool |
codesearch-find-references |
0m
Args: map[Name:cm109_submit_buzz_toggle]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_toggle_buzzer_async SourceFile:drivers/input/misc/cm109.c SourceLine:484] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_urb_ctl_callback SourceFile:drivers/input/misc/cm109.c SourceLine:463]] TruncatedOutput:false]
|
| 51/4 |
2026/05/10 20:03 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 25094
output: 27
thoughts: 31
Thoughts: Here's my analysis of the provided code snippets, thinking like a seasoned developer:
**Delving into Asynchronous Buzzer Control and URB Callbacks**
Alright, let's dissect `cm109_toggle_buzzer_async` and `cm109_urb_ctl_callback`. My first thought is to understand how these two functions interact, and what's the broader picture of their role within the system. `cm109_toggle_buzzer_async` immediately suggests an asynchronous operation related to a buzzer. "Async" implies a non-blocking execution, meaning the main thread isn't held up waiting for the buzzer operation to complete. This is critical for responsiveness in embedded systems and user interfaces, especially.
My immediate follow up question is: is the buzzer control implemented using interrupts, timers, or perhaps a dedicated hardware peripheral? If it's truly asynchronous, it's probably *not* polling a hardware register in a tight loop. I'd need to examine the function's internals to confirm, but the 'async' naming is a very strong hint.
The presence of `cm109_urb_ctl_callback` is even more intriguing. "URB" hints at USB, and "ctl" typically means "control." So, this likely represents a callback function triggered when a USB control transfer related to the CM109 device completes. This control transfer might be how the asynchronous buzzer toggle is initiated or monitored. It might even handle the status update from hardware.
Now, my mind starts drawing connections. Are these functions part of a USB driver for a CM109 device, and does the device feature a buzzer? The `_async` part might imply some sort of underlying URB setup or completion handling. The control callback would then provide a way to handle the result of the `toggle` or some related task.
I need to understand: What parameters do these functions take? What are their return values? What external data structures or global variables do they access? And crucially, how is `cm109_toggle_buzzer_async` called, and how is the `cm109_urb_ctl_callback` registered? Answering these will provide the complete design.
|
| 52/4 |
2026/05/10 20:03 |
tool |
read-file |
0m
Args: map[File:drivers/input/misc/cm109.c]
Results: map[Contents:// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the VoIP USB phones with CM109 chipsets.
*
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
*/
/*
* Tested devices:
* - Komunikate KIP1000
* - Genius G-talk
* - Allied-Telesis Corega USBPH01
* - ...
*
* This driver is based on the yealink.c driver
*
* Thanks to:
* - Authors of yealink.c
* - Thomas Reitmayr
* - Oliver Neukum for good review comments and code
* - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
* - Dmitry Torokhov for valuable input and review
*
* Todo:
* - Read/write EEPROM
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "20080805"
#define DRIVER_AUTHOR "Alfred E. Heggestad"
#define DRIVER_DESC "CM109 phone driver"
static char *phone = "kip1000";
module_param(phone, charp, S_IRUSR);
MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
enum {
/* HID Registers */
HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
HID_OR1 = 0x01, /* GPO - General Purpose Output */
HID_OR2 = 0x02, /* Set GPIO to input/output mode */
HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
/* HID_IR0 */
RECORD_MUTE = 1 << 3,
PLAYBACK_MUTE = 1 << 2,
VOLUME_DOWN = 1 << 1,
VOLUME_UP = 1 << 0,
/* HID_OR0 */
/* bits 7-6
0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
and SPDIF
1: HID_OR0-3 are used as generic HID registers
2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
EEPROM_DATA0-1, EEPROM_CTRL (see Note)
3: Reserved
*/
HID_OR_GPO_BUZ_SPDIF = 0 << 6,
HID_OR_GENERIC_HID_REG = 1 << 6,
HID_OR_MAP_MCU_EEPROM = 2 << 6,
BUZZER_ON = 1 << 5,
/* up to 256 normal keys, up to 15 special key combinations */
KEYMAP_SIZE = 256 + 15,
};
/* CM109 protocol packet */
struct cm109_ctl_packet {
u8 byte[4];
} __attribute__ ((packed));
enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
/* CM109 device structure */
struct cm109_dev {
struct input_dev *idev; /* input device */
struct usb_device *udev; /* usb device */
struct usb_interface *intf;
/* irq input channel */
struct cm109_ctl_packet *irq_data;
dma_addr_t irq_dma;
struct urb *urb_irq;
/* control output channel */
struct cm109_ctl_packet *ctl_data;
dma_addr_t ctl_dma;
struct usb_ctrlrequest *ctl_req;
struct urb *urb_ctl;
/*
* The 3 bitfields below are protected by ctl_submit_lock.
* They have to be separate since they are accessed from IRQ
* context.
*/
unsigned irq_urb_pending:1; /* irq_urb is in flight */
unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
unsigned buzzer_pending:1; /* need to issue buzz command */
spinlock_t ctl_submit_lock;
unsigned char buzzer_state; /* on/off */
/* flags */
unsigned open:1;
unsigned resetting:1;
unsigned shutdown:1;
/* This mutex protects writes to the above flags */
struct mutex pm_mutex;
unsigned short keymap[KEYMAP_SIZE];
char phys[64]; /* physical device path */
int key_code; /* last reported key */
int keybit; /* 0=new scan 1,2,4,8=scan columns */
u8 gpi; /* Cached value of GPI (high nibble) */
};
/******************************************************************************
* CM109 key interface
*****************************************************************************/
static unsigned short special_keymap(int code)
{
if (code > 0xff) {
switch (code - 0xff) {
case RECORD_MUTE: return KEY_MICMUTE;
case PLAYBACK_MUTE: return KEY_MUTE;
case VOLUME_DOWN: return KEY_VOLUMEDOWN;
case VOLUME_UP: return KEY_VOLUMEUP;
}
}
return KEY_RESERVED;
}
/* Map device buttons to internal key events.
*
* The "up" and "down" keys, are symbolised by arrows on the button.
* The "pickup" and "hangup" keys are symbolised by a green and red phone
* on the button.
Komunikate KIP1000 Keyboard Matrix
-> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
| | | |
<- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
| | | |
END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
| | | |
OK -- * -- 0 -- # --> GPI pin 7 (0x80)
| | | |
/|\ /|\ /|\ /|\
| | | |
GPO
pin: 3 2 1 0
0x8 0x4 0x2 0x1
*/
static unsigned short keymap_kip1000(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x14: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x11: return KEY_NUMERIC_3; /* 3 */
case 0x24: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x21: return KEY_NUMERIC_6; /* 6 */
case 0x44: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x41: return KEY_NUMERIC_9; /* 9 */
case 0x81: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x88: return KEY_ENTER; /* pickup */
case 0x48: return KEY_ESC; /* hangup */
case 0x28: return KEY_LEFT; /* IN */
case 0x18: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
Contributed by Shaun Jackman <sjackman@gmail.com>
Genius G-Talk keyboard matrix
0 1 2 3
4: 0 4 8 Talk
5: 1 5 9 End
6: 2 6 # Up
7: 3 7 * Down
*/
static unsigned short keymap_gtalk(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0;
case 0x21: return KEY_NUMERIC_1;
case 0x41: return KEY_NUMERIC_2;
case 0x81: return KEY_NUMERIC_3;
case 0x12: return KEY_NUMERIC_4;
case 0x22: return KEY_NUMERIC_5;
case 0x42: return KEY_NUMERIC_6;
case 0x82: return KEY_NUMERIC_7;
case 0x14: return KEY_NUMERIC_8;
case 0x24: return KEY_NUMERIC_9;
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* Talk (green handset) */
case 0x28: return KEY_ESC; /* End (red handset) */
case 0x48: return KEY_UP; /* Menu up (rocker switch) */
case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
default: return special_keymap(scancode);
}
}
/*
* Keymap for Allied-Telesis Corega USBPH01
* http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
*
* Contributed by july@nat.bg
*/
static unsigned short keymap_usbph01(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0; /* 0 */
case 0x21: return KEY_NUMERIC_1; /* 1 */
case 0x41: return KEY_NUMERIC_2; /* 2 */
case 0x81: return KEY_NUMERIC_3; /* 3 */
case 0x12: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x42: return KEY_NUMERIC_6; /* 6 */
case 0x82: return KEY_NUMERIC_7; /* 7 */
case 0x14: return KEY_NUMERIC_8; /* 8 */
case 0x24: return KEY_NUMERIC_9; /* 9 */
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* IN */
case 0x88: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
* Keymap for ATCom AU-100
* http://www.atcom.cn/products.html
* http://www.packetizer.com/products/au100/
* http://www.voip-info.org/wiki/view/AU-100
*
* Contributed by daniel@gimpelevich.san-francisco.ca.us
*/
static unsigned short keymap_atcom(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x11: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x14: return KEY_NUMERIC_3; /* 3 */
case 0x21: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x24: return KEY_NUMERIC_6; /* 6 */
case 0x41: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x44: return KEY_NUMERIC_9; /* 9 */
case 0x84: return KEY_NUMERIC_POUND; /* # */
case 0x81: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* left arrow */
case 0x88: return KEY_RIGHT; /* right arrow */
default: return special_keymap(scancode);
}
}
static unsigned short (*keymap)(int) = keymap_kip1000;
/*
* Completes a request by converting the data into events for the
* input subsystem.
*/
static void report_key(struct cm109_dev *dev, int key)
{
struct input_dev *idev = dev->idev;
if (dev->key_code >= 0) {
/* old key up */
input_report_key(idev, dev->key_code, 0);
}
dev->key_code = key;
if (key >= 0) {
/* new valid key */
input_report_key(idev, key, 1);
}
input_sync(idev);
}
/*
* Converts data of special key presses (volume, mute) into events
* for the input subsystem, sends press-n-release for mute keys.
*/
static void cm109_report_special(struct cm109_dev *dev)
{
static const u8 autorelease = RECORD_MUTE | PLAYBACK_MUTE;
struct input_dev *idev = dev->idev;
u8 data = dev->irq_data->byte[HID_IR0];
unsigned short keycode;
int i;
for (i = 0; i < 4; i++) {
keycode = dev->keymap[0xff + BIT(i)];
if (keycode == KEY_RESERVED)
continue;
input_report_key(idev, keycode, data & BIT(i));
if (data & autorelease & BIT(i)) {
input_sync(idev);
input_report_key(idev, keycode, 0);
}
}
input_sync(idev);
}
/******************************************************************************
* CM109 usb communication interface
*****************************************************************************/
static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
static void cm109_submit_ctl(struct cm109_dev *dev)
{
int error;
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
/*
* IRQ handler
*/
static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
dev->irq_data->byte[1],
dev->irq_data->byte[2],
dev->irq_data->byte[3],
dev->keybit);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
goto out;
}
/* Special keys */
cm109_report_special(dev);
/* Scan key column */
if (dev->keybit == 0xf) {
/* Any changes ? */
if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
goto out;
dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
dev->keybit = 0x1;
} else {
report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
dev->keybit <<= 1;
if (dev->keybit > 0x8)
dev->keybit = 0xf;
}
out:
cm109_submit_ctl(dev);
}
static void cm109_urb_ctl_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
dev->ctl_data->byte[1],
dev->ctl_data->byte[2],
dev->ctl_data->byte[3]);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
}
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_pending || status) {
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
} else if (likely(!dev->irq_urb_pending)) {
/* ask for key data */
dev->irq_urb_pending = 1;
error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_irq) failed %d\n",
__func__, error);
}
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
{
int error;
if (on)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_control_msg(dev->udev,
usb_sndctrlpipe(dev->udev, 0),
dev->ctl_req->bRequest,
dev->ctl_req->bRequestType,
le16_to_cpu(dev->ctl_req->wValue),
le16_to_cpu(dev->ctl_req->wIndex),
dev->ctl_data,
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
if (error < 0 && error != -EINTR)
dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
__func__, error);
}
static void cm109_stop_traffic(struct cm109_dev *dev)
{
dev->shutdown = 1;
/*
* Make sure other CPUs see this
*/
smp_wmb();
usb_kill_urb(dev->urb_ctl);
usb_kill_urb(dev->urb_irq);
cm109_toggle_buzzer_sync(dev, 0);
dev->shutdown = 0;
smp_wmb();
}
static void cm109_restore_state(struct cm109_dev *dev)
{
if (dev->open) {
/*
* Restore buzzer state.
* This will also kick regular URB submission
*/
cm109_toggle_buzzer_async(dev);
}
}
/******************************************************************************
* input event interface
*****************************************************************************/
static int cm109_input_open(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
int error;
error = usb_autopm_get_interface(dev->intf);
if (error < 0) {
dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
__func__, error);
return error;
}
scoped_guard(mutex, &dev->pm_mutex) {
dev->buzzer_state = 0;
dev->key_code = -1; /* no keys pressed */
dev->keybit = 0xf;
/* issue INIT */
dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
if (!error) {
dev->open = 1;
return 0;
}
}
dev->ctl_urb_pending = 0;
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
return error;
}
static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
/*
* Once we are here event delivery is stopped so we
* don't need to worry about someone starting buzzer
* again
*/
cm109_stop_traffic(dev);
dev->open = 0;
}
usb_autopm_put_interface(dev->intf);
}
static int cm109_input_ev(struct input_dev *idev, unsigned int type,
unsigned int code, int value)
{
struct cm109_dev *dev = input_get_drvdata(idev);
dev_dbg(&dev->intf->dev,
"input_ev: type=%u code=%u value=%d\n", type, code, value);
if (type != EV_SND)
return -EINVAL;
switch (code) {
case SND_TONE:
case SND_BELL:
dev->buzzer_state = !!value;
if (!dev->resetting)
cm109_toggle_buzzer_async(dev);
return 0;
default:
return -EINVAL;
}
}
/******************************************************************************
* Linux interface and usb initialisation
*****************************************************************************/
struct driver_info {
char *name;
};
static const struct driver_info info_cm109 = {
.name = "CM109 USB driver",
};
enum {
VENDOR_ID = 0x0d8c, /* C-Media Electronics */
PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
};
/* table of devices that work with this driver */
static const struct usb_device_id cm109_usb_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID_CM109,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t) &info_cm109
},
/* you can add more devices here with product ID 0x0008 - 0x000f */
{ }
};
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
kfree(dev->ctl_req);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
kfree(dev);
}
static void cm109_usb_disconnect(struct usb_interface *interface)
{
struct cm109_dev *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(dev->idev);
cm109_usb_cleanup(dev);
}
static int cm109_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct driver_info *nfo = (struct driver_info *)id->driver_info;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct cm109_dev *dev;
struct input_dev *input_dev = NULL;
int ret, pipe, i;
int error = -ENOMEM;
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints < 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
spin_lock_init(&dev->ctl_submit_lock);
mutex_init(&dev->pm_mutex);
dev->udev = udev;
dev->intf = intf;
dev->idev = input_dev = input_allocate_device();
if (!input_dev)
goto err_out;
/* allocate usb buffers */
dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->irq_dma);
if (!dev->irq_data)
goto err_out;
dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->ctl_dma);
if (!dev->ctl_data)
goto err_out;
dev->ctl_req = kmalloc_obj(*(dev->ctl_req));
if (!dev->ctl_req)
goto err_out;
/* allocate urb structures */
dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_irq)
goto err_out;
dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_ctl)
goto err_out;
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
ret = usb_maxpacket(udev, pipe);
if (ret != USB_PKT_LEN)
dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
ret, USB_PKT_LEN);
/* initialise irq urb */
usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
USB_PKT_LEN,
cm109_urb_irq_callback, dev, endpoint->bInterval);
dev->urb_irq->transfer_dma = dev->irq_dma;
dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_irq->dev = udev;
/* initialise ctl urb */
dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_OUT;
dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
dev->ctl_req->wValue = cpu_to_le16(0x200);
dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
(void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
cm109_urb_ctl_callback, dev);
dev->urb_ctl->transfer_dma = dev->ctl_dma;
dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_ctl->dev = udev;
/* find out the physical bus location */
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
/* register settings for the input device */
input_dev->name = nfo->name;
input_dev->phys = dev->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = cm109_input_open;
input_dev->close = cm109_input_close;
input_dev->event = cm109_input_ev;
input_dev->keycode = dev->keymap;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
/* register available key events */
for (i = 0; i < KEYMAP_SIZE; i++) {
unsigned short k = keymap(i);
dev->keymap[i] = k;
__set_bit(k, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
error = input_register_device(dev->idev);
if (error)
goto err_out;
usb_set_intfdata(intf, dev);
return 0;
err_out:
input_free_device(input_dev);
cm109_usb_cleanup(dev);
return error;
}
static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
guard(mutex)(&dev->pm_mutex);
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_resume(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_resume\n");
guard(mutex)(&dev->pm_mutex);
cm109_restore_state(dev);
return 0;
}
static int cm109_usb_pre_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
mutex_lock(&dev->pm_mutex);
/*
* Make sure input events don't try to toggle buzzer
* while we are resetting
*/
dev->resetting = 1;
smp_wmb();
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_post_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev->resetting = 0;
smp_wmb();
cm109_restore_state(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static struct usb_driver cm109_driver = {
.name = "cm109",
.probe = cm109_usb_probe,
.disconnect = cm109_usb_disconnect,
.suspend = cm109_usb_suspend,
.resume = cm109_usb_resume,
.reset_resume = cm109_usb_resume,
.pre_reset = cm109_usb_pre_reset,
.post_reset = cm109_usb_post_reset,
.id_table = cm109_usb_table,
.supports_autosuspend = 1,
};
static int __init cm109_select_keymap(void)
{
/* Load the phone keymap */
if (!strcasecmp(phone, "kip1000")) {
keymap = keymap_kip1000;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Komunikate KIP1000 phone loaded\n");
} else if (!strcasecmp(phone, "gtalk")) {
keymap = keymap_gtalk;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Genius G-talk phone loaded\n");
} else if (!strcasecmp(phone, "usbph01")) {
keymap = keymap_usbph01;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
} else if (!strcasecmp(phone, "atcom")) {
keymap = keymap_atcom;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for ATCom AU-100 phone loaded\n");
} else {
printk(KERN_ERR KBUILD_MODNAME ": "
"Unsupported phone: %s\n", phone);
return -EINVAL;
}
return 0;
}
static int __init cm109_init(void)
{
int err;
err = cm109_select_keymap();
if (err)
return err;
err = usb_register(&cm109_driver);
if (err)
return err;
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
return 0;
}
static void __exit cm109_exit(void)
{
usb_deregister(&cm109_driver);
}
module_init(cm109_init);
module_exit(cm109_exit);
MODULE_DEVICE_TABLE(usb, cm109_usb_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
]
|
| 53/4 |
2026/05/10 20:03 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 35566
output: 26
thoughts: 795
Thoughts: **Analyzing the cm109 Buzzer Control Flow**
Okay, let's break this down. First, I need to trace the calls. I see `cm109_submit_buzz_toggle` gets called from `cm109_toggle_buzzer_async` and `cm109_urb_ctl_callback`. Now, specifically in `cm109_urb_ctl_callback`, the logic is key. If there's an error (`status` is non-zero) _or_ `dev->buzzer_pending` is set, then `cm109_submit_buzz_toggle(dev)` gets called.
Wait, let me just confirm this. Yep, if `status` is non-zero, it resubmits `urb_ctl` via `cm109_submit_buzz_toggle(dev)`. But if `status` equals `-ESHUTDOWN`, it *returns* immediately. So, if the error isn't a shutdown signal, the control URB is resubmitted. The question is why resubmit on error? I’m guessing it's a retry mechanism, makes sense.
With my change, `cm109_submit_buzz_toggle` also updates `HID_OR1` and `HID_OR2` with `dev->keybit`. Now, is this correct? Yes, it is. Because `dev->keybit` reflects the current keybit, resending it is safe and ensures the correct state is maintained.
Now, let's look at where `cm109_toggle_buzzer_async` is called from. I see it's called from `cm109_input_ev` (for SND_TONE and SND_BELL events) and from `cm109_restore_state`. In the `restore_state` function, it's explicitly called to kick off regular URB submissions. This is important: it means that the asynchronous toggle function is the entry point to both control URB submission and, subsequently, the IRQ URB submission. Perfect, it is working as intended.
Just one final point to check, is `cm109_submit_ctl` called from anywhere else? I need to know where all the submissions are coming from, let me make sure I cover all bases.
|
| 54/4 |
2026/05/10 20:05 |
tool |
codesearch-find-references |
0m
Args: map[Name:cm109_submit_ctl]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:cm109_urb_irq_callback SourceFile:drivers/input/misc/cm109.c SourceLine:431]] TruncatedOutput:false]
|
| 55/4 |
2026/05/10 20:05 |
llm |
patch-generator |
1m
Model:
Tokens:
input: 36965
output: 21
thoughts: 2533
Thoughts: **Analysis of USB Control URB Submission in cm109 Driver**
Okay, so I'm diving back into this `cm109` driver code, specifically the control URB submission logic. My initial thought was that `cm109_submit_ctl` is only called from `cm109_urb_irq_callback`. But, no, it's called from `cm109_urb_ctl_callback` too. Let's see if the fix is correct. I see that `cm109_submit_buzz_toggle` now moves the setting of `HID_OR1` and `HID_OR2` into the `cm109_submit_buzz_toggle` function. The original version was setting the buzzer state in `cm109_submit_ctl`.
Now the question is, am I still protecting the lock correctly? `cm109_submit_buzz_toggle` doesn't take the `ctl_submit_lock`, so it *must* be called with the lock already held. I trace the calls: `cm109_submit_ctl` does indeed take the lock before calling `cm109_submit_buzz_toggle`. Also, `cm109_toggle_buzzer_async` also takes the lock before calling `cm109_submit_buzz_toggle`. Looks safe from the code.
I need to confirm if `dev->buzzer_pending` is correctly managed. Is it cleared at the right times? `cm109_submit_ctl` *does* clear it, as does `cm109_urb_ctl_callback` before calling `cm109_submit_buzz_toggle`. However, `cm109_toggle_buzzer_async` doesn't clear it when `ctl_urb_pending` is 0.
Wait, is that a problem? If `ctl_urb_pending` is zero, we're submitting the URB for the first time or after a previous completion, and in this case, `buzzer_pending` should definitely be zero. If not, something went wrong. But `cm109_submit_ctl` *does* reset that flag. But to be super clear I could add this reset in `cm109_toggle_buzzer_async`, but it seems like it's redundant because the code path is already consistent elsewhere.
Okay, let me review the diff again, really carefully. I think it looks clean and correct, given the control flow. The diff also moves the setting of `HID_OR1` and `HID_OR2` into the helper function, which makes sense.
Wait, I need to consider `cm109_urb_ctl_callback` calling `cm109_submit_buzz_toggle`. What is this callback doing with `status`? Right, if there's a non-zero `status`, it means the previous `urb_ctl` failed, and we resubmit with the *current* `keybit` values from the device struct, so that should be okay.
And lastly, consider when `cm109_toggle_buzzer_async` is called. That also handles the `keybit`. Correct.
I think I've covered all the potential call paths and locking scenarios. Time to review the code one last time. It seems robust.
|
| 56/4 |
2026/05/10 20:06 |
tool |
codesearch-find-references |
0m
Error:
Args:
Results: map[References:<nil> TruncatedOutput:false]
|
| 57/4 |
2026/05/10 20:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 40271
output: 28
thoughts: 20
Thoughts: Here's my understanding of the thought process, summarized as you requested:
**Deconstructing `urb_ctl`**
Okay, so I'm looking at something and my mind immediately jumps to `urb_ctl`. I recognize that this is a field, right? Specifically, it's a field *within* a `struct cm109_dev`. It's not a standalone entity, it's nested. That's a key distinction – it has context. Knowing this, I need to understand what `cm109_dev` represents to fully grasp the significance and purpose of this `urb_ctl` field. Is it a device structure? A driver-specific representation? That’s my next line of inquiry.
|
| 58/4 |
2026/05/10 20:06 |
tool |
codesearch-find-references |
0m
Args: map[Name:cm109_dev::urb_ctl]
Results: map[References:[map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_input_open SourceFile:drivers/input/misc/cm109.c SourceLine:566] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_stop_traffic SourceFile:drivers/input/misc/cm109.c SourceLine:518] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_submit_buzz_toggle SourceFile:drivers/input/misc/cm109.c SourceLine:351] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_submit_ctl SourceFile:drivers/input/misc/cm109.c SourceLine:380] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_cleanup SourceFile:drivers/input/misc/cm109.c SourceLine:664] map[ReferenceKind:writes ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:733] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:734] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:760] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:763] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:764] map[ReferenceKind:reads ReferencingEntityKind:function ReferencingEntityName:cm109_usb_probe SourceFile:drivers/input/misc/cm109.c SourceLine:765]] TruncatedOutput:false]
|
| 59/4 |
2026/05/10 20:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 41123
output: 27
thoughts: 14
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience, and delivered as a first-person summary:
**Initial Checkpoint: cm109_input_open**
Alright, let's dive in. First order of business, my mental checklist flags `cm109_input_open`. That's where we start, the foundation. I need to quickly ascertain the state of that input; I have to confirm the file's current status. Is it open? If so, with what permissions? Read? Write? Appending? Any locks in place that I should be aware of? This initial check is critical; it informs every subsequent step in my analysis. I have to avoid making assumptions here; a cursory glance might suffice if it were a simple script but with the complexities I often work with I need to ensure there are no surprises or assumptions in my process, as I know that can trip me up later. I'll need to know which checks should be run on the file before it can be trusted as operational, I'll need to know.
|
| 60/4 |
2026/05/10 20:06 |
tool |
read-file |
0m
Args: map[File:drivers/input/misc/cm109.c]
Results: map[Contents:// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the VoIP USB phones with CM109 chipsets.
*
* Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
*/
/*
* Tested devices:
* - Komunikate KIP1000
* - Genius G-talk
* - Allied-Telesis Corega USBPH01
* - ...
*
* This driver is based on the yealink.c driver
*
* Thanks to:
* - Authors of yealink.c
* - Thomas Reitmayr
* - Oliver Neukum for good review comments and code
* - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
* - Dmitry Torokhov for valuable input and review
*
* Todo:
* - Read/write EEPROM
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/rwsem.h>
#include <linux/usb/input.h>
#define DRIVER_VERSION "20080805"
#define DRIVER_AUTHOR "Alfred E. Heggestad"
#define DRIVER_DESC "CM109 phone driver"
static char *phone = "kip1000";
module_param(phone, charp, S_IRUSR);
MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
enum {
/* HID Registers */
HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
HID_OR1 = 0x01, /* GPO - General Purpose Output */
HID_OR2 = 0x02, /* Set GPIO to input/output mode */
HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
/* HID_IR0 */
RECORD_MUTE = 1 << 3,
PLAYBACK_MUTE = 1 << 2,
VOLUME_DOWN = 1 << 1,
VOLUME_UP = 1 << 0,
/* HID_OR0 */
/* bits 7-6
0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
and SPDIF
1: HID_OR0-3 are used as generic HID registers
2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
EEPROM_DATA0-1, EEPROM_CTRL (see Note)
3: Reserved
*/
HID_OR_GPO_BUZ_SPDIF = 0 << 6,
HID_OR_GENERIC_HID_REG = 1 << 6,
HID_OR_MAP_MCU_EEPROM = 2 << 6,
BUZZER_ON = 1 << 5,
/* up to 256 normal keys, up to 15 special key combinations */
KEYMAP_SIZE = 256 + 15,
};
/* CM109 protocol packet */
struct cm109_ctl_packet {
u8 byte[4];
} __attribute__ ((packed));
enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
/* CM109 device structure */
struct cm109_dev {
struct input_dev *idev; /* input device */
struct usb_device *udev; /* usb device */
struct usb_interface *intf;
/* irq input channel */
struct cm109_ctl_packet *irq_data;
dma_addr_t irq_dma;
struct urb *urb_irq;
/* control output channel */
struct cm109_ctl_packet *ctl_data;
dma_addr_t ctl_dma;
struct usb_ctrlrequest *ctl_req;
struct urb *urb_ctl;
/*
* The 3 bitfields below are protected by ctl_submit_lock.
* They have to be separate since they are accessed from IRQ
* context.
*/
unsigned irq_urb_pending:1; /* irq_urb is in flight */
unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
unsigned buzzer_pending:1; /* need to issue buzz command */
spinlock_t ctl_submit_lock;
unsigned char buzzer_state; /* on/off */
/* flags */
unsigned open:1;
unsigned resetting:1;
unsigned shutdown:1;
/* This mutex protects writes to the above flags */
struct mutex pm_mutex;
unsigned short keymap[KEYMAP_SIZE];
char phys[64]; /* physical device path */
int key_code; /* last reported key */
int keybit; /* 0=new scan 1,2,4,8=scan columns */
u8 gpi; /* Cached value of GPI (high nibble) */
};
/******************************************************************************
* CM109 key interface
*****************************************************************************/
static unsigned short special_keymap(int code)
{
if (code > 0xff) {
switch (code - 0xff) {
case RECORD_MUTE: return KEY_MICMUTE;
case PLAYBACK_MUTE: return KEY_MUTE;
case VOLUME_DOWN: return KEY_VOLUMEDOWN;
case VOLUME_UP: return KEY_VOLUMEUP;
}
}
return KEY_RESERVED;
}
/* Map device buttons to internal key events.
*
* The "up" and "down" keys, are symbolised by arrows on the button.
* The "pickup" and "hangup" keys are symbolised by a green and red phone
* on the button.
Komunikate KIP1000 Keyboard Matrix
-> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
| | | |
<- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
| | | |
END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
| | | |
OK -- * -- 0 -- # --> GPI pin 7 (0x80)
| | | |
/|\ /|\ /|\ /|\
| | | |
GPO
pin: 3 2 1 0
0x8 0x4 0x2 0x1
*/
static unsigned short keymap_kip1000(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x14: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x11: return KEY_NUMERIC_3; /* 3 */
case 0x24: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x21: return KEY_NUMERIC_6; /* 6 */
case 0x44: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x41: return KEY_NUMERIC_9; /* 9 */
case 0x81: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x88: return KEY_ENTER; /* pickup */
case 0x48: return KEY_ESC; /* hangup */
case 0x28: return KEY_LEFT; /* IN */
case 0x18: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
Contributed by Shaun Jackman <sjackman@gmail.com>
Genius G-Talk keyboard matrix
0 1 2 3
4: 0 4 8 Talk
5: 1 5 9 End
6: 2 6 # Up
7: 3 7 * Down
*/
static unsigned short keymap_gtalk(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0;
case 0x21: return KEY_NUMERIC_1;
case 0x41: return KEY_NUMERIC_2;
case 0x81: return KEY_NUMERIC_3;
case 0x12: return KEY_NUMERIC_4;
case 0x22: return KEY_NUMERIC_5;
case 0x42: return KEY_NUMERIC_6;
case 0x82: return KEY_NUMERIC_7;
case 0x14: return KEY_NUMERIC_8;
case 0x24: return KEY_NUMERIC_9;
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* Talk (green handset) */
case 0x28: return KEY_ESC; /* End (red handset) */
case 0x48: return KEY_UP; /* Menu up (rocker switch) */
case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
default: return special_keymap(scancode);
}
}
/*
* Keymap for Allied-Telesis Corega USBPH01
* http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
*
* Contributed by july@nat.bg
*/
static unsigned short keymap_usbph01(int scancode)
{
switch (scancode) {
case 0x11: return KEY_NUMERIC_0; /* 0 */
case 0x21: return KEY_NUMERIC_1; /* 1 */
case 0x41: return KEY_NUMERIC_2; /* 2 */
case 0x81: return KEY_NUMERIC_3; /* 3 */
case 0x12: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x42: return KEY_NUMERIC_6; /* 6 */
case 0x82: return KEY_NUMERIC_7; /* 7 */
case 0x14: return KEY_NUMERIC_8; /* 8 */
case 0x24: return KEY_NUMERIC_9; /* 9 */
case 0x44: return KEY_NUMERIC_POUND; /* # */
case 0x84: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* IN */
case 0x88: return KEY_RIGHT; /* OUT */
default: return special_keymap(scancode);
}
}
/*
* Keymap for ATCom AU-100
* http://www.atcom.cn/products.html
* http://www.packetizer.com/products/au100/
* http://www.voip-info.org/wiki/view/AU-100
*
* Contributed by daniel@gimpelevich.san-francisco.ca.us
*/
static unsigned short keymap_atcom(int scancode)
{
switch (scancode) { /* phone key: */
case 0x82: return KEY_NUMERIC_0; /* 0 */
case 0x11: return KEY_NUMERIC_1; /* 1 */
case 0x12: return KEY_NUMERIC_2; /* 2 */
case 0x14: return KEY_NUMERIC_3; /* 3 */
case 0x21: return KEY_NUMERIC_4; /* 4 */
case 0x22: return KEY_NUMERIC_5; /* 5 */
case 0x24: return KEY_NUMERIC_6; /* 6 */
case 0x41: return KEY_NUMERIC_7; /* 7 */
case 0x42: return KEY_NUMERIC_8; /* 8 */
case 0x44: return KEY_NUMERIC_9; /* 9 */
case 0x84: return KEY_NUMERIC_POUND; /* # */
case 0x81: return KEY_NUMERIC_STAR; /* * */
case 0x18: return KEY_ENTER; /* pickup */
case 0x28: return KEY_ESC; /* hangup */
case 0x48: return KEY_LEFT; /* left arrow */
case 0x88: return KEY_RIGHT; /* right arrow */
default: return special_keymap(scancode);
}
}
static unsigned short (*keymap)(int) = keymap_kip1000;
/*
* Completes a request by converting the data into events for the
* input subsystem.
*/
static void report_key(struct cm109_dev *dev, int key)
{
struct input_dev *idev = dev->idev;
if (dev->key_code >= 0) {
/* old key up */
input_report_key(idev, dev->key_code, 0);
}
dev->key_code = key;
if (key >= 0) {
/* new valid key */
input_report_key(idev, key, 1);
}
input_sync(idev);
}
/*
* Converts data of special key presses (volume, mute) into events
* for the input subsystem, sends press-n-release for mute keys.
*/
static void cm109_report_special(struct cm109_dev *dev)
{
static const u8 autorelease = RECORD_MUTE | PLAYBACK_MUTE;
struct input_dev *idev = dev->idev;
u8 data = dev->irq_data->byte[HID_IR0];
unsigned short keycode;
int i;
for (i = 0; i < 4; i++) {
keycode = dev->keymap[0xff + BIT(i)];
if (keycode == KEY_RESERVED)
continue;
input_report_key(idev, keycode, data & BIT(i));
if (data & autorelease & BIT(i)) {
input_sync(idev);
input_report_key(idev, keycode, 0);
}
}
input_sync(idev);
}
/******************************************************************************
* CM109 usb communication interface
*****************************************************************************/
static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
{
int error;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
static void cm109_submit_ctl(struct cm109_dev *dev)
{
int error;
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
/*
* IRQ handler
*/
static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
dev->irq_data->byte[1],
dev->irq_data->byte[2],
dev->irq_data->byte[3],
dev->keybit);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
goto out;
}
/* Special keys */
cm109_report_special(dev);
/* Scan key column */
if (dev->keybit == 0xf) {
/* Any changes ? */
if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
goto out;
dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
dev->keybit = 0x1;
} else {
report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
dev->keybit <<= 1;
if (dev->keybit > 0x8)
dev->keybit = 0xf;
}
out:
cm109_submit_ctl(dev);
}
static void cm109_urb_ctl_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
dev->ctl_data->byte[1],
dev->ctl_data->byte[2],
dev->ctl_data->byte[3]);
if (status) {
if (status == -ESHUTDOWN)
return;
dev_err_ratelimited(&dev->intf->dev, "%s: urb status %d\n",
__func__, status);
}
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_pending || status) {
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
} else if (likely(!dev->irq_urb_pending)) {
/* ask for key data */
dev->irq_urb_pending = 1;
error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_irq) failed %d\n",
__func__, error);
}
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
dev->buzzer_pending = 1;
} else {
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
{
int error;
if (on)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
error = usb_control_msg(dev->udev,
usb_sndctrlpipe(dev->udev, 0),
dev->ctl_req->bRequest,
dev->ctl_req->bRequestType,
le16_to_cpu(dev->ctl_req->wValue),
le16_to_cpu(dev->ctl_req->wIndex),
dev->ctl_data,
USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
if (error < 0 && error != -EINTR)
dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
__func__, error);
}
static void cm109_stop_traffic(struct cm109_dev *dev)
{
dev->shutdown = 1;
/*
* Make sure other CPUs see this
*/
smp_wmb();
usb_kill_urb(dev->urb_ctl);
usb_kill_urb(dev->urb_irq);
cm109_toggle_buzzer_sync(dev, 0);
dev->shutdown = 0;
smp_wmb();
}
static void cm109_restore_state(struct cm109_dev *dev)
{
if (dev->open) {
/*
* Restore buzzer state.
* This will also kick regular URB submission
*/
cm109_toggle_buzzer_async(dev);
}
}
/******************************************************************************
* input event interface
*****************************************************************************/
static int cm109_input_open(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
int error;
error = usb_autopm_get_interface(dev->intf);
if (error < 0) {
dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
__func__, error);
return error;
}
scoped_guard(mutex, &dev->pm_mutex) {
dev->buzzer_state = 0;
dev->key_code = -1; /* no keys pressed */
dev->keybit = 0xf;
/* issue INIT */
dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
if (!error) {
dev->open = 1;
return 0;
}
}
dev->ctl_urb_pending = 0;
usb_autopm_put_interface(dev->intf);
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
return error;
}
static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
scoped_guard(mutex, &dev->pm_mutex) {
/*
* Once we are here event delivery is stopped so we
* don't need to worry about someone starting buzzer
* again
*/
cm109_stop_traffic(dev);
dev->open = 0;
}
usb_autopm_put_interface(dev->intf);
}
static int cm109_input_ev(struct input_dev *idev, unsigned int type,
unsigned int code, int value)
{
struct cm109_dev *dev = input_get_drvdata(idev);
dev_dbg(&dev->intf->dev,
"input_ev: type=%u code=%u value=%d\n", type, code, value);
if (type != EV_SND)
return -EINVAL;
switch (code) {
case SND_TONE:
case SND_BELL:
dev->buzzer_state = !!value;
if (!dev->resetting)
cm109_toggle_buzzer_async(dev);
return 0;
default:
return -EINVAL;
}
}
/******************************************************************************
* Linux interface and usb initialisation
*****************************************************************************/
struct driver_info {
char *name;
};
static const struct driver_info info_cm109 = {
.name = "CM109 USB driver",
};
enum {
VENDOR_ID = 0x0d8c, /* C-Media Electronics */
PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
};
/* table of devices that work with this driver */
static const struct usb_device_id cm109_usb_table[] = {
{
.match_flags = USB_DEVICE_ID_MATCH_DEVICE |
USB_DEVICE_ID_MATCH_INT_INFO,
.idVendor = VENDOR_ID,
.idProduct = PRODUCT_ID_CM109,
.bInterfaceClass = USB_CLASS_HID,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
.driver_info = (kernel_ulong_t) &info_cm109
},
/* you can add more devices here with product ID 0x0008 - 0x000f */
{ }
};
static void cm109_usb_cleanup(struct cm109_dev *dev)
{
kfree(dev->ctl_req);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->ctl_data, dev->ctl_dma);
usb_free_coherent(dev->udev, USB_PKT_LEN, dev->irq_data, dev->irq_dma);
usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
kfree(dev);
}
static void cm109_usb_disconnect(struct usb_interface *interface)
{
struct cm109_dev *dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
input_unregister_device(dev->idev);
cm109_usb_cleanup(dev);
}
static int cm109_usb_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct driver_info *nfo = (struct driver_info *)id->driver_info;
struct usb_host_interface *interface;
struct usb_endpoint_descriptor *endpoint;
struct cm109_dev *dev;
struct input_dev *input_dev = NULL;
int ret, pipe, i;
int error = -ENOMEM;
interface = intf->cur_altsetting;
if (interface->desc.bNumEndpoints < 1)
return -ENODEV;
endpoint = &interface->endpoint[0].desc;
if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
dev = kzalloc_obj(*dev);
if (!dev)
return -ENOMEM;
spin_lock_init(&dev->ctl_submit_lock);
mutex_init(&dev->pm_mutex);
dev->udev = udev;
dev->intf = intf;
dev->idev = input_dev = input_allocate_device();
if (!input_dev)
goto err_out;
/* allocate usb buffers */
dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->irq_dma);
if (!dev->irq_data)
goto err_out;
dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
GFP_KERNEL, &dev->ctl_dma);
if (!dev->ctl_data)
goto err_out;
dev->ctl_req = kmalloc_obj(*(dev->ctl_req));
if (!dev->ctl_req)
goto err_out;
/* allocate urb structures */
dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_irq)
goto err_out;
dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb_ctl)
goto err_out;
/* get a handle to the interrupt data pipe */
pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
ret = usb_maxpacket(udev, pipe);
if (ret != USB_PKT_LEN)
dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
ret, USB_PKT_LEN);
/* initialise irq urb */
usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
USB_PKT_LEN,
cm109_urb_irq_callback, dev, endpoint->bInterval);
dev->urb_irq->transfer_dma = dev->irq_dma;
dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_irq->dev = udev;
/* initialise ctl urb */
dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
USB_DIR_OUT;
dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
dev->ctl_req->wValue = cpu_to_le16(0x200);
dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
(void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
cm109_urb_ctl_callback, dev);
dev->urb_ctl->transfer_dma = dev->ctl_dma;
dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->urb_ctl->dev = udev;
/* find out the physical bus location */
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
/* register settings for the input device */
input_dev->name = nfo->name;
input_dev->phys = dev->phys;
usb_to_input_id(udev, &input_dev->id);
input_dev->dev.parent = &intf->dev;
input_set_drvdata(input_dev, dev);
input_dev->open = cm109_input_open;
input_dev->close = cm109_input_close;
input_dev->event = cm109_input_ev;
input_dev->keycode = dev->keymap;
input_dev->keycodesize = sizeof(unsigned char);
input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
/* register available key events */
for (i = 0; i < KEYMAP_SIZE; i++) {
unsigned short k = keymap(i);
dev->keymap[i] = k;
__set_bit(k, input_dev->keybit);
}
__clear_bit(KEY_RESERVED, input_dev->keybit);
error = input_register_device(dev->idev);
if (error)
goto err_out;
usb_set_intfdata(intf, dev);
return 0;
err_out:
input_free_device(input_dev);
cm109_usb_cleanup(dev);
return error;
}
static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
guard(mutex)(&dev->pm_mutex);
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_resume(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev_info(&intf->dev, "cm109: usb_resume\n");
guard(mutex)(&dev->pm_mutex);
cm109_restore_state(dev);
return 0;
}
static int cm109_usb_pre_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
mutex_lock(&dev->pm_mutex);
/*
* Make sure input events don't try to toggle buzzer
* while we are resetting
*/
dev->resetting = 1;
smp_wmb();
cm109_stop_traffic(dev);
return 0;
}
static int cm109_usb_post_reset(struct usb_interface *intf)
{
struct cm109_dev *dev = usb_get_intfdata(intf);
dev->resetting = 0;
smp_wmb();
cm109_restore_state(dev);
mutex_unlock(&dev->pm_mutex);
return 0;
}
static struct usb_driver cm109_driver = {
.name = "cm109",
.probe = cm109_usb_probe,
.disconnect = cm109_usb_disconnect,
.suspend = cm109_usb_suspend,
.resume = cm109_usb_resume,
.reset_resume = cm109_usb_resume,
.pre_reset = cm109_usb_pre_reset,
.post_reset = cm109_usb_post_reset,
.id_table = cm109_usb_table,
.supports_autosuspend = 1,
};
static int __init cm109_select_keymap(void)
{
/* Load the phone keymap */
if (!strcasecmp(phone, "kip1000")) {
keymap = keymap_kip1000;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Komunikate KIP1000 phone loaded\n");
} else if (!strcasecmp(phone, "gtalk")) {
keymap = keymap_gtalk;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Genius G-talk phone loaded\n");
} else if (!strcasecmp(phone, "usbph01")) {
keymap = keymap_usbph01;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
} else if (!strcasecmp(phone, "atcom")) {
keymap = keymap_atcom;
printk(KERN_INFO KBUILD_MODNAME ": "
"Keymap for ATCom AU-100 phone loaded\n");
} else {
printk(KERN_ERR KBUILD_MODNAME ": "
"Unsupported phone: %s\n", phone);
return -EINVAL;
}
return 0;
}
static int __init cm109_init(void)
{
int err;
err = cm109_select_keymap();
if (err)
return err;
err = usb_register(&cm109_driver);
if (err)
return err;
printk(KERN_INFO KBUILD_MODNAME ": "
DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
return 0;
}
static void __exit cm109_exit(void)
{
usb_deregister(&cm109_driver);
}
module_init(cm109_init);
module_exit(cm109_exit);
MODULE_DEVICE_TABLE(usb, cm109_usb_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
]
|
| 61/4 |
2026/05/10 20:06 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 51335
output: 24
thoughts: 4068
Thoughts: **Analysis of the CM109 Driver's URB State Machine and Race Condition Fix**
Alright, let's break this down. The core issue revolves around a race condition within the CM109 driver, specifically related to the management of USB Request Blocks (URBs) for control and interrupt transfers. We have `urb_ctl` for control messages (like buzzer toggling), and `urb_irq` for interrupt messages (like key matrix scans). The intended operation is an alternating sequence: `urb_ctl` submits, then upon completion, submits `urb_irq`, then completion, and back to submitting `urb_ctl`.
The problem? `cm109_toggle_buzzer_async` can bypass this sequence and submit `urb_ctl` directly if `dev->ctl_urb_pending` is 0. This can lead to both `urb_ctl` and `urb_irq` being active at the same time. Then, if `urb_irq` completes first, its handler `cm109_urb_irq_callback` calls `cm109_submit_ctl` which would trigger another `usb_submit_urb(dev->urb_ctl)`, but now it’s already active. Boom.
The fix involves a few key changes:
1. **Check and Defer in `cm109_submit_ctl`**: Before submitting `urb_ctl`, `cm109_submit_ctl` now *checks* `dev->ctl_urb_pending`. If an `urb_ctl` is already pending, it sets `dev->buzzer_pending = 1` and returns. This defers the submission and avoids concurrency.
2. **Ensure Correct Data in `cm109_submit_buzz_toggle`**: `cm109_submit_buzz_toggle` now *copies the key matrix column information* from `dev->keybit` to `HID_OR1` and `HID_OR2`. This is crucial. `cm109_submit_buzz_toggle` is the actual submission function. This function may be called from within a "control URB complete" handler, `cm109_urb_ctl_callback`, after `urb_ctl` completes or after a call to `cm109_toggle_buzzer_async`. Without updating `HID_OR1` and `HID_OR2` here, the key matrix column might be lost on any deferred submission.
3. **Refactor `cm109_submit_ctl`**: Now, `cm109_submit_ctl` can simply call `cm109_submit_buzz_toggle` after it has checked the pending flags, which makes the code more clean and less error-prone. This removes the code duplication.
Now, as an expert, I'm confident in the changes. The race condition is eliminated, and the `dev->keybit` is guaranteed to be up-to-date.
One note on the error handling, if `usb_submit_urb` fails, the existing code doesn't clear `dev->ctl_urb_pending`, so it remains in a bad state. It's a pre-existing issue, and I'm treating the prompt as asking me to fix issues in the provided file only.
|
| 62/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Args: map[Expression:cm109_submit_buzz_toggle]
Results: map[Output:drivers/input/misc/cm109.c=316=static void cm109_report_special(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-341-
drivers/input/misc/cm109.c:342:static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
drivers/input/misc/cm109.c-343-{
--
drivers/input/misc/cm109.c=434=static void cm109_urb_ctl_callback(struct urb *urb)
--
drivers/input/misc/cm109.c-462- dev->ctl_urb_pending = 1;
drivers/input/misc/cm109.c:463: cm109_submit_buzz_toggle(dev);
drivers/input/misc/cm109.c-464- } else if (likely(!dev->irq_urb_pending)) {
--
drivers/input/misc/cm109.c=475=static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-483- dev->ctl_urb_pending = 1;
drivers/input/misc/cm109.c:484: cm109_submit_buzz_toggle(dev);
drivers/input/misc/cm109.c-485- }
]
|
| 63/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 56536
output: 22
thoughts: 20
Thoughts: Here's my thought process, summarized as you requested:
**Verification Checkpoint**
Okay, hold on a second. I believe I just implemented that modification using `codeeditor`. Before I move forward, it's crucial to confirm the change was successfully applied. The next logical step is to verify the contents of the target file to ensure the desired modification is now present. Double-checking is essential to avoid any unexpected behavior down the line, especially considering the potential complexity of the task I'm working on. No point in continuing if the underlying change isn't as expected, because then there may be later issues.
|
| 64/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Args: map[Expression:cm109_submit_ctl]
Results: map[Output:drivers/input/misc/cm109.c=342=static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-357-
drivers/input/misc/cm109.c:358:static void cm109_submit_ctl(struct cm109_dev *dev)
drivers/input/misc/cm109.c-359-{
--
drivers/input/misc/cm109.c=390=static void cm109_urb_irq_callback(struct urb *urb)
--
drivers/input/misc/cm109.c-430- out:
drivers/input/misc/cm109.c:431: cm109_submit_ctl(dev);
drivers/input/misc/cm109.c-432-}
]
|
| 65/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 56945
output: 24
thoughts: 21
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the `cm109_submit_ctl` Implementation**
Okay, first things first, let's dive into that `cm109_submit_ctl` function implementation. I need to refresh my memory on the specifics of how it's handling submissions. I'm primarily interested in the control flow. Are we looking at any potential race conditions? What's the thread safety strategy? Does it employ any specific locking mechanisms, or are we relying on atomic operations? The comment block at the beginning might give me a quick overview of the intended behavior and any known limitations, but I'll need to confirm that against the code itself. I'll need to quickly understand:
* **Data Structures:** What structures does it interact with? Are we dealing with shared resources, queues, or anything that might require synchronization? I'll be looking for any indications of concurrent access patterns.
* **Error Handling:** How robust is the error handling? Does it check return codes appropriately? Are there any potential failure points that aren't gracefully handled, especially related to the submission queue or underlying system resources?
* **Resource Management:** Are there any memory leaks, or resource leaks present in this function? It will be useful to understand how resources are allocated, deallocated, and if there are any specific resource limits involved.
* **Dependencies:** What other modules or functions does it call? Any complex dependencies or nested calls will require further investigation.
* **Efficiency:** Is there any opportunity to improve efficiency? Could any of the operations be optimized? I'll probably be looking at how we submit things, and if any time is wasted on data transformation or processing that is unnecessary.
I'll quickly review the code, focusing on those aspects. It's a quick scan, looking for patterns and potential issues I'm already aware of, or have experienced. The goal is to get a high-level understanding of the submission control logic and identify any red flags quickly. From there, I can analyze specific sections more deeply if required.
|
| 66/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Args: map[Expression:static void cm109_submit_ctl]
Results: map[Output:drivers/input/misc/cm109.c=342=static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-357-
drivers/input/misc/cm109.c:358:static void cm109_submit_ctl(struct cm109_dev *dev)
drivers/input/misc/cm109.c-359-{
]
|
| 67/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 57563
output: 24
thoughts: 28
Thoughts: Okay, let's dive into this. Here's my take on those lines of code, presented as if they were my own internal thought process:
**Diving into the cm109 Driver: Lines 342-380**
Right, let's get down to brass tacks. I'm focusing in on lines 342 to 380 within `drivers/input/misc/cm109.c`. I have a feeling this section deals with either the device's initialization or the handling of a specific input event. Based on the naming convention, `cm109` points to some specific USB device and `misc` indicates something of a general purpose, rather than say a keyboard, mouse, or touchscreen.
I'm mentally preparing for a parsing task now. I'm expecting to see calls to `usb_control_msg` or `usb_bulk_msg`, I know that I will have to look for some bit-masking operations or conditional statements related to device status or incoming data. Depending on how sophisticated this device is, I might see the interrupt handling routines in this section.
I'll be looking specifically for any data structures being used here. A glance at the surrounding code might be needed to understand their definition and structure. I am mentally prepared for struct dereferencing and any potential bitwise manipulations necessary to decode the device's communication protocol. My hope is this is well documented.
Ultimately, I hope to understand the interaction logic to give a proper analysis of the code.
|
| 68/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Error:
Args: map[Expression:-n 342,380]
Results:
|
| 69/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 57953
output: 27
thoughts: 15
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Analyzing the Text File**
Alright, the task is to analyze a text file, I presume. My initial instinct is to reach for a fundamental tool, probably something like `read-file`. I'll use that to pull the file's contents into a workable format – likely individual lines. Once I have the lines, I can then begin the parsing, analysis, and data extraction that's needed. From here, I can decide what I'll do with the resulting data.
|
| 70/4 |
2026/05/10 20:07 |
tool |
read-file |
0m
Error:
Args: map[File:drivers/input/misc/cm109.c]
|
| 71/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 58150
output: 38
thoughts: 45
Thoughts: Okay, here's my summarized thought process:
**Running a Targeted Search on Modified File Content**
Right, I've got the updated content of that file loaded from before. No need to reload it. Now, what I need to do is quickly and efficiently zero in on the relevant sections. My usual approach for this is `grepper`. I'll whip up a regex to get the job done. I'm thinking I should search for `cm109_submit_ctl` along with the lines immediately following it. That should pinpoint the areas I'm interested in for this task.
|
| 72/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Error:
Args: map[Expression:cm109_submit_ctl.*\{(\n.*)*cm109_submit_buzz_toggle]
Results:
|
| 73/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 58373
output: 22
thoughts: 26
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Investigating a `cm109_submit_ctl` Issue: Leveraging `grepper` for Comprehensive Context**
Alright, I'm thinking I need to dive into this `cm109_submit_ctl` issue a bit deeper. Standard logs aren't cutting it, and I need a much broader view to understand the context surrounding the problem. My first instinct, given the nature of the systems I usually deal with, is to reach for a powerful and targeted approach.
`grepper` is perfect here. It's designed for exactly this kind of situation: rapid and intelligent searching across complex log structures. I know I can feed it a specific search term. The key here will be not just finding the event but seeing what *immediately* surrounds it. That `-A 20` flag is critical. Twenty lines of surrounding context should provide enough temporal and operational background to identify any adjacent actions or states that might be related to the issue. This isn't just about locating the string; it's about seeing the entire narrative arc of the operation at hand.
Using `grepper` with `cm109_submit_ctl` as the search term, with that adjacency, is the way to gain real insight here.
|
| 74/4 |
2026/05/10 20:07 |
tool |
grepper |
0m
Args: map[Expression:cm109_submit_ctl]
Results: map[Output:drivers/input/misc/cm109.c=342=static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
--
drivers/input/misc/cm109.c-357-
drivers/input/misc/cm109.c:358:static void cm109_submit_ctl(struct cm109_dev *dev)
drivers/input/misc/cm109.c-359-{
--
drivers/input/misc/cm109.c=390=static void cm109_urb_irq_callback(struct urb *urb)
--
drivers/input/misc/cm109.c-430- out:
drivers/input/misc/cm109.c:431: cm109_submit_ctl(dev);
drivers/input/misc/cm109.c-432-}
]
|
| 75/4 |
2026/05/10 20:07 |
llm |
patch-generator |
0m
Model:
Tokens:
input: 58949
output: 513
thoughts: 590
Thoughts: **My Reasoning Behind the Fix**
Okay, so `grepper` wasn't as helpful as I'd hoped in quickly referencing context, but I'm trusting my earlier `codeeditor` call was successful in loading the necessary files. Good, it returns an empty object on success. I need to make sure I haven't missed a step here. Let me check the prompt again: ah, yes, I need a detailed explanation of what I did and why. Specifically, I need to elaborate on details *not* present in the initial bug description, and include a pr_err call only if I removed a WARN_ON macro and the condition can legitimately happen, and check if the fix applies to similar cases. I didn't remove any WARN_ON macros, so I don't need to add a `pr_err` call. Also, I checked the other functions mentioned – `cm109_toggle_buzzer_async` and `cm109_urb_ctl_callback` – and they're fine.
Alright, time to structure the explanation.
1. First, the core of the fix is the check for `dev->ctl_urb_pending` inside `cm109_submit_ctl()`. If `ctl_urb_pending` is already set, it indicates that `urb_ctl` is currently active. This could be due to an out-of-band submission by `cm109_toggle_buzzer_async()`. To prevent the "URB submitted while active" crash, I set `dev->buzzer_pending = 1` to defer the submission and immediately return. This is the crucial protection.
2. Next, I had to ensure that when the deferred submission actually occurs, the device is sent the latest key matrix information. I updated `cm109_submit_buzz_toggle()` to copy the current `dev->keybit` to `HID_OR1` and `HID_OR2`. This guarantees that the most up-to-date state of the key matrix column is sent to the device when the completion handler eventually calls `cm109_submit_buzz_toggle()`, thus preventing any lost updates.
3. Thinking about the code structure, I realized `cm109_submit_ctl()` and `cm109_submit_buzz_toggle()` now prepare identical data for `urb_ctl`. So I refactored `cm109_submit_ctl()` to just call `cm109_submit_buzz_toggle()` after checking and updating the pending flags. This eliminates code duplication and makes the URB submission logic cleaner, more consistent, and simpler to reason about across the driver.
4. Finally, I verified the locking strategy. I confirmed that `cm109_submit_buzz_toggle()` is always called with `ctl_submit_lock` held, whether from `cm109_toggle_buzzer_async`, `cm109_urb_ctl_callback`, or now `cm109_submit_ctl()`. This makes reading `dev->keybit` and modifying `dev->ctl_data` safe from race conditions, crucial for ensuring data integrity and preventing further issues.
|
| 76/3 |
2026/05/10 20:07 |
action |
test-patch |
23m
Results: map[PatchDiff:diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..0114d7293 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -357,8 +360,6 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
static void cm109_submit_ctl(struct cm109_dev *dev)
{
- int error;
-
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
@@ -366,22 +367,15 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
+ cm109_submit_buzz_toggle(dev);
}
/*
TestError:]
|
| 77/1 |
2026/05/10 20:31 |
agent |
fixes-finder |
7m
Model:
Results: map[FixesHash:c04148f915e5ba7947752e6348e0da4cdab1329e]
Instruction:
You are an experienced Linux kernel developer tasked with identifying the commit
that introduced the bug being fixed. Identifying the correct buggy commit is crucial
for proper kernel maintenance (backporting to stable trees, etc.).
Your investigation strategy:
1. Examine the patch that fixes the bug. Use git tools (like git-log or git-blame)
to trace the history of the lines or functions modified by the patch.
2. Analyze the stack trace in the crash report. Identify the key files and functions
involved in the crash and investigate their history to see when the problematic
logic was introduced.
3. Compare the bug explanation with the commit history to find the point where
the described logic error first appeared.
A bug is typically introduced when a piece of code is first written, or when
a refactoring changed its logic in a way that introduced the bug.
Trace the history of relevant symbols or find when specific code patterns were introduced/removed.
You must provide exactly one bug-introducing commit hash.
If you are unable to confidently determine the bug-introducing commit after investigation,
return an empty string rather than guessing.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The crash is:
cm109 1-1:0.8: cm109_urb_irq_callback: urb status -71
------------[ cut here ]------------
URB ffff888046210500 submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x7b/0x18b0 drivers/usb/core/urb.c:379, CPU#1: swapper/1/0
Modules linked in:
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:usb_submit_urb+0x7e/0x18b0 drivers/usb/core/urb.c:379
Code: 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 17 72 06 fb 49 83 3e 00 74 40 e8 6c 63 9c fa 48 8d 3d 65 92 ff 08 48 89 de <67> 48 0f b9 3a b8 f0 ff ff ff eb 11 e8 51 63 9c fa eb 05 e8 4a 63
RSP: 0018:ffffc900001f8a00 EFLAGS: 00010046
RAX: ffffffff8727c654 RBX: ffff888046210500 RCX: ffff8880404b0000
RDX: 0000000000000100 RSI: ffff888046210500 RDI: ffffffff902758c0
RBP: 000000000000000f R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff5200003f140 R12: 0000000000000820
R13: ffff88804fd73030 R14: ffff888046210508 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff8880ebc29000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558409200a78 CR3: 0000000046b1e000 CR4: 00000000000006f0
Call Trace:
<IRQ>
cm109_submit_ctl drivers/input/misc/cm109.c:380 [inline]
cm109_urb_irq_callback+0x6e2/0xc90 drivers/input/misc/cm109.c:431
__usb_hcd_giveback_urb+0x376/0x540 drivers/usb/core/hcd.c:1657
dummy_timer+0xbc0/0x4650 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2011
handle_softirqs+0x22a/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1061
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ab 90 02 e9 d3 f5 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 53 33 21 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffc9000046fe20 EFLAGS: 00000202
RAX: 0000000000076dab RBX: ffffffff8199768a RCX: 0000000080000001
RDX: 0000000000000001 RSI: ffffffff8deddb4e RDI: ffffffff8c0ea120
RBP: ffffc9000046ff10 R08: ffff88807ee339db R09: 1ffff1100fdc673b
R10: dffffc0000000000 R11: ffffed100fdc673c R12: 0000000000000001
R13: 1ffff11008096000 R14: 0000000000000001 R15: 1ffff11008096000
arch_safe_halt arch/x86/kernel/process.c:766 [inline]
default_idle+0x9/0x20 arch/x86/kernel/process.c:767
default_idle_call+0x72/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x36a/0x5f0 kernel/sched/idle.c:352
cpu_startup_entry+0x43/0x60 kernel/sched/idle.c:451
start_secondary+0x101/0x110 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x147
</TASK>
----------------
Code disassembly (best guess):
0: 89 f0 mov %esi,%eax
2: 48 c1 e8 03 shr $0x3,%rax
6: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
b: 74 08 je 0x15
d: 4c 89 f7 mov %r14,%rdi
10: e8 17 72 06 fb call 0xfb06722c
15: 49 83 3e 00 cmpq $0x0,(%r14)
19: 74 40 je 0x5b
1b: e8 6c 63 9c fa call 0xfa9c638c
20: 48 8d 3d 65 92 ff 08 lea 0x8ff9265(%rip),%rdi # 0x8ff928c
27: 48 89 de mov %rbx,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 f0 ff ff ff mov $0xfffffff0,%eax
34: eb 11 jmp 0x47
36: e8 51 63 9c fa call 0xfa9c638c
3b: eb 05 jmp 0x42
3d: e8 .byte 0xe8
3e: 4a rex.WX
3f: 63 .byte 0x63
The explanation of the root cause is:
The root cause of this crash is a race condition in the `cm109` driver's URB state machine, where a control URB (`urb_ctl`) is submitted while it is already active.
### Root Cause Analysis
The driver uses two URBs to communicate with the device:
1. `urb_ctl` (Control OUT): Sends the current key matrix column (`dev->keybit`) and the buzzer state.
2. `urb_irq` (Interrupt IN): Reads the key matrix row state.
These URBs are designed to run in a continuous, strictly alternating loop:
- When `urb_ctl` completes, `cm109_urb_ctl_callback()` submits `urb_irq`.
- When `urb_irq` completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()` which submits `urb_ctl`.
However, the driver also supports sound events (`SND_BELL` / `SND_TONE`) to toggle the buzzer. When a sound event is received (e.g., when the syzkaller reproducer writes a `\a` BEL character to `/dev/tty1`), `cm109_input_ev()` calls `cm109_toggle_buzzer_async()`.
If `urb_ctl` is not currently pending (which is true 50% of the time, whenever `urb_irq` is in flight), `cm109_toggle_buzzer_async()` immediately submits `urb_ctl` out-of-band to update the buzzer state. This breaks the strict alternation and causes **both** `urb_irq` and `urb_ctl` to be active concurrently.
When `urb_irq` subsequently completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()`. `cm109_submit_ctl()` blindly assumes `urb_ctl` is idle, updates the DMA buffer, and calls `usb_submit_urb(dev->urb_ctl)`. Since `urb_ctl` is already active, the USB core catches it and triggers the `URB ... submitted while active` warning.
### Sequence of Events
1. The normal polling loop is running. `urb_irq` is submitted and active (`dev->irq_urb_pending = 1`, `dev->ctl_urb_pending = 0`).
2. A sound event is received. `cm109_toggle_buzzer_async()` is called.
3. Since `dev->ctl_urb_pending == 0`, it sets `dev->ctl_urb_pending = 1` and submits `urb_ctl` via `cm109_submit_buzz_toggle()`. Now **both** URBs are active.
4. `urb_irq` completes. `cm109_urb_irq_callback()` processes the key data and calls `cm109_submit_ctl()`.
5. `cm109_submit_ctl()` clears `dev->irq_urb_pending = 0`, sets `dev->ctl_urb_pending = 1`, and calls `usb_submit_urb(dev->urb_ctl)`.
6. **CRASH:** `urb_ctl` is already active from step 3.
### Proposed Fix
To fix this, `cm109_submit_ctl()` must check if `dev->ctl_urb_pending` is already set. If it is, it should defer the submission by setting `dev->buzzer_pending = 1` (which acts as a general "resubmit `urb_ctl`" flag in the completion handler) and return immediately.
Additionally, `cm109_submit_buzz_toggle()` (which is called by the completion handler when `buzzer_pending` is set) must be updated to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`, ensuring that the deferred key matrix column update is not lost.
Here is the logic for the fix:
```c
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -366,6 +369,11 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
```
The patch that fixes the bug is:
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..0114d7293 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -357,8 +360,6 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
static void cm109_submit_ctl(struct cm109_dev *dev)
{
- int error;
-
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
@@ -366,22 +367,15 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
+ cm109_submit_buzz_toggle(dev);
}
/*
Search for the commit(s) that introduced this bug.
|
| 78/2 |
2026/05/10 20:31 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 8220
output: 60
thoughts: 164
Thoughts: **Pinpointing the Bug in cm109.c**
Okay, so we've got a nasty bug report and it's pointing the finger at `drivers/input/misc/cm109.c`. Sounds like a race condition within the `cm109` driver's URB state machine. Specifically, `urb_ctl` is getting submitted while it's already active. Right, so we need to track down the commit that introduced this.
The fix, as I understand it, involves some modifications to the `cm109_submit_ctl` and `cm109_submit_buzz_toggle` functions. That narrows the focus considerably. My approach will be to dive into the history of `drivers/input/misc/cm109.c` with `git log`. I'll zero in on the changes to those two functions, `cm109_submit_ctl` and `cm109_submit_buzz_toggle`. Hopefully, by examining the commit history and the changes to those key functions, I can quickly identify the offending commit and, from there, understand the root cause of the race condition and the logic of the fix. It's a classic debugging scenario – trace the evolution, pinpoint the change, and then dissect the before-and-after to understand the problem.
|
| 79/2 |
2026/05/10 20:31 |
tool |
git-log |
0m
Error:
Args: map[PathPrefix:drivers/input/misc/cm109.c SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_submit_ctl]
Results:
|
| 80/2 |
2026/05/10 20:31 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 8748
output: 42
thoughts: 19
Thoughts: Okay, here's my summary, tailored as if I'm an expert in the field thinking through this:
**Streamlining Data References**
Right, so I'm thinking about the way we're handling symbols and their paths. It's becoming clear that for clarity and efficiency, I should be using `SymbolName` and `SourcePath` directly, without relying on that `PathPrefix` parameter in the context where I'm working now. The `PathPrefix` has its place, but in this specific part of the code/system, it’s just adding an unnecessary layer of indirection. It’s making things harder to reason about and potentially slowing things down. Going with the raw `SymbolName` and `SourcePath` will keep things cleaner and more intuitive. Seems obvious, really.
|
| 81/2 |
2026/05/10 20:31 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_submit_ctl]
Results: map[Output:d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
c04148f915e5 Input: add driver for USB VoIP phones with CM109 chipset
]
|
| 82/2 |
2026/05/10 20:32 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 9047
output: 27
thoughts: 21
Thoughts: Okay, let's dive into this. Here's my perspective on that commit:
**Analyzing Commit c04148f915e5**
Alright, so the first thing I'm doing is pulling up commit `c04148f915e5`. My initial pass is always about getting a high-level sense of the scope. Is this a minor bug fix, a feature addition, a refactor, or something more substantial? I’m looking at the description, the changed files, and any comments – anything to get a grasp of the intent.
Then, I'm likely moving on to scanning the diff itself. I'm not going line-by-line immediately unless the commit is tiny. I'm looking for patterns, for the *style* of the change. Is this consistent with the existing codebase? Are there any obvious code smells? Do the changes align with the project's architectural principles and coding conventions? I'm checking for things like indentation, naming, the presence of comments, and general readability. Are there any unnecessary complexities introduced, or are there clever optimizations?
I'm keeping my expert hat on, so I’m also mentally running through edge cases, potential performance implications, and security vulnerabilities that could arise from the changes. I want to see if the developer has accounted for these. Have they addressed dependencies correctly? Have they introduced any regressions? Does this change require updates to documentation or tests? If it's a critical area, I’m mentally constructing test cases to ensure that everything's covered.
Ultimately, I'm forming a judgment. Is this a good commit? Is it well-written, well-tested, and well-integrated? Does it add value or potentially introduce new issues? My summary will reflect my considered assessment, drawing on years of expertise and a deep understanding of the project's context.
|
| 83/2 |
2026/05/10 20:32 |
tool |
git-show |
0m
Args:
Results: map[Output:commit c04148f915e5ba7947752e6348e0da4cdab1329e
Author: Alfred E. Heggestad <aeh@db.org>
Date: Fri Aug 8 11:49:08 2008 -0400
Input: add driver for USB VoIP phones with CM109 chipset
Signed-off-by: Alfred E. Heggestad <aeh@db.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index e99b7882f382..199055db5082 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -180,6 +180,19 @@ config INPUT_YEALINK
To compile this driver as a module, choose M here: the module will be
called yealink.
+config INPUT_CM109
+ tristate "C-Media CM109 USB I/O Controller"
+ depends on EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to enable keyboard and buzzer functions of the
+ C-Media CM109 usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cm109.
+
config INPUT_UINPUT
tristate "User level driver support"
help
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index f48009b52226..d7db2aeb8a98 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_APANEL) += apanel.o
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 000000000000..404fd49243f8
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,884 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+/*
+ * Tested devices:
+ * - Komunikate KIP1000
+ * - Genius G-talk
+ * - Allied-Telesis Corega USBPH01
+ * - ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ * - Authors of yealink.c
+ * - Thomas Reitmayr
+ * - Oliver Neukum for good review comments and code
+ * - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ * - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ * - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define CM109_DEBUG 0
+
+#define DRIVER_VERSION "20080805"
+#define DRIVER_AUTHOR "Alfred E. Heggestad"
+#define DRIVER_DESC "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01}");
+
+enum {
+ /* HID Registers */
+ HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
+ HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
+ HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
+ HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
+ HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+ HID_OR1 = 0x01, /* GPO - General Purpose Output */
+ HID_OR2 = 0x02, /* Set GPIO to input/output mode */
+ HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
+
+ /* HID_IR0 */
+ RECORD_MUTE = 1 << 3,
+ PLAYBACK_MUTE = 1 << 2,
+ VOLUME_DOWN = 1 << 1,
+ VOLUME_UP = 1 << 0,
+
+ /* HID_OR0 */
+ /* bits 7-6
+ 0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+ and SPDIF
+ 1: HID_OR0-3 are used as generic HID registers
+ 2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+ EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+ 3: Reserved
+ */
+ HID_OR_GPO_BUZ_SPDIF = 0 << 6,
+ HID_OR_GENERIC_HID_REG = 1 << 6,
+ HID_OR_MAP_MCU_EEPROM = 2 << 6,
+
+ BUZZER_ON = 1 << 5,
+
+ /* up to 256 normal keys, up to 16 special keys */
+ KEYMAP_SIZE = 256 + 16,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+ u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf;
+
+ /* irq input channel */
+ struct cm109_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct cm109_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ dma_addr_t ctl_req_dma;
+ struct urb *urb_ctl;
+ /*
+ * The 3 bitfields below are protected by ctl_submit_lock.
+ * They have to be separate since they are accessed from IRQ
+ * context.
+ */
+ unsigned irq_urb_pending:1; /* irq_urb is in flight */
+ unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
+ unsigned buzzer_pending:1; /* need to issue buzz command */
+ spinlock_t ctl_submit_lock;
+
+ unsigned char buzzer_state; /* on/off */
+
+ /* flags */
+ unsigned open:1;
+ unsigned resetting:1;
+ unsigned shutdown:1;
+
+ /* This mutex protects writes to the above flags */
+ struct mutex pm_mutex;
+
+ unsigned short keymap[KEYMAP_SIZE];
+
+ char phys[64]; /* physical device path */
+ int key_code; /* last reported key */
+ int keybit; /* 0=new scan 1,2,4,8=scan columns */
+ u8 gpi; /* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+static unsigned short special_keymap(int code)
+{
+ if (code > 0xff) {
+ switch (code - 0xff) {
+ case RECORD_MUTE: return KEY_MUTE;
+ case PLAYBACK_MUTE: return KEY_MUTE;
+ case VOLUME_DOWN: return KEY_VOLUMEDOWN;
+ case VOLUME_UP: return KEY_VOLUMEUP;
+ }
+ }
+ return KEY_RESERVED;
+}
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+ -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
+ | | | |
+ <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
+ | | | |
+ END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
+ | | | |
+ OK -- * -- 0 -- # --> GPI pin 7 (0x80)
+ | | | |
+
+ /|\ /|\ /|\ /|\
+ | | | |
+GPO
+pin: 3 2 1 0
+ 0x8 0x4 0x2 0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+ switch (scancode) { /* phone key: */
+ case 0x82: return KEY_NUMERIC_0; /* 0 */
+ case 0x14: return KEY_NUMERIC_1; /* 1 */
+ case 0x12: return KEY_NUMERIC_2; /* 2 */
+ case 0x11: return KEY_NUMERIC_3; /* 3 */
+ case 0x24: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x21: return KEY_NUMERIC_6; /* 6 */
+ case 0x44: return KEY_NUMERIC_7; /* 7 */
+ case 0x42: return KEY_NUMERIC_8; /* 8 */
+ case 0x41: return KEY_NUMERIC_9; /* 9 */
+ case 0x81: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x88: return KEY_ENTER; /* pickup */
+ case 0x48: return KEY_ESC; /* hangup */
+ case 0x28: return KEY_LEFT; /* IN */
+ case 0x18: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ Contributed by Shaun Jackman <sjackman@gmail.com>
+
+ Genius G-Talk keyboard matrix
+ 0 1 2 3
+ 4: 0 4 8 Talk
+ 5: 1 5 9 End
+ 6: 2 6 # Up
+ 7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0;
+ case 0x21: return KEY_NUMERIC_1;
+ case 0x41: return KEY_NUMERIC_2;
+ case 0x81: return KEY_NUMERIC_3;
+ case 0x12: return KEY_NUMERIC_4;
+ case 0x22: return KEY_NUMERIC_5;
+ case 0x42: return KEY_NUMERIC_6;
+ case 0x82: return KEY_NUMERIC_7;
+ case 0x14: return KEY_NUMERIC_8;
+ case 0x24: return KEY_NUMERIC_9;
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* Talk (green handset) */
+ case 0x28: return KEY_ESC; /* End (red handset) */
+ case 0x48: return KEY_UP; /* Menu up (rocker switch) */
+ case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0; /* 0 */
+ case 0x21: return KEY_NUMERIC_1; /* 1 */
+ case 0x41: return KEY_NUMERIC_2; /* 2 */
+ case 0x81: return KEY_NUMERIC_3; /* 3 */
+ case 0x12: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x42: return KEY_NUMERIC_6; /* 6 */
+ case 0x82: return KEY_NUMERIC_7; /* 7 */
+ case 0x14: return KEY_NUMERIC_8; /* 8 */
+ case 0x24: return KEY_NUMERIC_9; /* 9 */
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* pickup */
+ case 0x28: return KEY_ESC; /* hangup */
+ case 0x48: return KEY_LEFT; /* IN */
+ case 0x88: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+ struct input_dev *idev = dev->idev;
+
+ if (dev->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, dev->key_code, 0);
+ }
+
+ dev->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key, 1);
+ }
+
+ input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+ int error;
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+#if CM109_DEBUG
+ info("### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x",
+ dev->irq_data->byte[0],
+ dev->irq_data->byte[1],
+ dev->irq_data->byte[2],
+ dev->irq_data->byte[3],
+ dev->keybit);
+#endif
+
+ if (status) {
+ if (status == -ESHUTDOWN)
+ return;
+ err("%s: urb status %d", __func__, status);
+ }
+
+ /* Special keys */
+ if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+ const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+ report_key(dev, dev->keymap[0xff + code]);
+ }
+
+ /* Scan key column */
+ if (dev->keybit == 0xf) {
+
+ /* Any changes ? */
+ if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+ goto out;
+
+ dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+ dev->keybit = 0x1;
+ } else {
+ report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+ dev->keybit <<= 1;
+ if (dev->keybit > 0x8)
+ dev->keybit = 0xf;
+ }
+
+ out:
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->irq_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d",
+ __func__, error);
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+#if CM109_DEBUG
+ info("### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]",
+ dev->ctl_data->byte[0],
+ dev->ctl_data->byte[1],
+ dev->ctl_data->byte[2],
+ dev->ctl_data->byte[3]);
+#endif
+
+ if (status)
+ err("%s: urb status %d", __func__, status);
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->ctl_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_pending) {
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ } else if (likely(!dev->irq_urb_pending)) {
+ /* ask for key data */
+ dev->irq_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+ if (error)
+ err("%s: usb_submit_urb (urb_irq) failed %d",
+ __func__, error);
+ }
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+ if (dev->ctl_urb_pending) {
+ /* URB completion will resubmit */
+ dev->buzzer_pending = 1;
+ } else {
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ }
+
+ spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+ int error;
+
+ if (on)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ dev->ctl_req->bRequest,
+ dev->ctl_req->bRequestType,
+ le16_to_cpu(dev->ctl_req->wValue),
+ le16_to_cpu(dev->ctl_req->wIndex),
+ dev->ctl_data,
+ USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+ if (error && error != EINTR)
+ err("%s: usb_control_msg() failed %d", __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+ dev->shutdown = 1;
+ /*
+ * Make sure other CPUs see this
+ */
+ smp_wmb();
+
+ usb_kill_urb(dev->urb_ctl);
+ usb_kill_urb(dev->urb_irq);
+
+ cm109_toggle_buzzer_sync(dev, 0);
+
+ dev->shutdown = 0;
+ smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+ if (dev->open) {
+ /*
+ * Restore buzzer state.
+ * This will also kick regular URB submission
+ */
+ cm109_toggle_buzzer_async(dev);
+ }
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+ int error;
+
+ error = usb_autopm_get_interface(dev->intf);
+ if (error < 0) {
+ err("%s - cannot autoresume, result %d",
+ __func__, error);
+ return error;
+ }
+
+ mutex_lock(&dev->pm_mutex);
+
+ dev->buzzer_state = 0;
+ dev->key_code = -1; /* no keys pressed */
+ dev->keybit = 0xf;
+
+ /* issue INIT */
+ dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ dev->ctl_data->byte[HID_OR3] = 0x00;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+ if (error)
+ err("%s: usb_submit_urb (urb_ctl) failed %d", __func__, error);
+ else
+ dev->open = 1;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ if (error)
+ usb_autopm_put_interface(dev->intf);
+
+ return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Once we are here event delivery is stopped so we
+ * don't need to worry about someone starting buzzer
+ * again
+ */
+ cm109_stop_traffic(dev);
+ dev->open = 0;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+#if CM109_DEBUG
+ info("input_ev: type=%u code=%u value=%d", type, code, value);
+#endif
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_TONE:
+ case SND_BELL:
+ dev->buzzer_state = !!value;
+ if (!dev->resetting)
+ cm109_toggle_buzzer_async(dev);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_cm109 = {
+ .name = "CM109 USB driver",
+};
+
+enum {
+ VENDOR_ID = 0x0d8c, /* C-Media Electronics */
+ PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = VENDOR_ID,
+ .idProduct = PRODUCT_ID_CM109,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t) &info_cm109
+ },
+ /* you can add more devices here with product ID 0x0008 - 0x000f */
+ { }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+ if (dev->ctl_req)
+ usb_buffer_free(dev->udev, sizeof(*(dev->ctl_req)),
+ dev->ctl_req, dev->ctl_req_dma);
+ if (dev->ctl_data)
+ usb_buffer_free(dev->udev, USB_PKT_LEN,
+ dev->ctl_data, dev->ctl_dma);
+ if (dev->irq_data)
+ usb_buffer_free(dev->udev, USB_PKT_LEN,
+ dev->irq_data, dev->irq_dma);
+
+ usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
+ usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
+ kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+ struct cm109_dev *dev = usb_get_intfdata(interface);
+
+ usb_set_intfdata(interface, NULL);
+ input_unregister_device(dev->idev);
+ cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct cm109_dev *dev;
+ struct input_dev *input_dev = NULL;
+ int ret, pipe, i;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->ctl_submit_lock);
+ mutex_init(&dev->pm_mutex);
+
+ dev->udev = udev;
+ dev->intf = intf;
+
+ dev->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ goto err_out;
+
+ /* allocate usb buffers */
+ dev->irq_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->irq_dma);
+ if (!dev->irq_data)
+ goto err_out;
+
+ dev->ctl_data = usb_buffer_alloc(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->ctl_dma);
+ if (!dev->ctl_data)
+ goto err_out;
+
+ dev->ctl_req = usb_buffer_alloc(udev, sizeof(*(dev->ctl_req)),
+ GFP_KERNEL, &dev->ctl_req_dma);
+ if (!dev->ctl_req)
+ goto err_out;
+
+ /* allocate urb structures */
+ dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_irq)
+ goto err_out;
+
+ dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_ctl)
+ goto err_out;
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ err("invalid payload size %d, expected %d", ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+ USB_PKT_LEN,
+ cm109_urb_irq_callback, dev, endpoint->bInterval);
+ dev->urb_irq->transfer_dma = dev->irq_dma;
+ dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ dev->ctl_req->wValue = cpu_to_le16(0x200);
+ dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+ cm109_urb_ctl_callback, dev);
+ dev->urb_ctl->setup_dma = dev->ctl_req_dma;
+ dev->urb_ctl->transfer_dma = dev->ctl_dma;
+ dev->urb_ctl->transfer_flags |= URB_NO_SETUP_DMA_MAP |
+ URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = dev->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, dev);
+ input_dev->open = cm109_input_open;
+ input_dev->close = cm109_input_close;
+ input_dev->event = cm109_input_ev;
+
+ input_dev->keycode = dev->keymap;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+ /* register available key events */
+ for (i = 0; i < KEYMAP_SIZE; i++) {
+ unsigned short k = keymap(i);
+ dev->keymap[i] = k;
+ __set_bit(k, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ error = input_register_device(dev->idev);
+ if (error)
+ goto err_out;
+
+ usb_set_intfdata(intf, dev);
+
+ return 0;
+
+ err_out:
+ input_free_device(input_dev);
+ cm109_usb_cleanup(dev);
+ return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ info("cm109: usb_suspend (event=%d)", message.event);
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_stop_traffic(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ info("cm109: usb_resume");
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_restore_state(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Make sure input events don't try to toggle buzzer
+ * while we are resetting
+ */
+ dev->resetting = 1;
+ smp_wmb();
+
+ cm109_stop_traffic(dev);
+
+ return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev->resetting = 0;
+ smp_wmb();
+
+ cm109_restore_state(dev);
+
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static struct usb_driver cm109_driver = {
+ .name = "cm109",
+ .probe = cm109_usb_probe,
+ .disconnect = cm109_usb_disconnect,
+ .suspend = cm109_usb_suspend,
+ .resume = cm109_usb_resume,
+ .reset_resume = cm109_usb_resume,
+ .pre_reset = cm109_usb_pre_reset,
+ .post_reset = cm109_usb_post_reset,
+ .id_table = cm109_usb_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+ /* Load the phone keymap */
+ if (!strcasecmp(phone, "kip1000")) {
+ keymap = keymap_kip1000;
+ info("Keymap for Komunikate KIP1000 phone loaded");
+ } else if (!strcasecmp(phone, "gtalk")) {
+ keymap = keymap_gtalk;
+ info("Keymap for Genius G-talk phone loaded");
+ } else if (!strcasecmp(phone, "usbph01")) {
+ keymap = keymap_usbph01;
+ info("Keymap for Allied-Telesis Corega USBPH01 phone loaded");
+ } else {
+ err("Unsupported phone: %s", phone);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init cm109_init(void)
+{
+ int err;
+
+ err = cm109_select_keymap();
+ if (err)
+ return err;
+
+ err = usb_register(&cm109_driver);
+ if (err)
+ return err;
+
+ info(DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR);
+
+ return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+ usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
]
|
| 84/2 |
2026/05/10 20:32 |
llm |
fixes-finder |
0m
Model:
Tokens:
input: 20373
output: 42
thoughts: 890
Thoughts: **Analyzing the `cm109` Driver Bug**
Okay, so we're looking at a bug related to the `cm109` driver, specifically a race condition involving the `urb_ctl` USB control URB. The bug description is pretty clear: it's a double submission of this URB, leading to a crash. The report pins this to a race condition within the `cm109` driver's URB state machine, a situation where the driver submits `urb_ctl` while it's already active. The description highlights the issue stems from an out-of-band submission of `urb_ctl` within `cm109_toggle_buzzer_async()`, which breaks the expected alternating submission of `urb_irq` and `urb_ctl`.
Now, let's dive into the code. The commit in question is `c04148f915e5`, where the `cm109` driver was introduced. I'm focusing on `cm109_urb_irq_callback` and `cm109_toggle_buzzer_async`. It seems that if the `urb_ctl` isn't pending, the function immediately submits the `urb_ctl` via the callback. Checking this code, the problem description matches what I'm seeing: the callback submits the `urb_ctl` unconditionally.
However, the bug report mentions `cm109_urb_irq_callback` calling `cm109_submit_ctl()`. But in *this* commit, the function *directly* submits the URB. That's a discrepancy! So, the report might be referring to a later version. The original commit doesn't have the `cm109_submit_ctl()` function at all. It makes me wonder if that function was added in a subsequent commit to *address* this very issue? I need to check the Git history to see when `cm109_submit_ctl()` was introduced.
|
| 85/2 |
2026/05/10 20:32 |
tool |
git-log |
0m
Args: map[SourcePath:drivers/input/misc/cm109.c SymbolName:cm109_submit_ctl]
Results: map[Output:d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
c04148f915e5 Input: add driver for USB VoIP phones with CM109 chipset
]
|
| 86/2 |
2026/05/10 20:32 |
llm |
fixes-finder |
1m
Model:
Tokens:
input: 21807
output: 27
thoughts: 169
Thoughts: **Investigating the Git History of `cm109_submit_ctl`**
Okay, so I ran a `git log` search for commits involving "cm109_submit_ctl" and got `c04148f915e5` as a result. Now, I need to double-check that commit. Did it actually *contain* `cm109_submit_ctl`? Let me see... Nope. Just ran a diff search within `c04148f915e5`, looking for `cm109_submit_ctl` specifically, and it's not there. Hmm...
Right, that's it. It's a common oversight, but `git log -L` is behaving exactly as expected. It tracks the function definition by its *current* name and location. If the code was refactored, maybe extracted into a new function, or the name changed at some point, `git log` won't necessarily pick up the history of the *original* code. I need to trace this back. Let's see what happens if I look at `d8a43a83633a`.
|
| 87/2 |
2026/05/10 20:33 |
tool |
git-show |
0m
Args:
Results: map[Output:commit d8a43a83633a4770f00ee77079fbb7a2218cea2a
Author: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: Tue Sep 3 21:42:23 2024 -0700
Input: cm109 - use guard notation when acquiring mutex and spinlock
Using guard notation makes the code more compact and error handling
more robust by ensuring that locks are released in all code paths
when control leaves critical section.
Reviewed-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Link: https://lore.kernel.org/r/20240904044244.1042174-4-dmitry.torokhov@gmail.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 728325a2d574..0cfe5d4a573c 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -355,6 +355,35 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
__func__, error);
}
+static void cm109_submit_ctl(struct cm109_dev *dev)
+{
+ int error;
+
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
+
+ dev->irq_urb_pending = 0;
+
+ if (unlikely(dev->shutdown))
+ return;
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
+}
+
/*
* IRQ handler
*/
@@ -362,8 +391,6 @@ static void cm109_urb_irq_callback(struct urb *urb)
{
struct cm109_dev *dev = urb->context;
const int status = urb->status;
- int error;
- unsigned long flags;
dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
dev->irq_data->byte[0],
@@ -401,32 +428,7 @@ static void cm109_urb_irq_callback(struct urb *urb)
}
out:
-
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
-
- dev->irq_urb_pending = 0;
-
- if (likely(!dev->shutdown)) {
-
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
-
- dev->buzzer_pending = 0;
- dev->ctl_urb_pending = 1;
-
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
- }
-
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+ cm109_submit_ctl(dev);
}
static void cm109_urb_ctl_callback(struct urb *urb)
@@ -434,7 +436,6 @@ static void cm109_urb_ctl_callback(struct urb *urb)
struct cm109_dev *dev = urb->context;
const int status = urb->status;
int error;
- unsigned long flags;
dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
dev->ctl_data->byte[0],
@@ -449,35 +450,31 @@ static void cm109_urb_ctl_callback(struct urb *urb)
__func__, status);
}
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->ctl_urb_pending = 0;
- if (likely(!dev->shutdown)) {
-
- if (dev->buzzer_pending || status) {
- dev->buzzer_pending = 0;
- dev->ctl_urb_pending = 1;
- cm109_submit_buzz_toggle(dev);
- } else if (likely(!dev->irq_urb_pending)) {
- /* ask for key data */
- dev->irq_urb_pending = 1;
- error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_irq) failed %d\n",
- __func__, error);
- }
- }
+ if (unlikely(dev->shutdown))
+ return;
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+ if (dev->buzzer_pending || status) {
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ } else if (likely(!dev->irq_urb_pending)) {
+ /* ask for key data */
+ dev->irq_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_irq) failed %d\n",
+ __func__, error);
+ }
}
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
- unsigned long flags;
-
- spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+ guard(spinlock_irqsave)(&dev->ctl_submit_lock);
if (dev->ctl_urb_pending) {
/* URB completion will resubmit */
@@ -486,8 +483,6 @@ static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
dev->ctl_urb_pending = 1;
cm109_submit_buzz_toggle(dev);
}
-
- spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
}
static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
@@ -556,32 +551,30 @@ static int cm109_input_open(struct input_dev *idev)
return error;
}
- mutex_lock(&dev->pm_mutex);
-
- dev->buzzer_state = 0;
- dev->key_code = -1; /* no keys pressed */
- dev->keybit = 0xf;
+ scoped_guard(mutex, &dev->pm_mutex) {
+ dev->buzzer_state = 0;
+ dev->key_code = -1; /* no keys pressed */
+ dev->keybit = 0xf;
- /* issue INIT */
- dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
- dev->ctl_data->byte[HID_OR3] = 0x00;
+ /* issue INIT */
+ dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ dev->ctl_data->byte[HID_OR3] = 0x00;
- dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (error) {
- dev->ctl_urb_pending = 0;
- dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
- } else {
- dev->open = 1;
+ dev->ctl_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+ if (!error) {
+ dev->open = 1;
+ return 0;
+ }
}
- mutex_unlock(&dev->pm_mutex);
+ dev->ctl_urb_pending = 0;
+ usb_autopm_put_interface(dev->intf);
- if (error)
- usb_autopm_put_interface(dev->intf);
+ dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
return error;
}
@@ -590,17 +583,15 @@ static void cm109_input_close(struct input_dev *idev)
{
struct cm109_dev *dev = input_get_drvdata(idev);
- mutex_lock(&dev->pm_mutex);
-
- /*
- * Once we are here event delivery is stopped so we
- * don't need to worry about someone starting buzzer
- * again
- */
- cm109_stop_traffic(dev);
- dev->open = 0;
-
- mutex_unlock(&dev->pm_mutex);
+ scoped_guard(mutex, &dev->pm_mutex) {
+ /*
+ * Once we are here event delivery is stopped so we
+ * don't need to worry about someone starting buzzer
+ * again
+ */
+ cm109_stop_traffic(dev);
+ dev->open = 0;
+ }
usb_autopm_put_interface(dev->intf);
}
@@ -823,9 +814,9 @@ static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
- mutex_lock(&dev->pm_mutex);
+ guard(mutex)(&dev->pm_mutex);
+
cm109_stop_traffic(dev);
- mutex_unlock(&dev->pm_mutex);
return 0;
}
@@ -836,9 +827,9 @@ static int cm109_usb_resume(struct usb_interface *intf)
dev_info(&intf->dev, "cm109: usb_resume\n");
- mutex_lock(&dev->pm_mutex);
+ guard(mutex)(&dev->pm_mutex);
+
cm109_restore_state(dev);
- mutex_unlock(&dev->pm_mutex);
return 0;
}
]
|
| 88/2 |
2026/05/10 20:33 |
llm |
fixes-finder |
1m
Model:
Tokens:
input: 25632
output: 45
thoughts: 1522
Thoughts: **Analyzing the CM109 USB Driver URB Submission Issue**
Okay, so I'm looking at this CM109 driver and trying to understand this "URB submitted while active" warning. Initially, it looks like `d8a43a83633a` introduced `cm109_submit_ctl` which is called from `cm109_urb_irq_callback`. The bug report says `cm109_submit_ctl` naively resubmits the `urb_ctl` without checking its state. Let's see if this logic existed before `d8a43a83633a`.
Peeking at the code before that commit, specifically the `cm109_urb_irq_callback` function, it's clear the problematic behavior *was* present. It blindly submits the `urb_ctl`. So, `d8a43a83633a` didn't introduce the bug; it just isolated this part of the logic into a separate function, `cm109_submit_ctl`.
Now, let's verify if the original commit `c04148f915e5` that introduced the driver is indeed the culprit. Looking at that commit, specifically the `cm109_urb_irq_callback` and the related `cm109_toggle_buzzer_async`, it's clear: the driver, right from the start, had this race condition. `cm109_toggle_buzzer_async` sets `dev->ctl_urb_pending`, and calls `cm109_submit_buzz_toggle` which submits `urb_ctl`. Then, when the `urb_irq` completes, the callback always resubmits the `urb_ctl` no matter what. Exactly as the bug report describes.
So, the issue isn't a *regression*; it's a fundamental design flaw present from the very beginning. The later commit `d8a43a83633a` just refactored the code, but the race condition allowing `urb_ctl` to be submitted while active has been there since `c04148f915e5`. I should probably check for any other related commits, just to be thorough and verify there were no other changes in logic in this area between the initial commit and the refactoring, just to be sure.
|
| 89/2 |
2026/05/10 20:34 |
tool |
git-log |
0m
Args: map[CodeRegexp:dev->ctl_urb_pending = 1 PathPrefix:drivers/input/misc/cm109.c]
Results: map[Output:d8a43a83633a Input: cm109 - use guard notation when acquiring mutex and spinlock
82e060904732 Input: cm109 - do not stomp on control URB
c04148f915e5 Input: add driver for USB VoIP phones with CM109 chipset
]
|
| 90/2 |
2026/05/10 20:34 |
llm |
fixes-finder |
1m
Model:
Tokens:
input: 27827
output: 27
thoughts: 39
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing a Potential Regression**
Okay, hold on a second. That commit hash, `82e060904732`... looks like it's flagged as a potential regression. The message is pretty direct: "Input: cm109 - do not stomp on control URB". Right, cm109, likely related to a specific hardware or driver module, and the issue centers around URBs, specifically control URBs. This immediately makes me think of interrupt handling, USB endpoint management, and potential race conditions. It's screaming a conflict over a shared resource within the USB stack. Time to dig into this commit to understand the context. I need to see what changed, how it affects the URB handling, and crucially, what assumptions were made that might be incorrect. I need to see if it involved locking changes, interrupt masking, or anything related to data structures being accessed by multiple threads. I need to trace this back and see where the stomping is occurring. This is the kind of bug that can manifest intermittently and be a real pain to debug, so I'll be prepared for that.
|
| 91/2 |
2026/05/10 20:36 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 82e06090473289ce63e23fdeb8737aad59b10645
Author: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Date: Wed Dec 9 20:13:24 2020 -0800
Input: cm109 - do not stomp on control URB
We need to make sure we are not stomping on the control URB that was
issued when opening the device when attempting to toggle buzzer.
To do that we need to mark it as pending in cm109_open().
Reported-and-tested-by: syzbot+150f793ac5bc18eee150@syzkaller.appspotmail.com
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index e413801f0491..f515fae465c3 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -568,12 +568,15 @@ static int cm109_input_open(struct input_dev *idev)
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->ctl_data->byte[HID_OR3] = 0x00;
+ dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
- if (error)
+ if (error) {
+ dev->ctl_urb_pending = 0;
dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
- else
+ } else {
dev->open = 1;
+ }
mutex_unlock(&dev->pm_mutex);
]
|
| 92/2 |
2026/05/10 20:36 |
llm |
fixes-finder |
2m
Model:
Tokens:
input: 28720
output: 53
thoughts: 3118
Thoughts: **Analysis of the CM109 Driver USB URB Submission Bug**
Okay, so this commit `82e060904732` addresses a related, but distinct, issue concerning `cm109_input_open`. I need to stay focused on the core problem: `cm109_urb_irq_callback` submitting `urb_ctl` without checking if `dev->ctl_urb_pending` is set. This looks like a classic race condition.
I need to confirm which commit introduced the issue. The bug report points to code that was first introduced in `c04148f915e5`. The description highlights that `cm109_submit_ctl()` blindly assumes the URB is idle, updating the DMA buffer, and resubmitting it in the IRQ callback, which is what triggers the "URB ... submitted while active" warning in the USB core.
Now, hold on. `cm109_submit_ctl()` wasn't even introduced until `d8a43a83633a` – which is later. The bug description is specific to `cm109_submit_ctl()`. But the crash happened in a kernel with `d8a43a83633a`. It makes sense that I'm seeing it in `cm109_submit_ctl` even though the actual bug (missing `ctl_urb_pending` check) was present before.
Let's do a code comparison between the two commits, starting with `c04148f915e5`:
```c
spin_lock(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (likely(!dev->shutdown)) {
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
err("%s: usb_submit_urb (urb_ctl) failed %d",
__func__, error);
}
spin_unlock(&dev->ctl_submit_lock);
```
Then, comparing with `d8a43a83633a`:
```c
static void cm109_submit_ctl(struct cm109_dev *dev)
{
int error;
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
if (unlikely(dev->shutdown))
return;
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
dev->ctl_data->byte[HID_OR1] = dev->keybit;
dev->ctl_data->byte[HID_OR2] = dev->keybit;
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
"%s: usb_submit_urb (urb_ctl) failed %d\n",
__func__, error);
}
```
The logic is virtually identical; this confirms that `d8a43a83633a` didn't introduce the bug. It was, as suspected, already present in `c04148f915e5`.
Just to be absolutely certain, let's explore if other commits could have contributed. Perhaps the buzzer toggle function? In `c04148f915e5`:
```c
static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
{
unsigned long flags;
spin_lock_irqsave
|
| 93/2 |
2026/05/10 20:38 |
tool |
set-results |
0m
Args: map[FixesHash:c04148f915e5ba7947752e6348e0da4cdab1329e]
Results: map[FixesHash:c04148f915e5ba7947752e6348e0da4cdab1329e]
|
| 94/1 |
2026/05/10 20:38 |
action |
format-fixes |
0m
Results: map[Fixes:map[Hash:c04148f915e5ba7947752e6348e0da4cdab1329e Title:Input: add driver for USB VoIP phones with CM109 chipset]]
|
| 95/1 |
2026/05/10 20:38 |
action |
get-maintainers |
0m
Results: map[Recipients:[map[Email:dmitry.torokhov@gmail.com Name:Dmitry Torokhov To:false] map[Email:linux-input@vger.kernel.org Name:open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)... To:false] map[Email:linux-kernel@vger.kernel.org Name: To:true]]]
|
| 96/1 |
2026/05/10 20:38 |
action |
get-recent-commits |
0m
Results: map[RecentCommits:Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
Input: cm109 - use guard notation when acquiring mutex and spinlock
Input: remove third argument of usb_maxpacket()
Input: cm109 - do not stomp on control URB
Input: cm109 - remove needless check before usb_free_coherent()
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 372
Input: cm109 - validate number of endpoints before using them
Input: cm109 - spin_lock in complete() cleanup
Input: cm109 - fix handling of volume and mute buttons
Input: cm109 - convert high volume dev_err() to dev_err_ratelimited()
USB: input: cm109.c: fix up dev_* messages
USB: input: cm109.c: fix up dev_err() usage
USB: cm109.c: remove err() usage
Input: cm109 - fix checking return value of usb_control_msg
Update broken web addresses in the kernel.
USB: rename usb_buffer_alloc() and usb_buffer_free() users
USB: remove uses of URB_NO_SETUP_DMA_MAP
Input: cm109 - add keymap for ATCom AU-100 phone
Input: cm109 - add missing newlines to messages
]
|
| 97/1 |
2026/05/10 20:38 |
agent |
description-generator |
0m
Model:
Instruction:
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.
Your final reply should contain only the text of the commit description.
Phrase the one-line summary so that it is not longer than 72 characters.
Prefer calling several tools at the same time to save round-trips.
Prompt:
The crash that corresponds to the bug is:
cm109 1-1:0.8: cm109_urb_irq_callback: urb status -71
------------[ cut here ]------------
URB ffff888046210500 submitted while active
WARNING: drivers/usb/core/urb.c:379 at usb_submit_urb+0x7b/0x18b0 drivers/usb/core/urb.c:379, CPU#1: swapper/1/0
Modules linked in:
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #1 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:usb_submit_urb+0x7e/0x18b0 drivers/usb/core/urb.c:379
Code: 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 17 72 06 fb 49 83 3e 00 74 40 e8 6c 63 9c fa 48 8d 3d 65 92 ff 08 48 89 de <67> 48 0f b9 3a b8 f0 ff ff ff eb 11 e8 51 63 9c fa eb 05 e8 4a 63
RSP: 0018:ffffc900001f8a00 EFLAGS: 00010046
RAX: ffffffff8727c654 RBX: ffff888046210500 RCX: ffff8880404b0000
RDX: 0000000000000100 RSI: ffff888046210500 RDI: ffffffff902758c0
RBP: 000000000000000f R08: 0000000000000003 R09: 0000000000000004
R10: dffffc0000000000 R11: fffff5200003f140 R12: 0000000000000820
R13: ffff88804fd73030 R14: ffff888046210508 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff8880ebc29000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000558409200a78 CR3: 0000000046b1e000 CR4: 00000000000006f0
Call Trace:
<IRQ>
cm109_submit_ctl drivers/input/misc/cm109.c:380 [inline]
cm109_urb_irq_callback+0x6e2/0xc90 drivers/input/misc/cm109.c:431
__usb_hcd_giveback_urb+0x376/0x540 drivers/usb/core/hcd.c:1657
dummy_timer+0xbc0/0x4650 drivers/usb/gadget/udc/dummy_hcd.c:2005
__run_hrtimer kernel/time/hrtimer.c:1930 [inline]
__hrtimer_run_queues+0x3c0/0xa20 kernel/time/hrtimer.c:1994
hrtimer_run_softirq+0x17a/0x240 kernel/time/hrtimer.c:2011
handle_softirqs+0x22a/0x840 kernel/softirq.c:622
__do_softirq kernel/softirq.c:656 [inline]
invoke_softirq kernel/softirq.c:496 [inline]
__irq_exit_rcu+0xca/0x220 kernel/softirq.c:735
irq_exit_rcu+0x9/0x30 kernel/softirq.c:752
instr_sysvec_apic_timer_interrupt arch/x86/kernel/apic/apic.c:1061 [inline]
sysvec_apic_timer_interrupt+0xa6/0xc0 arch/x86/kernel/apic/apic.c:1061
</IRQ>
<TASK>
asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:63
Code: ab 90 02 e9 d3 f5 02 00 cc cc cc 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa eb 07 0f 00 2d 53 33 21 00 fb f4 <c3> cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc cc 90 90 90 90 90
RSP: 0018:ffffc9000046fe20 EFLAGS: 00000202
RAX: 0000000000076dab RBX: ffffffff8199768a RCX: 0000000080000001
RDX: 0000000000000001 RSI: ffffffff8deddb4e RDI: ffffffff8c0ea120
RBP: ffffc9000046ff10 R08: ffff88807ee339db R09: 1ffff1100fdc673b
R10: dffffc0000000000 R11: ffffed100fdc673c R12: 0000000000000001
R13: 1ffff11008096000 R14: 0000000000000001 R15: 1ffff11008096000
arch_safe_halt arch/x86/kernel/process.c:766 [inline]
default_idle+0x9/0x20 arch/x86/kernel/process.c:767
default_idle_call+0x72/0xb0 kernel/sched/idle.c:122
cpuidle_idle_call kernel/sched/idle.c:199 [inline]
do_idle+0x36a/0x5f0 kernel/sched/idle.c:352
cpu_startup_entry+0x43/0x60 kernel/sched/idle.c:451
start_secondary+0x101/0x110 arch/x86/kernel/smpboot.c:312
common_startup_64+0x13e/0x147
</TASK>
----------------
Code disassembly (best guess):
0: 89 f0 mov %esi,%eax
2: 48 c1 e8 03 shr $0x3,%rax
6: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
b: 74 08 je 0x15
d: 4c 89 f7 mov %r14,%rdi
10: e8 17 72 06 fb call 0xfb06722c
15: 49 83 3e 00 cmpq $0x0,(%r14)
19: 74 40 je 0x5b
1b: e8 6c 63 9c fa call 0xfa9c638c
20: 48 8d 3d 65 92 ff 08 lea 0x8ff9265(%rip),%rdi # 0x8ff928c
27: 48 89 de mov %rbx,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 f0 ff ff ff mov $0xfffffff0,%eax
34: eb 11 jmp 0x47
36: e8 51 63 9c fa call 0xfa9c638c
3b: eb 05 jmp 0x42
3d: e8 .byte 0xe8
3e: 4a rex.WX
3f: 63 .byte 0x63
The explanation of the root cause of the bug is:
The root cause of this crash is a race condition in the `cm109` driver's URB state machine, where a control URB (`urb_ctl`) is submitted while it is already active.
### Root Cause Analysis
The driver uses two URBs to communicate with the device:
1. `urb_ctl` (Control OUT): Sends the current key matrix column (`dev->keybit`) and the buzzer state.
2. `urb_irq` (Interrupt IN): Reads the key matrix row state.
These URBs are designed to run in a continuous, strictly alternating loop:
- When `urb_ctl` completes, `cm109_urb_ctl_callback()` submits `urb_irq`.
- When `urb_irq` completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()` which submits `urb_ctl`.
However, the driver also supports sound events (`SND_BELL` / `SND_TONE`) to toggle the buzzer. When a sound event is received (e.g., when the syzkaller reproducer writes a `\a` BEL character to `/dev/tty1`), `cm109_input_ev()` calls `cm109_toggle_buzzer_async()`.
If `urb_ctl` is not currently pending (which is true 50% of the time, whenever `urb_irq` is in flight), `cm109_toggle_buzzer_async()` immediately submits `urb_ctl` out-of-band to update the buzzer state. This breaks the strict alternation and causes **both** `urb_irq` and `urb_ctl` to be active concurrently.
When `urb_irq` subsequently completes, `cm109_urb_irq_callback()` calls `cm109_submit_ctl()`. `cm109_submit_ctl()` blindly assumes `urb_ctl` is idle, updates the DMA buffer, and calls `usb_submit_urb(dev->urb_ctl)`. Since `urb_ctl` is already active, the USB core catches it and triggers the `URB ... submitted while active` warning.
### Sequence of Events
1. The normal polling loop is running. `urb_irq` is submitted and active (`dev->irq_urb_pending = 1`, `dev->ctl_urb_pending = 0`).
2. A sound event is received. `cm109_toggle_buzzer_async()` is called.
3. Since `dev->ctl_urb_pending == 0`, it sets `dev->ctl_urb_pending = 1` and submits `urb_ctl` via `cm109_submit_buzz_toggle()`. Now **both** URBs are active.
4. `urb_irq` completes. `cm109_urb_irq_callback()` processes the key data and calls `cm109_submit_ctl()`.
5. `cm109_submit_ctl()` clears `dev->irq_urb_pending = 0`, sets `dev->ctl_urb_pending = 1`, and calls `usb_submit_urb(dev->urb_ctl)`.
6. **CRASH:** `urb_ctl` is already active from step 3.
### Proposed Fix
To fix this, `cm109_submit_ctl()` must check if `dev->ctl_urb_pending` is already set. If it is, it should defer the submission by setting `dev->buzzer_pending = 1` (which acts as a general "resubmit `urb_ctl`" flag in the completion handler) and return immediately.
Additionally, `cm109_submit_buzz_toggle()` (which is called by the completion handler when `buzzer_pending` is set) must be updated to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`, ensuring that the deferred key matrix column update is not lost.
Here is the logic for the fix:
```c
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -366,6 +369,11 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
+
if (dev->buzzer_state)
dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
else
```
The diff of the bug fix is:
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
index 353d3c1d3..0114d7293 100644
--- a/drivers/input/misc/cm109.c
+++ b/drivers/input/misc/cm109.c
@@ -348,6 +348,9 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
else
dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
if (error)
dev_err(&dev->intf->dev,
@@ -357,8 +360,6 @@ static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
static void cm109_submit_ctl(struct cm109_dev *dev)
{
- int error;
-
guard(spinlock_irqsave)(&dev->ctl_submit_lock);
dev->irq_urb_pending = 0;
@@ -366,22 +367,15 @@ static void cm109_submit_ctl(struct cm109_dev *dev)
if (unlikely(dev->shutdown))
return;
- if (dev->buzzer_state)
- dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
- else
- dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
-
- dev->ctl_data->byte[HID_OR1] = dev->keybit;
- dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ if (dev->ctl_urb_pending) {
+ dev->buzzer_pending = 1;
+ return;
+ }
dev->buzzer_pending = 0;
dev->ctl_urb_pending = 1;
- error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
- if (error)
- dev_err(&dev->intf->dev,
- "%s: usb_submit_urb (urb_ctl) failed %d\n",
- __func__, error);
+ cm109_submit_buzz_toggle(dev);
}
/*
Additional description of the patch:
I have implemented a fix for the race condition in the `cm109` driver's URB state machine that was causing the `URB submitted while active` crash.
### What was done and why:
1. **Prevent Concurrent URB Submission:** I added a check for `dev->ctl_urb_pending` at the beginning of `cm109_submit_ctl()`. If it is already set, it means `urb_ctl` is currently active (e.g., it was submitted out-of-band by `cm109_toggle_buzzer_async()` due to a sound event). In this case, we set `dev->buzzer_pending = 1` to defer the submission and return immediately. This directly prevents the crash by ensuring the URB is never submitted while it's already in flight.
2. **Preserve Key Matrix State:** I updated `cm109_submit_buzz_toggle()` to also copy the latest `dev->keybit` to `HID_OR1` and `HID_OR2`. This is crucial because when the deferred submission is eventually processed by the completion handler (`cm109_urb_ctl_callback`), it calls `cm109_submit_buzz_toggle()`. By including the `keybit` update here, we ensure that the deferred key matrix column update is not lost and the device continues scanning correctly.
3. **Code Refactoring & Deduplication:** Since `cm109_submit_ctl()` and `cm109_submit_buzz_toggle()` now prepare the exact same data for `urb_ctl` (buzzer state and keybit), I refactored `cm109_submit_ctl()` to simply call `cm109_submit_buzz_toggle()` after checking and updating the pending flags. This removes code duplication and makes the URB submission logic cleaner and more consistent across the driver.
4. **Concurrency Safety:** I verified that `cm109_submit_buzz_toggle()` is always called with the `ctl_submit_lock` held across all its call sites (`cm109_toggle_buzzer_async`, `cm109_urb_ctl_callback`, and now `cm109_submit_ctl`). This ensures that reading `dev->keybit` and modifying `dev->ctl_data` remains perfectly safe from concurrency issues.
Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc.
Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
treewide: Replace kmalloc with kmalloc_obj for non-scalar types
Input: cm109 - use guard notation when acquiring mutex and spinlock
Input: remove third argument of usb_maxpacket()
Input: cm109 - do not stomp on control URB
Input: cm109 - remove needless check before usb_free_coherent()
treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 372
Input: cm109 - validate number of endpoints before using them
Input: cm109 - spin_lock in complete() cleanup
Input: cm109 - fix handling of volume and mute buttons
Input: cm109 - convert high volume dev_err() to dev_err_ratelimited()
USB: input: cm109.c: fix up dev_* messages
USB: input: cm109.c: fix up dev_err() usage
USB: cm109.c: remove err() usage
Input: cm109 - fix checking return value of usb_control_msg
Update broken web addresses in the kernel.
USB: rename usb_buffer_alloc() and usb_buffer_free() users
USB: remove uses of URB_NO_SETUP_DMA_MAP
Input: cm109 - add keymap for ATCom AU-100 phone
Input: cm109 - add missing newlines to messages
If the patch removes the WARN_ON macro, refer to the fact that WARN_ON
must not be used for conditions that can legitimately happen, and that pr_err
should be used instead if necessary.
Reply: Input: cm109 - prevent concurrent control URB submissions
The cm109 driver uses two URBs to communicate with the device: urb_ctl
(Control OUT) and urb_irq (Interrupt IN). These URBs are designed to run
in a continuous, strictly alternating loop.
However, the driver also supports sound events to toggle the buzzer.
When a sound event is received, cm109_toggle_buzzer_async() is called.
If urb_ctl is not currently pending, it immediately submits urb_ctl
out-of-band to update the buzzer state. This breaks the strict
alternation and causes both urb_irq and urb_ctl to be active
concurrently.
When urb_irq subsequently completes, cm109_urb_irq_callback() calls
cm109_submit_ctl(). cm109_submit_ctl() blindly assumes urb_ctl is idle,
updates the DMA buffer, and calls usb_submit_urb(dev->urb_ctl). Since
urb_ctl is already active, the USB core catches it and triggers the
"URB submitted while active" warning.
To fix this, check if dev->ctl_urb_pending is already set in
cm109_submit_ctl(). If it is, defer the submission by setting
dev->buzzer_pending = 1 and return immediately.
Additionally, update cm109_submit_buzz_toggle() to also copy the latest
dev->keybit to HID_OR1 and HID_OR2, ensuring that the deferred key
matrix column update is not lost. Since cm109_submit_ctl() and
cm109_submit_buzz_toggle() now prepare the exact same data for urb_ctl,
refactor cm109_submit_ctl() to simply call cm109_submit_buzz_toggle()
after checking and updating the pending flags.
|
| 98/2 |
2026/05/10 20:38 |
llm |
description-generator |
0m
Model:
Tokens:
input: 8861
output: 418
thoughts: 1271
Thoughts: **Bug Fix for Concurrent URB Submissions in cm109 Driver**
Okay, so I've got this crash in the `cm109` driver – "URB submitted while active" in `usb_submit_urb`. It's a classic race condition, which always makes me cringe. Digging into it, the problem is that `urb_ctl` is getting submitted while it's already active. The driver alternates between `urb_ctl` (Control OUT) and `urb_irq` (Interrupt IN) in a loop, which is pretty standard for this type of device interaction. However, the sound events, specifically `cm109_toggle_buzzer_async()`, are submitting `urb_ctl` out-of-band to toggle the buzzer, breaking the expected sequence.
Here's the rub: When the `urb_irq` completes and calls `cm109_submit_ctl()`, it blindly submits `urb_ctl` again, assuming it's idle. Since the buzzer event already triggered an `urb_ctl` submission, this causes the USB core to rightly complain about the concurrent submission.
The fix I've worked out is to add a check in `cm109_submit_ctl()` to see if `dev->ctl_urb_pending` is already set. If it is, then the submission is deferred. I'll set `dev->buzzer_pending = 1` and return immediately. The sound event handler will then know when it's safe to submit the control URB.
Also, the control URB also updates the key matrix column, and the key bit state was getting lost when `cm109_submit_buzz_toggle()` was called via the sound event handler. So, I updated `cm109_submit_buzz_toggle()` to also copy `dev->keybit` to `HID_OR1` and `HID_OR2`, to ensure the key matrix data is not lost during the deferral. Since `cm109_submit_ctl()` and `cm109_submit_buzz_toggle()` are now doing essentially the same thing, the best approach seems to refactor `cm109_submit_ctl()` to simply call `cm109_submit_buzz_toggle()` after checking and updating the pending flags. I'll summarize this as "prevent concurrent control URB submissions". The commit message should convey the fix clearly, outlining the race, the deferred submission, and the handling of the key matrix state. Should be good to go.
|
| 99/1 |
2026/05/10 20:39 |
action |
format-patch-description |
0m
Results: map[PatchDescription:Input: cm109 - prevent concurrent control URB submissions
The cm109 driver uses two URBs to communicate with the device: urb_ctl
(Control OUT) and urb_irq (Interrupt IN). These URBs are designed to run
in a continuous, strictly alternating loop.
However, the driver also supports sound events to toggle the buzzer.
When a sound event is received, cm109_toggle_buzzer_async() is called.
If urb_ctl is not currently pending, it immediately submits urb_ctl
out-of-band to update the buzzer state. This breaks the strict
alternation and causes both urb_irq and urb_ctl to be active
concurrently.
When urb_irq subsequently completes, cm109_urb_irq_callback() calls
cm109_submit_ctl(). cm109_submit_ctl() blindly assumes urb_ctl is idle,
updates the DMA buffer, and calls usb_submit_urb(dev->urb_ctl). Since
urb_ctl is already active, the USB core catches it and triggers the
"URB submitted while active" warning.
To fix this, check if dev->ctl_urb_pending is already set in
cm109_submit_ctl(). If it is, defer the submission by setting
dev->buzzer_pending = 1 and return immediately.
Additionally, update cm109_submit_buzz_toggle() to also copy the latest
dev->keybit to HID_OR1 and HID_OR2, ensuring that the deferred key
matrix column update is not lost. Since cm109_submit_ctl() and
cm109_submit_buzz_toggle() now prepare the exact same data for urb_ctl,
refactor cm109_submit_ctl() to simply call cm109_submit_buzz_toggle()
after checking and updating the pending flags.]
|